From cf534c73594b6733341a2e80fa141889d8a52d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Dywicki?= Date: Fri, 10 Apr 2020 13:20:11 +0200 Subject: [PATCH 1/3] PLC4X-192 Support for connection string parameter conversion. --- .../configuration/ConfigurationFactory.java | 19 +++++++- .../ConfigurationParameterConverter.java | 44 +++++++++++++++++++ .../annotations/ParameterConverter.java | 42 ++++++++++++++++++ .../configuration/AdsConfiguration.java | 26 +++++++++++ 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/ConfigurationParameterConverter.java create mode 100644 plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/annotations/ParameterConverter.java diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/ConfigurationFactory.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/ConfigurationFactory.java index 832b9db98bf..088cd1fe899 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/ConfigurationFactory.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/ConfigurationFactory.java @@ -170,10 +170,25 @@ private static String getConfigurationName(Field field) { * @return parsed value of the field in the type the field requires */ private static Object toFieldValue(Field field, String valueString) { - if(field == null) { + if (field == null) { throw new IllegalArgumentException("Field not defined"); } - if(field.getType() == String.class) { + + if (field.getAnnotation(ParameterConverter.class) != null) { + Class> converterClass = field.getAnnotation(ParameterConverter.class).value(); + + try { + ConfigurationParameterConverter converter = converterClass.getDeclaredConstructor().newInstance(); + if (converter.getType().isAssignableFrom(field.getType())) { + return converter.convert(valueString); + } + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new IllegalArgumentException("Could not initialize parameter converter", e); + } + throw new IllegalArgumentException("Unsupported field type " + field.getType() + " for converter " + converterClass); + } + + if (field.getType() == String.class) { return valueString; } if ((field.getType() == boolean.class) || (field.getType() == Boolean.class)) { diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/ConfigurationParameterConverter.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/ConfigurationParameterConverter.java new file mode 100644 index 00000000000..5080c4dc1dd --- /dev/null +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/ConfigurationParameterConverter.java @@ -0,0 +1,44 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +*/ +package org.apache.plc4x.java.spi.configuration; + +/** + * Interface which allows to convert parameter from URI into its complex form. + */ +public interface ConfigurationParameterConverter { + + /** + * Type of supported configuration parameter. + * + * Returned value determines Java type to which this converter is able to turn string representation. Only if field + * type is assignable to returned type conversion attempt will be made. + * + * @return Java type constructed by converter. + */ + Class getType(); + + /** + * Executes conversion of parameter textual representation into java object. + * + * @param value Parameter value. + * @return Object representing passed string value. + */ + T convert(String value); + +} diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/annotations/ParameterConverter.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/annotations/ParameterConverter.java new file mode 100644 index 00000000000..733eaeb15eb --- /dev/null +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/annotations/ParameterConverter.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.spi.configuration.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.apache.plc4x.java.spi.configuration.ConfigurationParameterConverter; + +/** + * Helper annotation to customize handling of configuration parameter conversion. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ParameterConverter { + + /** + * Type which should be used for conversion of text value to object representation. + * + * @return Class implementing necessary conversion logic. + */ + Class> value(); + +} diff --git a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/configuration/AdsConfiguration.java b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/configuration/AdsConfiguration.java index e34ff6b5e11..606bc24d3d4 100644 --- a/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/configuration/AdsConfiguration.java +++ b/sandbox/test-java-amsads-driver/src/main/java/org/apache/plc4x/java/amsads/configuration/AdsConfiguration.java @@ -21,17 +21,31 @@ Licensed to the Apache Software Foundation (ASF) under one import org.apache.plc4x.java.amsads.AMSADSPlcDriver; import org.apache.plc4x.java.amsads.readwrite.AmsNetId; import org.apache.plc4x.java.spi.configuration.Configuration; +import org.apache.plc4x.java.spi.configuration.ConfigurationParameterConverter; +import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter; +import org.apache.plc4x.java.spi.configuration.annotations.ParameterConverter; +import org.apache.plc4x.java.spi.configuration.annotations.Required; import org.apache.plc4x.java.transport.serial.SerialTransportConfiguration; import org.apache.plc4x.java.transport.tcp.TcpTransportConfiguration; public class AdsConfiguration implements Configuration, TcpTransportConfiguration, SerialTransportConfiguration { + @Required + @ConfigurationParameter + @ParameterConverter(AmsNetIdConverter.class) protected AmsNetId targetAmsNetId; + @Required + @ConfigurationParameter protected int targetAmsPort; + @Required + @ConfigurationParameter + @ParameterConverter(AmsNetIdConverter.class) protected AmsNetId sourceAmsNetId; + @Required + @ConfigurationParameter protected int sourceAmsPort; public AmsNetId getTargetAmsNetId() { @@ -71,4 +85,16 @@ public int getDefaultPort() { return AMSADSPlcDriver.TCP_PORT; } + public static class AmsNetIdConverter implements ConfigurationParameterConverter { + + @Override + public Class getType() { + return AmsNetId.class; + } + + @Override + public AmsNetId convert(String value) { + return AMSADSPlcDriver.AmsNetIdOf(value); + } + } } From bc2bbdf7082672c5190401f208422422a8423ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Dywicki?= Date: Sat, 11 Apr 2020 01:51:53 +0200 Subject: [PATCH 2/3] PLC4x-193 Support for little endian fields. --- .../java/JavaLanguageTemplateHelper.java | 30 ++++---- .../templates/java/data-io-template.ftlh | 2 + .../resources/templates/java/io-template.ftlh | 4 +- .../codegenerator/language/mspec/MSpec.g4 | 8 ++- .../DefaultIntegerTypeReference.java | 10 ++- .../DefaultSimpleVarLengthTypeReference.java | 2 +- .../mspec/parser/MessageFormatListener.java | 10 ++- .../src/test/resources/mspec.example | 4 +- .../plc4x/java/spi/generation/ReadBuffer.java | 20 ++++++ .../java/spi/generation/WriteBuffer.java | 35 +++++++-- pom.xml | 2 +- .../resources/protocols/amsads/amsads.mspec | 8 +-- ...java => AmsNetIdSerializerParserTest.java} | 7 +- .../AmsNetIdParserSerializerTest.xml | 72 +++++++++++++++++++ 14 files changed, 181 insertions(+), 33 deletions(-) rename sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/{AmsAdsSerializerParserTest.java => AmsNetIdSerializerParserTest.java} (75%) create mode 100644 sandbox/test-java-amsads-driver/src/test/resources/testsuite/AmsNetIdParserSerializerTest.xml 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 37f7490fc01..5f33ef64746 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 @@ -242,33 +242,35 @@ public String getReadBufferReadMethodCall(SimpleTypeReference simpleTypeReferenc } case UINT: { IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference; + String endianness = integerTypeReference.isLittleEndian() ? ", true" : ""; if (integerTypeReference.getSizeInBits() <= 4) { return "io.readUnsignedByte(" + integerTypeReference.getSizeInBits() + ")"; } if (integerTypeReference.getSizeInBits() <= 8) { - return "io.readUnsignedShort(" + integerTypeReference.getSizeInBits() + ")"; + return "io.readUnsignedShort(" + integerTypeReference.getSizeInBits() + endianness + ")"; } if (integerTypeReference.getSizeInBits() <= 16) { - return "io.readUnsignedInt(" + integerTypeReference.getSizeInBits() + ")"; + return "io.readUnsignedInt(" + integerTypeReference.getSizeInBits() + endianness + ")"; } if (integerTypeReference.getSizeInBits() <= 32) { - return "io.readUnsignedLong(" + integerTypeReference.getSizeInBits() + ")"; + return "io.readUnsignedLong(" + integerTypeReference.getSizeInBits() + endianness + ")"; } return "io.readUnsignedBigInteger(" + integerTypeReference.getSizeInBits() + ")"; } case INT: { IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference; + String endianness = integerTypeReference.isLittleEndian() ? ", true" : ""; if (integerTypeReference.getSizeInBits() <= 8) { return "io.readByte(" + integerTypeReference.getSizeInBits() + ")"; } if (integerTypeReference.getSizeInBits() <= 16) { - return "io.readShort(" + integerTypeReference.getSizeInBits() + ")"; + return "io.readShort(" + integerTypeReference.getSizeInBits() + endianness + ")"; } if (integerTypeReference.getSizeInBits() <= 32) { - return "io.readInt(" + integerTypeReference.getSizeInBits() + ")"; + return "io.readInt(" + integerTypeReference.getSizeInBits() + endianness + ")"; } if (integerTypeReference.getSizeInBits() <= 64) { - return "io.readLong(" + integerTypeReference.getSizeInBits() + ")"; + return "io.readLong(" + integerTypeReference.getSizeInBits() + endianness + ")"; } return "io.readBigInteger(" + integerTypeReference.getSizeInBits() + ")"; } @@ -301,33 +303,35 @@ public String getWriteBufferReadMethodCall(SimpleTypeReference simpleTypeReferen } case UINT: { IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference; + String endianness = integerTypeReference.isLittleEndian() ? ", true" : ""; if (integerTypeReference.getSizeInBits() <= 4) { return "io.writeUnsignedByte(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").byteValue())"; } if (integerTypeReference.getSizeInBits() <= 8) { - return "io.writeUnsignedShort(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").shortValue())"; + return "io.writeUnsignedShort(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").shortValue()" + endianness + ")"; } if (integerTypeReference.getSizeInBits() <= 16) { - return "io.writeUnsignedInt(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").intValue())"; + return "io.writeUnsignedInt(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").intValue()" + endianness + ")"; } if (integerTypeReference.getSizeInBits() <= 32) { - return "io.writeUnsignedLong(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").longValue())"; + return "io.writeUnsignedLong(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").longValue()" + endianness + ")"; } return "io.writeUnsignedBigInteger(" + integerTypeReference.getSizeInBits() + ", (BigInteger) " + fieldName + ")"; } case INT: { IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference; + String endianness = integerTypeReference.isLittleEndian() ? ", true" : ""; if (integerTypeReference.getSizeInBits() <= 8) { return "io.writeByte(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").byteValue())"; } if (integerTypeReference.getSizeInBits() <= 16) { - return "io.writeShort(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").shortValue())"; + return "io.writeShort(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").shortValue()" + endianness + ")"; } if (integerTypeReference.getSizeInBits() <= 32) { - return "io.writeInt(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").intValue())"; + return "io.writeInt(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").intValue()" + endianness + ")"; } if (integerTypeReference.getSizeInBits() <= 64) { - return "io.writeLong(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").longValue())"; + return "io.writeLong(" + integerTypeReference.getSizeInBits() + ", ((Number) " + fieldName + ").longValue() " + endianness + ")"; } return "io.writeBigInteger(" + integerTypeReference.getSizeInBits() + ", BigInteger.valueOf( " + fieldName + "))"; } @@ -359,6 +363,8 @@ public String getReadMethodName(SimpleTypeReference simpleTypeReference) { languageTypeName = languageTypeName.substring(0, 1).toUpperCase() + languageTypeName.substring(1); if(simpleTypeReference.getBaseType().equals(SimpleTypeReference.SimpleBaseType.UINT)) { return "readUnsigned" + languageTypeName; + } else if(simpleTypeReference.getBaseType().equals(SimpleTypeReference.SimpleBaseType.UINT)) { + return "readUnsigned" + languageTypeName + "LE"; } else { return "read" + languageTypeName; } diff --git a/build-utils/language-java/src/main/resources/templates/java/data-io-template.ftlh b/build-utils/language-java/src/main/resources/templates/java/data-io-template.ftlh index 8591e2c47b6..2da5d88b3fe 100644 --- a/build-utils/language-java/src/main/resources/templates/java/data-io-template.ftlh +++ b/build-utils/language-java/src/main/resources/templates/java/data-io-template.ftlh @@ -60,6 +60,8 @@ import java.util.function.Supplier; public class ${typeName}IO { + // data-io + private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class); public static PlcValue staticParse(ReadBuffer io<#if type.parserArguments?has_content>, <#list type.parserArguments as parserArgument>${helper.getLanguageTypeName(parserArgument.type, false)} ${parserArgument.name}<#sep>, ) throws ParseException { 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 88177bdb96b..1d8784f9b72 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 @@ -58,7 +58,9 @@ import java.util.function.Supplier; public class ${typeName}IO implements <#if outputFlavor != "passive">MessageIO<${typeName}, ${typeName}><#else>MessageInput<${typeName}> { - private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class); + // io template ${type} little endian ${type.littleEndian?c} + +private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class); <#if !helper.isDiscriminatedType(type)> @Override 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 d0a2943eeee..8c876db8025 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 @@ -138,8 +138,8 @@ caseStatement dataType : base='bit' - | base='int' size=INTEGER_LITERAL - | base='uint' size=INTEGER_LITERAL + | base='int' size=INTEGER_LITERAL (endianness=ENDIANNESS_LITERAL)? + | base='uint' size=INTEGER_LITERAL (endianness=ENDIANNESS_LITERAL)? | base='float' exponent=INTEGER_LITERAL '.' mantissa=INTEGER_LITERAL | base='ufloat' exponent=INTEGER_LITERAL '.' mantissa=INTEGER_LITERAL /* For the following types the parsing/serialization has to be handled manually */ @@ -152,6 +152,10 @@ dataType | base='dateTime' ; +ENDIANNESS_LITERAL + : 'big endian' | 'little endian' + ; + argument : type=typeReference name=idExpression ; diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/references/DefaultIntegerTypeReference.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/references/DefaultIntegerTypeReference.java index 793cc0eae11..58a810af5e0 100644 --- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/references/DefaultIntegerTypeReference.java +++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/references/DefaultIntegerTypeReference.java @@ -23,8 +23,16 @@ Licensed to the Apache Software Foundation (ASF) under one public class DefaultIntegerTypeReference extends DefaultSimpleTypeReference implements IntegerTypeReference { - public DefaultIntegerTypeReference(SimpleBaseType baseType, int sizeInBits) { + private final boolean littleEndian; + + public DefaultIntegerTypeReference(SimpleBaseType baseType, int sizeInBits, boolean littleEndian) { super(baseType, sizeInBits); + this.littleEndian = littleEndian; + } + + @Override + public boolean isLittleEndian() { + return littleEndian; } } diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/references/DefaultSimpleVarLengthTypeReference.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/references/DefaultSimpleVarLengthTypeReference.java index f4febf59aa9..d47e20eb916 100644 --- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/references/DefaultSimpleVarLengthTypeReference.java +++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/references/DefaultSimpleVarLengthTypeReference.java @@ -24,7 +24,7 @@ Licensed to the Apache Software Foundation (ASF) under one public class DefaultSimpleVarLengthTypeReference extends DefaultIntegerTypeReference implements SimpleVarLengthTypeReference { public DefaultSimpleVarLengthTypeReference(SimpleBaseType baseType) { - super(baseType, -1); + super(baseType, -1, false); } } 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 1608bcb8a93..8c553388f30 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 @@ -447,7 +447,8 @@ private SimpleTypeReference getSimpleTypeReference(MSpecParser.DataTypeContext c // If a size it specified its a simple integer length based type. if (ctx.size != null) { int size = Integer.parseInt(ctx.size.getText()); - return new DefaultIntegerTypeReference(simpleBaseType, size); + boolean littleEndian = isLittleEndian(() -> ctx.endianness); + return new DefaultIntegerTypeReference(simpleBaseType, size, littleEndian); } // If exponent and mantissa are present, it's a floating point representation. else if((ctx.exponent != null) && (ctx.mantissa != null)) { @@ -462,7 +463,7 @@ else if((simpleBaseType == SimpleTypeReference.SimpleBaseType.TIME) || } // In all other cases (bit) it's just assume it's length it 1. else { - return new DefaultIntegerTypeReference(simpleBaseType, 1); + return new DefaultIntegerTypeReference(simpleBaseType, 1, false); } } @@ -517,4 +518,9 @@ private String unquoteString(String quotedString) { return quotedString; } + private boolean isLittleEndian(Supplier endianness) { + Token token = endianness.get(); + return token != null && token.getText().equalsIgnoreCase("little endian"); + } + } diff --git a/build-utils/protocol-base-mspec/src/test/resources/mspec.example b/build-utils/protocol-base-mspec/src/test/resources/mspec.example index 9cb94d8325e..82c7de04540 100644 --- a/build-utils/protocol-base-mspec/src/test/resources/mspec.example +++ b/build-utils/protocol-base-mspec/src/test/resources/mspec.example @@ -132,11 +132,11 @@ // This is the AmsNetId of the station, for which the packet is intended. Remarks see below. [simple AmsNetId 'targetAmsNetId' ] // This is the AmsPort of the station, for which the packet is intended. - [simple uint 16 'targetAmsPort' ] + [simple uint 16 little endian 'targetAmsPort' ] // This contains the AmsNetId of the station, from which the packet was sent. [simple AmsNetId 'sourceAmsNetId' ] // This contains the AmsPort of the station, from which the packet was sent. - [simple uint 16 'sourceAmsPort' ] + [simple uint 16 little endian 'sourceAmsPort' ] // 2 bytes. [enum CommandId 'commandId' ] // 2 bytes. diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java index a9087f96418..2b1765295f0 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBuffer.java @@ -118,6 +118,10 @@ public short readUnsignedShort(int bitLength) throws ParseException { } public int readUnsignedInt(int bitLength) throws ParseException { + return readUnsignedInt(bitLength, littleEndian); + } + + public int readUnsignedInt(int bitLength, boolean littleEndian) throws ParseException { if (bitLength <= 0) { throw new ParseException("unsigned int must contain at least 1 bit"); } @@ -135,6 +139,10 @@ public int readUnsignedInt(int bitLength) throws ParseException { } public long readUnsignedLong(int bitLength) throws ParseException { + return readUnsignedLong(bitLength, littleEndian); + } + + public long readUnsignedLong(int bitLength, boolean littleEndian) throws ParseException { if (bitLength <= 0) { throw new ParseException("unsigned long must contain at least 1 bit"); } @@ -170,6 +178,10 @@ public byte readByte(int bitLength) throws ParseException { } public short readShort(int bitLength) throws ParseException { + return readShort(bitLength, littleEndian); + } + + public short readShort(int bitLength, boolean littleEndian) throws ParseException { if (bitLength <= 0) { throw new ParseException("short must contain at least 1 bit"); } @@ -187,6 +199,10 @@ public short readShort(int bitLength) throws ParseException { } public int readInt(int bitLength) throws ParseException { + return readInt(bitLength, littleEndian); + } + + public int readInt(int bitLength, boolean littleEndian) throws ParseException { if (bitLength <= 0) { throw new ParseException("int must contain at least 1 bit"); } @@ -204,6 +220,10 @@ public int readInt(int bitLength) throws ParseException { } public long readLong(int bitLength) throws ParseException { + return readLong(bitLength, littleEndian); + } + + public long readLong(int bitLength, boolean littleEndian) throws ParseException { if (bitLength <= 0) { throw new ParseException("long must contain at least 1 bit"); } diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBuffer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBuffer.java index 53d5fc5e6fd..7237175dc4e 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBuffer.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBuffer.java @@ -84,6 +84,10 @@ public void writeUnsignedByte(int bitLength, byte value) throws ParseException { } public void writeUnsignedShort(int bitLength, short value) throws ParseException { + writeUnsignedShort(bitLength, value, littleEndian); + } + + public void writeUnsignedShort(int bitLength, short value, boolean littleEndian) throws ParseException { if(bitLength <= 0) { throw new ParseException("unsigned short must contain at least 1 bit"); } @@ -91,6 +95,9 @@ public void writeUnsignedShort(int bitLength, short value) throws ParseException throw new ParseException("unsigned short can only contain max 8 bits"); } try { + if (littleEndian) { + value = Short.reverseBytes(value); + } bo.writeShort(true, bitLength, value); } catch (IOException e) { throw new ParseException("Error reading", e); @@ -98,6 +105,10 @@ public void writeUnsignedShort(int bitLength, short value) throws ParseException } public void writeUnsignedInt(int bitLength, int value) throws ParseException { + writeUnsignedInt(bitLength, value, littleEndian); + } + + public void writeUnsignedInt(int bitLength, int value, boolean littleEndian) throws ParseException { if(bitLength <= 0) { throw new ParseException("unsigned int must contain at least 1 bit"); } @@ -105,7 +116,7 @@ public void writeUnsignedInt(int bitLength, int value) throws ParseException { throw new ParseException("unsigned int can only contain max 16 bits"); } try { - if(littleEndian) { + if (littleEndian) { value = Integer.reverseBytes(value) >> 16; } bo.writeInt(true, bitLength, value); @@ -115,6 +126,10 @@ public void writeUnsignedInt(int bitLength, int value) throws ParseException { } public void writeUnsignedLong(int bitLength, long value) throws ParseException { + writeUnsignedLong(bitLength, value, littleEndian); + } + + public void writeUnsignedLong(int bitLength, long value, boolean littleEndian) throws ParseException { if(bitLength <= 0) { throw new ParseException("unsigned long must contain at least 1 bit"); } @@ -122,7 +137,7 @@ public void writeUnsignedLong(int bitLength, long value) throws ParseException { throw new ParseException("unsigned long can only contain max 32 bits"); } try { - if(littleEndian) { + if (littleEndian) { value = Long.reverseBytes(value) >> 32; } bo.writeLong(true, bitLength, value); @@ -150,6 +165,10 @@ public void writeByte(int bitLength, byte value) throws ParseException { } public void writeShort(int bitLength, short value) throws ParseException { + writeShort(bitLength, value, littleEndian); + } + + public void writeShort(int bitLength, short value, boolean littleEndian) throws ParseException { if(bitLength <= 0) { throw new ParseException("short must contain at least 1 bit"); } @@ -157,7 +176,7 @@ public void writeShort(int bitLength, short value) throws ParseException { throw new ParseException("short can only contain max 16 bits"); } try { - if(littleEndian) { + if (littleEndian) { value = Short.reverseBytes(value); } bo.writeShort(false, bitLength, value); @@ -167,6 +186,10 @@ public void writeShort(int bitLength, short value) throws ParseException { } public void writeInt(int bitLength, int value) throws ParseException { + writeInt(bitLength, value, littleEndian); + } + + public void writeInt(int bitLength, int value, boolean littleEndian) throws ParseException { if(bitLength <= 0) { throw new ParseException("int must contain at least 1 bit"); } @@ -184,6 +207,10 @@ public void writeInt(int bitLength, int value) throws ParseException { } public void writeLong(int bitLength, long value) throws ParseException { + writeLong(bitLength, value, littleEndian); + } + + public void writeLong(int bitLength, long value, boolean littleEndian) throws ParseException { if(bitLength <= 0) { throw new ParseException("long must contain at least 1 bit"); } @@ -191,7 +218,7 @@ public void writeLong(int bitLength, long value) throws ParseException { throw new ParseException("long can only contain max 64 bits"); } try { - if(littleEndian) { + if (littleEndian) { value = Long.reverseBytes(value); } bo.writeLong(false, bitLength, value); diff --git a/pom.xml b/pom.xml index bd94126172b..ef9c5024dfc 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ **/generated-sources - 1.2.0 + 1.3.0-SNAPSHOT 4.7.2 1.1.0 diff --git a/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec b/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec index 7db8ca7d581..999224c05e3 100644 --- a/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec +++ b/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec @@ -126,17 +126,17 @@ // This is the AmsNetId of the station, for which the packet is intended. Remarks see below. [simple AmsNetId 'targetAmsNetId' ] // This is the AmsPort of the station, for which the packet is intended. - [simple uint 16 'targetAmsPort' ] + [simple uint 16 little endian 'targetAmsPort' ] // This contains the AmsNetId of the station, from which the packet was sent. [simple AmsNetId 'sourceAmsNetId' ] // This contains the AmsPort of the station, from which the packet was sent. - [simple uint 16 'sourceAmsPort' ] + [simple uint 16 little endian 'sourceAmsPort' ] // 2 bytes. [enum CommandId 'commandId' ] // 2 bytes. [simple State 'state' ] // 4 bytes Size of the data range. The unit is byte. - [simple uint 32 'length' ] + [simple uint 32 little endian 'length' ] // 4 bytes AMS error number. See ADS Return Codes. [simple uint 32 'errorCode' ] // free usable field of 4 bytes @@ -144,7 +144,7 @@ [simple uint 32 'invokeId' ] ] -[enum uint 16 'CommandId' +[enum uint 16 little endian 'CommandId' ['0x00' INVALID] ['0x01' ADS_READ_DEVICE_INFO] ['0x02' ADS_READ] diff --git a/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsAdsSerializerParserTest.java b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsNetIdSerializerParserTest.java similarity index 75% rename from sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsAdsSerializerParserTest.java rename to sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsNetIdSerializerParserTest.java index cf76143ff4d..8febe59a0c4 100644 --- a/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsAdsSerializerParserTest.java +++ b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsNetIdSerializerParserTest.java @@ -19,11 +19,12 @@ Licensed to the Apache Software Foundation (ASF) under one package org.apache.plc4x.protocol.amsads; +import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner; -public class AmsAdsSerializerParserTest /*extends ProtocolTestsuiteRunner*/ { +public class AmsNetIdSerializerParserTest extends ParserSerializerTestsuiteRunner { - public AmsAdsSerializerParserTest() { - //super("/testsuite/Df1Testsuite.xml"); + public AmsNetIdSerializerParserTest() { + super("/testsuite/AmsNetIdParserSerializerTest.xml"); } } diff --git a/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AmsNetIdParserSerializerTest.xml b/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AmsNetIdParserSerializerTest.xml new file mode 100644 index 00000000000..87548d3779f --- /dev/null +++ b/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AmsNetIdParserSerializerTest.xml @@ -0,0 +1,72 @@ + + + + + AmsNetId + + + Test parsing of ams id 0.0.0.0.1.1 + 000000000101 + AmsNetId + + + 0 + 0 + 0 + 0 + 1 + 1 + + + + + + Test parsing of ams id 10.10.2.232.1.1 + 0a0a02e80101 + AmsNetId + + + 10 + 10 + 2 + 232 + 1 + 1 + + + + + + Test parsing of ams id 10.10.10.10.10.10 + 0a0a0a0a0a0a + AmsNetId + + + 10 + 10 + 10 + 10 + 10 + 10 + + + + + \ No newline at end of file From 4040ba9b1864e05438e1827a173813857f2d3d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Dywicki?= Date: Tue, 14 Apr 2020 01:56:55 +0200 Subject: [PATCH 3/3] PLC4x-193 Support for little endian types. Little endian type is a type which is read slightly different than big endian. Field reading and writing for integer types is reordered to match full bytes. --- .../resources/templates/java/io-template.ftlh | 13 ++-- .../codegenerator/language/mspec/MSpec.g4 | 8 +-- .../DefaultComplexTypeDefinition.java | 43 +++++++++++- .../DefaultDataIoTypeDefinition.java | 4 +- ...ultDiscriminatedComplexTypeDefinition.java | 4 +- .../DefaultEnumTypeDefinition.java | 4 +- .../definitions/DefaultTypeDefinition.java | 8 ++- .../mspec/parser/MessageFormatListener.java | 25 ++++--- .../resources/protocols/amsads/amsads.mspec | 10 +-- .../amsads/AmsHeaderSerializerParserTest.java | 33 +++++++++ .../amsads/StateSerializerParserTest.java | 30 +++++++++ .../AmsHeaderParserSerializerTest.xml | 67 +++++++++++++++++++ .../testsuite/StateParserSerializerTest.xml | 62 +++++++++++++++++ 13 files changed, 278 insertions(+), 33 deletions(-) create mode 100644 sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsHeaderSerializerParserTest.java create mode 100644 sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/StateSerializerParserTest.java create mode 100644 sandbox/test-java-amsads-driver/src/test/resources/testsuite/AmsHeaderParserSerializerTest.xml create mode 100644 sandbox/test-java-amsads-driver/src/test/resources/testsuite/StateParserSerializerTest.xml 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 1d8784f9b72..7aedf5692c3 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 @@ -58,9 +58,7 @@ import java.util.function.Supplier; public class ${typeName}IO implements <#if outputFlavor != "passive">MessageIO<${typeName}, ${typeName}><#else>MessageInput<${typeName}> { - // io template ${type} little endian ${type.littleEndian?c} - -private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class); + private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class); <#if !helper.isDiscriminatedType(type)> @Override @@ -114,7 +112,10 @@ private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class public static ${typeName}<#if helper.isDiscriminatedType(type)>Builder staticParse(ReadBuffer io<#if type.parserArguments?has_content>, <#list type.parserArguments as parserArgument>${helper.getLanguageTypeName(parserArgument.type, false)} ${parserArgument.name}<#sep>, ) throws ParseException { int startPos = io.getPos(); int curPos; -<#list type.fields as field> + + <#if type.littleEndian>// this type is declared as little endian, field order have been changed to match full bytes in big endian order. + +<#list type.fieldsInOrder as field> <#switch field.typeName> <#case "array"> @@ -352,7 +353,9 @@ private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class public static void staticSerialize(WriteBuffer io, ${typeName} _value<#if helper.getSerializerArguments(type.parserArguments)?has_content>, <#list helper.getSerializerArguments(type.parserArguments) as parserArgument>${helper.getLanguageTypeName(parserArgument.type, false)} ${parserArgument.name}<#sep>, ) throws ParseException { int startPos = io.getPos(); -<#list type.fields as field> + <#if type.littleEndian>// this type is declared as little endian, field order have been changed to match full bytes in big endian order. + +<#list type.fieldsInOrder as field> <#switch field.typeName> <#case "array"> 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 8c876db8025..7f2e7cff573 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 @@ -27,10 +27,10 @@ complexTypeDefinition ; complexType - : 'type' name=idExpression (LBRACKET params=argumentList RBRACKET)? fieldDefinition* - | 'discriminatedType' name=idExpression (LBRACKET params=argumentList RBRACKET)? fieldDefinition+ - | 'enum' type=typeReference name=idExpression (LBRACKET params=argumentList RBRACKET)? enumValues=enumValueDefinition+ - | 'dataIo' name=idExpression (LBRACKET params=argumentList RBRACKET)? dataIoTypeSwitch=dataIoDefinition + : 'type' (endianness=ENDIANNESS_LITERAL)? name=idExpression (LBRACKET params=argumentList RBRACKET)? fieldDefinition* + | 'discriminatedType' (endianness=ENDIANNESS_LITERAL)? name=idExpression (LBRACKET params=argumentList RBRACKET)? fieldDefinition+ + | 'enum' (endianness=ENDIANNESS_LITERAL)? type=typeReference name=idExpression (LBRACKET params=argumentList RBRACKET)? enumValues=enumValueDefinition+ + | 'dataIo' (endianness=ENDIANNESS_LITERAL)? name=idExpression (LBRACKET params=argumentList RBRACKET)? dataIoTypeSwitch=dataIoDefinition ; fieldDefinition diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultComplexTypeDefinition.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultComplexTypeDefinition.java index 39d87f4bb1c..c34a9151163 100644 --- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultComplexTypeDefinition.java +++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultComplexTypeDefinition.java @@ -22,10 +22,13 @@ Licensed to the Apache Software Foundation (ASF) under one import org.apache.plc4x.plugins.codegenerator.types.definitions.Argument; import org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition; import org.apache.plc4x.plugins.codegenerator.types.fields.*; +import org.apache.plc4x.plugins.codegenerator.types.references.IntegerTypeReference; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; public class DefaultComplexTypeDefinition extends DefaultTypeDefinition implements ComplexTypeDefinition { @@ -33,8 +36,8 @@ public class DefaultComplexTypeDefinition extends DefaultTypeDefinition implemen private final boolean isAbstract; private final List fields; - public DefaultComplexTypeDefinition(String name, Argument[] parserArguments, String[] tags, boolean isAbstract, List fields) { - super(name, parserArguments, tags); + public DefaultComplexTypeDefinition(String name, Argument[] parserArguments, String[] tags, boolean littleEndian, boolean isAbstract, List fields) { + super(name, parserArguments, tags, littleEndian); this.isAbstract = isAbstract; this.fields = fields; } @@ -89,4 +92,40 @@ public List getParentPropertyFields() { return Collections.emptyList(); } + public List getFieldsInOrder() { + if (!isLittleEndian()) { + return getFields(); + } + + List ordered = new ArrayList<>(); + List group = new ArrayList<>(); + int groupSize = 0; + int length = this.fields.size(); + for (int index = length; index > 0; index--) { + Field field = fields.get(index - 1); + + if (field instanceof TypedField) { + TypedField typed = (TypedField) field; + if (typed.getType() instanceof IntegerTypeReference) { + IntegerTypeReference size = (IntegerTypeReference) typed.getType(); + group.add(field); + groupSize += size.getSizeInBits(); + + // we can have odd sizes then its best to + if ((groupSize % 8) == 0) { + Collections.reverse(group); + ordered.addAll(group); + group.clear(); + groupSize = 0; + } + } else { + ordered.add(field); + } + } else { + ordered.add(field); + } + } + + return ordered; + } } diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultDataIoTypeDefinition.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultDataIoTypeDefinition.java index 0e6e2fd9f6b..d5a2ade56c1 100644 --- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultDataIoTypeDefinition.java +++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultDataIoTypeDefinition.java @@ -26,8 +26,8 @@ public class DefaultDataIoTypeDefinition extends DefaultTypeDefinition implement private final SwitchField switchField; - public DefaultDataIoTypeDefinition(String name, Argument[] parserArguments, String[] tags, SwitchField switchField) { - super(name, parserArguments, tags); + public DefaultDataIoTypeDefinition(String name, Argument[] parserArguments, String[] tags, boolean littleEndian, SwitchField switchField) { + super(name, parserArguments, tags, littleEndian); this.switchField = switchField; } diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultDiscriminatedComplexTypeDefinition.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultDiscriminatedComplexTypeDefinition.java index 8d64b08ce1e..8d47b3ea252 100644 --- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultDiscriminatedComplexTypeDefinition.java +++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultDiscriminatedComplexTypeDefinition.java @@ -30,8 +30,8 @@ public class DefaultDiscriminatedComplexTypeDefinition extends DefaultComplexTyp private final String[] discriminatorValues; - public DefaultDiscriminatedComplexTypeDefinition(String name, Argument[] parserArguments, String[] tags, String[] discriminatorValues, List fields) { - super(name, parserArguments, tags, false, fields); + public DefaultDiscriminatedComplexTypeDefinition(String name, Argument[] parserArguments, String[] tags, boolean littleEndian, String[] discriminatorValues, List fields) { + super(name, parserArguments, tags, littleEndian, false, fields); this.discriminatorValues = discriminatorValues; } diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultEnumTypeDefinition.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultEnumTypeDefinition.java index 67d0d60f84f..af9f09f2a38 100644 --- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultEnumTypeDefinition.java +++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultEnumTypeDefinition.java @@ -34,8 +34,8 @@ public class DefaultEnumTypeDefinition extends DefaultTypeDefinition implements private final Map constants; public DefaultEnumTypeDefinition(String name, TypeReference type, EnumValue[] enumValues, - Argument[] constants, String[] tags) { - super(name, constants, tags); + Argument[] constants, String[] tags, boolean littleEndian) { + super(name, constants, tags, littleEndian); this.type = type; this.enumValues = enumValues; this.constants = new HashMap<>(); diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultTypeDefinition.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultTypeDefinition.java index c4ebb227c28..6e5ee226319 100644 --- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultTypeDefinition.java +++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/model/definitions/DefaultTypeDefinition.java @@ -28,12 +28,14 @@ public abstract class DefaultTypeDefinition { private final String name; private final Argument[] parserArguments; private final String[] tags; + private final boolean littleEndian; private TypeDefinition parentType; - public DefaultTypeDefinition(String name, Argument[] parserArguments, String[] tags) { + public DefaultTypeDefinition(String name, Argument[] parserArguments, String[] tags, boolean littleEndian) { this.name = name; this.parserArguments = parserArguments; this.tags = tags; + this.littleEndian = littleEndian; this.parentType = null; } @@ -53,6 +55,10 @@ public TypeDefinition getParentType() { return parentType; } + public boolean isLittleEndian() { + return littleEndian; + } + public void setParentType(TypeDefinition parentType) { this.parentType = parentType; } 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 8c553388f30..86464624161 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 @@ -19,6 +19,7 @@ Licensed to the Apache Software Foundation (ASF) under one package org.apache.plc4x.plugins.codegenerator.language.mspec.parser; +import org.antlr.v4.runtime.Token; import org.apache.commons.io.IOUtils; import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecBaseListener; import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecParser; @@ -42,6 +43,7 @@ Licensed to the Apache Software Foundation (ASF) under one import java.io.InputStream; import java.nio.charset.Charset; import java.util.*; +import java.util.function.Supplier; public class MessageFormatListener extends MSpecBaseListener { @@ -94,7 +96,7 @@ public void exitComplexType(MSpecParser.ComplexTypeContext ctx) { TypeReference type = getTypeReference(ctx.type); EnumValue[] enumValues = getEnumValues(); DefaultEnumTypeDefinition enumType = new DefaultEnumTypeDefinition(typeName, type, enumValues, - parserArguments, null); + parserArguments, null, isLittleEndian(() -> ctx.endianness)); types.put(typeName, enumType); enumContexts.pop(); } @@ -103,7 +105,7 @@ public void exitComplexType(MSpecParser.ComplexTypeContext ctx) { else if (ctx.dataIoTypeSwitch != null) { SwitchField switchField = getSwitchField(); DefaultDataIoTypeDefinition type = new DefaultDataIoTypeDefinition( - typeName, parserArguments, null, switchField); + typeName, parserArguments, null, isLittleEndian(() -> ctx.endianness), switchField); types.put(typeName, type); // Set the parent type for all sub-types. @@ -123,7 +125,7 @@ else if (ctx.dataIoTypeSwitch != null) { SwitchField switchField = getSwitchField(); boolean abstractType = switchField != null; DefaultComplexTypeDefinition type = new DefaultComplexTypeDefinition(typeName, parserArguments, null, - abstractType, parserContexts.peek()); + isLittleEndian(() -> ctx.endianness), abstractType, parserContexts.peek()); types.put(typeName, type); // Set the parent type for all sub-types. @@ -343,19 +345,22 @@ public void enterCaseStatement(MSpecParser.CaseStatementContext ctx) { public void exitCaseStatement(MSpecParser.CaseStatementContext ctx) { String typeName = ctx.name.getText(); List parserArguments = new LinkedList<>(); + Supplier endianness = null; // Add all the arguments from the parent type. if(ctx.parent.parent.parent.parent instanceof MSpecParser.ComplexTypeContext) { - if (((MSpecParser.ComplexTypeContext) ctx.parent.parent.parent.parent).params != null) { - parserArguments.addAll(Arrays.asList(getParserArguments( - ((MSpecParser.ComplexTypeContext) ctx.parent.parent.parent.parent).params.argument()))); + MSpecParser.ComplexTypeContext parentTypeContext = (MSpecParser.ComplexTypeContext) ctx.parent.parent.parent.parent; + if (parentTypeContext.params != null) { + parserArguments.addAll(Arrays.asList(getParserArguments(parentTypeContext.params.argument()))); } + endianness = () -> parentTypeContext.endianness; } // For dataIo there is one level less to navigate. else { - if (((MSpecParser.ComplexTypeContext) ctx.parent.parent.parent).params != null) { - parserArguments.addAll(Arrays.asList(getParserArguments( - ((MSpecParser.ComplexTypeContext) ctx.parent.parent.parent).params.argument()))); + MSpecParser.ComplexTypeContext parentTypeContext = (MSpecParser.ComplexTypeContext) ctx.parent.parent.parent; + if (parentTypeContext.params != null) { + parserArguments.addAll(Arrays.asList(getParserArguments(parentTypeContext.params.argument()))); } + endianness = () -> parentTypeContext.endianness; } // Add all eventually existing local arguments. if (ctx.argumentList() != null) { @@ -374,7 +379,7 @@ public void exitCaseStatement(MSpecParser.CaseStatementContext ctx) { } DefaultDiscriminatedComplexTypeDefinition type = new DefaultDiscriminatedComplexTypeDefinition(typeName, parserArguments.toArray(new Argument[0]), null, - discriminatorValues, parserContexts.pop()); + isLittleEndian(endianness), discriminatorValues, parserContexts.pop()); // Add the type to the switch field definition. DefaultSwitchField switchField = getSwitchField(); diff --git a/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec b/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec index 999224c05e3..50229aada63 100644 --- a/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec +++ b/protocols/amsads/src/main/resources/protocols/amsads/amsads.mspec @@ -141,7 +141,7 @@ [simple uint 32 'errorCode' ] // free usable field of 4 bytes // 4 bytes Free usable 32 bit array. Usually this array serves to send an Id. This Id makes is possible to assign a received response to a request, which was sent before. - [simple uint 32 'invokeId' ] + [simple uint 32 little endian 'invokeId' ] ] [enum uint 16 little endian 'CommandId' @@ -157,7 +157,7 @@ ['0x09' ADS_READ_WRITE] ] -[type 'State' +[type little endian 'State' [simple bit 'broadcast' ] [reserved int 7 '0x0' ] [simple bit 'initCommand' ] @@ -225,11 +225,11 @@ ] ['CommandId.ADS_READ', 'false' AdsReadRequest // 4 bytes Index Group of the data which should be read. - [simple uint 32 'indexGroup'] + [simple uint 32 little endian 'indexGroup'] // 4 bytes Index Offset of the data which should be read. - [simple uint 32 'indexOffset'] + [simple uint 32 little endian 'indexOffset'] // 4 bytes Length of the data (in bytes) which should be read. - [simple uint 32 'length'] + [simple uint 32 little endian 'length'] ] ['CommandId.ADS_WRITE', 'true' AdsWriteResponse // 4 bytes ADS error number diff --git a/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsHeaderSerializerParserTest.java b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsHeaderSerializerParserTest.java new file mode 100644 index 00000000000..1fce36f7467 --- /dev/null +++ b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/AmsHeaderSerializerParserTest.java @@ -0,0 +1,33 @@ +/* + 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.amsads; + +import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; + +public class AmsHeaderSerializerParserTest extends ParserSerializerTestsuiteRunner { + + public AmsHeaderSerializerParserTest() { + super("/testsuite/AmsHeaderParserSerializerTest.xml"); + } + +} diff --git a/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/StateSerializerParserTest.java b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/StateSerializerParserTest.java new file mode 100644 index 00000000000..d9b7e348ae0 --- /dev/null +++ b/sandbox/test-java-amsads-driver/src/test/java/org/apache/plc4x/protocol/amsads/StateSerializerParserTest.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.amsads; + +import org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner; + +public class StateSerializerParserTest extends ParserSerializerTestsuiteRunner { + + public StateSerializerParserTest() { + super("/testsuite/StateParserSerializerTest.xml"); + } + +} diff --git a/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AmsHeaderParserSerializerTest.xml b/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AmsHeaderParserSerializerTest.xml new file mode 100644 index 00000000000..6e62f3e802b --- /dev/null +++ b/sandbox/test-java-amsads-driver/src/test/resources/testsuite/AmsHeaderParserSerializerTest.xml @@ -0,0 +1,67 @@ + + + + + AmsHeader + + + Header 0.0.0.0.1.1/10000, 0.0.0.0.1.1/32769, read state + 0000000001011027000000000101018004000400000000000000000009000000 + AmsHeader + + + + 0 + 0 + 0 + 0 + 1 + 1 + + 10000 + + 0 + 0 + 0 + 0 + 1 + 1 + + 32769 + ADS_READ_STATE + + false + false + false + false + false + false + true + false + false + + 0 + 0 + 9 + + + + + \ No newline at end of file diff --git a/sandbox/test-java-amsads-driver/src/test/resources/testsuite/StateParserSerializerTest.xml b/sandbox/test-java-amsads-driver/src/test/resources/testsuite/StateParserSerializerTest.xml new file mode 100644 index 00000000000..d18490c0c1e --- /dev/null +++ b/sandbox/test-java-amsads-driver/src/test/resources/testsuite/StateParserSerializerTest.xml @@ -0,0 +1,62 @@ + + + + + State + + + State ADS_COMMAND + 0400 + State + + + false + false + false + false + false + false + true + false + false + + + + + + State ADS_COMMAND RESPONSE + 0500 + State + + + false + false + false + false + false + false + true + false + true + + + + + \ No newline at end of file