You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2021/12/23 15:17:11 UTC

[plc4x] 01/02: fix: Made it possible to use enum types in arrays, made at least simple fields also support encoding feature: added support for variable length uint 32 encoding (varLenUint32)

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

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

commit c21998dde654673aec113e6a6a002d8fbf0c5334
Author: cdutz <ch...@c-ware.de>
AuthorDate: Thu Dec 23 16:14:46 2021 +0100

    fix: Made it possible to use enum types in arrays, made at least simple fields also support encoding
    feature: added support for variable length uint 32 encoding (varLenUint32)
---
 .../language/java/JavaLanguageTemplateHelper.java  | 25 ++++++++++++++++++++++
 .../templates/java/model-template.java.ftlh        | 16 ++++++++++++--
 .../plc4x/java/spi/codegen/FieldCommons.java       | 17 +++++++++++++++
 .../java/spi/codegen/fields/FieldWriterArray.java  | 12 +++++++++++
 .../spi/codegen/fields/FieldWriterFactory.java     |  4 ++++
 .../codegen/io/DataReaderSimpleUnsignedLong.java   | 10 +++++++--
 .../codegen/io/DataWriterSimpleUnsignedLong.java   | 10 +++++++--
 .../plc4x/java/spi/generation/ReadBuffer.java      | 17 +++++++++++++++
 .../plc4x/java/spi/generation/WriteBuffer.java     | 15 +++++++++++++
 9 files changed, 120 insertions(+), 6 deletions(-)

diff --git a/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java b/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
index a0a6a72..db1a478 100644
--- a/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
+++ b/code-generation/language-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
@@ -1135,6 +1135,31 @@ public class JavaLanguageTemplateHelper extends BaseFreemarkerLanguageTemplateHe
             variableLiteral.getChild().map(child -> "." + toVariableExpressionRest(field, resultType, child)).orElse(""));
     }
 
+    public String getSizeInBits(TypedField field, List<Argument> parserArguments) {
+        String encoding = null;
+        final Optional<Term> encodingOptional = field.getEncoding();
+        if (encodingOptional.isPresent()) {
+            encoding = toParseExpression(field, field.getType(), encodingOptional.get(), parserArguments);
+        }
+        if(field.getType().isSimpleTypeReference()) {
+            final SimpleTypeReference simpleTypeReference = field.getType().asSimpleTypeReference().orElseThrow();
+            if(encoding != null) {
+                if("\"varLenUint32\"".equals(encoding)) {
+                    if((simpleTypeReference.getSizeInBits() != 32) && (simpleTypeReference.getBaseType() == SimpleTypeReference.SimpleBaseType.UINT)) {
+                        throw new RuntimeException("varLenUint32 currently only supported for 'unit 32' typed fields");
+                    }
+                    String fieldName = field.asNamedField().orElseThrow().getName();
+                    return "(" + fieldName + " <= 0x7F) ? 8 : (" + fieldName + " <= 0xFF7F) ? 16 : (" + fieldName + " <= 0xFFFF7F) ? 24 : (" + fieldName + " <= 0xFFFFFF7FL) ? 32 : -1";
+                } else {
+                    throw new RuntimeException("Unsupported encoding '" + encoding + "'");
+                }
+            } else {
+                return "" + simpleTypeReference.getSizeInBits();
+            }
+        }
+        throw new RuntimeException("getSizeInBits only supported for simple types.");
+    }
+
     public String getSizeInBits(ComplexTypeDefinition complexTypeDefinition, List<Argument> parserArguments) {
         int sizeInBits = 0;
         StringBuilder sb = new StringBuilder();
diff --git a/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh b/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
index 07dc000..c2f3364 100644
--- a/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
+++ b/code-generation/language-java/src/main/resources/templates/java/model-template.java.ftlh
@@ -54,6 +54,8 @@ import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
 import org.apache.plc4x.java.api.value.*;
 import org.apache.plc4x.java.spi.codegen.io.DataWriterComplexDefault;
 import org.apache.plc4x.java.spi.codegen.io.DataWriterEnumDefault;
+import org.apache.plc4x.java.spi.codegen.WithOption;
+import org.apache.plc4x.java.spi.generation.ByteOrder;
 import org.apache.plc4x.java.spi.generation.Message;
 import org.apache.plc4x.java.spi.generation.MessageIO;
 import org.apache.plc4x.java.spi.generation.SerializationException;
@@ -191,6 +193,8 @@ public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${ty
                         writeByteArrayField("${namedField.name}", ${namedField.name}, writeByteArray(writeBuffer, 8));
                         <#elseif arrayField.type.isSimpleTypeReference()>
                         writeSimpleTypeArrayField("${namedField.name}", ${namedField.name}, ${helper.getDataWriterCall(typedField.type, namedField.name)});
+                        <#elseif helper.isEnumTypeReference(arrayField.type)>
+                        writeEnumTypeArrayField("${namedField.name}", ${namedField.name}, ${helper.getEnumDataWriterCall(typedField.type, namedField.name, "value")});
                         <#else>
                         writeComplexTypeArrayField("${namedField.name}", ${namedField.name}, writeBuffer);
                         </#if>
@@ -281,7 +285,7 @@ public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${ty
                         <#assign namedField = field.asNamedField().orElseThrow()>
 
                         // Simple Field (${namedField.name})
-                        <#if helper.isEnumTypeReference(typedField.type)>writeSimpleEnumField("${namedField.name}", "${helper.getLanguageTypeNameForField(field)}", ${namedField.name}, ${helper.getEnumDataWriterCall(typedField.type, namedField.name, "value")});<#else>writeSimpleField("${namedField.name}", ${simpleField.name}, ${helper.getDataWriterCall(typedField.type, namedField.name)});</#if>
+                        <#if helper.isEnumTypeReference(typedField.type)>writeSimpleEnumField("${namedField.name}", "${helper.getLanguageTypeNameForField(field)}", ${namedField.name}, ${helper.getEnumDataWriterCall(typedField.type, namedField.name, "value")});<#else>writeSimpleField("${namedField.name}", ${simpleField.name}, ${helper.getDataWriterCall(typedField.type, namedField.name)}${helper.getFieldOptions(typedField, parserArguments)});</#if>
                         <#break>
                     <#case "switch">
                         <#assign switchField = field.asSwitchField().orElseThrow()>
@@ -330,11 +334,19 @@ public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${ty
             <#assign complexTypeReference = arrayField.type.asComplexTypeReference().orElseThrow()>
             for(${complexTypeReference.name} element : ${arrayField.name}) {
                 boolean last = ++i >= ${arrayField.name}.size();
+                <#if helper.isEnumField(field)>
+                lengthInBits += ${helper.getEnumBaseTypeReference(arrayField.type).sizeInBits};
+                <#else>
                 lengthInBits += element.getLengthInBits();
+                </#if>
             }
         <#else>
             for(Message element : ${arrayField.name}) {
+                <#if helper.isEnumField(field)>
+                lengthInBits += ${helper.getEnumBaseTypeReference(arrayField.type).sizeInBits};
+                <#else>
                 lengthInBits += element.getLengthInBits();
+                </#if>
             }
         </#if>
         }
@@ -450,7 +462,7 @@ public<#if type.isDiscriminatedParentTypeDefinition()> abstract</#if> class ${ty
                 <#assign vstringTypeReference = simpleTypeReference.asVstringTypeReference().orElseThrow()>
         lengthInBits += ${helper.toSerializationExpression(simpleField, helper.intTypeReference, vstringTypeReference.getLengthExpression(), parserArguments)};
             <#else>
-        lengthInBits += ${simpleTypeReference.sizeInBits};
+        lengthInBits += ${helper.getSizeInBits(simpleField, parserArguments)};
             </#if>
         <#elseif helper.isEnumField(field)>
         lengthInBits += ${helper.getEnumBaseTypeReference(simpleField.type).sizeInBits};
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/FieldCommons.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/FieldCommons.java
index 5225f60..66d4ab3 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/FieldCommons.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/FieldCommons.java
@@ -75,6 +75,23 @@ public interface FieldCommons {
         }
     }
 
+    default Optional<String> extractEncoding(WithReaderArgs... readerArgs) {
+        return extractEncoding(Stream.of(readerArgs).map(WithReaderWriterArgs.class::cast).toArray(WithReaderWriterArgs[]::new));
+    }
+
+    default Optional<String> extractEncoding(WithWriterArgs... writerArgs) {
+        return extractEncoding(Stream.of(writerArgs).map(WithReaderWriterArgs.class::cast).toArray(WithReaderWriterArgs[]::new));
+    }
+
+    default Optional<String> extractEncoding(WithReaderWriterArgs... readerWriterArgs) {
+        for (WithReaderWriterArgs arg : readerWriterArgs) {
+            if (arg instanceof withOptionEncoding) {
+                return Optional.of(((withOptionEncoding) arg).encoding());
+            }
+        }
+        return Optional.empty();
+    }
+
     @FunctionalInterface
     interface RunParseWrapped<T> {
         T run() throws ParseException;
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriterArray.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriterArray.java
index d1fdce0..474bcd2 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriterArray.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriterArray.java
@@ -56,4 +56,16 @@ public class FieldWriterArray<T> implements FieldCommons {
         }, writeBuffer, extractByteOder(writerArgs).orElse(null));
     }
 
+    public void writeEnumTypeArrayField(String logicalName, List<T> values, DataWriter<T> dataWriter, WithWriterArgs... writerArgs) throws SerializationException {
+        switchSerializeByteOrderIfNecessary(() -> {
+            if (values != null) {
+                dataWriter.pushContext(logicalName, WithReaderWriterArgs.WithRenderAsList(true));
+                for (T value : values) {
+                    dataWriter.write("value", value, writerArgs);
+                }
+                dataWriter.popContext(logicalName, WithReaderWriterArgs.WithRenderAsList(true));
+            }
+        }, dataWriter, extractByteOder(writerArgs).orElse(null));
+    }
+
 }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriterFactory.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriterFactory.java
index 2fd10c2..8ca7a67 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriterFactory.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/fields/FieldWriterFactory.java
@@ -39,6 +39,10 @@ public class FieldWriterFactory {
         new FieldWriterArray<Message>().writeComplexTypeArrayField(logicalName, value, writeBuffer, writerArgs);
     }
 
+    public static <T> void writeEnumTypeArrayField(String logicalName, List<T> value, DataWriter<T> dataWriter, WithWriterArgs... writerArgs) throws SerializationException {
+        new FieldWriterArray<T>().writeEnumTypeArrayField(logicalName, value, dataWriter, writerArgs);
+    }
+
     public static <T> void writeByteArrayField(String logicalName, byte[] value, DataWriter<byte[]> dataWriter, WithWriterArgs... writerArgs) throws SerializationException {
         new FieldWriterArray<T>().writeByteArrayField(logicalName, value, dataWriter, writerArgs);
     }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedLong.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedLong.java
index d36ba98..5d42da7 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedLong.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataReaderSimpleUnsignedLong.java
@@ -18,11 +18,12 @@
  */
 package org.apache.plc4x.java.spi.codegen.io;
 
+import org.apache.plc4x.java.spi.codegen.FieldCommons;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBuffer;
 import org.apache.plc4x.java.spi.generation.WithReaderArgs;
 
-public class DataReaderSimpleUnsignedLong extends DataReaderSimpleBase<Long> {
+public class DataReaderSimpleUnsignedLong extends DataReaderSimpleBase<Long> implements FieldCommons {
 
     public DataReaderSimpleUnsignedLong(ReadBuffer readBuffer, int bitLength) {
         super(readBuffer, bitLength);
@@ -30,7 +31,12 @@ public class DataReaderSimpleUnsignedLong extends DataReaderSimpleBase<Long> {
 
     @Override
     public Long read(String logicalName, WithReaderArgs... readerArgs) throws ParseException  {
-        return readBuffer.readUnsignedLong(logicalName, bitLength, readerArgs);
+        String encoding = extractEncoding(readerArgs).orElse(null);
+        if("varLenUint32".equals(encoding)) {
+            return readBuffer.readVariableLengthUnsignedInteger(logicalName, bitLength, readerArgs);
+        } else {
+            return readBuffer.readUnsignedLong(logicalName, bitLength, readerArgs);
+        }
     }
 
 }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSimpleUnsignedLong.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSimpleUnsignedLong.java
index 7d3e1e6..cbc917c 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSimpleUnsignedLong.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/codegen/io/DataWriterSimpleUnsignedLong.java
@@ -18,11 +18,12 @@
  */
 package org.apache.plc4x.java.spi.codegen.io;
 
+import org.apache.plc4x.java.spi.codegen.FieldCommons;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WithWriterArgs;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
-public class DataWriterSimpleUnsignedLong extends DataWriterSimpleBase<Long> {
+public class DataWriterSimpleUnsignedLong extends DataWriterSimpleBase<Long> implements FieldCommons {
 
     public DataWriterSimpleUnsignedLong(WriteBuffer writeBuffer, int bitLength) {
         super(writeBuffer, bitLength);
@@ -30,7 +31,12 @@ public class DataWriterSimpleUnsignedLong extends DataWriterSimpleBase<Long> {
 
     @Override
     public void write(String logicalName, Long value, WithWriterArgs... writerArgs) throws SerializationException {
-        writeBuffer.writeUnsignedLong(logicalName, bitLength, value, writerArgs);
+        String encoding = extractEncoding(writerArgs).orElse(null);
+        if("varLenUint32".equals(encoding)) {
+            writeBuffer.writeVariableLengthUnsignedInteger(logicalName, bitLength, value, writerArgs);
+        } else {
+            writeBuffer.writeUnsignedLong(logicalName, bitLength, value, writerArgs);
+        }
     }
 
 }
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 5bc4246..2c635ff 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
@@ -80,6 +80,23 @@ public interface ReadBuffer extends ByteOrderAware {
         return readUnsignedBigInteger("", bitLength);
     }
 
+    default long readVariableLengthUnsignedInteger(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException {
+        long value = readUnsignedShort(8);
+        if(value >= 0x7F) {
+            value = (value << 8) | (readUnsignedShort(8));
+            if(value >= 0xFF7F) {
+                value = (value << 8) | (readUnsignedShort(8));
+                if(value >= 0xFFFF7F) {
+                    value = (value << 8) | (readUnsignedShort(8));
+                    if(value >= 0xFFFFFF7F) {
+                        throw new ParseException("Value too big for variable length 32 bit unsigned integer");
+                    }
+                }
+            }
+        }
+        return value;
+    }
+
     byte readSignedByte(String logicalName, int bitLength, WithReaderArgs... readerArgs) throws ParseException;
 
     default byte readSignedByte(int bitLength) throws ParseException {
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 7cf9eca..e953311 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
@@ -78,6 +78,21 @@ public interface WriteBuffer extends ByteOrderAware {
         writeUnsignedBigInteger("", bitLength, value);
     }
 
+    default void writeVariableLengthUnsignedInteger(String logicalName, int maxBitLength, Long value, WithWriterArgs... writerArgs) throws SerializationException {
+        if((value <= 0x7F) && (maxBitLength >= 8)) {
+            writeUnsignedShort(8, value.byteValue());
+        } else if((value <= 0xFF7F) && (maxBitLength >= 16)) {
+            writeUnsignedInt(16, value.shortValue());
+        } else if((value <= 0xFFFF7F) && (maxBitLength >= 24)) {
+            writeUnsignedLong(24, value.intValue());
+        } else if((value <= 0xFFFFFF7FL) && (maxBitLength >= 32)) {
+            // TODO: This sucks, I know ...
+            writeUnsignedBigInteger(32, new BigInteger(value.toString()));
+        } else {
+            throw new SerializationException("Error serializing variable length unsigned int with value " + value);
+        }
+    }
+
     void writeSignedByte(String logicalName, int bitLength, byte value, WithWriterArgs... writerArgs) throws SerializationException;
 
     default void writeSignedByte(int bitLength, byte value) throws SerializationException {