This is an automated email from the ASF dual-hosted git repository. sruehl pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git
commit f74c7bf69efd515dd948d6a8c48388cb80a0c618 Author: Sebastian Rühl <sru...@apache.org> AuthorDate: Wed Sep 26 12:19:29 2018 +0200 [Modbus] added possibility to add a quantity to addresses to read multiple values. --- .../plc4x/java/modbus/model/CoilModbusField.java | 9 ++++--- .../modbus/model/MaskWriteRegisterModbusField.java | 9 ++++--- .../plc4x/java/modbus/model/ModbusField.java | 16 +++++++++--- .../model/ReadDiscreteInputsModbusField.java | 9 ++++--- .../model/ReadHoldingRegistersModbusField.java | 9 ++++--- .../model/ReadInputRegistersModbusField.java | 9 ++++--- .../java/modbus/model/RegisterModbusField.java | 9 ++++--- .../java/modbus/netty/Plc4XModbusProtocol.java | 29 ++++++++++++++-------- .../plc4x/java/modbus/ManualPlc4XModbusTest.java | 13 +++++----- 9 files changed, 73 insertions(+), 39 deletions(-) diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilModbusField.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilModbusField.java index 731e699..adb7600 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilModbusField.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/CoilModbusField.java @@ -27,8 +27,8 @@ public class CoilModbusField extends ModbusField { public static final Pattern ADDRESS_PATTERN = Pattern.compile("coil:" + ModbusField.ADDRESS_PATTERN); - protected CoilModbusField(int address) { - super(address); + public CoilModbusField(int address, Integer quantity) { + super(address, quantity); } public static CoilModbusField of(String addressString) throws PlcInvalidFieldException { @@ -37,6 +37,9 @@ public class CoilModbusField extends ModbusField { throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN); } int address = Integer.parseInt(matcher.group("address")); - return new CoilModbusField(address); + + String quantityString = matcher.group("quantity"); + Integer quantity = quantityString != null ? Integer.valueOf(quantityString) : null; + return new CoilModbusField(address, quantity); } } diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/MaskWriteRegisterModbusField.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/MaskWriteRegisterModbusField.java index 4f33f8b..2d4870d 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/MaskWriteRegisterModbusField.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/MaskWriteRegisterModbusField.java @@ -31,8 +31,8 @@ public class MaskWriteRegisterModbusField extends ModbusField { private final int andMask; private final int orMask; - protected MaskWriteRegisterModbusField(int address, int andMask, int orMask) { - super(address); + protected MaskWriteRegisterModbusField(int address, int andMask, int orMask, Integer quantity) { + super(address, quantity); this.andMask = andMask; this.orMask = orMask; } @@ -45,7 +45,10 @@ public class MaskWriteRegisterModbusField extends ModbusField { int address = Integer.parseInt(matcher.group("address")); int andMask = Integer.parseInt(matcher.group("andMask")); int orMask = Integer.parseInt(matcher.group("orMask")); - return new MaskWriteRegisterModbusField(address, andMask, orMask); + + String quantityString = matcher.group("quantity"); + Integer quantity = quantityString != null ? Integer.valueOf(quantityString) : null; + return new MaskWriteRegisterModbusField(address, andMask, orMask, quantity); } public int getAndMask() { diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ModbusField.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ModbusField.java index b3ad29a..3254e02 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ModbusField.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ModbusField.java @@ -25,18 +25,28 @@ import java.util.regex.Pattern; public abstract class ModbusField implements PlcField { - public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?<address>\\d+)"); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?<address>\\d+)(\\[(?<quantity>\\d)])?"); private final int address; - protected ModbusField(int address) { + private final int quantity; + + protected ModbusField(int address, Integer quantity) { this.address = address; + this.quantity = quantity != null ? quantity : 1; + if (this.quantity <= 0) { + throw new IllegalArgumentException("quantity must be greater then zero. Was " + this.quantity); + } } public int getAddress() { return address; } + public int getQuantity() { + return quantity; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -51,7 +61,6 @@ public abstract class ModbusField implements PlcField { @Override public int hashCode() { - return Objects.hash(address); } @@ -59,6 +68,7 @@ public abstract class ModbusField implements PlcField { public String toString() { return "ModbusField{" + "address=" + address + + "quantity=" + quantity + '}'; } } diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadDiscreteInputsModbusField.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadDiscreteInputsModbusField.java index 1c518d2..00cb495 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadDiscreteInputsModbusField.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadDiscreteInputsModbusField.java @@ -27,8 +27,8 @@ public class ReadDiscreteInputsModbusField extends ModbusField { public static final Pattern ADDRESS_PATTERN = Pattern.compile("readdiscreteinputs:" + ModbusField.ADDRESS_PATTERN); - protected ReadDiscreteInputsModbusField(int address) { - super(address); + public ReadDiscreteInputsModbusField(int address, Integer quantity) { + super(address, quantity); } public static ReadDiscreteInputsModbusField of(String addressString) throws PlcInvalidFieldException { @@ -37,6 +37,9 @@ public class ReadDiscreteInputsModbusField extends ModbusField { throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN); } int address = Integer.parseInt(matcher.group("address")); - return new ReadDiscreteInputsModbusField(address); + + String quantityString = matcher.group("quantity"); + Integer quantity = quantityString != null ? Integer.valueOf(quantityString) : null; + return new ReadDiscreteInputsModbusField(address, quantity); } } diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadHoldingRegistersModbusField.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadHoldingRegistersModbusField.java index e00a510..9f51e1e 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadHoldingRegistersModbusField.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadHoldingRegistersModbusField.java @@ -27,8 +27,8 @@ public class ReadHoldingRegistersModbusField extends ModbusField { public static final Pattern ADDRESS_PATTERN = Pattern.compile("readholdingregisters:" + ModbusField.ADDRESS_PATTERN); - protected ReadHoldingRegistersModbusField(int address) { - super(address); + protected ReadHoldingRegistersModbusField(int address, Integer quantity) { + super(address, quantity); } public static ReadHoldingRegistersModbusField of(String addressString) throws PlcInvalidFieldException { @@ -37,6 +37,9 @@ public class ReadHoldingRegistersModbusField extends ModbusField { throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN); } int address = Integer.parseInt(matcher.group("address")); - return new ReadHoldingRegistersModbusField(address); + + String quantityString = matcher.group("quantity"); + Integer quantity = quantityString != null ? Integer.valueOf(quantityString) : null; + return new ReadHoldingRegistersModbusField(address, quantity); } } diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadInputRegistersModbusField.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadInputRegistersModbusField.java index c42e3ae..3b61343 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadInputRegistersModbusField.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/ReadInputRegistersModbusField.java @@ -27,8 +27,8 @@ public class ReadInputRegistersModbusField extends ModbusField { public static final Pattern ADDRESS_PATTERN = Pattern.compile("readinputregisters:" + ModbusField.ADDRESS_PATTERN); - protected ReadInputRegistersModbusField(int address) { - super(address); + protected ReadInputRegistersModbusField(int address, Integer quantity) { + super(address, quantity); } public static ReadInputRegistersModbusField of(String addressString) throws PlcInvalidFieldException { @@ -37,6 +37,9 @@ public class ReadInputRegistersModbusField extends ModbusField { throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN); } int address = Integer.parseInt(matcher.group("address")); - return new ReadInputRegistersModbusField(address); + + String quantityString = matcher.group("quantity"); + Integer quantity = quantityString != null ? Integer.valueOf(quantityString) : null; + return new ReadInputRegistersModbusField(address, quantity); } } diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/RegisterModbusField.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/RegisterModbusField.java index 8b5db79..0697364 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/RegisterModbusField.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/model/RegisterModbusField.java @@ -28,8 +28,8 @@ public class RegisterModbusField extends ModbusField { public static final Pattern ADDRESS_PATTERN = Pattern.compile("register:" + ModbusField.ADDRESS_PATTERN); - protected RegisterModbusField(int address) { - super(address); + protected RegisterModbusField(int address, Integer quantity) { + super(address, quantity); } public static RegisterModbusField of(String addressString) throws PlcInvalidFieldException { @@ -38,6 +38,9 @@ public class RegisterModbusField extends ModbusField { throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN); } int address = Integer.parseInt(matcher.group("address")); - return new RegisterModbusField(address); + + String quantityString = matcher.group("quantity"); + Integer quantity = quantityString != null ? Integer.valueOf(quantityString) : null; + return new RegisterModbusField(address, quantity); } } diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java index f1d1313..774368a 100644 --- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java +++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java @@ -84,6 +84,11 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, // TODO: for higher data types float, double etc we might need to split the bytes into chunks String fieldName = request.getFieldNames().iterator().next(); int quantity = request.getNumberOfValues(fieldName); + ModbusField field = (ModbusField) request.getField(fieldName); + if (quantity != field.getQuantity()) { + LOGGER.warn("Supplied number of values [{}] don't match t the addressed quantity of [{}]", field.getQuantity(), quantity); + } + short unitId = 0; /* @@ -105,7 +110,6 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, * when addressing them. So if reading 2 32bit integers, this is split up into four registers. So for the second * int we have to increment the address accordingly. */ - ModbusField field = (ModbusField) request.getField(fieldName); ModbusPdu modbusRequest; if (field instanceof RegisterModbusField) { RegisterModbusField registerModbusField = (RegisterModbusField) field; @@ -171,12 +175,12 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, // TODO: check if we can map like this. Implication is that we can only work with int, short, byte and boolean // TODO: for higher data types float, double etc we might need to split the bytes into chunks String fieldName = request.getFieldNames().iterator().next(); - // TODO: The quantity is encoded in the field attribute - int quantity = 1; + + ModbusField field = (ModbusField) request.getField(fieldName); + int quantity = field.getQuantity(); // TODO: the unit the should be used for multiple Requests short unitId = 0; - ModbusField field = (ModbusField) request.getField(fieldName); ModbusPdu modbusRequest; if (field instanceof CoilModbusField) { CoilModbusField coilModbusField = (CoilModbusField) field; @@ -258,7 +262,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, ReadCoilsResponse readCoilsResponse = (ReadCoilsResponse) modbusPdu; LOGGER.debug("{}: Nothing", readCoilsResponse); ByteBuf byteBuf = readCoilsResponse.getCoilStatus(); - DefaultBooleanFieldItem data = produceCoilValueList(byteBuf); + DefaultBooleanFieldItem data = produceCoilValueList(byteBuf, field.getQuantity()); Map<String, Pair<PlcResponseCode, FieldItem>> responseValues = new HashMap<>(); responseValues.put(fieldName, new ImmutablePair<>(PlcResponseCode.OK, data)); plcRequestContainer.getResponseFuture().complete(new DefaultPlcReadResponse((InternalPlcReadRequest) request, responseValues)); @@ -267,7 +271,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, ReadDiscreteInputsResponse readDiscreteInputsResponse = (ReadDiscreteInputsResponse) modbusPdu; LOGGER.debug("{}: Nothing", readDiscreteInputsResponse); ByteBuf byteBuf = readDiscreteInputsResponse.getInputStatus(); - DefaultBooleanFieldItem data = produceCoilValueList(byteBuf); + DefaultBooleanFieldItem data = produceCoilValueList(byteBuf, field.getQuantity()); Map<String, Pair<PlcResponseCode, FieldItem>> responseValues = new HashMap<>(); responseValues.put(fieldName, new ImmutablePair<>(PlcResponseCode.OK, data)); plcRequestContainer.getResponseFuture().complete(new DefaultPlcReadResponse((InternalPlcReadRequest) request, responseValues)); @@ -277,7 +281,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, LOGGER.debug("{}: Nothing", readHoldingRegistersResponse); ByteBuf byteBuf = readHoldingRegistersResponse.getRegisters(); // TODO: use register method - DefaultByteArrayFieldItem data = produceRegisterValueList(byteBuf); + DefaultByteArrayFieldItem data = produceRegisterValueList(byteBuf, field.getQuantity()); Map<String, Pair<PlcResponseCode, FieldItem>> responseValues = new HashMap<>(); responseValues.put(fieldName, new ImmutablePair<>(PlcResponseCode.OK, data)); plcRequestContainer.getResponseFuture().complete(new DefaultPlcReadResponse((InternalPlcReadRequest) request, responseValues)); @@ -287,7 +291,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, LOGGER.debug("{}: Nothing", readInputRegistersResponse); ByteBuf byteBuf = readInputRegistersResponse.getRegisters(); // TODO: use register method - DefaultByteArrayFieldItem data = produceRegisterValueList(byteBuf); + DefaultByteArrayFieldItem data = produceRegisterValueList(byteBuf, field.getQuantity()); Map<String, Pair<PlcResponseCode, FieldItem>> responseValues = new HashMap<>(); responseValues.put(fieldName, new ImmutablePair<>(PlcResponseCode.OK, data)); plcRequestContainer.getResponseFuture().complete(new DefaultPlcReadResponse((InternalPlcReadRequest) request, responseValues)); @@ -478,7 +482,10 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, //////////////////////////////////////////////////////////////////////////////// // Decoding helpers. //////////////////////////////////////////////////////////////////////////////// - private DefaultBooleanFieldItem produceCoilValueList(ByteBuf byteBuf) { + private DefaultBooleanFieldItem produceCoilValueList(ByteBuf byteBuf, int expectedQuantity) { + if (byteBuf.readableBytes() < expectedQuantity / 8) { + LOGGER.warn("Expected to read {} coils but only max of {} can be supplied", expectedQuantity, byteBuf.readableBytes() * 8); + } byte[] bytes = new byte[byteBuf.readableBytes()]; if (bytes.length < 1) { return new DefaultBooleanFieldItem(); @@ -487,7 +494,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, List<Boolean> data = new LinkedList<>(); int bitIndex = 0; int coilIndex = 0; - while (coilIndex < bytes.length) { + while (coilIndex < bytes.length && data.size() < expectedQuantity) { if (bitIndex > 7) { // Every 8 Coils we need to increase the access coilIndex++; @@ -503,7 +510,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload, return new DefaultBooleanFieldItem(data.toArray(new Boolean[0])); } - private DefaultByteArrayFieldItem produceRegisterValueList(ByteBuf byteBuf) throws PlcProtocolException { + private DefaultByteArrayFieldItem produceRegisterValueList(ByteBuf byteBuf, int expectedQuantity) throws PlcProtocolException { int readableBytes = byteBuf.readableBytes(); if (readableBytes % 2 != 0) { throw new PlcProtocolException("Readables bytes should even: " + readableBytes); diff --git a/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualPlc4XModbusTest.java b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualPlc4XModbusTest.java index 70ca7a2..6c2fab3 100644 --- a/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualPlc4XModbusTest.java +++ b/plc4j/protocols/modbus/src/test/java/org/apache/plc4x/java/modbus/ManualPlc4XModbusTest.java @@ -43,29 +43,28 @@ public class ManualPlc4XModbusTest { { PlcReader reader = plcConnection.getReader().orElseThrow(() -> new RuntimeException("No Reader found")); - PlcReadResponse<?> readResponse = reader.read(builder -> builder.addItem("randomRegister", "register:7")).get(); + PlcReadResponse<?> readResponse = reader.read(builder -> builder.addItem("randomRegister", "register:7[3]")).get(); System.out.println("Response " + readResponse); readResponse.getAllByteArrays("randomRegister").stream() .map(HexUtil::toHex) - .map(hex -> "Value: " + hex) + .map(hex -> "Register Value: " + hex) .forEach(System.out::println); } { PlcReader reader = plcConnection.getReader().orElseThrow(() -> new RuntimeException("No Reader found")); - PlcReadResponse<?> readResponse = reader.read(builder -> builder.addItem("randomRegister", "coil:1")).get(); + PlcReadResponse<?> readResponse = reader.read(builder -> builder.addItem("randomCoil", "coil:1[9]")).get(); System.out.println("Response " + readResponse); - readResponse.getAllByteArrays("randomRegister").stream() - .map(HexUtil::toHex) - .map(hex -> "Value: " + hex) + readResponse.getAllBooleans("randomCoil").stream() + .map(hex -> "Coil Value: " + hex) .forEach(System.out::println); } { PlcWriter writer = plcConnection.getWriter().orElseThrow(() -> new RuntimeException("No Writer found")); - PlcWriteResponse<?> writeResponse = writer.write(builder -> builder.addItem("randomCoilField", "coil:1", 1)).get(); + PlcWriteResponse<?> writeResponse = writer.write(builder -> builder.addItem("randomCoilField", "coil:1", true)).get(); System.out.println("Response " + writeResponse); } } catch (Exception e) {