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/08/10 12:02:15 UTC

[plc4x] 05/05: - Continued working on the new AMS/ADS driver

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 eded6d8750c756b88acb69a1269341871df79dfe
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Mon Aug 10 14:02:00 2020 +0200

    - Continued working on the new AMS/ADS driver
---
 .../main/resources/protocols/amsads/amsads.mspec   | 178 +++++++-
 .../apache/plc4x/java/amsads/AMSADSPlcDriver.java  |  13 +-
 .../attic/connection/AdsTcpPlcConnection.java      |  13 +-
 .../amsads/attic/protocol/Ads2PayloadProtocol.java |   2 +-
 .../attic/protocol/Payload2SerialProtocol.java     |   4 +-
 .../amsads/attic/protocol/Payload2TcpProtocol.java |   2 +-
 .../amsads/attic/protocol/Plc4x2AdsProtocol.java   |  15 +-
 .../protocol/exception/AdsException.java           |   2 +-
 .../exception/AdsProtocolOverflowException.java    |   2 +-
 .../{ => attic}/protocol/util/DigestUtil.java      |   2 +-
 .../protocol/util/LittleEndianDecoder.java         |   8 +-
 .../protocol/util/LittleEndianEncoder.java         |  42 +-
 .../protocol/util/SingleMessageRateLimiter.java    |   2 +-
 .../java/amsads/{ => attic}/types/AdsDataType.java |   7 +-
 .../amsads/configuration/AdsConfiguration.java     |   2 +-
 .../apache/plc4x/java/amsads/field/AdsField.java   |   2 +-
 .../plc4x/java/amsads/field/AdsFieldHandler.java   | 101 +++--
 .../plc4x/java/amsads/field/DirectAdsField.java    |   5 +-
 .../plc4x/java/amsads/field/SymbolicAdsField.java  |   2 +-
 .../java/amsads/model/AdsSubscriptionHandle.java   |   2 +-
 .../java/amsads/protocol/AdsProtocolLogic.java     | 274 ++++++++++--
 .../java/amsads/protocol/util/package-info.java    |  22 -
 .../plc4x/java/amsads/utils/StaticHelper.java      |  37 ++
 .../services/org.apache.plc4x.java.api.PlcDriver   |  19 -
 .../apache/plc4x/protocol/amsads/AdsDriverIT.java  |  29 ++
 ...arserTest.java => AdsSerializerParserTest.java} |   4 +-
 .../src/test/resources/testsuite/AdsDriverIT.xml   | 463 +++++++++++++++++++++
 27 files changed, 1062 insertions(+), 192 deletions(-)

diff --git a/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec b/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec
index e5131f4..69d7283 100644
--- a/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec
+++ b/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec
@@ -319,7 +319,7 @@
             // 4 bytes	Length of the data (in bytes) which should be written.
             [implicit uint 32 'writeLength' '(COUNT(items) * 12) + COUNT(data)']
             // Only if the indexGroup implies a sum-read response, will the indexOffset indicate the number of elements.
-            [array  AdsReadRequest 'items' COUNT '(indexGroup == ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.value) ? indexOffset : 0']
+            [array  AdsReadWriteRequest 'items' COUNT '(indexGroup == ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.value) ? indexOffset : 0']
             // n bytes	Data which are written in the ADS device.
             [array int 8 'data' count 'writeLength - (COUNT(items) * 12)']
         ]
@@ -352,6 +352,182 @@
     [array int 8 'data' count 'sampleSize']
 ]
 
+[dataIo 'DataItem' [AdsDataType 'adsDataType']
+    [typeSwitch 'adsDataType'
+        // -----------------------------------------
+        // Bit
+        // -----------------------------------------
+        ['AdsDataType.BOOL' Boolean
+            [reserved uint 7 '0x00']
+            [simple   bit    'value']
+        ]
+        ['AdsDataType.BIT' Boolean
+            [reserved uint 7 '0x00']
+            [simple   bit    'value']
+        ]
+        ['AdsDataType.BIT8' Boolean
+            [reserved uint 7 '0x00']
+            [simple   bit    'value']
+        ]
+
+        // -----------------------------------------
+        // Bit-strings
+        // -----------------------------------------
+        // 1 byte
+        ['AdsDataType.BYTE' List
+            [array bit 'value' count '8']
+        ]
+        ['AdsDataType.BITARR8' List
+            [array bit 'value' count '8']
+        ]
+        // 2 byte (16 bit)
+        ['AdsDataType.WORD' List
+            [array bit 'value' count '16']
+        ]
+        ['AdsDataType.BITARR16' List
+            [array bit 'value' count '16']
+        ]
+        // 4 byte (32 bit)
+        ['AdsDataType.DWORD' List
+            [array bit 'value' count '32']
+        ]
+        ['AdsDataType.BITARR32' List
+            [array bit 'value' count '32']
+        ]
+
+        // -----------------------------------------
+        // Integers
+        // -----------------------------------------
+        // 8 bit:
+        ['AdsDataType.SINT' Integer
+            [simple int 8 'value']
+        ]
+        ['AdsDataType.INT8' Integer
+            [simple int 8 'value']
+        ]
+        ['AdsDataType.USINT' Integer
+            [simple uint 8 'value']
+        ]
+        ['AdsDataType.UINT8' Integer
+            [simple uint 8 'value']
+        ]
+        // 16 bit:
+        ['AdsDataType.INT' Integer
+            [simple int 16 'value']
+        ]
+        ['AdsDataType.INT16' Integer
+            [simple int 16 'value']
+        ]
+        ['AdsDataType.UINT' Integer
+            [simple uint 16 'value']
+        ]
+        ['AdsDataType.UINT16' Integer
+            [simple uint 16 'value']
+        ]
+        // 32 bit:
+        ['AdsDataType.DINT' Integer
+            [simple int 32 'value']
+        ]
+        ['AdsDataType.INT32' Integer
+            [simple int 32 'value']
+        ]
+        ['AdsDataType.UDINT' Long
+            [simple uint 32 'value']
+        ]
+        ['AdsDataType.UINT32' Long
+            [simple uint 32 'value']
+        ]
+        // 64 bit:
+        ['AdsDataType.LINT' Long
+            [simple int 64 'value']
+        ]
+        ['AdsDataType.INT64' Long
+            [simple int 64 'value']
+        ]
+        ['AdsDataType.ULINT' BigInteger
+            [simple uint 64 'value']
+        ]
+        ['AdsDataType.UINT64' BigInteger
+            [simple uint 64 'value']
+        ]
+
+        // -----------------------------------------
+        // Floating point values
+        // -----------------------------------------
+        ['AdsDataType.REAL' Float
+            [simple float 8.23  'value']
+        ]
+        ['AdsDataType.FLOAT' Float
+            [simple float 8.23  'value']
+        ]
+        ['AdsDataType.LREAL' Double
+            [simple float 11.52 'value']
+        ]
+        ['AdsDataType.DOUBLE' Double
+            [simple float 11.52 'value']
+        ]
+
+        // -----------------------------------------
+        // Characters & Strings
+        // -----------------------------------------
+        ['AdsDataType.STRING' String
+//            [manual string 'UTF-8' 'value' 'STATIC_CALL("org.apache.plc4x.java.amsads.utils.StaticHelper.parseAmsString", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.amsads.utils.StaticHelper.serializeAmsString", io, _value, _type.encoding)' '_value.length + 2']
+        ]
+    ]
+]
+
+[enum 'AdsDataType' [uint 8 'numBytes']
+    [BOOL       ['1']]
+    [BIT        ['1']]
+    [BIT8       ['1']]
+    // -----------------------------------------
+    // Bit-strings
+    // -----------------------------------------
+    // 1 byte
+    [BYTE       ['1']]
+    [BITARR8    ['1']]
+    // 2 byte (16 bit)
+    [WORD       ['2']]
+    [BITARR16   ['2']]
+    // 4 byte (32 bit)
+    [DWORD      ['4']]
+    [BITARR32   ['4']]
+    // -----------------------------------------
+    // Integers
+    // -----------------------------------------
+    // 8 bit:
+    [SINT       ['1']]
+    [INT8       ['1']]
+    [USINT      ['1']]
+    [UINT8      ['1']]
+    // 16 bit:
+    [INT        ['2']]
+    [INT16      ['2']]
+    [UINT       ['2']]
+    [UINT16     ['2']]
+    // 32 bit:
+    [DINT       ['4']]
+    [INT32      ['4']]
+    [UDINT      ['4']]
+    [UINT32     ['4']]
+    // 64 bit:
+    [LINT       ['8']]
+    [INT64      ['8']]
+    [ULINT      ['8']]
+    [UINT64     ['8']]
+    // -----------------------------------------
+    // Floating point values
+    // -----------------------------------------
+    [REAL       ['4']]
+    [FLOAT      ['4']]
+    [LREAL      ['8']]
+    [DOUBLE     ['8']]
+    // -----------------------------------------
+    // Characters & Strings
+    // -----------------------------------------
+    [STRING     ['9']]
+]
+
 [enum uint 16 'ReservedIndexGroups'
     ['0xF000' ADSIGRP_SYMTAB]
     ['0xF001' ADSIGRP_SYMNAME]
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/AMSADSPlcDriver.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/AMSADSPlcDriver.java
index 4961373..55ee0dd 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/AMSADSPlcDriver.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/AMSADSPlcDriver.java
@@ -45,7 +45,17 @@ public class AMSADSPlcDriver extends GeneratedDriverBase<AmsPacket> {
 
     @Override
     public String getProtocolName() {
-        return "Beckhoff Twincat ADS";
+        return "Beckhoff TwinCat ADS";
+    }
+
+    @Override
+    protected boolean canRead() {
+        return true;
+    }
+
+    @Override
+    protected boolean canWrite() {
+        return true;
     }
 
     @Override
@@ -67,6 +77,7 @@ public class AMSADSPlcDriver extends GeneratedDriverBase<AmsPacket> {
     protected ProtocolStackConfigurer<AmsPacket> getStackConfigurer() {
         return SingleProtocolStackConfigurer.builder(AmsPacket.class, AmsPacketIO.class)
             .withProtocol(AdsProtocolLogic.class)
+            .littleEndian()
             .build();
     }
 
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/connection/AdsTcpPlcConnection.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/connection/AdsTcpPlcConnection.java
index b69eb94..58f7af2 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/connection/AdsTcpPlcConnection.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/connection/AdsTcpPlcConnection.java
@@ -22,11 +22,11 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.plc4x.java.amsads.field.AdsFieldHandler;
 import org.apache.plc4x.java.amsads.field.DirectAdsField;
 import org.apache.plc4x.java.amsads.field.SymbolicAdsField;
-import org.apache.plc4x.java.amsads.model.*;
 import org.apache.plc4x.java.amsads.attic.protocol.Plc4x2AdsProtocol;
-import org.apache.plc4x.java.amsads.protocol.util.LittleEndianDecoder;
+import org.apache.plc4x.java.amsads.attic.protocol.util.LittleEndianDecoder;
+import org.apache.plc4x.java.amsads.model.AdsSubscriptionHandle;
 import org.apache.plc4x.java.amsads.readwrite.*;
-import org.apache.plc4x.java.amsads.types.AdsDataType;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.*;
@@ -46,7 +46,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.math.BigInteger;
-import java.net.*;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.*;
@@ -200,7 +203,7 @@ public class AdsTcpPlcConnection extends AdsAbstractPlcConnection implements Plc
                 AdsAddDeviceNotificationRequest adsAddDeviceNotificationRequest = new AdsAddDeviceNotificationRequest(
                     indexGroup,
                     indexOffset,
-                    adsDataType.getTargetByteSize() * (long) numberOfElements,
+                    adsDataType.getNumBytes() * (long) numberOfElements,
                     transmissionMode,
                     cycleTime + 1,
                     cycleTime
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Ads2PayloadProtocol.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Ads2PayloadProtocol.java
index a20ff7c..b583aba 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Ads2PayloadProtocol.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Ads2PayloadProtocol.java
@@ -22,7 +22,7 @@ import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToMessageCodec;
-import org.apache.plc4x.java.amsads.protocol.exception.AdsException;
+import org.apache.plc4x.java.amsads.attic.protocol.exception.AdsException;
 import org.apache.plc4x.java.amsads.readwrite.AmsPacket;
 import org.apache.plc4x.java.amsads.readwrite.io.AmsPacketIO;
 import org.apache.plc4x.java.spi.generation.ParseException;
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Payload2SerialProtocol.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Payload2SerialProtocol.java
index 4092bbf..716b4f4 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Payload2SerialProtocol.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Payload2SerialProtocol.java
@@ -24,8 +24,8 @@ import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToMessageCodec;
 import io.netty.util.concurrent.ScheduledFuture;
 import org.apache.commons.lang3.ArrayUtils;
-import org.apache.plc4x.java.amsads.protocol.exception.AdsException;
-import org.apache.plc4x.java.amsads.protocol.util.DigestUtil;
+import org.apache.plc4x.java.amsads.attic.protocol.exception.AdsException;
+import org.apache.plc4x.java.amsads.attic.protocol.util.DigestUtil;
 import org.apache.plc4x.java.amsads.readwrite.AmsPacket;
 import org.apache.plc4x.java.amsads.readwrite.AmsSerialFrame;
 import org.apache.plc4x.java.amsads.readwrite.AmsTCPPacket;
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Payload2TcpProtocol.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Payload2TcpProtocol.java
index e719bf0..49f39b6 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Payload2TcpProtocol.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Payload2TcpProtocol.java
@@ -22,7 +22,7 @@ import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToMessageCodec;
-import org.apache.plc4x.java.amsads.protocol.exception.AdsException;
+import org.apache.plc4x.java.amsads.attic.protocol.exception.AdsException;
 import org.apache.plc4x.java.amsads.readwrite.AmsPacket;
 import org.apache.plc4x.java.amsads.readwrite.AmsTCPPacket;
 import org.apache.plc4x.java.amsads.readwrite.io.AmsPacketIO;
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Plc4x2AdsProtocol.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Plc4x2AdsProtocol.java
index 3d12b7a..8f9946e 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Plc4x2AdsProtocol.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/Plc4x2AdsProtocol.java
@@ -20,12 +20,12 @@ package org.apache.plc4x.java.amsads.attic.protocol;
 
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToMessageCodec;
-import org.apache.plc4x.java.amsads.types.AdsDataType;
 import org.apache.plc4x.java.amsads.field.AdsField;
 import org.apache.plc4x.java.amsads.field.DirectAdsField;
 import org.apache.plc4x.java.amsads.field.SymbolicAdsField;
-import org.apache.plc4x.java.amsads.protocol.exception.AdsException;
+import org.apache.plc4x.java.amsads.attic.protocol.exception.AdsException;
 import org.apache.plc4x.java.amsads.readwrite.*;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.amsads.readwrite.types.CommandId;
 import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.exceptions.PlcIoException;
@@ -53,8 +53,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import static org.apache.plc4x.java.amsads.protocol.util.LittleEndianDecoder.decodeData;
-import static org.apache.plc4x.java.amsads.protocol.util.LittleEndianEncoder.encodeData;
+import static org.apache.plc4x.java.amsads.attic.protocol.util.LittleEndianDecoder.decodeData;
 
 @Deprecated
 public class Plc4x2AdsProtocol extends MessageToMessageCodec<AmsPacket, PlcRequestContainer<InternalPlcRequest, InternalPlcResponse>> {
@@ -160,9 +159,9 @@ public class Plc4x2AdsProtocol extends MessageToMessageCodec<AmsPacket, PlcReque
             plcValues[0] = plcValue.getObject();
         }
 
-        byte[] bytes = encodeData(directAdsField.getAdsDataType(), plcValues);
+        byte[] bytes = null;//encodeData(directAdsField.getAdsDataType(), plcValues);
         int bytesToBeWritten = bytes.length;
-        int maxTheoreticalSize = directAdsField.getAdsDataType().getTargetByteSize() * directAdsField.getNumberOfElements();
+        int maxTheoreticalSize = directAdsField.getAdsDataType().getNumBytes() * directAdsField.getNumberOfElements();
         if (bytesToBeWritten > maxTheoreticalSize) {
             LOGGER.debug("Requested AdsDatatype {} is exceeded by number of bytes {}. Limit {}.", directAdsField.getAdsDataType(), bytesToBeWritten, maxTheoreticalSize);
             throw new PlcProtocolPayloadTooBigException("ADS", maxTheoreticalSize, bytesToBeWritten, plcValues);
@@ -198,8 +197,8 @@ public class Plc4x2AdsProtocol extends MessageToMessageCodec<AmsPacket, PlcReque
         long indexOffset = directAdsField.getIndexOffset();
         AdsDataType adsDataType = directAdsField.getAdsDataType();
         int numberOfElements = directAdsField.getNumberOfElements();
-        int readLength = adsDataType.getTargetByteSize() * numberOfElements;
-        AdsReadWriteRequest data = new AdsReadWriteRequest(indexGroup, indexOffset, readLength, new AdsReadRequest[0], new byte[0]);
+        int readLength = adsDataType.getNumBytes() * numberOfElements;
+        AdsReadWriteRequest data = new AdsReadWriteRequest(indexGroup, indexOffset, readLength, new AdsReadWriteRequest[0], new byte[0]);
         AmsPacket amsPacket = new AmsPacket(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, CommandId.ADS_READ, new State(false, false, false, false, false, false, true, false, false), 0, invokeId, data);
         LOGGER.debug("encoded read request {}", amsPacket);
         out.add(amsPacket);
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/exception/AdsException.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/exception/AdsException.java
similarity index 96%
rename from sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/exception/AdsException.java
rename to sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/exception/AdsException.java
index 909ecc1..249ca9f 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/exception/AdsException.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/exception/AdsException.java
@@ -16,7 +16,7 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.amsads.protocol.exception;
+package org.apache.plc4x.java.amsads.attic.protocol.exception;
 
 import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
 
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/exception/AdsProtocolOverflowException.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/exception/AdsProtocolOverflowException.java
similarity index 95%
rename from sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/exception/AdsProtocolOverflowException.java
rename to sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/exception/AdsProtocolOverflowException.java
index 023b467..d81bece 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/exception/AdsProtocolOverflowException.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/exception/AdsProtocolOverflowException.java
@@ -17,7 +17,7 @@
  under the License.
  */
 
-package org.apache.plc4x.java.amsads.protocol.exception;
+package org.apache.plc4x.java.amsads.attic.protocol.exception;
 
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/DigestUtil.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/DigestUtil.java
similarity index 96%
rename from sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/DigestUtil.java
rename to sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/DigestUtil.java
index b3e9a62..753b9ee 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/DigestUtil.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/DigestUtil.java
@@ -16,7 +16,7 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.amsads.protocol.util;
+package org.apache.plc4x.java.amsads.attic.protocol.util;
 
 import com.github.snksoft.crc.CRC;
 
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/LittleEndianDecoder.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/LittleEndianDecoder.java
similarity index 99%
rename from sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/LittleEndianDecoder.java
rename to sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/LittleEndianDecoder.java
index cd3d554..cdff936 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/LittleEndianDecoder.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/LittleEndianDecoder.java
@@ -16,13 +16,13 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.amsads.protocol.util;
+package org.apache.plc4x.java.amsads.attic.protocol.util;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.NotImplementedException;
-import org.apache.plc4x.java.amsads.types.AdsDataType;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcUnsupportedDataTypeException;
 import org.apache.plc4x.java.api.value.*;
 
@@ -410,7 +410,7 @@ public class LittleEndianDecoder {
                     return new PlcList(values);
                 }
             }
-            case TIME: {
+/*            case TIME: {
                 LinkedList<Long> values = new LinkedList<>();
                 while (wrappedBuffer.isReadable()) {
                     long aByte = wrappedBuffer.readUnsignedIntLE();
@@ -480,7 +480,7 @@ public class LittleEndianDecoder {
             case SUB_RANGE_DATA_TYPE: {
                 throw new NotImplementedException("not implemented yet " + adsDataType);
             }
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcUnsupportedDataTypeException("Unsupported adsDataType " + adsDataType);
         }
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/LittleEndianEncoder.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/LittleEndianEncoder.java
similarity index 89%
rename from sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/LittleEndianEncoder.java
rename to sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/LittleEndianEncoder.java
index 620a551..d3ccad4 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/LittleEndianEncoder.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/LittleEndianEncoder.java
@@ -16,10 +16,10 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.amsads.protocol.util;
+package org.apache.plc4x.java.amsads.attic.protocol.util;
 
 import org.apache.commons.lang3.ArrayUtils;
-import org.apache.plc4x.java.amsads.types.AdsDataType;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
 import org.apache.plc4x.java.api.exceptions.PlcProtocolPayloadTooBigException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
@@ -110,8 +110,8 @@ public class LittleEndianEncoder {
         // TODO: add boundchecks and add optional extension
         return byteArrayStream
             .peek(bytes -> {
-                if (bytes.length > adsDataType.getTargetByteSize()) {
-                    throw new PlcRuntimeException(new PlcProtocolPayloadTooBigException("ads", adsDataType.getTargetByteSize(), bytes.length, bytes));
+                if (bytes.length > adsDataType.getNumBytes()) {
+                    throw new PlcRuntimeException(new PlcProtocolPayloadTooBigException("ads", adsDataType.getNumBytes(), bytes.length, bytes));
                 }
             });
     }
@@ -120,8 +120,8 @@ public class LittleEndianEncoder {
         // TODO: add boundchecks and add optional extension
         return byteArrayStream.map(ArrayUtils::toPrimitive)
             .peek(bytes -> {
-                if (bytes.length > adsDataType.getTargetByteSize()) {
-                    throw new PlcRuntimeException(new PlcProtocolPayloadTooBigException("ads", adsDataType.getTargetByteSize(), bytes.length, bytes));
+                if (bytes.length > adsDataType.getNumBytes()) {
+                    throw new PlcRuntimeException(new PlcProtocolPayloadTooBigException("ads", adsDataType.getNumBytes(), bytes.length, bytes));
                 }
             });
     }
@@ -137,7 +137,7 @@ public class LittleEndianEncoder {
                 (byte) ((intValue & 0x00ff0000) >> 16),
                 (byte) ((intValue & 0xff000000) >> 24),
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeDouble(AdsDataType adsDataType, Stream<Double> doubleStream) {
@@ -155,7 +155,7 @@ public class LittleEndianEncoder {
                 (byte) ((longValue & 0x00ff0000_00000000L) >> 48),
                 (byte) ((longValue & 0xff000000_00000000L) >> 56),
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeInteger(AdsDataType adsDataType, Stream<Integer> integerStream) {
@@ -167,7 +167,7 @@ public class LittleEndianEncoder {
                 (byte) ((intValue & 0x00ff0000) >> 16),
                 (byte) ((intValue & 0xff000000) >> 24),
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeLong(AdsDataType adsDataType, Stream<Long> longStream) {
@@ -183,7 +183,7 @@ public class LittleEndianEncoder {
                 (byte) ((longValue & 0x00ff0000_00000000L) >> 48),
                 (byte) ((longValue & 0xff000000_00000000L) >> 56),
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeBigInteger(AdsDataType adsDataType, Stream<BigInteger> bigIntegerStream) {
@@ -192,15 +192,15 @@ public class LittleEndianEncoder {
             .map(bigIntValue -> {
                 byte[] bytes = bigIntValue.toByteArray();
                 if (bytes.length > 1 && bytes[0] == 0x0) {
-                    byte[] subArray = Arrays.copyOf(ArrayUtils.subarray(bytes, 1, bytes.length), adsDataType.getTargetByteSize());
+                    byte[] subArray = Arrays.copyOf(ArrayUtils.subarray(bytes, 1, bytes.length), adsDataType.getNumBytes());
                     ArrayUtils.reverse(subArray);
                     return subArray;
                 } else {
-                    ArrayUtils.reverse(Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+                    ArrayUtils.reverse(Arrays.copyOf(bytes, adsDataType.getNumBytes()));
                     return bytes;
                 }
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeLocalTime(AdsDataType adsDataType, Stream<LocalTime> localTimeStream) {
@@ -214,7 +214,7 @@ public class LittleEndianEncoder {
                 (byte) ((time & 0x00000000_00ff0000L) >> 16),
                 (byte) ((time & 0x00000000_ff000000L) >> 24),
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeLocalDate(AdsDataType adsDataType, Stream<LocalDate> localDateStream) {
@@ -230,7 +230,7 @@ public class LittleEndianEncoder {
                 (byte) ((time & 0x00000000_00ff0000L) >> 16),
                 (byte) ((time & 0x00000000_ff000000L) >> 24),
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeLocalDateTime(AdsDataType adsDataType, Stream<LocalDateTime> localDateTimeStream) {
@@ -250,7 +250,7 @@ public class LittleEndianEncoder {
                 (byte) ((time & 0x00ff0000_00000000L) >> 48),
                 (byte) ((time & 0xff000000_00000000L) >> 56),
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
 
@@ -261,25 +261,25 @@ public class LittleEndianEncoder {
                 (byte) (shortValue & 0x00ff),
                 (byte) ((shortValue & 0xff00) >> 8),
             })
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeByte(AdsDataType adsDataType, Stream<Byte> byteStream) {
         return byteStream
             .peek(value -> checkBound(adsDataType, value))
             .map(aByte -> new byte[]{aByte})
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static Stream<byte[]> encodeBoolean(AdsDataType adsDataType, Stream<Boolean> booleanStream) {
         return booleanStream
             .map(booleanValue -> new byte[]{booleanValue ? (byte) 0x01 : (byte) 0x00})
-            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getTargetByteSize()));
+            .map(bytes -> Arrays.copyOf(bytes, adsDataType.getNumBytes()));
     }
 
     private static void checkBound(AdsDataType adsDataType, double other) {
-        if (!adsDataType.withinBounds(other)) {
+        /*if (!adsDataType.withinBounds(other)) {
             throw new PlcRuntimeException(other + " not within bounds of " + adsDataType);
-        }
+        }*/
     }
 }
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/SingleMessageRateLimiter.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/SingleMessageRateLimiter.java
similarity index 98%
rename from sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/SingleMessageRateLimiter.java
rename to sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/SingleMessageRateLimiter.java
index 90e8ecd..79de06c 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/SingleMessageRateLimiter.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/protocol/util/SingleMessageRateLimiter.java
@@ -16,7 +16,7 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.amsads.protocol.util;
+package org.apache.plc4x.java.amsads.attic.protocol.util;
 
 import io.netty.channel.ChannelDuplexHandler;
 import io.netty.channel.ChannelHandlerContext;
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/types/AdsDataType.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/types/AdsDataType.java
similarity index 98%
rename from sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/types/AdsDataType.java
rename to sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/types/AdsDataType.java
index 07a4f23..8b2a99e 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/types/AdsDataType.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/attic/types/AdsDataType.java
@@ -16,7 +16,7 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.amsads.types;
+package org.apache.plc4x.java.amsads.attic.types;
 
 import org.apache.commons.lang3.ArrayUtils;
 
@@ -37,8 +37,9 @@ import java.util.stream.IntStream;
  * @see <a href="https://infosys.beckhoff.com/english.php?content=../content/1033/tcplccontrol/html/tcplcctrl_plc_data_types_overview.htm&id">TwinCAT PLC Control: Data Types</a>
  */
 public enum AdsDataType {
-    // TODO: maybe this are just types for the plc ide and can be removed
-    // https://infosys.beckhoff.com/english.php?content=../content/1033/tcsystemmanager/basics/TcSysMgr_DatatypeComparison.htm&id=
+    // System-Manager versions of the IEC61131-3 datatypes.
+    // See: https://infosys.beckhoff.com/english.php?content=../content/1033/tcsystemmanager/basics/TcSysMgr_DatatypeComparison.htm&id=
+    // For mapping
     BIT(0, 1, 8),
     BIT8((short) 0x00, (short) 0xFF, 8),
     BITARR8((short) 0x00, (short) 0xFF, 8),
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/configuration/AdsConfiguration.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/configuration/AdsConfiguration.java
index 8919d38..5a01e54 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/configuration/AdsConfiguration.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/configuration/AdsConfiguration.java
@@ -141,7 +141,7 @@ public class AdsConfiguration implements Configuration, TcpTransportConfiguratio
         }
         String[] split = address.split("\\.");
         short[] shorts = ArrayUtils.toPrimitive(Stream.of(split).map(Integer::parseInt).map(Integer::shortValue).toArray(Short[]::new));
-        return new AmsNetId(shorts[5], shorts[4], shorts[3], shorts[2], shorts[1], shorts[0]);
+        return new AmsNetId(shorts[0], shorts[1], shorts[2], shorts[3], shorts[4], shorts[5]);
     }
 
 }
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/AdsField.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/AdsField.java
index 4bbf0e4..9c16181 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/AdsField.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/AdsField.java
@@ -18,7 +18,7 @@
  */
 package org.apache.plc4x.java.amsads.field;
 
-import org.apache.plc4x.java.amsads.types.AdsDataType;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.api.model.PlcField;
 
 @FunctionalInterface
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/AdsFieldHandler.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/AdsFieldHandler.java
index ce81453..21ffa4a 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/AdsFieldHandler.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/AdsFieldHandler.java
@@ -18,7 +18,7 @@
  */
 package org.apache.plc4x.java.amsads.field;
 
-import org.apache.plc4x.java.amsads.types.AdsDataType;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.model.PlcField;
@@ -87,7 +87,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -98,7 +98,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeBoolean(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -138,7 +138,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -149,7 +149,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeInteger(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -189,7 +189,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -200,7 +200,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeInteger(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -240,7 +240,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -251,7 +251,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeInteger(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -291,7 +291,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -302,7 +302,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeInteger(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -342,7 +342,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -353,7 +353,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeInteger(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -393,7 +393,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -404,7 +404,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeFloatingPoint(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -444,7 +444,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -455,7 +455,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeFloatingPoint(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -495,7 +495,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -506,7 +506,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalEncodeString(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -546,7 +546,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -557,7 +557,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalTimeTemporal(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -597,7 +597,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -608,7 +608,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalDateTemporal(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -648,7 +648,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case REAL:
             case LREAL:
             case STRING:
-            case TIME:
+            /*case TIME:
             case TIME_OF_DAY:
             case DATE:
             case DATE_AND_TIME:
@@ -659,7 +659,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             case ALIAS:
             case SUB_RANGE_DATA_TYPE:
                 return internalDateTimeTemporal(field, values);
-            case UNKNOWN:
+            case UNKNOWN:*/
             default:
                 throw new PlcRuntimeException("Invalid encoder for type " + adsField.getAdsDataType().name());
         }
@@ -777,9 +777,9 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
                 }
 
                 Number value = (Number) values[i];
-                if (!adsField.getAdsDataType().withinBounds(value.doubleValue())) {
+/*                if (!adsField.getAdsDataType().withinBounds(value.doubleValue())) {
                     throw new IllegalArgumentException("Value " + values[i] + " ist not within bounds of " + adsField.getAdsDataType());
-                }
+                }*/
                 longValues[i] = value.longValue();
             }
             if(longValues.length == 1) {
@@ -801,9 +801,9 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
                         "Value of type " + values[i].getClass().getName() +
                             " is not assignable to " + adsField.getAdsDataType().name() + " fields.");
                 }
-                if (!adsField.getAdsDataType().withinBounds(value.doubleValue())) {
+/*                if (!adsField.getAdsDataType().withinBounds(value.doubleValue())) {
                     throw new IllegalArgumentException("Value " + values[i] + " ist not within bounds of " + adsField.getAdsDataType());
-                }
+                }*/
                 bigIntegerValues[i] = value.toBigInteger();
             }
             if(bigIntegerValues.length == 1) {
@@ -843,9 +843,9 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
                 }
 
                 Number value = (Number) values[i];
-                if (!adsDataType.withinBounds(value.doubleValue())) {
+/*                if (!adsDataType.withinBounds(value.doubleValue())) {
                     throw new IllegalArgumentException("Value " + values[i] + " ist not within bounds of " + adsDataType);
-                }
+                }*/
             }
             if(floatingPointValues.length == 1) {
                 return new PlcDouble(floatingPointValues[0]);
@@ -857,11 +857,11 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             for (int i = 0; i < values.length; i++) {
                 if (values[i] instanceof Double) {
                     Double aDouble = (Double) values[i];
-                    if (!adsDataType.withinBounds(aDouble)) {
+/*                    if (!adsDataType.withinBounds(aDouble)) {
                         throw new IllegalArgumentException(
                             "Value of " + aDouble + " exceeds allowed minimum for type "
                                 + adsDataType.name() + " (min " + adsDataType.getLowerBound() + "/max +" + adsDataType.getUpperBound() + ")");
-                    }
+                    }*/
                     floatingPointValues[i] = aDouble.floatValue();
                 } else if (values[i] instanceof Float) {
                     floatingPointValues[i] = (Float) values[i];
@@ -872,9 +872,9 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
                 }
 
                 Number value = (Number) values[i];
-                if (!adsDataType.withinBounds(value.doubleValue())) {
+/*                if (!adsDataType.withinBounds(value.doubleValue())) {
                     throw new IllegalArgumentException("Value " + values[i] + " ist not within bounds of " + adsDataType);
-                }
+                }*/
             }
             if(floatingPointValues.length == 1) {
                 return new PlcFloat(floatingPointValues[0]);
@@ -886,7 +886,7 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
 
     private PlcValue internalEncodeString(PlcField field, Object[] values) {
         AdsField adsField = (AdsField) field;
-        Number maxLength = adsField.getAdsDataType().getUpperBound();
+//        Number maxLength = adsField.getAdsDataType().getUpperBound();
         //boolean encoding16Bit;
         switch (adsField.getAdsDataType()) {
             case STRING:
@@ -900,11 +900,11 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
         for (Object value : values) {
             if (value instanceof String) {
                 String stringValue = (String) value;
-                if (stringValue.length() > maxLength.intValue()) {
+/*                if (stringValue.length() > maxLength.intValue()) {
                     throw new IllegalArgumentException(
                         "String length " + stringValue.length() + " exceeds allowed maximum for type "
                             + adsField.getAdsDataType().name() + " (max " + maxLength + ")");
-                }
+                }*/
                 stringValues.add(stringValue);
             }
             // All other types just translate to max one String character.
@@ -970,16 +970,16 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
     private PlcValue internalTimeTemporal(PlcField field, Object[] values) {
         AdsField adsField = (AdsField) field;
         switch (adsField.getAdsDataType()) {
-            case TIME:
+/*            case TIME:
             case DATE:
             case DATE_AND_TIME:
-                break;
+                break;*/
             default:
                 throw new IllegalArgumentException(
                     "Cannot assign temporal values to " + adsField.getAdsDataType().name() + " fields.");
         }
         // TODO: support other types
-        List<LocalTime> localTimeValues = Arrays.stream(values)
+        /*List<LocalTime> localTimeValues = Arrays.stream(values)
             .filter(LocalTime.class::isInstance)
             .map(LocalTime.class::cast)
             .collect(Collectors.toList());
@@ -987,22 +987,22 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             return new PlcTime(localTimeValues.get(0));
         } else {
             return new PlcList(localTimeValues);
-        }
+        }*/
     }
 
     private PlcValue internalDateTemporal(PlcField field, Object[] values) {
         AdsField adsField = (AdsField) field;
         switch (adsField.getAdsDataType()) {
-            case TIME:
+/*            case TIME:
             case DATE:
             case DATE_AND_TIME:
-                break;
+                break;*/
             default:
                 throw new IllegalArgumentException(
                     "Cannot assign temporal values to " + adsField.getAdsDataType().name() + " fields.");
         }
         // TODO: support other types
-        List<LocalDate> localDateValues = Arrays.stream(values)
+        /*List<LocalDate> localDateValues = Arrays.stream(values)
             .filter(LocalDate.class::isInstance)
             .map(LocalDate.class::cast)
             .collect(Collectors.toList());
@@ -1010,14 +1010,14 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             return new PlcDate(localDateValues.get(0));
         } else {
             return new PlcList(localDateValues);
-        }
+        }*/
     }
 
     private PlcValue internalDateTimeTemporal(PlcField field, Object[] values) {
         AdsField adsField = (AdsField) field;
         Class<? extends PlcValue> fieldType;
         switch (adsField.getAdsDataType()) {
-            case TIME:
+/*            case TIME:
                 fieldType = PlcTime.class;
                 break;
             case DATE:
@@ -1025,12 +1025,12 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
                 break;
             case DATE_AND_TIME:
                 fieldType = PlcDateTime.class;
-                break;
+                break;*/
             default:
                 throw new IllegalArgumentException(
                     "Cannot assign temporal values to " + adsField.getAdsDataType().name() + " fields.");
         }
-        if(values.length == 1) {
+        /*if(values.length == 1) {
             // TODO: add type conversion
             if (fieldType == PlcTime.class) {
                 return new PlcTime((LocalTime) values[0]);
@@ -1041,7 +1041,6 @@ public class AdsFieldHandler extends DefaultPlcFieldHandler {
             }
         } else {
             return new PlcList(Arrays.asList(values));
-        }
-
+        }*/
     }
 }
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/DirectAdsField.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/DirectAdsField.java
index 475a97c..0db8d34 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/DirectAdsField.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/DirectAdsField.java
@@ -18,8 +18,7 @@
  */
 package org.apache.plc4x.java.amsads.field;
 
-//import org.apache.plc4x.java.ads.api.util.ByteValue;
-import org.apache.plc4x.java.amsads.types.AdsDataType;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 
 import java.util.Objects;
@@ -42,7 +41,7 @@ public class DirectAdsField implements AdsField {
 
     private final int numberOfElements;
 
-    private DirectAdsField(long indexGroup, long indexOffset, AdsDataType adsDataType, Integer numberOfElements) {
+    public DirectAdsField(long indexGroup, long indexOffset, AdsDataType adsDataType, Integer numberOfElements) {
         //ByteValue.checkUnsignedBounds(indexGroup, 4);
         this.indexGroup = indexGroup;
         //ByteValue.checkUnsignedBounds(indexOffset, 4);
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/SymbolicAdsField.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/SymbolicAdsField.java
index d6ab98e..c4edfce 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/SymbolicAdsField.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/field/SymbolicAdsField.java
@@ -18,7 +18,7 @@
  */
 package org.apache.plc4x.java.amsads.field;
 
-import org.apache.plc4x.java.amsads.types.AdsDataType;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 
 import java.util.Objects;
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/model/AdsSubscriptionHandle.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/model/AdsSubscriptionHandle.java
index 883ce25..1a0f60e 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/model/AdsSubscriptionHandle.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/model/AdsSubscriptionHandle.java
@@ -18,7 +18,7 @@ under the License.
 */
 package org.apache.plc4x.java.amsads.model;
 
-import org.apache.plc4x.java.amsads.types.AdsDataType;
+import org.apache.plc4x.java.amsads.readwrite.types.AdsDataType;
 import org.apache.plc4x.java.spi.messages.PlcSubscriber;
 import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle;
 
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/AdsProtocolLogic.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/AdsProtocolLogic.java
index 8ecba8f..ad67193 100644
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/AdsProtocolLogic.java
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/AdsProtocolLogic.java
@@ -22,6 +22,7 @@ import org.apache.plc4x.java.amsads.configuration.AdsConfiguration;
 import org.apache.plc4x.java.amsads.field.DirectAdsField;
 import org.apache.plc4x.java.amsads.field.SymbolicAdsField;
 import org.apache.plc4x.java.amsads.readwrite.*;
+import org.apache.plc4x.java.amsads.readwrite.io.DataItemIO;
 import org.apache.plc4x.java.amsads.readwrite.types.CommandId;
 import org.apache.plc4x.java.amsads.readwrite.types.ReservedIndexGroups;
 import org.apache.plc4x.java.api.messages.PlcReadRequest;
@@ -29,32 +30,42 @@ 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.*;
 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.messages.InternalPlcReadRequest;
+import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 public class AdsProtocolLogic extends Plc4xProtocolBase<AmsPacket> implements HasConfiguration<AdsConfiguration> {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(AdsProtocolLogic.class);
+
     private AdsConfiguration configuration;
     public static final State DEFAULT_COMMAND_STATE = new State(
-        false, false, false, false, false, false, true, false, false);
+        false, false, false, false, false, true, false, false, false);
 
     private ConversationContext<AmsPacket> adsDriverContext;
-    private static final AtomicLong invokeIdGenerator = new AtomicLong(0);
+    private final AtomicLong invokeIdGenerator = new AtomicLong(1);
     private RequestTransactionManager tm;
 
     private ConcurrentHashMap<SymbolicAdsField, DirectAdsField> symbolicFieldMapping;
-    private ConcurrentHashMap<SymbolicAdsField, CompletableFuture<DirectAdsField>> pendingResolutionRequests;
+    private ConcurrentHashMap<SymbolicAdsField, CompletableFuture<Void>> pendingResolutionRequests;
 
     public AdsProtocolLogic() {
         symbolicFieldMapping = new ConcurrentHashMap<>();
@@ -104,7 +115,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsPacket> implements Ha
         final List<DirectAdsField> directAdsFields = getDirectAddresses(readRequest.getFields());
 
         // Depending on the number of fields, use a single item request or a sum-request
-        if(directAdsFields.size() == 1) {
+        if (directAdsFields.size() == 1) {
             // Do a normal (single item) ADS Read Request
             return singleRead(readRequest, directAdsFields.get(0));
         } else {
@@ -116,7 +127,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsPacket> implements Ha
     protected CompletableFuture<PlcReadResponse> singleRead(PlcReadRequest readRequest, DirectAdsField directAdsField) {
         CompletableFuture<PlcReadResponse> future = new CompletableFuture<>();
 
-        int size = directAdsField.getAdsDataType().getTargetByteSize() * directAdsField.getNumberOfElements();
+        int size = directAdsField.getAdsDataType().getNumBytes() * directAdsField.getNumberOfElements();
         AdsData adsData = new AdsReadRequest(directAdsField.getIndexGroup(), directAdsField.getIndexOffset(), size);
         AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
             configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
@@ -146,17 +157,16 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsPacket> implements Ha
         // Calculate the size of all fields together.
         int size = 0;
 
-        // TODO: Add the items ...
-        List<AdsReadRequest> items = new ArrayList<>(directAdsFields.size());
-
         // With multi-requests, the index-group is fixed and the index offset indicates the number of elements.
         AdsData adsData = new AdsReadWriteRequest(
             ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.getValue(), directAdsFields.size(), size,
-            items.toArray(new AdsReadRequest[0]), new byte[0]);
+            directAdsFields.stream().map(directAdsField -> new AdsReadWriteRequest(
+                directAdsField.getIndexGroup(), directAdsField.getIndexOffset(), directAdsField.getNumberOfElements(),
+                new AdsReadWriteRequest[0], new byte[0])).toArray(AdsReadWriteRequest[]::new), new byte[0]);
 
         AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
             configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
-            CommandId.ADS_READ, DEFAULT_COMMAND_STATE, 0, invokeIdGenerator.getAndIncrement(), adsData);
+            CommandId.ADS_READ_WRITE, DEFAULT_COMMAND_STATE, 0, invokeIdGenerator.getAndIncrement(), adsData);
 
         // Start a new request-transaction (Is ended in the response-handler)
         RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
@@ -177,9 +187,78 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsPacket> implements Ha
     }
 
     protected PlcReadResponse convertToPlc4xReadResponse(PlcReadRequest readRequest, AdsData adsData) {
+        ReadBuffer readBuffer = null;
+        Map<String, PlcResponseCode> responseCodes = new HashMap<>();
+        if (adsData instanceof AdsReadResponse) {
+            AdsReadResponse adsReadResponse = (AdsReadResponse) adsData;
+            readBuffer = new ReadBuffer(adsReadResponse.getData());
+            responseCodes.put(readRequest.getFieldNames().stream().findFirst().orElse(""),
+                parsePlcResponseCode(adsReadResponse.getResult()));
+        } else if (adsData instanceof AdsReadWriteResponse) {
+            AdsReadWriteResponse adsReadWriteResponse = (AdsReadWriteResponse) adsData;
+            readBuffer = new ReadBuffer(adsReadWriteResponse.getData());
+            // When parsing a multi-item response, the error codes of each items come
+            // in sequence and then come the values.
+            for (String fieldName : readRequest.getFieldNames()) {
+                try {
+                    final long result = readBuffer.readUnsignedLong(32);
+                    responseCodes.put(fieldName, parsePlcResponseCode(result));
+                } catch (ParseException e) {
+                    responseCodes.put(fieldName, PlcResponseCode.INTERNAL_ERROR);
+                }
+            }
+        }
+        if(readBuffer != null) {
+            Map<String, ResponseItem<PlcValue>> values = new HashMap<>();
+            for (String fieldName : readRequest.getFieldNames()) {
+                DirectAdsField directAdsField = (DirectAdsField) readRequest.getField(fieldName);
+                // If the response-code was anything but OK, we don't need to parse the payload.
+                if(responseCodes.get(fieldName) != PlcResponseCode.OK) {
+                    values.put(fieldName, new ResponseItem<>(responseCodes.get(fieldName), null));
+                }
+                // If the response-code was ok, parse the data returned.
+                else {
+                    values.put(fieldName, parsePlcValue(directAdsField, readBuffer));
+                }
+            }
+            return new DefaultPlcReadResponse((InternalPlcReadRequest) readRequest, values);
+        }
         return null;
     }
 
+    private PlcResponseCode parsePlcResponseCode(long adsResult) {
+            if (adsResult == 0L) {
+                return PlcResponseCode.OK;
+            } else {
+                // TODO: Implement this a little more ...
+                return PlcResponseCode.INTERNAL_ERROR;
+            }
+    }
+
+    private ResponseItem<PlcValue> parsePlcValue(DirectAdsField field, ReadBuffer readBuffer) {
+        try {
+            if (field.getNumberOfElements() == 1) {
+                return new ResponseItem<>(PlcResponseCode.OK,
+                    DataItemIO.staticParse(readBuffer, field.getAdsDataType()));
+            } else {
+                // Fetch all
+                final PlcValue[] resultItems = IntStream.range(0, field.getNumberOfElements()).mapToObj(i -> {
+                    try {
+                        return DataItemIO.staticParse(readBuffer, field.getAdsDataType());
+                    } catch (ParseException e) {
+                        LOGGER.warn("Error parsing field item of type: '{}' (at position {}})", field.getAdsDataType(), i, e);
+                    }
+                    return null;
+                }).toArray(PlcValue[]::new);
+                return new ResponseItem<>(PlcResponseCode.OK, PlcValues.of(resultItems));
+            }
+        } catch (ParseException e) {
+            LOGGER.warn(String.format("Error parsing field item of type: '%s'", field.getAdsDataType()), e);
+            return new ResponseItem<>(PlcResponseCode.INTERNAL_ERROR, null);
+        }
+    }
+
+
     @Override
     public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
         return super.write(writeRequest);
@@ -199,34 +278,43 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsPacket> implements Ha
 
         // Find out for which of these symbolic addresses no resolution has been initiated.
         final List<SymbolicAdsField> symbolicFieldsNeedingResolution = referencedSymbolicFields.stream()
-            .filter(symbolicAdsField -> symbolicFieldMapping.containsKey(symbolicAdsField))
+            .filter(symbolicAdsField -> !symbolicFieldMapping.containsKey(symbolicAdsField))
             .collect(Collectors.toList());
 
         // If there are unresolved symbolic addresses, initiate the resolution
-        if(!symbolicFieldsNeedingResolution.isEmpty()) {
-            // If a previous request initiated a resolution request, join that resolutions future instead.
-            // If not, initiate a new resolution request.
-            final CompletableFuture<Void> resolutionComplete =
-                CompletableFuture.allOf(symbolicFieldsNeedingResolution.stream().map(symbolicAdsField -> {
-                    if (pendingResolutionRequests.containsKey(symbolicAdsField)) {
-                        return pendingResolutionRequests.get(symbolicAdsField);
-                    } else {
-                        // Initiate a new resolution-request and add that to the pending resolution requests.
-                        CompletableFuture<DirectAdsField> internalResolutionFuture =
-                            resolveSymbolicAddress(symbolicAdsField);
-                        // Create a second future which will be completed as soon as the resolution result has
-                        // been added to the map.
-                        CompletableFuture<DirectAdsField> resolutionFuture = new CompletableFuture<>();
-                        // Make sure the resolved address is added to the mapping.
-                        internalResolutionFuture.thenAccept(directAdsField -> {
-                            symbolicFieldMapping.put(symbolicAdsField, directAdsField);
-                            // Now we can tell the other waiting processes about the result.
-                            resolutionFuture.complete(directAdsField);
-                        });
-                        return resolutionFuture;
+        if (!symbolicFieldsNeedingResolution.isEmpty()) {
+            // Get a list of symbolic addresses for which no resolution request has been sent yet
+            // (A parallel request initiated a bit earlier might have already initiated a resolution
+            // which has not yet been completed)
+            final List<SymbolicAdsField> requiredResolutionFields =
+                symbolicFieldsNeedingResolution.stream().filter(symbolicAdsField ->
+                    !pendingResolutionRequests.containsKey(symbolicAdsField)).collect(Collectors.toList());
+            // If there are fields for which no resolution request has been sent yet,
+            // send a request.
+            if (!requiredResolutionFields.isEmpty()) {
+                CompletableFuture<Void> resolutionFuture;
+                // Create a future which will be completed as soon as the
+                // resolution result has been added to the map.
+                if (requiredResolutionFields.size() == 1) {
+                    SymbolicAdsField symbolicAdsField = requiredResolutionFields.get(0);
+                    resolutionFuture = resolveSymbolicAddress(requiredResolutionFields.get(0));
+                    pendingResolutionRequests.put(symbolicAdsField, resolutionFuture);
+                } else {
+                    resolutionFuture = resolveSymbolicAddresses(requiredResolutionFields);
+                    for (SymbolicAdsField symbolicAdsField : requiredResolutionFields) {
+                        pendingResolutionRequests.put(symbolicAdsField, resolutionFuture);
                     }
-                }).toArray(CompletableFuture[]::new));
-            // Wait for the resolution to finish.
+                }
+            }
+
+            // Create a global future which is completed as soon as all sub-futures for this request are completed.
+            final CompletableFuture<Void> resolutionComplete =
+                CompletableFuture.allOf(symbolicFieldsNeedingResolution.stream()
+                    .map(symbolicAdsField -> pendingResolutionRequests.get(symbolicAdsField))
+                    .toArray(CompletableFuture[]::new));
+
+            // BLOCKING: Wait for the resolution to finish.
+            // TODO: Make this asynchronous ...
             try {
                 resolutionComplete.get(configuration.getTimeoutSymbolicAddressResolution(), TimeUnit.MILLISECONDS);
             } catch (TimeoutException e) {
@@ -238,7 +326,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsPacket> implements Ha
 
         // So here all fields should be resolved so we can continue normally.
         return fields.stream().map(plcField -> {
-            if(plcField instanceof SymbolicAdsField) {
+            if (plcField instanceof SymbolicAdsField) {
                 return symbolicFieldMapping.get(plcField);
             } else {
                 return (DirectAdsField) plcField;
@@ -246,8 +334,114 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsPacket> implements Ha
         }).collect(Collectors.toList());
     }
 
-    protected CompletableFuture<DirectAdsField> resolveSymbolicAddress(SymbolicAdsField symbolicAdsField) {
-        return null;
+    protected CompletableFuture<Void> resolveSymbolicAddress(SymbolicAdsField symbolicAdsField) {
+        CompletableFuture<Void> future = new CompletableFuture<>();
+
+        // TODO: Instead of using 4 we need the size of the expected response
+        AdsData adsData = new AdsReadWriteRequest(ReservedIndexGroups.ADSIGRP_SYM_HNDBYNAME.getValue(), 0, 4, null,
+            getNullByteTerminatedArray(symbolicAdsField.getSymbolicField()));
+        AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+            configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
+            CommandId.ADS_READ, DEFAULT_COMMAND_STATE, 0, invokeIdGenerator.getAndIncrement(), adsData);
+
+        // Start a new request-transaction (Is ended in the response-handler)
+        RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+        transaction.submit(() -> context.sendRequest(amsPacket)
+            .expectResponse(AmsPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+            .onTimeout(future::completeExceptionally)
+            .onError((p, e) -> future.completeExceptionally(e))
+            .check(responseAmsPacket -> responseAmsPacket.getInvokeId() == amsPacket.getInvokeId())
+            .unwrap(AmsPacket::getData)
+            .check(adsDataResponse -> adsDataResponse instanceof AdsReadWriteResponse)
+            .unwrap(adsDataResponse -> (AdsReadWriteResponse) adsDataResponse)
+            .handle(responseAdsData -> {
+                ReadBuffer readBuffer = new ReadBuffer(responseAdsData.getData());
+                try {
+                    // This should be 0 in the success case.
+                    long returnCode = readBuffer.readLong(32);
+                    // This is always 4
+                    long itemLength = readBuffer.readLong(32);
+                    // Get the handle from the response.
+                    long handle = readBuffer.readLong(32);
+                    if (returnCode == 0) {
+                        DirectAdsField directAdsField = new DirectAdsField(ReservedIndexGroups.ADSIGRP_SYM_VALBYHND.getValue(),
+                            handle, symbolicAdsField.getAdsDataType(), symbolicAdsField.getNumberOfElements());
+                        symbolicFieldMapping.put(symbolicAdsField, directAdsField);
+                        future.complete(null);
+                    } else {
+                        // TODO: Handle the case of unsuccessful resolution ..
+                    }
+                } catch (ParseException e) {
+                    e.printStackTrace();
+                }
+            }));
+        return future;
+    }
+
+    protected CompletableFuture<Void> resolveSymbolicAddresses(List<SymbolicAdsField> symbolicAdsFields) {
+        CompletableFuture<Void> future = new CompletableFuture<>();
+
+        // TODO: Instead of using 4 we need the size of the expected response
+        AdsData adsData = new AdsReadWriteRequest(ReservedIndexGroups.ADSIGRP_MULTIPLE_GET_HANDLE.getValue(),
+            symbolicAdsFields.size(), 4, symbolicAdsFields.stream().map(symbolicAdsField ->
+            new AdsReadWriteRequest(ReservedIndexGroups.ADSIGRP_SYM_HNDBYNAME.getValue(), 0, 4, null,
+                getNullByteTerminatedArray(symbolicAdsField.getSymbolicField()))).toArray(AdsReadWriteRequest[]::new), null);
+        AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+            configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
+            CommandId.ADS_READ_WRITE, DEFAULT_COMMAND_STATE, 0, invokeIdGenerator.getAndIncrement(), adsData);
+
+        // Start a new request-transaction (Is ended in the response-handler)
+        RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+        transaction.submit(() -> context.sendRequest(amsPacket)
+            .expectResponse(AmsPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+            .onTimeout(future::completeExceptionally)
+            .onError((p, e) -> future.completeExceptionally(e))
+            .check(responseAmsPacket -> responseAmsPacket.getInvokeId() == amsPacket.getInvokeId())
+            .unwrap(AmsPacket::getData)
+            .check(adsDataResponse -> adsDataResponse instanceof AdsReadWriteResponse)
+            .unwrap(adsDataResponse -> (AdsReadWriteResponse) adsDataResponse)
+            .handle(responseAdsData -> {
+                ReadBuffer readBuffer = new ReadBuffer(responseAdsData.getData());
+                Map<SymbolicAdsField, Long> returnCodes = new HashMap<>();
+                symbolicAdsFields.forEach(symbolicAdsField -> {
+                    try {
+                        // This should be 0 in the success case.
+                        long returnCode = readBuffer.readLong(32);
+                        // This is always 4
+                        long itemLength = readBuffer.readLong(32);
+
+                        returnCodes.put(symbolicAdsField, returnCode);
+                    } catch (ParseException e) {
+                        e.printStackTrace();
+                    }
+                });
+                symbolicAdsFields.forEach(symbolicAdsField -> {
+                    try {
+                        if (returnCodes.get(symbolicAdsField) == 0) {
+                            // Read the handle.
+                            long handle = readBuffer.readLong(32);
+
+                            DirectAdsField directAdsField = new DirectAdsField(
+                                ReservedIndexGroups.ADSIGRP_SYM_VALBYHND.getValue(), handle,
+                                symbolicAdsField.getAdsDataType(), symbolicAdsField.getNumberOfElements());
+                            symbolicFieldMapping.put(symbolicAdsField, directAdsField);
+                        } else {
+                            // TODO: Handle the case of unsuccessful resolution ..
+                        }
+                    } catch (ParseException e) {
+                        e.printStackTrace();
+                    }
+                });
+                future.complete(null);
+            }));
+        return future;
+    }
+
+    protected byte[] getNullByteTerminatedArray(String value) {
+        byte[] valueBytes = value.getBytes();
+        byte[] nullTerminatedBytes = new byte[valueBytes.length + 1];
+        System.arraycopy(valueBytes, 0, nullTerminatedBytes, 0, valueBytes.length);
+        return nullTerminatedBytes;
     }
 
 }
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/package-info.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/package-info.java
deleted file mode 100644
index 123c1c7..0000000
--- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/protocol/util/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- 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.
- */
-/**
- * This package contains utils regarding endianess and crc digest.
- */
-package org.apache.plc4x.java.amsads.protocol.util;
\ No newline at end of file
diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/utils/StaticHelper.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/utils/StaticHelper.java
new file mode 100644
index 0000000..f218b14
--- /dev/null
+++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/utils/StaticHelper.java
@@ -0,0 +1,37 @@
+/*
+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.amsads.utils;
+
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+
+public class StaticHelper {
+
+    public static String parseAmsString(ReadBuffer io, Object encoding) {
+        return "";
+    }
+
+    public static void serializeAmsString(WriteBuffer io, PlcValue value, Object encoding) {
+        // TODO: Need to implement the serialization or we can't write strings
+        throw new PlcRuntimeException("Not implemented yet");
+    }
+
+}
diff --git a/sandbox/test-java-amsads-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver b/sandbox/test-java-amsads-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
index f59c2cd..332bf55 100644
--- a/sandbox/test-java-amsads-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
+++ b/sandbox/test-java-amsads-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
@@ -16,23 +16,4 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-
-#
-# 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.
-#
 org.apache.plc4x.java.amsads.AMSADSPlcDriver
diff --git a/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AdsDriverIT.java b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AdsDriverIT.java
new file mode 100644
index 0000000..5ffcada
--- /dev/null
+++ b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AdsDriverIT.java
@@ -0,0 +1,29 @@
+/*
+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.protocol.amsads;
+
+import org.apache.plc4x.test.driver.DriverTestsuiteRunner;
+
+public class AdsDriverIT extends DriverTestsuiteRunner {
+
+    public AdsDriverIT() {
+        super("/testsuite/AdsDriverIT.xml");
+    }
+
+}
diff --git a/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsAdsSerializerParserTest.java b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AdsSerializerParserTest.java
similarity index 88%
rename from sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsAdsSerializerParserTest.java
rename to sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AdsSerializerParserTest.java
index 360d92f..fbb55ca 100644
--- a/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsAdsSerializerParserTest.java
+++ b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AdsSerializerParserTest.java
@@ -22,9 +22,9 @@ package org.apache.plc4x.protocol.amsads;
 
 import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
 
-public class AmsAdsSerializerParserTest extends ParserSerializerTestsuiteRunner {
+public class AdsSerializerParserTest extends ParserSerializerTestsuiteRunner {
 
-    public AmsAdsSerializerParserTest() {
+    public AdsSerializerParserTest() {
         super("/testsuite/AdsParserSerializerTest.xml");
     }
 
diff --git a/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AdsDriverIT.xml b/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AdsDriverIT.xml
new file mode 100644
index 0000000..2f21aee
--- /dev/null
+++ b/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AdsDriverIT.xml
@@ -0,0 +1,463 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<test:driver-testsuite xmlns:test="https://plc4x.apache.org/schemas/driver-testsuite.xsd"
+                       bigEndian="false">
+
+  <name>Beckhoff ADS/AMS</name>
+
+  <driver-name>ads</driver-name>
+  <driver-parameters>
+    <parameter>
+      <name>sourceAmsNetId</name>
+      <value>192.168.23.200.1.1</value>
+    </parameter>
+    <parameter>
+      <name>sourceAmsPort</name>
+      <value>48898</value>
+    </parameter>
+    <parameter>
+      <name>targetAmsNetId</name>
+      <value>192.168.23.20.1.1</value>
+    </parameter>
+    <parameter>
+      <name>targetAmsPort</name>
+      <value>48898</value>
+    </parameter>
+  </driver-parameters>
+
+  <testcase>
+    <name>Single element direct read request</name>
+    <steps>
+      <api-request name="Receive Read Request from application">
+        <TestReadRequest className="org.apache.plc4x.test.driver.model.api.TestReadRequest">
+          <fields>
+            <field className="org.apache.plc4x.test.driver.model.api.TestField">
+              <name>hurz</name>
+              <address>00004040/00000008:BOOL</address>
+            </field>
+          </fields>
+        </TestReadRequest>
+      </api-request>
+      <outgoing-plc-message name="Send Ads Read Request">
+        <!-- TODO: Should be AmsTCPPacket -->
+        <AmsPacket className="org.apache.plc4x.java.amsads.readwrite.AmsPacket">
+          <targetAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>20</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </targetAmsNetId>
+          <targetAmsPort>48898</targetAmsPort>
+          <sourceAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>200</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </sourceAmsNetId>
+          <sourceAmsPort>48898</sourceAmsPort>
+          <commandId>ADS_READ</commandId>
+          <state className="org.apache.plc4x.java.amsads.readwrite.State">
+            <initCommand>false</initCommand>
+            <updCommand>false</updCommand>
+            <timestampAdded>false</timestampAdded>
+            <highPriorityCommand>false</highPriorityCommand>
+            <systemCommand>false</systemCommand>
+            <adsCommand>true</adsCommand>
+            <noReturn>false</noReturn>
+            <response>false</response>
+            <broadcast>false</broadcast>
+          </state>
+          <errorCode>0</errorCode>
+          <invokeId>1</invokeId>
+          <data className="org.apache.plc4x.java.amsads.readwrite.AdsReadRequest">
+            <indexGroup>4040</indexGroup>
+            <indexOffset>8</indexOffset>
+            <length>1</length>
+          </data>
+        </AmsPacket>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive Ads Read Response">
+        <AmsPacket className="org.apache.plc4x.java.amsads.readwrite.AmsPacket">
+          <targetAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>200</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </targetAmsNetId>
+          <targetAmsPort>48898</targetAmsPort>
+          <sourceAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>20</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </sourceAmsNetId>
+          <sourceAmsPort>48898</sourceAmsPort>
+          <commandId>ADS_READ</commandId>
+          <state className="org.apache.plc4x.java.amsads.readwrite.State">
+            <initCommand>false</initCommand>
+            <updCommand>false</updCommand>
+            <timestampAdded>false</timestampAdded>
+            <highPriorityCommand>false</highPriorityCommand>
+            <systemCommand>false</systemCommand>
+            <adsCommand>true</adsCommand>
+            <noReturn>false</noReturn>
+            <response>true</response>
+            <broadcast>false</broadcast>
+          </state>
+          <errorCode>0</errorCode>
+          <invokeId>1</invokeId>
+          <data className="org.apache.plc4x.java.amsads.readwrite.AdsReadResponse">
+            <result>0</result>
+            <data>AQ==</data>
+          </data>
+        </AmsPacket>
+      </incoming-plc-message>
+      <api-response name="Report Read Response to application">
+        <DefaultPlcReadResponse className="org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse">
+          <request className="org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest">
+            <hurz className="org.apache.plc4x.java.amsads.field.DirectAdsField">
+              <indexGroup>4040</indexGroup>
+              <indexOffset>8</indexOffset>
+              <adsDataType>BOOL</adsDataType>
+              <numberOfElements>1</numberOfElements>
+              <defaultJavaType>java.lang.Object</defaultJavaType>
+            </hurz>
+          </request>
+          <hurz>
+            <code>OK</code>
+            <value className="org.apache.plc4x.java.api.value.PlcBoolean">
+              <object>true</object>
+            </value>
+          </hurz>
+        </DefaultPlcReadResponse>
+      </api-response>
+      <delay>1000</delay>
+    </steps>
+  </testcase>
+
+  <testcase>
+    <name>Multi element direct read request</name>
+    <steps>
+      <api-request name="Receive Read Request from application">
+        <TestReadRequest className="org.apache.plc4x.test.driver.model.api.TestReadRequest">
+          <fields>
+            <field className="org.apache.plc4x.test.driver.model.api.TestField">
+              <name>hurz1</name>
+              <address>00004040/00000008:BOOL</address>
+            </field>
+            <field className="org.apache.plc4x.test.driver.model.api.TestField">
+              <name>hurz2</name>
+              <address>00004040/00000012:BOOL</address>
+            </field>
+          </fields>
+        </TestReadRequest>
+      </api-request>
+      <outgoing-plc-message name="Send Ads Read Request">
+        <!-- TODO: Should be AmsTCPPacket -->
+        <AmsPacket className="org.apache.plc4x.java.amsads.readwrite.AmsPacket">
+          <targetAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>20</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </targetAmsNetId>
+          <targetAmsPort>48898</targetAmsPort>
+          <sourceAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>200</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </sourceAmsNetId>
+          <sourceAmsPort>48898</sourceAmsPort>
+          <commandId>ADS_READ_WRITE</commandId>
+          <state className="org.apache.plc4x.java.amsads.readwrite.State">
+            <initCommand>false</initCommand>
+            <updCommand>false</updCommand>
+            <timestampAdded>false</timestampAdded>
+            <highPriorityCommand>false</highPriorityCommand>
+            <systemCommand>false</systemCommand>
+            <adsCommand>true</adsCommand>
+            <noReturn>false</noReturn>
+            <response>false</response>
+            <broadcast>false</broadcast>
+          </state>
+          <errorCode>0</errorCode>
+          <invokeId>2</invokeId>
+          <data className="org.apache.plc4x.java.amsads.readwrite.AdsReadWriteRequest">
+            <indexGroup>61568</indexGroup>
+            <indexOffset>2</indexOffset>
+            <readLength>0</readLength>
+            <items>
+              <items className="org.apache.plc4x.java.amsads.readwrite.AdsReadWriteRequest">
+                <indexGroup>4040</indexGroup>
+                <indexOffset>8</indexOffset>
+                <readLength>1</readLength>
+                <items/>
+                <data></data>
+              </items>
+              <items className="org.apache.plc4x.java.amsads.readwrite.AdsReadWriteRequest">
+                <indexGroup>4040</indexGroup>
+                <indexOffset>12</indexOffset>
+                <readLength>1</readLength>
+                <items/>
+                <data></data>
+              </items>
+            </items>
+            <data></data>
+          </data>
+        </AmsPacket>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive Ads Read Response">
+        <AmsPacket className="org.apache.plc4x.java.amsads.readwrite.AmsPacket">
+          <targetAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>200</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </targetAmsNetId>
+          <targetAmsPort>48898</targetAmsPort>
+          <sourceAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>20</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </sourceAmsNetId>
+          <sourceAmsPort>48898</sourceAmsPort>
+          <commandId>ADS_READ_WRITE</commandId>
+          <state className="org.apache.plc4x.java.amsads.readwrite.State">
+            <initCommand>false</initCommand>
+            <updCommand>false</updCommand>
+            <timestampAdded>false</timestampAdded>
+            <highPriorityCommand>false</highPriorityCommand>
+            <systemCommand>false</systemCommand>
+            <adsCommand>true</adsCommand>
+            <noReturn>false</noReturn>
+            <response>true</response>
+            <broadcast>false</broadcast>
+          </state>
+          <errorCode>0</errorCode>
+          <invokeId>2</invokeId>
+          <data className="org.apache.plc4x.java.amsads.readwrite.AdsReadWriteResponse">
+            <result>0</result>
+            <data>AAAAAAAAAAABAQ==</data>
+          </data>
+        </AmsPacket>
+      </incoming-plc-message>
+      <api-response name="Report Read Response to application">
+        <DefaultPlcReadResponse className="org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse">
+          <request className="org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest">
+            <hurz1 className="org.apache.plc4x.java.amsads.field.DirectAdsField">
+              <indexGroup>4040</indexGroup>
+              <indexOffset>8</indexOffset>
+              <adsDataType>BOOL</adsDataType>
+              <numberOfElements>1</numberOfElements>
+              <defaultJavaType>java.lang.Object</defaultJavaType>
+            </hurz1>
+            <hurz2 className="org.apache.plc4x.java.amsads.field.DirectAdsField">
+              <indexGroup>4040</indexGroup>
+              <indexOffset>12</indexOffset>
+              <adsDataType>BOOL</adsDataType>
+              <numberOfElements>1</numberOfElements>
+              <defaultJavaType>java.lang.Object</defaultJavaType>
+            </hurz2>
+          </request>
+          <hurz1>
+            <code>OK</code>
+            <value className="org.apache.plc4x.java.api.value.PlcBoolean">
+              <object>true</object>
+            </value>
+          </hurz1>
+          <hurz2>
+            <code>OK</code>
+            <value className="org.apache.plc4x.java.api.value.PlcBoolean">
+              <object>true</object>
+            </value>
+          </hurz2>
+        </DefaultPlcReadResponse>
+      </api-response>
+      <delay>1000</delay>
+    </steps>
+  </testcase>
+
+  <!--testcase>
+    <name>Single element symbolic read request</name>
+    <steps>
+      <api-request name="Receive Read Request from application">
+        <TestReadRequest className="org.apache.plc4x.test.driver.model.api.TestReadRequest">
+          <fields>
+            <field className="org.apache.plc4x.test.driver.model.api.TestField">
+              <name>hurz1</name>
+              <address>main.f_trigDateiGelesen.M:BOOL</address>
+            </field>
+          </fields>
+        </TestReadRequest>
+      </api-request>
+      <outgoing-plc-message name="Send Ads Read Request">
+        <!- TODO: Should be AmsTCPPacket ->
+        <AmsPacket className="org.apache.plc4x.java.amsads.readwrite.AmsPacket">
+          <targetAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>20</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </targetAmsNetId>
+          <targetAmsPort>48898</targetAmsPort>
+          <sourceAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>200</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </sourceAmsNetId>
+          <sourceAmsPort>48898</sourceAmsPort>
+          <commandId>ADS_READ_WRITE</commandId>
+          <state className="org.apache.plc4x.java.amsads.readwrite.State">
+            <initCommand>false</initCommand>
+            <updCommand>false</updCommand>
+            <timestampAdded>false</timestampAdded>
+            <highPriorityCommand>false</highPriorityCommand>
+            <systemCommand>false</systemCommand>
+            <adsCommand>true</adsCommand>
+            <noReturn>false</noReturn>
+            <response>false</response>
+            <broadcast>false</broadcast>
+          </state>
+          <errorCode>0</errorCode>
+          <invokeId>2</invokeId>
+          <data className="org.apache.plc4x.java.amsads.readwrite.AdsReadWriteRequest">
+            <indexGroup>61568</indexGroup>
+            <indexOffset>2</indexOffset>
+            <readLength>0</readLength>
+            <items>
+              <items className="org.apache.plc4x.java.amsads.readwrite.AdsReadWriteRequest">
+                <indexGroup>4040</indexGroup>
+                <indexOffset>8</indexOffset>
+                <readLength>1</readLength>
+                <items/>
+                <data></data>
+              </items>
+              <items className="org.apache.plc4x.java.amsads.readwrite.AdsReadWriteRequest">
+                <indexGroup>4040</indexGroup>
+                <indexOffset>12</indexOffset>
+                <readLength>1</readLength>
+                <items/>
+                <data></data>
+              </items>
+            </items>
+            <data></data>
+          </data>
+        </AmsPacket>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive Ads Read Response">
+        <AmsPacket className="org.apache.plc4x.java.amsads.readwrite.AmsPacket">
+          <targetAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>200</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </targetAmsNetId>
+          <targetAmsPort>48898</targetAmsPort>
+          <sourceAmsNetId className="org.apache.plc4x.java.amsads.readwrite.AmsNetId">
+            <octet1>192</octet1>
+            <octet2>168</octet2>
+            <octet3>23</octet3>
+            <octet4>20</octet4>
+            <octet5>1</octet5>
+            <octet6>1</octet6>
+          </sourceAmsNetId>
+          <sourceAmsPort>48898</sourceAmsPort>
+          <commandId>ADS_READ_WRITE</commandId>
+          <state className="org.apache.plc4x.java.amsads.readwrite.State">
+            <initCommand>false</initCommand>
+            <updCommand>false</updCommand>
+            <timestampAdded>false</timestampAdded>
+            <highPriorityCommand>false</highPriorityCommand>
+            <systemCommand>false</systemCommand>
+            <adsCommand>true</adsCommand>
+            <noReturn>false</noReturn>
+            <response>true</response>
+            <broadcast>false</broadcast>
+          </state>
+          <errorCode>0</errorCode>
+          <invokeId>2</invokeId>
+          <data className="org.apache.plc4x.java.amsads.readwrite.AdsReadWriteResponse">
+            <result>0</result>
+            <data>AAAAAAAAAAABAQ==</data>
+          </data>
+        </AmsPacket>
+      </incoming-plc-message>
+      <api-response name="Report Read Response to application">
+        <DefaultPlcReadResponse className="org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse">
+          <request className="org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest">
+            <hurz1 className="org.apache.plc4x.java.amsads.field.DirectAdsField">
+              <indexGroup>4040</indexGroup>
+              <indexOffset>8</indexOffset>
+              <adsDataType>BOOL</adsDataType>
+              <numberOfElements>1</numberOfElements>
+              <defaultJavaType>java.lang.Object</defaultJavaType>
+            </hurz1>
+            <hurz2 className="org.apache.plc4x.java.amsads.field.DirectAdsField">
+              <indexGroup>4040</indexGroup>
+              <indexOffset>12</indexOffset>
+              <adsDataType>BOOL</adsDataType>
+              <numberOfElements>1</numberOfElements>
+              <defaultJavaType>java.lang.Object</defaultJavaType>
+            </hurz2>
+          </request>
+          <hurz1>
+            <code>OK</code>
+            <value className="org.apache.plc4x.java.api.value.PlcBoolean">
+              <object>true</object>
+            </value>
+          </hurz1>
+          <hurz2>
+            <code>OK</code>
+            <value className="org.apache.plc4x.java.api.value.PlcBoolean">
+              <object>true</object>
+            </value>
+          </hurz2>
+        </DefaultPlcReadResponse>
+      </api-response>
+      <delay>1000</delay>
+    </steps>
+  </testcase-->
+
+</test:driver-testsuite>
\ No newline at end of file