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/11/18 10:14:43 UTC

[plc4x] branch feature/plc4go updated: - Implemented a general purpose manual testsuite class for testing all of the different datatypes. - Fixed multiple issues in the ADS driver -- The driver was missing a ByteLengthEstimator -- The parse methods for parsing of strings were not implemented -- Made the ADS spec ignore the 4 trailing bytes in case of a STRING and the 8 in case of a WSTRING - The ReadBuffer was shifting bytes in case of little-endianness, but was filling up in 2s complement changed that to fill with 0s - The Rea [...]

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

cdutz pushed a commit to branch feature/plc4go
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feature/plc4go by this push:
     new ce53c89  - Implemented a general purpose manual testsuite class for testing all of the different datatypes. - Fixed multiple issues in the ADS driver -- The driver was missing a ByteLengthEstimator -- The parse methods for parsing of strings were not implemented -- Made the ADS spec ignore the 4 trailing bytes in case of a STRING and the 8 in case of a WSTRING - The ReadBuffer was shifting bytes in case of little-endianness, but was filling up in 2s complement changed that to fil [...]
ce53c89 is described below

commit ce53c8984bfa40fe7873a942d5dc81f60fbec41b
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Wed Nov 18 11:14:35 2020 +0100

    - Implemented a general purpose manual testsuite class for testing all of the different datatypes.
    - Fixed multiple issues in the ADS driver
    -- The driver was missing a ByteLengthEstimator
    -- The parse methods for parsing of strings were not implemented
    -- Made the ADS spec ignore the 4 trailing bytes in case of a STRING and the 8 in case of a WSTRING
    - The ReadBuffer was shifting bytes in case of little-endianness, but was filling up in 2s complement changed that to fill with 0s
    - The ReadRequestBuilder was sorting the fields
    - Introduced a new PlcBitString PlcValue type simplifying converting byte, short, word and dword to lists of boolean values
    - Added numeric constructors for the Date&Time PlcValues
    -
---
 plc4j/drivers/ads/pom.xml                          |   4 +
 .../org/apache/plc4x/java/ads/ADSPlcDriver.java    |  15 +++
 .../apache/plc4x/java/ads/utils/StaticHelper.java  |  46 ++++++-
 .../plc4x/protocol/ads/ManualAdsDriverTest.java    |  53 +++++---
 ...erTest.java => ManualParserSerializerTest.java} |  18 +--
 .../plc4x/java/spi/generation/ReadBuffer.java      |   6 +-
 .../java/spi/messages/DefaultPlcReadRequest.java   |   3 +-
 .../apache/plc4x/java/spi/values/PlcBitString.java |  61 +++++++++
 .../plc4x/java/spi/values/PlcTIME_OF_DAY.java      |   3 +-
 .../org/apache/plc4x/test/manual/ManualTest.java   | 144 +++++++++++++++++++++
 .../ads/src/main/resources/protocols/ads/ads.mspec |  18 +--
 11 files changed, 320 insertions(+), 51 deletions(-)

diff --git a/plc4j/drivers/ads/pom.xml b/plc4j/drivers/ads/pom.xml
index d37eed2..800d57a 100644
--- a/plc4j/drivers/ads/pom.xml
+++ b/plc4j/drivers/ads/pom.xml
@@ -143,6 +143,10 @@
     </dependency>
 
     <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-buffer</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
     </dependency>
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
index d4553f2..fd5f25c 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
@@ -18,6 +18,7 @@
  */
 package org.apache.plc4x.java.ads;
 
+import io.netty.buffer.ByteBuf;
 import org.apache.plc4x.java.ads.configuration.AdsConfiguration;
 import org.apache.plc4x.java.ads.field.AdsFieldHandler;
 import org.apache.plc4x.java.ads.protocol.AdsProtocolLogic;
@@ -30,6 +31,8 @@ import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
 import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
 import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
 
+import java.util.function.ToIntFunction;
+
 /**
  * Implementation of the ADS protocol, based on:
  * - ADS Protocol
@@ -83,9 +86,21 @@ public class ADSPlcDriver extends GeneratedDriverBase<AmsTCPPacket> {
     @Override
     protected ProtocolStackConfigurer<AmsTCPPacket> getStackConfigurer() {
         return SingleProtocolStackConfigurer.builder(AmsTCPPacket.class, AmsTCPPacketIO.class)
+            .withPacketSizeEstimator(ByteLengthEstimator.class)
             .withProtocol(AdsProtocolLogic.class)
             .littleEndian()
             .build();
     }
 
+    /** Estimate the Length of a Packet */
+    public static class ByteLengthEstimator implements ToIntFunction<ByteBuf> {
+        @Override
+        public int applyAsInt(ByteBuf byteBuf) {
+            if (byteBuf.readableBytes() >= 6) {
+                return (int) byteBuf.getUnsignedIntLE(byteBuf.readerIndex() + 2) + 6;
+            }
+            return -1;
+        }
+    }
+
 }
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/utils/StaticHelper.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/utils/StaticHelper.java
index c374334..1ae2ca0 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/utils/StaticHelper.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/utils/StaticHelper.java
@@ -20,13 +20,55 @@ package org.apache.plc4x.java.ads.utils;
 
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
 public class StaticHelper {
 
-    public static String parseAmsString(ReadBuffer io, Object encoding) {
-        return "";
+    public static String parseAmsString(ReadBuffer io, String encoding) {
+        try {
+            if ("UTF-8".equalsIgnoreCase(encoding)) {
+                List<Byte> bytes = new ArrayList<>();
+                while (io.hasMore(8)) {
+                    final byte curByte = io.readByte(8);
+                    if (curByte != 0) {
+                        bytes.add(curByte);
+                    } else {
+                        final byte[] byteArray = new byte[bytes.size()];
+                        for (int i = 0; i < bytes.size(); i++) {
+                            byteArray[i] = bytes.get(i);
+                        }
+                        return new String(byteArray, StandardCharsets.UTF_8);
+                    }
+                }
+                throw new PlcRuntimeException("Reached the end of the input without finishing the string");
+            } else if ("UTF-16".equalsIgnoreCase(encoding)) {
+                List<Byte> bytes = new ArrayList<>();
+                while (io.hasMore(16)) {
+                    final short curShort = io.readShort(16);
+                    if (curShort != 0) {
+                        bytes.add((byte) (curShort >>> 8));
+                        bytes.add((byte) (curShort & 0xFF));
+                    } else {
+                        final byte[] byteArray = new byte[bytes.size()];
+                        for (int i = 0; i < bytes.size(); i++) {
+                            byteArray[i] = bytes.get(i);
+                        }
+                        return new String(byteArray, StandardCharsets.UTF_16);
+                    }
+                }
+                throw new PlcRuntimeException("Reached the end of the input without finishing the string");
+            } else {
+                throw new PlcRuntimeException("Unsupported string encoding " + encoding);
+            }
+        } catch (ParseException e) {
+            throw new PlcRuntimeException("Error parsing string", e);
+        }
     }
 
     public static void serializeAmsString(WriteBuffer io, PlcValue value, Object encoding) {
diff --git a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java
index 4685b74..ca2b17b 100644
--- a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java
+++ b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java
@@ -18,31 +18,42 @@ under the License.
 */
 package org.apache.plc4x.protocol.ads;
 
-import org.apache.commons.codec.binary.Hex;
-import org.apache.plc4x.java.ads.readwrite.AmsTCPPacket;
-import org.apache.plc4x.java.ads.readwrite.io.AmsTCPPacketIO;
-import org.apache.plc4x.java.api.PlcConnection;
-import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.test.manual.ManualTest;
 
-public class ManualAdsDriverTest {
-
-    public static void main(String[] args) throws Exception {
-        // Working request:
-        // 000050000000c0a8171401015303c0a817c80101feff09000400300000000000000002000000 81f0000002000000080000002000000006f00000000000000400000006f0000000000000040000000300801b3702001b
-        // Working response:
-        // 000030000000c0a817cd0101feffc0a81714010153030900050010000000000000000100000000000000080000000000000000000000
-
-        // PLC4X request:
-        // 000050000000c0a8171401015303c0a817c80101feff09000400300000000000000002000000 80f0000002000000020000002000000005f000000000801d010000000000000005f000003702001d0100000000000000
-        // PLC4X response:
-        // 000028000000c0a817c80101feffc0a8171401015303090005000800000000000000020000000507000000000000
+import java.util.Arrays;
 
+public class ManualAdsDriverTest extends ManualTest {
 
+    public ManualAdsDriverTest(String connectionString) {
+        super(connectionString);
+    }
 
-        byte[] data = Hex.decodeHex("000028000000c0a817c80101feffc0a8171401015303090005000800000000000000010000000507000000000000");
-        ReadBuffer readBuffer = new ReadBuffer(data, true);
-        final AmsTCPPacket amsTCPPacket = AmsTCPPacketIO.staticParse(readBuffer);
-        System.out.println(amsTCPPacket);
+    public static void main(String[] args) throws Exception {
+        ManualAdsDriverTest test = new ManualAdsDriverTest("ads:tcp://192.168.23.20?sourceAmsNetId=192.168.23.200.1.1&sourceAmsPort=65534&targetAmsNetId=192.168.23.20.1.1&targetAmsPort=851");
+        test.addTestCase("main.hurz_BOOL:BOOL", true);
+        test.addTestCase("main.hurz_BYTE:BYTE", Arrays.asList(false, false, true, false, true, false, true, false));
+        test.addTestCase("main.hurz_WORD:WORD", Arrays.asList(true, false, true, false, false, true, false, true, true, false, true, true, true, false, false, false));
+        test.addTestCase("main.hurz_DWORD:DWORD", Arrays.asList(true, true, true, true, true, true, false, false, true, true, false, true, true, true, true, false, true, false, false, false, true, false, false, false, true, false, true, true, true, false, false, false));
+        test.addTestCase("main.hurz_SINT:SINT", -42);
+        test.addTestCase("main.hurz_USINT:USINT", 42);
+        test.addTestCase("main.hurz_INT:INT", -2424);
+        test.addTestCase("main.hurz_UINT:UINT", 42424);
+        test.addTestCase("main.hurz_DINT:DINT", -242442424);
+        test.addTestCase("main.hurz_UDINT:UDINT", 4242442424L);
+        test.addTestCase("main.hurz_LINT:LINT", -4242442424242424242L);
+        test.addTestCase("main.hurz_ULINT:ULINT", 4242442424242424242L);
+        test.addTestCase("main.hurz_REAL:REAL", 3.14159265359F);
+        test.addTestCase("main.hurz_LREAL:LREAL", 2.71828182846D);
+        test.addTestCase("main.hurz_STRING:STRING", "hurz");
+        test.addTestCase("main.hurz_WSTRING:WSTRING", "wolf");
+        test.addTestCase("main.hurz_TIME:TIME", "PT1.234S");
+        test.addTestCase("main.hurz_LTIME:LTIME", "PT24015H23M12.034002044S");
+        test.addTestCase("main.hurz_DATE:DATE", "1978-03-28");
+        test.addTestCase("main.hurz_TIME_OF_DAY:TIME_OF_DAY", "15:36:30.123");
+        test.addTestCase("main.hurz_TOD:TOD", "16:17:18.123");
+        test.addTestCase("main.hurz_DATE_AND_TIME:DATE_AND_TIME", "1996-05-06T15:36:30");
+        test.addTestCase("main.hurz_DT:DT", "1972-03-29T00:00");
+        test.run();
     }
 
 }
diff --git a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualParserSerializerTest.java
similarity index 57%
copy from plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java
copy to plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualParserSerializerTest.java
index 4685b74..f6dd3c2 100644
--- a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java
+++ b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualParserSerializerTest.java
@@ -21,25 +21,13 @@ package org.apache.plc4x.protocol.ads;
 import org.apache.commons.codec.binary.Hex;
 import org.apache.plc4x.java.ads.readwrite.AmsTCPPacket;
 import org.apache.plc4x.java.ads.readwrite.io.AmsTCPPacketIO;
-import org.apache.plc4x.java.api.PlcConnection;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 
-public class ManualAdsDriverTest {
+public class ManualParserSerializerTest {
 
     public static void main(String[] args) throws Exception {
-        // Working request:
-        // 000050000000c0a8171401015303c0a817c80101feff09000400300000000000000002000000 81f0000002000000080000002000000006f00000000000000400000006f0000000000000040000000300801b3702001b
-        // Working response:
-        // 000030000000c0a817cd0101feffc0a81714010153030900050010000000000000000100000000000000080000000000000000000000
-
-        // PLC4X request:
-        // 000050000000c0a8171401015303c0a817c80101feff09000400300000000000000002000000 80f0000002000000020000002000000005f000000000801d010000000000000005f000003702001d0100000000000000
-        // PLC4X response:
-        // 000028000000c0a817c80101feffc0a8171401015303090005000800000000000000020000000507000000000000
-
-
-
-        byte[] data = Hex.decodeHex("000028000000c0a817c80101feffc0a8171401015303090005000800000000000000010000000507000000000000");
+        byte[] data = Hex.decodeHex("0000f1000000c0a817c80101feffc0a817140101530309000500d1000000000000002f00000000000000c90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012a00a07d0f7e1c8e31489f8cf1006b3604b888defc88f64eb6fbb217d11fc5cf5f148b0abf05407c941a93b8263301db0f4940d66875727a0000000000d2040000ab6459032bbf7e03b888defcb8a5b249044de82ee03a2ab [...]
+        //byte[] data = Hex.decodeHex("0000f1000000c0a817c80101feffc0a817140101530309000500d1000000000000002f00000000000000c90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012a00a07d0f7e1c8e31489f8cf1006b3604b888defc");
         ReadBuffer readBuffer = new ReadBuffer(data, true);
         final AmsTCPPacket amsTCPPacket = AmsTCPPacketIO.staticParse(readBuffer);
         System.out.println(amsTCPPacket);
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
index 57ef551..1beef09 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java
@@ -126,7 +126,8 @@ public class ReadBuffer {
         }
         try {
             if (littleEndian) {
-                return (Integer.reverseBytes(bi.readInt(true, bitLength)) >> 16) & 0xFFFF;
+                int intValue = bi.readInt(true, bitLength);
+                return Integer.reverseBytes(intValue) >>> 16;
             }
             return bi.readInt(true, bitLength);
         } catch (IOException e) {
@@ -143,7 +144,8 @@ public class ReadBuffer {
         }
         try {
             if (littleEndian) {
-                return Long.reverseBytes(bi.readLong(true, bitLength)) >> 32;
+                final long longValue = bi.readLong(true, bitLength);
+                return Long.reverseBytes(longValue) >>> 32;
             }
             return bi.readLong(true, bitLength);
         } catch (IOException e) {
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcReadRequest.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcReadRequest.java
index ace32d3..26bfe38 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcReadRequest.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcReadRequest.java
@@ -44,6 +44,7 @@ import java.util.stream.Collectors;
 public class DefaultPlcReadRequest implements PlcReadRequest, PlcFieldRequest, XmlSerializable {
 
     private final PlcReader reader;
+    // This is intentionally a linked hash map in order to keep the order of how elements were added.
     private LinkedHashMap<String, PlcField> fields;
 
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
@@ -135,7 +136,7 @@ public class DefaultPlcReadRequest implements PlcReadRequest, PlcFieldRequest, X
         public Builder(PlcReader reader, PlcFieldHandler fieldHandler) {
             this.reader = reader;
             this.fieldHandler = fieldHandler;
-            fields = new TreeMap<>();
+            fields = new LinkedHashMap<>();
         }
 
         @Override
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBitString.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBitString.java
new file mode 100644
index 0000000..3550816
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcBitString.java
@@ -0,0 +1,61 @@
+/*
+ * 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.spi.values;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.value.PlcValue;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
+public class PlcBitString extends PlcList {
+
+    public PlcBitString(short byteBitString) {
+        super(toBitString(BigInteger.valueOf(byteBitString), 8));
+    }
+
+    public PlcBitString(int wordBitString) {
+        super(toBitString(BigInteger.valueOf(wordBitString), 16));
+    }
+
+    public PlcBitString(long dwordBitString) {
+        super(toBitString(BigInteger.valueOf(dwordBitString), 32));
+    }
+
+    public PlcBitString(BigInteger lwordBitString) {
+        super(toBitString(lwordBitString, 64));
+    }
+
+    public static List<PlcValue> toBitString(BigInteger bigInteger, int numBits) {
+        if(bigInteger.bitCount() > numBits) {
+            throw new PlcRuntimeException("value too big");
+        }
+        // Convert the numeric value into an array of bits.
+        List<PlcValue> values = new ArrayList<>(numBits);
+        for (int i = numBits - 1; i >= 0; i--) {
+            values.add(new PlcBOOL(bigInteger.testBit(i)));
+        }
+        return values;
+    }
+
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcTIME_OF_DAY.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcTIME_OF_DAY.java
index f415ad8..4f59c74 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcTIME_OF_DAY.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcTIME_OF_DAY.java
@@ -26,7 +26,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.w3c.dom.Element;
 
-import java.time.Duration;
 import java.time.LocalTime;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
@@ -48,7 +47,7 @@ public class PlcTIME_OF_DAY extends PlcSimpleValue<LocalTime> {
 
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
     public PlcTIME_OF_DAY(@JsonProperty("value") Long value) {
-        super(LocalTime.ofSecondOfDay(value / 1000), true);
+        super(LocalTime.ofNanoOfDay(value * 1000000), true);
     }
 
     @Override
diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/manual/ManualTest.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/manual/ManualTest.java
new file mode 100644
index 0000000..c9110ec
--- /dev/null
+++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/manual/ManualTest.java
@@ -0,0 +1,144 @@
+/*
+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.test.manual;
+
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.spi.values.PlcList;
+import org.junit.jupiter.api.Assertions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+public abstract class ManualTest {
+
+    private final String connectionString;
+    private final List<TestCase> testCases;
+
+    public ManualTest(String connectionString) {
+        this.connectionString = connectionString;
+        testCases = new ArrayList<>();
+    }
+
+    public void addTestCase(String address, Object expectedValue) {
+        testCases.add(new TestCase(address, expectedValue));
+    }
+
+    public void run() throws Exception {
+        try (PlcConnection plcConnection = new PlcDriverManager().getConnection(connectionString)) {
+            System.out.println("Reading all types in separate requests");
+            // Run all entries separately:
+            for (TestCase testCase : testCases) {
+                String fieldName = testCase.address;
+                // Prepare the read-request
+                final PlcReadRequest readRequest = plcConnection.readRequestBuilder().addItem(fieldName, testCase.address).build();
+
+                // Execute the read request
+                final PlcReadResponse readResponse = readRequest.execute().get();
+
+                // Check the result
+                Assertions.assertEquals(1, readResponse.getFieldNames().size());
+                Assertions.assertEquals(fieldName, readResponse.getFieldNames().iterator().next());
+                Assertions.assertEquals(PlcResponseCode.OK, readResponse.getResponseCode(fieldName));
+                Assertions.assertNotNull(readResponse.getPlcValue(fieldName));
+                if(readResponse.getPlcValue(fieldName) instanceof PlcList) {
+                    PlcList plcList = (PlcList) readResponse.getPlcValue(fieldName);
+                    List<Object> expectedValues = (List<Object>) testCase.expectedValue;
+                    for (int j = 0; j < expectedValues.size(); j++) {
+                        Assertions.assertEquals(expectedValues.get(j), plcList.getIndex(j).getObject());
+                    }
+                } else {
+                    Assertions.assertEquals(
+                        testCase.expectedValue.toString(), readResponse.getPlcValue(fieldName).getObject().toString());
+                }
+            }
+            System.out.println("Success");
+
+
+            // Read all items in one big request.
+            // Shuffle the list of test cases and run the test 10 times.
+            System.out.println("Reading all items together in random order");
+            for (int i = 0; i < 100; i++) {
+                System.out.println(" - run number " + i + " of " + 100);
+                final List<TestCase> shuffledTestcases = new ArrayList<>(testCases);
+                Collections.shuffle(shuffledTestcases);
+
+                StringBuilder sb = new StringBuilder();
+                for (TestCase testCase : shuffledTestcases) {
+                    sb.append(testCase.address).append(", ");
+                }
+                System.out.println("       using order: " + sb.toString());
+
+                final PlcReadRequest.Builder builder = plcConnection.readRequestBuilder();
+                for (TestCase testCase : shuffledTestcases) {
+                    String fieldName = testCase.address;
+                    builder.addItem(fieldName, testCase.address);
+                }
+                final PlcReadRequest readRequest = builder.build();
+
+                // Execute the read request
+                final PlcReadResponse readResponse = readRequest.execute().get();
+
+                // Check the result
+                Assertions.assertEquals(shuffledTestcases.size(), readResponse.getFieldNames().size());
+                for (TestCase testCase : shuffledTestcases) {
+                    String fieldName = testCase.address;
+                    Assertions.assertEquals(PlcResponseCode.OK, readResponse.getResponseCode(fieldName));
+                    Assertions.assertNotNull(readResponse.getPlcValue(fieldName));
+                    if(readResponse.getPlcValue(fieldName) instanceof PlcList) {
+                        PlcList plcList = (PlcList) readResponse.getPlcValue(fieldName);
+                        List<Object> expectedValues = (List<Object>) testCase.expectedValue;
+                        for (int j = 0; j < expectedValues.size(); j++) {
+                            Assertions.assertEquals(expectedValues.get(j), plcList.getIndex(j).getObject());
+                        }
+                    } else {
+                        Assertions.assertEquals(
+                            testCase.expectedValue.toString(), readResponse.getPlcValue(fieldName).getObject().toString());
+                    }
+                }
+            }
+            System.out.println("Success");
+        }
+    }
+
+    public static class TestCase {
+        private final String address;
+        private final Object expectedValue;
+
+        public TestCase(String address, Object expectedValue) {
+            this.address = address;
+            this.expectedValue = expectedValue;
+        }
+
+        public String getAddress() {
+            return address;
+        }
+
+        public Object getExpectedValue() {
+            return expectedValue;
+        }
+    }
+
+
+}
diff --git a/protocols/ads/src/main/resources/protocols/ads/ads.mspec b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
index adcc785..508dcd0 100644
--- a/protocols/ads/src/main/resources/protocols/ads/ads.mspec
+++ b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
@@ -400,16 +400,16 @@
         // Bit-strings
         // -----------------------------------------
         // 1 byte
-        ['IEC61131_BYTE' List
-            [array bit 'value' count '8']
+        ['IEC61131_BYTE' BitString
+            [simple uint 8 'value']
         ]
         // 2 byte (16 bit)
-        ['IEC61131_WORD' List
-            [array bit 'value' count '16']
+        ['IEC61131_WORD' BitString
+            [simple uint 16 'value']
         ]
         // 4 byte (32 bit)
-        ['IEC61131_DWORD' List
-            [array bit 'value' count '32']
+        ['IEC61131_DWORD' BitString
+            [simple uint 32 'value']
         ]
 
         // -----------------------------------------
@@ -464,10 +464,12 @@
 //            [simple string 16 'UTF-16' 'value']
         ]
         ['IEC61131_STRING' STRING
-//            [manual string 'UTF-8' 'value' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.parseAmsString", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.serializeAmsString", io, _value, _type.encoding)' '_value.length + 2']
+            [manual   string  'UTF-8' 'value' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.parseAmsString", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.serializeAmsString", io, _value, _type.encoding)' '_value.length + 2']
+            [reserved uint 32 '0x00000000']
         ]
         ['IEC61131_WSTRING' STRING
-//            [manual string 'UTF-16' 'value' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.parseAmsString", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.serializeAmsString", io, _value, _type.encoding)' '_value.length + 2']
+            [manual string 'UTF-16' 'value' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.parseAmsString", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.serializeAmsString", io, _value, _type.encoding)' '_value.length + 2']
+            [reserved uint 64 '0x00000000']
         ]
 
         // -----------------------------------------