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/13 15:15:46 UTC

[plc4x] 01/02: - Got the new Modbus driver to actually do something.

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

commit 53b1d49484c394674e757367f59e83105ecb55ef
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Feb 13 16:14:59 2020 +0100

    - Got the new Modbus driver to actually do something.
---
 .../org/apache/plc4x/java/api/value/PlcByte.java   | 156 +++++++++++++++++++++
 .../org/apache/plc4x/java/api/value/PlcList.java   |   2 +-
 .../org/apache/plc4x/java/api/value/PlcShort.java  | 156 +++++++++++++++++++++
 .../utils/rawsockets/netty/RawSocketChannel.java   |   4 +-
 .../main/resources/protocols/modbus/modbus.mspec   |  21 ++-
 .../org/apache/plc4x/java/modbus/ModbusDriver.java |  11 +-
 .../plc4x/java/modbus/field/ModbusField.java       |   2 +-
 .../modbus/field/ModbusFieldDiscreteInput.java     |   2 +-
 .../modbus/field/ModbusFieldHoldingRegister.java   |   2 +-
 .../modbus/field/ModbusFieldInputRegister.java     |   2 +-
 .../modbus/field/ModbusFieldMaskWriteRegister.java |   2 +-
 .../java/modbus/protocol/ModbusProtocolLogic.java  |  92 +++++++++++-
 12 files changed, 437 insertions(+), 15 deletions(-)

diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcByte.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcByte.java
new file mode 100644
index 0000000..35eaefa
--- /dev/null
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcByte.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.plc4x.java.api.value;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class PlcByte extends PlcSimpleValue<Byte> {
+
+    public PlcByte(Byte value) {
+        super(value, true);
+    }
+
+    public PlcByte(byte value) {
+        super(value, false);
+    }
+
+    public PlcByte(Short value) {
+        super(value.byteValue(), true);
+    }
+
+    public PlcByte(short value) {
+        super(((Short) value).byteValue(), false);
+    }
+
+    public PlcByte(Integer value) {
+        super(value.byteValue(), true);
+    }
+
+    public PlcByte(int value) {
+        super(((Integer) value).byteValue(), false);
+    }
+
+    @Override
+    public boolean isBoolean() {
+        return true;
+    }
+
+    @Override
+    public boolean getBoolean() {
+        return (value != null) && !value.equals((byte) 0);
+    }
+
+    @Override
+    public boolean isByte() {
+        return true;
+    }
+
+    @Override
+    public byte getByte() {
+        return value;
+    }
+
+    @Override
+    public boolean isShort() {
+        return true;
+    }
+
+    @Override
+    public short getShort() {
+        return value.shortValue();
+    }
+
+    @Override
+    public boolean isInteger() {
+        return true;
+    }
+
+    @Override
+    public int getInteger() {
+        return value.intValue();
+    }
+
+    @Override
+    public boolean isLong() {
+        return true;
+    }
+
+    @Override
+    public long getLong() {
+        return value.longValue();
+    }
+
+    @Override
+    public boolean isBigInteger() {
+        return true;
+    }
+
+    @Override
+    public BigInteger getBigInteger() {
+        return BigInteger.valueOf((long) value);
+    }
+
+    @Override
+    public boolean isFloat() {
+        return true;
+    }
+
+    @Override
+    public float getFloat() {
+        return value.floatValue();
+    }
+
+    @Override
+    public boolean isDouble() {
+        return true;
+    }
+
+    @Override
+    public double getDouble() {
+        return value.doubleValue();
+    }
+
+    @Override
+    public boolean isBigDecimal() {
+        return true;
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        return new BigDecimal(value);
+    }
+
+    @Override
+    public boolean isString() {
+        return true;
+    }
+
+    @Override
+    public String getString() {
+        return toString();
+    }
+
+    @Override
+    public String toString() {
+        return Integer.toString(value);
+    }
+
+}
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcList.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcList.java
index 1cfb684..1218a2a 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcList.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcList.java
@@ -31,7 +31,7 @@ public class PlcList extends PlcValueAdapter {
         List<PlcValue> safelist = listItems.stream().map(plcValue -> {
             // to avoid unwrapped list cause of type erasure
             if (plcValue instanceof PlcValue) {
-                return (PlcValue)plcValue;
+                return (PlcValue) plcValue;
             } else {
                 return PlcValues.of(plcValue);
             }
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcShort.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcShort.java
new file mode 100644
index 0000000..5e93c8e
--- /dev/null
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcShort.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.plc4x.java.api.value;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class PlcShort extends PlcSimpleValue<Short> {
+
+    public PlcShort(Byte value) {
+        super(value.shortValue(), true);
+    }
+
+    public PlcShort(byte value) {
+        super(((Byte) value).shortValue(), false);
+    }
+
+    public PlcShort(Short value) {
+        super(value, true);
+    }
+
+    public PlcShort(short value) {
+        super(value, false);
+    }
+
+    public PlcShort(Integer value) {
+        super(value.shortValue(), true);
+    }
+
+    public PlcShort(int value) {
+        super(((Integer) value).shortValue(), false);
+    }
+
+    @Override
+    public boolean isBoolean() {
+        return true;
+    }
+
+    @Override
+    public boolean getBoolean() {
+        return (value != null) && !value.equals((short) 0);
+    }
+
+    @Override
+    public boolean isByte() {
+        return true;
+    }
+
+    @Override
+    public byte getByte() {
+        return value.byteValue();
+    }
+
+    @Override
+    public boolean isShort() {
+        return true;
+    }
+
+    @Override
+    public short getShort() {
+        return value;
+    }
+
+    @Override
+    public boolean isInteger() {
+        return true;
+    }
+
+    @Override
+    public int getInteger() {
+        return value.intValue();
+    }
+
+    @Override
+    public boolean isLong() {
+        return true;
+    }
+
+    @Override
+    public long getLong() {
+        return value.longValue();
+    }
+
+    @Override
+    public boolean isBigInteger() {
+        return true;
+    }
+
+    @Override
+    public BigInteger getBigInteger() {
+        return BigInteger.valueOf((long) value);
+    }
+
+    @Override
+    public boolean isFloat() {
+        return true;
+    }
+
+    @Override
+    public float getFloat() {
+        return value.floatValue();
+    }
+
+    @Override
+    public boolean isDouble() {
+        return true;
+    }
+
+    @Override
+    public double getDouble() {
+        return value.doubleValue();
+    }
+
+    @Override
+    public boolean isBigDecimal() {
+        return true;
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        return new BigDecimal(value);
+    }
+
+    @Override
+    public boolean isString() {
+        return true;
+    }
+
+    @Override
+    public String getString() {
+        return toString();
+    }
+
+    @Override
+    public String toString() {
+        return Integer.toString(value);
+    }
+
+}
diff --git a/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/netty/RawSocketChannel.java b/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/netty/RawSocketChannel.java
index 2e1b8d8..386a033 100644
--- a/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/netty/RawSocketChannel.java
+++ b/plc4j/utils/raw-sockets/src/main/java/org/apache/plc4x/java/utils/rawsockets/netty/RawSocketChannel.java
@@ -84,9 +84,9 @@ public class RawSocketChannel extends OioByteStreamChannel {
         // Try to get the device name of the network interface that we want to open.
         String deviceName = getDeviceName(remoteRawSocketAddress);
         if(deviceName == null) {
-            logger.error("Network device not specified and coudln't detect it automatically");
+            logger.error("Network device not specified and couldn't detect it automatically");
             pipeline().fireExceptionCaught(
-                new RawSocketException("Network device not specified and coudln't detect it automatically"));
+                new RawSocketException("Network device not specified and couldn't detect it automatically"));
             return;
         }
 
diff --git a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
index 6347dda..24f41a8 100644
--- a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
+++ b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
@@ -56,8 +56,8 @@
 ]
 
 [discriminatedType 'ModbusPDU' [bit 'response']
-    [implicit       uint 7      'function'  'DISCRIMINATOR_VALUES[1]']
     [implicit       bit         'error'     'DISCRIMINATOR_VALUES[0]']
+    [implicit       uint 7      'function'  'DISCRIMINATOR_VALUES[1]']
     [typeSwitch 'error','function','response'
         ['true'                     ModbusPDUError
             [simple     uint 8      'exceptionCode']
@@ -267,3 +267,22 @@
     [implicit   uint 16    'recordLength'   '(COUNT(recordData) * 2) / 2']
     [array      uint 16    'recordData'     length  'recordLength * 2']
 ]
+
+[dataIo 'DataItem' [uint 8 'dataType', uint 8 'numberOfValues']
+    [typeSwitch 'dataType','numberOfValues'
+        ['1','1' Boolean
+            [reserved uint 7 '0x00']
+            [simple   bit    'value']
+        ]
+        ['1' List
+            [array bit 'value' count 'numberOfValues']
+        ]
+        ['2','1' Integer
+            [simple int 16 'value']
+        ]
+        ['2' List
+            [array int 16 'value' count 'numberOfValues']
+        ]
+    ]
+]
+
diff --git a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/ModbusDriver.java b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/ModbusDriver.java
index 9a2a6f3..6000572 100644
--- a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/ModbusDriver.java
+++ b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/ModbusDriver.java
@@ -56,6 +56,15 @@ public class ModbusDriver extends GeneratedDriverBase<ModbusTcpADU> {
         return "tcp";
     }
 
+    /**
+     * Modbus doesn't have a login procedure, so there is no need to wait for a login to finish.
+     * @return false
+     */
+    @Override
+    protected boolean awaitSetupComplete() {
+        return false;
+    }
+
     @Override
     protected boolean canRead() {
         return true;
@@ -86,7 +95,7 @@ public class ModbusDriver extends GeneratedDriverBase<ModbusTcpADU> {
         @Override
         public int applyAsInt(ByteBuf byteBuf) {
             if (byteBuf.readableBytes() >= 6) {
-                return byteBuf.getUnsignedShort(byteBuf.readerIndex() + 4);
+                return byteBuf.getUnsignedShort(byteBuf.readerIndex() + 4) + 6;
             }
             return -1;
         }
diff --git a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusField.java b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusField.java
index 1752d77..2edce00 100644
--- a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusField.java
+++ b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusField.java
@@ -25,7 +25,7 @@ import java.util.regex.Pattern;
 
 public abstract class ModbusField implements PlcField {
 
-    public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?<address>\\d+)(\\[(?<quantity>\\d)])?");
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?<address>\\d+)(\\[(?<quantity>\\d+)])?");
 
     private final int address;
 
diff --git a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldDiscreteInput.java b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldDiscreteInput.java
index 0ccdbf5..5f35670 100644
--- a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldDiscreteInput.java
+++ b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldDiscreteInput.java
@@ -25,7 +25,7 @@ import java.util.regex.Pattern;
 
 public class ModbusFieldDiscreteInput extends ModbusField {
 
-    public static final Pattern ADDRESS_PATTERN = Pattern.compile("readdiscreteinputs:" + ModbusField.ADDRESS_PATTERN);
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("discrete-input:" + ModbusField.ADDRESS_PATTERN);
 
     public ModbusFieldDiscreteInput(int address, Integer quantity) {
         super(address, quantity);
diff --git a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldHoldingRegister.java b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldHoldingRegister.java
index 7d2cba3..a720cac 100644
--- a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldHoldingRegister.java
+++ b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldHoldingRegister.java
@@ -25,7 +25,7 @@ import java.util.regex.Pattern;
 
 public class ModbusFieldHoldingRegister extends ModbusField {
 
-    public static final Pattern ADDRESS_PATTERN = Pattern.compile("readholdingregisters:" + ModbusField.ADDRESS_PATTERN);
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("holding-register:" + ModbusField.ADDRESS_PATTERN);
 
     protected ModbusFieldHoldingRegister(int address, Integer quantity) {
         super(address, quantity);
diff --git a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldInputRegister.java b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldInputRegister.java
index 1185554..6dc0016 100644
--- a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldInputRegister.java
+++ b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldInputRegister.java
@@ -25,7 +25,7 @@ import java.util.regex.Pattern;
 
 public class ModbusFieldInputRegister extends ModbusField {
 
-    public static final Pattern ADDRESS_PATTERN = Pattern.compile("readinputregisters:" + ModbusField.ADDRESS_PATTERN);
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("input-register:" + ModbusField.ADDRESS_PATTERN);
 
     protected ModbusFieldInputRegister(int address, Integer quantity) {
         super(address, quantity);
diff --git a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldMaskWriteRegister.java b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldMaskWriteRegister.java
index 5ad9f09..7c3d513 100644
--- a/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldMaskWriteRegister.java
+++ b/sandbox/test-java-modbus-driver/src/main/java/org/apache/plc4x/java/modbus/field/ModbusFieldMaskWriteRegister.java
@@ -33,7 +33,7 @@ import java.util.regex.Pattern;
 @Deprecated
 public class ModbusFieldMaskWriteRegister extends ModbusField {
 
-    public static final Pattern ADDRESS_PATTERN = Pattern.compile("maskwrite:" + ModbusField.ADDRESS_PATTERN + "/" + "(?<andMask>\\d+)/(?<orMask>\\d+)");
+    public static final Pattern ADDRESS_PATTERN = Pattern.compile("mask-write:" + ModbusField.ADDRESS_PATTERN + "/" + "(?<andMask>\\d+)/(?<orMask>\\d+)");
 
     private final int andMask;
     private final int orMask;
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 796c542..4beef07 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
@@ -18,24 +18,38 @@ under the License.
 */
 package org.apache.plc4x.java.modbus.protocol;
 
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.PlcReadRequest;
 import org.apache.plc4x.java.api.messages.PlcReadResponse;
 import org.apache.plc4x.java.api.messages.PlcWriteRequest;
 import org.apache.plc4x.java.api.messages.PlcWriteResponse;
 import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.value.PlcBoolean;
+import org.apache.plc4x.java.api.value.PlcList;
+import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.modbus.config.ModbusConfiguration;
 import org.apache.plc4x.java.modbus.field.ModbusFieldCoil;
 import org.apache.plc4x.java.modbus.field.ModbusFieldDiscreteInput;
 import org.apache.plc4x.java.modbus.field.ModbusFieldHoldingRegister;
 import org.apache.plc4x.java.modbus.field.ModbusFieldInputRegister;
 import org.apache.plc4x.java.modbus.readwrite.*;
+import org.apache.plc4x.java.modbus.readwrite.io.DataItemIO;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.configuration.HasConfiguration;
+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.optimizer.RequestTransactionManager;
 
 import java.time.Duration;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -75,7 +89,8 @@ public class ModbusProtocolLogic  extends Plc4xProtocolBase<ModbusTcpADU> implem
 
         // Example for sending a request ...
         // TODO: Break down multiple items into sub-futures that are acknowledged at the end
-        for (PlcField field : request.getFields()) {
+        for (String fieldName : request.getFieldNames()) {
+            PlcField field = request.getField(fieldName);
             ModbusPDU requestPdu = getRequestPdu(field);
             int transactionIdentifier = transactionIdentifierGenerator.getAndIncrement();
             ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitIdentifier, requestPdu);
@@ -86,9 +101,25 @@ public class ModbusProtocolLogic  extends Plc4xProtocolBase<ModbusTcpADU> implem
                 .onError((p, e) -> future.completeExceptionally(e))
                 .check(p -> p.getTransactionIdentifier() == transactionIdentifier)
                 .unwrap(ModbusTcpADU::getPdu)
-                .handle(p -> {
-                    // TODO: Do something sensible with the response ...
-                    //future.complete(p);
+                .handle(responsePdu -> {
+                    // Try to decode the response data based on the corresponding request.
+                    PlcValue plcValue = null;
+                    PlcResponseCode responseCode;
+                    try {
+                        plcValue = toPlcValue(requestPdu, responsePdu);
+                        responseCode = PlcResponseCode.OK;
+                    } catch (ParseException e) {
+                        // Add an error response code ...
+                        responseCode = PlcResponseCode.INTERNAL_ERROR;
+                    }
+
+                    // Prepare the response.
+                    PlcReadResponse response = new DefaultPlcReadResponse(request,
+                        Collections.singletonMap(fieldName, Pair.of(responseCode, plcValue)));
+
+                    // Pass the response back to the application.
+                    future.complete(response);
+
                     // Finish the request-transaction.
                     transaction.endRequest();
             }));
@@ -124,7 +155,58 @@ public class ModbusProtocolLogic  extends Plc4xProtocolBase<ModbusTcpADU> implem
             ModbusFieldHoldingRegister holdingRegister = (ModbusFieldHoldingRegister) field;
             return new ModbusPDUReadHoldingRegistersRequest(holdingRegister.getAddress(), holdingRegister.getQuantity());
         }
-        throw new RuntimeException("Unsupported field type " + field.getClass().getName());
+        throw new PlcRuntimeException("Unsupported field type " + field.getClass().getName());
+    }
+
+    private PlcValue toPlcValue(ModbusPDU request, ModbusPDU response) throws ParseException {
+        if (request instanceof ModbusPDUReadDiscreteInputsRequest) {
+            if (!(response instanceof ModbusPDUReadDiscreteInputsResponse)) {
+                throw new PlcRuntimeException("Unexpected response type ModbusPDUReadDiscreteInputsResponse");
+            }
+            ModbusPDUReadDiscreteInputsRequest req = (ModbusPDUReadDiscreteInputsRequest) request;
+            ModbusPDUReadDiscreteInputsResponse resp = (ModbusPDUReadDiscreteInputsResponse) response;
+            return readBooleanList(req.getQuantity(), resp.getValue());
+        } else if (request instanceof ModbusPDUReadCoilsRequest) {
+            if (!(response instanceof ModbusPDUReadCoilsResponse)) {
+                throw new PlcRuntimeException("Unexpected response type ModbusPDUReadCoilsResponse");
+            }
+            ModbusPDUReadCoilsRequest req = (ModbusPDUReadCoilsRequest) request;
+            ModbusPDUReadCoilsResponse resp = (ModbusPDUReadCoilsResponse) response;
+            return readBooleanList(req.getQuantity(), resp.getValue());
+        } else if (request instanceof ModbusPDUReadInputRegistersRequest) {
+            if (!(response instanceof ModbusPDUReadInputRegistersResponse)) {
+                throw new PlcRuntimeException("Unexpected response type ModbusPDUReadInputRegistersResponse");
+            }
+            ModbusPDUReadInputRegistersRequest req = (ModbusPDUReadInputRegistersRequest) request;
+            ModbusPDUReadInputRegistersResponse resp = (ModbusPDUReadInputRegistersResponse) response;
+            ReadBuffer io = new ReadBuffer(resp.getValue());
+            return DataItemIO.staticParse(io, (short) 2, (short) req.getQuantity());
+        } else if (request instanceof ModbusPDUReadHoldingRegistersRequest) {
+            if (!(response instanceof ModbusPDUReadHoldingRegistersResponse)) {
+                throw new PlcRuntimeException("Unexpected response type ModbusPDUReadHoldingRegistersResponse");
+            }
+            ModbusPDUReadHoldingRegistersRequest req = (ModbusPDUReadHoldingRegistersRequest) request;
+            ModbusPDUReadHoldingRegistersResponse resp = (ModbusPDUReadHoldingRegistersResponse) response;
+            ReadBuffer io = new ReadBuffer(resp.getValue());
+            return DataItemIO.staticParse(io, (short) 2, (short) req.getQuantity());
+        }
+        return null;
+    }
+
+    private PlcValue readBooleanList(int count, byte[] data) throws ParseException {
+        ReadBuffer io = new ReadBuffer(data);
+        if(count == 1) {
+            return DataItemIO.staticParse(io, (short) 1, (short) 1);
+        }
+        // Make sure we read in all the bytes. Unfortunately when requesting 9 bytes
+        // they are ordered like this: 8 7 6 5 4 3 2 1 | 0 0 0 0 0 0 0 9
+        // Luckily it turns out that this is exactly how BitSet parses byte[]
+        BitSet bits = BitSet.valueOf(data);
+        List<PlcBoolean> result = new ArrayList<>(count);
+        for(int i = 0; i < count; i++) {
+            result.add(new PlcBoolean(bits.get(i)));
+        }
+        return new PlcList(result);
     }
 
 }