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) {