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 2022/09/26 08:39:13 UTC

[plc4x] branch develop updated: chore(protocol/ads): Work on the new ADS driver - Switched the dataIo parsing of STRING and WSTRING from manual fields to simple fields with vstring - Fixed a problem in the go code generation causing problems accessing the "stringLength" serializer argument in dataIo types - Implemented the new state-machine for PLC4J ads driver

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

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


The following commit(s) were added to refs/heads/develop by this push:
     new afd3939c6 chore(protocol/ads): Work on the new ADS driver - Switched the dataIo parsing of STRING and WSTRING from manual fields to simple fields with vstring - Fixed a problem in the go code generation causing problems accessing the "stringLength" serializer argument in dataIo types - Implemented the new state-machine for PLC4J ads driver
afd3939c6 is described below

commit afd3939c6eaf52670e69b3daaa4ecea87a5c3ab0
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Mon Sep 26 10:37:30 2022 +0200

    chore(protocol/ads): Work on the new ADS driver
    - Switched the dataIo parsing of STRING and WSTRING from manual fields to simple fields with vstring
    - Fixed a problem in the go code generation causing problems accessing the "stringLength" serializer argument in dataIo types
    - Implemented the new state-machine for PLC4J ads driver
---
 .../language/go/GoLanguageTemplateHelper.java      |  12 +-
 .../mspec/model/definitions/DefaultArgument.java   |   5 +
 plc4go/protocols/ads/readwrite/model/DataItem.go   |  44 +-
 .../ads/readwrite/model/DefaultAmsPorts.go         | 552 +++++++++++++++++++++
 .../ads/readwrite/model/ReservedIndexGroups.go     |   8 +
 .../knxnetip/readwrite/model/KnxManufacturer.go    |  34 +-
 plc4go/protocols/s7/readwrite/model/DataItem.go    |   4 +-
 .../plc4x/java/ads/protocol/AdsProtocolLogic.java  | 444 ++++++++++++-----
 .../java/ads/readwrite/utils/StaticHelper.java     |  93 ----
 .../plc4x/protocol/ads/ManualAdsDriverTest.java    |  50 +-
 .../java/canopen/readwrite/utils/StaticHelper.java |   2 +-
 .../java/s7/readwrite/utils/StaticHelper.java      |   2 +-
 .../plc4x/java/spi/generation/ReadBuffer.java      |   4 +-
 .../java/spi/generation/ReadBufferByteBased.java   |  69 ++-
 .../plc4x/java/spi/generation/ReadBufferTest.java  |   2 +-
 .../apache/plc4x/java/s7/utils/StaticHelper.java   |   2 +-
 .../org/apache/plc4x/test/manual/ManualTest.java   |  13 +-
 .../knxnetip/readwrite/model/KnxManufacturer.cs    |  23 +-
 .../ads/src/main/resources/protocols/ads/ads.mspec |  10 +-
 19 files changed, 1060 insertions(+), 313 deletions(-)

diff --git a/code-generation/language-go/src/main/java/org/apache/plc4x/language/go/GoLanguageTemplateHelper.java b/code-generation/language-go/src/main/java/org/apache/plc4x/language/go/GoLanguageTemplateHelper.java
index 0afc466df..858ef2c34 100644
--- a/code-generation/language-go/src/main/java/org/apache/plc4x/language/go/GoLanguageTemplateHelper.java
+++ b/code-generation/language-go/src/main/java/org/apache/plc4x/language/go/GoLanguageTemplateHelper.java
@@ -20,9 +20,11 @@ package org.apache.plc4x.language.go;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.model.definitions.DefaultArgument;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.model.references.DefaultBooleanTypeReference;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.model.references.DefaultFloatTypeReference;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.model.references.DefaultIntegerTypeReference;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.model.references.DefaultVstringTypeReference;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.model.terms.DefaultStringLiteral;
 import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.BaseFreemarkerLanguageTemplateHelper;
 import org.apache.plc4x.plugins.codegenerator.protocol.freemarker.FreemarkerException;
@@ -467,7 +469,7 @@ public class GoLanguageTemplateHelper extends BaseFreemarkerLanguageTemplateHelp
                     .orElseThrow(() -> new RuntimeException("Encoding must be a literal"))
                     .asStringLiteral()
                     .orElseThrow(() -> new RuntimeException("Encoding must be a quoted string value")).getValue();
-                String lengthExpression = toExpression(field, null, vstringTypeReference.getLengthExpression(), null, null, true, false);
+                String lengthExpression = toExpression(field, null, vstringTypeReference.getLengthExpression(), null, Collections.singletonList(new DefaultArgument("stringLength", new DefaultIntegerTypeReference(SimpleTypeReference.SimpleBaseType.INT, 32))), true, false);
                 String length = Integer.toString(simpleTypeReference.getSizeInBits());
                 return "writeBuffer.WriteString(\"" + logicalName + "\", uint32(" + lengthExpression + "), \"" +
                     encoding + "\", " + fieldName + writerArgsString + ")";
@@ -847,7 +849,15 @@ public class GoLanguageTemplateHelper extends BaseFreemarkerLanguageTemplateHelp
             }
         }
 
+        // This is a special case for DataIo string types, which need to access the stringLength
         if ((serializerArguments != null) && serializerArguments.stream()
+            .anyMatch(argument -> argument.getName().equals(variableLiteralName)) && "stringLength".equals(variableLiteralName)) {
+            tracer = tracer.dive("serialization argument");
+            return tracer + variableLiteralName +
+                variableLiteral.getChild()
+                    .map(child -> "." + capitalize(toVariableExpression(field, typeReference, child, parserArguments, serializerArguments, false, suppressPointerAccess, true)))
+                    .orElse("");
+        } else if ((serializerArguments != null) && serializerArguments.stream()
             .anyMatch(argument -> argument.getName().equals(variableLiteralName))) {
             tracer = tracer.dive("serialization argument");
             return tracer + "m." + capitalize(variableLiteralName) +
diff --git a/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultArgument.java b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultArgument.java
index 93c57f81b..df1fad80e 100644
--- a/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultArgument.java
+++ b/code-generation/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultArgument.java
@@ -37,6 +37,11 @@ public class DefaultArgument implements Argument {
         this.name = Objects.requireNonNull(name);
     }
 
+    public DefaultArgument(String name, TypeReference type) {
+        this.name = Objects.requireNonNull(name);
+        this.type = type;
+    }
+
     public String getName() {
         return name;
     }
diff --git a/plc4go/protocols/ads/readwrite/model/DataItem.go b/plc4go/protocols/ads/readwrite/model/DataItem.go
index ad0f76c64..4eb0283da 100644
--- a/plc4go/protocols/ads/readwrite/model/DataItem.go
+++ b/plc4go/protocols/ads/readwrite/model/DataItem.go
@@ -166,19 +166,29 @@ func DataItemParse(readBuffer utils.ReadBuffer, plcValueType PlcValueType, strin
 		readBuffer.CloseContext("DataItem")
 		return values.NewPlcSTRING(value), nil
 	case plcValueType == PlcValueType_STRING: // STRING
-		// Manual Field (value)
-		value, _valueErr := ParseAmsString(readBuffer, stringLength, "UTF-8")
+		// Simple Field (value)
+		value, _valueErr := readBuffer.ReadString("value", uint32((stringLength)*(8)))
 		if _valueErr != nil {
 			return nil, errors.Wrap(_valueErr, "Error parsing 'value' field")
 		}
+
+		// Reserved Field (Just skip the bytes)
+		if _, _err := readBuffer.ReadUint8("reserved", 8); _err != nil {
+			return nil, errors.Wrap(_err, "Error parsing reserved field")
+		}
 		readBuffer.CloseContext("DataItem")
 		return values.NewPlcSTRING(value), nil
 	case plcValueType == PlcValueType_WSTRING: // STRING
-		// Manual Field (value)
-		value, _valueErr := ParseAmsString(readBuffer, stringLength, "UTF-16")
+		// Simple Field (value)
+		value, _valueErr := readBuffer.ReadString("value", uint32(((stringLength)*(8))*(2)))
 		if _valueErr != nil {
 			return nil, errors.Wrap(_valueErr, "Error parsing 'value' field")
 		}
+
+		// Reserved Field (Just skip the bytes)
+		if _, _err := readBuffer.ReadUint16("reserved", 16); _err != nil {
+			return nil, errors.Wrap(_err, "Error parsing reserved field")
+		}
 		readBuffer.CloseContext("DataItem")
 		return values.NewPlcSTRING(value), nil
 	case plcValueType == PlcValueType_TIME: // TIME
@@ -320,20 +330,28 @@ func DataItemSerialize(writeBuffer utils.WriteBuffer, value api.PlcValue, plcVal
 		}
 	case plcValueType == PlcValueType_WCHAR: // STRING
 		// Simple Field (value)
-		if _err := writeBuffer.WriteString("value", uint32(16), "UTF-16", value.GetString()); _err != nil {
+		if _err := writeBuffer.WriteString("value", uint32(16), "UTF-16LE", value.GetString()); _err != nil {
 			return errors.Wrap(_err, "Error serializing 'value' field")
 		}
 	case plcValueType == PlcValueType_STRING: // STRING
-		// Manual Field (value)
-		_valueErr := SerializeAmsString(writeBuffer, value, m.StringLength, "UTF-8")
-		if _valueErr != nil {
-			return errors.Wrap(_valueErr, "Error serializing 'value' field")
+		// Simple Field (value)
+		if _err := writeBuffer.WriteString("value", uint32((stringLength)*(8)), "UTF-8", value.GetString()); _err != nil {
+			return errors.Wrap(_err, "Error serializing 'value' field")
+		}
+
+		// Reserved Field (Just skip the bytes)
+		if _err := writeBuffer.WriteUint8("reserved", 8, uint8(0x00)); _err != nil {
+			return errors.Wrap(_err, "Error serializing reserved field")
 		}
 	case plcValueType == PlcValueType_WSTRING: // STRING
-		// Manual Field (value)
-		_valueErr := SerializeAmsString(writeBuffer, value, m.StringLength, "UTF-16")
-		if _valueErr != nil {
-			return errors.Wrap(_valueErr, "Error serializing 'value' field")
+		// Simple Field (value)
+		if _err := writeBuffer.WriteString("value", uint32(((stringLength)*(8))*(2)), "UTF-16LE", value.GetString()); _err != nil {
+			return errors.Wrap(_err, "Error serializing 'value' field")
+		}
+
+		// Reserved Field (Just skip the bytes)
+		if _err := writeBuffer.WriteUint16("reserved", 16, uint16(0x0000)); _err != nil {
+			return errors.Wrap(_err, "Error serializing reserved field")
 		}
 	case plcValueType == PlcValueType_TIME: // TIME
 		// Simple Field (value)
diff --git a/plc4go/protocols/ads/readwrite/model/DefaultAmsPorts.go b/plc4go/protocols/ads/readwrite/model/DefaultAmsPorts.go
new file mode 100644
index 000000000..2219b94a7
--- /dev/null
+++ b/plc4go/protocols/ads/readwrite/model/DefaultAmsPorts.go
@@ -0,0 +1,552 @@
+/*
+ * 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
+ *
+ *   https://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 model
+
+import (
+	"github.com/apache/plc4x/plc4go/spi/utils"
+	"github.com/pkg/errors"
+)
+
+// Code generated by code-generation. DO NOT EDIT.
+
+// DefaultAmsPorts is an enum
+type DefaultAmsPorts uint16
+
+type IDefaultAmsPorts interface {
+	Serialize(writeBuffer utils.WriteBuffer) error
+}
+
+const (
+	DefaultAmsPorts_CAM_CONTROLLER      DefaultAmsPorts = 900
+	DefaultAmsPorts_RUNTIME_SYSTEM_01   DefaultAmsPorts = 851
+	DefaultAmsPorts_RUNTIME_SYSTEM_02   DefaultAmsPorts = 852
+	DefaultAmsPorts_RUNTIME_SYSTEM_03   DefaultAmsPorts = 853
+	DefaultAmsPorts_RUNTIME_SYSTEM_04   DefaultAmsPorts = 854
+	DefaultAmsPorts_RUNTIME_SYSTEM_05   DefaultAmsPorts = 855
+	DefaultAmsPorts_RUNTIME_SYSTEM_06   DefaultAmsPorts = 856
+	DefaultAmsPorts_RUNTIME_SYSTEM_07   DefaultAmsPorts = 857
+	DefaultAmsPorts_RUNTIME_SYSTEM_08   DefaultAmsPorts = 858
+	DefaultAmsPorts_RUNTIME_SYSTEM_09   DefaultAmsPorts = 859
+	DefaultAmsPorts_RUNTIME_SYSTEM_10   DefaultAmsPorts = 860
+	DefaultAmsPorts_RUNTIME_SYSTEM_11   DefaultAmsPorts = 861
+	DefaultAmsPorts_RUNTIME_SYSTEM_12   DefaultAmsPorts = 862
+	DefaultAmsPorts_RUNTIME_SYSTEM_13   DefaultAmsPorts = 863
+	DefaultAmsPorts_RUNTIME_SYSTEM_14   DefaultAmsPorts = 864
+	DefaultAmsPorts_RUNTIME_SYSTEM_15   DefaultAmsPorts = 865
+	DefaultAmsPorts_RUNTIME_SYSTEM_16   DefaultAmsPorts = 866
+	DefaultAmsPorts_RUNTIME_SYSTEM_17   DefaultAmsPorts = 867
+	DefaultAmsPorts_RUNTIME_SYSTEM_18   DefaultAmsPorts = 868
+	DefaultAmsPorts_RUNTIME_SYSTEM_19   DefaultAmsPorts = 869
+	DefaultAmsPorts_RUNTIME_SYSTEM_20   DefaultAmsPorts = 870
+	DefaultAmsPorts_RUNTIME_SYSTEM_21   DefaultAmsPorts = 871
+	DefaultAmsPorts_RUNTIME_SYSTEM_22   DefaultAmsPorts = 872
+	DefaultAmsPorts_RUNTIME_SYSTEM_23   DefaultAmsPorts = 873
+	DefaultAmsPorts_RUNTIME_SYSTEM_24   DefaultAmsPorts = 874
+	DefaultAmsPorts_RUNTIME_SYSTEM_25   DefaultAmsPorts = 875
+	DefaultAmsPorts_RUNTIME_SYSTEM_26   DefaultAmsPorts = 876
+	DefaultAmsPorts_RUNTIME_SYSTEM_27   DefaultAmsPorts = 877
+	DefaultAmsPorts_RUNTIME_SYSTEM_28   DefaultAmsPorts = 878
+	DefaultAmsPorts_RUNTIME_SYSTEM_29   DefaultAmsPorts = 879
+	DefaultAmsPorts_RUNTIME_SYSTEM_30   DefaultAmsPorts = 880
+	DefaultAmsPorts_RUNTIME_SYSTEM_31   DefaultAmsPorts = 881
+	DefaultAmsPorts_RUNTIME_SYSTEM_32   DefaultAmsPorts = 882
+	DefaultAmsPorts_RUNTIME_SYSTEM_33   DefaultAmsPorts = 883
+	DefaultAmsPorts_RUNTIME_SYSTEM_34   DefaultAmsPorts = 884
+	DefaultAmsPorts_RUNTIME_SYSTEM_35   DefaultAmsPorts = 885
+	DefaultAmsPorts_RUNTIME_SYSTEM_36   DefaultAmsPorts = 886
+	DefaultAmsPorts_RUNTIME_SYSTEM_37   DefaultAmsPorts = 887
+	DefaultAmsPorts_RUNTIME_SYSTEM_38   DefaultAmsPorts = 888
+	DefaultAmsPorts_RUNTIME_SYSTEM_39   DefaultAmsPorts = 889
+	DefaultAmsPorts_RUNTIME_SYSTEM_40   DefaultAmsPorts = 890
+	DefaultAmsPorts_RUNTIME_SYSTEM_41   DefaultAmsPorts = 891
+	DefaultAmsPorts_RUNTIME_SYSTEM_42   DefaultAmsPorts = 892
+	DefaultAmsPorts_RUNTIME_SYSTEM_43   DefaultAmsPorts = 893
+	DefaultAmsPorts_RUNTIME_SYSTEM_44   DefaultAmsPorts = 894
+	DefaultAmsPorts_RUNTIME_SYSTEM_45   DefaultAmsPorts = 895
+	DefaultAmsPorts_RUNTIME_SYSTEM_46   DefaultAmsPorts = 896
+	DefaultAmsPorts_RUNTIME_SYSTEM_47   DefaultAmsPorts = 897
+	DefaultAmsPorts_RUNTIME_SYSTEM_48   DefaultAmsPorts = 898
+	DefaultAmsPorts_RUNTIME_SYSTEM_49   DefaultAmsPorts = 899
+	DefaultAmsPorts_NC                  DefaultAmsPorts = 500
+	DefaultAmsPorts_RESERVED            DefaultAmsPorts = 400
+	DefaultAmsPorts_IO                  DefaultAmsPorts = 300
+	DefaultAmsPorts_REAL_TIME_CORE      DefaultAmsPorts = 200
+	DefaultAmsPorts_EVENT_SYSTEM_LOGGER DefaultAmsPorts = 100
+)
+
+var DefaultAmsPortsValues []DefaultAmsPorts
+
+func init() {
+	_ = errors.New
+	DefaultAmsPortsValues = []DefaultAmsPorts{
+		DefaultAmsPorts_CAM_CONTROLLER,
+		DefaultAmsPorts_RUNTIME_SYSTEM_01,
+		DefaultAmsPorts_RUNTIME_SYSTEM_02,
+		DefaultAmsPorts_RUNTIME_SYSTEM_03,
+		DefaultAmsPorts_RUNTIME_SYSTEM_04,
+		DefaultAmsPorts_RUNTIME_SYSTEM_05,
+		DefaultAmsPorts_RUNTIME_SYSTEM_06,
+		DefaultAmsPorts_RUNTIME_SYSTEM_07,
+		DefaultAmsPorts_RUNTIME_SYSTEM_08,
+		DefaultAmsPorts_RUNTIME_SYSTEM_09,
+		DefaultAmsPorts_RUNTIME_SYSTEM_10,
+		DefaultAmsPorts_RUNTIME_SYSTEM_11,
+		DefaultAmsPorts_RUNTIME_SYSTEM_12,
+		DefaultAmsPorts_RUNTIME_SYSTEM_13,
+		DefaultAmsPorts_RUNTIME_SYSTEM_14,
+		DefaultAmsPorts_RUNTIME_SYSTEM_15,
+		DefaultAmsPorts_RUNTIME_SYSTEM_16,
+		DefaultAmsPorts_RUNTIME_SYSTEM_17,
+		DefaultAmsPorts_RUNTIME_SYSTEM_18,
+		DefaultAmsPorts_RUNTIME_SYSTEM_19,
+		DefaultAmsPorts_RUNTIME_SYSTEM_20,
+		DefaultAmsPorts_RUNTIME_SYSTEM_21,
+		DefaultAmsPorts_RUNTIME_SYSTEM_22,
+		DefaultAmsPorts_RUNTIME_SYSTEM_23,
+		DefaultAmsPorts_RUNTIME_SYSTEM_24,
+		DefaultAmsPorts_RUNTIME_SYSTEM_25,
+		DefaultAmsPorts_RUNTIME_SYSTEM_26,
+		DefaultAmsPorts_RUNTIME_SYSTEM_27,
+		DefaultAmsPorts_RUNTIME_SYSTEM_28,
+		DefaultAmsPorts_RUNTIME_SYSTEM_29,
+		DefaultAmsPorts_RUNTIME_SYSTEM_30,
+		DefaultAmsPorts_RUNTIME_SYSTEM_31,
+		DefaultAmsPorts_RUNTIME_SYSTEM_32,
+		DefaultAmsPorts_RUNTIME_SYSTEM_33,
+		DefaultAmsPorts_RUNTIME_SYSTEM_34,
+		DefaultAmsPorts_RUNTIME_SYSTEM_35,
+		DefaultAmsPorts_RUNTIME_SYSTEM_36,
+		DefaultAmsPorts_RUNTIME_SYSTEM_37,
+		DefaultAmsPorts_RUNTIME_SYSTEM_38,
+		DefaultAmsPorts_RUNTIME_SYSTEM_39,
+		DefaultAmsPorts_RUNTIME_SYSTEM_40,
+		DefaultAmsPorts_RUNTIME_SYSTEM_41,
+		DefaultAmsPorts_RUNTIME_SYSTEM_42,
+		DefaultAmsPorts_RUNTIME_SYSTEM_43,
+		DefaultAmsPorts_RUNTIME_SYSTEM_44,
+		DefaultAmsPorts_RUNTIME_SYSTEM_45,
+		DefaultAmsPorts_RUNTIME_SYSTEM_46,
+		DefaultAmsPorts_RUNTIME_SYSTEM_47,
+		DefaultAmsPorts_RUNTIME_SYSTEM_48,
+		DefaultAmsPorts_RUNTIME_SYSTEM_49,
+		DefaultAmsPorts_NC,
+		DefaultAmsPorts_RESERVED,
+		DefaultAmsPorts_IO,
+		DefaultAmsPorts_REAL_TIME_CORE,
+		DefaultAmsPorts_EVENT_SYSTEM_LOGGER,
+	}
+}
+
+func DefaultAmsPortsByValue(value uint16) (enum DefaultAmsPorts, ok bool) {
+	switch value {
+	case 100:
+		return DefaultAmsPorts_EVENT_SYSTEM_LOGGER, true
+	case 200:
+		return DefaultAmsPorts_REAL_TIME_CORE, true
+	case 300:
+		return DefaultAmsPorts_IO, true
+	case 400:
+		return DefaultAmsPorts_RESERVED, true
+	case 500:
+		return DefaultAmsPorts_NC, true
+	case 851:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_01, true
+	case 852:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_02, true
+	case 853:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_03, true
+	case 854:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_04, true
+	case 855:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_05, true
+	case 856:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_06, true
+	case 857:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_07, true
+	case 858:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_08, true
+	case 859:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_09, true
+	case 860:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_10, true
+	case 861:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_11, true
+	case 862:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_12, true
+	case 863:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_13, true
+	case 864:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_14, true
+	case 865:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_15, true
+	case 866:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_16, true
+	case 867:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_17, true
+	case 868:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_18, true
+	case 869:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_19, true
+	case 870:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_20, true
+	case 871:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_21, true
+	case 872:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_22, true
+	case 873:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_23, true
+	case 874:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_24, true
+	case 875:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_25, true
+	case 876:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_26, true
+	case 877:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_27, true
+	case 878:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_28, true
+	case 879:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_29, true
+	case 880:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_30, true
+	case 881:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_31, true
+	case 882:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_32, true
+	case 883:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_33, true
+	case 884:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_34, true
+	case 885:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_35, true
+	case 886:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_36, true
+	case 887:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_37, true
+	case 888:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_38, true
+	case 889:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_39, true
+	case 890:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_40, true
+	case 891:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_41, true
+	case 892:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_42, true
+	case 893:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_43, true
+	case 894:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_44, true
+	case 895:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_45, true
+	case 896:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_46, true
+	case 897:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_47, true
+	case 898:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_48, true
+	case 899:
+		return DefaultAmsPorts_RUNTIME_SYSTEM_49, true
+	case 900:
+		return DefaultAmsPorts_CAM_CONTROLLER, true
+	}
+	return 0, false
+}
+
+func DefaultAmsPortsByName(value string) (enum DefaultAmsPorts, ok bool) {
+	switch value {
+	case "EVENT_SYSTEM_LOGGER":
+		return DefaultAmsPorts_EVENT_SYSTEM_LOGGER, true
+	case "REAL_TIME_CORE":
+		return DefaultAmsPorts_REAL_TIME_CORE, true
+	case "IO":
+		return DefaultAmsPorts_IO, true
+	case "RESERVED":
+		return DefaultAmsPorts_RESERVED, true
+	case "NC":
+		return DefaultAmsPorts_NC, true
+	case "RUNTIME_SYSTEM_01":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_01, true
+	case "RUNTIME_SYSTEM_02":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_02, true
+	case "RUNTIME_SYSTEM_03":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_03, true
+	case "RUNTIME_SYSTEM_04":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_04, true
+	case "RUNTIME_SYSTEM_05":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_05, true
+	case "RUNTIME_SYSTEM_06":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_06, true
+	case "RUNTIME_SYSTEM_07":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_07, true
+	case "RUNTIME_SYSTEM_08":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_08, true
+	case "RUNTIME_SYSTEM_09":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_09, true
+	case "RUNTIME_SYSTEM_10":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_10, true
+	case "RUNTIME_SYSTEM_11":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_11, true
+	case "RUNTIME_SYSTEM_12":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_12, true
+	case "RUNTIME_SYSTEM_13":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_13, true
+	case "RUNTIME_SYSTEM_14":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_14, true
+	case "RUNTIME_SYSTEM_15":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_15, true
+	case "RUNTIME_SYSTEM_16":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_16, true
+	case "RUNTIME_SYSTEM_17":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_17, true
+	case "RUNTIME_SYSTEM_18":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_18, true
+	case "RUNTIME_SYSTEM_19":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_19, true
+	case "RUNTIME_SYSTEM_20":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_20, true
+	case "RUNTIME_SYSTEM_21":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_21, true
+	case "RUNTIME_SYSTEM_22":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_22, true
+	case "RUNTIME_SYSTEM_23":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_23, true
+	case "RUNTIME_SYSTEM_24":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_24, true
+	case "RUNTIME_SYSTEM_25":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_25, true
+	case "RUNTIME_SYSTEM_26":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_26, true
+	case "RUNTIME_SYSTEM_27":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_27, true
+	case "RUNTIME_SYSTEM_28":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_28, true
+	case "RUNTIME_SYSTEM_29":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_29, true
+	case "RUNTIME_SYSTEM_30":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_30, true
+	case "RUNTIME_SYSTEM_31":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_31, true
+	case "RUNTIME_SYSTEM_32":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_32, true
+	case "RUNTIME_SYSTEM_33":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_33, true
+	case "RUNTIME_SYSTEM_34":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_34, true
+	case "RUNTIME_SYSTEM_35":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_35, true
+	case "RUNTIME_SYSTEM_36":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_36, true
+	case "RUNTIME_SYSTEM_37":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_37, true
+	case "RUNTIME_SYSTEM_38":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_38, true
+	case "RUNTIME_SYSTEM_39":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_39, true
+	case "RUNTIME_SYSTEM_40":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_40, true
+	case "RUNTIME_SYSTEM_41":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_41, true
+	case "RUNTIME_SYSTEM_42":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_42, true
+	case "RUNTIME_SYSTEM_43":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_43, true
+	case "RUNTIME_SYSTEM_44":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_44, true
+	case "RUNTIME_SYSTEM_45":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_45, true
+	case "RUNTIME_SYSTEM_46":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_46, true
+	case "RUNTIME_SYSTEM_47":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_47, true
+	case "RUNTIME_SYSTEM_48":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_48, true
+	case "RUNTIME_SYSTEM_49":
+		return DefaultAmsPorts_RUNTIME_SYSTEM_49, true
+	case "CAM_CONTROLLER":
+		return DefaultAmsPorts_CAM_CONTROLLER, true
+	}
+	return 0, false
+}
+
+func DefaultAmsPortsKnows(value uint16) bool {
+	for _, typeValue := range DefaultAmsPortsValues {
+		if uint16(typeValue) == value {
+			return true
+		}
+	}
+	return false
+}
+
+func CastDefaultAmsPorts(structType interface{}) DefaultAmsPorts {
+	castFunc := func(typ interface{}) DefaultAmsPorts {
+		if sDefaultAmsPorts, ok := typ.(DefaultAmsPorts); ok {
+			return sDefaultAmsPorts
+		}
+		return 0
+	}
+	return castFunc(structType)
+}
+
+func (m DefaultAmsPorts) GetLengthInBits() uint16 {
+	return 16
+}
+
+func (m DefaultAmsPorts) GetLengthInBytes() uint16 {
+	return m.GetLengthInBits() / 8
+}
+
+func DefaultAmsPortsParse(readBuffer utils.ReadBuffer) (DefaultAmsPorts, error) {
+	val, err := readBuffer.ReadUint16("DefaultAmsPorts", 16)
+	if err != nil {
+		return 0, errors.Wrap(err, "error reading DefaultAmsPorts")
+	}
+	if enum, ok := DefaultAmsPortsByValue(val); !ok {
+		Plc4xModelLog.Debug().Msgf("no value %x found for RequestType", val)
+		return DefaultAmsPorts(val), nil
+	} else {
+		return enum, nil
+	}
+}
+
+func (e DefaultAmsPorts) Serialize(writeBuffer utils.WriteBuffer) error {
+	return writeBuffer.WriteUint16("DefaultAmsPorts", 16, uint16(e), utils.WithAdditionalStringRepresentation(e.PLC4XEnumName()))
+}
+
+// PLC4XEnumName returns the name that is used in code to identify this enum
+func (e DefaultAmsPorts) PLC4XEnumName() string {
+	switch e {
+	case DefaultAmsPorts_EVENT_SYSTEM_LOGGER:
+		return "EVENT_SYSTEM_LOGGER"
+	case DefaultAmsPorts_REAL_TIME_CORE:
+		return "REAL_TIME_CORE"
+	case DefaultAmsPorts_IO:
+		return "IO"
+	case DefaultAmsPorts_RESERVED:
+		return "RESERVED"
+	case DefaultAmsPorts_NC:
+		return "NC"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_01:
+		return "RUNTIME_SYSTEM_01"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_02:
+		return "RUNTIME_SYSTEM_02"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_03:
+		return "RUNTIME_SYSTEM_03"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_04:
+		return "RUNTIME_SYSTEM_04"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_05:
+		return "RUNTIME_SYSTEM_05"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_06:
+		return "RUNTIME_SYSTEM_06"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_07:
+		return "RUNTIME_SYSTEM_07"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_08:
+		return "RUNTIME_SYSTEM_08"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_09:
+		return "RUNTIME_SYSTEM_09"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_10:
+		return "RUNTIME_SYSTEM_10"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_11:
+		return "RUNTIME_SYSTEM_11"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_12:
+		return "RUNTIME_SYSTEM_12"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_13:
+		return "RUNTIME_SYSTEM_13"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_14:
+		return "RUNTIME_SYSTEM_14"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_15:
+		return "RUNTIME_SYSTEM_15"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_16:
+		return "RUNTIME_SYSTEM_16"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_17:
+		return "RUNTIME_SYSTEM_17"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_18:
+		return "RUNTIME_SYSTEM_18"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_19:
+		return "RUNTIME_SYSTEM_19"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_20:
+		return "RUNTIME_SYSTEM_20"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_21:
+		return "RUNTIME_SYSTEM_21"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_22:
+		return "RUNTIME_SYSTEM_22"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_23:
+		return "RUNTIME_SYSTEM_23"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_24:
+		return "RUNTIME_SYSTEM_24"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_25:
+		return "RUNTIME_SYSTEM_25"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_26:
+		return "RUNTIME_SYSTEM_26"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_27:
+		return "RUNTIME_SYSTEM_27"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_28:
+		return "RUNTIME_SYSTEM_28"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_29:
+		return "RUNTIME_SYSTEM_29"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_30:
+		return "RUNTIME_SYSTEM_30"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_31:
+		return "RUNTIME_SYSTEM_31"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_32:
+		return "RUNTIME_SYSTEM_32"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_33:
+		return "RUNTIME_SYSTEM_33"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_34:
+		return "RUNTIME_SYSTEM_34"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_35:
+		return "RUNTIME_SYSTEM_35"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_36:
+		return "RUNTIME_SYSTEM_36"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_37:
+		return "RUNTIME_SYSTEM_37"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_38:
+		return "RUNTIME_SYSTEM_38"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_39:
+		return "RUNTIME_SYSTEM_39"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_40:
+		return "RUNTIME_SYSTEM_40"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_41:
+		return "RUNTIME_SYSTEM_41"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_42:
+		return "RUNTIME_SYSTEM_42"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_43:
+		return "RUNTIME_SYSTEM_43"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_44:
+		return "RUNTIME_SYSTEM_44"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_45:
+		return "RUNTIME_SYSTEM_45"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_46:
+		return "RUNTIME_SYSTEM_46"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_47:
+		return "RUNTIME_SYSTEM_47"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_48:
+		return "RUNTIME_SYSTEM_48"
+	case DefaultAmsPorts_RUNTIME_SYSTEM_49:
+		return "RUNTIME_SYSTEM_49"
+	case DefaultAmsPorts_CAM_CONTROLLER:
+		return "CAM_CONTROLLER"
+	}
+	return ""
+}
+
+func (e DefaultAmsPorts) String() string {
+	return e.PLC4XEnumName()
+}
diff --git a/plc4go/protocols/ads/readwrite/model/ReservedIndexGroups.go b/plc4go/protocols/ads/readwrite/model/ReservedIndexGroups.go
index 4ea3e9c11..11990ece9 100644
--- a/plc4go/protocols/ads/readwrite/model/ReservedIndexGroups.go
+++ b/plc4go/protocols/ads/readwrite/model/ReservedIndexGroups.go
@@ -50,6 +50,7 @@ const (
 	ReservedIndexGroups_ADSIGRP_DATA_TYPE_TABLE_UPLOAD               ReservedIndexGroups = 0x0000F00E
 	ReservedIndexGroups_ADSIGRP_SYMBOL_AND_DATA_TYPE_SIZES           ReservedIndexGroups = 0x0000F00F
 	ReservedIndexGroups_ADSIGRP_SYMNOTE                              ReservedIndexGroups = 0x0000F010
+	ReservedIndexGroups_ADSIGRP_DT_INFOBYNAMEEX                      ReservedIndexGroups = 0x0000F011
 	ReservedIndexGroups_ADSIGRP_IOIMAGE_RWIB                         ReservedIndexGroups = 0x0000F020
 	ReservedIndexGroups_ADSIGRP_IOIMAGE_RWIX                         ReservedIndexGroups = 0x0000F021
 	ReservedIndexGroups_ADSIGRP_IOIMAGE_RISIZE                       ReservedIndexGroups = 0x0000F025
@@ -92,6 +93,7 @@ func init() {
 		ReservedIndexGroups_ADSIGRP_DATA_TYPE_TABLE_UPLOAD,
 		ReservedIndexGroups_ADSIGRP_SYMBOL_AND_DATA_TYPE_SIZES,
 		ReservedIndexGroups_ADSIGRP_SYMNOTE,
+		ReservedIndexGroups_ADSIGRP_DT_INFOBYNAMEEX,
 		ReservedIndexGroups_ADSIGRP_IOIMAGE_RWIB,
 		ReservedIndexGroups_ADSIGRP_IOIMAGE_RWIX,
 		ReservedIndexGroups_ADSIGRP_IOIMAGE_RISIZE,
@@ -152,6 +154,8 @@ func ReservedIndexGroupsByValue(value uint32) (enum ReservedIndexGroups, ok bool
 		return ReservedIndexGroups_ADSIGRP_SYMBOL_AND_DATA_TYPE_SIZES, true
 	case 0x0000F010:
 		return ReservedIndexGroups_ADSIGRP_SYMNOTE, true
+	case 0x0000F011:
+		return ReservedIndexGroups_ADSIGRP_DT_INFOBYNAMEEX, true
 	case 0x0000F020:
 		return ReservedIndexGroups_ADSIGRP_IOIMAGE_RWIB, true
 	case 0x0000F021:
@@ -228,6 +232,8 @@ func ReservedIndexGroupsByName(value string) (enum ReservedIndexGroups, ok bool)
 		return ReservedIndexGroups_ADSIGRP_SYMBOL_AND_DATA_TYPE_SIZES, true
 	case "ADSIGRP_SYMNOTE":
 		return ReservedIndexGroups_ADSIGRP_SYMNOTE, true
+	case "ADSIGRP_DT_INFOBYNAMEEX":
+		return ReservedIndexGroups_ADSIGRP_DT_INFOBYNAMEEX, true
 	case "ADSIGRP_IOIMAGE_RWIB":
 		return ReservedIndexGroups_ADSIGRP_IOIMAGE_RWIB, true
 	case "ADSIGRP_IOIMAGE_RWIX":
@@ -349,6 +355,8 @@ func (e ReservedIndexGroups) PLC4XEnumName() string {
 		return "ADSIGRP_SYMBOL_AND_DATA_TYPE_SIZES"
 	case ReservedIndexGroups_ADSIGRP_SYMNOTE:
 		return "ADSIGRP_SYMNOTE"
+	case ReservedIndexGroups_ADSIGRP_DT_INFOBYNAMEEX:
+		return "ADSIGRP_DT_INFOBYNAMEEX"
 	case ReservedIndexGroups_ADSIGRP_IOIMAGE_RWIB:
 		return "ADSIGRP_IOIMAGE_RWIB"
 	case ReservedIndexGroups_ADSIGRP_IOIMAGE_RWIX:
diff --git a/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go b/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go
index 9050d1663..4c70cdae6 100644
--- a/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go
+++ b/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go
@@ -635,8 +635,9 @@ const (
 	KnxManufacturer_M_GUANGDONG_KANWAY                                   KnxManufacturer = 596
 	KnxManufacturer_M_PHOENIX_CONTACT_2                                  KnxManufacturer = 597
 	KnxManufacturer_M_RAMIREZ_ENGINEERING_GMBH                           KnxManufacturer = 598
-	KnxManufacturer_M_ABB___RESERVED                                     KnxManufacturer = 599
-	KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED                    KnxManufacturer = 600
+	KnxManufacturer_M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD                KnxManufacturer = 599
+	KnxManufacturer_M_ABB___RESERVED                                     KnxManufacturer = 600
+	KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED                    KnxManufacturer = 601
 )
 
 var KnxManufacturerValues []KnxManufacturer
@@ -1243,6 +1244,7 @@ func init() {
 		KnxManufacturer_M_GUANGDONG_KANWAY,
 		KnxManufacturer_M_PHOENIX_CONTACT_2,
 		KnxManufacturer_M_RAMIREZ_ENGINEERING_GMBH,
+		KnxManufacturer_M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD,
 		KnxManufacturer_M_ABB___RESERVED,
 		KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED,
 	}
@@ -3472,7 +3474,7 @@ func (e KnxManufacturer) Number() uint16 {
 		}
 	case 599:
 		{ /* '599' */
-			return 43954
+			return 657
 		}
 	case 6:
 		{ /* '6' */
@@ -3484,6 +3486,10 @@ func (e KnxManufacturer) Number() uint16 {
 		}
 	case 600:
 		{ /* '600' */
+			return 43954
+		}
+	case 601:
+		{ /* '601' */
 			return 43959
 		}
 	case 61:
@@ -5894,7 +5900,7 @@ func (e KnxManufacturer) Name() string {
 		}
 	case 599:
 		{ /* '599' */
-			return "ABB - reserved"
+			return "Zhongshan Taiyang IMP&EXP. CO LTD"
 		}
 	case 6:
 		{ /* '6' */
@@ -5906,6 +5912,10 @@ func (e KnxManufacturer) Name() string {
 		}
 	case 600:
 		{ /* '600' */
+			return "ABB - reserved"
+		}
+	case 601:
+		{ /* '601' */
 			return "Busch-Jaeger Elektro - reserved"
 		}
 	case 61:
@@ -7204,12 +7214,14 @@ func KnxManufacturerByValue(value uint16) (enum KnxManufacturer, ok bool) {
 	case 598:
 		return KnxManufacturer_M_RAMIREZ_ENGINEERING_GMBH, true
 	case 599:
-		return KnxManufacturer_M_ABB___RESERVED, true
+		return KnxManufacturer_M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD, true
 	case 6:
 		return KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO, true
 	case 60:
 		return KnxManufacturer_M_TECHEM, true
 	case 600:
+		return KnxManufacturer_M_ABB___RESERVED, true
+	case 601:
 		return KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED, true
 	case 61:
 		return KnxManufacturer_M_SCHNEIDER_ELECTRIC_INDUSTRIES_SAS, true
@@ -8411,12 +8423,14 @@ func KnxManufacturerByName(value string) (enum KnxManufacturer, ok bool) {
 		return KnxManufacturer_M_PHOENIX_CONTACT_2, true
 	case "M_RAMIREZ_ENGINEERING_GMBH":
 		return KnxManufacturer_M_RAMIREZ_ENGINEERING_GMBH, true
-	case "M_ABB___RESERVED":
-		return KnxManufacturer_M_ABB___RESERVED, true
+	case "M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD":
+		return KnxManufacturer_M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD, true
 	case "M_BUSCH_JAEGER_ELEKTRO":
 		return KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO, true
 	case "M_TECHEM":
 		return KnxManufacturer_M_TECHEM, true
+	case "M_ABB___RESERVED":
+		return KnxManufacturer_M_ABB___RESERVED, true
 	case "M_BUSCH_JAEGER_ELEKTRO___RESERVED":
 		return KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED, true
 	case "M_SCHNEIDER_ELECTRIC_INDUSTRIES_SAS":
@@ -9664,12 +9678,14 @@ func (e KnxManufacturer) PLC4XEnumName() string {
 		return "M_PHOENIX_CONTACT_2"
 	case KnxManufacturer_M_RAMIREZ_ENGINEERING_GMBH:
 		return "M_RAMIREZ_ENGINEERING_GMBH"
-	case KnxManufacturer_M_ABB___RESERVED:
-		return "M_ABB___RESERVED"
+	case KnxManufacturer_M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD:
+		return "M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD"
 	case KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO:
 		return "M_BUSCH_JAEGER_ELEKTRO"
 	case KnxManufacturer_M_TECHEM:
 		return "M_TECHEM"
+	case KnxManufacturer_M_ABB___RESERVED:
+		return "M_ABB___RESERVED"
 	case KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED:
 		return "M_BUSCH_JAEGER_ELEKTRO___RESERVED"
 	case KnxManufacturer_M_SCHNEIDER_ELECTRIC_INDUSTRIES_SAS:
diff --git a/plc4go/protocols/s7/readwrite/model/DataItem.go b/plc4go/protocols/s7/readwrite/model/DataItem.go
index 42010eed2..d0ad80537 100644
--- a/plc4go/protocols/s7/readwrite/model/DataItem.go
+++ b/plc4go/protocols/s7/readwrite/model/DataItem.go
@@ -410,13 +410,13 @@ func DataItemSerialize(writeBuffer utils.WriteBuffer, value api.PlcValue, dataPr
 		}
 	case dataProtocolId == "IEC61131_STRING": // STRING
 		// Manual Field (value)
-		_valueErr := SerializeS7String(writeBuffer, value, m.StringLength, "UTF-8")
+		_valueErr := SerializeS7String(writeBuffer, value, stringLength, "UTF-8")
 		if _valueErr != nil {
 			return errors.Wrap(_valueErr, "Error serializing 'value' field")
 		}
 	case dataProtocolId == "IEC61131_WSTRING": // STRING
 		// Manual Field (value)
-		_valueErr := SerializeS7String(writeBuffer, value, m.StringLength, "UTF-16")
+		_valueErr := SerializeS7String(writeBuffer, value, stringLength, "UTF-16")
 		if _valueErr != nil {
 			return errors.Wrap(_valueErr, "Error serializing 'value' field")
 		}
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
index 377334bbe..1f4fa085f 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
@@ -28,10 +28,12 @@ import org.apache.plc4x.java.ads.readwrite.DataItem;
 import org.apache.plc4x.java.api.authentication.PlcUsernamePasswordAuthentication;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
 import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
 import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.model.PlcSubscriptionField;
 import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
 import org.apache.plc4x.java.api.types.PlcSubscriptionType;
@@ -51,12 +53,14 @@ import org.slf4j.LoggerFactory;
 
 import java.math.BigInteger;
 import java.net.*;
+import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -67,22 +71,28 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
 
     private AdsConfiguration configuration;
 
+    private String adsVersion;
+    private String deviceName;
+
     private final AtomicLong invokeIdGenerator = new AtomicLong(1);
     private final RequestTransactionManager tm;
 
     private final Map<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> consumers = new ConcurrentHashMap<>();
 
-    //    private final ConcurrentHashMap<SymbolicAdsField, DirectAdsField> symbolicFieldMapping;
     private final ConcurrentHashMap<SymbolicAdsField, CompletableFuture<Void>> pendingResolutionRequests;
 
+    private int symbolVersion;
+    private long onlineVersion;
     private final Map<String, AdsSymbolTableEntry> symbolTable;
     private final Map<String, AdsDataTypeTableEntry> dataTypeTable;
+    private final ReentrantLock invalidationLock;
 
     public AdsProtocolLogic() {
 //        symbolicFieldMapping = new ConcurrentHashMap<>();
         pendingResolutionRequests = new ConcurrentHashMap<>();
         symbolTable = new HashMap<>();
         dataTypeTable = new HashMap<>();
+        invalidationLock = new ReentrantLock();
 
         // Initialize Transaction Manager.
         // Until the number of concurrent requests is successfully negotiated we set it to a
@@ -125,19 +135,104 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         // If the configuration asks us to load the symbol and data type tables, do so,
         // otherwise just mark the connection as completed instantly.
         setupAmsRouteFuture.whenComplete((unused, throwable) -> {
-            if (configuration.isLoadSymbolAndDataTypeTables()) {
-                LOGGER.debug("Fetching sizes of symbol and datatype table sizes.");
-                CompletableFuture<Void> readSymbolTableFuture = readSymbolTableAndDatatypeTable(context);
-                readSymbolTableFuture.whenComplete((unused2, throwable2) -> {
-                    if (throwable2 != null) {
-                        LOGGER.error("Error fetching symbol and datatype table sizes");
-                    } else {
-                        context.fireConnected();
-                    }
-                });
-            } else {
-                context.fireConnected();
+            if (!configuration.isLoadSymbolAndDataTypeTables()) {
+                future.completeExceptionally(new PlcConnectionException(
+                    "Lazy loading is generally planned, but not implemented yet. " +
+                        "If you are in need for this feature, please reach out to the community."));
             }
+            //if (configuration.isLoadSymbolAndDataTypeTables()) {
+                // Execute a ReadDeviceInfo command
+                AmsPacket readDeviceInfoRequest = new AdsReadDeviceInfoRequest(
+                    configuration.getTargetAmsNetId(), DefaultAmsPorts.RUNTIME_SYSTEM_01.getValue(),
+                    configuration.getSourceAmsNetId(), 800, 0, getInvokeId());
+                RequestTransactionManager.RequestTransaction readDeviceInfoTx = tm.startRequest();
+                readDeviceInfoTx.submit(() -> context.sendRequest(new AmsTCPPacket(readDeviceInfoRequest))
+                    .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+                    .onTimeout(future::completeExceptionally)
+                    .onError((p, e) -> future.completeExceptionally(e))
+                    .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == readDeviceInfoRequest.getInvokeId())
+                    .unwrap(response -> (AdsReadDeviceInfoResponse) response.getUserdata())
+                    .handle(readDeviceInfoResponse -> {
+                        readDeviceInfoTx.endRequest();
+                        if (readDeviceInfoResponse.getResult() != ReturnCode.OK) {
+                            // TODO: Handle this
+                            future.completeExceptionally(new PlcException("Result is " + readDeviceInfoResponse.getResult()));
+                            return;
+                        }
+
+                        // Get the twin-cat version and PLC name.
+                        adsVersion = String.format("%d.%d.%d", readDeviceInfoResponse.getMajorVersion(),
+                            readDeviceInfoResponse.getMinorVersion(), readDeviceInfoResponse.getVersion());
+                        deviceName = new String(readDeviceInfoResponse.getDevice()).trim();
+
+                        // Read the online version number (Address; GroupID: 0xF004 (read symbol by name),Offset: 0, Read length: 4, ... Payload: "TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt")
+                        AmsPacket readOnlineVersionNumberRequest = new AdsReadWriteRequest(
+                            configuration.getTargetAmsNetId(), DefaultAmsPorts.RUNTIME_SYSTEM_01.getValue(),
+                            configuration.getSourceAmsNetId(), 800, 0, getInvokeId(),
+                            ReservedIndexGroups.ADSIGRP_SYM_VALBYNAME.getValue(), 0, 4, null,
+                            "TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt".getBytes(StandardCharsets.UTF_8));
+                        RequestTransactionManager.RequestTransaction readOnlineVersionNumberTx = tm.startRequest();
+                        readOnlineVersionNumberTx.submit(() -> context.sendRequest(new AmsTCPPacket(readOnlineVersionNumberRequest))
+                            .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+                            .onTimeout(future::completeExceptionally)
+                            .onError((p, e) -> future.completeExceptionally(e))
+                            .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == readOnlineVersionNumberRequest.getInvokeId())
+                            .unwrap(response -> (AdsReadWriteResponse) response.getUserdata())
+                            .handle(readOnlineVersionNumberResponse -> {
+                                readOnlineVersionNumberTx.endRequest();
+                                if (readOnlineVersionNumberResponse.getResult() != ReturnCode.OK) {
+                                    // TODO: Handle this
+                                    future.completeExceptionally(new PlcException("Result is " + readOnlineVersionNumberResponse.getResult()));
+                                    return;
+                                }
+                                try {
+                                    ReadBuffer rb = new ReadBufferByteBased(readOnlineVersionNumberResponse.getData());
+                                    onlineVersion = rb.readUnsignedLong(32);
+
+                                    // Read the offline version number (Address: GroupID: 0xF008, Offset: 0, Read length: 1)
+                                    AmsPacket readSymbolVersionNumberRequest = new AdsReadRequest(
+                                        configuration.getTargetAmsNetId(), DefaultAmsPorts.RUNTIME_SYSTEM_01.getValue(),
+                                        configuration.getSourceAmsNetId(), 800, 0, getInvokeId(),
+                                        ReservedIndexGroups.ADSIGRP_SYM_VERSION.getValue(), 0, 1);
+                                    RequestTransactionManager.RequestTransaction readSymbolVersionNumberTx = tm.startRequest();
+                                    readSymbolVersionNumberTx.submit(() -> context.sendRequest(new AmsTCPPacket(readSymbolVersionNumberRequest))
+                                        .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+                                        .onTimeout(future::completeExceptionally)
+                                        .onError((p, e) -> future.completeExceptionally(e))
+                                        .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == readSymbolVersionNumberRequest.getInvokeId())
+                                        .unwrap(response -> (AdsReadResponse) response.getUserdata())
+                                        .handle(readSymbolVersionNumberResponse -> {
+                                            readSymbolVersionNumberTx.endRequest();
+                                            if (readSymbolVersionNumberResponse.getResult() != ReturnCode.OK) {
+                                                // TODO: Handle this
+                                                future.completeExceptionally(new PlcException("Result is " + readSymbolVersionNumberResponse.getResult()));
+                                                return;
+                                            }
+                                            try {
+                                                ReadBuffer rb2 = new ReadBufferByteBased(readSymbolVersionNumberResponse.getData());
+                                                symbolVersion = rb2.readUnsignedInt(8);
+
+                                                LOGGER.debug("Fetching sizes of symbol and datatype table sizes.");
+                                                CompletableFuture<Void> readSymbolTableFuture = readSymbolTableAndDatatypeTable(context);
+                                                readSymbolTableFuture.whenComplete((unused2, throwable2) -> {
+                                                    if (throwable2 != null) {
+                                                        LOGGER.error("Error fetching symbol and datatype table sizes");
+                                                    } else {
+                                                        context.fireConnected();
+                                                    }
+                                                });
+                                            } catch (ParseException e) {
+                                                future.completeExceptionally(new PlcConnectionException("Error reading the symbol version of data type and symbol data.", e));
+                                            }
+                                        }));
+                                } catch (ParseException e) {
+                                    future.completeExceptionally(new PlcConnectionException("Error reading the online version of data type and symbol data.", e));
+                                }
+                            }));
+                    }));
+            /*} else {
+                context.fireConnected();
+            }*/
         });
     }
 
@@ -217,88 +312,146 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     protected CompletableFuture<Void> readSymbolTableAndDatatypeTable(ConversationContext<AmsTCPPacket> context) {
         final CompletableFuture<Void> future = new CompletableFuture<>();
 
-        // Initialize the request.
-        AmsPacket amsPacket = new AdsReadRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+        // Read the data-type and symbol table sizes
+        AmsPacket readDataAndSymbolTableSizesRequest = new AdsReadRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
             configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(), 0, getInvokeId(),
             ReservedIndexGroups.ADSIGRP_SYMBOL_AND_DATA_TYPE_SIZES.getValue(), 0x00000000, 24);
-        AmsTCPPacket amsTCPPacket = new AmsTCPPacket(amsPacket);
-        // Start a new request-transaction (Is ended in the response-handler)
-        RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
-        transaction.submit(() -> context.sendRequest(amsTCPPacket)
+        RequestTransactionManager.RequestTransaction readDataAndSymbolTableSizesTx = tm.startRequest();
+        readDataAndSymbolTableSizesTx.submit(() -> context.sendRequest(new AmsTCPPacket(readDataAndSymbolTableSizesRequest))
             .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
             .onTimeout(future::completeExceptionally)
             .onError((p, e) -> future.completeExceptionally(e))
-            .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsPacket.getInvokeId())
+            .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == readDataAndSymbolTableSizesRequest.getInvokeId())
             .unwrap(response -> (AdsReadResponse) response.getUserdata())
-            .handle(responseAdsData -> {
-                transaction.endRequest();
-                if (responseAdsData.getResult() == ReturnCode.OK) {
-                    ReadBuffer readBuffer = new ReadBufferByteBased(responseAdsData.getData());
-                    try {
-                        AdsTableSizes adsTableSizes = AdsTableSizes.staticParse(readBuffer);
-                        LOGGER.debug("PLC contains {} symbols and {} data-types", adsTableSizes.getSymbolCount(), adsTableSizes.getDataTypeCount());
-
-                        // Now we load the datatype definitions.
-                        AmsPacket amsReadTablePacket = new AdsReadRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
-                            configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(), 0, getInvokeId(),
-                            ReservedIndexGroups.ADSIGRP_DATA_TYPE_TABLE_UPLOAD.getValue(), 0x00000000, adsTableSizes.getDataTypeLength());
-                        RequestTransactionManager.RequestTransaction transaction2 = tm.startRequest();
-                        AmsTCPPacket amsReadTableTCPPacket = new AmsTCPPacket(amsReadTablePacket);
-                        transaction2.submit(() -> context.sendRequest(amsReadTableTCPPacket)
-                            .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
-                            .onTimeout(future::completeExceptionally)
-                            .onError((p, e) -> future.completeExceptionally(e))
-                            .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsReadTablePacket.getInvokeId())
-                            .unwrap(response -> (AdsReadResponse) response.getUserdata())
-                            .handle(responseAdsReadTableData -> {
-                                transaction2.endRequest();
-                                if (responseAdsData.getResult() == ReturnCode.OK) {
-                                    // Parse the result.
-                                    ReadBuffer rb = new ReadBufferByteBased(responseAdsReadTableData.getData());
-                                    for (int i = 0; i < adsTableSizes.getDataTypeCount(); i++) {
+            .handle(readDataAndSymbolTableSizesResponse -> {
+                readDataAndSymbolTableSizesTx.endRequest();
+                if (readDataAndSymbolTableSizesResponse.getResult() != ReturnCode.OK) {
+                    // TODO: Handle this
+                    future.completeExceptionally(new PlcException("Reading data type and symbol table sizes failed: " + readDataAndSymbolTableSizesResponse.getResult()));
+                    return;
+                }
+                try {
+                    ReadBuffer readBuffer = new ReadBufferByteBased(readDataAndSymbolTableSizesResponse.getData());
+                    AdsTableSizes adsTableSizes = AdsTableSizes.staticParse(readBuffer);
+                    LOGGER.debug("PLC contains {} symbols and {} data-types", adsTableSizes.getSymbolCount(), adsTableSizes.getDataTypeCount());
+
+                    // Now we load the datatype definitions.
+                    AmsPacket readDataTypeTableRequest = new AdsReadRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+                        configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(), 0, getInvokeId(),
+                        ReservedIndexGroups.ADSIGRP_DATA_TYPE_TABLE_UPLOAD.getValue(), 0x00000000, adsTableSizes.getDataTypeLength());
+                    RequestTransactionManager.RequestTransaction readDataTypeTableTx = tm.startRequest();
+                    AmsTCPPacket amsReadTableTCPPacket = new AmsTCPPacket(readDataTypeTableRequest);
+                    readDataTypeTableTx.submit(() -> context.sendRequest(amsReadTableTCPPacket)
+                        .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+                        .onTimeout(future::completeExceptionally)
+                        .onError((p, e) -> future.completeExceptionally(e))
+                        .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == readDataTypeTableRequest.getInvokeId())
+                        .unwrap(response -> (AdsReadResponse) response.getUserdata())
+                        .handle(readDataTypeTableResponse -> {
+                            readDataTypeTableTx.endRequest();
+                            if (readDataTypeTableResponse.getResult() != ReturnCode.OK) {
+                                // TODO: Handle this
+                                future.completeExceptionally(new PlcException("Reading data type table failed: " + readDataTypeTableResponse.getResult()));
+                                return;
+                            }
+                            // Parse the result.
+                            ReadBuffer rb = new ReadBufferByteBased(readDataTypeTableResponse.getData());
+                            for (int i = 0; i < adsTableSizes.getDataTypeCount(); i++) {
+                                try {
+                                    AdsDataTypeTableEntry adsDataTypeTableEntry = AdsDataTypeTableEntry.staticParse(rb);
+                                    dataTypeTable.put(adsDataTypeTableEntry.getDataTypeName(), adsDataTypeTableEntry);
+                                } catch (ParseException e) {
+                                    throw new RuntimeException(e);
+                                }
+                            }
+
+                            // Now we load the symbol definitions
+                            AmsPacket readSymbolTableRequest = new AdsReadRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+                                configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(), 0, getInvokeId(),
+                                ReservedIndexGroups.ADSIGRP_SYM_UPLOAD.getValue(), 0x00000000, adsTableSizes.getSymbolLength());
+                            RequestTransactionManager.RequestTransaction readSymbolTableTx = tm.startRequest();
+                            AmsTCPPacket amsReadSymbolTableTCPPacket = new AmsTCPPacket(readSymbolTableRequest);
+                            readSymbolTableTx.submit(() -> context.sendRequest(amsReadSymbolTableTCPPacket)
+                                .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+                                .onTimeout(future::completeExceptionally)
+                                .onError((p, e) -> future.completeExceptionally(e))
+                                .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == readSymbolTableRequest.getInvokeId())
+                                .unwrap(response -> (AdsReadResponse) response.getUserdata())
+                                .handle(readSymbolTableResponse -> {
+                                    readSymbolTableTx.endRequest();
+                                    if (readSymbolTableResponse.getResult() != ReturnCode.OK) {
+                                        // TODO: Handle this
+                                        future.completeExceptionally(new PlcException("Reading symbol table failed: " + readSymbolTableResponse.getResult()));
+                                        return;
+                                    }
+                                    ReadBuffer rb2 = new ReadBufferByteBased(readSymbolTableResponse.getData());
+                                    for (int i = 0; i < adsTableSizes.getSymbolCount(); i++) {
                                         try {
-                                            AdsDataTypeTableEntry adsDataTypeTableEntry = AdsDataTypeTableEntry.staticParse(rb);
-                                            dataTypeTable.put(adsDataTypeTableEntry.getDataTypeName(), adsDataTypeTableEntry);
+                                            AdsSymbolTableEntry adsSymbolTableEntry = AdsSymbolTableEntry.staticParse(rb2);
+                                            symbolTable.put(adsSymbolTableEntry.getName(), adsSymbolTableEntry);
                                         } catch (ParseException e) {
                                             throw new RuntimeException(e);
                                         }
                                     }
 
-                                    AmsPacket amsReadSymbolTablePacket = new AdsReadRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
-                                        configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(), 0, getInvokeId(),
-                                        ReservedIndexGroups.ADSIGRP_SYM_UPLOAD.getValue(), 0x00000000, adsTableSizes.getSymbolLength());
-                                    RequestTransactionManager.RequestTransaction transaction3 = tm.startRequest();
-                                    AmsTCPPacket amsReadSymbolTableTCPPacket = new AmsTCPPacket(amsReadSymbolTablePacket);
-                                    transaction3.submit(() -> context.sendRequest(amsReadSymbolTableTCPPacket)
-                                        .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
-                                        .onTimeout(future::completeExceptionally)
-                                        .onError((p, e) -> future.completeExceptionally(e))
-                                        .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsReadSymbolTablePacket.getInvokeId())
-                                        .unwrap(response -> (AdsReadResponse) response.getUserdata())
-                                        .handle(responseAdsReadSymbolTableData -> {
-                                            transaction3.endRequest();
-                                            if (responseAdsData.getResult() == ReturnCode.OK) {
-                                                ReadBuffer rb2 = new ReadBufferByteBased(responseAdsReadSymbolTableData.getData());
-                                                for (int i = 0; i < adsTableSizes.getSymbolCount(); i++) {
-                                                    try {
-                                                        AdsSymbolTableEntry adsSymbolTableEntry = AdsSymbolTableEntry.staticParse(rb2);
-                                                        symbolTable.put(adsSymbolTableEntry.getName(), adsSymbolTableEntry);
-                                                    } catch (ParseException e) {
-                                                        throw new RuntimeException(e);
+                                    LinkedHashMap<String, PlcSubscriptionField> subscriptionFields = new LinkedHashMap<>();
+                                    // Subscribe to online-version changes (get the address from the collected data for symbol: "TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt")
+                                    subscriptionFields.put("onlineVersion", new DefaultPlcSubscriptionField(
+                                        PlcSubscriptionType.CHANGE_OF_STATE,
+                                        new SymbolicAdsField("TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt"),
+                                        Duration.ofMillis(1000)));
+                                    // Subscribe to symbol-version changes (Address: GroupID: 0xF008, Offset: 0, Read length: 1)
+                                    subscriptionFields.put("symbolVersion", new DefaultPlcSubscriptionField(
+                                        PlcSubscriptionType.CHANGE_OF_STATE,
+                                        new DirectAdsField(0xF008, 0x0000, "USINT", 1),
+                                        Duration.ofMillis(1000)));
+                                    LinkedHashMap<String, List<Consumer<PlcSubscriptionEvent>>> consumer = new LinkedHashMap<>();
+                                    consumer.put("onlineVersion", Collections.singletonList(plcSubscriptionEvent -> {
+                                        long oldVersion = onlineVersion;
+                                        long newVersion = plcSubscriptionEvent.getPlcValue("onlineVersion").getLong();
+                                        if(oldVersion != newVersion) {
+                                            if(invalidationLock.tryLock()) {
+                                                LOGGER.info("Detected change of the 'online-version', invalidating data type and symbol information.");
+                                                CompletableFuture<Void> reloadingFuture = readSymbolTableAndDatatypeTable(context);
+                                                reloadingFuture.whenComplete((unused, throwable) -> {
+                                                    if(throwable != null) {
+                                                        LOGGER.error("Error reloading data type and symbol data", throwable);
                                                     }
-                                                }
-                                                future.complete(null);
+                                                    invalidationLock.unlock();
+                                                });
                                             }
-                                        }));
-                                }
-                            }));
+                                        }
+                                    }));
+                                    consumer.put("symbolVersion", Collections.singletonList(plcSubscriptionEvent -> {
+                                        int oldVersion = symbolVersion;
+                                        int newVersion = plcSubscriptionEvent.getPlcValue("symbolVersion").getInteger();
+                                        if(oldVersion != newVersion) {
+                                            if(invalidationLock.tryLock()) {
+                                                LOGGER.info("Detected change of the 'symbol-version', invalidating data type and symbol information.");
+                                                CompletableFuture<Void> reloadingFuture = readSymbolTableAndDatatypeTable(context);
+                                                reloadingFuture.whenComplete((unused, throwable) -> {
+                                                    if(throwable != null) {
+                                                        LOGGER.error("Error reloading data type and symbol data", throwable);
+                                                    }
+                                                    invalidationLock.unlock();
+                                                });
+                                            }
+                                        }
+                                    }));
+                                    PlcSubscriptionRequest subscriptionRequest = new DefaultPlcSubscriptionRequest(this, subscriptionFields, consumer);
+                                    CompletableFuture<PlcSubscriptionResponse> subscriptionResponseCompletableFuture = subscribe(subscriptionRequest);
+
+                                    // Wait for the subscription to be finished
+                                    subscriptionResponseCompletableFuture.whenComplete((plcSubscriptionResponse, throwable) -> {
+                                        if(throwable == null) {
+                                            future.complete(null);
+                                        }
+                                    });
+                                }));
+                        }));
                     } catch (ParseException e) {
                         future.completeExceptionally(new PlcException("Error loading the table sizes", e));
                     }
-                } else {
-                    // TODO: Implement this correctly.
-                    future.completeExceptionally(new PlcException("Result is " + responseAdsData.getResult()));
-                }
             }));
         return future;
     }
@@ -404,14 +557,14 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     @Override
     public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
         // Get all ADS addresses in their resolved state.
-        final CompletableFuture<List<DirectAdsField>> directAdsFieldsFuture =
+        final CompletableFuture<Map<AdsField, DirectAdsField>> directAdsFieldsFuture =
             getDirectAddresses(readRequest.getFields());
 
         // If all addresses were already resolved we can send the request immediately.
         if (directAdsFieldsFuture.isDone()) {
-            final List<DirectAdsField> fields = directAdsFieldsFuture.getNow(null);
-            if (fields != null) {
-                return executeRead(readRequest, fields);
+            final Map<AdsField, DirectAdsField> resolvedFields = directAdsFieldsFuture.getNow(null);
+            if (resolvedFields != null) {
+                return executeRead(readRequest, resolvedFields);
             } else {
                 final CompletableFuture<PlcReadResponse> errorFuture = new CompletableFuture<>();
                 errorFuture.completeExceptionally(new PlcException("Fields are null"));
@@ -448,15 +601,15 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     }
 
     protected CompletableFuture<PlcReadResponse> executeRead(PlcReadRequest readRequest,
-                                                             List<DirectAdsField> directAdsFields) {
+                                                             Map<AdsField, DirectAdsField> resolvedFields) {
         // Depending on the number of fields, use a single item request or a sum-request
-        if (directAdsFields.size() == 1) {
+        if (resolvedFields.size() == 1) {
             // Do a normal (single item) ADS Read Request
-            return singleRead(readRequest, directAdsFields.get(0));
+            return singleRead(readRequest, resolvedFields.values().stream().findFirst().get());
         } else {
             // TODO: Check if the version of the remote station is at least TwinCAT v2.11 Build >= 1550 otherwise split up into single item requests.
             // Do a ADS-Sum Read Request.
-            return multiRead(readRequest, directAdsFields);
+            return multiRead(readRequest, resolvedFields);
         }
     }
 
@@ -495,12 +648,12 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         return future;
     }
 
-    protected CompletableFuture<PlcReadResponse> multiRead(PlcReadRequest readRequest, List<DirectAdsField> directAdsFields) {
+    protected CompletableFuture<PlcReadResponse> multiRead(PlcReadRequest readRequest, Map<AdsField, DirectAdsField> resolvedFields) {
         CompletableFuture<PlcReadResponse> future = new CompletableFuture<>();
 
         // Calculate the size of all fields together.
         // Calculate the expected size of the response data.
-        long expectedResponseDataSize = directAdsFields.stream().mapToLong(
+        long expectedResponseDataSize = resolvedFields.values().stream().mapToLong(
             field -> {
                 String dataTypeName = field.getAdsDataTypeName();
                 AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
@@ -512,15 +665,17 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         // With multi-requests, the index-group is fixed and the index offset indicates the number of elements.
         AmsPacket amsPacket = new AdsReadWriteRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
             configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
-            0, getInvokeId(), ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.getValue(), directAdsFields.size(),
-            expectedResponseDataSize, directAdsFields.stream().map(directAdsField -> {
-            String dataTypeName = directAdsField.getAdsDataTypeName();
-            AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
-            long size = adsDataTypeTableEntry.getSize();
-            return new AdsMultiRequestItemRead(
-                directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),
-                (size * directAdsField.getNumberOfElements()));
-        }).collect(Collectors.toList()), null);
+            0, getInvokeId(), ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.getValue(), resolvedFields.size(),
+            expectedResponseDataSize, readRequest.getFieldNames().stream().map(fieldName -> {
+                AdsField field = (AdsField) readRequest.getField(fieldName);
+                DirectAdsField directAdsField = resolvedFields.get(field);
+                String dataTypeName = directAdsField.getAdsDataTypeName();
+                AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+                long size = adsDataTypeTableEntry.getSize();
+                return new AdsMultiRequestItemRead(
+                    directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),
+                    (size * directAdsField.getNumberOfElements()));
+            }).collect(Collectors.toList()), null);
         AmsTCPPacket amsTCPPacket = new AmsTCPPacket(amsPacket);
 
         // Start a new request-transaction (Is ended in the response-handler)
@@ -561,7 +716,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         } else if (adsData instanceof AdsReadWriteResponse) {
             AdsReadWriteResponse adsReadWriteResponse = (AdsReadWriteResponse) adsData;
             readBuffer = new ReadBufferByteBased(adsReadWriteResponse.getData(), ByteOrder.LITTLE_ENDIAN);
-            // When parsing a multi-item response, the error codes of each items come
+            // When parsing a multi-item response, the error codes of each item comes
             // in sequence and then come the values.
             for (String fieldName : readRequest.getFieldNames()) {
                 try {
@@ -613,7 +768,8 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
 
             int strLen = 0;
             if ((plcValueType == PlcValueType.STRING) || (plcValueType == PlcValueType.WSTRING)) {
-                strLen = (field instanceof AdsStringField) ? ((AdsStringField) field).getStringLength() : 256;
+                // Extract the string length from the data type name.
+                strLen = Integer.parseInt(dataTypeName.substring(dataTypeName.indexOf("(") + 1, dataTypeName.indexOf(")")));
             }
             final int stringLength = strLen;
             if (field.getNumberOfElements() == 1) {
@@ -693,14 +849,14 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     @Override
     public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
         // Get all ADS addresses in their resolved state.
-        final CompletableFuture<List<DirectAdsField>> directAdsFieldsFuture =
+        final CompletableFuture<Map<AdsField, DirectAdsField>> directAdsFieldsFuture =
             getDirectAddresses(writeRequest.getFields());
 
         // If all addresses were already resolved we can send the request immediately.
         if (directAdsFieldsFuture.isDone()) {
-            final List<DirectAdsField> fields = directAdsFieldsFuture.getNow(null);
-            if (fields != null) {
-                return executeWrite(writeRequest, fields);
+            final Map<AdsField, DirectAdsField> resolvedFields = directAdsFieldsFuture.getNow(null);
+            if (resolvedFields != null) {
+                return executeWrite(writeRequest, resolvedFields);
             } else {
                 final CompletableFuture<PlcWriteResponse> errorFuture = new CompletableFuture<>();
                 errorFuture.completeExceptionally(new PlcException("Fields are null"));
@@ -738,15 +894,15 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     }
 
     protected CompletableFuture<PlcWriteResponse> executeWrite(PlcWriteRequest writeRequest,
-                                                               List<DirectAdsField> directAdsFields) {
+                                                               Map<AdsField, DirectAdsField> resolvedFields) {
         // Depending on the number of fields, use a single item request or a sum-request
-        if (directAdsFields.size() == 1) {
+        if (resolvedFields.size() == 1) {
             // Do a normal (single item) ADS Write Request
-            return singleWrite(writeRequest, directAdsFields.get(0));
+            return singleWrite(writeRequest, resolvedFields.values().stream().findFirst().get());
         } else {
             // TODO: Check if the version of the remote station is at least TwinCAT v2.11 Build >= 1550 otherwise split up into single item requests.
             // Do a ADS-Sum Read Request.
-            return multiWrite(writeRequest, directAdsFields);
+            return multiWrite(writeRequest, resolvedFields);
         }
     }
 
@@ -801,7 +957,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         return future;
     }
 
-    protected CompletableFuture<PlcWriteResponse> multiWrite(PlcWriteRequest writeRequest, List<DirectAdsField> directAdsFields) {
+    protected CompletableFuture<PlcWriteResponse> multiWrite(PlcWriteRequest writeRequest, Map<AdsField, DirectAdsField> resolvedFields) {
         CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
 
         // Calculate the size of all fields together.
@@ -896,7 +1052,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     @Override
     public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
         // Get all ADS addresses in their resolved state.
-        final CompletableFuture<List<DirectAdsField>> directAdsFieldsFuture =
+        final CompletableFuture<Map<AdsField, DirectAdsField>> directAdsFieldsFuture =
             getDirectAddresses(subscriptionRequest.getFields()
                 .stream()
                 .map(field -> ((DefaultPlcSubscriptionField) field).getPlcField())
@@ -904,9 +1060,9 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
 
         // If all addresses were already resolved we can send the request immediately.
         if (directAdsFieldsFuture.isDone()) {
-            final List<DirectAdsField> fields = directAdsFieldsFuture.getNow(null);
-            if (fields != null) {
-                return executeSubscribe(subscriptionRequest);
+            final Map<AdsField, DirectAdsField> resolvedFields = directAdsFieldsFuture.getNow(null);
+            if (resolvedFields != null) {
+                return executeSubscribe(subscriptionRequest, resolvedFields);
             } else {
                 final CompletableFuture<PlcSubscriptionResponse> errorFuture = new CompletableFuture<>();
                 errorFuture.completeExceptionally(new PlcException("Fields are null"));
@@ -922,10 +1078,10 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         // we can complete the initial one.
         else {
             CompletableFuture<PlcSubscriptionResponse> delayedSubscribe = new CompletableFuture<>();
-            directAdsFieldsFuture.handle((directAdsFields, throwable) -> {
-                if (directAdsFields != null) {
+            directAdsFieldsFuture.handle((fieldMapping, throwable) -> {
+                if (fieldMapping != null) {
                     final CompletableFuture<PlcSubscriptionResponse> delayedResponse =
-                        executeSubscribe(subscriptionRequest);
+                        executeSubscribe(subscriptionRequest, fieldMapping);
                     delayedResponse.handle((plcSubscribeResponse, throwable1) -> {
                         if (plcSubscribeResponse != null) {
                             delayedSubscribe.complete(plcSubscribeResponse);
@@ -943,14 +1099,14 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         }
     }
 
-    private CompletableFuture<PlcSubscriptionResponse> executeSubscribe(PlcSubscriptionRequest subscribeRequest) {
+    private CompletableFuture<PlcSubscriptionResponse> executeSubscribe(PlcSubscriptionRequest subscribeRequest, Map<AdsField, DirectAdsField> resolvedFields) {
         CompletableFuture<PlcSubscriptionResponse> future = new CompletableFuture<>();
 
         List<AmsTCPPacket> amsTCPPackets = subscribeRequest.getFields().stream()
             .map(field -> (DefaultPlcSubscriptionField) field)
             .map(field -> {
-                AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(((DirectAdsField) field.getPlcField()).getAdsDataTypeName());
-                DirectAdsField directAdsField = getDirectAdsFieldForSymbolicName(field);
+                AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(resolvedFields.get((AdsField) field.getPlcField()).getAdsDataTypeName());
+                DirectAdsField directAdsField = getDirectAdsFieldForSymbolicName(field.getPlcField());
                 return new AmsTCPPacket(new AdsAddDeviceNotificationRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
                     configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
                     0, getInvokeId(),
@@ -970,6 +1126,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         transaction.submit(subscribeRecursively(
             subscribeRequest,
             subscribeRequest.getFieldNames().iterator(),
+            resolvedFields,
             responses,
             future,
             amsTCPPackets.iterator(),
@@ -977,7 +1134,9 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         return future;
     }
 
-    private Runnable subscribeRecursively(PlcSubscriptionRequest subscriptionRequest, Iterator<String> fieldNames,
+    private Runnable subscribeRecursively(PlcSubscriptionRequest subscriptionRequest,
+                                          Iterator<String> fieldNames,
+                                          Map<AdsField, DirectAdsField> resolvedFields,
                                           Map<String, ResponseItem<PlcSubscriptionHandle>> responses,
                                           CompletableFuture<PlcSubscriptionResponse> future,
                                           Iterator<AmsTCPPacket> amsTCPPackets,
@@ -995,7 +1154,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                 .handle(response -> {
                     if (response.getResult() == ReturnCode.OK) {
                         DefaultPlcSubscriptionField subscriptionField = (DefaultPlcSubscriptionField) subscriptionRequest.getField(fieldName);
-                        AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(((DirectAdsField) subscriptionField.getPlcField()).getAdsDataTypeName());
+                        AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get((resolvedFields.get((AdsField) subscriptionField.getPlcField())).getAdsDataTypeName());
 
                         // Collect notification handle from individual response.
                         responses.put(fieldName, new ResponseItem<>(
@@ -1025,7 +1184,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                     if (hasMorePackets) {
                         RequestTransactionManager.RequestTransaction nextTransaction = tm.startRequest();
                         nextTransaction.submit(subscribeRecursively(
-                            subscriptionRequest, fieldNames, responses, future, amsTCPPackets, nextTransaction));
+                            subscriptionRequest, fieldNames, resolvedFields, responses, future, amsTCPPackets, nextTransaction));
                     }
                 });
         };
@@ -1150,8 +1309,8 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         consumers.remove(consumerRegistration);
     }
 
-    protected CompletableFuture<List<DirectAdsField>> getDirectAddresses(List<PlcField> fields) {
-        CompletableFuture<List<DirectAdsField>> future = new CompletableFuture<>();
+    protected CompletableFuture<Map<AdsField, DirectAdsField>> getDirectAddresses(List<PlcField> fields) {
+        CompletableFuture<Map<AdsField, DirectAdsField>> future = new CompletableFuture<>();
 
         // Get all symbolic fields from the current request.
         // These potentially need to be resolved to direct addresses, if this has not been done before.
@@ -1202,26 +1361,28 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
                 if (throwable != null) {
                     return future.completeExceptionally(throwable.getCause());
                 } else {
-                    List<DirectAdsField> directAdsFields = new ArrayList<>(fields.size());
+                    Map<AdsField, DirectAdsField> directAdsFieldMapping = new HashMap<>(fields.size());
                     for (PlcField field : fields) {
                         if (field instanceof SymbolicAdsField) {
-                            directAdsFields.add(getDirectAdsFieldForSymbolicName(field));
+                            directAdsFieldMapping.put((AdsField) field, getDirectAdsFieldForSymbolicName(field));
                         } else {
-                            directAdsFields.add((DirectAdsField) field);
+                            directAdsFieldMapping.put((AdsField) field, (DirectAdsField) field);
                         }
                     }
-                    return future.complete(directAdsFields);
+                    return future.complete(directAdsFieldMapping);
                 }
             });
         } else {
             // If all fields were resolved, we can continue instantly.
-            future.complete(fields.stream().map(plcField -> {
-                if (plcField instanceof SymbolicAdsField) {
-                    return getDirectAdsFieldForSymbolicName(plcField);
+            Map<AdsField, DirectAdsField> directAdsFieldMapping = new HashMap<>(fields.size());
+            for (PlcField field : fields) {
+                if (field instanceof SymbolicAdsField) {
+                    directAdsFieldMapping.put((AdsField) field, getDirectAdsFieldForSymbolicName(field));
                 } else {
-                    return (DirectAdsField) plcField;
+                    directAdsFieldMapping.put((AdsField) field, (DirectAdsField) field);
                 }
-            }).collect(Collectors.toList()));
+            }
+            future.complete(directAdsFieldMapping);
         }
         return future;
     }
@@ -1364,9 +1525,16 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
             if (!symbolTable.containsKey(symbolicAddress)) {
                 return null;
             }
-
             AdsSymbolTableEntry adsSymbolTableEntry = symbolTable.get(symbolicAddress);
+            if(adsSymbolTableEntry == null) {
+                throw new PlcInvalidFieldException("Couldn't resolve symbolic address: " + symbolicAddress);
+            }
             AdsDataTypeTableEntry dataTypeTableEntry = dataTypeTable.get(adsSymbolTableEntry.getDataTypeName());
+            if(dataTypeTableEntry == null) {
+                throw new PlcInvalidFieldException(
+                    "Couldn't resolve datatype: '" + adsSymbolTableEntry.getDataTypeName() +
+                        "' for address: '" + ((SymbolicAdsField) field).getSymbolicAddress() + "'");
+            }
             return new DirectAdsField(adsSymbolTableEntry.getGroup(), adsSymbolTableEntry.getOffset(),
                 dataTypeTableEntry.getDataTypeName(), dataTypeTableEntry.getArrayDimensions());
         }
@@ -1374,7 +1542,15 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         else {
             String symbolName = addressParts[0] + "." + addressParts[1];
             AdsSymbolTableEntry adsSymbolTableEntry = symbolTable.get(symbolName);
+            if(adsSymbolTableEntry == null) {
+                throw new PlcInvalidFieldException("Couldn't resolve symbolic address: " + symbolName);
+            }
             AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(adsSymbolTableEntry.getDataTypeName());
+            if(adsDataTypeTableEntry == null) {
+                throw new PlcInvalidFieldException(
+                    "Couldn't resolve datatype: '" + adsSymbolTableEntry.getDataTypeName() +
+                        "' for address: '" + ((SymbolicAdsField) field).getSymbolicAddress() + "'");
+            }
             return resolveDirectAdsFieldForSymbolicNameFromDataType(
                 Arrays.asList(addressParts).subList(2, addressParts.length),
                 adsSymbolTableEntry.getGroup(), adsSymbolTableEntry.getOffset(), adsDataTypeTableEntry);
@@ -1384,7 +1560,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
     protected DirectAdsField resolveDirectAdsFieldForSymbolicNameFromDataType(List<String> remainingAddressParts, long currentGroup, long currentOffset, AdsDataTypeTableEntry adsDataTypeTableEntry) {
         if (remainingAddressParts.isEmpty()) {
             // TODO: Implement the Array support
-            return new DirectAdsField(currentGroup, currentOffset, adsDataTypeTableEntry.getDataTypeName(), null);
+            return new DirectAdsField(currentGroup, currentOffset, adsDataTypeTableEntry.getDataTypeName(), 1);
         }
 
         // Go through all children looking for a matching one.
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java
deleted file mode 100644
index f0b874735..000000000
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   https://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.readwrite.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 readBuffer, int stringLength, String encoding) {
-        try {
-            if ("UTF-8".equalsIgnoreCase(encoding)) {
-                List<Byte> bytes = new ArrayList<>();
-                for(int i = 0; (i < stringLength) && readBuffer.hasMore(8); i++) {
-                    final byte curByte = readBuffer.readByte();
-                    if (curByte != 0) {
-                        bytes.add(curByte);
-                    } else {
-                        // Gobble up the remaining data, which is not added to the string.
-                        i++;
-                        for(; (i < stringLength) && readBuffer.hasMore(8); i++) {
-                            readBuffer.readByte();
-                        }
-                        break;
-                    }
-                }
-                // Read the terminating byte.
-                readBuffer.readByte();
-                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);
-            } else if ("UTF-16".equalsIgnoreCase(encoding)) {
-                List<Byte> bytes = new ArrayList<>();
-                for(int i = 0; (i < stringLength) && readBuffer.hasMore(16); i++) {
-                    final short curShort = readBuffer.readShort(16);
-                    if (curShort != 0) {
-                        bytes.add((byte) (curShort >>> 8));
-                        bytes.add((byte) (curShort & 0xFF));
-                    } else {
-                        // Gobble up the remaining data, which is not added to the string.
-                        i++;
-                        for(; (i < stringLength) && readBuffer.hasMore(16); i++) {
-                            readBuffer.readShort(16);
-                        }
-                        break;
-                    }
-                }
-                // Read the terminating byte.
-                readBuffer.readByte();
-                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);
-            } else {
-                throw new PlcRuntimeException("Unsupported string encoding " + encoding);
-            }
-        } catch (ParseException e) {
-            throw new PlcRuntimeException("Error parsing string", e);
-        }
-    }
-
-    public static void serializeAmsString(WriteBuffer writeBuffer, PlcValue value, int stringLength, Object encoding) {
-        // TODO: Need to implement the serialization or we can't write strings
-        throw new PlcRuntimeException("Not implemented yet");
-    }
-
-}
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 8f9d25e64..d89a68d95 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
@@ -69,40 +69,36 @@ public class ManualAdsDriverTest extends ManualTest {
 
     public static void main(String[] args) throws Exception {
         String spsIp = "192.168.23.20";
-        /////
-        // TODO: adjust this to your ip address
         String clientIp = "192.168.23.200";
-        //
-        ////
         String sourceAmsNetId = clientIp + ".1.1";
         int sourceAmsPort = 65534;
         String targetAmsNetId = spsIp + ".1.1";
         int targetAmsPort = 851;
         String connectionString = String.format("ads:tcp://%s?sourceAmsNetId=%s&sourceAmsPort=%d&targetAmsNetId=%s&targetAmsPort=%d", spsIp, sourceAmsNetId, sourceAmsPort, targetAmsNetId, targetAmsPort);
         ManualAdsDriverTest test = new ManualAdsDriverTest(connectionString);
-        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.addTestCase("MAIN.hurz_BOOL", true);
+        test.addTestCase("MAIN.hurz_BYTE", Arrays.asList(false, false, true, false, true, false, true, false));
+        test.addTestCase("MAIN.hurz_WORD", Arrays.asList(true, false, true, false, false, true, false, true, true, false, true, true, true, false, false, false));
+        test.addTestCase("MAIN.hurz_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", -42);
+        test.addTestCase("MAIN.hurz_USINT", 42);
+        test.addTestCase("MAIN.hurz_INT", -2424);
+        test.addTestCase("MAIN.hurz_UINT", 42424);
+        test.addTestCase("MAIN.hurz_DINT", -242442424);
+        test.addTestCase("MAIN.hurz_UDINT", 4242442424L);
+        test.addTestCase("MAIN.hurz_LINT", -4242442424242424242L);
+        test.addTestCase("MAIN.hurz_ULINT", 4242442424242424242L);
+        test.addTestCase("MAIN.hurz_REAL", 3.14159265359F);
+        test.addTestCase("MAIN.hurz_LREAL", 2.71828182846D);
+        test.addTestCase("MAIN.hurz_STRING", "hurz");
+        test.addTestCase("MAIN.hurz_WSTRING", "wolf");
+        test.addTestCase("MAIN.hurz_TIME", "PT1.234S");
+        test.addTestCase("MAIN.hurz_LTIME", "PT24015H23M12.034002044S");
+        test.addTestCase("MAIN.hurz_DATE", "1978-03-28");
+        test.addTestCase("MAIN.hurz_TIME_OF_DAY", "15:36:30.123");
+        test.addTestCase("MAIN.hurz_TOD", "16:17:18.123");
+        test.addTestCase("MAIN.hurz_DATE_AND_TIME", "1996-05-06T15:36:30");
+        test.addTestCase("MAIN.hurz_DT", "1972-03-29T00:00");
         test.run();
     }
 
diff --git a/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/readwrite/utils/StaticHelper.java b/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/readwrite/utils/StaticHelper.java
index 5aff2986d..87b016139 100644
--- a/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/readwrite/utils/StaticHelper.java
+++ b/plc4j/drivers/canopen/src/main/java/org/apache/plc4x/java/canopen/readwrite/utils/StaticHelper.java
@@ -48,7 +48,7 @@ public class StaticHelper {
         // NOOP - a placeholder to let mspec compile
     }
 
-    public static Object parseString(ReadBuffer io, int length, String charset) {
+    public static Object parseString(ReadBuffer io, int length, String charset) throws ParseException {
         return io.readString(8 * length, charset);
     }
 
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/StaticHelper.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/StaticHelper.java
index 054e1c7b8..660aa7a4f 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/StaticHelper.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/utils/StaticHelper.java
@@ -1930,7 +1930,7 @@ public class StaticHelper {
         throw new NotImplementedException("Serializing DATE_AND_TIME not implemented");
     }
 
-    public static String parseS7Char(ReadBuffer io, String encoding) {
+    public static String parseS7Char(ReadBuffer io, String encoding) throws ParseException {
         if ("UTF-8".equalsIgnoreCase(encoding)) {
             return io.readString(8, encoding);
         } else if ("UTF-16".equalsIgnoreCase(encoding)) {
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 79ccaec5b..663a43c78 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
@@ -128,9 +128,9 @@ public interface ReadBuffer extends ByteOrderAware, PositionAware {
         return readBigDecimal("", bitLength);
     }
 
-    String readString(String logicalName, int bitLength, String encoding, WithReaderArgs... readerArgs);
+    String readString(String logicalName, int bitLength, String encoding, WithReaderArgs... readerArgs) throws ParseException;
 
-    default String readString(int bitLength, String encoding) {
+    default String readString(int bitLength, String encoding) throws ParseException {
         return readString("", bitLength, encoding);
     }
 
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferByteBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferByteBased.java
index 8c90ac6e8..6afe32f14 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferByteBased.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferByteBased.java
@@ -26,6 +26,7 @@ import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.Objects;
 
 public class ReadBufferByteBased implements ReadBuffer {
@@ -321,7 +322,7 @@ public class ReadBufferByteBased implements ReadBuffer {
         if(sign) {
             fraction = (short) (fraction | 0xF800);
         }
-        if ((exponent >= 1) && (exponent <= 15)) {
+        if ((exponent >= 1) && (exponent < 15)) {
             return (float) (0.01 * fraction * Math.pow(2, exponent));
         }
         if (exponent == 0) {
@@ -388,17 +389,65 @@ public class ReadBufferByteBased implements ReadBuffer {
     }
 
     @Override
-    public String readString(String logicalName, int bitLength, String encoding, WithReaderArgs... readerArgs) {
-        byte[] strBytes = new byte[bitLength / 8];
-        for (int i = 0; (i < (bitLength / 8)) && hasMore(8); i++) {
-            try {
-                strBytes[i] = readByte(logicalName);
-            } catch (Exception e) {
-                throw new PlcRuntimeException(e);
+    public String readString(String logicalName, int bitLength, String encoding, WithReaderArgs... readerArgs) throws ParseException {
+        encoding = encoding.replaceAll("[^a-zA-Z0-9]", "");
+        switch (encoding.toUpperCase()) {
+            case "UTF8": {
+                byte[] strBytes = new byte[bitLength / 8];
+                int realLength = 0;
+                boolean finishedReading = false;
+                for (int i = 0; (i < (bitLength / 8)) && hasMore(8); i++) {
+                    try {
+                        byte b = readByte(logicalName);
+                        if (b == 0x00) {
+                            finishedReading = true;
+                        } else if (!finishedReading) {
+                            strBytes[i] = b;
+                            realLength++;
+                        }
+                    } catch (Exception e) {
+                        throw new PlcRuntimeException(e);
+                    }
+                }
+                return new String(strBytes, StandardCharsets.UTF_8).substring(0, realLength);
             }
+            case "UTF16":
+            case "UTF16LE":
+            case "UTF16BE": {
+                byte[] strBytes = new byte[bitLength / 8];
+                int realLength = 0;
+                boolean finishedReading = false;
+                for (int i = 0; (i < (bitLength / 16)) && hasMore(16); i++) {
+                    try {
+                        byte b1 = readByte(logicalName);
+                        byte b2 = readByte(logicalName);
+                        if ((b1 == 0x00) && (b2 == 0x00)) {
+                            finishedReading = true;
+                        } else if (!finishedReading){
+                            strBytes[(i * 2)] = b1;
+                            strBytes[(i * 2) + 1] = b2;
+                            realLength++;
+                        }
+                    } catch (Exception e) {
+                        throw new PlcRuntimeException(e);
+                    }
+                }
+                Charset charset;
+                switch (encoding) {
+                    case "UTF16LE":
+                        charset = StandardCharsets.UTF_16LE;
+                        break;
+                    case "UTF16BE":
+                        charset = StandardCharsets.UTF_16BE;
+                        break;
+                    default:
+                        charset = StandardCharsets.UTF_16;
+                }
+                return new String(strBytes, charset).substring(0, realLength);
+            }
+            default:
+                throw new ParseException("Unsupported encoding: " + encoding);
         }
-        //replaceAll function removes and leading ' char or hypens.
-        return new String(strBytes, Charset.forName(encoding.replaceAll("[^a-zA-Z0-9]", "")));
     }
 
 
diff --git a/plc4j/spi/src/test/java/org/apache/plc4x/java/spi/generation/ReadBufferTest.java b/plc4j/spi/src/test/java/org/apache/plc4x/java/spi/generation/ReadBufferTest.java
index 757a0b30d..aeb6d60a4 100644
--- a/plc4j/spi/src/test/java/org/apache/plc4x/java/spi/generation/ReadBufferTest.java
+++ b/plc4j/spi/src/test/java/org/apache/plc4x/java/spi/generation/ReadBufferTest.java
@@ -30,7 +30,7 @@ class ReadBufferTest {
      * Test which makes sure that PLC4X-256 is not happening.
      */
     @Test
-    void readString() {
+    void readString() throws ParseException {
         String value = new String("abcdef");
         final ReadBuffer buffer = new ReadBufferByteBased(value.getBytes(StandardCharsets.UTF_8));
         String answer = buffer.readString("", value.length() * 8, "UTF-8");
diff --git a/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/java/s7/utils/StaticHelper.java b/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/java/s7/utils/StaticHelper.java
index e74b5ed9c..5f70844b9 100644
--- a/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/java/s7/utils/StaticHelper.java
+++ b/plc4j/utils/plc-simulator/src/main/java/org/apache/plc4x/java/s7/utils/StaticHelper.java
@@ -137,7 +137,7 @@ public class StaticHelper {
         throw new NotImplementedException("Serializing STRING not implemented");
     }
 
-    public static String parseS7Char(ReadBuffer io, Object encoding) {
+    public static String parseS7Char(ReadBuffer io, Object encoding) throws ParseException {
         // Read the full size of the string.
         return io.readString(8, (String) encoding);
     }
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
index b5ab8eb8a..92e4733c3 100644
--- 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
@@ -91,7 +91,7 @@ public abstract class ManualTest {
                 for (TestCase testCase : shuffledTestcases) {
                     sb.append(testCase.address).append(", ");
                 }
-                System.out.println("       using order: " + sb.toString());
+                System.out.println("       using order: " + sb);
 
                 final PlcReadRequest.Builder builder = plcConnection.readRequestBuilder();
                 for (TestCase testCase : shuffledTestcases) {
@@ -107,17 +107,20 @@ public abstract class ManualTest {
                 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));
+                    Assertions.assertEquals(PlcResponseCode.OK, readResponse.getResponseCode(fieldName),
+                        "Field: " + fieldName);
+                    Assertions.assertNotNull(readResponse.getPlcValue(fieldName), "Field: " + fieldName);
                     if (readResponse.getPlcValue(fieldName) instanceof PlcList) {
                         PlcList plcList = (PlcList) readResponse.getPlcValue(fieldName);
                         List<Object> expectedValues = (List<Object>) testCase.expectedReadValue;
                         for (int j = 0; j < expectedValues.size(); j++) {
-                            Assertions.assertEquals(expectedValues.get(j), plcList.getIndex(j).getObject());
+                            Assertions.assertEquals(expectedValues.get(j), plcList.getIndex(j).getObject(),
+                                "Field: " + fieldName);
                         }
                     } else {
                         Assertions.assertEquals(
-                            testCase.expectedReadValue.toString(), readResponse.getPlcValue(fieldName).getObject().toString());
+                            testCase.expectedReadValue.toString(), readResponse.getPlcValue(fieldName).getObject().toString(),
+                            "Field: " + fieldName);
                     }
                 }
             }
diff --git a/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs b/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs
index ae91c3575..59306feb1 100644
--- a/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs
+++ b/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs
@@ -623,8 +623,9 @@ namespace org.apache.plc4net.drivers.knxnetip.readwrite.model
         M_GUANGDONG_KANWAY = 596,
         M_PHOENIX_CONTACT_2 = 597,
         M_RAMIREZ_ENGINEERING_GMBH = 598,
-        M_ABB___RESERVED = 599,
-        M_BUSCH_JAEGER_ELEKTRO___RESERVED = 600,
+        M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD = 599,
+        M_ABB___RESERVED = 600,
+        M_BUSCH_JAEGER_ELEKTRO___RESERVED = 601,
     }
 
     public static class KnxManufacturerInfo
@@ -2299,8 +2300,8 @@ namespace org.apache.plc4net.drivers.knxnetip.readwrite.model
                 case KnxManufacturer.M_RAMIREZ_ENGINEERING_GMBH: { /* '598' */
                     return 656;
                 }
-                case KnxManufacturer.M_ABB___RESERVED: { /* '599' */
-                    return 43954;
+                case KnxManufacturer.M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD: { /* '599' */
+                    return 657;
                 }
                 case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO: { /* '6' */
                     return 7;
@@ -2308,7 +2309,10 @@ namespace org.apache.plc4net.drivers.knxnetip.readwrite.model
                 case KnxManufacturer.M_TECHEM: { /* '60' */
                     return 99;
                 }
-                case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '600' */
+                case KnxManufacturer.M_ABB___RESERVED: { /* '600' */
+                    return 43954;
+                }
+                case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '601' */
                     return 43959;
                 }
                 case KnxManufacturer.M_SCHNEIDER_ELECTRIC_INDUSTRIES_SAS: { /* '61' */
@@ -4112,8 +4116,8 @@ namespace org.apache.plc4net.drivers.knxnetip.readwrite.model
                 case KnxManufacturer.M_RAMIREZ_ENGINEERING_GMBH: { /* '598' */
                     return "RAMIREZ Engineering GmbH";
                 }
-                case KnxManufacturer.M_ABB___RESERVED: { /* '599' */
-                    return "ABB - reserved";
+                case KnxManufacturer.M_ZHONGSHAN_TAIYANG_IMPANDEXP__CO_LTD: { /* '599' */
+                    return "Zhongshan Taiyang IMP&EXP. CO LTD";
                 }
                 case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO: { /* '6' */
                     return "Busch-Jaeger Elektro";
@@ -4121,7 +4125,10 @@ namespace org.apache.plc4net.drivers.knxnetip.readwrite.model
                 case KnxManufacturer.M_TECHEM: { /* '60' */
                     return "Techem";
                 }
-                case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '600' */
+                case KnxManufacturer.M_ABB___RESERVED: { /* '600' */
+                    return "ABB - reserved";
+                }
+                case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '601' */
                     return "Busch-Jaeger Elektro - reserved";
                 }
                 case KnxManufacturer.M_SCHNEIDER_ELECTRIC_INDUSTRIES_SAS: { /* '61' */
diff --git a/protocols/ads/src/main/resources/protocols/ads/ads.mspec b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
index d1ce4bee6..284835ac0 100644
--- a/protocols/ads/src/main/resources/protocols/ads/ads.mspec
+++ b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
@@ -458,15 +458,15 @@
             [simple string 8 value]
         ]
         ['WCHAR' STRING
-            [simple string 16 value encoding='"UTF-16"']
+            [simple string 16 value encoding='"UTF-16LE"']
         ]
         ['STRING' STRING
-            // TODO: Fix this length
-            [manual vstring value 'STATIC_CALL("parseAmsString", readBuffer, stringLength, _type.encoding)' 'STATIC_CALL("serializeAmsString", writeBuffer, _value, stringLength, _type.encoding)' 'stringLength + 1']
+            [simple   vstring 'stringLength * 8' value ]
+            [reserved uint 8                    '0x00']
         ]
         ['WSTRING' STRING
-            // TODO: Fix this length
-            [manual vstring value 'STATIC_CALL("parseAmsString", readBuffer, stringLength, _type.encoding)' 'STATIC_CALL("serializeAmsString", writeBuffer, _value, stringLength, _type.encoding)' '(stringLength * 2) + 2' encoding='"UTF-16"']
+            [simple   vstring 'stringLength * 8 * 2' value    encoding='"UTF-16LE"']
+            [reserved uint 16                        '0x0000'                      ]
         ]
 
         // -----------------------------------------