From eb05ae9b1a060bb83b7e0001ca18479dd02dd5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Garc=C3=ADa?= Date: Sun, 1 Dec 2024 19:34:02 -0400 Subject: [PATCH] Fix read coil and inputregisters. --- .../base/optimizer/ModbusOptimizer.java | 41 +++++++++++++++---- .../plc4x/java/modbus/ManualDriverTest.java | 2 +- .../modbus/ManualModbusTCPDriverTest.java | 33 ++++++++++++--- .../optimizer/LittleEndianByteSwapTest.java | 9 ++-- 4 files changed, 69 insertions(+), 16 deletions(-) diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusOptimizer.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusOptimizer.java index 76daa183867..b93405ea28b 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusOptimizer.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/optimizer/ModbusOptimizer.java @@ -206,7 +206,21 @@ protected PlcReadResponse processReadResponses(PlcReadRequest readRequest, Map

2)) { + byte[] ret = new byte[2]; + ret[1] = 0x00; + ret[0] = (isSet(responseData, modbusTag.getLogicalAddress()) ?(byte) 0x01 : 0x00); + readBuffer = getReadBuffer(ret, ModbusByteOrder.BIG_ENDIAN); + } else { + readBuffer = getReadBuffer(responseData, ModbusByteOrder.BIG_ENDIAN); + } + } else { + readBuffer = getReadBuffer(responseData, modbusContext.getByteOrder()); + } + try { PlcValue plcValue = DataItem.staticParse(readBuffer, modbusTag.getDataType(), modbusTag.getNumberOfElements(), @@ -383,13 +397,19 @@ public byte[] getResponseData() { public byte[] getResponseDataForTag(ModbusTag modbusTag) { byte[] itemData = new byte[modbusTag.getLengthBytes()]; - int value = 0; + int value = 0; switch(modbusTag.getDataType()) { case BOOL: { itemData = new byte[responseData.length]; - for (int i= 0; i < responseData.length; i++){ - itemData[i] = byteReverse(responseData[i]); - } + if ((modbusTag instanceof ModbusTagCoil) || (modbusTag instanceof ModbusTagDiscreteInput)) { + for (int i= 0; i < responseData.length; i++){ + itemData[i] = byteReverse(responseData[i]); + } + } else { + for (int i= 0; i < responseData.length; i++){ + itemData[i] = responseData[i]; + } + } } break; default: @@ -410,10 +430,17 @@ public static byte byteReverse(byte x) { } return b; } - - + } + + public boolean isSet(byte[] arr, int bit) { + int index = bit / 8; + int bitPosition = 8 - bit % 8; + return (arr[index] >> bitPosition & 1) == 1; + } + + protected interface TagFactory { PlcTag createTag(int address, int count, ModbusDataType dataType); } diff --git a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualDriverTest.java b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualDriverTest.java index 297adf5eb3b..2ab15cf87a7 100644 --- a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualDriverTest.java +++ b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualDriverTest.java @@ -35,7 +35,7 @@ public class ManualDriverTest { */ public static void main(String[] args) throws Exception { //final PlcConnection connection = new DefaultPlcDriverManager().getConnection("modbus-tcp://10.211.55.3?default-payload-byte-order=BIG_ENDIAN"); - final PlcConnection connection = new DefaultPlcDriverManager().getConnection("modbus-tcp://10.211.55.3?default-payload-byte-order=LITTLE_ENDIAN"); + final PlcConnection connection = new DefaultPlcDriverManager().getConnection("modbus-tcp://10.10.1.200:10502?default-payload-byte-order=LITTLE_ENDIAN"); //final PlcConnection connection = new DefaultPlcDriverManager().getConnection("modbus-tcp://10.211.55.3?default-payload-byte-order=BIG_ENDIAN_BYTE_SWAP"); //final PlcConnection connection = new DefaultPlcDriverManager().getConnection("modbus-tcp://10.211.55.3?default-payload-byte-order=LITTLE_ENDIAN_BYTE_SWAP"); final PlcReadRequest readRequest = connection.readRequestBuilder() diff --git a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualModbusTCPDriverTest.java b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualModbusTCPDriverTest.java index 46f3b02d100..db1c5ee7db7 100644 --- a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualModbusTCPDriverTest.java +++ b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualModbusTCPDriverTest.java @@ -30,6 +30,29 @@ public class ManualModbusTCPDriverTest extends ManualTest { * * Located in "main" * + * Reference server/client: ModbusTools + * https://github.com/serhmarch/ModbusTools/releases/tag/v0.3.8 + * + * If you report any improvement points on the Modbus driver, + * please run the tests on the reference device indicated above. + * One of the virtues and weaknesses of the Modbus protocol is + * that it gives creative freedom to manufacturers for the implementation + * of Scalar types, so the indicated device is the reference. + * + * Modbus Application Protocol Specification, V1.1.b + * Section 4.2 Data Encoding + * MODBUS uses a ‘big-Endian’ representation for addresses and data items. + * This means that when a numerical quantity larger than a single byte is + * transmitted, the most significant byte is sent first. So for example + * + * Register size value + * 16 - bits 0x1234 the first byte sent is 0x12 then 0x34 + * + * + * https://www.h-schmidt.net/FloatConverter/IEEE754.html + * 123456.00 -> 47 f1 20 00 + * Modbus -> 20 00 47 f1 + hurz_BOOL := TRUE; hurz_BYTE := 42; @@ -69,22 +92,22 @@ public ManualModbusTCPDriverTest(String connectionString) { } public static void main(String[] args) throws Exception { - ManualModbusTCPDriverTest test = new ManualModbusTCPDriverTest("modbus-tcp://192.168.23.30"); + ManualModbusTCPDriverTest test = new ManualModbusTCPDriverTest("modbus-tcp://10.10.1.200:10502?default-payload-byte-order=LITTLE_ENDIAN_BYTE_SWAP"); test.addTestCase("holding-register:1:BOOL", new PlcBOOL(true)); // 0001 test.addTestCase("holding-register:2:BYTE", new PlcBYTE(42)); // 2A //test.addTestCase("holding-register:3:WORD", new PlcWORD(42424)); // A5B8 - test.addTestCase("holding-register:4:DWORD", new PlcDWORD(4242442424L)); // FCDE 88B8 + test.addTestCase("holding-register:4:DWORD", new PlcDWORD(424244242L)); // 1949 7412 // test.addTestCase("holding-register:6:LWORD", new PlcLWORD(4242442424242424242L)); // FCDE 88B8 FCDE 88B8 test.addTestCase("holding-register:10:SINT", new PlcSINT(-42)); // D6 test.addTestCase("holding-register:11:USINT", new PlcUSINT(42)); // 2A test.addTestCase("holding-register:12:INT", new PlcINT(-2424)); // F688 test.addTestCase("holding-register:13:UINT", new PlcUINT(42424)); // A5B8 - test.addTestCase("holding-register:14:DINT", new PlcDINT(-242442424)); // F18C 9F48 - test.addTestCase("holding-register:16:UDINT", new PlcUDINT(4242442424L));// FCDE 88B8 + test.addTestCase("holding-register:14:DINT", new PlcDINT(-242442424)); // 1949 7412 + test.addTestCase("holding-register:16:UDINT", new PlcUDINT(424244242L));// FCDE 88B8 test.addTestCase("holding-register:18:LINT", new PlcLINT(-4242442424242424242L));// C51F D117 B2FB B64E test.addTestCase("holding-register:22:ULINT", new PlcULINT(4242442424242424242L));// 3AE0 2EE8 4D04 49B2 test.addTestCase("holding-register:26:REAL", new PlcREAL(3.141593F));// 4049 0FDC - test.addTestCase("holding-register:28:LREAL", new PlcLREAL(2.71828182846D)); // 4005 BF0A 8B14 5FCF + test.addTestCase("holding-register:28:LREAL", new PlcLREAL(2.71)); // 4005 BF0A 8B14 5FCF //test.addTestCase("holding-register:32:TIME", "PT1.234S"); // 04D2 //test.addTestCase("holding-register::LTIME", "PT24015H23M12.034002044S"); //test.addTestCase("holding-register::DATE", "1998-03-28"); diff --git a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/base/optimizer/LittleEndianByteSwapTest.java b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/base/optimizer/LittleEndianByteSwapTest.java index cad86516331..37f6c5fb166 100644 --- a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/base/optimizer/LittleEndianByteSwapTest.java +++ b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/base/optimizer/LittleEndianByteSwapTest.java @@ -98,6 +98,7 @@ public void testLittleEndianByteSwap() throws Exception { input.put("variable44", new String[]{"holding-register:177:REAL", "0.3768"}); input.put("variable45", new String[]{"holding-register:181:REAL", "0.0"}); input.put("variable46", new String[]{"holding-register:185:REAL", "-0.01143"}); + input.put("variable47", new String[]{"coil:1:BOOL", "false"}); input.put("variable48", new String[]{"coil:3:BOOL", "false"}); input.put("variable49", new String[]{"coil:5:BOOL", "true"}); @@ -183,9 +184,9 @@ public void testLittleEndianByteSwap() throws Exception { input.put("variable129", new String[]{"coil:165:BOOL", "false"}); input.put("variable130", new String[]{"coil:167:BOOL", "false"}); input.put("variable131", new String[]{"coil:169:BOOL", "false"}); - input.put("variable132", new String[]{"coil:171:BOOL", "false"}); + input.put("variable132", new String[]{"coil:171:BOOL", "true"}); input.put("variable133", new String[]{"coil:173:BOOL", "false"}); - input.put("variable134", new String[]{"coil:175:BOOL", "false"}); + input.put("variable134", new String[]{"coil:175:BOOL", "true"}); PlcReader mockPlcReader = Mockito.mock(PlcReader.class); PlcTagHandler modbusTagHandler = new ModbusTagHandler(); PlcReadRequest.Builder builder = new DefaultPlcReadRequest.Builder(mockPlcReader, modbusTagHandler); @@ -248,7 +249,8 @@ public void testLittleEndianByteSwap() throws Exception { Map> readResponses = new HashMap<>(); readResponses.put(firstRequest, new BaseOptimizer.SubResponse<>( new DefaultPlcReadResponse(firstRequest, Map.of( - "coils0", new DefaultPlcResponseItem<>(PlcResponseCode.OK, new PlcRawByteArray(Hex.decodeHex("3060480c00c084000000000000000000000000000000"))))))); + //"coils0", new DefaultPlcResponseItem<>(PlcResponseCode.OK, new PlcRawByteArray(Hex.decodeHex("3060480c00c084000000000000000000000000000000"))))))); + "coils0", new DefaultPlcResponseItem<>(PlcResponseCode.OK, new PlcRawByteArray(Hex.decodeHex("10404004004004000000000000000000000000000044"))))))); readResponses.put(secondRequest, new BaseOptimizer.SubResponse<>( new DefaultPlcReadResponse(secondRequest, Map.of( "registers0", new DefaultPlcResponseItem<>(PlcResponseCode.OK, new PlcRawByteArray(Hex.decodeHex("134141b000000000f80146c3000000003852c31f000000000b7841ae00000000fc0046e000000000028fc31f00000000c50441ab00000000540046dd00000000c000c31e000000006e973f32000000009998420300000000a5e33c9b00000000ccd041fc00000000020c3f2f00000000c3f9409e0000000047ae3f39000000009fbe3f3a00000000fbe83ff900000000d70a3aa30000000033333e33000000008f5c412400000000b83143d4000000005c293f5700000000c28f3f0500000000ac093f3c00000000fbe7413a0000000000000000000000000000000000000000e0df3f3b0000000023a33f3900000000a3d74220"))))))); @@ -270,6 +272,7 @@ public void testLittleEndianByteSwap() throws Exception { for (String name : input.keySet()) { String[] data = input.get(name); String readValue = readResponse.getString(name); + System.out.println(name + " : " + data[1] + " : " + readValue ); Assertions.assertEquals(data[1], readValue); } }