From aebe1d4a0bd574431e3b21730c03787109e2a2fe Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 28 Dec 2020 05:53:53 +0000 Subject: [PATCH] [modbus] Added support for RTU encoding over TCP (#9435) * [modbus] add support for rtu encoded over tcp * [modbus] move classes to openhab/jamod * [modbus] spotless * [modbus] revert EndpointPoolConfiguration * [modbus] remove virtual serial * [modbus] resolve dependencies * [modbus.studer] add support for RTU over TCP bridge Signed-off-by: Andrew Fiddian-Green --- .../README.md | 5 +++++ .../resources/OH-INF/thing/thing-types.xml | 1 + bundles/org.openhab.binding.modbus/README.md | 15 ++++++------- .../config/ModbusTcpConfiguration.java | 5 +++++ .../handler/ModbusTcpThingHandler.java | 2 +- .../resources/OH-INF/thing/bridge-tcp.xml | 6 ++++++ .../modbus/tests/ModbusDataHandlerTest.java | 21 ++++++++++++------- .../tests/ModbusPollerThingHandlerTest.java | 2 +- .../tests/ModbusTcpThingHandlerTest.java | 2 +- 9 files changed, 41 insertions(+), 18 deletions(-) diff --git a/bundles/org.openhab.binding.modbus.studer/README.md b/bundles/org.openhab.binding.modbus.studer/README.md index 4520f35f4..f03eca6bd 100644 --- a/bundles/org.openhab.binding.modbus.studer/README.md +++ b/bundles/org.openhab.binding.modbus.studer/README.md @@ -114,6 +114,11 @@ All channels read for a VarioString device ``` Bridge modbus:serial:bridge [port="/dev/ttyUSB0",baud=9600,dataBits=8,parity="even",stopBits="1.0",encoding="rtu"] +..or.. +Bridge modbus:tcp:bridge [host="192.168.178.56", port=502, rtuEncoded=true] + +... + Thing modbus:xtender:bridge:xtenderdevice "Xtender" (modbus:serial:modbusbridge) [ slaveAddress=10, refresh=5 ] ``` diff --git a/bundles/org.openhab.binding.modbus.studer/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus.studer/src/main/resources/OH-INF/thing/thing-types.xml index 8c0d9ea38..b690db675 100644 --- a/bundles/org.openhab.binding.modbus.studer/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.modbus.studer/src/main/resources/OH-INF/thing/thing-types.xml @@ -7,6 +7,7 @@ + Thing for Studer BSP Device diff --git a/bundles/org.openhab.binding.modbus/README.md b/bundles/org.openhab.binding.modbus/README.md index bec9f8b36..2471568ac 100644 --- a/bundles/org.openhab.binding.modbus/README.md +++ b/bundles/org.openhab.binding.modbus/README.md @@ -37,8 +37,8 @@ The binding can also *write* data to Modbus slaves using FC05 (Write single coil Please note the following caveats or limitations -* the binding does *not* act as Modbus slave (e.g. as Modbus TCP server). -* the binding does *not* support Modbus RTU over Modbus TCP, also known as "Modbus over TCP/IP" or "Modbus over TCP" or "Modbus RTU/IP", although normal "Modbus TCP" is supported. However, there is a workaround: you can use a Virtual Serial Port Server, to emulate a COM Port and Bind it with openHAB using Modbus Serial. +* The binding does *not* act as Modbus slave (e.g. as Modbus TCP server). +* The binding *does* support Modbus RTU over Modbus TCP, (also known as "Modbus over TCP/IP" or "Modbus over TCP" or "Modbus RTU/IP"), as well as normal "Modbus TCP". ## Background Material @@ -108,11 +108,12 @@ When optional parameters are not specified, they default to the values shown in Basic parameters -| Parameter | Type | Required | Default if omitted | Description | -| --------- | ------- | -------- | ------------------ | ----------------------------------------------------------- | -| `host` | text | | `"localhost"` | IP Address or hostname | -| `port` | integer | | `502` | Port number | -| `id` | integer | | `1` | Slave id. Also known as station address or unit identifier. | +| Parameter | Type | Required | Default if omitted | Description | +| ------------ | ------- | -------- | ------------------ | ----------------------------------------------------------- | +| `host` | text | | `"localhost"` | IP Address or hostname | +| `port` | integer | | `502` | Port number | +| `id` | integer | | `1` | Slave id. Also known as station address or unit identifier. | +| `rtuEncoded` | boolean | | `false` | Use RTU encoding instead of regular TCP encoding. | Advanced parameters diff --git a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java index b4314f45e..832e54187 100644 --- a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java +++ b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java @@ -32,6 +32,11 @@ public class ModbusTcpConfiguration { private int reconnectAfterMillis; private int connectTimeoutMillis; private boolean enableDiscovery; + private boolean rtuEncoded; + + public boolean getRtuEncoded() { + return rtuEncoded; + } public @Nullable String getHost() { return host; diff --git a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusTcpThingHandler.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusTcpThingHandler.java index 87fbc4864..8d90b7666 100644 --- a/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusTcpThingHandler.java +++ b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusTcpThingHandler.java @@ -51,7 +51,7 @@ public class ModbusTcpThingHandler } this.config = config; - endpoint = new ModbusTCPSlaveEndpoint(host, config.getPort()); + endpoint = new ModbusTCPSlaveEndpoint(host, config.getPort(), config.getRtuEncoded()); EndpointPoolConfiguration poolConfiguration = new EndpointPoolConfiguration(); this.poolConfiguration = poolConfiguration; diff --git a/bundles/org.openhab.binding.modbus/src/main/resources/OH-INF/thing/bridge-tcp.xml b/bundles/org.openhab.binding.modbus/src/main/resources/OH-INF/thing/bridge-tcp.xml index b1e48f945..3d4006748 100644 --- a/bundles/org.openhab.binding.modbus/src/main/resources/OH-INF/thing/bridge-tcp.xml +++ b/bundles/org.openhab.binding.modbus/src/main/resources/OH-INF/thing/bridge-tcp.xml @@ -32,6 +32,12 @@ false + + + Use RTU Encoding over IP + false + + diff --git a/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusDataHandlerTest.java b/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusDataHandlerTest.java index 77fa7c3da..16c11ee64 100644 --- a/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusDataHandlerTest.java +++ b/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusDataHandlerTest.java @@ -20,8 +20,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.concurrent.ScheduledFuture; import java.util.function.Consumer; import java.util.function.Function; @@ -151,7 +156,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest { } private Bridge createTcpMock() { - ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); + ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false); Bridge tcpBridge = ModbusPollerThingHandlerTest.createTcpThingBuilder("tcp1").build(); ModbusTcpThingHandler tcpThingHandler = Mockito.mock(ModbusTcpThingHandler.class); tcpBridge.setStatusInfo(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, "")); @@ -257,7 +262,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest { private void testOutOfBoundsGeneric(int pollStart, int pollLength, String start, ModbusReadFunctionCode functionCode, ValueType valueType, ThingStatus expectedStatus, BundleContext context) { - ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); + ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false); // Minimally mocked request ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class); @@ -409,7 +414,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest { private ModbusDataThingHandler testReadHandlingGeneric(ModbusReadFunctionCode functionCode, String start, String transform, ValueType valueType, BitArray bits, ModbusRegisterArray registers, Exception error, BundleContext context, boolean autoCreateItemsAndLinkToChannels) { - ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); + ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false); int pollLength = 3; @@ -461,7 +466,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest { private ModbusDataThingHandler testWriteHandlingGeneric(String start, String transform, ValueType valueType, String writeType, ModbusWriteFunctionCode successFC, String channel, Command command, Exception error, BundleContext context) { - ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); + ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false); // Minimally mocked request ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class); @@ -716,7 +721,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest { private void testValueTypeGeneric(ModbusReadFunctionCode functionCode, ValueType valueType, ThingStatus expectedStatus) { - ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); + ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false); // Minimally mocked request ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class); @@ -764,7 +769,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest { public void testRefreshOnData() throws InterruptedException { ModbusReadFunctionCode functionCode = ModbusReadFunctionCode.READ_COILS; - ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); + ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false); int pollLength = 3; @@ -817,7 +822,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest { ThingHandler foo = parent.getHandler(); addThing(parent); } else { - ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); + ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false); // Minimally mocked request ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class); diff --git a/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusPollerThingHandlerTest.java b/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusPollerThingHandlerTest.java index 26693da10..e09d07158 100644 --- a/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusPollerThingHandlerTest.java +++ b/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusPollerThingHandlerTest.java @@ -276,7 +276,7 @@ public class ModbusPollerThingHandlerTest extends AbstractModbusOSGiTest { @SuppressWarnings("null") private boolean checkEndpoint(ModbusSlaveEndpoint endpointParam) { - return endpointParam.equals(new ModbusTCPSlaveEndpoint(HOST, PORT)); + return endpointParam.equals(new ModbusTCPSlaveEndpoint(HOST, PORT, false)); } private boolean checkRequest(ModbusReadRequestBlueprint request, ModbusReadFunctionCode functionCode) { diff --git a/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusTcpThingHandlerTest.java b/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusTcpThingHandlerTest.java index 5e18fe8e0..4e7769e11 100644 --- a/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusTcpThingHandlerTest.java +++ b/itests/org.openhab.binding.modbus.tests/src/main/java/org/openhab/binding/modbus/tests/ModbusTcpThingHandlerTest.java @@ -70,7 +70,7 @@ public class ModbusTcpThingHandlerTest extends AbstractModbusOSGiTest { ModbusTcpThingHandler thingHandler = (ModbusTcpThingHandler) thing.getHandler(); assertNotNull(thingHandler); ModbusSlaveEndpoint slaveEndpoint = thingHandler.getEndpoint(); - assertThat(slaveEndpoint, is(equalTo(new ModbusTCPSlaveEndpoint("thisishost", 44)))); + assertThat(slaveEndpoint, is(equalTo(new ModbusTCPSlaveEndpoint("thisishost", 44, false)))); assertThat(thingHandler.getSlaveId(), is(9)); InOrder orderedVerify = Mockito.inOrder(mockedModbusManager);