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 2019/08/08 14:22:33 UTC

[plc4x] branch feature/implement-df1-driver updated: - Renamed the ArrayField and ManualArrayFields "lengthType" and "lengthExpression" to "loopType" and "loopExpression" because the ManualArrayField needed a "lengthExpression" - Implemented the code-generation for "manual" and "manualArray" types

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

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


The following commit(s) were added to refs/heads/feature/implement-df1-driver by this push:
     new 19c0977  - Renamed the ArrayField and ManualArrayFields "lengthType" and "lengthExpression" to "loopType" and "loopExpression" because the ManualArrayField needed a "lengthExpression" - Implemented the code-generation for "manual" and "manualArray" types
19c0977 is described below

commit 19c09771cdc7ef095588b4a7df349a0c4e62f8e8
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Aug 8 16:22:26 2019 +0200

    - Renamed the ArrayField and ManualArrayFields "lengthType" and "lengthExpression" to "loopType" and "loopExpression" because the ManualArrayField needed a "lengthExpression"
    - Implemented the code-generation for "manual" and "manualArray" types
---
 .../language/java/JavaLanguageTemplateHelper.java  |  20 +++-
 .../main/resources/templates/java/io-template.ftlh | 132 +++++++++++++++------
 .../resources/templates/java/pojo-template.ftlh    |  16 +--
 .../plugins/codegenerator/language/mspec/MSpec.g4  |   6 +-
 .../mspec/model/fields/DefaultArrayField.java      |  18 +--
 .../model/fields/DefaultManualArrayField.java      |  24 ++--
 .../mspec/parser/MessageFormatListener.java        |  21 ++--
 .../df1/src/main/resources/protocols/df1/df1.mspec |   2 +-
 sandbox/test-java-df1-driver/pom.xml               |   5 -
 .../org/apache/plc4x/java/df1/util/DF1Utils.java   |  39 ++++++
 .../src/test/resources/testsuite/Df1Testsuite.xml  |  20 ++++
 11 files changed, 215 insertions(+), 88 deletions(-)

diff --git a/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java b/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
index 034a690..5f4685f 100644
--- a/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
+++ b/build-utils/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
@@ -373,15 +373,27 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel
     }
 
     public boolean isCountArray(ArrayField arrayField) {
-        return arrayField.getLengthType() == ArrayField.LengthType.COUNT;
+        return arrayField.getLoopType() == ArrayField.LoopType.COUNT;
     }
 
     public boolean isLengthArray(ArrayField arrayField) {
-        return arrayField.getLengthType() == ArrayField.LengthType.LENGTH;
+        return arrayField.getLoopType() == ArrayField.LoopType.LENGTH;
     }
 
     public boolean isTerminatedArray(ArrayField arrayField) {
-        return arrayField.getLengthType() == ArrayField.LengthType.TERMINATED;
+        return arrayField.getLoopType() == ArrayField.LoopType.TERMINATED;
+    }
+
+    public boolean isCountArray(ManualArrayField arrayField) {
+        return arrayField.getLoopType() == ManualArrayField.LoopType.COUNT;
+    }
+
+    public boolean isLengthArray(ManualArrayField arrayField) {
+        return arrayField.getLoopType() == ManualArrayField.LoopType.LENGTH;
+    }
+
+    public boolean isTerminatedArray(ManualArrayField arrayField) {
+        return arrayField.getLoopType() == ManualArrayField.LoopType.TERMINATED;
     }
 
     public String toSwitchExpression(String expression) {
@@ -545,7 +557,7 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel
                 if(arg instanceof VariableLiteral) {
                     VariableLiteral va = (VariableLiteral) arg;
                     // "io" and "value" are always available in every parser.
-                    boolean isSerializerArg = "io".equals(va.getName()) || "value".equals(va.getName());
+                    boolean isSerializerArg = "io".equals(va.getName()) || "value".equals(va.getName()) || "element".equals(va.getName());
                     if(parserArguments != null) {
                         for (Argument parserArgument : parserArguments) {
                             if (parserArgument.getName().equals(va.getName())) {
diff --git a/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh b/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
index e89daa6..d6770a8 100644
--- a/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
+++ b/build-utils/language-java/src/main/resources/templates/java/io-template.ftlh
@@ -77,15 +77,15 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
 <#switch field.typeName>
     <#case "array">
 
-        // Array field
+        // Array field (${field.name})
         <#-- Only update curPos if the length expression uses it -->
-        <#if field.lengthExpression.contains("curPos")>
+        <#if field.loopExpression.contains("curPos")>
         curPos = io.getPos() - startPos;
         </#if>
         <#-- If this is a count array, we can directly initialize an array with the given size -->
         <#if helper.isCountArray(field)>
         // Count array
-        int _${field.name}Count = ${helper.toDeserializationExpression(field.lengthExpression, type.parserArguments)};
+        int _${field.name}Count = ${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)};
         ${helper.getLanguageTypeNameForField(field)}[] ${field.name} = new ${helper.getLanguageTypeNameForField(field)}[_${field.name}Count];
         for(int i = 0; i < _${field.name}Count; i++) {
             ${field.name}[i] = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>${parserArgument}<#sep>, </#sep></#list></#if>)</#if>;
@@ -95,13 +95,13 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
             <#-- For a length array, we read data till the read position of the buffer reaches a given position -->
             <#if helper.isLengthArray(field)>
         // Length array
-        int _${field.name}Length = ${helper.toDeserializationExpression(field.lengthExpression, type.parserArguments)};
+        int _${field.name}Length = ${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)};
         List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}> _${field.name}List = new LinkedList<>();
         int ${field.name}EndPos = io.getPos() + _${field.name}Length;
         while(io.getPos() < ${field.name}EndPos) {
             _${field.name}List.add(<#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>(${helper.getArgumentType(field.type, parserArgument?index)}) (${helper.toDeserializationExpression(parserArgument, type.parserArguments)})<#sep>, </#sep></#list></#if>)</#if>);
             <#-- After parsing, update the current position, but only if it's needed -->
-            <#if field.lengthExpression.contains("curPos")>
+            <#if field.loopExpression.contains("curPos")>
             curPos = io.getPos() - startPos;
             </#if>
         }
@@ -109,11 +109,11 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
             <#elseif helper.isTerminatedArray(field)>
         // Terminated array
         List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}> _${field.name}List = new LinkedList<>();
-        while(!((boolean) (${helper.toDeserializationExpression(field.lengthExpression, type.parserArguments)}))) {
+        while(!((boolean) (${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)}))) {
             _${field.name}List.add(<#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>(${helper.getArgumentType(field.type, parserArgument?index)}) (${helper.toDeserializationExpression(parserArgument, type.parserArguments)})<#sep>, </#sep></#list></#if>)</#if>);
 
             <#-- After parsing, update the current position, but only if it's needed -->
-            <#if field.lengthExpression.contains("curPos")>
+            <#if field.loopExpression.contains("curPos")>
             curPos = io.getPos() - startPos;
             </#if>
         }
@@ -135,16 +135,16 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
         <#break>
     <#case "checksum">
 
-        // Checksum Field
+        // Checksum Field (${field.name})
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = io.${helper.getReadMethodName(field.type)}(${field.type.size});
         ${helper.getLanguageTypeNameForField(field)} _${field.name}Ref = (${helper.getLanguageTypeNameForField(field)}) (${helper.toDeserializationExpression(field.checksumExpression, type.parserArguments)});
         if(${field.name} != _${field.name}Ref) {
-            throw new ParseException("Checksum verification failed. Expected " + _${field.name}Ref + " but got " + ${field.name});
+            throw new ParseException(String.format("Checksum verification failed. Expected %04X but got %04X",_${field.name}Ref & 0xFFFF, ${field.name} & 0xFFFF));
         }
         <#break>
     <#case "const">
 
-        // Const Field
+        // Const Field (${field.name})
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = io.${helper.getReadMethodName(field.type)}(${field.type.size});
         if(${field.name} != ${typeName}.${field.name?upper_case}) {
             throw new ParseException("Expected constant value " + ${typeName}.${field.name?upper_case} + " but got " + ${field.name});
@@ -152,25 +152,80 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
         <#break>
     <#case "discriminator">
 
-        // Discriminator Field (Used as input to a switch field)
+        // Discriminator Field (${field.name}) (Used as input to a switch field)
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = io.${helper.getReadMethodName(field.type)}(${field.type.size});
     <#break>
     <#case "implicit">
 
-        // Implicit Field (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+        // Implicit Field (${field.name}) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = io.${helper.getReadMethodName(field.type)}(${field.type.size});
         <#break>
     <#case "manualArray">
 
-        <#-- TODO: Implement this -->
+        // Manual Array Field (${field.name})
+        <#-- Only update curPos if the length expression uses it -->
+        <#if field.loopExpression.contains("curPos")>
+        curPos = io.getPos() - startPos;
+        </#if>
+        <#-- If this is a count array, we can directly initialize an array with the given size -->
+        <#if helper.isCountArray(field)>
+        // Count array
+        int _${field.name}Count = ${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)};
+        ${helper.getLanguageTypeNameForField(field)}[] ${field.name} = new ${helper.getLanguageTypeNameForField(field)}[_${field.name}Count];
+        for(int i = 0; i < _${field.name}Count; i++) {
+            ${field.name}[i] = (${helper.getLanguageTypeNameForField(field)}) (${helper.toDeserializationExpression(field.deserializationExpression, type.parserArguments)});
+        }
+        <#-- In all other cases do we have to work with a list, that is later converted to an array -->
+        <#else>
+            <#-- For a length array, we read data till the read position of the buffer reaches a given position -->
+            <#if helper.isLengthArray(field)>
+        // Length array
+        int _${field.name}Length = ${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)};
+        List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}> _${field.name}List = new LinkedList<>();
+        int ${field.name}EndPos = io.getPos() + _${field.name}Length;
+        while(io.getPos() < ${field.name}EndPos) {
+            _${field.name}List.add((${helper.getLanguageTypeNameForField(field)}) (${helper.toDeserializationExpression(field.deserializationExpression, type.parserArguments)}));
+            <#-- After parsing, update the current position, but only if it's needed -->
+            <#if field.loopExpression.contains("curPos")>
+            curPos = io.getPos() - startPos;
+            </#if>
+        }
+            <#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
+            <#elseif helper.isTerminatedArray(field)>
+        // Terminated array
+        List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}> _${field.name}List = new LinkedList<>();
+        while(!((boolean) (${helper.toDeserializationExpression(field.loopExpression, type.parserArguments)}))) {
+            _${field.name}List.add((${helper.getLanguageTypeNameForField(field)}) (${helper.toDeserializationExpression(field.deserializationExpression, type.parserArguments)}));
+
+            <#-- After parsing, update the current position, but only if it's needed -->
+            <#if field.loopExpression.contains("curPos")>
+            curPos = io.getPos() - startPos;
+            </#if>
+        }
+            </#if>
+            <#--
+                Convert the list into an array. However if the array is of a primitive
+                type we have to iterate over it's elements and explicitly cast them.
+                Otherwise a simple toArray call is fine.
+            -->
+            <#if helper.isSimpleType(field.type)>
+        ${helper.getLanguageTypeNameForField(field)}[] ${field.name} = new ${helper.getLanguageTypeNameForField(field)}[_${field.name}List.size()];
+        for(int i = 0; i < _${field.name}List.size(); i++) {
+            ${field.name}[i] = (${helper.getLanguageTypeNameForField(field)}) _${field.name}List.get(i);
+        }
+            <#else>
+        ${helper.getLanguageTypeNameForField(field)}[] ${field.name} = _${field.name}List.toArray(new ${helper.getNonPrimitiveLanguageTypeNameForField(field)}[0]);
+            </#if>
+        </#if>
         <#break>
     <#case "manual">
 
-        <#-- TODO: Implement this -->
+        // Manual Field (${field.name})
+        ${helper.getLanguageTypeNameForField(field)} ${field.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toDeserializationExpression(field.deserializationExpression, type.parserArguments)});
         <#break>
     <#case "optional">
 
-        // Optional Field (Can be skipped, if a given expression evaluates to false)
+        // Optional Field (${field.name}) (Can be skipped, if a given expression evaluates to false)
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getNullValueForType(field.type)};
         if(${helper.toDeserializationExpression(field.conditionExpression, type.parserArguments)}) {
             ${field.name} = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io);</#if>;
@@ -178,7 +233,7 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
         <#break>
     <#case "padding">
 
-        // Padding Field
+        // Padding Field (${field.name})
         boolean _${field.name}NeedsPadding = (boolean) (${helper.toDeserializationExpression(field.paddingCondition, type.parserArguments)});
         if(_${field.name}NeedsPadding) {
             // Just read the padding data and ignore it
@@ -197,12 +252,12 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
         <#break>
     <#case "simple">
 
-        // Simple field
+        // Simple Field (${field.name})
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name?uncap_first}IO.parse(io<#if field.params?has_content>, <#list field.params as parserArgument>(${helper.getArgumentType(field.type, parserArgument?index)}) (${helper.toDeserializationExpression(parserArgument, type.parserArguments)})<#sep>, </#sep></#list></#if>)</#if>;
         <#break>
     <#case "switch">
 
-        // Switch field (Depending on the discriminator values, passes the instantiation to a sub-type)
+        // Switch Field (Depending on the discriminator values, passes the instantiation to a sub-type)
         ${typeName}Builder builder = null;
         <#list field.cases as case>
         if(<#list case.discriminatorValues as discriminatorValue>EvaluationHelper.equals(${helper.toSwitchExpression(field.discriminatorNames[discriminatorValue?index])}, ${discriminatorValue})<#sep> && </#sep></#list>) {
@@ -236,7 +291,7 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
 <#switch field.typeName>
     <#case "array">
 
-        // Array field
+        // Array Field (${field.name})
         if(value.get${field.name?cap_first}() != null) {
             for(${helper.getLanguageTypeNameForField(field)} element : value.get${field.name?cap_first}()) {
                 <#if helper.isSimpleType(field.type)>
@@ -249,42 +304,45 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
         <#break>
     <#case "checksum">
 
-        // Checksum Field (Calculated)
+        // Checksum Field (${field.name}) (Calculated)
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(field.checksumExpression, type.parserArguments)});
         io.${helper.getWriteBufferReadMethodCall(field.type, "(" + field.name + ")")?no_esc};
         <#break>
     <#case "const">
 
-        // Const Field
+        // Const Field (${field.name})
         io.${helper.getWriteBufferReadMethodCall(field.type, field.referenceValue)};
         <#break>
-    <#case "context">
-
-        // Context Field (Artificial value not retrieved by the input, but the context)
-        <#break>
     <#case "discriminator">
 
-        // Discriminator Field (Used as input to a switch field)
+        // Discriminator Field (${field.name}) (Used as input to a switch field)
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = (${helper.getLanguageTypeNameForField(field)}) value.getDiscriminatorValues()[0];
         io.${helper.getWriteBufferReadMethodCall(field.type, "(" + field.name + ")")};
         <#break>
     <#case "implicit">
 
-        // Implicit Field (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+        // Implicit Field (${field.name}) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(field.serializationExpression, type.parserArguments)});
         io.${helper.getWriteBufferReadMethodCall(field.type, "(" + field.name + ")")?no_esc};
         <#break>
     <#case "manualArray">
 
-        <#-- TODO: Implement this -->
+        // Manual Array Field (${field.name})
+        if(value.get${field.name?cap_first}() != null) {
+            for(${helper.getLanguageTypeNameForField(field)} element : value.get${field.name?cap_first}()) {
+                ${helper.toSerializationExpression(field.serializationExpression, type.parserArguments)};
+            }
+        }
         <#break>
     <#case "manual">
 
-        <#-- TODO: Implement this -->
-        <#break>
+        // Manual Field (${field.name})
+        ${helper.getLanguageTypeNameForField(field)} ${field.name} = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(field.serializationExpression, type.parserArguments)});
+         io.${helper.getWriteBufferReadMethodCall(field.type, "(" + field.name + ")")?no_esc};
+       <#break>
     <#case "optional">
 
-        // Optional Field (Can be skipped, if the value is null)
+        // Optional Field (${field.name}) (Can be skipped, if the value is null)
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = null;
         if(value.get${field.name?cap_first}() != null) {
             ${field.name} = (${helper.getLanguageTypeNameForField(field)}) value.get${field.name?cap_first}();
@@ -297,7 +355,7 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
         <#break>
     <#case "padding">
 
-        // Padding Field
+        // Padding Field (${field.name})
         boolean _${field.name}NeedsPadding = (boolean) (${helper.toSerializationExpression(field.paddingCondition, type.parserArguments)});
         if(_${field.name}NeedsPadding) {
             ${helper.getLanguageTypeNameForField(field)} _${field.name}PaddingValue = (${helper.getLanguageTypeNameForField(field)}) (${helper.toSerializationExpression(field.paddingValue, type.parserArguments)});
@@ -311,7 +369,7 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
         <#break>
     <#case "simple">
 
-        // Simple field
+        // Simple Field (${field.name})
         ${helper.getLanguageTypeNameForField(field)} ${field.name} = (${helper.getLanguageTypeNameForField(field)}) value.get${field.name?cap_first}();
         <#if helper.isSimpleType(field.type)>
         io.${helper.getWriteBufferReadMethodCall(field.type, "(" + field.name + ")")};
@@ -353,7 +411,7 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
 
 <#if type.abstract>
     public static interface ${typeName}Builder {
-        ${typeName} build(<#list type.propertyFields as field>${helper.getLanguageTypeNameForField(field)}<#if field.lengthType??>[]</#if> ${field.name}<#sep>, </#sep></#list>);
+        ${typeName} build(<#list type.propertyFields as field>${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name}<#sep>, </#sep></#list>);
     }
 
 </#if>
@@ -361,17 +419,17 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim
     public static class ${typeName}Builder implements ${type.parentType.name}IO.${type.parentType.name}Builder {
         <#if type.propertyFields?has_content>
         <#list type.propertyFields as field>
-        private final ${helper.getLanguageTypeNameForField(field)}<#if field.lengthType??>[]</#if> ${field.name};
+        private final ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name};
         </#list>
         </#if>
 
-        public ${typeName}Builder(<#list type.propertyFields as field>${helper.getLanguageTypeNameForField(field)}<#if field.lengthType??>[]</#if> ${field.name}<#sep>, </#sep></#list>) {
+        public ${typeName}Builder(<#list type.propertyFields as field>${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name}<#sep>, </#sep></#list>) {
             <#list type.propertyFields as field>
             this.${field.name} = ${field.name};
             </#list>
         }
 
-        public ${typeName} build(<#list type.parentType.propertyFields as field>${helper.getLanguageTypeNameForField(field)}<#if field.lengthType??>[]</#if> ${field.name}<#sep>, </#sep></#list>) {
+        public ${typeName} build(<#list type.parentType.propertyFields as field>${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name}<#sep>, </#sep></#list>) {
             return new ${typeName}(<#list type.getAllPropertyFields() as field>${field.name}<#sep>, </#sep></#list>);
         }
     }
diff --git a/build-utils/language-java/src/main/resources/templates/java/pojo-template.ftlh b/build-utils/language-java/src/main/resources/templates/java/pojo-template.ftlh
index 31e0cb9..066a303 100644
--- a/build-utils/language-java/src/main/resources/templates/java/pojo-template.ftlh
+++ b/build-utils/language-java/src/main/resources/templates/java/pojo-template.ftlh
@@ -67,13 +67,13 @@ public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??
 
     // Properties.
 <#list type.propertyFields as field>
-    private final ${helper.getLanguageTypeNameForField(field)}<#if field.lengthType??>[]</#if> ${field.name};
+    private final ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name};
 </#list>
 </#if>
 
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
     <#-- getAllPropertyFields() returns not only the property fields of this type but also of it's parents -->
-    public ${typeName}(<#list type.getAllPropertyFields() as field>@JsonProperty("${field.name}") ${helper.getLanguageTypeNameForField(field)}<#if field.lengthType??>[]</#if> ${field.name}<#sep>, </#sep></#list>) {
+    public ${typeName}(<#list type.getAllPropertyFields() as field>@JsonProperty("${field.name}") ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> ${field.name}<#sep>, </#sep></#list>) {
 <#if type.getParentPropertyFields()?has_content>
         super(<#list type.getParentPropertyFields() as field>${field.name}<#sep>, </#sep></#list>);
 </#if>
@@ -94,7 +94,7 @@ public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??
 </#if>
 
 <#list type.propertyFields as field>
-    public ${helper.getLanguageTypeNameForField(field)}<#if field.lengthType??>[]</#if> get${field.name?cap_first}() {
+    public ${helper.getLanguageTypeNameForField(field)}<#if field.loopType??>[]</#if> get${field.name?cap_first}() {
         return ${field.name};
     }
 
@@ -139,18 +139,12 @@ public<#if type.abstract> abstract</#if> class ${typeName}<#if type.parentType??
     <#case "manualArray">
 
         // Manual Array Field (${field.name})
-        <#if helper.isSimpleType(field.type)>
-        lengthInBits += ${field.type.size} * ${field.name}.length;
-        <#else>
-        for(SizeAware element : ${field.name}) {
-            lengthInBits += element.getLengthInBytes() * 8;
-        }
-        </#if>
+        lengthInBits += ${helper.toDeserializationExpression(field.lengthExpression, null)} * 8;
         <#break>
     <#case "manual">
 
         // Manual Field (${field.name})
-        lengthInBits += ${field.type.size};
+        lengthInBits += ${helper.toDeserializationExpression(field.lengthExpression, null)} * 8;
         <#break>
     <#case "optional">
 
diff --git a/build-utils/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4 b/build-utils/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4
index e88c327..ad94f70 100644
--- a/build-utils/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4
+++ b/build-utils/protocol-base-mspec/src/main/antlr4/org/apache/plc4x/plugins/codegenerator/language/mspec/MSpec.g4
@@ -53,7 +53,7 @@ field
  ;
 
 arrayField
- : 'array' type=typeReference name=idExpression lengthType=arrayType lengthExpression=expression
+ : 'array' type=typeReference name=idExpression loopType=arrayType loopExpression=expression
  ;
 
 checksumField
@@ -73,11 +73,11 @@ implicitField
  ;
 
 manualArrayField
- : 'manualArray' type=typeReference name=idExpression lengthType=arrayType lengthExpression=expression serializationExpression=expression deserializationExpression=expression
+ : 'manualArray' type=typeReference name=idExpression loopType=arrayType loopExpression=expression deserializationExpression=expression serializationExpression=expression lengthExpression=expression
  ;
 
 manualField
- : 'manual' type=typeReference name=idExpression serializationExpression=expression deserializationExpression=expression
+ : 'manual' type=typeReference name=idExpression deserializationExpression=expression serializationExpression=expression lengthExpression=expression
  ;
 
 optionalField
diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/fields/DefaultArrayField.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/fields/DefaultArrayField.java
index 83737f8..97efe2c 100644
--- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/fields/DefaultArrayField.java
+++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/fields/DefaultArrayField.java
@@ -27,15 +27,15 @@ public class DefaultArrayField implements ArrayField {
 
     private final TypeReference type;
     private final String name;
-    private final ArrayField.LengthType lengthType;
-    private final Term lengthExpression;
+    private final LoopType loopType;
+    private final Term loopExpression;
     private final Term[] params;
 
-    public DefaultArrayField(TypeReference type, String name, ArrayField.LengthType lengthType, Term lengthExpression, Term[] params) {
+    public DefaultArrayField(TypeReference type, String name, LoopType loopType, Term loopExpression, Term[] params) {
         this.type = type;
         this.name = name;
-        this.lengthType = lengthType;
-        this.lengthExpression = lengthExpression;
+        this.loopType = loopType;
+        this.loopExpression = loopExpression;
         this.params = params;
     }
 
@@ -47,12 +47,12 @@ public class DefaultArrayField implements ArrayField {
         return name;
     }
 
-    public ArrayField.LengthType getLengthType() {
-        return lengthType;
+    public LoopType getLoopType() {
+        return loopType;
     }
 
-    public Term getLengthExpression() {
-        return lengthExpression;
+    public Term getLoopExpression() {
+        return loopExpression;
     }
 
     @Override
diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/fields/DefaultManualArrayField.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/fields/DefaultManualArrayField.java
index c90bef1..52461da 100644
--- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/fields/DefaultManualArrayField.java
+++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/fields/DefaultManualArrayField.java
@@ -27,19 +27,21 @@ public class DefaultManualArrayField implements ManualArrayField {
 
     private final TypeReference type;
     private final String name;
-    private final LengthType lengthType;
-    private final Term lengthExpression;
+    private final LoopType loopType;
+    private final Term loopExpression;
     private final Term serializationExpression;
     private final Term deserializationExpression;
+    private final Term lengthExpression;
     private final Term[] params;
 
-    public DefaultManualArrayField(TypeReference type, String name, LengthType lengthType, Term lengthExpression, Term serializationExpression, Term deserializationExpression, Term[] params) {
+    public DefaultManualArrayField(TypeReference type, String name, LoopType loopType, Term loopExpression, Term serializationExpression, Term deserializationExpression, Term lengthExpression, Term[] params) {
         this.type = type;
         this.name = name;
-        this.lengthType = lengthType;
-        this.lengthExpression = lengthExpression;
+        this.loopType = loopType;
+        this.loopExpression = loopExpression;
         this.serializationExpression = serializationExpression;
         this.deserializationExpression = deserializationExpression;
+        this.lengthExpression = lengthExpression;
         this.params = params;
     }
 
@@ -51,12 +53,12 @@ public class DefaultManualArrayField implements ManualArrayField {
         return name;
     }
 
-    public LengthType getLengthType() {
-        return lengthType;
+    public LoopType getLoopType() {
+        return loopType;
     }
 
-    public Term getLengthExpression() {
-        return lengthExpression;
+    public Term getLoopExpression() {
+        return loopExpression;
     }
 
     public Term getSerializationExpression() {
@@ -67,6 +69,10 @@ public class DefaultManualArrayField implements ManualArrayField {
         return deserializationExpression;
     }
 
+    public Term getLengthExpression() {
+        return lengthExpression;
+    }
+
     @Override
     public Term[] getParams() {
         return params;
diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
index 4d616e8..74eafac 100644
--- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
+++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
@@ -96,11 +96,11 @@ public class MessageFormatListener extends MSpecBaseListener {
     public void enterArrayField(MSpecParser.ArrayFieldContext ctx) {
         TypeReference type = getTypeReference(ctx.type);
         String name = ctx.name.id.getText();
-        ArrayField.LengthType lengthType = ArrayField.LengthType.valueOf(ctx.lengthType.getText().toUpperCase());
-        String lengthExpressionString = ctx.lengthExpression.expr.getText();
-        Term lengthExpression =  getExpressionTerm(lengthExpressionString);
+        ArrayField.LoopType loopType = ArrayField.LoopType.valueOf(ctx.loopType.getText().toUpperCase());
+        String loopExpressionString = ctx.loopExpression.expr.getText();
+        Term loopExpression =  getExpressionTerm(loopExpressionString);
         Term[] params = getFieldParams((MSpecParser.FieldDefinitionContext) ctx.parent.parent);
-        Field field = new DefaultArrayField(type, name, lengthType, lengthExpression, params);
+        Field field = new DefaultArrayField(type, name, loopType, loopExpression, params);
         if(parserContexts.peek() != null) {
             parserContexts.peek().add(field);
         }
@@ -155,16 +155,19 @@ public class MessageFormatListener extends MSpecBaseListener {
     public void enterManualArrayField(MSpecParser.ManualArrayFieldContext ctx) {
         TypeReference type = getTypeReference(ctx.type);
         String name = ctx.name.id.getText();
-        ManualArrayField.LengthType lengthType = ManualArrayField.LengthType.valueOf(
-            ctx.lengthType.getText().toUpperCase());
-        String lengthExpressionString = ctx.lengthExpression.expr.getText();
-        Term lengthExpression =  getExpressionTerm(lengthExpressionString);
+        ManualArrayField.LoopType loopType = ManualArrayField.LoopType.valueOf(
+            ctx.loopType.getText().toUpperCase());
+        String loopExpressionString = ctx.loopExpression.expr.getText();
+        Term loopExpression =  getExpressionTerm(loopExpressionString);
         String serializationExpressionString = ctx.serializationExpression.expr.getText();
         Term serializationExpression = getExpressionTerm(serializationExpressionString);
         String deserializationExpressionString = ctx.deserializationExpression.expr.getText();
         Term deserializationExpression = getExpressionTerm(deserializationExpressionString);
+        String lengthExpressionString = ctx.lengthExpression.expr.getText();
+        Term lengthExpression =  getExpressionTerm(lengthExpressionString);
         Term[] params = getFieldParams((MSpecParser.FieldDefinitionContext) ctx.parent.parent);
-        Field field = new DefaultManualArrayField(type, name, lengthType, lengthExpression, serializationExpression, deserializationExpression, params);
+        Field field = new DefaultManualArrayField(type, name, loopType, loopExpression, serializationExpression,
+            deserializationExpression, lengthExpression, params);
         if(parserContexts.peek() != null) {
             parserContexts.peek().add(field);
         }
diff --git a/protocols/df1/src/main/resources/protocols/df1/df1.mspec b/protocols/df1/src/main/resources/protocols/df1/df1.mspec
index 7d927a6..b035a1d 100644
--- a/protocols/df1/src/main/resources/protocols/df1/df1.mspec
+++ b/protocols/df1/src/main/resources/protocols/df1/df1.mspec
@@ -46,7 +46,7 @@
             [simple uint 8     'size']
         ]
         ['0x41' DF1UnprotectedReadResponse
-            [array  uint 8 'data' terminated 'STATIC_CALL("org.apache.plc4x.java.df1.util.DF1Utils.dataTerminate", io)']
+            [manualArray  uint 8 'data' terminated 'STATIC_CALL("org.apache.plc4x.java.df1.util.DF1Utils.dataTerminate", io)' 'STATIC_CALL("org.apache.plc4x.java.df1.util.DF1Utils.readData", io)' 'STATIC_CALL("org.apache.plc4x.java.df1.util.DF1Utils.writeData", io, element)' 'STATIC_CALL("org.apache.plc4x.java.df1.util.DF1Utils.dataLength", data)']
         ]
     ]
 ]
diff --git a/sandbox/test-java-df1-driver/pom.xml b/sandbox/test-java-df1-driver/pom.xml
index bbd71ea..b7f437e 100644
--- a/sandbox/test-java-df1-driver/pom.xml
+++ b/sandbox/test-java-df1-driver/pom.xml
@@ -91,11 +91,6 @@
     </dependency>
 
     <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-    </dependency>
-
-    <dependency>
       <groupId>org.apache.plc4x</groupId>
       <artifactId>plc4x-build-utils-language-java</artifactId>
       <version>0.5.0-SNAPSHOT</version>
diff --git a/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/util/DF1Utils.java b/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/util/DF1Utils.java
index 6d87c77..28e9f1c 100644
--- a/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/util/DF1Utils.java
+++ b/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/util/DF1Utils.java
@@ -110,6 +110,8 @@ public class DF1Utils {
 
     public static boolean dataTerminate(ReadBuffer io) {
         try {
+            // The byte sequence 0x10 followed by 0x03 indicates the end of the message,
+            // so if we would read this, we abort the loop and stop reading data.
             if ((io.peekByte(0) == (byte) 0x10) && (io.peekByte(1) == (byte) 0x03)) {
                 return true;
             }
@@ -119,4 +121,41 @@ public class DF1Utils {
         return false;
     }
 
+    public static short readData(ReadBuffer io) {
+        try {
+            // If we read a 0x10, this has to be followed by another 0x10, which is how
+            // this value is escaped in DF1, so if we encounter two 0x10, we simply ignore the first.
+            if ((io.peekByte(0) == (byte) 0x10) && (io.peekByte(1) == 0x10)) {
+                io.readByte(8);
+            }
+            return io.readUnsignedShort(8);
+        } catch (ParseException e) {
+            throw new RuntimeException("Error parsing data", e);
+        }
+    }
+
+    public static void writeData(WriteBuffer io, short data) {
+        try {
+            // If a value is 0x10, this has to be duplicated in order to be escaped.
+            if ((data == (short) 0x10)) {
+                io.writeUnsignedShort(8, (short) 0x10);
+            }
+            io.writeUnsignedShort(8, data);
+        } catch (ParseException e) {
+            throw new RuntimeException("Error parsing data", e);
+        }
+    }
+
+    public static int dataLength(short[] data) {
+        int i = 0;
+        for(short dataByte : data) {
+            // If a value is 0x10, this has to be duplicated which increases the message size by one.
+            if(dataByte == 0x10) {
+                i++;
+            }
+            i++;
+        }
+        return i;
+    }
+
 }
diff --git a/sandbox/test-java-df1-driver/src/test/resources/testsuite/Df1Testsuite.xml b/sandbox/test-java-df1-driver/src/test/resources/testsuite/Df1Testsuite.xml
index b0691ef..0cf6434 100644
--- a/sandbox/test-java-df1-driver/src/test/resources/testsuite/Df1Testsuite.xml
+++ b/sandbox/test-java-df1-driver/src/test/resources/testsuite/Df1Testsuite.xml
@@ -61,6 +61,26 @@
   </testcase>
 
   <testcase>
+    <name>Unprotected Read Address Response (Containing 0x10 in the data)</name>
+    <raw>10020A09410001001010FF1003BAAD</raw>
+    <root-type>DF1Symbol</root-type>
+    <xml>
+      <DF1SymbolMessageFrame className="org.apache.plc4x.java.df1.DF1SymbolMessageFrame">
+        <destinationAddress>10</destinationAddress>
+        <sourceAddress>9</sourceAddress>
+        <command className="org.apache.plc4x.java.df1.DF1UnprotectedReadResponse">
+          <status>0</status>
+          <transactionCounter>1</transactionCounter>
+          <data>
+            <data>16</data>
+            <data>255</data>
+          </data>
+        </command>
+      </DF1SymbolMessageFrame>
+    </xml>
+  </testcase>
+
+  <testcase>
     <name>ACK Response</name>
     <raw>1006</raw>
     <root-type>DF1Symbol</root-type>