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>