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.
      */

Reply via email to