[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 <software@whitebear.ch>
This commit is contained in:
Andrew Fiddian-Green 2020-12-28 05:53:53 +00:00 committed by GitHub
parent 6cb9f3a93e
commit aebe1d4a0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 41 additions and 18 deletions

View File

@ -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"] 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 ] Thing modbus:xtender:bridge:xtenderdevice "Xtender" (modbus:serial:modbusbridge) [ slaveAddress=10, refresh=5 ]
``` ```

View File

@ -7,6 +7,7 @@
<thing-type id="bsp"> <thing-type id="bsp">
<supported-bridge-type-refs> <supported-bridge-type-refs>
<bridge-type-ref id="serial"/> <bridge-type-ref id="serial"/>
<bridge-type-ref id="tcp"/>
</supported-bridge-type-refs> </supported-bridge-type-refs>
<label>BSP Studer</label> <label>BSP Studer</label>
<description>Thing for Studer BSP Device</description> <description>Thing for Studer BSP Device</description>

View File

@ -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 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* 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* 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 ## Background Material
@ -109,10 +109,11 @@ When optional parameters are not specified, they default to the values shown in
Basic parameters Basic parameters
| Parameter | Type | Required | Default if omitted | Description | | Parameter | Type | Required | Default if omitted | Description |
| --------- | ------- | -------- | ------------------ | ----------------------------------------------------------- | | ------------ | ------- | -------- | ------------------ | ----------------------------------------------------------- |
| `host` | text | | `"localhost"` | IP Address or hostname | | `host` | text | | `"localhost"` | IP Address or hostname |
| `port` | integer | | `502` | Port number | | `port` | integer | | `502` | Port number |
| `id` | integer | | `1` | Slave id. Also known as station address or unit identifier. | | `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 Advanced parameters

View File

@ -32,6 +32,11 @@ public class ModbusTcpConfiguration {
private int reconnectAfterMillis; private int reconnectAfterMillis;
private int connectTimeoutMillis; private int connectTimeoutMillis;
private boolean enableDiscovery; private boolean enableDiscovery;
private boolean rtuEncoded;
public boolean getRtuEncoded() {
return rtuEncoded;
}
public @Nullable String getHost() { public @Nullable String getHost() {
return host; return host;

View File

@ -51,7 +51,7 @@ public class ModbusTcpThingHandler
} }
this.config = config; this.config = config;
endpoint = new ModbusTCPSlaveEndpoint(host, config.getPort()); endpoint = new ModbusTCPSlaveEndpoint(host, config.getPort(), config.getRtuEncoded());
EndpointPoolConfiguration poolConfiguration = new EndpointPoolConfiguration(); EndpointPoolConfiguration poolConfiguration = new EndpointPoolConfiguration();
this.poolConfiguration = poolConfiguration; this.poolConfiguration = poolConfiguration;

View File

@ -32,6 +32,12 @@
<default>false</default> <default>false</default>
</parameter> </parameter>
<parameter name="rtuEncoded" type="boolean">
<label>RTU Encoding</label>
<description>Use RTU Encoding over IP</description>
<default>false</default>
</parameter>
<!-- connection handling --> <!-- connection handling -->
<parameter name="timeBetweenTransactionsMillis" type="integer" min="0" unit="ms"> <parameter name="timeBetweenTransactionsMillis" type="integer" min="0" unit="ms">
<label>Time Between Transactions</label> <label>Time Between Transactions</label>

View File

@ -20,8 +20,13 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal.*; 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.Map.Entry;
import java.util.Objects;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -151,7 +156,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
} }
private Bridge createTcpMock() { private Bridge createTcpMock() {
ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false);
Bridge tcpBridge = ModbusPollerThingHandlerTest.createTcpThingBuilder("tcp1").build(); Bridge tcpBridge = ModbusPollerThingHandlerTest.createTcpThingBuilder("tcp1").build();
ModbusTcpThingHandler tcpThingHandler = Mockito.mock(ModbusTcpThingHandler.class); ModbusTcpThingHandler tcpThingHandler = Mockito.mock(ModbusTcpThingHandler.class);
tcpBridge.setStatusInfo(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, "")); 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, private void testOutOfBoundsGeneric(int pollStart, int pollLength, String start,
ModbusReadFunctionCode functionCode, ValueType valueType, ThingStatus expectedStatus, ModbusReadFunctionCode functionCode, ValueType valueType, ThingStatus expectedStatus,
BundleContext context) { BundleContext context) {
ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false);
// Minimally mocked request // Minimally mocked request
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class); ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
@ -409,7 +414,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
private ModbusDataThingHandler testReadHandlingGeneric(ModbusReadFunctionCode functionCode, String start, private ModbusDataThingHandler testReadHandlingGeneric(ModbusReadFunctionCode functionCode, String start,
String transform, ValueType valueType, BitArray bits, ModbusRegisterArray registers, Exception error, String transform, ValueType valueType, BitArray bits, ModbusRegisterArray registers, Exception error,
BundleContext context, boolean autoCreateItemsAndLinkToChannels) { BundleContext context, boolean autoCreateItemsAndLinkToChannels) {
ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false);
int pollLength = 3; int pollLength = 3;
@ -461,7 +466,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
private ModbusDataThingHandler testWriteHandlingGeneric(String start, String transform, ValueType valueType, private ModbusDataThingHandler testWriteHandlingGeneric(String start, String transform, ValueType valueType,
String writeType, ModbusWriteFunctionCode successFC, String channel, Command command, Exception error, String writeType, ModbusWriteFunctionCode successFC, String channel, Command command, Exception error,
BundleContext context) { BundleContext context) {
ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false);
// Minimally mocked request // Minimally mocked request
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class); ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
@ -716,7 +721,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
private void testValueTypeGeneric(ModbusReadFunctionCode functionCode, ValueType valueType, private void testValueTypeGeneric(ModbusReadFunctionCode functionCode, ValueType valueType,
ThingStatus expectedStatus) { ThingStatus expectedStatus) {
ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false);
// Minimally mocked request // Minimally mocked request
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class); ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
@ -764,7 +769,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
public void testRefreshOnData() throws InterruptedException { public void testRefreshOnData() throws InterruptedException {
ModbusReadFunctionCode functionCode = ModbusReadFunctionCode.READ_COILS; ModbusReadFunctionCode functionCode = ModbusReadFunctionCode.READ_COILS;
ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false);
int pollLength = 3; int pollLength = 3;
@ -817,7 +822,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
ThingHandler foo = parent.getHandler(); ThingHandler foo = parent.getHandler();
addThing(parent); addThing(parent);
} else { } else {
ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502); ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false);
// Minimally mocked request // Minimally mocked request
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class); ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);

View File

@ -276,7 +276,7 @@ public class ModbusPollerThingHandlerTest extends AbstractModbusOSGiTest {
@SuppressWarnings("null") @SuppressWarnings("null")
private boolean checkEndpoint(ModbusSlaveEndpoint endpointParam) { 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) { private boolean checkRequest(ModbusReadRequestBlueprint request, ModbusReadFunctionCode functionCode) {

View File

@ -70,7 +70,7 @@ public class ModbusTcpThingHandlerTest extends AbstractModbusOSGiTest {
ModbusTcpThingHandler thingHandler = (ModbusTcpThingHandler) thing.getHandler(); ModbusTcpThingHandler thingHandler = (ModbusTcpThingHandler) thing.getHandler();
assertNotNull(thingHandler); assertNotNull(thingHandler);
ModbusSlaveEndpoint slaveEndpoint = thingHandler.getEndpoint(); 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)); assertThat(thingHandler.getSlaveId(), is(9));
InOrder orderedVerify = Mockito.inOrder(mockedModbusManager); InOrder orderedVerify = Mockito.inOrder(mockedModbusManager);