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
commit 376101d1ef939b3d1e191b4c5d467117af596b3a Author: Christofer Dutz <[email protected]> AuthorDate: Wed Aug 7 16:26:29 2019 +0200 - Simplified the mspec for the DF1 protocol - Added a "terminated" type of arrayField - Made the ReadBuffer endianness aware - Made the testsuite endianness aware --- .../language/java/JavaLanguageTemplateHelper.java | 58 ++++++++- .../main/resources/templates/java/io-template.ftlh | 62 +++++++--- .../plugins/codegenerator/language/mspec/MSpec.g4 | 2 + .../mspec/model/fields/DefaultArrayField.java | 5 - .../mspec/parser/MessageFormatListener.java | 6 +- plc4j/examples/hello-world-plc4x/pom.xml | 1 + .../org/apache/plc4x/java/utils/ReadBuffer.java | 39 ++++++- .../org/apache/plc4x/java/utils/WriteBuffer.java | 8 +- .../protocol/test/ProtocolTestsuiteRunner.java | 14 +-- .../protocol/test/model/ProtocolTestsuite.java | 8 +- .../src/main/resources/schemas/testsuite.xsd | 1 + .../main/resources/protocols/df1/protocol.mspec | 43 +++---- sandbox/test-java-df1-driver/pom.xml | 31 ++++- .../plc4x/java/df1/protocol/Df1Protocol.java | 16 +-- .../org/apache/plc4x/java/df1/util/DF1Utils.java | 129 +++++++++++++++++++++ .../org/apache/plc4x/protocol/df1/DF1Utils.java | 84 -------------- .../plc4x/protocol/df1/BenchmarkGeneratedDf1.java | 7 +- .../plc4x/protocol/df1/BenchmarkManualDf1.java | 1 - .../org/apache/plc4x/protocol/df1/Df1Test.java | 30 +++++ .../apache/plc4x/protocol/df1/EndToEndTest.java | 3 +- .../java/org/apache/plc4x/protocol/df1/IOTest.java | 99 ++++++++++++++++ .../src/test/resources/testsuite/Df1Testsuite.xml | 81 +++++++++++++ 22 files changed, 554 insertions(+), 174 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 bf293a6..034a690 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 @@ -376,6 +376,14 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel return arrayField.getLengthType() == ArrayField.LengthType.COUNT; } + public boolean isLengthArray(ArrayField arrayField) { + return arrayField.getLengthType() == ArrayField.LengthType.LENGTH; + } + + public boolean isTerminatedArray(ArrayField arrayField) { + return arrayField.getLengthType() == ArrayField.LengthType.TERMINATED; + } + public String toSwitchExpression(String expression) { StringBuilder sb = new StringBuilder(); Pattern pattern = Pattern.compile("([^\\.]*)\\.([a-zA-Z\\d]+)(.*)"); @@ -390,8 +398,8 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel return sb.toString(); } - public String toDeserializationExpression(Term term) { - return toExpression(term, this::toVariableDeserializationExpression); + public String toDeserializationExpression(Term term, Argument[] parserArguments) { + return toExpression(term, term1 -> toVariableDeserializationExpression(term1, parserArguments)); } public String toSerializationExpression(Term term, Argument[] parserArguments) { @@ -450,7 +458,7 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel } } - private String toVariableDeserializationExpression(Term term) { + private String toVariableDeserializationExpression(Term term, Argument[] parserArguments) { VariableLiteral vl = (VariableLiteral) term; // CAST expressions are special as we need to add a ".class" to the second parameter in Java. if("CAST".equals(vl.getName())) { @@ -458,10 +466,47 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel if((vl.getArgs() == null) || (vl.getArgs().size() != 2)) { throw new RuntimeException("A CAST expression expects exactly two arguments."); } - sb.append("(").append(toVariableDeserializationExpression(vl.getArgs().get(0))) + sb.append("(").append(toVariableDeserializationExpression(vl.getArgs().get(0), parserArguments)) .append(", ").append(((VariableLiteral) vl.getArgs().get(1)).getName()).append(".class)"); return sb.toString() + ((vl.getChild() != null) ? "." + toVariableExpressionRest(vl.getChild()) : ""); } + else if("STATIC_CALL".equals(vl.getName())) { + StringBuilder sb = new StringBuilder(); + if(!(vl.getArgs().get(0) instanceof StringLiteral)) { + throw new RuntimeException("Expecting the first argument of a 'STATIC_CALL' to be a StringLiteral"); + } + String methodName = ((StringLiteral) vl.getArgs().get(0)).getValue(); + methodName = methodName.substring(1, methodName.length() - 1); + sb.append(methodName).append("("); + for(int i = 1; i < vl.getArgs().size(); i++) { + Term arg = vl.getArgs().get(i); + if(i > 1) { + sb.append(", "); + } + if(arg instanceof VariableLiteral) { + VariableLiteral va = (VariableLiteral) arg; + // "io" is the default name of the reader argument which is always available. + boolean isDeserializerArg = "io".equals(va.getName()); + if(parserArguments != null) { + for (Argument parserArgument : parserArguments) { + if (parserArgument.getName().equals(va.getName())) { + isDeserializerArg = true; + break; + } + } + } + if(isDeserializerArg) { + sb.append(va.getName() + ((va.getChild() != null) ? "." + toVariableExpressionRest(va.getChild()) : "")); + } else { + sb.append(toVariableDeserializationExpression(va, null)); + } + } else if(arg instanceof StringLiteral) { + sb.append(((StringLiteral) arg).getValue()); + } + } + sb.append(")"); + return sb.toString(); + } // All uppercase names are not fields, but utility methods. else if(vl.getName().equals(vl.getName().toUpperCase())) { StringBuilder sb = new StringBuilder(vl.getName()); @@ -472,7 +517,7 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel if(!firstArg) { sb.append(", "); } - sb.append(toVariableDeserializationExpression(arg)); + sb.append(toVariableDeserializationExpression(arg, parserArguments)); firstArg = false; } sb.append(")"); @@ -499,7 +544,8 @@ public class JavaLanguageTemplateHelper implements FreemarkerLanguageTemplateHel } if(arg instanceof VariableLiteral) { VariableLiteral va = (VariableLiteral) arg; - boolean isSerializerArg = false; + // "io" and "value" are always available in every parser. + boolean isSerializerArg = "io".equals(va.getName()) || "value".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 33d0696..82a8056 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 @@ -78,32 +78,59 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim <#case "array"> // Array field + <#-- Only update curPos if the length expression uses it --> <#if field.lengthExpression.contains("curPos")> curPos = io.getPos() - startPos; </#if> - int ${field.name}Size = ${helper.toDeserializationExpression(field.lengthExpression)}; + <#-- If this is a count array, we can directly initialize an array with the given size --> <#if helper.isCountArray(field)> - ${helper.getLanguageTypeNameForField(field)}[] ${field.name} = new ${helper.getLanguageTypeNameForField(field)}[${field.name}Size]; - for(int i = 0; i < ${field.name}Size; i++) { + // Count array + int _${field.name}Count = ${helper.toDeserializationExpression(field.lengthExpression, 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>; } + <#-- In all other cases do we have to work with a list, that is later converted to an array --> <#else> - List<${helper.getNonPrimitiveLanguageTypeNameForField(field)}> ${field.name}List = <#if helper.isCountArray(field)>new ArrayList<>(size)<#else>new LinkedList<>()</#if>; - int ${field.name}EndPos = io.getPos() + ${field.name}Size; + <#-- 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)}; + 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")> curPos = io.getPos() - startPos; </#if> - ${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)})<#sep>, </#sep></#list></#if>)</#if>); } - <#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); + <#-- 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.lengthExpression, 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")> + curPos = io.getPos() - startPos; + </#if> } - <#else> - ${helper.getLanguageTypeNameForField(field)}[] ${field.name} = ${field.name}List.toArray(new ${helper.getNonPrimitiveLanguageTypeNameForField(field)}[0]); - </#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 "const"> @@ -128,7 +155,7 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim // Optional Field (Can be skipped, if a given expression evaluates to false) ${helper.getLanguageTypeNameForField(field)} ${field.name} = ${helper.getNullValueForType(field.type)}; - if(${helper.toDeserializationExpression(field.conditionExpression)}) { + 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>; } <#break> @@ -145,7 +172,7 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim <#case "simple"> // Simple field - ${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)})<#sep>, </#sep></#list></#if>)</#if>; + ${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"> @@ -240,7 +267,7 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim <#if helper.isSimpleType(field.type)> io.${helper.getWriteBufferReadMethodCall(field.type, "(" + field.name + ")")}; <#else> - ${field.type.name?uncap_first}IO.serialize(io, ${field.name}<#if field.params?has_content>, <#list field.params as term>(${helper.getArgumentType(field.type, term?index)}) (${helper.toDeserializationExpression(term)})<#sep>, </#sep></#list></#if>); + ${field.type.name?uncap_first}IO.serialize(io, ${field.name}<#if field.params?has_content>, <#list field.params as term>(${helper.getArgumentType(field.type, term?index)}) (${helper.toDeserializationExpression(term, type.parserArguments)})<#sep>, </#sep></#list></#if>); </#if> <#break> <#case "switch"> @@ -255,9 +282,6 @@ public class ${typeName}IO implements MessageIO<${typeName}<#if helper.isDiscrim </#switch> </#list> } - public static int CRC(Object... args) { - return 0; - } private static int COUNT(Object obj) { if(obj.getClass().isArray()) { 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 374894b..3b50253 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 @@ -148,6 +148,7 @@ fragment HexDigit arrayType : K_COUNT | K_LENGTH + | K_TERMINATED ; idExpression @@ -170,6 +171,7 @@ K_TYPE_SWITCH : 'typeSwitch'; K_COUNT : 'count'; K_LENGTH : 'length'; +K_TERMINATED : 'terminated'; K_BIT : 'bit'; K_INT : 'int'; 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 4a06206..83737f8 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 @@ -60,9 +60,4 @@ public class DefaultArrayField implements ArrayField { return params; } - public static enum LengthType { - COUNT, - LENGTH - } - } 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 5a6441c..aeec697 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,8 +96,12 @@ public class MessageFormatListener extends MSpecBaseListener { ArrayField.LengthType lengthType; if(ctx.lengthType.K_COUNT() != null) { lengthType = ArrayField.LengthType.COUNT; - } else { + } else if(ctx.lengthType.K_LENGTH() != null){ lengthType = ArrayField.LengthType.LENGTH; + } else if(ctx.lengthType.K_TERMINATED() != null) { + lengthType = ArrayField.LengthType.TERMINATED; + } else { + throw new RuntimeException("Unsupported lenghtType for arrayField"); } String lengthExpressionString = ctx.lengthExpression.expr.getText(); InputStream inputStream = IOUtils.toInputStream(lengthExpressionString); diff --git a/plc4j/examples/hello-world-plc4x/pom.xml b/plc4j/examples/hello-world-plc4x/pom.xml index e3295da..eaee6fe 100644 --- a/plc4j/examples/hello-world-plc4x/pom.xml +++ b/plc4j/examples/hello-world-plc4x/pom.xml @@ -91,6 +91,7 @@ <usedDependencies combine.children="append"> <usedDependency>org.apache.plc4x:plc4j-driver-s7</usedDependency> <usedDependency>org.apache.plc4x:plc4j-driver-simulated</usedDependency> + <usedDependency>org.apache.plc4x.sandbox:test-java-df1-driver</usedDependency> <usedDependency>org.slf4j:log4j-over-slf4j</usedDependency> </usedDependencies> </configuration> diff --git a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java index 283e1b3..43ac53c 100644 --- a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java +++ b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/ReadBuffer.java @@ -28,17 +28,39 @@ import java.math.BigInteger; public class ReadBuffer { - private MyDefaultBitInput bi; + private final MyDefaultBitInput bi; + private final boolean littleEndian; public ReadBuffer(byte[] input) { + this(input, true); + } + + public ReadBuffer(byte[] input, boolean littleEndian) { ArrayByteInput abi = new ArrayByteInput(input); bi = new MyDefaultBitInput(abi); + this.littleEndian = littleEndian; } public int getPos() { return (int) bi.getPos(); } + public byte peekByte(int offset) throws ParseException { + // Remember the old index. + int oldIndex = bi.getDelegate().getIndex(); + try { + // Set the delegate to the desired position. + bi.getDelegate().index(oldIndex + offset); + // Read the byte. + return bi.readByte(false, 8); + } catch (IOException e) { + throw new ParseException("Error reading", e); + } finally { + // Reset the delegate to the old index. + bi.getDelegate().index(oldIndex); + } + } + public boolean readBit() throws ParseException { try { return bi.readBoolean(); @@ -83,6 +105,9 @@ public class ReadBuffer { throw new ParseException("unsigned int can only contain max 16 bits"); } try { + if(!littleEndian) { + return Integer.reverseBytes(bi.readInt(true, bitLength)) >> 16; + } return bi.readInt(true, bitLength); } catch (IOException e) { throw new ParseException("Error reading", e); @@ -97,6 +122,9 @@ public class ReadBuffer { throw new ParseException("unsigned long can only contain max 32 bits"); } try { + if(!littleEndian) { + return Long.reverseBytes(bi.readLong(true, bitLength)) >> 32; + } return bi.readLong(true, bitLength); } catch (IOException e) { throw new ParseException("Error reading", e); @@ -129,6 +157,9 @@ public class ReadBuffer { throw new ParseException("short can only contain max 16 bits"); } try { + if(!littleEndian) { + return Short.reverseBytes(bi.readShort(false, bitLength)); + } return bi.readShort(false, bitLength); } catch (IOException e) { throw new ParseException("Error reading", e); @@ -143,6 +174,9 @@ public class ReadBuffer { throw new ParseException("int can only contain max 32 bits"); } try { + if(!littleEndian) { + return Integer.reverseBytes(bi.readInt(false, bitLength)); + } return bi.readInt(false, bitLength); } catch (IOException e) { throw new ParseException("Error reading", e); @@ -157,6 +191,9 @@ public class ReadBuffer { throw new ParseException("long can only contain max 64 bits"); } try { + if(!littleEndian) { + return Long.reverseBytes(bi.readLong(false, bitLength)); + } return bi.readLong(false, bitLength); } catch (IOException e) { throw new ParseException("Error reading", e); diff --git a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java index bc10358..8c14102 100644 --- a/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java +++ b/plc4j/utils/driver-base-java/src/main/java/org/apache/plc4x/java/utils/WriteBuffer.java @@ -30,10 +30,10 @@ import java.nio.ByteBuffer; public class WriteBuffer { - private ByteBuffer bb; - private BufferByteOutput bbo; - private BitOutput bo; - private boolean littleEndian; + private final ByteBuffer bb; + private final BufferByteOutput bbo; + private final BitOutput bo; + private final boolean littleEndian; public WriteBuffer(int size) { this(size, true); diff --git a/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/ProtocolTestsuiteRunner.java b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/ProtocolTestsuiteRunner.java index f85d3af..50dbc41 100644 --- a/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/ProtocolTestsuiteRunner.java +++ b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/ProtocolTestsuiteRunner.java @@ -59,7 +59,7 @@ public class ProtocolTestsuiteRunner { String testcaseName = testcase.getName(); String testcaseLabel = testSuite.getName() + ": " + testcaseName; DynamicTest test = DynamicTest.dynamicTest(testcaseLabel, () -> - run(testcase) + run(testSuite, testcase) ); dynamicTests.add(test); } @@ -71,6 +71,7 @@ public class ProtocolTestsuiteRunner { SAXReader reader = new SAXReader(); Document document = reader.read(testsuiteDocumentXml); Element testsuiteXml = document.getRootElement(); + boolean littleEndian = !"true".equals(testsuiteXml.attributeValue("bigEndian")); Element testsuiteName = testsuiteXml.element(new QName("name")); List<Element> testcasesXml = testsuiteXml.elements(new QName("testcase")); List<Testcase> testcases = new ArrayList<>(testcasesXml.size()); @@ -89,7 +90,7 @@ public class ProtocolTestsuiteRunner { testcases.add(new Testcase(name, description, raw, rootType, xmlElement)); } LOGGER.info(String.format("Found %d testcases.", testcases.size())); - return new ProtocolTestsuite(testsuiteName.getTextTrim(), testcases); + return new ProtocolTestsuite(testsuiteName.getTextTrim(), testcases, littleEndian); } catch (DocumentException e) { throw new ProtocolTestsuiteException("Error parsing testsuite xml", e); } catch (DecoderException e) { @@ -97,9 +98,9 @@ public class ProtocolTestsuiteRunner { } } - private void run(Testcase testcase) throws ProtocolTestsuiteException { + private void run(ProtocolTestsuite testSuite, Testcase testcase) throws ProtocolTestsuiteException { ObjectMapper mapper = new XmlMapper().enableDefaultTyping(); - ReadBuffer readBuffer = new ReadBuffer(testcase.getRaw()); + ReadBuffer readBuffer = new ReadBuffer(testcase.getRaw(), testSuite.isLittleEndian()); String referenceXml = testcase.getXml().elements().get(0).asXML(); MessageIO messageIO = getMessageIOForTestcase(testcase); @@ -108,10 +109,9 @@ public class ProtocolTestsuiteRunner { String xmlString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(msg); Diff diff = DiffBuilder.compare(referenceXml).withTest(xmlString).ignoreWhitespace().build(); if(diff.hasDifferences()) { - // TODO: Add some more information ... - throw new ProtocolTestsuiteException("Differences were found after parsing."); + throw new ProtocolTestsuiteException("Differences were found after parsing.\n" + diff.toString()); } - WriteBuffer writeBuffer = new WriteBuffer(((SizeAware) msg).getLengthInBytes()); + WriteBuffer writeBuffer = new WriteBuffer(((SizeAware) msg).getLengthInBytes(), testSuite.isLittleEndian()); messageIO.serialize(writeBuffer, msg); byte[] data = writeBuffer.getData(); if(!Arrays.equals(testcase.getRaw(), data)) { diff --git a/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/ProtocolTestsuite.java b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/ProtocolTestsuite.java index 68b0ffe..83de706 100644 --- a/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/ProtocolTestsuite.java +++ b/plc4j/utils/protocol-test-utils/src/main/java/org/apache/plc4x/protocol/test/model/ProtocolTestsuite.java @@ -25,10 +25,12 @@ public class ProtocolTestsuite { private final String name; private final List<Testcase> testcases; + private final boolean littleEndian; - public ProtocolTestsuite(String name, List<Testcase> testcases) { + public ProtocolTestsuite(String name, List<Testcase> testcases, boolean littleEndian) { this.name = name; this.testcases = testcases; + this.littleEndian = littleEndian; } public String getName() { @@ -39,4 +41,8 @@ public class ProtocolTestsuite { return testcases; } + public boolean isLittleEndian() { + return littleEndian; + } + } diff --git a/plc4j/utils/protocol-test-utils/src/main/resources/schemas/testsuite.xsd b/plc4j/utils/protocol-test-utils/src/main/resources/schemas/testsuite.xsd index 5ef4977..58c099b 100644 --- a/plc4j/utils/protocol-test-utils/src/main/resources/schemas/testsuite.xsd +++ b/plc4j/utils/protocol-test-utils/src/main/resources/schemas/testsuite.xsd @@ -48,6 +48,7 @@ </xs:complexType> </xs:element> </xs:sequence> + <xs:attribute name="bigEndian" type="xs:boolean"/> </xs:complexType> </xs:element> diff --git a/protocols/df1/src/main/resources/protocols/df1/protocol.mspec b/protocols/df1/src/main/resources/protocols/df1/protocol.mspec index d63974c..5e997fb 100644 --- a/protocols/df1/src/main/resources/protocols/df1/protocol.mspec +++ b/protocols/df1/src/main/resources/protocols/df1/protocol.mspec @@ -17,32 +17,17 @@ // under the License. // - -[type 'ReadRequest' - [field DF1Symbol 'messageFrameStart' ['0', 'null']] - [field DF1Symbol 'messageFrameEnd' ['0', 'messageFrameStart']] -] - -[type 'ReadResponse' [uint 8 'payloadSize'] - [field DF1Symbol 'messageFrameStart' ['payloadSize', 'null']] - [field DF1Symbol 'messageFrameEnd' ['0', 'messageFrameStart']] -] - -[type 'Result' - [field DF1Symbol 'result' ['0', 'null']] -] - -[discriminatedType 'DF1Symbol' [uint 8 'payloadSize', DF1SymbolMessageFrameStart 'messageStartSymbol'] +[discriminatedType 'DF1Symbol' [const uint 8 'messageStart' '0x10'] [discriminator uint 8 'symbolType'] [typeSwitch 'symbolType' - ['0x02' DF1SymbolMessageFrameStart + ['0x02' DF1SymbolMessageFrame [field uint 8 'destinationAddress'] [field uint 8 'sourceAddress'] - [field DF1Command 'command' ['payloadSize']] - ] - ['0x03' DF1SymbolMessageFrameEnd - [implicit uint 16 'crc' 'STATIC_CALL("org.apache.plc4x.protocol.df1.DF1Utils.CRCCheck", discriminatorValues[0], messageStartSymbol)'] + [field DF1Command 'command'] + [const uint 8 'messageEnd' '0x10'] + [const uint 8 'endTransaction' '0x03'] + [implicit uint 16 'crc' 'STATIC_CALL("org.apache.plc4x.java.df1.util.DF1Utils.CRCCheck", value)'] ] ['0x06' DF1SymbolMessageFrameACK ] @@ -51,17 +36,17 @@ ] ] -[discriminatedType 'DF1Command' [uint 8 'payloadSize'] - [discriminator uint 8 'commandType'] +[discriminatedType 'DF1Command' + [discriminator uint 8 'commandCode'] [field uint 8 'status'] [field uint 16 'transactionCounter'] - [typeSwitch 'commandType' - ['0x01' DF1ReadRequest - [field uint 16 'address'] - [field uint 8 'size'] + [typeSwitch 'commandCode' + ['0x01' DF1UnprotectedReadRequest + [field uint 16 'address'] + [field uint 8 'size'] ] - ['0x41' DF1ReadResponse - [arrayField uint 8 'data' length 'payloadSize'] + ['0x41' DF1UnprotectedReadResponse + [arrayField uint 8 'data' terminated 'STATIC_CALL("org.apache.plc4x.java.df1.util.DF1Utils.dataTerminate", io)'] ] ] ] diff --git a/sandbox/test-java-df1-driver/pom.xml b/sandbox/test-java-df1-driver/pom.xml index 7159072..904ef55 100644 --- a/sandbox/test-java-df1-driver/pom.xml +++ b/sandbox/test-java-df1-driver/pom.xml @@ -132,10 +132,37 @@ <scope>provided</scope> </dependency> <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> + <groupId>org.apache.plc4x</groupId> + <artifactId>plc4j-utils-protocol-test-utils</artifactId> + <version>0.5.0-SNAPSHOT</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.dataformat</groupId> + <artifactId>jackson-dataformat-xml</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-launcher</artifactId> + <scope>test</scope> + </dependency> + </dependencies> </project> diff --git a/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/protocol/Df1Protocol.java b/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/protocol/Df1Protocol.java index bbeece2..31a9024 100644 --- a/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/protocol/Df1Protocol.java +++ b/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/protocol/Df1Protocol.java @@ -23,9 +23,9 @@ import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; import org.apache.plc4x.java.api.exceptions.PlcProtocolException; import org.apache.plc4x.java.base.PlcByteToMessageCodec; -import org.apache.plc4x.java.df1.DF1ReadRequest; import org.apache.plc4x.java.df1.DF1Symbol; -import org.apache.plc4x.java.df1.DF1SymbolMessageFrameStart; +import org.apache.plc4x.java.df1.DF1SymbolMessageFrame; +import org.apache.plc4x.java.df1.DF1UnprotectedReadRequest; import org.apache.plc4x.java.df1.io.DF1SymbolIO; import org.apache.plc4x.java.utils.ReadBuffer; import org.apache.plc4x.java.utils.WriteBuffer; @@ -52,12 +52,12 @@ public class Df1Protocol extends PlcByteToMessageCodec<DF1Symbol> { @Override protected void encode(ChannelHandlerContext ctx, DF1Symbol msg, ByteBuf out) throws Exception { // Remember the size of the request as we need this to decode the response. - if(msg instanceof DF1SymbolMessageFrameStart) { - DF1SymbolMessageFrameStart frameStart = (DF1SymbolMessageFrameStart) msg; - if(frameStart.getCommand() instanceof DF1ReadRequest) { - DF1ReadRequest readRequest = (DF1ReadRequest) frameStart.getCommand(); - int transactionCounter = readRequest.getTransactionCounter(); - readRequestSizes.put(transactionCounter, readRequest.getSize()); + if(msg instanceof DF1SymbolMessageFrame) { + DF1SymbolMessageFrame frame = (DF1SymbolMessageFrame) msg; + if(frame.getCommand() instanceof DF1UnprotectedReadRequest) { + DF1UnprotectedReadRequest unprotectedReadRequest = (DF1UnprotectedReadRequest) frame.getCommand(); + int transactionCounter = unprotectedReadRequest.getTransactionCounter(); + readRequestSizes.put(transactionCounter, unprotectedReadRequest.getSize()); } } 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 new file mode 100644 index 0000000..1d36d65 --- /dev/null +++ b/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/java/df1/util/DF1Utils.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.plc4x.java.df1.util; + +import org.apache.plc4x.java.df1.DF1Symbol; +import org.apache.plc4x.java.df1.DF1SymbolMessageFrame; +import org.apache.plc4x.java.df1.DF1UnprotectedReadRequest; +import org.apache.plc4x.java.df1.DF1UnprotectedReadResponse; +import org.apache.plc4x.java.utils.ParseException; +import org.apache.plc4x.java.utils.ReadBuffer; +import org.apache.plc4x.java.utils.WriteBuffer; + +public class DF1Utils { + + public static short CRCCheck(Object... args) { + DF1Symbol symbol = (DF1Symbol) args[0]; + if(symbol instanceof DF1SymbolMessageFrame) { + DF1SymbolMessageFrame messageFrame = (DF1SymbolMessageFrame) symbol; + + short destinationAddress = messageFrame.getDestinationAddress(); + short sourceAddress = messageFrame.getSourceAddress(); + short commandDiscriminatorValues = (short) messageFrame.getCommand().getDiscriminatorValues()[0]; + short status = messageFrame.getCommand().getStatus(); + int counter = messageFrame.getCommand().getTransactionCounter(); + if(messageFrame.getCommand() instanceof DF1UnprotectedReadRequest) { + DF1UnprotectedReadRequest unprotectedReadRequestCommand = (DF1UnprotectedReadRequest) messageFrame.getCommand(); + try { + WriteBuffer writeBuffer = new WriteBuffer(10, false); + writeBuffer.writeUnsignedShort(8, destinationAddress); + writeBuffer.writeUnsignedShort(8, sourceAddress); + writeBuffer.writeUnsignedShort(8, commandDiscriminatorValues); + writeBuffer.writeUnsignedShort(8, status); + writeBuffer.writeUnsignedInt(16, (short) counter); + writeBuffer.writeUnsignedInt(16, (short) unprotectedReadRequestCommand.getAddress()); + writeBuffer.writeUnsignedShort(8, (byte) unprotectedReadRequestCommand.getSize()); + writeBuffer.writeUnsignedShort(8, (byte) 0x03); + + byte[] data = writeBuffer.getData(); + + int tmp = 0; + int crcL, crcR; + + for (int newByte : data) { + crcL = tmp >> 8; + crcR = tmp & 0xFF; + tmp = (crcL << 8) + (newByte ^ crcR); + for (int j=0; j<8; j++) + if (tmp % 2 == 1) { // check if LSB shifted out is 1 or 0 + tmp = tmp >> 1; + tmp = tmp ^ 0xA001; + } else { + tmp = tmp >> 1; + } + } + + return (short) tmp; + } catch (ParseException e) { + throw new RuntimeException("Something wen't wrong during the CRC check", e); + } + } else if(messageFrame.getCommand() instanceof DF1UnprotectedReadResponse) { + DF1UnprotectedReadResponse unprotectedReadResponseCommand = (DF1UnprotectedReadResponse) messageFrame.getCommand(); + try { + WriteBuffer writeBuffer = new WriteBuffer(10, false); + writeBuffer.writeUnsignedShort(8, destinationAddress); + writeBuffer.writeUnsignedShort(8, sourceAddress); + writeBuffer.writeUnsignedShort(8, commandDiscriminatorValues); + writeBuffer.writeUnsignedShort(8, status); + writeBuffer.writeUnsignedInt(16, (short) counter); + for (short data : unprotectedReadResponseCommand.getData()) { + writeBuffer.writeUnsignedShort(8, data); + } + writeBuffer.writeUnsignedShort(8, (byte) 0x03); + + byte[] data = writeBuffer.getData(); + + int tmp = 0; + int crcL, crcR; + + for (int newByte : data) { + crcL = tmp >> 8; + crcR = tmp & 0xFF; + tmp = (crcL << 8) + (newByte ^ crcR); + for (int j=0; j<8; j++) + if (tmp % 2 == 1) { // check if LSB shifted out is 1 or 0 + tmp = tmp >> 1; + tmp = tmp ^ 0xA001; + } else { + tmp = tmp >> 1; + } + } + + return (short) tmp; + } catch (ParseException e) { + throw new RuntimeException("Something wen't wrong during the CRC check", e); + } + } + } + + return 0; + } + + public static boolean dataTerminate(ReadBuffer io) { + try { + if ((io.peekByte(0) == (byte) 0x10) && (io.peekByte(1) == (byte) 0x03)) { + return true; + } + } catch (ParseException e) { + // Just ignore and return false. + } + return false; + } + +} diff --git a/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/protocol/df1/DF1Utils.java b/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/protocol/df1/DF1Utils.java deleted file mode 100644 index a85ab8f..0000000 --- a/sandbox/test-java-df1-driver/src/main/java/org/apache/plc4x/protocol/df1/DF1Utils.java +++ /dev/null @@ -1,84 +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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.plc4x.protocol.df1; - -import org.apache.plc4x.java.df1.DF1ReadRequest; -import org.apache.plc4x.java.df1.DF1Symbol; -import org.apache.plc4x.java.df1.DF1SymbolMessageFrameStart; -import org.apache.plc4x.java.utils.ParseException; -import org.apache.plc4x.java.utils.WriteBuffer; - -import java.nio.ByteBuffer; - -public class DF1Utils { - - public static short CRCCheck(Object... args) { - DF1Symbol symbol = (DF1Symbol) args[1]; - short messageType = (short) args[0]; - if(symbol instanceof DF1SymbolMessageFrameStart) { - DF1SymbolMessageFrameStart messageFrameStart = (DF1SymbolMessageFrameStart) symbol; - - short destinationAddress = messageFrameStart.getDestinationAddress(); - short sourceAddress = messageFrameStart.getSourceAddress(); - short commandDiscriminatorValues = (short) messageFrameStart.getCommand().getDiscriminatorValues()[0]; - short status = messageFrameStart.getCommand().getStatus(); - int counter = messageFrameStart.getCommand().getTransactionCounter(); - if(messageFrameStart.getCommand() instanceof DF1ReadRequest) { - DF1ReadRequest readRequestCommand = (DF1ReadRequest) messageFrameStart.getCommand(); - - try { - WriteBuffer writeBuffer = new WriteBuffer(10, false); - writeBuffer.writeUnsignedShort(8, destinationAddress); - writeBuffer.writeUnsignedShort(8, sourceAddress); - writeBuffer.writeUnsignedShort(8, commandDiscriminatorValues); - writeBuffer.writeUnsignedShort(8, status); - writeBuffer.writeUnsignedInt(16, (short) counter); - writeBuffer.writeUnsignedInt(16, (short) readRequestCommand.getAddress()); - writeBuffer.writeUnsignedShort(8, (byte) readRequestCommand.getSize()); - writeBuffer.writeUnsignedShort(8, (byte) messageType); - - byte[] data = writeBuffer.getData(); - - int tmp = 0; - int crcL, crcR; - - for (int newByte : data) { - crcL = tmp >> 8; - crcR = tmp & 0xFF; - tmp = (crcL << 8) + (newByte ^ crcR); - for (int j=0; j<8; j++) - if (tmp % 2 == 1) { // check if LSB shifted out is 1 or 0 - tmp = tmp >> 1; - tmp = tmp ^ 0xA001; - } else { - tmp = tmp >> 1; - } - } - - return (short) tmp; - } catch (ParseException e) { - throw new RuntimeException("Something wen't wrong during the CRC check", e); - } - } - } - - return 0; - } - -} diff --git a/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/BenchmarkGeneratedDf1.java b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/BenchmarkGeneratedDf1.java index e092cb6..27c9be2 100644 --- a/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/BenchmarkGeneratedDf1.java +++ b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/BenchmarkGeneratedDf1.java @@ -21,16 +21,13 @@ package org.apache.plc4x.protocol.df1; import com.fazecast.jSerialComm.SerialPort; import org.apache.plc4x.java.df1.*; -import org.apache.plc4x.java.df1.io.ReadRequestIO; -import org.apache.plc4x.java.df1.io.ReadResponseIO; -import org.apache.plc4x.java.df1.io.ResultIO; import org.apache.plc4x.java.utils.ReadBuffer; import org.apache.plc4x.java.utils.WriteBuffer; public class BenchmarkGeneratedDf1 { public static void main(String[] args) throws Exception { - // Manually build a message + /*// Manually build a message ReadRequest readRequest = new ReadRequest(new DF1SymbolMessageFrameStart((short) 0x09, (short) 0x00, new DF1ReadRequest((short) 0x00, 0x01, 0x0B, (short) 0x02)), new DF1SymbolMessageFrameEnd()); // Serialize the message @@ -78,7 +75,7 @@ public class BenchmarkGeneratedDf1 { System.out.println("Didn't get an ACK"); } - comPort.closePort(); + comPort.closePort();*/ } } diff --git a/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/BenchmarkManualDf1.java b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/BenchmarkManualDf1.java index c2571fe..f832798 100644 --- a/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/BenchmarkManualDf1.java +++ b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/BenchmarkManualDf1.java @@ -22,7 +22,6 @@ package org.apache.plc4x.protocol.df1; import com.fazecast.jSerialComm.SerialPort; import org.apache.plc4x.java.df1.DF1Command; import org.apache.plc4x.java.df1.DF1Symbol; -import org.apache.plc4x.java.df1.DF1SymbolMessageFrameStart; import org.apache.plc4x.java.df1.io.DF1SymbolIO; import org.apache.plc4x.java.utils.ReadBuffer; diff --git a/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/Df1Test.java b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/Df1Test.java new file mode 100644 index 0000000..bcadf1a --- /dev/null +++ b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/Df1Test.java @@ -0,0 +1,30 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +package org.apache.plc4x.protocol.df1; + +import org.apache.plc4x.protocol.test.ProtocolTestsuiteRunner; + +public class Df1Test extends ProtocolTestsuiteRunner { + + public Df1Test() { + super("/testsuite/Df1Testsuite.xml"); + } + +} diff --git a/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/EndToEndTest.java b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/EndToEndTest.java index 954d092..e35fcce 100644 --- a/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/EndToEndTest.java +++ b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/EndToEndTest.java @@ -24,6 +24,7 @@ import org.apache.plc4x.java.api.PlcConnection; import org.apache.plc4x.java.api.exceptions.PlcConnectionException; import org.apache.plc4x.java.api.messages.PlcReadRequest; import org.apache.plc4x.java.api.messages.PlcReadResponse; +import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; @@ -35,7 +36,7 @@ import java.util.concurrent.TimeUnit; */ public class EndToEndTest { - @org.junit.Test + @Test public void helloDf1() { try (PlcConnection plcConnection = new PlcDriverManager().getConnection("df1:serial///dev/cu.usbserial-AL065SUZ")) { PlcReadRequest request = plcConnection.readRequestBuilder() diff --git a/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/IOTest.java b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/IOTest.java new file mode 100644 index 0000000..18543cc --- /dev/null +++ b/sandbox/test-java-df1-driver/src/test/java/org/apache/plc4x/protocol/df1/IOTest.java @@ -0,0 +1,99 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +package org.apache.plc4x.protocol.df1; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import org.apache.commons.codec.binary.Hex; +import org.apache.plc4x.java.df1.DF1Symbol; +import org.apache.plc4x.java.df1.io.DF1SymbolIO; +import org.apache.plc4x.java.utils.ReadBuffer; +import org.junit.jupiter.api.Test; + +public class IOTest { + + @Test + public void testXml() throws Exception { + byte[] rData = Hex.decodeHex("10020A0941000100FFFF1003473D"); + ObjectMapper mapper = new XmlMapper().enableDefaultTyping(); + ReadBuffer rBuf = new ReadBuffer(rData); + DF1Symbol symbol = new DF1SymbolIO().parse(rBuf); + String xml = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(symbol); + System.out.println(xml); + DF1Symbol symbol2 = mapper.readValue(xml, DF1Symbol.class); + System.out.println(symbol2); + } + + @Test + public void testJson() throws Exception { + byte[] rData = Hex.decodeHex("10020900010001001100021003ABE2"); + ObjectMapper mapper = new ObjectMapper().enableDefaultTyping(); + ReadBuffer rBuf = new ReadBuffer(rData); + DF1Symbol symbol = new DF1SymbolIO().parse(rBuf); + String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(symbol); + System.out.println(json); + DF1Symbol symbol2 = mapper.readValue(json, DF1Symbol.class); + System.out.println(symbol2); + } + + /*@Test + public void testParser() throws Exception { + byte[] rData = Hex.decodeHex("0610020500180801c0a82a46c4090801c0a82a46c40a0203"); + long start = System.currentTimeMillis(); + int numRunsParse = 20000; + + KNXNetIPMessageIO knxNetIPMessageIO = new KNXNetIPMessageIO(); + + // Benchmark the parsing code + KNXNetIPMessage packet = null; + for(int i = 0; i < numRunsParse; i++) { + ReadBuffer rBuf = new ReadBuffer(rData); + packet = knxNetIPMessageIO.parse(rBuf); + } + long endParsing = System.currentTimeMillis(); + + System.out.println("Parsed " + numRunsParse + " packets in " + (endParsing - start) + "ms"); + System.out.println("That's " + ((float) (endParsing - start) / numRunsParse) + "ms per packet"); + + // Benchmark the serializing code + int numRunsSerialize = 20000; + byte[] oData = null; + for(int i = 0; i < numRunsSerialize; i++) { + WriteBuffer wBuf = new WriteBuffer(packet.getLengthInBytes()); + knxNetIPMessageIO.serialize(wBuf, packet); + oData = wBuf.getData(); + } + long endSerializing = System.currentTimeMillis(); + + System.out.println("Serialized " + numRunsSerialize + " packets in " + (endSerializing - endParsing) + "ms"); + System.out.println("That's " + ((float) (endSerializing - endParsing) / numRunsSerialize) + "ms per packet"); + if(!Arrays.equals(rData, oData)) { + for(int i = 0; i < rData.length; i++) { + if(rData[i] != oData[i]) { + System.out.println("Difference in byte " + i); + } + } + System.out.println("Not equals"); + } else { + System.out.println("Bytes equal"); + } + }*/ + +} 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 new file mode 100644 index 0000000..b0691ef --- /dev/null +++ b/sandbox/test-java-df1-driver/src/test/resources/testsuite/Df1Testsuite.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + --> +<test:testsuite xmlns:test="https://plc4x.apache.org/schemas/testsuite.xsd" + bigEndian="true"> + + <name>Allen-Bradley DF1</name> + + <testcase> + <name>Unprotected Read Address Request</name> + <raw>10020900010001001100021003546F</raw> + <root-type>DF1Symbol</root-type> + <xml> + <DF1SymbolMessageFrame className="org.apache.plc4x.java.df1.DF1SymbolMessageFrame"> + <destinationAddress>9</destinationAddress> + <sourceAddress>0</sourceAddress> + <command className="org.apache.plc4x.java.df1.DF1UnprotectedReadRequest"> + <status>0</status> + <transactionCounter>1</transactionCounter> + <address>17</address> + <size>2</size> + </command> + </DF1SymbolMessageFrame> + </xml> + </testcase> + + <testcase> + <name>Unprotected Read Address Response</name> + <raw>10020A0941000100FFFF1003DFB9</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>255</data> + <data>255</data> + </data> + </command> + </DF1SymbolMessageFrame> + </xml> + </testcase> + + <testcase> + <name>ACK Response</name> + <raw>1006</raw> + <root-type>DF1Symbol</root-type> + <xml> + <DF1SymbolMessageFrameACK className="org.apache.plc4x.java.df1.DF1SymbolMessageFrameACK"/> + </xml> + </testcase> + + <testcase> + <name>NACK Response</name> + <raw>1015</raw> + <root-type>DF1Symbol</root-type> + <xml> + <DF1SymbolMessageFrameNAK className="org.apache.plc4x.java.df1.DF1SymbolMessageFrameNAK"/> + </xml> + </testcase> + +</test:testsuite> \ No newline at end of file
