This is an automated email from the ASF dual-hosted git repository. chrisdutz pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit b236ab7d4d6c6c5a977aeb88819277fbfb8dd60a Author: Christofer Dutz <[email protected]> AuthorDate: Fri Aug 14 10:56:47 2026 +0200 fix(plc4j/modbus): Added a safeguard if someone used a non BOOL tag to read coils. --- .../java/modbus/base/optimizer/ModbusReadOptimizer.java | 12 ++++++++++++ .../modbus/base/optimizer/ModbusReadOptimizerTest.java | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusReadOptimizer.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusReadOptimizer.java index 57e880ebb5..9c5cec1065 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusReadOptimizer.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusReadOptimizer.java @@ -33,6 +33,8 @@ import org.apache.plc4x.java.spi.drivers.messages.items.DefaultPlcResponseItem; import org.apache.plc4x.java.spi.drivers.messages.items.PlcResponseItem; import org.apache.plc4x.java.spi.values.PlcBOOL; import org.apache.plc4x.java.spi.values.PlcList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; @@ -45,6 +47,8 @@ import java.util.*; */ public class ModbusReadOptimizer { + private static final Logger LOGGER = LoggerFactory.getLogger(ModbusReadOptimizer.class); + private final int maxCoilsPerRequest; private final int maxRegistersPerRequest; private final ModbusByteOrder defaultByteOrder; @@ -125,6 +129,14 @@ public class ModbusReadOptimizer { // Coils/discrete inputs: bit-level extraction. An array tag (BOOL[n]) occupies // n consecutive coils - the block read already covers all of them (see // optimizeCoils), so every element has to be extracted, not just the first. + if (originalTag.getDataType() != ModbusDataType.BOOL) { + // A coil carries a single bit. Assembling coils into wider types is not + // implemented - report that instead of silently returning the first bit. + LOGGER.warn("Reading coils/discrete inputs as {} is not supported (tag '{}'), only BOOL is.", + originalTag.getDataType(), tagName); + result.put(tagName, new DefaultPlcResponseItem<>(PlcResponseCode.UNSUPPORTED, null)); + continue; + } int firstBitPosition = originalTag.getAddress() - blockTag.getAddress(); int numberOfElements = originalTag.getNumberOfElements(); int lastBytePosition = (firstBitPosition + numberOfElements - 1) / 8; diff --git a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusReadOptimizerTest.java b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusReadOptimizerTest.java index b230c5558d..0020ac10a0 100644 --- a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusReadOptimizerTest.java +++ b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusReadOptimizerTest.java @@ -307,6 +307,20 @@ class ModbusReadOptimizerTest { assertTrue(value.getBoolean()); } + /** + * A coil holds a single bit, so anything other than BOOL has to be reported as unsupported + * rather than silently answered with the first bit. + */ + @Test + void nonBoolCoilIsReportedAsUnsupported() { + Map<String, PlcResponseItem<PlcValue>> response = + splitSingleRead(new ModbusTagCoil(0, 1, ModbusDataType.INT, Collections.emptyMap()), + new byte[]{(byte) 0b00000001, (byte) 0b00000000}); + + assertEquals(PlcResponseCode.UNSUPPORTED, response.get("tag0").getResponseCode()); + assertNull(response.get("tag0").getValue()); + } + /** * A device answering with fewer coils than requested must not blow up the whole response. */
