+ * A complex field is a field which has a type which is not a simple type (like int or boolean). + * A complex type is a type which is not a primitive type and is not a {@link ComplexTypeReference}. + * Examples of complex types are {@link java.util.List}, {@link java.util.Map}, etc. + * @param field the field to check + * @return true if the field is a complex field, false otherwise + */ public boolean isComplex(Field field) { return field instanceof PropertyField && ((PropertyField) field).getType() instanceof NonSimpleTypeReference; } + /** + * Returns the language type name for a given type reference. + *
+ * This method is used in the generated code to generate type names for fields. The returned type name
+ * is the name of the type as it should be used in the generated code. If the type is a primitive
+ * type, the returned type name is usually more specific and will indicate that a
+ * com.plc4x.common.value.Value of the corresponding type should be used.
+ * @param typeReference the type reference to get the language type name for
+ * @return the language type name for the given type reference
+ */
@Override
public String getLanguageTypeNameForTypeReference(TypeReference typeReference) {
return getLanguageTypeNameForTypeReference(typeReference, null);
}
+ /**
+ * Get the language name for a given field, but return a non-primitive type name if the type is a
+ * primitive type.
+ *
+ * This method is used in the generated code to generate type names for fields. If the type is a
+ * primitive type, the returned type name is not the same as the one returned by
+ * {@link #getLanguageTypeNameForField(Field) getLanguageTypeNameForField}. Instead, the returned
+ * type name is usually more specific and will indicate that a complex type is expected.
+ *
+ * @param field the field to get the language name for
+ * @return the language name for the field
+ */
public String getNonPrimitiveLanguageTypeNameForField(TypedField field) {
return getLanguageTypeNameForTypeReference(field.getType(), false);
}
@@ -120,79 +200,117 @@ public String getLanguageTypeNameForTypeReference(TypeReference typeReference, b
return getLanguageTypeNameForTypeReference(typeReference, null);
}
+ /**
+ * Get the language name for a given type reference.
+ * This method is used to generate code for type references
+ * in the generated code.
+ *
+ * @param typeReference the type reference for which the language name should be generated
+ * @param encoding the encoding for the type reference, if applicable
+ * @return the language name for the given type reference
+ */
public String getLanguageTypeNameForTypeReference(TypeReference typeReference, String encoding) {
if (typeReference == null) {
// TODO: shouldn't this be an error case
return "";
}
if (typeReference.isArrayTypeReference()) {
+ // We can use the array type reference to generate code for the type of the array.
final ArrayTypeReference arrayTypeReference = (ArrayTypeReference) typeReference;
TypeReference elementTypeReference = arrayTypeReference.getElementTypeReference();
emitRequiredImport("from typing import List");
+ // We use List to represent an array in python, as the type system doesn't support arrays.
+ // We also use the name of the element type as the type parameter.
return "List[" + getLanguageTypeNameForTypeReference(elementTypeReference) + "]";
}
if (typeReference.isNonSimpleTypeReference()) {
+ // We have a complex type, such as a data io type or a custom type.
emitRequiredImport("from plc4py.protocols." + protocolName + "." + flavorName.replace("-", "") + "." + typeReference.asNonSimpleTypeReference().orElseThrow().getName() + " import " + typeReference.asNonSimpleTypeReference().orElseThrow().getName());
+ // We import the name of the complex type, and then use the name of the complex type as the type name.
return typeReference.asNonSimpleTypeReference().orElseThrow().getName();
}
if (typeReference instanceof ByteOrderTypeReference) {
- return "binary.byteOrder";
+ // Byte order is represented by a class in the protocol, so we import that class and use its name as the type name.
+ return "binary.ByteOrder";
}
SimpleTypeReference simpleTypeReference = typeReference.asSimpleTypeReference().orElseThrow();
switch (simpleTypeReference.getBaseType()) {
case BIT:
+ // Bit is represented by a boolean in python.
return "bool";
case BYTE:
+ // Byte is represented by an integer in python.
return "int";
case UINT:
+ // Unsigned integer is represented by an integer in python, with the appropriate size.
IntegerTypeReference unsignedIntegerTypeReference = simpleTypeReference.asIntegerTypeReference().orElseThrow();
- if (unsignedIntegerTypeReference.getSizeInBits() <= 8) {
+ int sizeInBits = unsignedIntegerTypeReference.getSizeInBits();
+ if (sizeInBits <= 8) {
+ // Size <= 8 bit is represented by an int in python.
return "int";
}
- if (unsignedIntegerTypeReference.getSizeInBits() <= 16) {
+ if (sizeInBits <= 16) {
+ // Size <= 16 bit is represented by an int in python.
return "int";
}
- if (unsignedIntegerTypeReference.getSizeInBits() <= 32) {
+ if (sizeInBits <= 32) {
+ // Size <= 32 bit is represented by an int in python.
return "int";
}
- if (unsignedIntegerTypeReference.getSizeInBits() <= 64) {
+ if (sizeInBits <= 64) {
+ // Size <= 64 bit is represented by an int in python.
return "int";
}
- return "int";
+ // Size > 64 bit is not supported in python.
+ throw new UnsupportedOperationException("Size > 64 bit is not supported in python");
case INT:
+ // Integer is represented by an integer in python, with the appropriate size.
IntegerTypeReference integerTypeReference = simpleTypeReference.asIntegerTypeReference().orElseThrow();
- if (integerTypeReference.getSizeInBits() <= 8) {
+ int sizeInIntegerBits = integerTypeReference.getSizeInBits();
+ if (sizeInIntegerBits <= 8) {
+ // Size <= 8 bit is represented by an int in python.
return "int";
}
- if (integerTypeReference.getSizeInBits() <= 16) {
+ if (sizeInIntegerBits <= 16) {
+ // Size <= 16 bit is represented by an int in python.
return "int";
}
- if (integerTypeReference.getSizeInBits() <= 32) {
+ if (sizeInIntegerBits <= 32) {
+ // Size <= 32 bit is represented by an int in python.
return "int";
}
- if (integerTypeReference.getSizeInBits() <= 64) {
+ if (sizeInIntegerBits <= 64) {
+ // Size <= 64 bit is represented by an int in python.
return "int";
}
- return "int";
+ // Size > 64 bit is not supported in python.
+ throw new UnsupportedOperationException("Size > 64 bit is not supported in python");
case FLOAT:
case UFLOAT:
+ // Float is represented by a float in python, with the appropriate size.
FloatTypeReference floatTypeReference = simpleTypeReference.asFloatTypeReference().orElseThrow();
- int sizeInBits = floatTypeReference.getSizeInBits();
- if (sizeInBits <= 32) {
+ int sizeInFloatBits = floatTypeReference.getSizeInBits();
+ if (sizeInFloatBits <= 32) {
+ // Size <= 32 bit is represented by a float in python.
return "float";
}
- if (sizeInBits <= 64) {
+ if (sizeInFloatBits <= 64) {
+ // Size <= 64 bit is represented by a float in python.
return "float";
}
- return "float";
+ // Size > 64 bit is not supported in python.
+ throw new UnsupportedOperationException("Size > 64 bit is not supported in python");
case STRING:
case VSTRING:
+ // String is represented by a str in python.
return "str";
case TIME:
case DATE:
case DATETIME:
- return "time.Time";
+ // Time is represented by a datetime in python.
+ return "datetime";
default:
+ // For all other types, an error should be thrown.
throw new FreemarkerException("Unsupported simple type");
}
}
@@ -202,16 +320,28 @@ public String getReservedValue(ReservedField reservedField) {
return languageTypeName + "(" + reservedField.getReferenceValue() + ")";
}
+ /**
+ * Generate code for options of a field, such as encoding and byte order.
+ *
+ * @param field The field for which the options should be generated
+ * @param parserArguments The arguments of the parser, which may contain
+ * information about the encoding and byte order.
+ * @return A string containing the options, or an empty string if no options
+ * are needed.
+ */
public String getFieldOptions(TypedField field, List
+ * This method is used to generate code for converting a type reference to a PLC4Py type name.
+ *
+ * @param simpleTypeReference the type reference to get the PLC4Py type name for
+ * @return the name of the Python data reader method
+ */
public String getDataReaderCall(SimpleTypeReference simpleTypeReference) {
final int sizeInBits = simpleTypeReference.getSizeInBits();
switch (simpleTypeReference.getBaseType()) {
@@ -301,21 +440,57 @@ public String getDataReaderCall(SimpleTypeReference simpleTypeReference) {
throw new UnsupportedOperationException("Unsupported type " + simpleTypeReference.getBaseType());
}
}
+ /**
+ * Returns the name of the Python data writer method that should be used to write a value of the given type.
+ *
+ *
+ * This method is used to generate code for converting a type reference to a PLC4Py type name.
+ *
+ * @param typeReference the type reference to get the PLC4Py type name for
+ * @param fieldName the name of the field to write
+ * @return the name of the Python data writer method
+ */
+ /**
+ * Returns the name of the Python data writer method that should be used to write a value of the given type.
+ *
+ *
+ * This method is used to generate code for converting a type reference to a PLC4Py type name.
+ *
+ * @param typeReference the type reference to get the PLC4Py type name for
+ * @param fieldName the name of the field to write
+ * @return the name of the Python data writer method
+ */
public String getDataWriterCall(TypeReference typeReference, String fieldName) {
if (typeReference.isSimpleTypeReference()) {
- SimpleTypeReference simpleTypeReference = typeReference.asSimpleTypeReference().orElseThrow(IllegalStateException::new);
- return getDataWriterCall(simpleTypeReference);
+ // The type is a simple type, such as a byte or int.
+ // We can directly use the methods provided by the simple type.
+ return getDataWriterCall(typeReference.asSimpleTypeReference().orElseThrow());
} else if (typeReference.isArrayTypeReference()) {
- final ArrayTypeReference arrayTypeReference = typeReference.asArrayTypeReference().orElseThrow();
- return getDataWriterCall(arrayTypeReference.getElementTypeReference(), fieldName);
+ // The type is an array, such as an array of bytes or an array of strings.
+ // We need to recursively call getDataWriterCall to determine the name of the PLC4Py
+ // type name for the element type of the array.
+ return getDataWriterCall(typeReference.asArrayTypeReference().orElseThrow().getElementTypeReference(), fieldName);
} else if (typeReference.isComplexTypeReference()) {
+ // The type is a complex type, such as a struct.
+ // We can directly use the write_serializable method.
return "write_serializable";
} else {
throw new IllegalStateException("What is this type? " + typeReference);
}
}
+ /**
+ * Returns the name of the Python data writer method that should be used to write a value of the given enum type.
+ *
+ *
+ * This method is used to generate code for converting a type reference to a PLC4Py type name.
+ *
+ * @param typeReference the enum type reference to get the PLC4Py type name for
+ * @param fieldName the name of the field to write
+ * @param attributeName the name of the enum attribute to write, defaults to "value"
+ * @return the PLC4Py type name or an empty string if the type reference is not supported
+ */
public String getEnumDataWriterCall(EnumTypeReference typeReference, String fieldName, String attributeName) {
if (!typeReference.isEnumTypeReference()) {
throw new IllegalArgumentException("this method should only be called for enum types");
@@ -330,6 +505,16 @@ public String getEnumDataWriterCall(EnumTypeReference typeReference, String fiel
return getDataWriterCall(outputTypeReference, fieldName);
}
+ /**
+ * Returns the name of the Python data writer method that should be used to write a value of the given type.
+ *
+ *
+ * This method is used to generate code for converting a type reference to a PLC4Py type name.
+ *
+ * @param simpleTypeReference the type reference to get the PLC4Py type name for
+ *
+ * @return the PLC4Py type name or an empty string if the type reference is not supported
+ */
public String getDataWriterCall(SimpleTypeReference simpleTypeReference) {
final int sizeInBits = simpleTypeReference.getSizeInBits();
switch (simpleTypeReference.getBaseType()) {
@@ -353,6 +538,7 @@ public String getDataWriterCall(SimpleTypeReference simpleTypeReference) {
case STRING:
return "write_str";
case VSTRING:
+ // Vstring is serialized as string
VstringTypeReference vstringTypeReference = (VstringTypeReference) simpleTypeReference;
return "write_str";
case TIME:
@@ -366,6 +552,16 @@ public String getDataWriterCall(SimpleTypeReference simpleTypeReference) {
}
}
+ /**
+ * Returns the name of the PLC4Py value type that corresponds to the given type reference.
+ *
+ *
+ * This method is used to generate code for converting a type reference to a PLC4Py type name.
+ *
+ * @param typeReference the type reference to get the PLC4Py type name for
+ *
+ * @return the PLC4Py type name or an empty string if the type reference is not supported
+ */
public String getPlcValueTypeForTypeReference(TypeReference typeReference) {
if (typeReference == null) {
// TODO: shouldn't this be an error case
@@ -377,67 +573,80 @@ public String getPlcValueTypeForTypeReference(TypeReference typeReference) {
SimpleTypeReference simpleTypeReference = (SimpleTypeReference) typeReference;
switch (simpleTypeReference.getBaseType()) {
case BIT:
+ // Bit values are represented as boolean in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcBOOL");
return "PlcBOOL";
case BYTE:
+ // Byte values are represented as signed integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcSINT");
return "PlcSINT";
case UINT:
IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference;
if (unsignedIntegerTypeReference.getSizeInBits() <= 4) {
+ // Ubyte values are represented as unsigned short integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcUSINT");
return "PlcUSINT";
}
if (unsignedIntegerTypeReference.getSizeInBits() <= 8) {
+ // Uword values are represented as unsigned integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcUINT");
return "PlcUINT";
}
if (unsignedIntegerTypeReference.getSizeInBits() <= 16) {
+ // Uword16 values are represented as unsigned long integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcUDINT");
return "PlcUDINT";
}
if (unsignedIntegerTypeReference.getSizeInBits() <= 32) {
+ // Uword32 values are represented as unsigned long long integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcULINT");
return "PlcULINT";
}
case INT:
IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
if (integerTypeReference.getSizeInBits() <= 8) {
+ // Byte values are represented as signed integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcSINT");
return "PlcSINT";
}
if (integerTypeReference.getSizeInBits() <= 16) {
+ // Word values are represented as signed long integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcINT");
return "PlcINT";
}
if (integerTypeReference.getSizeInBits() <= 32) {
+ // Dword values are represented as signed long long integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcDINT");
return "PlcDINT";
}
if (integerTypeReference.getSizeInBits() <= 64) {
+ // Lword values are represented as signed long long integers in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcLINT");
return "PlcLINT";
}
-
case FLOAT:
case UFLOAT:
FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
int sizeInBits = floatTypeReference.getSizeInBits();
if (sizeInBits <= 32) {
+ // Float values are represented as float in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcREAL");
return "PlcREAL";
}
if (sizeInBits <= 64) {
+ // Double values are represented as double in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcLREAL");
return "PlcLREAL";
}
case STRING:
case VSTRING:
+ // String values are represented as a string in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcSTRING");
return "PlcSTRING";
case TIME:
case DATE:
case DATETIME:
+ // Time values are represented as a time in PLC4Py
emitRequiredImport("from plc4py.spi.values.PlcValues import PlcTIME");
return "PlcTIME";
default:
@@ -445,62 +654,131 @@ public String getPlcValueTypeForTypeReference(TypeReference typeReference) {
}
}
+ /**
+ * Returns the null value for the given type reference.
+ *
+ *
+ * This method is used to generate code for accessing the null value of a type.
+ * The null value is returned as a string that can be used directly in Python
+ * code.
+ *
+ * @param typeReference the type reference to get the null value for
+ * @return the null value for the given type reference
+ */
@Override
public String getNullValueForTypeReference(TypeReference typeReference) {
if (typeReference instanceof SimpleTypeReference) {
SimpleTypeReference simpleTypeReference = (SimpleTypeReference) typeReference;
switch (simpleTypeReference.getBaseType()) {
case BIT:
+ // Boolean type: False is the null value
return "False";
case BYTE:
case UINT:
case INT:
+ // Integer types: 0 is the null value
return "0";
case FLOAT:
+ // Float types: 0.0 is the null value
return "0.0";
case STRING:
case VSTRING:
+ // String types: an empty string is the null value
return "\"\"";
}
} else if (typeReference.isEnumTypeReference()) {
+ // Enum types: 0 is the null value
return "0";
}
+ // For all other types, return None as the null value
return "None";
}
+
+ /**
+ * Returns the number of bits for the given simple type reference.
+ *
+ * @param simpleTypeReference the simple type reference to get the number of bits for
+ * @return the number of bits for the given simple type reference
+ */
public int getNumBits(SimpleTypeReference simpleTypeReference) {
switch (simpleTypeReference.getBaseType()) {
case BIT:
+ // bit is always 1 bit
return 1;
case BYTE:
+ // byte is always 8 bits
return 8;
case UINT:
case INT:
+ // integer type references have a variable number of bits depending on the
+ // size specified by the type reference
IntegerTypeReference integerTypeReference = (IntegerTypeReference) simpleTypeReference;
return integerTypeReference.getSizeInBits();
case FLOAT:
+ // float type references have a variable number of bits depending on the
+ // precision specified by the type reference
FloatTypeReference floatTypeReference = (FloatTypeReference) simpleTypeReference;
return floatTypeReference.getSizeInBits();
case STRING:
case VSTRING:
+ // string type references have a variable number of bits depending on the
+ // length of the string
StringTypeReference stringTypeReference = (StringTypeReference) simpleTypeReference;
return stringTypeReference.getSizeInBits();
default:
+ // for all other types, return 0 as there is no meaningfull number of bits
return 0;
}
}
+ /**
+ * Returns true if the given property field needs pointer access or not.
+ *
+ *
+ * This method returns true if the property field has an optional attribute and its type reference
+ * needs pointer access or not. This is the case if the type reference is not a complex type reference
+ * and the type reference is not an array type reference or if the array type reference has an
+ * element type reference that is not a complex type reference.
+ *
+ * @param field the property field to check
+ * @return true if the property field needs pointer access, false otherwise
+ */
public boolean needsPointerAccess(PropertyField field) {
boolean isAnTypeOfOptional = "optional".equals(field.getTypeName());
return isAnTypeOfOptional && needPointerAccess(field.getType());
}
+ /**
+ * Returns true if the given type reference needs pointer access or not. This
+ * method is used to determine whether the generated code needs to use a
+ * pointer for accessing data of the given type.
+ *
+ *
+ * This method returns true if the type reference is not a complex type reference
+ * and the type reference is not an array type reference or if the array type
+ * reference has an element type reference that is not a complex type reference.
+ *
+ * @param typeReference the type reference to check
+ * @return true if the type reference needs pointer access, false otherwise
+ */
public boolean needPointerAccess(TypeReference typeReference) {
boolean isNotAnComplexTypeReference = !typeReference.isComplexTypeReference();
boolean arrayTypeIsNotAnComplexTypeReference = !(typeReference.isArrayTypeReference() && typeReference.asArrayTypeReference().orElseThrow().getElementTypeReference().isComplexTypeReference());
return isNotAnComplexTypeReference && arrayTypeIsNotAnComplexTypeReference;
}
+ /**
+ * Gets a special read buffer read method call for a simple type reference and a typed field.
+ * The method is a convenience method that is used to call one of the read buffer methods depending
+ * on the type of the field. The method is used to generate code for reading data from a buffer.
+ *
+ * @param logicalName the logical name of the field
+ * @param simpleTypeReference the simple type reference of the field
+ * @param parserArguments the parser arguments used for the field
+ * @param field the typed field
+ * @return a string representing the read buffer read method call
+ */
public String getSpecialReadBufferReadMethodCall(String logicalName, SimpleTypeReference simpleTypeReference, TypedField field) {
return getReadBufferReadMethodCall(logicalName, simpleTypeReference, null, field);
}
@@ -509,11 +787,22 @@ public String getReadBufferReadMethodCall(String logicalName, SimpleTypeReferenc
return getReadBufferReadMethodCall(logicalName, simpleTypeReference, null, field);
}
+ /**
+ * Gets a read buffer read method call for a simple type reference and a typed field.
+ * The method will handle cases where the value string is present and in other cases it will default to the read buffer read method call without any arguments.
+ *
+ * @param logicalName the logical name of the field
+ * @param simpleTypeReference the simple type reference of the field
+ * @param valueString the value string to be passed to the read buffer read method call
+ * @param field the typed field
+ * @return the read buffer read method call
+ */
@Override
public String getReadBufferReadMethodCall(SimpleTypeReference simpleTypeReference, String valueString, TypedField field) {
return getReadBufferReadMethodCall("", simpleTypeReference, valueString, field);
}
+
public String getReadBufferReadMethodCall(String logicalName, SimpleTypeReference simpleTypeReference, String valueString, TypedField field) {
switch (simpleTypeReference.getBaseType()) {
case BIT:
@@ -685,31 +974,85 @@ public String toParseExpression(Field field, TypeReference resultType, Term term
return tracer + toTypedParseExpression(field, resultType, term, parserArguments);
}
+ /**
+ * Returns a Python expression that represents the given term parsed according to the given type reference.
+ *
+ * @param field the field of the given type reference
+ * @param resultType the type reference of the given data
+ * @param term the term to parse
+ * @param parserArguments the parser arguments
+ * @param suppressPointerAccess whether to suppress pointer access
+ * @return a Python expression representing the parsed term
+ */
public String toParseExpression(Field field, TypeReference resultType, Term term, List
+ * This method is used by the generated Python code to properly handle string values when
+ * writing data to a PLC device.
+ *
+ * @param typeReference the type reference of the field the value is being written to.
+ * @param valueString the string value to be escaped.
+ * @return an escaped string value.
+ */
public String escapeEnumValue(TypeReference typeReference, String valueString) {
// Currently the only case in which here complex type references are used are when referencing enum constants.
if (typeReference != null && typeReference.isNonSimpleTypeReference()) {
@@ -1399,41 +2073,94 @@ public String escapeEnumValue(TypeReference typeReference, String valueString) {
return "0";
}
if (valueString.contains(".")) {
+ // If the value string contains a dot, it's assumed to be a string
+ // representation of an enum constant with a name in the form:
+ // "typeName_constantName" (e.g. "MyEnum_MY_CONSTANT")
String typeName = valueString.substring(0, valueString.indexOf('.'));
String constantName = valueString.substring(valueString.indexOf('.') + 1);
return typeName + "_" + constantName;
}
+ // Otherwise, return the value string as is.
return valueString;
} else {
+ // For simple types, just use the {@link #escapeValue(TypeReference, String)}
+ // method.
return escapeValue(typeReference, valueString);
}
}
+ /**
+ * Returns a list of unique {@link EnumValue}s from the given list.
+ *
+ * This is used by the generated Python code to filter out duplicates in a list of {@link EnumValue}s.
+ *
+ * @param enumValues the list of {@link EnumValue}s to filter.
+ * @return a list of unique {@link EnumValue}s.
+ */
public Collection
+ * This is used by the generated Python code to filter out duplicates in a {@link DiscriminatedComplexTypeDefinition} list.
+ *
+ * @param allSwitchCases the list of {@link DiscriminatedComplexTypeDefinition}s to filter.
+ * @return a list of unique {@link DiscriminatedComplexTypeDefinition}s.
+ */
public List
+ * This is used by the generated Python code to import classes needed to read and write PLC
+ * data.
+ *
+ * @param requiredImport the name of the data io class to import
+ */
public void emitRequiredImport(String requiredImport) {
- LOGGER.debug("emitting import '\"{}\"'", requiredImport);
+ /**
+ * Logs the emitted import statement at debug level. This is useful for debugging
+ * purposes, as it allows you to quickly identify which classes are being imported for
+ * reading and writing PLC data.
+ */
+ LOGGER.debug("emitting import \"{}\"", requiredImport);
+ /**
+ * Keeps track of all the required imports that have been emitted. This collection is used
+ * when generating the __all__ list for the generated module.
+ */
requiredImports.add(requiredImport);
}
+ /**
+ * Emits a required import statement for a data io class.
+ *
+ * This is used by the generated Python code to import classes needed to read and write PLC
+ * data.
+ *
+ * @param alias the alias of the imported class
+ * @param requiredImport the name of the data io class to import
+ */
public void emitRequiredImport(String alias, String requiredImport) {
LOGGER.debug("emitting import '{} \"{}'\"", alias, requiredImport);
requiredImports.add(alias + ' ' + '"' + requiredImport + '"');
@@ -1443,20 +2170,63 @@ public Set
+ * This is used by the generated Python code to import classes needed to read and write PLC
+ * data.
+ *
+ * The imported class must be a subclass of {@link PlcDataIo}.
+ * @param requiredImport the name of the data io class to import
+ */
public void emitDataIoRequiredImport(String requiredImport) {
LOGGER.debug("emitting io import '\"{}\"'", requiredImport);
requiredImportsForDataIo.add(requiredImport);
}
+
+ /**
+ * Emits a required import statement for a data io class.
+ *
+ * This is used by the generated Python code to import classes needed to read and write PLC
+ * data.
+ *
+ * The alias parameter is used to specify an alias for the imported class. The alias is
+ * used in the generated Python code to refer to the imported class.
+ * @param alias an alias for the imported class
+ * @param requiredImport the class name to import
+ */
public void emitDataIoRequiredImport(String alias, String requiredImport) {
LOGGER.debug("emitting data io import '{} \"{}'\"", alias, requiredImport);
requiredImportsForDataIo.add(alias + ' ' + '"' + requiredImport + '"');
}
+ /**
+ * Returns a set of required import statements for data io classes.
+ *
+ * This is used by the generated Python code to import classes needed to read and write PLC
+ * data.
+ *
+ * The set returned is not modified by the code generator and is intended to be used by the
+ * generated code.
+ * @return a set of required import statements for data io classes
+ */
public Set
+ * If the field is a named field, the name is used.
+ * Otherwise, the default is "_".
+ *
+ * If there is a conflict between fields, the first one found is used.
+ *
+ * This is used to generate the names of variables in the generated Python code.
+ * @param field the field to get the variable name for
+ * @return the variable name
+ */
public String getVariableName(Field field) {
if (!(field instanceof NamedField)) {
return "_";
@@ -1465,9 +2235,21 @@ public String getVariableName(Field field) {
String name = null;
for (Field curField : ((ComplexTypeDefinition) thisType).getFields()) {
+ // If this is the current field, use its name
if (curField == field) {
name = namedField.getName();
} else if (name != null) {
+ // Otherwise, check if this field's expressions contain the given name
+ // We check for:
+ // - Array fields and their loop expressions
+ // - Checksum fields and their checksum expressions
+ // - Implicit fields and their serialize expressions
+ // - Manual array fields and their length, loop, and serialize expressions
+ // - Manual fields and their length, parse, and serialize expressions
+ // - Optional fields and their condition expressions
+ // - Switch fields and their discriminator expressions and parser arguments
+ // - Virtual fields and their value expressions
+ // - Typed fields and their type's params (if applicable)
if (curField instanceof ArrayField) {
ArrayField arrayField = (ArrayField) curField;
if (arrayField.getLoopExpression().contains(name)) {
@@ -1515,11 +2297,13 @@ public String getVariableName(Field field) {
}
} else if (curField instanceof SwitchField) {
SwitchField switchField = (SwitchField) curField;
+ // Check discriminator expressions first
for (Term discriminatorExpression : switchField.getDiscriminatorExpressions()) {
if (discriminatorExpression.contains(name)) {
return name;
}
}
+ // Then check parser arguments
for (DiscriminatedComplexTypeDefinition curCase : switchField.getCases()) {
for (Argument parserArgument : curCase.getParserArguments().orElse(Collections.emptyList())) {
if (parserArgument.getName().equals(name)) {
@@ -1533,6 +2317,7 @@ public String getVariableName(Field field) {
return name;
}
}
+ // Finally, if the field is a typed field, check its type's params (if applicable)
List This method is a bit confusing: it checks if the argument type is
+ * complex and then checks if one of the properties of the complex type
+ * definition has the same name as the property name given as argument. If
+ * such a property exists and its type is not an enum, then the argument
+ * needs to be passed as a reference.
+
+ * This method is implemented as it is because the information the
+ * {@link ComplexTypeDefinition} provides is not sufficient to determine
+ * whether an argument needs to be passed as a reference: the information
+ * about the fields of the complex type does not contain information about
+ * the type of the field. Instead, it contains information about the type
+ * definition of the field, which does not necessarily reflect the type of
+ * the field in the context of the given property.
+
+ * This method is a bit broken because it does not consider the type
+ * of the argument. Instead, it only looks at the type of the property.
+ * This is problematic because the type of the argument could be a
+ * reference to a type that is not a complex type, but still needs to be
+ * passed as a reference. This is not the case here, because we only
+ * consider complex types.
+
+ * @param propertyName the name of the property
+ * @param argumentType the type of the parser argument
+ * @return whether the parser argument needs to be passed as a reference
+ */
public boolean needsReferenceForParserArgument(String propertyName, TypeReference argumentType) {
return argumentType.asComplexTypeReference()
.map(complexTypeReference -> {
@@ -1742,6 +2636,19 @@ public boolean needsReferenceForParserArgument(String propertyName, TypeReferenc
.orElse(false);
}
+ /**
+ * Capitalizes a string by removing traces and capitalizing the first character
+ * of the remaining string.
+ *
+ * @param str the string to be capitalized
+ * @return the capitalized string
+ *
+ * This method works by first extracting traces from the string using
+ * {@link #pythonTracerStart(String)}, then removing all traces from the
+ * string using {@link #removeTraces(String)}, and finally capitalizing the
+ * first character of the remaining string using
+ * {@link org.apache.commons.lang3.StringUtils#capitalize(String)}.
+ */
public String capitalize(String str) {
Tracer dummyTracer = pythonTracerStart("");
String extractedTrace = dummyTracer.extractTraces(str);
@@ -1755,6 +2662,11 @@ public String capitalize(String str) {
*
* @param camelCase camel-case string
* @return snake-case string
+ *
+ * Method that takes a camel-case string and converts it to a snake-case
+ * string. This is done by iterating over the characters in the string and
+ * adding an underscore in between uppercase and lowercase letters or when
+ * a dash is encountered.
*/
public String camelCaseToSnakeCase(String camelCase) {
StringBuilder snakeCase = new StringBuilder();
@@ -1764,15 +2676,21 @@ public String camelCaseToSnakeCase(String camelCase) {
// If the previous letter is a lowercase letter and this one is uppercase, create a new snake-segment.
if ((i > 0) && !Character.isUpperCase(chars[i - 1]) && Character.isUpperCase(chars[i])) {
snakeCase.append('_').append(lowerCaseChar);
- } else if ((i < (chars.length - 2)) && Character.isUpperCase(chars[i]) && !Character.isUpperCase(chars[i + 1])) {
- snakeCase.append('_').append(lowerCaseChar);
}
- // If this is uppercase and the previous one is too ... just make this letter lowercase.
+ // If the next letter is lowercase, or if this is the last letter, append this character directly.
+ else if ((i < (chars.length - 2)) && (Character.isLowerCase(chars[i + 1]) || i == (chars.length - 1))) {
+ snakeCase.append(lowerCaseChar);
+ }
+ // If this is uppercase and the previous one is too ..., append this letter in lowercase.
else if ((i > 0) && Character.isUpperCase(chars[i - 1]) && Character.isUpperCase(chars[i])) {
snakeCase.append(lowerCaseChar);
- } else if (chars[i] == '-') {
+ }
+ // If this is a dash, append it directly to the snake-case string.
+ else if (chars[i] == '-') {
snakeCase.append("_");
- } else {
+ }
+ // Otherwise, append the character to the snake-case string.
+ else {
snakeCase.append(lowerCaseChar);
}
}
@@ -1784,16 +2702,37 @@ else if ((i > 0) && Character.isUpperCase(chars[i - 1]) && Character.isUpperCase
return snakeCase.toString();
}
+ /**
+ * Creates a Python tracing object with a given base name. The Tracer object
+ * will use a '/' as separator, and prefix and suffix strings to include
+ * tracing info.
+ *
+ * @param base base name of the tracing object
+ * @return a Python Tracer object
+ */
public Tracer pythonTracerStart(String base) {
return new Tracer(base) {
+ /**
+ * Returns the separator used in the tracing object.
+ *
+ * @return '/' as separator
+ */
protected String separator() {
return "/";
}
+ /**
+ * Returns the prefix string used in the tracing object.
+ * @return a string used to prefix tracing information
+ */
protected String prefix() {
return "\"\"\"";
}
+ /**
+ * Returns the suffix string used in the tracing object.
+ * @return a string used to suffix tracing information
+ */
protected String suffix() {
return "\"\"\"";
}
diff --git a/code-generation/language-python/src/main/resources/templates/python/complex-type-template.python.ftlh b/code-generation/language-python/src/main/resources/templates/python/complex-type-template.python.ftlh
index 2e64858ea17..626d950a536 100644
--- a/code-generation/language-python/src/main/resources/templates/python/complex-type-template.python.ftlh
+++ b/code-generation/language-python/src/main/resources/templates/python/complex-type-template.python.ftlh
@@ -198,11 +198,11 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
# Array Field (${arrayField.name})
<#if arrayField.type.elementTypeReference.isByteBased()>
- write_buffer.write_byte_array(self.${helper.camelCaseToSnakeCase(namedField.name)}, logical_name="${namedField.name}")
+ write_buffer.write_byte_array(self.${helper.camelCaseToSnakeCase(namedField.name)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
<#elseif arrayField.type.elementTypeReference.isSimpleTypeReference()>
- write_buffer.write_simple_array(self.${helper.camelCaseToSnakeCase(namedField.name)}, write_buffer.${helper.getDataWriterCall(arrayField.type.elementTypeReference, namedField.name)}, logical_name="${namedField.name}")
+ write_buffer.write_simple_array(self.${helper.camelCaseToSnakeCase(namedField.name)}, write_buffer.${helper.getDataWriterCall(arrayField.type.elementTypeReference, namedField.name)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
<#else>
- write_buffer.write_complex_array(self.${helper.camelCaseToSnakeCase(namedField.name)}, logical_name="${namedField.name}")
+ write_buffer.write_complex_array(self.${helper.camelCaseToSnakeCase(namedField.name)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
#if>
<#break>
<#case "checksum">
@@ -212,7 +212,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<@emitImport import="from plc4py.protocols.${protocolName} import StaticHelper" />
# Checksum Field (checksum) (Calculated)
- write_buffer.${helper.getDataWriterCall(typedField.type, namedField.name)}(${helper.getLanguageTypeNameForField(field)}(StaticHelper.${helper.toParseExpression(checksumField, checksumField.type, checksumField.checksumExpression, parserArguments)}), logical_name="${namedField.name}")
+ write_buffer.${helper.getDataWriterCall(typedField.type, namedField.name)}(${helper.getLanguageTypeNameForField(field)}(StaticHelper.${helper.toParseExpression(checksumField, checksumField.type, checksumField.checksumExpression, parserArguments)}), logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
<#break>
<#case "const">
<#assign constField = field.asConstField().orElseThrow()>
@@ -229,10 +229,10 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
# Discriminator Field (${discriminatorField.name}) (Used as input to a switch field)
<#if typedField.type.isEnumTypeReference()>
- write_buffer.${helper.getEnumDataWriterCall(typedField.type, namedField.name, "value")}(self.${helper.camelCaseToSnakeCase(discriminatorField.name)}, logical_name="${namedField.name}" ${helper.getFieldOptions(typedField, parserArguments)})
+ write_buffer.${helper.getEnumDataWriterCall(typedField.type, namedField.name, "value")}(self.${helper.camelCaseToSnakeCase(discriminatorField.name)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}" ${helper.getFieldOptions(typedField, parserArguments)})
<#else>
<#assign simpleTypeReference = discriminatorField.type.asSimpleTypeReference().orElseThrow()>
- write_buffer.${helper.getDataWriterCall(typedField.type, namedField.name)}(self.${helper.camelCaseToSnakeCase(discriminatorField.name)}, logical_name="${namedField.name}", bit_length=${simpleTypeReference.sizeInBits} ${helper.getFieldOptions(typedField, parserArguments)})
+ write_buffer.${helper.getDataWriterCall(typedField.type, namedField.name)}(self.${helper.camelCaseToSnakeCase(discriminatorField.name)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}", bit_length=${simpleTypeReference.sizeInBits} ${helper.getFieldOptions(typedField, parserArguments)})
#if>
<#break>
<#case "enum">
@@ -241,7 +241,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign namedField = field.asNamedField().orElseThrow()>
# Enum field (${namedField.name})
- write_buffer.${helper.getEnumDataWriterCall(typedField.type, namedField.name, enumField.fieldName)}(self.${helper.camelCaseToSnakeCase(namedField.name)}, logical_name="${namedField.name}")
+ write_buffer.${helper.getEnumDataWriterCall(typedField.type, namedField.name, enumField.fieldName)}(self.${helper.camelCaseToSnakeCase(namedField.name)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
<#break>
<#case "implicit">
<#assign implicitField = field.asImplicitField().orElseThrow()>
@@ -251,7 +251,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
# Implicit Field (${helper.camelCaseToSnakeCase(implicitField.name)}) (Used for parsing, but its value is not stored as it's implicitly given by the objects content)
<#-- Implicit field values might be used in expressions, in order to avoid problems, we generate a temporary variable with the given name. -->
${helper.camelCaseToSnakeCase(implicitField.name)}: ${helper.getLanguageTypeNameForField(field)} = (${helper.toSerializationExpression(implicitField, implicitField.type, implicitField.serializeExpression, parserArguments)})
- write_buffer.${helper.getDataWriterCall(typedField.type, namedField.name)}(${helper.camelCaseToSnakeCase(implicitField.name)}, logical_name="${namedField.name}")
+ write_buffer.${helper.getDataWriterCall(typedField.type, namedField.name)}(${helper.camelCaseToSnakeCase(implicitField.name)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
<#break>
<#case "manualArray">
<#assign manualArrayField = field.asManualArrayField().orElseThrow()>
@@ -259,7 +259,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign namedField = field.asNamedField().orElseThrow()>
# Manual Array Field (${manualArrayField.name})
- write_buffer.${helper.getDataWriterCall(typedField.type, namedField.name)}((${helper.getLanguageTypeNameForTypeReference(manualArrayField.type.elementTypeReference)} _value) -> ${helper.toParseExpression(manualArrayField, manualArrayField.type.elementTypeReference, manualArrayField.serializeExpression, parserArguments)}, logical_name="${namedField.name}")
+ write_buffer.${helper.getDataWriterCall(typedField.type, namedField.name)}((${helper.getLanguageTypeNameForTypeReference(manualArrayField.type.elementTypeReference)} _value) -> ${helper.toParseExpression(manualArrayField, manualArrayField.type.elementTypeReference, manualArrayField.serializeExpression, parserArguments)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
<#break>
<#case "manual">
<#assign manualField = field.asManualField().orElseThrow()>
@@ -267,7 +267,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign namedField = field.asNamedField().orElseThrow()>
<@emitImport import="from plc4py.protocols.${protocolName} import StaticHelper" />
# Manual Field (${manualField.name})
- write_buffer.write_manual(write_function=lambda : StaticHelper.${helper.toSerializationExpression(manualField, manualField.type, manualField.serializeExpression, parserArguments)}, logical_name="${namedField.name}")
+ write_buffer.write_manual(write_function=lambda : StaticHelper.${helper.toSerializationExpression(manualField, manualField.type, manualField.serializeExpression, parserArguments)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
<#break>
<#case "optional">
<#assign optionalField = field.asOptionalField().orElseThrow()>
@@ -275,6 +275,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign namedField = field.asNamedField().orElseThrow()>
# Optional Field (${optionalField.name}) (Can be skipped, if the value is null)
+ <#if optionalField.conditionExpression.present>
if ${helper.toSerializationExpression(optionalField, helper.boolTypeReference, optionalField.conditionExpression.get(), parserArguments)}:
<#if optionalField.type.isEnumTypeReference()>
write_buffer.${helper.getEnumDataWriterCall(optionalField.type, optionalField.name, "value")}(self.${helper.camelCaseToSnakeCase(optionalField.name)}, logical_name="${optionalField.name}")
@@ -283,6 +284,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#else>
write_buffer.${helper.getDataWriterCall(typedField.type, optionalField.name)}(self.${helper.camelCaseToSnakeCase(optionalField.name)}, logical_name="${optionalField.name}"<#if optionalField.conditionExpression.present>#if>)
#if>
+ #if>
<#break>
<#case "padding">
<#assign paddingField = field.asPaddingField().orElseThrow()>
@@ -402,9 +404,9 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
length_in_bits += ${simpleTypeReference.sizeInBits}
#if>
<#elseif helper.isEnumField(field)>
- length_in_bits += ${helper.getEnumBaseTypeReference(discriminatorField.type).sizeInBits}
+ length_in_bits += ${helper.getEnumBaseTypeReference(discriminatorField.type).sizeInBits}
<#else>
- length_in_bits += self.${helper.camelCaseToSnakeCase(discriminatorField.name)}.length_in_bits()
+ length_in_bits += self.${helper.camelCaseToSnakeCase(discriminatorField.name)}.length_in_bits()
#if>
<#break>
<#case "enum">
@@ -437,8 +439,8 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign optionalField = field.asOptionalField().orElseThrow()>
# Optional Field (${optionalField.name})
- if ${helper.toSerializationExpression(optionalField, helper.boolTypeReference, optionalField.conditionExpression.get(), parserArguments)}:
<#if optionalField.type.isSimpleTypeReference()>
+ if ${helper.toSerializationExpression(optionalField, helper.boolTypeReference, optionalField.conditionExpression.get(), parserArguments)}:
<#assign simpleTypeReference = optionalField.type.asSimpleTypeReference().orElseThrow()>
<#if simpleTypeReference.isVstringTypeReference()>
<#assign vstringTypeReference = simpleTypeReference.asVstringTypeReference().orElseThrow()>
@@ -461,9 +463,8 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
# Padding Field (padding)
<#-- We're replacing the "lastItem" with 'false' here as the item itself can't know if it is the last -->
- int _timesPadding = int(${helper.toParseExpression(paddingField, helper.intTypeReference, paddingField.paddingCondition, parserArguments)})
- while _timesPadding-- > 0:
- length_in_bits += ${simpleTypeReference.sizeInBits}
+ _timesPadding: int = int(${helper.toSerializationExpression(paddingField, helper.intTypeReference, paddingField.paddingCondition, parserArguments)})
+ length_in_bits = ${simpleTypeReference.sizeInBits} * _timesPadding
<#break>
<#case "reserved">
@@ -557,6 +558,21 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
start_pos: int = read_buffer.get_pos()
cur_pos: int = 0
#if>
+
+ <#if hasParserArguments>
+ <#list parserArguments as parserArgument>
+ if isinstance(${helper.camelCaseToSnakeCase(parserArgument.name)}, str):
+ <#if parserArgument.type.isEnumTypeReference()>
+ ${helper.camelCaseToSnakeCase(parserArgument.name)} = ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}[${helper.camelCaseToSnakeCase(parserArgument.name)}]
+ <#elseif helper.getLanguageTypeNameForTypeReference(parserArgument.type, false) = "bool">
+ <@emitImport import="from distutils.util import strtobool" />
+ ${helper.camelCaseToSnakeCase(parserArgument.name)} = bool(strtobool(${helper.camelCaseToSnakeCase(parserArgument.name)}))
+ <#else>
+ ${helper.camelCaseToSnakeCase(parserArgument.name)} = ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}(${helper.camelCaseToSnakeCase(parserArgument.name)})
+ #if>
+ #list>
+ #if>
+
<#assign reservedFieldIndex=0>
<#list type.fields as field>
<#switch field.typeName>
@@ -598,7 +614,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign typedField = field.asTypedField().orElseThrow()>
<#assign namedField = field.asNamedField().orElseThrow()>
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(assertField.type)}(logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(assertField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
<#break>
<#case "checksum">
<#assign checksumField = field.asChecksumField().orElseThrow()>
@@ -606,14 +622,14 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign namedField = field.asNamedField().orElseThrow()>
<@emitImport import="from plc4py.protocols.${protocolName} import StaticHelper" />
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(checksumField.type)}(logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(checksumField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
<#break>
<#case "const">
<#assign constField = field.asConstField().orElseThrow()>
<#assign typedField = field.asTypedField().orElseThrow()>
<#assign namedField = field.asNamedField().orElseThrow()>
- ${helper.camelCaseToSnakeCase(namedField.name)?upper_case}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(constField.type)}(logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ ${helper.camelCaseToSnakeCase(namedField.name)?upper_case}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(constField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
<#break>
<#case "discriminator">
<#assign discriminatorField = field.asDiscriminatorField().orElseThrow()>
@@ -621,9 +637,9 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign namedField = field.asNamedField().orElseThrow()>
<#if typedField.type.isSimpleTypeReference()>
<#assign simpleTypeReference = typedField.type.asSimpleTypeReference().orElseThrow()>
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(discriminatorField.type)}(logical_name="${namedField.name}", bit_length=${simpleTypeReference.sizeInBits}${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(discriminatorField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}", bit_length=${simpleTypeReference.sizeInBits}${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
<#else>
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(discriminatorField.type)}(logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(discriminatorField.type)}logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
#if>
@@ -633,14 +649,14 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign typedField = field.asTypedField().orElseThrow()>
<#assign namedField = field.asNamedField().orElseThrow()>
- ${helper.camelCaseToSnakeCase(namedField.name)} = read_buffer.${helper.getDataReaderCall(enumField.type)}(logical_name="${namedField.name}", readEnum(${enumField.type.asNonSimpleTypeReference().orElseThrow().typeDefinition.name}::firstEnumForField${enumField.fieldName?cap_first}, ${helper.getDataReaderCall(helper.getEnumFieldTypeReference(enumField.type, enumField.fieldName))}${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ ${helper.camelCaseToSnakeCase(namedField.name)} = read_buffer.${helper.getDataReaderCall(enumField.type)}logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
<#break>
<#case "implicit">
<#assign implicitField = field.asImplicitField().orElseThrow()>
<#assign typedField = field.asTypedField().orElseThrow()>
<#assign namedField = field.asNamedField().orElseThrow()>
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(implicitField.type)}(logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(implicitField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
<#break>
<#case "manualArray">
<#assign manualArrayField = field.asManualArrayField().orElseThrow()>
@@ -663,29 +679,34 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign typedField = field.asTypedField().orElseThrow()>
<#assign namedField = field.asNamedField().orElseThrow()>
- ${helper.camelCaseToSnakeCase(manualField.name)} = read_buffer.read_manual(read_function=lambda : StaticHelper.${helper.toParseExpression(manualField, manualField.type, manualField.parseExpression, parserArguments)}, logical_name="${namedField.name}")
+ ${helper.camelCaseToSnakeCase(manualField.name)} = read_buffer.read_manual(read_function=lambda : StaticHelper.${helper.toParseExpression(manualField, manualField.type, manualField.parseExpression, parserArguments)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}")
<#break>
<#case "optional">
<#assign optionalField = field.asOptionalField().orElseThrow()>
<#assign typedField = field.asTypedField().orElseThrow()>
<#assign namedField = field.asNamedField().orElseThrow()>
+
${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForTypeReference(optionalField.type, false)} = None
+ <#if optionalField.conditionExpression.present>
if ${helper.toParseExpression(optionalField, helper.boolTypeReference, optionalField.conditionExpression.get(), parserArguments)}:
- ${helper.camelCaseToSnakeCase(namedField.name)} = read_buffer.${helper.getDataReaderCall(optionalField.type)}(logical_name="${namedField.name}"<#if optionalField.conditionExpression.present>#if>${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
-
+ ${helper.camelCaseToSnakeCase(namedField.name)} = <#if typedField.type.isEnumTypeReference()>read_buffer.${helper.getDataReaderCall(optionalField.type)}bit_length=${helper.getEnumBaseTypeReference(typedField.type).sizeInBits}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else><#if optionalField.type.isComplexTypeReference()>read_buffer.${helper.getDataReaderCall(optionalField.type)}logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else>read_buffer.${helper.getDataReaderCall(optionalField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>) #if>#if>
+ #if>
<#break>
<#case "padding">
<#assign paddingField = field.asPaddingField().orElseThrow()>
<#assign typedField = field.asTypedField().orElseThrow()>
<#assign simpleTypeReference = paddingField.type.asSimpleTypeReference().orElseThrow()>
- read_buffer.${helper.getDataReaderCall(paddingField.type)}(int) (${helper.toParseExpression(paddingField, paddingField.type, paddingField.paddingCondition, parserArguments)})${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ for _ in range(${helper.toSerializationExpression(paddingField, paddingField.type, paddingField.paddingCondition, parserArguments)}):
+ read_buffer.${helper.getDataReaderCall(paddingField.type)}(${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
<#break>
<#case "reserved">
<#assign reservedField = field.asReservedField().orElseThrow()>
<#assign typedField = field.asTypedField().orElseThrow()>
- reserved_field_${reservedFieldIndex}: ${helper.getLanguageTypeNameForTypeReference(reservedField.type, false)}<#assign reservedFieldIndex=reservedFieldIndex+1> = read_buffer.${helper.getDataReaderCall(reservedField.type)}(logical_name="reserved"${helper.getReservedValue(reservedField)}${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ reserved_field_${reservedFieldIndex}: ${helper.getLanguageTypeNameForTypeReference(reservedField.type, false)}<#assign reservedFieldIndex=reservedFieldIndex+1> = read_buffer.${helper.getDataReaderCall(reservedField.type)}(logical_name="reserved"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ if reserved_field_${reservedFieldIndex} != ${helper.getReservedValue(reservedField)}:
+ raise ParseValidationException("reserved_field_${reservedFieldIndex} != ${helper.getReservedValue(reservedField)}")
<#break>
<#case "simple">
<#assign simpleField = field.asSimpleField().orElseThrow()>
@@ -696,12 +717,12 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign simpleTypeReference = typedField.type.asSimpleTypeReference().orElseThrow()>
<#if simpleTypeReference.isVstringTypeReference()>
<#assign vstringTypeReference = simpleTypeReference.asVstringTypeReference().orElseThrow()>
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForTypeReference(simpleField.type, false)} = <#if typedField.type.isEnumTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}bit_length=${helper.toParseExpression(simpleField, helper.intTypeReference, vstringTypeReference.getLengthExpression(), parserArguments)}, logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else><#if simpleField.type.isComplexTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else>read_buffer.${helper.getDataReaderCall(simpleField.type)}(logical_name="${namedField.name}", bit_length=${helper.toParseExpression(simpleField, helper.intTypeReference, vstringTypeReference.getLengthExpression(), parserArguments)}${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>) #if>#if>
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForTypeReference(simpleField.type, false)} = <#if typedField.type.isEnumTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}bit_length=${helper.toParseExpression(simpleField, helper.intTypeReference, vstringTypeReference.getLengthExpression(), parserArguments)}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else><#if simpleField.type.isComplexTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else>read_buffer.${helper.getDataReaderCall(simpleField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}", bit_length=${helper.toParseExpression(simpleField, helper.intTypeReference, vstringTypeReference.getLengthExpression(), parserArguments)}${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>) #if>#if>
<#else>
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForTypeReference(simpleField.type, false)} = <#if typedField.type.isEnumTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}bit_length=${simpleTypeReference.sizeInBits}, logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else><#if simpleField.type.isComplexTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else>read_buffer.${helper.getDataReaderCall(simpleField.type)}(logical_name="${namedField.name}", bit_length=${simpleTypeReference.sizeInBits}${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>) #if>#if>
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForTypeReference(simpleField.type, false)} = <#if typedField.type.isEnumTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}bit_length=${simpleTypeReference.sizeInBits}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else><#if simpleField.type.isComplexTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else>read_buffer.${helper.getDataReaderCall(simpleField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}", bit_length=${simpleTypeReference.sizeInBits}${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>) #if>#if>
#if>
<#else>
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForTypeReference(simpleField.type, false)} = <#if typedField.type.isEnumTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}bit_length=${helper.getEnumBaseTypeReference(typedField.type).sizeInBits}, logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else><#if simpleField.type.isComplexTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else>read_buffer.${helper.getDataReaderCall(simpleField.type)}(logical_name="${namedField.name}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>) #if>#if>
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForTypeReference(simpleField.type, false)} = <#if typedField.type.isEnumTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}bit_length=${helper.getEnumBaseTypeReference(typedField.type).sizeInBits}, logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else><#if simpleField.type.isComplexTypeReference()>read_buffer.${helper.getDataReaderCall(simpleField.type)}logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)<#else>read_buffer.${helper.getDataReaderCall(simpleField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>) #if>#if>
#if>
@@ -759,8 +780,9 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#break>
<#case "validation">
<#assign validationField = field.asValidationField().orElseThrow()>
+
# Validation
- if not ${helper.toParseExpression(validationField, helper.boolTypeReference, validationField.getValidationExpression(), null)}):
+ if not ${helper.toParseExpression(validationField, helper.boolTypeReference, validationField.getValidationExpression(), null)}:
<#assign errorType="ParseValidationException">
<#if !validationField.shouldFail()><#assign errorType="ParseAssertException">#if>
raise ${errorType}(${validationField.getDescription().orElse("\"Validation failed\"")})
@@ -771,7 +793,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<#assign typedField = field.asTypedField().orElseThrow()>
<#assign namedField = field.asNamedField().orElseThrow()>
- ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(peekField.type)}(logical_name="${namedField.name}"<#if peekField.offsetExpression.present>, ${helper.toParseExpression(peekField, helper.boolTypeReference, peekField.offsetExpression.get(), parserArguments)}#if>${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
+ ${helper.camelCaseToSnakeCase(namedField.name)}: ${helper.getLanguageTypeNameForField(field)} = read_buffer.${helper.getDataReaderCall(peekField.type)}(logical_name="${helper.camelCaseToSnakeCase(namedField.name)}"<#if peekField.offsetExpression.present>, ${helper.toParseExpression(peekField, helper.boolTypeReference, peekField.offsetExpression.get(), parserArguments)}#if>${helper.getFieldOptions(typedField, parserArguments)}<#if hasParserArguments>, ${parsedList}#if>)
<#break>
#switch>
#list>
@@ -847,14 +869,9 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>#if>(<#if ty
<@emitImport import="from plc4py.api.exceptions.exceptions import SerializationException" />
<@emitImport import="from plc4py.api.exceptions.exceptions import PlcRuntimeException" />
def __str__(self) -> str:
- pass
- #write_buffer_box_based: WriteBufferBoxBased = WriteBufferBoxBased(True, True)
- #try:
- # write_buffer_box_based.writeSerializable(self)
- #except SerializationException as e:
- # raise PlcRuntimeException(e)
+ # TODO:- Implement a generic python object to probably json convertor or something.
+ return ""
- #return "\n" + str(write_buffer_box_based.get_box()) + "\n"
<#if type.isDiscriminatedParentTypeDefinition()>
@dataclass
diff --git a/code-generation/language-python/src/main/resources/templates/python/data-io-template.python.ftlh b/code-generation/language-python/src/main/resources/templates/python/data-io-template.python.ftlh
index cbef5a873be..029c4854085 100644
--- a/code-generation/language-python/src/main/resources/templates/python/data-io-template.python.ftlh
+++ b/code-generation/language-python/src/main/resources/templates/python/data-io-template.python.ftlh
@@ -76,7 +76,7 @@ class ${type.name}:
<#if discriminatorType.isEnumTypeReference()>
${helper.getLanguageTypeNameForTypeReference(discriminatorType)}.${helper.toParseExpression(dataIoTypeDefinition.switchField.orElseThrow(), discriminatorType, discriminatorValueTerm, parserArguments)}
<#else>
- ${helper.camelCaseToSnakeCase(helper.toParseExpression(dataIoTypeDefinition.switchField.orElseThrow(), discriminatorType, discriminatorValueTerm, parserArguments))}
+ ${helper.toParseExpression(dataIoTypeDefinition.switchField.orElseThrow(), discriminatorType, discriminatorValueTerm, parserArguments)}
#if>
<#sep> and #sep>
#list>
@@ -102,7 +102,7 @@ class ${type.name}:
<@emitImport import="from typing import List" />
<@emitImport import="from plc4py.api.value.PlcValue import PlcValue" />
${helper.camelCaseToSnakeCase(arrayField.name)}: List[PlcValue] = []
- for cur_item in range(item_count):
+ for _ in range(item_count):
${helper.camelCaseToSnakeCase(arrayField.name)}.append(${helper.getPlcValueTypeForTypeReference(elementTypeReference)}(${helper.getLanguageTypeNameForTypeReference(elementTypeReference, false)}(<#if elementTypeReference.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "", arrayField)})<#else>${elementTypeReference.asComplexTypeReference().orElseThrow().name}IO.static_parse(read_buffer<#if elementTypeReference.params.isPresent()>, <#list elementTypeReference.params.orElseThrow() as parserArgument>(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(elementTypeReference, parserArgument?index), true)}) (${helper.toParseExpression(arrayField, elementTypeReference, parserArgument,parserArguments)})<#sep>, #sep>#list>#if>)#if>))
<#-- In all other cases do we have to work with a list, that is later converted to an array -->
@@ -131,7 +131,7 @@ class ${type.name}:
)
@compress>
- <#-- A terminated array keeps on reading data as long as the termination expression evaluates to false -->
+ <#-- A terminated array keeps on reading data as long as the termination expression evaluates to False -->
<#elseif arrayField.isTerminatedArrayField()>
# Terminated array
${arrayField.name}: ${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)} = new LinkedList<>()
@@ -340,9 +340,8 @@ class ${type.name}:
def static_serialize(write_buffer: WriteBuffer, _value: PlcValue<#if type.parserArguments.isPresent()>, <#list type.parserArguments.orElseThrow() as parserArgument>${helper.camelCaseToSnakeCase(parserArgument.name)}: ${helper.getLanguageTypeNameForTypeReference(parserArgument.type, false)}<#sep>, #sep>#list>#if>, byte_order: ByteOrder) -> None:
<#assign defaultCaseOutput=false>
<#assign dataIoTypeDefinition=type.asDataIoTypeDefinition().orElseThrow()>
- <#list dataIoTypeDefinition.switchField.orElseThrow().cases as case>
- <#if case.discriminatorValueTerms?has_content>
- if <@compress single_line=true>
+ if <#list dataIoTypeDefinition.switchField.orElseThrow().cases as case><#if case.discriminatorValueTerms?has_content> <@compress single_line=true>
+
<#list case.discriminatorValueTerms as discriminatorValueTerm>
<#assign discriminatorExpression=dataIoTypeDefinition.switchField.orElseThrow().discriminatorExpressions[discriminatorValueTerm?index].asLiteral().orElseThrow().asVariableLiteral().orElseThrow()>
<#assign discriminatorType=helper.getDiscriminatorTypes()[discriminatorExpression.name]>
@@ -358,14 +357,13 @@ class ${type.name}:
<#else>
<#assign defaultCaseOutput=true>
#if>
-
<#list case.fields as field>
<#switch field.typeName>
<#case "array">
<#assign arrayField=field.asArrayField().orElseThrow()>
<#assign elementTypeReference=arrayField.type.elementTypeReference>
- values: PlcList = _value
-
+ <@emitImport import="from typing import cast" />
+ values: PlcList = cast(PlcList, _value)
<#if case.name == "Struct">
for val in values.getStruct().get("${arrayField.name}").get_list():
<#if elementTypeReference.isByteBased()>
@@ -379,8 +377,8 @@ class ${type.name}:
for val in values.get_list():
<#if elementTypeReference.isByteBased()>
<@emitImport import="from typing import List" />
- value: list[byte] = val.get_raw()
- write_buffer.write_byte_array("", value)
+ value: ${helper.getLanguageTypeNameForField(arrayField)} = val.get_raw()
+ write_buffer.write_byte_array("", value)
<#else>
value: ${helper.getLanguageTypeNameForTypeReference(elementTypeReference)} = val.get_${helper.camelCaseToSnakeCase(helper.getLanguageTypeNameForTypeReference(elementTypeReference)?cap_first)}()
${helper.getWriteBufferWriteMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "(" + arrayField.name + ")", arrayField)}
@@ -389,7 +387,7 @@ class ${type.name}:
<#if case.name == "BOOL">
while write_buffer.getPos() < len(write_buffer.get_data()):
- write_buffer.write_bit(false)
+ write_buffer.write_bit(False)
#if>
<#break>
<#case "const">
@@ -397,22 +395,26 @@ class ${type.name}:
# Const Field (${constField.name})
${helper.getWriteBufferWriteMethodCall(constField.type.asSimpleTypeReference().orElseThrow(), constField.referenceValue, constField)}
<#break>
+
<#case "enum">
<#assign enumField=field.asEnumField().orElseThrow()>
# Enum field (${enumField.name})
${enumField.name}: ${helper.getLanguageTypeNameForField(field)} = _value.get_${helper.camelCaseToSnakeCase(enumField.name?cap_first)}()
${helper.getWriteBufferWriteMethodCall(helper.getEnumBaseTypeReference(field.asTypedField().orElseThrow().type), "(" + enumField.name + ".value)", enumField)}
<#break>
+
<#case "manual">
<#assign manualField=field.asManualField().orElseThrow()>
# Manual Field (${manualField.name})
${helper.toSerializationExpression(manualField, manualField.type, manualField.serializeExpression, type.parserArguments.orElse(null))}
<#break>
+
<#case "reserved">
<#assign reservedField=field.asReservedField().orElseThrow()>
# Reserved Field
${helper.getWriteBufferWriteMethodCall(reservedField.type.asSimpleTypeReference().orElseThrow(), helper.getReservedValue(reservedField), reservedField)}
<#break>
+
<#case "simple">
<#assign simpleField=field.asSimpleField().orElseThrow()>
# Simple Field (${simpleField.name})
@@ -430,13 +432,16 @@ class ${type.name}:
#if>
<#if simpleField.type.isSimpleTypeReference()>
${helper.getWriteBufferWriteMethodCall(simpleField.type.asSimpleTypeReference().orElseThrow(), "(" + simpleField.name + ")", simpleField)}
+
<#else>
${simpleField.type.asComplexTypeReference().orElseThrow().name}IO.static_serialize(write_buffer, ${helper.camelCaseToSnakeCase(simpleField.name)})
+
#if>
<#break>
+
#switch>
#list>
- <#sep>#sep>#list>
+ <#sep><@compress single_line=true>elif @compress>#sep>#list>
#if>
<@emitImport import="import math" />
@@ -452,9 +457,7 @@ class ${type.name}:
size_in_bits: int = 0
<#assign defaultCaseOutput=false>
<#assign dataIoTypeDefinition=type.asDataIoTypeDefinition().orElseThrow()>
- <#list dataIoTypeDefinition.switchField.orElseThrow().cases as case>
- <#if case.discriminatorValueTerms?has_content>
- if <@compress single_line=true>
+ if <#list dataIoTypeDefinition.switchField.orElseThrow().cases as case><#if case.discriminatorValueTerms?has_content> <@compress single_line=true>
<#list case.discriminatorValueTerms as discriminatorValueTerm>
<#assign discriminatorExpression=dataIoTypeDefinition.switchField.orElseThrow().discriminatorExpressions[discriminatorValueTerm?index].asLiteral().orElseThrow().asVariableLiteral().orElseThrow()>
<#assign discriminatorType=helper.getDiscriminatorTypes()[discriminatorExpression.name]>
@@ -475,7 +478,8 @@ class ${type.name}:
<#case "array">
<#assign arrayField=field.asArrayField().orElseThrow()>
<#assign elementTypeReference=arrayField.type.elementTypeReference>
- values: PlcList = _value
+ <@emitImport import="from typing import cast" />
+ values: PlcList = cast(PlcList, _value)
<#if case.name == "Struct">
# TODO: Finish this!
<#elseif elementTypeReference.isComplexTypeReference()>
@@ -512,7 +516,7 @@ class ${type.name}:
<#break>
#switch>
#list>
- <#sep>#sep>#list>
+ <#sep><@compress single_line=true>elif @compress>#sep>#list>
return size_in_bits
diff --git a/code-generation/language-python/src/main/resources/templates/python/enum-template.python.ftlh b/code-generation/language-python/src/main/resources/templates/python/enum-template.python.ftlh
index a82bf2438af..f418d496834 100644
--- a/code-generation/language-python/src/main/resources/templates/python/enum-template.python.ftlh
+++ b/code-generation/language-python/src/main/resources/templates/python/enum-template.python.ftlh
@@ -83,7 +83,7 @@ class ${type.name}(AutoNumberEnum):
<#if helper.escapeValue(type.getConstantType(constantName), enumValue.getConstant(constantName).orElse(null)) == 'null'>
= None
<#elseif type.getConstantType(constantName).isEnumTypeReference()>
- = ${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)}.${helper.escapeValue(type.getConstantType(constantName), enumValue.getConstant(constantName).orElseThrow())}<#else>${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)}(${helper.escapeValue(type.getConstantType(constantName), enumValue.getConstant(constantName).orElseThrow())})#if><#else>${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)}(${helper.escapeValue(type.getConstantType(constantName), enumValue.getConstant(constantName).orElseThrow())})
+ ${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)}.${helper.escapeValue(type.getConstantType(constantName), enumValue.getConstant(constantName).orElseThrow())}<#else>${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)}(${helper.escapeValue(type.getConstantType(constantName), enumValue.getConstant(constantName).orElseThrow())})#if><#else>${helper.getLanguageTypeNameForTypeReference(type.getConstantType(constantName), true)}(${helper.escapeValue(type.getConstantType(constantName), enumValue.getConstant(constantName).orElseThrow())})
#if>
<#sep>, #sep>
#list>
diff --git a/code-generation/language-python/src/test/resources/integration-test/pom.xml b/code-generation/language-python/src/test/resources/integration-test/pom.xml
deleted file mode 100644
index 550b1281e6c..00000000000
--- a/code-generation/language-python/src/test/resources/integration-test/pom.xml
+++ /dev/null
@@ -1,201 +0,0 @@
-
-
-self.length(<child expression>)
.
+ * Otherwise, this expression will be simply self.length
.
+ * @param field the field of the given type reference
+ * @param variableLiteral the variable literal to access
+ * @param serialize whether the data is serialized
+ * @param tracer the tracer for this expression
+ * @return the expression to access the length of the given variable literal in the given type reference
+ */
private String toLengthVariableExpression(Field field, VariableLiteral variableLiteral, boolean serialize, Tracer tracer) {
tracer = tracer.dive("length");
return tracer + (serialize ? ("len(self." + camelCaseToSnakeCase(variableLiteral.getName()) + ")") : ("(" + variableLiteral.getName() + ")"));
}
+ /**
+ * Returns the expression to access the value of the given variable literal in the given type reference.
+ * If the given variable literal has a child, this expression will be a method call of the form
+ * self._value(<child expression>)
.
+ * Otherwise, this expression will be simply self.value
.
+ * @param field the field of the given type reference
+ * @param typeReference the type reference of the given data
+ * @param variableLiteral the variable literal to access
+ * @param parserArguments the parser arguments
+ * @param serializerArguments the serializer arguments
+ * @param serialize whether the data is serialized
+ * @param suppressPointerAccess whether to suppress pointer access
+ * @param tracer the tracer for this expression
+ * @return the expression to access the value of the given variable literal in the given type reference
+ */
private String toValueVariableExpression(Field field, TypeReference typeReference, VariableLiteral variableLiteral, List
OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+ OK
+
+ * This method safely attempts to convert the given tag address string into a PlcTag, returning an
+ * {@link Optional} that is empty if the parsing fails.
*
- * @throws PlcRuntimeException If the string cannot be parsed
+ * @param tagAddress The string representation of the tag address to parse.
+ * @return An optional holding the parsed PLC tag if successful, otherwise an empty optional.
*/
- @Deprecated
- default PlcTag parseTagAddress(String tagAddress) throws PlcInvalidTagException {
- throw new PlcRuntimeException("Parse method is not implemented for this connection / driver");
- }
+ Optional
- * "%DB400.xxx"
- *
- *
- * ["%DB400.xxx", ""]
- *
- *
- * { "item1": "%DB400.xxx", "item2": "xx" }
- *
- * Prepared Statement
- *
- * { "item1": ?, "item2": ? }
- *
- *
- * @param s
- * @return
- */
- @Experimental
- default Future
- * define { "item1": "%DB400.xxx", "item2": "xx" } AS my_pymél_struct
- *
- *
- * define %DN4ßß" AS "my_structure"
- *
- * @param pql
- * @return
- */
- @Experimental
- default Future
@@ -37,7 +41,7 @@
public interface PlcDriver {
/**
- * @return code of the implemented protocol. This is usually a lot shorter than the String returned by @see #getProtocolName().
+ * @return code of the implemented protocol. This is usually a lot shorter than the String returned by @see#getProtocolName().
*/
String getProtocolCode();
@@ -47,15 +51,36 @@ public interface PlcDriver {
String getProtocolName();
/**
- * @return the type of the Configuration used by this driver.
- */
- Class extends PlcConnectionConfiguration> getConfigurationType();
-
- /**
- * Provides driver metadata.
+ * @return Provides driver metadata.
*/
default PlcDriverMetadata getMetadata() {
- return () -> false;
+ return new PlcDriverMetadata() {
+
+ @Override
+ public Optional