You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/02/17 12:01:05 UTC
[plc4x] branch develop updated: - Implemented the writing of coils
and registers in modbus
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/develop by this push:
new eaca853 - Implemented the writing of coils and registers in modbus
eaca853 is described below
commit eaca8533c1255eeadab93a03822279fa22104abd
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Mon Feb 17 13:00:58 2020 +0100
- Implemented the writing of coils and registers in modbus
---
.../main/resources/protocols/modbus/modbus.mspec | 12 +--
.../java/modbus/field/ModbusFieldHandler.java | 68 +++++++++----
.../java/modbus/protocol/ModbusProtocolLogic.java | 105 +++++++++++++++++++--
3 files changed, 155 insertions(+), 30 deletions(-)
diff --git a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
index 24f41a8..01605d4 100644
--- a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
+++ b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
@@ -130,18 +130,18 @@
[simple uint 16 'value']
]
- ['false','0x10','false' ModbusPDUWriteMultipleRegistersRequest
+ ['false','0x10','false' ModbusPDUWriteMultipleHoldingRegistersRequest
[simple uint 16 'startingAddress']
[simple uint 16 'quantity']
[implicit uint 8 'byteCount' 'COUNT(value)']
[array int 8 'value' count 'byteCount']
]
- ['false','0x10','true' ModbusPDUWriteMultipleRegistersResponse
+ ['false','0x10','true' ModbusPDUWriteMultipleHoldingRegistersResponse
[simple uint 16 'startingAddress']
[simple uint 16 'quantity']
]
- ['false','0x17','false' ModbusPDUReadWriteMultipleRegistersRequest
+ ['false','0x17','false' ModbusPDUReadWriteMultipleHoldingRegistersRequest
[simple uint 16 'readStartingAddress']
[simple uint 16 'readQuantity']
[simple uint 16 'writeStartingAddress']
@@ -149,17 +149,17 @@
[implicit uint 8 'byteCount' 'COUNT(value)']
[array int 8 'value' count 'byteCount']
]
- ['false','0x17','true' ModbusPDUReadWriteMultipleRegistersResponse
+ ['false','0x17','true' ModbusPDUReadWriteMultipleHoldingRegistersResponse
[implicit uint 8 'byteCount' 'COUNT(value)']
[array int 8 'value' count 'byteCount']
]
- ['false','0x16','false' ModbusPDUMaskWriteRegisterRequest
+ ['false','0x16','false' ModbusPDUMaskWriteHoldingRegisterRequest
[simple uint 16 'referenceAddress']
[simple uint 16 'andMask']
[simple uint 16 'orMask']
]
- ['false','0x16','true' ModbusPDUMaskWriteRegisterResponse
+ ['false','0x16','true' ModbusPDUMaskWriteHoldingRegisterResponse
[simple uint 16 'referenceAddress']
[simple uint 16 'andMask']
[simple uint 16 'orMask']
diff --git a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldHandler.java b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldHandler.java
index 1ea13a1..b904743 100644
--- a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldHandler.java
+++ b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldHandler.java
@@ -23,6 +23,7 @@ import org.apache.plc4x.java.api.model.PlcField;
import org.apache.plc4x.java.api.value.*;
import org.apache.plc4x.java.spi.connection.DefaultPlcFieldHandler;
+import java.util.ArrayList;
import java.util.BitSet;
import java.util.LinkedList;
import java.util.List;
@@ -92,24 +93,57 @@ public class ModbusFieldHandler extends DefaultPlcFieldHandler {
}
}
- /*@Override
- public PlcValue encodeByteArray(PlcField field, Object[] values) {
- ModbusField modbusField = (ModbusField) field;
- List<Byte[]> byteArrays = new LinkedList<>();
- for (Object value : values) {
- if (value instanceof byte[]) {
- byte[] byteArray = (byte[]) value;
- byteArrays.add(ArrayUtils.toObject(byteArray));
- } else if (value instanceof Byte[]) {
- Byte[] byteArray = (Byte[]) value;
- byteArrays.add(byteArray);
- } else {
- throw new IllegalArgumentException(
- "Value of type " + value.getClass().getName() +
- " is not assignable to " + modbusField + " fields.");
+ @Override
+ public PlcValue encodeByte(PlcField field, Object[] values) {
+ return encodeShort(field, values);
+ }
+
+ @Override
+ public PlcValue encodeInteger(PlcField field, Object[] values) {
+ return encodeShort(field, values);
+ }
+
+ @Override
+ public PlcValue encodeLong(PlcField field, Object[] values) {
+ return encodeShort(field, values);
+ }
+
+ @Override
+ public PlcValue encodeBigInteger(PlcField field, Object[] values) {
+ return encodeShort(field, values);
+ }
+
+ @Override
+ public PlcValue encodeFloat(PlcField field, Object[] values) {
+ return encodeShort(field, values);
+ }
+
+ @Override
+ public PlcValue encodeDouble(PlcField field, Object[] values) {
+ return encodeShort(field, values);
+ }
+
+ @Override
+ public PlcValue encodeBigDecimal(PlcField field, Object[] values) {
+ return encodeShort(field, values);
+ }
+
+ @Override
+ public PlcValue encodeShort(PlcField field, Object[] values) {
+ if(values.length == 1) {
+ return new PlcShort(((Number) values[0]).shortValue());
+ } else {
+ List<PlcShort> shorts = new ArrayList<>(values.length);
+ for (Object value : values) {
+ Number numberValue = (Number) value;
+ // Intentionally checking the next larger type.
+ if((numberValue.intValue() < Short.MIN_VALUE) || (numberValue.intValue() > Short.MAX_VALUE)) {
+ throw new PlcInvalidFieldException("Value of " + numberValue.toString() + " exceeds the boundaries of a short value.");
+ }
+ shorts.add(new PlcShort(((Number) value).shortValue()));
}
+ return new PlcList(shorts);
}
- return new DefaultModbusByteArrayPlcValue(byteArrays.toArray(new Byte[0][0]));
- }*/
+ }
}
diff --git a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/protocol/ModbusProtocolLogic.java b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/protocol/ModbusProtocolLogic.java
index a07c415..9d3956c 100644
--- a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/protocol/ModbusProtocolLogic.java
+++ b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/protocol/ModbusProtocolLogic.java
@@ -43,6 +43,8 @@ import org.apache.plc4x.java.spi.generation.ParseException;
import org.apache.plc4x.java.spi.generation.ReadBuffer;
import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest;
import org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse;
+import org.apache.plc4x.java.spi.messages.DefaultPlcWriteRequest;
+import org.apache.plc4x.java.spi.messages.DefaultPlcWriteResponse;
import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
import java.time.Duration;
@@ -88,10 +90,10 @@ public class ModbusProtocolLogic extends Plc4xProtocolBase<ModbusTcpADU> impleme
// 2. Split up into multiple sub-requests
// Example for sending a request ...
- // TODO: Break down multiple items into sub-futures that are acknowledged at the end
- for (String fieldName : request.getFieldNames()) {
+ if(request.getFieldNames().size() == 1) {
+ String fieldName = request.getFieldNames().iterator().next();
PlcField field = request.getField(fieldName);
- ModbusPDU requestPdu = getRequestPdu(field);
+ final ModbusPDU requestPdu = getReadRequestPdu(field);
int transactionIdentifier = transactionIdentifierGenerator.getAndIncrement();
ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitIdentifier, requestPdu);
RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
@@ -123,13 +125,17 @@ public class ModbusProtocolLogic extends Plc4xProtocolBase<ModbusTcpADU> impleme
// Finish the request-transaction.
transaction.endRequest();
}));
+ } else {
+ future.completeExceptionally(new PlcRuntimeException("Modbus only supports single filed requests"));
}
- // TODO: Merge all the sub-futures to one big response.
return future;
}
@Override
public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
+ CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
+ DefaultPlcWriteRequest request = (DefaultPlcWriteRequest) writeRequest;
+
// 1. Sort all items by type:
// - DiscreteInput (read-only) --> Error
// - Coil (read-write) --> ModbusPduWriteSingleCoilRequest / ModbusPduWriteMultipleCoilsRequest
@@ -138,10 +144,44 @@ public class ModbusProtocolLogic extends Plc4xProtocolBase<ModbusTcpADU> impleme
// - FifoQueue (read-only) --> Error
// - FileRecord (read-write) --> ModbusPduWriteFileRecordRequest
// 2. Split up into multiple sub-requests
- return super.write(writeRequest);
+ if(request.getFieldNames().size() == 1) {
+ String fieldName = request.getFieldNames().iterator().next();
+ PlcField field = request.getField(fieldName);
+ final ModbusPDU requestPdu = getWriteRequestPdu(field, ((DefaultPlcWriteRequest) writeRequest).getPlcValue(fieldName));
+ int transactionIdentifier = transactionIdentifierGenerator.getAndIncrement();
+ ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitIdentifier, requestPdu);
+ RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+ transaction.submit(() -> context.sendRequest(modbusTcpADU)
+ .expectResponse(ModbusTcpADU.class, requestTimeout)
+ .onTimeout(future::completeExceptionally)
+ .onError((p, e) -> future.completeExceptionally(e))
+ .check(p -> p.getTransactionIdentifier() == transactionIdentifier)
+ .unwrap(ModbusTcpADU::getPdu)
+ .handle(responsePdu -> {
+ // TODO: Check the correct number of elements were written.
+
+ // Try to decode the response data based on the corresponding request.
+ PlcValue plcValue = null;
+ PlcResponseCode responseCode = PlcResponseCode.OK;
+
+ // Prepare the response.
+ PlcWriteResponse response = new DefaultPlcWriteResponse(request,
+ Collections.singletonMap(fieldName, responseCode));
+
+ // Pass the response back to the application.
+ future.complete(response);
+
+ // Finish the request-transaction.
+ transaction.endRequest();
+ }));
+
+ } else {
+ future.completeExceptionally(new PlcRuntimeException("Modbus only supports single filed requests"));
+ }
+ return future;
}
- private ModbusPDU getRequestPdu(PlcField field) {
+ private ModbusPDU getReadRequestPdu(PlcField field) {
if(field instanceof ModbusFieldDiscreteInput) {
ModbusFieldDiscreteInput discreteInput = (ModbusFieldDiscreteInput) field;
return new ModbusPDUReadDiscreteInputsRequest(discreteInput.getAddress(), discreteInput.getQuantity());
@@ -155,7 +195,20 @@ public class ModbusProtocolLogic extends Plc4xProtocolBase<ModbusTcpADU> impleme
ModbusFieldHoldingRegister holdingRegister = (ModbusFieldHoldingRegister) field;
return new ModbusPDUReadHoldingRegistersRequest(holdingRegister.getAddress(), holdingRegister.getQuantity());
}
- throw new PlcRuntimeException("Unsupported field type " + field.getClass().getName());
+ throw new PlcRuntimeException("Unsupported read field type " + field.getClass().getName());
+ }
+
+ private ModbusPDU getWriteRequestPdu(PlcField field, PlcValue plcValue) {
+ if(field instanceof ModbusFieldCoil) {
+ ModbusFieldCoil coil = (ModbusFieldCoil) field;
+ return new ModbusPDUWriteMultipleCoilsRequest(coil.getAddress(), coil.getQuantity(),
+ fromPlcValue(plcValue));
+ } else if(field instanceof ModbusFieldHoldingRegister) {
+ ModbusFieldHoldingRegister holdingRegister = (ModbusFieldHoldingRegister) field;
+ return new ModbusPDUWriteMultipleHoldingRegistersRequest(holdingRegister.getAddress(),
+ holdingRegister.getQuantity(), fromPlcValue(plcValue));
+ }
+ throw new PlcRuntimeException("Unsupported write field type " + field.getClass().getName());
}
private PlcValue toPlcValue(ModbusPDU request, ModbusPDU response) throws ParseException {
@@ -193,6 +246,44 @@ public class ModbusProtocolLogic extends Plc4xProtocolBase<ModbusTcpADU> impleme
return null;
}
+ private byte[] fromPlcValue(PlcValue plcValue) {
+ if(plcValue instanceof PlcList) {
+ PlcList plcList = (PlcList) plcValue;
+ BitSet booleans = null;
+ List<Short> shorts = null;
+ int b = 0;
+ for (PlcValue value : plcList.getList()) {
+ if("PlcBoolean".equals(value.getClass().getSimpleName())) {
+ if(booleans == null) {
+ booleans = new BitSet(plcList.getList().size());
+ }
+ PlcBoolean plcBoolean = (PlcBoolean) value;
+ booleans.set(b, plcBoolean.getBoolean());
+ b++;
+ } else if(value.isShort()) {
+ if(shorts == null) {
+ shorts = new ArrayList<>(plcList.getList().size());
+ }
+ shorts.add(value.getShort());
+ } else {
+ throw new PlcRuntimeException("Can only encode boolean or short values");
+ }
+ }
+ if(booleans != null) {
+ return booleans.toByteArray();
+ } else if(shorts != null) {
+ byte[] bytes = new byte[shorts.size() * 2];
+ for(int i = 0; i < shorts.size(); i++) {
+ Short shortValue = shorts.get(i);
+ bytes[i * 2] = (byte)((shortValue >> 8) & 0xff);
+ bytes[(i * 2) + 1] = (byte)(shortValue & 0xff);
+ }
+ return bytes;
+ }
+ }
+ return new byte[0];
+ }
+
private PlcValue readBooleanList(int count, byte[] data) throws ParseException {
ReadBuffer io = new ReadBuffer(data);
if(count == 1) {