Search in sources :

Example 1 with ValueType

use of org.openhab.core.io.transport.modbus.ModbusConstants.ValueType in project openhab-addons by openhab.

the class ModbusDataThingHandler method requestFromCommand.

@Nullable
private ModbusWriteRequestBlueprint requestFromCommand(ChannelUID channelUID, Command origCommand, ModbusDataConfiguration config, Command transformedCommand, Integer writeStart) {
    ModbusWriteRequestBlueprint request;
    boolean writeMultiple = config.isWriteMultipleEvenWithSingleRegisterOrCoil();
    String writeType = config.getWriteType();
    ModbusPollerThingHandler pollerHandler = this.pollerHandler;
    if (writeType == null) {
        // disposed thing
        return null;
    }
    if (writeType.equals(WRITE_TYPE_COIL)) {
        Optional<Boolean> commandAsBoolean = ModbusBitUtilities.translateCommand2Boolean(transformedCommand);
        if (!commandAsBoolean.isPresent()) {
            logger.warn("Cannot process command {} with channel {} since command is not OnOffType, OpenClosedType or Decimal trying to write to coil. Do not know how to convert to 0/1. Transformed command was '{}'", origCommand, channelUID, transformedCommand);
            return null;
        }
        boolean data = commandAsBoolean.get();
        request = new ModbusWriteCoilRequestBlueprint(slaveId, writeStart, data, writeMultiple, config.getWriteMaxTries());
    } else if (writeType.equals(WRITE_TYPE_HOLDING)) {
        ValueType writeValueType = this.writeValueType;
        if (writeValueType == null) {
            // Should not happen in practice, since we are not in configuration error (checked above)
            // This will make compiler happy anyways with the null checks
            logger.warn("Received command but write value type not set! Ignoring command");
            return null;
        }
        final ModbusRegisterArray data;
        if (writeValueType.equals(ValueType.BIT)) {
            if (writeSubIndex.isEmpty()) {
                // Should not happen! should be in configuration error
                logger.error("Bug: sub index not present but writeValueType=BIT. Should be in configuration error");
                return null;
            }
            Optional<Boolean> commandBool = ModbusBitUtilities.translateCommand2Boolean(transformedCommand);
            if (commandBool.isEmpty()) {
                logger.warn("Data thing is configured to write individual bit but we received command that is not convertible to 0/1 bit. Ignoring.");
                return null;
            } else if (pollerHandler == null) {
                logger.warn("Bug: sub index present but not child of poller. Should be in configuration erro");
                return null;
            }
            // writing bit of an individual register. Using cache from poller
            AtomicReference<@Nullable ModbusRegisterArray> cachedRegistersRef = pollerHandler.getLastPolledDataCache();
            ModbusRegisterArray mutatedRegisters = cachedRegistersRef.updateAndGet(cachedRegisters -> cachedRegisters == null ? null : combineCommandWithRegisters(cachedRegisters, writeStart, writeSubIndex.get(), commandBool.get()));
            if (mutatedRegisters == null) {
                logger.warn("Received command to thing with writeValueType=bit (pointing to individual bit of a holding register) but internal cache not yet populated. Ignoring command");
                return null;
            }
            // extract register (first byte index = register index * 2)
            byte[] allMutatedBytes = mutatedRegisters.getBytes();
            int writeStartRelative = writeStart - pollStart;
            data = new ModbusRegisterArray(allMutatedBytes[writeStartRelative * 2], allMutatedBytes[writeStartRelative * 2 + 1]);
        } else {
            data = ModbusBitUtilities.commandToRegisters(transformedCommand, writeValueType);
        }
        writeMultiple = writeMultiple || data.size() > 1;
        request = new ModbusWriteRegisterRequestBlueprint(slaveId, writeStart, data, writeMultiple, config.getWriteMaxTries());
    } else {
        // We keep this here for future-proofing the code (new writeType values)
        throw new IllegalStateException(String.format("writeType does not equal %s or %s and thus configuration is invalid. Should not end up this far with configuration error.", WRITE_TYPE_COIL, WRITE_TYPE_HOLDING));
    }
    return request;
}
Also used : ModbusBindingConstantsInternal(org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal) LoggerFactory(org.slf4j.LoggerFactory) OnOffType(org.openhab.core.library.types.OnOffType) DateTimeType(org.openhab.core.library.types.DateTimeType) ModbusConstants(org.openhab.core.io.transport.modbus.ModbusConstants) BitArray(org.openhab.core.io.transport.modbus.BitArray) DateTimeItem(org.openhab.core.library.items.DateTimeItem) HexUtils(org.openhab.core.util.HexUtils) BigDecimal(java.math.BigDecimal) Nullable(org.eclipse.jdt.annotation.Nullable) ModbusWriteCoilRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteCoilRequestBlueprint) ThingHandlerCallback(org.openhab.core.thing.binding.ThingHandlerCallback) Duration(java.time.Duration) Map(java.util.Map) ModbusCommunicationInterface(org.openhab.core.io.transport.modbus.ModbusCommunicationInterface) NumberItem(org.openhab.core.library.items.NumberItem) EndpointNotInitializedException(org.openhab.binding.modbus.handler.EndpointNotInitializedException) NonNullByDefault(org.eclipse.jdt.annotation.NonNullByDefault) Collection(java.util.Collection) UnDefType(org.openhab.core.types.UnDefType) BaseThingHandler(org.openhab.core.thing.binding.BaseThingHandler) BridgeHandler(org.openhab.core.thing.binding.BridgeHandler) ModbusReadFunctionCode(org.openhab.core.io.transport.modbus.ModbusReadFunctionCode) CascadedValueTransformationImpl(org.openhab.binding.modbus.internal.CascadedValueTransformationImpl) ModbusTransportException(org.openhab.core.io.transport.modbus.exception.ModbusTransportException) BundleContext(org.osgi.framework.BundleContext) Objects(java.util.Objects) List(java.util.List) ValueType(org.openhab.core.io.transport.modbus.ModbusConstants.ValueType) AsyncModbusWriteResult(org.openhab.core.io.transport.modbus.AsyncModbusWriteResult) Optional(java.util.Optional) ModbusPollerThingHandler(org.openhab.binding.modbus.handler.ModbusPollerThingHandler) ModbusReadRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint) OpenClosedType(org.openhab.core.library.types.OpenClosedType) DimmerItem(org.openhab.core.library.items.DimmerItem) AsyncModbusFailure(org.openhab.core.io.transport.modbus.AsyncModbusFailure) ModbusRegisterArray(org.openhab.core.io.transport.modbus.ModbusRegisterArray) ModbusWriteRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint) SwitchItem(org.openhab.core.library.items.SwitchItem) LocalDateTime(java.time.LocalDateTime) ValueTransformation(org.openhab.binding.modbus.internal.ValueTransformation) HashMap(java.util.HashMap) AtomicReference(java.util.concurrent.atomic.AtomicReference) ModbusDataConfiguration(org.openhab.binding.modbus.internal.config.ModbusDataConfiguration) ModbusBitUtilities(org.openhab.core.io.transport.modbus.ModbusBitUtilities) ModbusWriteRegisterRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint) Thing(org.openhab.core.thing.Thing) ChannelUID(org.openhab.core.thing.ChannelUID) ThingStatusInfo(org.openhab.core.thing.ThingStatusInfo) WriteRequestJsonUtilities(org.openhab.core.io.transport.modbus.json.WriteRequestJsonUtilities) DecimalType(org.openhab.core.library.types.DecimalType) ModbusConnectionException(org.openhab.core.io.transport.modbus.exception.ModbusConnectionException) ModbusConfigurationException(org.openhab.binding.modbus.internal.ModbusConfigurationException) ThingStatus(org.openhab.core.thing.ThingStatus) Command(org.openhab.core.types.Command) Logger(org.slf4j.Logger) RollershutterItem(org.openhab.core.library.items.RollershutterItem) State(org.openhab.core.types.State) RefreshType(org.openhab.core.types.RefreshType) ThingStatusDetail(org.openhab.core.thing.ThingStatusDetail) TimeUnit(java.util.concurrent.TimeUnit) ModbusEndpointThingHandler(org.openhab.binding.modbus.handler.ModbusEndpointThingHandler) StringItem(org.openhab.core.library.items.StringItem) ContactItem(org.openhab.core.library.items.ContactItem) AsyncModbusReadResult(org.openhab.core.io.transport.modbus.AsyncModbusReadResult) SingleValueTransformation(org.openhab.binding.modbus.internal.SingleValueTransformation) Collections(java.util.Collections) FrameworkUtil(org.osgi.framework.FrameworkUtil) Bridge(org.openhab.core.thing.Bridge) ModbusRegisterArray(org.openhab.core.io.transport.modbus.ModbusRegisterArray) ModbusWriteRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint) Optional(java.util.Optional) ValueType(org.openhab.core.io.transport.modbus.ModbusConstants.ValueType) AtomicReference(java.util.concurrent.atomic.AtomicReference) ModbusPollerThingHandler(org.openhab.binding.modbus.handler.ModbusPollerThingHandler) ModbusWriteRegisterRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint) Nullable(org.eclipse.jdt.annotation.Nullable) ModbusWriteCoilRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteCoilRequestBlueprint) Nullable(org.eclipse.jdt.annotation.Nullable)

Example 2 with ValueType

use of org.openhab.core.io.transport.modbus.ModbusConstants.ValueType in project openhab-addons by openhab.

the class ModbusDataThingHandler method validateReadIndex.

private void validateReadIndex() throws ModbusConfigurationException {
    @Nullable ModbusReadRequestBlueprint readRequest = this.readRequest;
    ValueType readValueType = this.readValueType;
    if (!readIndex.isPresent() || readRequest == null) {
        return;
    }
    assert readValueType != null;
    // bits represented by the value type, e.g. int32 -> 32
    int valueTypeBitCount = readValueType.getBits();
    int dataElementBits;
    switch(readRequest.getFunctionCode()) {
        case READ_INPUT_REGISTERS:
        case READ_MULTIPLE_REGISTERS:
            dataElementBits = 16;
            break;
        case READ_COILS:
        case READ_INPUT_DISCRETES:
            dataElementBits = 1;
            break;
        default:
            throw new IllegalStateException(readRequest.getFunctionCode().toString());
    }
    boolean bitQuery = dataElementBits == 1;
    if (bitQuery && readSubIndex.isPresent()) {
        String errmsg = String.format("readStart=X.Y is not allowed to be used with coils or discrete inputs!");
        throw new ModbusConfigurationException(errmsg);
    }
    if (valueTypeBitCount >= 16 && readSubIndex.isPresent()) {
        String errmsg = String.format("readStart=X.Y notation is not allowed to be used with value types larger than 16bit! Use readStart=X instead.");
        throw new ModbusConfigurationException(errmsg);
    } else if (!bitQuery && valueTypeBitCount < 16 && !readSubIndex.isPresent()) {
        // User has specified value type which is less than register width (16 bits).
        // readStart=X.Y notation must be used to define which data to extract from the 16 bit register.
        String errmsg = String.format("readStart=X.Y must be used with value types (readValueType) less than 16bit!");
        throw new ModbusConfigurationException(errmsg);
    } else if (readSubIndex.isPresent() && (readSubIndex.get() + 1) * valueTypeBitCount > 16) {
        // the sub index Y (in X.Y) is above the register limits
        String errmsg = String.format("readStart=X.Y, the value Y is too large");
        throw new ModbusConfigurationException(errmsg);
    }
    // Determine bit positions polled, both start and end inclusive
    int pollStartBitIndex = readRequest.getReference() * dataElementBits;
    int pollEndBitIndex = pollStartBitIndex + readRequest.getDataLength() * dataElementBits - 1;
    // Determine bit positions read, both start and end inclusive
    int readStartBitIndex = readIndex.get() * dataElementBits + readSubIndex.orElse(0) * valueTypeBitCount;
    int readEndBitIndex = readStartBitIndex + valueTypeBitCount - 1;
    if (readStartBitIndex < pollStartBitIndex || readEndBitIndex > pollEndBitIndex) {
        String errmsg = String.format("Out-of-bounds: Poller is reading from index %d to %d (inclusive) but this thing configured to read '%s' starting from element %d. Exceeds polled data bounds.", pollStartBitIndex / dataElementBits, pollEndBitIndex / dataElementBits, readValueType, readIndex.get());
        throw new ModbusConfigurationException(errmsg);
    }
}
Also used : ValueType(org.openhab.core.io.transport.modbus.ModbusConstants.ValueType) ModbusReadRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint) ModbusConfigurationException(org.openhab.binding.modbus.internal.ModbusConfigurationException) Nullable(org.eclipse.jdt.annotation.Nullable) ModbusWriteCoilRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteCoilRequestBlueprint) ModbusReadRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint) ModbusWriteRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint) ModbusWriteRegisterRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint)

Example 3 with ValueType

use of org.openhab.core.io.transport.modbus.ModbusConstants.ValueType in project openhab-addons by openhab.

the class ModbusDataThingHandler method validateAndParseWriteParameters.

private void validateAndParseWriteParameters(ModbusDataConfiguration config) throws ModbusConfigurationException {
    boolean writeTypeMissing = config.getWriteType() == null || config.getWriteType().isBlank();
    boolean writeStartMissing = config.getWriteStart() == null || config.getWriteStart().isBlank();
    boolean writeValueTypeMissing = config.getWriteValueType() == null || config.getWriteValueType().isBlank();
    boolean writeTransformationMissing = config.getWriteTransform() == null || config.getWriteTransform().isBlank();
    writeTransformation = new CascadedValueTransformationImpl(config.getWriteTransform());
    boolean writingCoil = WRITE_TYPE_COIL.equals(config.getWriteType());
    writeParametersHavingTransformationOnly = (writeTypeMissing && writeStartMissing && writeValueTypeMissing && !writeTransformationMissing);
    // 
    boolean allMissingOrAllPresentOrOnlyNonDefaultTransform = // read-only thing, no write specified
    (writeTypeMissing && writeStartMissing && writeValueTypeMissing) || // mandatory write parameters provided. With coils one can drop value type
    (!writeTypeMissing && !writeStartMissing && (!writeValueTypeMissing || writingCoil)) || // only transformation provided
    writeParametersHavingTransformationOnly;
    if (!allMissingOrAllPresentOrOnlyNonDefaultTransform) {
        String errmsg = String.format("writeType=%s, writeStart=%s, and writeValueType=%s should be all present, or all missing! Alternatively, you can provide just writeTransformation, and use transformation returning JSON.", config.getWriteType(), config.getWriteStart(), config.getWriteValueType());
        throw new ModbusConfigurationException(errmsg);
    } else if (!writeTypeMissing || writeParametersHavingTransformationOnly) {
        isWriteEnabled = true;
        // all write values are present
        if (!writeParametersHavingTransformationOnly && !WRITE_TYPE_HOLDING.equals(config.getWriteType()) && !WRITE_TYPE_COIL.equals(config.getWriteType())) {
            String errmsg = String.format("Invalid writeType=%s. Expecting %s or %s!", config.getWriteType(), WRITE_TYPE_HOLDING, WRITE_TYPE_COIL);
            throw new ModbusConfigurationException(errmsg);
        }
        final ValueType localWriteValueType;
        if (writeParametersHavingTransformationOnly) {
            // Placeholder for further checks
            localWriteValueType = writeValueType = ModbusConstants.ValueType.INT16;
        } else if (writingCoil && writeValueTypeMissing) {
            localWriteValueType = writeValueType = ModbusConstants.ValueType.BIT;
        } else {
            try {
                localWriteValueType = writeValueType = ValueType.fromConfigValue(config.getWriteValueType());
            } catch (IllegalArgumentException e) {
                String errmsg = String.format("Invalid writeValueType=%s!", config.getWriteValueType());
                throw new ModbusConfigurationException(errmsg);
            }
        }
        try {
            if (!writeParametersHavingTransformationOnly) {
                String localWriteStart = config.getWriteStart();
                if (localWriteStart == null) {
                    String errmsg = String.format("Thing %s invalid writeStart: %s", getThing().getUID(), config.getWriteStart());
                    throw new ModbusConfigurationException(errmsg);
                }
                String[] writeParts = localWriteStart.split("\\.", 2);
                try {
                    writeStart = Optional.of(Integer.parseInt(writeParts[0]));
                    if (writeParts.length == 2) {
                        writeSubIndex = Optional.of(Integer.parseInt(writeParts[1]));
                    } else {
                        writeSubIndex = Optional.empty();
                    }
                } catch (IllegalArgumentException e) {
                    String errmsg = String.format("Thing %s invalid writeStart: %s", getThing().getUID(), config.getReadStart());
                    throw new ModbusConfigurationException(errmsg);
                }
            }
        } catch (IllegalArgumentException e) {
            String errmsg = String.format("Thing %s invalid writeStart: %s", getThing().getUID(), config.getWriteStart());
            throw new ModbusConfigurationException(errmsg);
        }
        if (writingCoil && !ModbusConstants.ValueType.BIT.equals(localWriteValueType)) {
            String errmsg = String.format("Invalid writeValueType: Only writeValueType='%s' (or undefined) supported with coils. Value type was: %s", ModbusConstants.ValueType.BIT, config.getWriteValueType());
            throw new ModbusConfigurationException(errmsg);
        } else if (writeSubIndex.isEmpty() && !writingCoil && localWriteValueType.getBits() < 16) {
            // trying to write holding registers with < 16 bit value types. Not supported
            String errmsg = String.format("Invalid writeValueType: Only writeValueType with larger or equal to 16 bits are supported holding registers. Value type was: %s", config.getWriteValueType());
            throw new ModbusConfigurationException(errmsg);
        }
        if (writeSubIndex.isPresent()) {
            if (writeValueTypeMissing || writeTypeMissing || !WRITE_TYPE_HOLDING.equals(config.getWriteType()) || !ModbusConstants.ValueType.BIT.equals(localWriteValueType) || childOfEndpoint) {
                String errmsg = String.format("Thing %s invalid writeType, writeValueType or parent. Since writeStart=X.Y, one should set writeType=holding, writeValueType=bit and have the thing as child of poller", getThing().getUID(), config.getWriteStart());
                throw new ModbusConfigurationException(errmsg);
            }
            ModbusReadRequestBlueprint readRequest = this.readRequest;
            if (readRequest == null || readRequest.getFunctionCode() != ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS) {
                String errmsg = String.format("Thing %s invalid. Since writeStart=X.Y, expecting poller reading holding registers.", getThing().getUID());
                throw new ModbusConfigurationException(errmsg);
            }
        }
        validateWriteIndex();
    } else {
        isWriteEnabled = false;
    }
}
Also used : CascadedValueTransformationImpl(org.openhab.binding.modbus.internal.CascadedValueTransformationImpl) ValueType(org.openhab.core.io.transport.modbus.ModbusConstants.ValueType) ModbusConfigurationException(org.openhab.binding.modbus.internal.ModbusConfigurationException) ModbusReadRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint)

Example 4 with ValueType

use of org.openhab.core.io.transport.modbus.ModbusConstants.ValueType in project openhab-addons by openhab.

the class ModbusDataThingHandler method onRegisters.

private synchronized void onRegisters(ModbusReadRequestBlueprint request, ModbusRegisterArray registers) {
    if (hasConfigurationError()) {
        return;
    } else if (!isReadEnabled) {
        return;
    }
    ValueType readValueType = this.readValueType;
    if (readValueType == null) {
        return;
    }
    State numericState;
    // extractIndex:
    // e.g. with bit, extractIndex=4 means 5th bit (from right) ("10.4" -> 5th bit of register 10, "10.4" -> 5th bit
    // of register 10)
    // bit of second register)
    // e.g. with 8bit integer, extractIndex=3 means high byte of second register
    // 
    // with <16 bit types, this is the index of the N'th 1-bit/8-bit item. Each register has 16/2 items,
    // respectively.
    // with >=16 bit types, this is index of first register
    int extractIndex;
    if (readValueType.getBits() >= 16) {
        // Invariant, checked in initialize
        assert readSubIndex.orElse(0) == 0;
        extractIndex = readIndex.get() - pollStart;
    } else {
        int subIndex = readSubIndex.orElse(0);
        int itemsPerRegister = 16 / readValueType.getBits();
        extractIndex = (readIndex.get() - pollStart) * itemsPerRegister + subIndex;
    }
    numericState = ModbusBitUtilities.extractStateFromRegisters(registers, extractIndex, readValueType).map(state -> (State) state).orElse(UnDefType.UNDEF);
    boolean boolValue = !numericState.equals(DecimalType.ZERO);
    Map<ChannelUID, State> values = processUpdatedValue(numericState, boolValue);
    logger.debug("Thing {} channels updated: {}. readValueType={}, readIndex={}, readSubIndex(or 0)={}, extractIndex={} -> numeric value {} and boolValue={}. Registers {} for request {}", thing.getUID(), values, readValueType, readIndex, readSubIndex.orElse(0), extractIndex, numericState, boolValue, registers, request);
}
Also used : ValueType(org.openhab.core.io.transport.modbus.ModbusConstants.ValueType) State(org.openhab.core.types.State) ChannelUID(org.openhab.core.thing.ChannelUID) ModbusWriteCoilRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteCoilRequestBlueprint) ModbusReadRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint) ModbusWriteRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint) ModbusWriteRegisterRequestBlueprint(org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint)

Aggregations

ValueType (org.openhab.core.io.transport.modbus.ModbusConstants.ValueType)4 ModbusReadRequestBlueprint (org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint)4 ModbusConfigurationException (org.openhab.binding.modbus.internal.ModbusConfigurationException)3 ModbusWriteCoilRequestBlueprint (org.openhab.core.io.transport.modbus.ModbusWriteCoilRequestBlueprint)3 ModbusWriteRegisterRequestBlueprint (org.openhab.core.io.transport.modbus.ModbusWriteRegisterRequestBlueprint)3 ModbusWriteRequestBlueprint (org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint)3 Nullable (org.eclipse.jdt.annotation.Nullable)2 CascadedValueTransformationImpl (org.openhab.binding.modbus.internal.CascadedValueTransformationImpl)2 BigDecimal (java.math.BigDecimal)1 Duration (java.time.Duration)1 LocalDateTime (java.time.LocalDateTime)1 Collection (java.util.Collection)1 Collections (java.util.Collections)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1 Objects (java.util.Objects)1 Optional (java.util.Optional)1 TimeUnit (java.util.concurrent.TimeUnit)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1