You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2018/02/16 13:49:11 UTC

[incubator-plc4x] 02/02: added tests for ADS protocol

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch feature/Beckhoff_ADS_protocol
in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git

commit 83a4a0c89d95597fd0c4a7091362ccde4e99fc5b
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Feb 16 14:49:04 2018 +0100

    added tests for ADS protocol
---
 ...adStateRequest.java => ADSAbstractRequest.java} |  25 +--
 ...dStateRequest.java => ADSAbstractResponse.java} |  25 +--
 .../commands/ADSAddDeviceNotificationRequest.java  |  28 ++-
 .../commands/ADSAddDeviceNotificationResponse.java |  19 +-
 .../ADSDeleteDeviceNotificationRequest.java        |   9 +-
 .../ADSDeleteDeviceNotificationResponse.java       |  17 +-
 .../api/commands/ADSDeviceNotificationRequest.java |  11 +-
 .../commands/ADSDeviceNotificationResponse.java    |   5 +-
 .../ads/api/commands/ADSReadDeviceInfoRequest.java |   3 +-
 .../api/commands/ADSReadDeviceInfoResponse.java    |  25 ++-
 .../java/ads/api/commands/ADSReadRequest.java      |  11 +-
 .../java/ads/api/commands/ADSReadResponse.java     |  22 ++-
 .../java/ads/api/commands/ADSReadStateRequest.java |   3 +-
 .../ads/api/commands/ADSReadStateResponse.java     |  17 +-
 .../java/ads/api/commands/ADSReadWriteRequest.java |  15 +-
 .../ads/api/commands/ADSReadWriteResponse.java     |  23 ++-
 .../ads/api/commands/ADSWriteControlRequest.java   |  12 +-
 .../ads/api/commands/ADSWriteControlResponse.java  |  17 +-
 .../java/ads/api/commands/ADSWriteRequest.java     |  13 +-
 .../java/ads/api/commands/ADSWriteResponse.java    |  18 +-
 .../api/commands/types/AdsNotificationSample.java  |   8 +
 .../java/ads/api/commands/types/AdsReturnCode.java |  10 +-
 .../ads/api/commands/types/AdsStampHeader.java     |   9 +
 .../plc4x/java/ads/api/commands/types/Data.java    |   5 +
 .../plc4x/java/ads/api/commands/types/Device.java  |   4 +-
 .../java/ads/api/commands/types/MajorVersion.java  |   4 +-
 .../java/ads/api/commands/types/MinorVersion.java  |   4 +-
 .../java/ads/api/commands/types/TimeStamp.java     |  29 ++-
 .../plc4x/java/ads/api/generic/AMSHeader.java      |  15 ++
 .../plc4x/java/ads/api/generic/AMSTCPHeader.java   |  18 +-
 .../plc4x/java/ads/api/generic/AMSTCPPaket.java    |  11 +-
 .../generic/calculated/CalculatedAMSHeader.java    |  15 ++
 .../generic/calculated/CalculatedAMSTCPHeader.java |  15 +-
 .../plc4x/java/ads/api/generic/types/AMSNetId.java |   4 +-
 .../plc4x/java/ads/api/generic/types/State.java    |   2 +
 .../plc4x/java/ads/api/util/ByteReadable.java      |   5 +-
 .../apache/plc4x/java/ads/api/util/ByteValue.java  |  17 +-
 .../ads/api/util/UnsignedShortLEByteValue.java     |   4 +-
 .../apache/plc4x/java/ads/netty/ADSProtocol.java   |  37 ++--
 .../plc4x/java/ads/netty/Plc4XADSProtocol.java     |  14 +-
 plc4j/protocols/ads/src/site/asciidoc/index.adoc   |   2 +
 .../plc4x/java/ads/netty/ADSProtocolTest.java      | 199 +++++++++++++++++++++
 42 files changed, 635 insertions(+), 114 deletions(-)

diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAbstractRequest.java
similarity index 65%
copy from plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java
copy to plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAbstractRequest.java
index 85853d8..758a44b 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAbstractRequest.java
@@ -18,37 +18,28 @@
  */
 package org.apache.plc4x.java.ads.api.commands;
 
-import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
-import org.apache.plc4x.java.ads.api.generic.types.Command;
 import org.apache.plc4x.java.ads.api.generic.types.Invoke;
+import org.apache.plc4x.java.ads.api.generic.types.State;
 
-/**
- * Reads the ADS status and the device status of an ADS device.
- * <p>
- * No additional data required
- */
-@ADSCommandType(Command.ADS_Read_State)
-public class ADSReadStateRequest extends AMSTCPPaket {
-    public ADSReadStateRequest(AMSTCPHeader amstcpHeader, AMSHeader amsHeader) {
+public abstract class ADSAbstractRequest extends AMSTCPPaket {
+    public ADSAbstractRequest(AMSTCPHeader amstcpHeader, AMSHeader amsHeader) {
         super(amstcpHeader, amsHeader);
     }
 
-    public ADSReadStateRequest(AMSHeader amsHeader) {
+    public ADSAbstractRequest(AMSHeader amsHeader) {
         super(amsHeader);
     }
 
-    public ADSReadStateRequest(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId) {
-        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+    public ADSAbstractRequest(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, State.DEFAULT, invokeId);
     }
 
-    @Override
-    public ADSData getAdsData() {
-        return ADSData.EMPTY;
+    public ADSAbstractRequest(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, State stateId, Invoke invokeId) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, stateId, invokeId);
     }
-
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAbstractResponse.java
similarity index 70%
copy from plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java
copy to plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAbstractResponse.java
index 85853d8..2be453c 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAbstractResponse.java
@@ -18,37 +18,28 @@
  */
 package org.apache.plc4x.java.ads.api.commands;
 
-import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
-import org.apache.plc4x.java.ads.api.generic.types.Command;
 import org.apache.plc4x.java.ads.api.generic.types.Invoke;
+import org.apache.plc4x.java.ads.api.generic.types.State;
 
-/**
- * Reads the ADS status and the device status of an ADS device.
- * <p>
- * No additional data required
- */
-@ADSCommandType(Command.ADS_Read_State)
-public class ADSReadStateRequest extends AMSTCPPaket {
-    public ADSReadStateRequest(AMSTCPHeader amstcpHeader, AMSHeader amsHeader) {
+public abstract class ADSAbstractResponse extends AMSTCPPaket {
+    public ADSAbstractResponse(AMSTCPHeader amstcpHeader, AMSHeader amsHeader) {
         super(amstcpHeader, amsHeader);
     }
 
-    public ADSReadStateRequest(AMSHeader amsHeader) {
+    public ADSAbstractResponse(AMSHeader amsHeader) {
         super(amsHeader);
     }
 
-    public ADSReadStateRequest(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId) {
-        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+    public ADSAbstractResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, State.DEFAULT_RESPONSE, invokeId);
     }
 
-    @Override
-    public ADSData getAdsData() {
-        return ADSData.EMPTY;
+    public ADSAbstractResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, State stateId, Invoke invokeId) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, stateId, invokeId);
     }
-
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAddDeviceNotificationRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAddDeviceNotificationRequest.java
index 3c9b6da..144f4b1 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAddDeviceNotificationRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAddDeviceNotificationRequest.java
@@ -19,14 +19,11 @@
 package org.apache.plc4x.java.ads.api.commands;
 
 import org.apache.plc4x.java.ads.api.commands.types.*;
+import org.apache.plc4x.java.ads.api.commands.types.Length;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
-import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
-import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
-import org.apache.plc4x.java.ads.api.generic.types.Command;
-import org.apache.plc4x.java.ads.api.generic.types.Invoke;
+import org.apache.plc4x.java.ads.api.generic.types.*;
 import org.apache.plc4x.java.ads.api.util.ByteValue;
 
 /**
@@ -36,7 +33,7 @@ import org.apache.plc4x.java.ads.api.util.ByteValue;
  * You can increase the payload by organizing the data in structures.
  */
 @ADSCommandType(Command.ADS_Add_Device_Notification)
-public class ADSAddDeviceNotificationRequest extends AMSTCPPaket {
+public class ADSAddDeviceNotificationRequest extends ADSAbstractRequest {
 
     /**
      * 4 bytes	Index Group of the data, which should be sent per notification.
@@ -88,7 +85,7 @@ public class ADSAddDeviceNotificationRequest extends AMSTCPPaket {
     }
 
     public ADSAddDeviceNotificationRequest(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, IndexGroup indexGroup, IndexOffset indexOffset, Length length, TransmissionMode transmissionMode, MaxDelay maxDelay, CycleTime cycleTime) {
-        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, State.DEFAULT, invokeId);
         this.indexGroup = indexGroup;
         this.indexOffset = indexOffset;
         this.length = length;
@@ -104,11 +101,26 @@ public class ADSAddDeviceNotificationRequest extends AMSTCPPaket {
 
     public static class Reserved extends ByteValue {
 
+        public static final int NUM_BYTES = 16;
+
         private static final Reserved INSTANCE = new Reserved();
 
         private Reserved() {
             super((byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00);
-            assertLength(16);
+            assertLength(NUM_BYTES);
         }
     }
+
+    @Override
+    public String toString() {
+        return "ADSAddDeviceNotificationRequest{" +
+            "indexGroup=" + indexGroup +
+            ", indexOffset=" + indexOffset +
+            ", length=" + length +
+            ", transmissionMode=" + transmissionMode +
+            ", maxDelay=" + maxDelay +
+            ", cycleTime=" + cycleTime +
+            ", reserved=" + reserved +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAddDeviceNotificationResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAddDeviceNotificationResponse.java
index 27f3cf6..ce828a9 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAddDeviceNotificationResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSAddDeviceNotificationResponse.java
@@ -23,14 +23,16 @@ import org.apache.plc4x.java.ads.api.commands.types.Result;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
 
 /**
  * A notification is created in an ADS device.
  */
 @ADSCommandType(Command.ADS_Add_Device_Notification)
-public class ADSAddDeviceNotificationResponse extends AMSTCPPaket {
+public class ADSAddDeviceNotificationResponse extends ADSAbstractResponse {
 
     /**
      * 4 bytes	ADS error number
@@ -48,9 +50,22 @@ public class ADSAddDeviceNotificationResponse extends AMSTCPPaket {
         this.notificationHandle = notificationHandle;
     }
 
+    public ADSAddDeviceNotificationResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, Result result, NotificationHandle notificationHandle) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        this.result = result;
+        this.notificationHandle = notificationHandle;
+    }
+
     @Override
     public ADSData getAdsData() {
         return buildADSData(result, notificationHandle);
     }
 
+    @Override
+    public String toString() {
+        return "ADSAddDeviceNotificationResponse{" +
+            "result=" + result +
+            ", notificationHandle=" + notificationHandle +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeleteDeviceNotificationRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeleteDeviceNotificationRequest.java
index 1449f48..28e08fb 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeleteDeviceNotificationRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeleteDeviceNotificationRequest.java
@@ -22,7 +22,6 @@ import org.apache.plc4x.java.ads.api.commands.types.NotificationHandle;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
@@ -32,7 +31,7 @@ import org.apache.plc4x.java.ads.api.generic.types.Invoke;
  * One before defined notification is deleted in an ADS device.
  */
 @ADSCommandType(Command.ADS_Delete_Device_Notification)
-public class ADSDeleteDeviceNotificationRequest extends AMSTCPPaket {
+public class ADSDeleteDeviceNotificationRequest extends ADSAbstractRequest {
 
     /**
      * 4 bytes	Handle of notification
@@ -59,4 +58,10 @@ public class ADSDeleteDeviceNotificationRequest extends AMSTCPPaket {
         return buildADSData(notificationHandle);
     }
 
+    @Override
+    public String toString() {
+        return "ADSDeleteDeviceNotificationRequest{" +
+            "notificationHandle=" + notificationHandle +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeleteDeviceNotificationResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeleteDeviceNotificationResponse.java
index 8aab12f..9726b46 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeleteDeviceNotificationResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeleteDeviceNotificationResponse.java
@@ -22,14 +22,16 @@ import org.apache.plc4x.java.ads.api.commands.types.Result;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
 
 /**
  * One before defined notification is deleted in an ADS device.
  */
 @ADSCommandType(Command.ADS_Delete_Device_Notification)
-public class ADSDeleteDeviceNotificationResponse extends AMSTCPPaket {
+public class ADSDeleteDeviceNotificationResponse extends ADSAbstractResponse {
 
     /**
      * 4 bytes	ADS error number
@@ -41,9 +43,20 @@ public class ADSDeleteDeviceNotificationResponse extends AMSTCPPaket {
         this.result = result;
     }
 
+    public ADSDeleteDeviceNotificationResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, Result result) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        this.result = result;
+    }
+
     @Override
     public ADSData getAdsData() {
         return buildADSData(result);
     }
 
+    @Override
+    public String toString() {
+        return "ADSDeleteDeviceNotificationResponse{" +
+            "result=" + result +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeviceNotificationRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeviceNotificationRequest.java
index fb12fe0..ec870bf 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeviceNotificationRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeviceNotificationRequest.java
@@ -24,7 +24,6 @@ import org.apache.plc4x.java.ads.api.commands.types.Stamps;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
@@ -39,7 +38,7 @@ import java.util.List;
  * The data which are transfered at the Device Notification  are multiple nested into one another. The Notification Stream contains an array with elements of type AdsStampHeader. This array again contains elements of type AdsNotificationSample.
  */
 @ADSCommandType(Command.ADS_Device_Notification)
-public class ADSDeviceNotificationRequest extends AMSTCPPaket {
+public class ADSDeviceNotificationRequest extends ADSAbstractRequest {
 
     /**
      * 4 bytes	Size of data in byte.
@@ -80,4 +79,12 @@ public class ADSDeviceNotificationRequest extends AMSTCPPaket {
         return buildADSData(length, stamps, buildADSData(adsStampHeaders.toArray(new ByteReadable[adsStampHeaders.size()])));
     }
 
+    @Override
+    public String toString() {
+        return "ADSDeviceNotificationRequest{" +
+            "length=" + length +
+            ", stamps=" + stamps +
+            ", adsStampHeaders=" + adsStampHeaders +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeviceNotificationResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeviceNotificationResponse.java
index bfae33d..8797cdc 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeviceNotificationResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSDeviceNotificationResponse.java
@@ -19,16 +19,15 @@
 package org.apache.plc4x.java.ads.api.commands;
 
 import org.apache.plc4x.java.ads.api.generic.ADSData;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
 
 /**
  * Data will carry forward independently from an ADS device to a Client
  */
 @ADSCommandType(Command.ADS_Device_Notification)
-public class ADSDeviceNotificationResponse extends AMSTCPPaket {
+public class ADSDeviceNotificationResponse extends ADSAbstractResponse {
 
-    public ADSDeviceNotificationResponse() {
+    private ADSDeviceNotificationResponse() {
         super(null, null);
         // There is no {@link ADSDeviceNotificationResponse} specified
     }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadDeviceInfoRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadDeviceInfoRequest.java
index 5d40389..221fc59 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadDeviceInfoRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadDeviceInfoRequest.java
@@ -21,7 +21,6 @@ package org.apache.plc4x.java.ads.api.commands;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
@@ -33,7 +32,7 @@ import org.apache.plc4x.java.ads.api.generic.types.Invoke;
  * No additional data required
  */
 @ADSCommandType(Command.ADS_Read_Device_Info)
-public class ADSReadDeviceInfoRequest extends AMSTCPPaket {
+public class ADSReadDeviceInfoRequest extends ADSAbstractRequest {
     public ADSReadDeviceInfoRequest(AMSTCPHeader amstcpHeader, AMSHeader amsHeader) {
         super(amstcpHeader, amsHeader);
     }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadDeviceInfoResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadDeviceInfoResponse.java
index e2daa2a..b5d104d 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadDeviceInfoResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadDeviceInfoResponse.java
@@ -22,14 +22,16 @@ import org.apache.plc4x.java.ads.api.commands.types.*;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
 
 /**
  * Reads the name and the version number of the ADS device.
  */
 @ADSCommandType(Command.ADS_Read_Device_Info)
-public class ADSReadDeviceInfoResponse extends AMSTCPPaket {
+public class ADSReadDeviceInfoResponse extends ADSAbstractResponse {
     /**
      * 4 bytes	ADS error number.
      */
@@ -60,9 +62,28 @@ public class ADSReadDeviceInfoResponse extends AMSTCPPaket {
         this.device = device;
     }
 
+    public ADSReadDeviceInfoResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, Result result, MajorVersion majorVersion, MinorVersion minorVersion, Version version, Device device) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        this.result = result;
+        this.majorVersion = majorVersion;
+        this.minorVersion = minorVersion;
+        this.version = version;
+        this.device = device;
+    }
+
     @Override
     public ADSData getAdsData() {
         return buildADSData(result, majorVersion, minorVersion, version, device);
     }
 
+    @Override
+    public String toString() {
+        return "ADSReadDeviceInfoResponse{" +
+            "result=" + result +
+            ", majorVersion=" + majorVersion +
+            ", minorVersion=" + minorVersion +
+            ", version=" + version +
+            ", device=" + device +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadRequest.java
index 1635929..009c6a4 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadRequest.java
@@ -24,7 +24,6 @@ import org.apache.plc4x.java.ads.api.commands.types.Length;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
@@ -34,7 +33,7 @@ import org.apache.plc4x.java.ads.api.generic.types.Invoke;
  * With ADS Read data can be read from an ADS device.  The data are addressed by the Index Group and the Index Offset
  */
 @ADSCommandType(Command.ADS_Read)
-public class ADSReadRequest extends AMSTCPPaket {
+public class ADSReadRequest extends ADSAbstractRequest {
 
     /**
      * 4 bytes	Index Group of the data which should be read.
@@ -77,4 +76,12 @@ public class ADSReadRequest extends AMSTCPPaket {
         return buildADSData(indexGroup, indexOffset, length);
     }
 
+    @Override
+    public String toString() {
+        return "ADSReadRequest{" +
+            "indexGroup=" + indexGroup +
+            ", indexOffset=" + indexOffset +
+            ", length=" + length +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadResponse.java
index 479d48e..8c5f1e1 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadResponse.java
@@ -24,14 +24,16 @@ import org.apache.plc4x.java.ads.api.commands.types.Result;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
 
 /**
  * With ADS Read data can be read from an ADS device
  */
 @ADSCommandType(Command.ADS_Read)
-public class ADSReadResponse extends AMSTCPPaket {
+public class ADSReadResponse extends ADSAbstractResponse {
 
     /**
      * 4 bytes	ADS error number
@@ -53,6 +55,13 @@ public class ADSReadResponse extends AMSTCPPaket {
         this.data = data;
     }
 
+    public ADSReadResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, Result result, Length length, Data data) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        this.result = result;
+        this.length = length;
+        this.data = data;
+    }
+
     @Override
     public ADSData getAdsData() {
         return buildADSData(result, length, data);
@@ -69,4 +78,13 @@ public class ADSReadResponse extends AMSTCPPaket {
     public Data getData() {
         return data;
     }
+
+    @Override
+    public String toString() {
+        return "ADSReadResponse{" +
+            "result=" + result +
+            ", length=" + length +
+            ", data=" + data +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java
index 85853d8..caf6422 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateRequest.java
@@ -21,7 +21,6 @@ package org.apache.plc4x.java.ads.api.commands;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
@@ -33,7 +32,7 @@ import org.apache.plc4x.java.ads.api.generic.types.Invoke;
  * No additional data required
  */
 @ADSCommandType(Command.ADS_Read_State)
-public class ADSReadStateRequest extends AMSTCPPaket {
+public class ADSReadStateRequest extends ADSAbstractRequest {
     public ADSReadStateRequest(AMSTCPHeader amstcpHeader, AMSHeader amsHeader) {
         super(amstcpHeader, amsHeader);
     }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateResponse.java
index 2cb725a..6a1e389 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadStateResponse.java
@@ -22,14 +22,16 @@ import org.apache.plc4x.java.ads.api.commands.types.Result;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
 
 /**
  * Reads the ADS status and the device status of an ADS device.
  */
 @ADSCommandType(Command.ADS_Read_State)
-public class ADSReadStateResponse extends AMSTCPPaket {
+public class ADSReadStateResponse extends ADSAbstractResponse {
 
     /**
      * 4 bytes	ADS error number
@@ -41,9 +43,20 @@ public class ADSReadStateResponse extends AMSTCPPaket {
         this.result = result;
     }
 
+    public ADSReadStateResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, Result result) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        this.result = result;
+    }
+
     @Override
     public ADSData getAdsData() {
         return buildADSData(result);
     }
 
+    @Override
+    public String toString() {
+        return "ADSReadStateResponse{" +
+            "result=" + result +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadWriteRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadWriteRequest.java
index 70c8ab9..1d14035 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadWriteRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadWriteRequest.java
@@ -22,7 +22,6 @@ import org.apache.plc4x.java.ads.api.commands.types.*;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
@@ -34,7 +33,7 @@ import org.apache.plc4x.java.ads.api.generic.types.Invoke;
  * The data which can be read are addressed by the Index Group and the Index Offset
  */
 @ADSCommandType(Command.ADS_Read_Write)
-public class ADSReadWriteRequest extends AMSTCPPaket {
+public class ADSReadWriteRequest extends ADSAbstractRequest {
 
     /**
      * 4 bytes	Index Group, in which the data should be written.
@@ -86,7 +85,17 @@ public class ADSReadWriteRequest extends AMSTCPPaket {
 
     @Override
     public ADSData getAdsData() {
-        return ADSData.EMPTY;
+        return buildADSData(indexGroup, indexOffset, readLength, writeLength, data);
     }
 
+    @Override
+    public String toString() {
+        return "ADSReadWriteRequest{" +
+            "indexGroup=" + indexGroup +
+            ", indexOffset=" + indexOffset +
+            ", readLength=" + readLength +
+            ", writeLength=" + writeLength +
+            ", data=" + data +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadWriteResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadWriteResponse.java
index 769c073..c587017 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadWriteResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSReadWriteResponse.java
@@ -24,14 +24,16 @@ import org.apache.plc4x.java.ads.api.commands.types.Result;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
 
 /**
  * With ADS Read Write data will be written to an ADS device. Additionally, data can be read from the ADS device.
  */
 @ADSCommandType(Command.ADS_Read_Write)
-public class ADSReadWriteResponse extends AMSTCPPaket {
+public class ADSReadWriteResponse extends ADSAbstractResponse {
 
     /**
      * 4 bytes	ADS error number
@@ -55,9 +57,24 @@ public class ADSReadWriteResponse extends AMSTCPPaket {
         this.data = data;
     }
 
+    public ADSReadWriteResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, Result result, Length length, Data data) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        this.result = result;
+        this.length = length;
+        this.data = data;
+    }
+
     @Override
     public ADSData getAdsData() {
-        return ADSData.EMPTY;
+        return buildADSData(result, length, data);
     }
 
+    @Override
+    public String toString() {
+        return "ADSReadWriteResponse{" +
+            "result=" + result +
+            ", length=" + length +
+            ", data=" + data +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteControlRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteControlRequest.java
index 8393cd6..fbc0110 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteControlRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteControlRequest.java
@@ -25,7 +25,6 @@ import org.apache.plc4x.java.ads.api.commands.types.Length;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
@@ -37,7 +36,7 @@ import org.apache.plc4x.java.ads.api.generic.types.Invoke;
  * These data were not analysed from the current ADS devices (PLC, NC, ...)
  */
 @ADSCommandType(Command.ADS_Write_Control)
-public class ADSWriteControlRequest extends AMSTCPPaket {
+public class ADSWriteControlRequest extends ADSAbstractRequest {
 
     /**
      * 2 bytes	New ADS status (see data type ADSSTATE of the ADS-DLL).
@@ -85,4 +84,13 @@ public class ADSWriteControlRequest extends AMSTCPPaket {
         return buildADSData(adsState, deviceState, length, data);
     }
 
+    @Override
+    public String toString() {
+        return "ADSWriteControlRequest{" +
+            "adsState=" + adsState +
+            ", deviceState=" + deviceState +
+            ", length=" + length +
+            ", data=" + data +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteControlResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteControlResponse.java
index 488adfe..00cbc9c 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteControlResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteControlResponse.java
@@ -22,14 +22,16 @@ import org.apache.plc4x.java.ads.api.commands.types.Result;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
 
 /**
  * Changes the ADS status and the device status of an ADS device.
  */
 @ADSCommandType(Command.ADS_Write_Control)
-public class ADSWriteControlResponse extends AMSTCPPaket {
+public class ADSWriteControlResponse extends ADSAbstractResponse {
     /**
      * 4 bytes	ADS error number
      */
@@ -40,9 +42,20 @@ public class ADSWriteControlResponse extends AMSTCPPaket {
         this.result = result;
     }
 
+    public ADSWriteControlResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, Result result) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        this.result = result;
+    }
+
     @Override
     public ADSData getAdsData() {
         return buildADSData(result);
     }
 
+    @Override
+    public String toString() {
+        return "ADSWriteControlResponse{" +
+            "result=" + result +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteRequest.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteRequest.java
index b7c0dea..e3ac538 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteRequest.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteRequest.java
@@ -25,7 +25,6 @@ import org.apache.plc4x.java.ads.api.commands.types.Length;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
 import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
 import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
@@ -35,7 +34,7 @@ import org.apache.plc4x.java.ads.api.generic.types.Invoke;
  * With ADS Write data can be written to an ADS device. The data are addressed by the Index Group and the Index Offset.
  */
 @ADSCommandType(Command.ADS_Write)
-public class ADSWriteRequest extends AMSTCPPaket {
+public class ADSWriteRequest extends ADSAbstractRequest {
 
     /**
      * 4 bytes	Index Group in which the data should be written
@@ -82,4 +81,14 @@ public class ADSWriteRequest extends AMSTCPPaket {
     public ADSData getAdsData() {
         return buildADSData(indexGroup, indexOffset, length, data);
     }
+
+    @Override
+    public String toString() {
+        return "ADSWriteRequest{" +
+            "indexGroup=" + indexGroup +
+            ", indexOffset=" + indexOffset +
+            ", length=" + length +
+            ", data=" + data +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteResponse.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteResponse.java
index 97a5f90..cd6223a 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteResponse.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/ADSWriteResponse.java
@@ -22,14 +22,16 @@ import org.apache.plc4x.java.ads.api.commands.types.Result;
 import org.apache.plc4x.java.ads.api.generic.ADSData;
 import org.apache.plc4x.java.ads.api.generic.AMSHeader;
 import org.apache.plc4x.java.ads.api.generic.AMSTCPHeader;
-import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
 import org.apache.plc4x.java.ads.api.generic.types.Command;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
 
 /**
  * With ADS Write data can be written to an ADS device.
  */
 @ADSCommandType(Command.ADS_Write)
-public class ADSWriteResponse extends AMSTCPPaket {
+public class ADSWriteResponse extends ADSAbstractResponse {
 
     /**
      * 4 bytes	ADS error number
@@ -41,6 +43,11 @@ public class ADSWriteResponse extends AMSTCPPaket {
         this.result = result;
     }
 
+    public ADSWriteResponse(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId, Result result) {
+        super(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId);
+        this.result = result;
+    }
+
     @Override
     public ADSData getAdsData() {
         return buildADSData(result);
@@ -49,4 +56,11 @@ public class ADSWriteResponse extends AMSTCPPaket {
     public Result getResult() {
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "ADSWriteResponse{" +
+            "result=" + result +
+            "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsNotificationSample.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsNotificationSample.java
index ce2cd2f..24282a0 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsNotificationSample.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsNotificationSample.java
@@ -53,4 +53,12 @@ public class AdsNotificationSample implements ByteReadable {
         return buildByteBuff(notificationHandle, sampleSize, data);
     }
 
+    @Override
+    public String toString() {
+        return "AdsNotificationSample{" +
+            "notificationHandle=" + notificationHandle +
+            ", sampleSize=" + sampleSize +
+            ", data=" + data +
+            '}';
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsReturnCode.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsReturnCode.java
index 0809d66..9a7c534 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsReturnCode.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsReturnCode.java
@@ -188,12 +188,12 @@ public enum AdsReturnCode {
 
     @Override
     public String toString() {
-        return "AdsReturnCode{\n" +
+        return "AdsReturnCode{" +
             "hex=" + hex +
-            ",\n dec=" + dec +
-            ",\n description='" + description + '\'' +
-            ",\n possibleCauses='" + possibleCauses + '\'' +
-            ",\n solution='" + solution + '\'' +
+            ", dec=" + dec +
+            ", description='" + description + '\'' +
+            ", possibleCauses='" + possibleCauses + '\'' +
+            ", solution='" + solution + '\'' +
             '}';
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsStampHeader.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsStampHeader.java
index ab7edf7..29f00b7 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsStampHeader.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/AdsStampHeader.java
@@ -54,4 +54,13 @@ public class AdsStampHeader implements ByteReadable {
     public ByteBuf getByteBuf() {
         return buildByteBuff(timeStamp, samples, () -> buildByteBuff(adsNotificationSamples.toArray(new ByteReadable[adsNotificationSamples.size()])));
     }
+
+    @Override
+    public String toString() {
+        return "AdsStampHeader{" +
+            "timeStamp=" + timeStamp +
+            ", samples=" + samples +
+            ", adsNotificationSamples=" + adsNotificationSamples +
+            '}';
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/Data.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/Data.java
index a37cff8..a44a2b8 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/Data.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/Data.java
@@ -28,4 +28,9 @@ public class Data extends ByteValue {
     public static Data of(byte... values) {
         return new Data(values);
     }
+
+    @Override
+    public String toString() {
+        return "Data{" + new String(value) + "} " + super.toString();
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/Device.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/Device.java
index f174df3..a1cba41 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/Device.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/Device.java
@@ -35,6 +35,8 @@ public class Device extends ByteValue {
     }
 
     public static Device of(ByteBuf byteBuf) {
-        return of(byteBuf.readBytes(NUM_BYTES).array());
+        byte[] values = new byte[NUM_BYTES];
+        byteBuf.readBytes(values);
+        return of(values);
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/MajorVersion.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/MajorVersion.java
index 59926e7..fead91e 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/MajorVersion.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/MajorVersion.java
@@ -35,6 +35,8 @@ public class MajorVersion extends ByteValue {
     }
 
     public static MajorVersion of(ByteBuf byteBuf) {
-        return of(byteBuf.readBytes(NUM_BYTES).array());
+        byte[] values = new byte[NUM_BYTES];
+        byteBuf.readBytes(values);
+        return of(values);
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/MinorVersion.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/MinorVersion.java
index 180640c..5f41a33 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/MinorVersion.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/MinorVersion.java
@@ -35,6 +35,8 @@ public class MinorVersion extends ByteValue {
     }
 
     public static MinorVersion of(ByteBuf byteBuf) {
-        return of(byteBuf.readBytes(NUM_BYTES).array());
+        byte[] values = new byte[NUM_BYTES];
+        byteBuf.readBytes(values);
+        return of(values);
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/TimeStamp.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/TimeStamp.java
index a7e49e2..bb4627b 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/TimeStamp.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/TimeStamp.java
@@ -21,6 +21,9 @@ package org.apache.plc4x.java.ads.api.commands.types;
 import io.netty.buffer.ByteBuf;
 import org.apache.plc4x.java.ads.api.util.ByteValue;
 
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
 public class TimeStamp extends ByteValue {
 
     public static final int NUM_BYTES = 8;
@@ -30,11 +33,35 @@ public class TimeStamp extends ByteValue {
         assertLength(NUM_BYTES);
     }
 
+    public static TimeStamp of(long value) {
+        return of(BigInteger.valueOf(value));
+    }
+
+    public static TimeStamp of(BigInteger value) {
+        checkUnsignedBounds(value, NUM_BYTES);
+        byte[] valueBytes = value.toByteArray();
+        int length = valueBytes.length;
+        return new TimeStamp(ByteBuffer.allocate(NUM_BYTES)
+            // LE
+            .put(length > 0 ? valueBytes[0] : 0)
+            .put(length > 1 ? valueBytes[1] : 0)
+            .put(length > 2 ? valueBytes[2] : 0)
+            .put(length > 3 ? valueBytes[3] : 0)
+
+            .put(length > 4 ? valueBytes[4] : 0)
+            .put(length > 5 ? valueBytes[5] : 0)
+            .put(length > 6 ? valueBytes[6] : 0)
+            .put(length > 7 ? valueBytes[7] : 0)
+            .array());
+    }
+
     public static TimeStamp of(byte... values) {
         return new TimeStamp(values);
     }
 
     public static TimeStamp of(ByteBuf byteBuf) {
-        return of(byteBuf.readBytes(NUM_BYTES).array());
+        byte[] values = new byte[NUM_BYTES];
+        byteBuf.readBytes(values);
+        return of(values);
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSHeader.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSHeader.java
index ef5a1fb..09dffdc 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSHeader.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSHeader.java
@@ -132,4 +132,19 @@ public class AMSHeader implements ByteReadable {
     public Invoke getInvokeId() {
         return invokeId;
     }
+
+    @Override
+    public String toString() {
+        return "AMSHeader{" +
+            "targetAmsNetId=" + targetAmsNetId +
+            ", targetAmsPort=" + targetAmsPort +
+            ", sourceAmsNetId=" + sourceAmsNetId +
+            ", sourceAmsPort=" + sourceAmsPort +
+            ", commandId=" + commandId +
+            ", stateFlags=" + stateFlags +
+            ", dataLength=" + dataLength +
+            ", code=" + code +
+            ", invokeId=" + invokeId +
+            '}';
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSTCPHeader.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSTCPHeader.java
index 83bb944..1d257ab 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSTCPHeader.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSTCPHeader.java
@@ -52,17 +52,27 @@ public class AMSTCPHeader implements ByteReadable {
      * Size: 2 bytes
      * These bytes must be set to 0.
      */
-    protected static class Reserved extends ByteValue {
+    public static class Reserved extends ByteValue {
+
+        public static final int NUM_BYTES = 2;
 
         private static final Reserved CONSTANT = new Reserved();
 
         private Reserved() {
             super((byte) 0x00, (byte) 0x00);
-            assertLength(2);
+            assertLength(NUM_BYTES);
         }
     }
 
-    public Length getLengthValue() {
-        return length;
+    public long getCalculatedLength() {
+        return length.getAsLong();
+    }
+
+    @Override
+    public String toString() {
+        return "AMSTCPHeader{" +
+            "reserved=" + reserved +
+            ", length=" + length +
+            '}';
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSTCPPaket.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSTCPPaket.java
index 85ba515..328440b 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSTCPPaket.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/AMSTCPPaket.java
@@ -44,7 +44,7 @@ public abstract class AMSTCPPaket implements ByteReadable {
         this.amsHeader = amsHeader;
     }
 
-    public AMSTCPPaket(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, Invoke invokeId) {
+    public AMSTCPPaket(AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort, State stateId, Invoke invokeId) {
         if (!getClass().isAnnotationPresent(ADSCommandType.class)) {
             throw new IllegalArgumentException(ADSCommandType.class + " need to be present.");
         }
@@ -54,7 +54,7 @@ public abstract class AMSTCPPaket implements ByteReadable {
             sourceAmsNetId,
             sourceAmsPort,
             getClass().getAnnotation(ADSCommandType.class).value(),
-            State.DEFAULT,
+            stateId,
             () -> DataLength.of(getAdsData().getCalculatedLength()),
             invokeId);
         this.amstcpHeader = CalculatedAMSTCPHeader.of(amsHeader, () -> getAdsData().getCalculatedLength());
@@ -79,4 +79,11 @@ public abstract class AMSTCPPaket implements ByteReadable {
         return () -> buildByteBuff(byteReadables);
     }
 
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "{" +
+            "amstcpHeader=" + amstcpHeader +
+            ", amsHeader=" + amsHeader +
+            '}';
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/calculated/CalculatedAMSHeader.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/calculated/CalculatedAMSHeader.java
index e8817b5..92134cb 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/calculated/CalculatedAMSHeader.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/calculated/CalculatedAMSHeader.java
@@ -54,4 +54,19 @@ public class CalculatedAMSHeader extends AMSHeader {
             code,
             invokeId);
     }
+
+    @Override
+    public String toString() {
+        return "CalculatedAMSHeader{" +
+            "targetAmsNetId=" + targetAmsNetId +
+            ", targetAmsPort=" + targetAmsPort +
+            ", sourceAmsNetId=" + sourceAmsNetId +
+            ", sourceAmsPort=" + sourceAmsPort +
+            ", commandId=" + commandId +
+            ", stateFlags=" + stateFlags +
+            ", dataLength=" + lengthSupplier.getLength() +
+            ", code=" + code +
+            ", invokeId=" + invokeId +
+            '}';
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/calculated/CalculatedAMSTCPHeader.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/calculated/CalculatedAMSTCPHeader.java
index 94c033b..5afcd99 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/calculated/CalculatedAMSTCPHeader.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/calculated/CalculatedAMSTCPHeader.java
@@ -44,10 +44,23 @@ public class CalculatedAMSTCPHeader extends AMSTCPHeader {
 
     @Override
     public ByteBuf getByteBuf() {
+        return buildByteBuff(reserved, Length.of(getCalculatedLength()));
+    }
+
+    @Override
+    public long getCalculatedLength() {
         long aggregateLength = 0;
         for (LengthSupplier supplier : lengthSupplier) {
             aggregateLength += supplier.getCalculatedLength();
         }
-        return buildByteBuff(reserved, Length.of(aggregateLength));
+        return aggregateLength;
+    }
+
+    @Override
+    public String toString() {
+        return "CalculatedAMSTCPHeader{" +
+            "reserved=" + reserved +
+            ", length=" + getCalculatedLength() +
+            '}';
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSNetId.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSNetId.java
index 7e4b5a5..c22a7fd 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSNetId.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSNetId.java
@@ -66,7 +66,9 @@ public class AMSNetId extends ByteValue {
     }
 
     public static AMSNetId of(ByteBuf byteBuf) {
-        return of(byteBuf.readBytes(NUM_BYTES).array());
+        byte[] values = new byte[NUM_BYTES];
+        byteBuf.readBytes(values);
+        return of(values);
     }
 
     @Override
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/State.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/State.java
index 6c859d0..1c4a756 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/State.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/State.java
@@ -78,6 +78,8 @@ public class State extends UnsignedShortLEByteValue {
 
     public static final State DEFAULT = State.of(ADS_COMMAND);
 
+    public static final State DEFAULT_RESPONSE = State.of(ADS_COMMAND, RESPONSE);
+
     public static final int NUM_BYTES = UnsignedShortLEByteValue.NUM_BYTES;
 
     protected State(byte... values) {
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/ByteReadable.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/ByteReadable.java
index 9c3bd97..feadb17 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/ByteReadable.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/ByteReadable.java
@@ -24,7 +24,10 @@ import io.netty.buffer.ByteBuf;
 public interface ByteReadable extends LengthSupplier {
 
     default byte[] getBytes() {
-        return getByteBuf().array();
+        ByteBuf byteBuf = getByteBuf();
+        byte[] result = new byte[byteBuf.writerIndex()];
+        byteBuf.readBytes(result);
+        return result;
     }
 
     ByteBuf getByteBuf();
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/ByteValue.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/ByteValue.java
index bce6456..db39e44 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/ByteValue.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/ByteValue.java
@@ -21,12 +21,13 @@ package org.apache.plc4x.java.ads.api.util;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 
+import java.math.BigInteger;
 import java.util.Arrays;
 import java.util.Objects;
 
 public class ByteValue implements ByteReadable {
 
-    final byte[] value;
+    protected final byte[] value;
 
     public ByteValue(byte... value) {
         Objects.requireNonNull(value);
@@ -46,6 +47,13 @@ public class ByteValue implements ByteReadable {
         }
     }
 
+    public static void checkUnsignedBounds(BigInteger value, int numberOfBytes) {
+        BigInteger upperBound = BigInteger.valueOf(2).pow(8 * numberOfBytes);
+        if (value.compareTo(BigInteger.ZERO) < 0 || value.compareTo(upperBound) > 0) {
+            throw new IllegalArgumentException("Value must between 0 and " + upperBound + ". Was " + value);
+        }
+    }
+
     @Override
     public byte[] getBytes() {
         return value;
@@ -70,4 +78,11 @@ public class ByteValue implements ByteReadable {
     public int hashCode() {
         return Arrays.hashCode(value);
     }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "{" +
+            "value=" + Arrays.toString(value) +
+            '}';
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/UnsignedShortLEByteValue.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/UnsignedShortLEByteValue.java
index 89f2522..7b772ee 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/UnsignedShortLEByteValue.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/util/UnsignedShortLEByteValue.java
@@ -58,6 +58,8 @@ public abstract class UnsignedShortLEByteValue extends ByteValue {
 
     @Override
     public String toString() {
-        return String.valueOf(getAsInt());
+        return getClass().getSimpleName() + "{" +
+            "intValue=" + getAsInt() +
+            "} " + super.toString();
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/netty/ADSProtocol.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/netty/ADSProtocol.java
index c98ce36..37527e3 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/netty/ADSProtocol.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/netty/ADSProtocol.java
@@ -61,7 +61,7 @@ public class ADSProtocol extends MessageToMessageCodec<ByteBuf, AMSTCPPaket> {
     @Override
     protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> out) throws Exception {
         // Reserved
-        byteBuf.skipBytes(2);
+        byteBuf.skipBytes(AMSTCPHeader.Reserved.NUM_BYTES);
         long packetLength = byteBuf.readUnsignedIntLE();
         AMSNetId targetAmsNetId = AMSNetId.of(byteBuf);
         AMSPort targetAmsPort = AMSPort.of(byteBuf);
@@ -106,11 +106,13 @@ public class ADSProtocol extends MessageToMessageCodec<ByteBuf, AMSTCPPaket> {
                     out.add(new ADSReadRequest(amstcpHeader, amsHeader, indexGroup, indexOffset, length));
                 } else {
                     Result result = Result.of(commandBuffer);
-                    Length length = Length.of(byteBuf);
+                    Length length = Length.of(commandBuffer);
                     if (length.getAsLong() > Integer.MAX_VALUE) {
                         throw new IllegalStateException("Overflow in datalength: " + length.getAsLong());
                     }
-                    Data data = Data.of(commandBuffer.readBytes((int) length.getAsLong()).array());
+                    byte[] dataToRead = new byte[(int) length.getAsLong()];
+                    commandBuffer.readBytes(dataToRead);
+                    Data data = Data.of(dataToRead);
                     out.add(new ADSReadResponse(amstcpHeader, amsHeader, result, length, data));
                 }
                 break;
@@ -118,11 +120,13 @@ public class ADSProtocol extends MessageToMessageCodec<ByteBuf, AMSTCPPaket> {
                 if (stateId.isRequest()) {
                     IndexGroup indexGroup = IndexGroup.of(commandBuffer);
                     IndexOffset indexOffset = IndexOffset.of(commandBuffer);
-                    Length length = Length.of(byteBuf);
+                    Length length = Length.of(commandBuffer);
                     if (length.getAsLong() > Integer.MAX_VALUE) {
                         throw new IllegalStateException("Overflow in datalength: " + length.getAsLong());
                     }
-                    Data data = Data.of(commandBuffer.readBytes((int) length.getAsLong()).array());
+                    byte[] dataToRead = new byte[(int) length.getAsLong()];
+                    commandBuffer.readBytes(dataToRead);
+                    Data data = Data.of(dataToRead);
                     out.add(new ADSWriteRequest(amstcpHeader, amsHeader, indexGroup, indexOffset, length, data));
                 } else {
                     Result result = Result.of(commandBuffer);
@@ -140,12 +144,16 @@ public class ADSProtocol extends MessageToMessageCodec<ByteBuf, AMSTCPPaket> {
             case ADS_Write_Control:
                 if (stateId.isRequest()) {
                     ADSState adsState = ADSState.of(commandBuffer);
+                    LOGGER.info(""+adsState);
                     DeviceState deviceState = DeviceState.of(commandBuffer);
-                    Length length = Length.of(byteBuf);
+                    LOGGER.info(""+deviceState);
+                    Length length = Length.of(commandBuffer);
                     if (length.getAsLong() > Integer.MAX_VALUE) {
                         throw new IllegalStateException("Overflow in datalength: " + length.getAsLong());
                     }
-                    Data data = Data.of(commandBuffer.readBytes((int) length.getAsLong()).array());
+                    byte[] dataToRead = new byte[(int) length.getAsLong()];
+                    commandBuffer.readBytes(dataToRead);
+                    Data data = Data.of(dataToRead);
                     out.add(new ADSWriteControlRequest(amstcpHeader, amsHeader, adsState, deviceState, length, data));
                 } else {
                     Result result = Result.of(commandBuffer);
@@ -160,6 +168,7 @@ public class ADSProtocol extends MessageToMessageCodec<ByteBuf, AMSTCPPaket> {
                     TransmissionMode transmissionMode = TransmissionMode.of(commandBuffer);
                     MaxDelay maxDelay = MaxDelay.of(commandBuffer);
                     CycleTime cycleTime = CycleTime.of(commandBuffer);
+                    commandBuffer.skipBytes(ADSAddDeviceNotificationRequest.Reserved.NUM_BYTES);
                     out.add(new ADSAddDeviceNotificationRequest(amstcpHeader, amsHeader, indexGroup, indexOffset, length, transmissionMode, maxDelay, cycleTime));
                 } else {
                     Result result = Result.of(commandBuffer);
@@ -200,7 +209,9 @@ public class ADSProtocol extends MessageToMessageCodec<ByteBuf, AMSTCPPaket> {
                                 throw new IllegalStateException("Overflow in datalength: " + sampleSize.getAsLong());
                             }
                             // TODO: do we need a special marker class for: Notice: If your handle becomes invalid, one notification without data will be send once as advice.
-                            Data data = Data.of(adsDeviceNotificationBuffer.readBytes((int) sampleSize.getAsLong()).array());
+                            byte[] dataToRead = new byte[(int) sampleSize.getAsLong()];
+                            adsDeviceNotificationBuffer.readBytes(dataToRead);
+                            Data data = Data.of(dataToRead);
                             AdsNotificationSample adsNotificationSample = AdsNotificationSample.of(notificationHandle, sampleSize, data);
                             adsNotificationSamples.add(adsNotificationSample);
 
@@ -228,7 +239,9 @@ public class ADSProtocol extends MessageToMessageCodec<ByteBuf, AMSTCPPaket> {
                     if (readLength.getAsLong() + writeLength.getAsLong() > Integer.MAX_VALUE) {
                         throw new IllegalStateException("Overflow in datalength: " + readLength.getAsLong() + writeLength.getAsLong());
                     }
-                    Data data = Data.of(commandBuffer.readBytes((int) (readLength.getAsLong() + writeLength.getAsLong())).array());
+                    byte[] dataToRead = new byte[(int) readLength.getAsLong()];
+                    commandBuffer.readBytes(dataToRead);
+                    Data data = Data.of(dataToRead);
                     out.add(new ADSReadWriteRequest(amstcpHeader, amsHeader, indexGroup, indexOffset, readLength, writeLength, data));
                 } else {
                     Result result = Result.of(commandBuffer);
@@ -236,13 +249,17 @@ public class ADSProtocol extends MessageToMessageCodec<ByteBuf, AMSTCPPaket> {
                     if (length.getAsLong() > Integer.MAX_VALUE) {
                         throw new IllegalStateException("Overflow in datalength: " + length.getAsLong());
                     }
-                    Data data = Data.of(commandBuffer.readBytes((int) length.getAsLong()).array());
+                    byte[] dataToRead = new byte[(int) length.getAsLong()];
+                    commandBuffer.readBytes(dataToRead);
+                    Data data = Data.of(dataToRead);
                     out.add(new ADSReadWriteResponse(amstcpHeader, amsHeader, result, length, data));
                 }
                 break;
             case UNKNOWN:
                 out.add(new UnknownCommand(amstcpHeader, amsHeader, commandBuffer));
         }
+        // TODO: change it so we only do call out.add only once and log this on trace
+        LOGGER.info("Set to out {}", out.get(0));
         if (commandBuffer.readableBytes() > 0) {
             throw new IllegalStateException("Unread bytes left: " + commandBuffer.readableBytes());
         }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/netty/Plc4XADSProtocol.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/netty/Plc4XADSProtocol.java
index db9cbb6..32fd8b2 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/netty/Plc4XADSProtocol.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/netty/Plc4XADSProtocol.java
@@ -18,8 +18,6 @@ under the License.
 */
 package org.apache.plc4x.java.ads.netty;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToMessageCodec;
 import org.apache.commons.lang3.SerializationUtils;
@@ -49,6 +47,8 @@ import org.apache.plc4x.java.api.types.ResponseCode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.List;
@@ -102,13 +102,17 @@ public class Plc4XADSProtocol extends MessageToMessageCodec<AMSTCPPaket, PlcRequ
         Invoke invokeId = Invoke.of(correlationBuilder.incrementAndGet());
         IndexGroup indexGroup = IndexGroup.of(adsAddress.getIndexGroup());
         IndexOffset indexOffset = IndexOffset.of(adsAddress.getIndexOffset());
-        ByteBuf buffer = Unpooled.buffer();
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         for (Object o : writeRequestItem.getValues()) {
             // TODO: we need custom serialization here as java types don't really help here
             byte[] serialize = SerializationUtils.serialize((Serializable) o);
-            buffer.writeBytes(serialize);
+            try {
+                byteArrayOutputStream.write(serialize);
+            } catch (IOException e) {
+                throw new PlcException(e);
+            }
         }
-        byte[] bytes = buffer.array();
+        byte[] bytes = byteArrayOutputStream.toByteArray();
         Length length = Length.of(bytes.length);
         Data data = Data.of(bytes);
         AMSTCPPaket amstcpPaket = new ADSWriteRequest(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId, indexGroup, indexOffset, length, data);
diff --git a/plc4j/protocols/ads/src/site/asciidoc/index.adoc b/plc4j/protocols/ads/src/site/asciidoc/index.adoc
index 1f37781..1a42a5d 100644
--- a/plc4j/protocols/ads/src/site/asciidoc/index.adoc
+++ b/plc4j/protocols/ads/src/site/asciidoc/index.adoc
@@ -18,6 +18,8 @@
 
 == ADS Protocol Java Implementation
 
+// TODO: change this copy&paste from S7... document me
+
 The current version of the ADS protocol java driver is based upon Netty 4 (http://netty.io/).
 
 As the ADS protocol running on TCP is a hierarchy of protocols, the ADS driver is implemented by providing several Protocol implementations, each encoding one layer of the protocol stack.
diff --git a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/netty/ADSProtocolTest.java b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/netty/ADSProtocolTest.java
new file mode 100644
index 0000000..60b7fb3
--- /dev/null
+++ b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/netty/ADSProtocolTest.java
@@ -0,0 +1,199 @@
+/*
+ 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.ads.netty;
+
+import org.apache.commons.io.HexDump;
+import org.apache.plc4x.java.ads.api.commands.*;
+import org.apache.plc4x.java.ads.api.commands.types.*;
+import org.apache.plc4x.java.ads.api.generic.AMSTCPPaket;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(Parameterized.class)
+public class ADSProtocolTest {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ADSProtocolTest.class);
+
+    private ADSProtocol SUT;
+
+    @Parameterized.Parameter
+    public AMSTCPPaket amstcpPaket;
+
+    @Parameterized.Parameters(name = "{0} {index}")
+    public static Collection<Object[]> data() {
+        AMSNetId targetAmsNetId = AMSNetId.of("1.2.3.4.5.6");
+        AMSPort targetAmsPort = AMSPort.of(7);
+        AMSNetId sourceAmsNetId = AMSNetId.of("8.9.10.11.12.13");
+        AMSPort sourceAmsPort = AMSPort.of(14);
+        Invoke invokeId = Invoke.of(15);
+        Data data = Data.of("Hello World!".getBytes());
+        return Stream.of(
+            new ADSAddDeviceNotificationRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                IndexGroup.of(1), IndexOffset.of(1), Length.of(1), TransmissionMode.of(1), MaxDelay.of(1), CycleTime.of(1)),
+            new ADSAddDeviceNotificationResponse(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Result.of(0),
+                NotificationHandle.of(0)
+            ),
+            new ADSDeleteDeviceNotificationRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                NotificationHandle.of(0)
+            ),
+            new ADSDeleteDeviceNotificationResponse(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Result.of(0)
+            ),
+            new ADSDeviceNotificationRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                // TODO: must be calculated when using this constructor
+                Length.of(32),
+                Stamps.of(1),
+                Collections.singletonList(
+                    // Nano times need to be offset by (1.1.1970 - 1.1.1601) years in nanos
+                    AdsStampHeader.of(TimeStamp.of(System.nanoTime()),
+                        Samples.of(1),
+                        Collections.singletonList(
+                            AdsNotificationSample.of(NotificationHandle.of(0), SampleSize.of(data.getCalculatedLength()), data))
+                    )
+                )
+            ),
+            new ADSReadDeviceInfoRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId
+            ),
+            new ADSReadDeviceInfoResponse(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Result.of(0),
+                MajorVersion.of((byte) 1),
+                MinorVersion.of((byte) 2),
+                Version.of(3),
+                Device.of(
+                    (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6, (byte) 7, (byte) 8,
+                    (byte) 9, (byte) 10, (byte) 11, (byte) 12, (byte) 13, (byte) 14, (byte) 15, (byte) 16
+                )
+            ),
+            new ADSReadRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                IndexGroup.of(0),
+                IndexOffset.of(0),
+                Length.of(1)
+            ),
+            new ADSReadResponse(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Result.of(0),
+                Length.of(data.getCalculatedLength()),
+                data
+            ),
+            new ADSReadStateRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId
+            ),
+            new ADSReadStateResponse(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Result.of(0)
+            ),
+            new ADSReadWriteRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                IndexGroup.of(0),
+                IndexOffset.of(0),
+                ReadLength.of(data.getCalculatedLength()),
+                WriteLength.of(data.getCalculatedLength()),
+                data
+            ),
+            new ADSReadWriteResponse(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Result.of(0),
+                Length.of(data.getCalculatedLength()),
+                data
+            ),
+            new ADSWriteControlRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                ADSState.of(0xaffe),
+                DeviceState.of(0xaffe),
+                Length.of(data.getCalculatedLength()),
+                data
+            ),
+            new ADSWriteControlResponse(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Result.of(0)
+            ),
+            new ADSWriteRequest(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                IndexGroup.of(0),
+                IndexOffset.of(0),
+                Length.of(data.getCalculatedLength()),
+                data
+            ),
+            new ADSWriteResponse(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Result.of(0)
+            )/*,
+            new UnknownCommand(
+                targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, invokeId,
+                Unpooled.wrappedBuffer(new byte[]{42})
+            )*/
+        ).map(amstcpPaket -> new Object[]{amstcpPaket}).collect(Collectors.toList());
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        SUT = new ADSProtocol();
+        byte[] bytes = amstcpPaket.getBytes();
+        LOGGER.info("amstcpPaket:\n{} has \n{}bytes", amstcpPaket, bytes.length);
+        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+            HexDump.dump(bytes, 0, byteArrayOutputStream, 0);
+            byteArrayOutputStream.flush();
+            LOGGER.info("HexDump:\n{}", byteArrayOutputStream);
+        }
+    }
+
+    @Test
+    public void encode() throws Exception {
+        ArrayList<Object> out = new ArrayList<>();
+        SUT.encode(null, amstcpPaket, out);
+        assertEquals(1, out.size());
+        // TODO: replace with hamcrest
+        //assertThat(out, hasSize(1));
+    }
+
+    @Test
+    public void decode() throws Exception {
+        ArrayList<Object> out = new ArrayList<>();
+        SUT.decode(null, amstcpPaket.getByteBuf(), out);
+        assertEquals(1, out.size());
+        // TODO: replace with hamcrest
+        //assertThat(out, hasSize(1));
+    }
+
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
sruehl@apache.org.