diff --git a/code-generation/language-python/src/main/java/org/apache/plc4x/language/python/PythonLanguageTemplateHelper.java b/code-generation/language-python/src/main/java/org/apache/plc4x/language/python/PythonLanguageTemplateHelper.java index bd2370721bd..c842e5ebe95 100644 --- a/code-generation/language-python/src/main/java/org/apache/plc4x/language/python/PythonLanguageTemplateHelper.java +++ b/code-generation/language-python/src/main/java/org/apache/plc4x/language/python/PythonLanguageTemplateHelper.java @@ -579,7 +579,7 @@ public String getPlcValueTypeForTypeReference(TypeReference typeReference) { case BYTE: // Byte values are represented as signed integers in PLC4Py emitRequiredImport("from plc4py.spi.values.PlcValues import PlcSINT"); - return "PlcSINT"; + return "PlcBYTE"; case UINT: IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference; if (unsignedIntegerTypeReference.getSizeInBits() <= 4) { @@ -807,10 +807,10 @@ public String getReadBufferReadMethodCall(String logicalName, SimpleTypeReferenc switch (simpleTypeReference.getBaseType()) { case BIT: String bitType = "bit"; - return "read_buffer.read_" + bitType + "(\"" + logicalName + "\")"; + return "read_buffer.read_" + bitType + "(\"" + logicalName + "\""; case BYTE: String byteType = "byte"; - return "read_buffer.read_" + byteType + "(\"" + logicalName + "\")"; + return "read_buffer.read_" + byteType + "(\"" + logicalName + "\""; case UINT: String unsignedIntegerType; IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference; @@ -825,7 +825,7 @@ public String getReadBufferReadMethodCall(String logicalName, SimpleTypeReferenc } else { unsignedIntegerType = "unsigned_long"; } - return "read_buffer.read_" + unsignedIntegerType + "(" + simpleTypeReference.getSizeInBits() + ", logical_name=\"" + logicalName + "\")"; + return "read_buffer.read_" + unsignedIntegerType + "(" + simpleTypeReference.getSizeInBits() + ", logical_name=\"" + logicalName + "\""; case INT: String integerType; if (simpleTypeReference.getSizeInBits() <= 8) { @@ -839,10 +839,10 @@ public String getReadBufferReadMethodCall(String logicalName, SimpleTypeReferenc } else { integerType = "long"; } - return "read_buffer.read_" + integerType + "(" + simpleTypeReference.getSizeInBits() + ", logical_name=\"" + logicalName + "\")"; + return "read_buffer.read_" + integerType + "(" + simpleTypeReference.getSizeInBits() + ", logical_name=\"" + logicalName + "\""; case FLOAT: String floatType = (simpleTypeReference.getSizeInBits() <= 32) ? "float" : "double"; - return "read_buffer.read_" + floatType + "(" + simpleTypeReference.getSizeInBits() + ", logical_name=\"" + logicalName + "\")"; + return "read_buffer.read_" + floatType + "(" + simpleTypeReference.getSizeInBits() + ", logical_name=\"" + logicalName + "\""; case STRING: case VSTRING: String stringType = "str"; @@ -856,7 +856,7 @@ public String getReadBufferReadMethodCall(String logicalName, SimpleTypeReferenc VstringTypeReference vstringTypeReference = (VstringTypeReference) simpleTypeReference; length = toParseExpression(field, INT_TYPE_REFERENCE, vstringTypeReference.getLengthExpression(), null); } - return "read_buffer.read_" + stringType + "(" + simpleTypeReference.getSizeInBits() + ", logical_name=\"" + logicalName + "\", encoding=" + "\"\")"; + return "read_buffer.read_" + stringType + "(" + simpleTypeReference.getSizeInBits() + ", logical_name=\"" + logicalName + "\""; default: return ""; @@ -895,7 +895,8 @@ public String getWriteBufferWriteMethodCall(String logicalName, SimpleTypeRefere case BIT: return "write_buffer.write_bit(" + fieldName + ", \"" + logicalName + "\"" + writerArgsString + ")"; case BYTE: - return "write_buffer.write_byte(" + fieldName + ", \"" + logicalName + "\"" + writerArgsString + ")"; + ByteTypeReference byteTypeReference = (ByteTypeReference) simpleTypeReference; + return "write_buffer.write_byte(" + fieldName + ", " + byteTypeReference.getSizeInBits() + ", \"" + logicalName + "\"" + writerArgsString + ")"; case UINT: IntegerTypeReference unsignedIntegerTypeReference = (IntegerTypeReference) simpleTypeReference; if (unsignedIntegerTypeReference.getSizeInBits() <= 8) { @@ -945,7 +946,7 @@ public String getWriteBufferWriteMethodCall(String logicalName, SimpleTypeRefere .orElseThrow(() -> new FreemarkerException("Encoding must be a quoted string value")).getValue(); String length = Integer.toString(simpleTypeReference.getSizeInBits()); return "write_buffer.write_str(" + fieldName + ", " + length + ", \"" + - encoding + "\", \"" + logicalName + "\"" + writerArgsString + ")"; + logicalName + "\", \"" + encoding + "\"" + writerArgsString + ")"; } case VSTRING: { VstringTypeReference vstringTypeReference = (VstringTypeReference) simpleTypeReference; @@ -957,7 +958,7 @@ public String getWriteBufferWriteMethodCall(String logicalName, SimpleTypeRefere String lengthExpression = toExpression(field, null, vstringTypeReference.getLengthExpression(), null, Collections.singletonList(new DefaultArgument("stringLength", new DefaultIntegerTypeReference(SimpleTypeReference.SimpleBaseType.INT, 32))), true, false); String length = Integer.toString(simpleTypeReference.getSizeInBits()); return "write_buffer.write_str(" + fieldName + ", " + lengthExpression + ", \"" + - encoding + "\", \"" + logicalName + "\"" + writerArgsString + ")"; + logicalName + "\", \"" + encoding + "\"" + writerArgsString + ")"; } case DATE: case TIME: @@ -1511,7 +1512,7 @@ else if ((variableLiteral.getChild().isPresent()) && ((ComplexTypeDefinition) th } else if ((serializerArguments != null) && serializerArguments.stream() .anyMatch(argument -> argument.getName().equals(variableLiteralName))) { tracer = tracer.dive("serialization argument"); - return tracer + "self." + camelCaseToSnakeCase(variableLiteralName) + + return tracer + camelCaseToSnakeCase(variableLiteralName) + variableLiteral.getChild() .map(child -> "." + camelCaseToSnakeCase(toVariableExpression(field, typeReference, child, parserArguments, serializerArguments, serialize, suppressPointerAccess, true))) .orElse(""); 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 626d950a536..10a98c6a29c 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 @@ -565,7 +565,7 @@ class ${type.name}<#if type.isDiscriminatedParentTypeDefinition()>(<#if ty <#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" /> + <@emitImport import="from plc4py.utils.ConnectionStringHandling 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)}) 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 029c4854085..1daeb76bcc6 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 @@ -89,6 +89,7 @@ class ${type.name}: <#switch field.typeName> <#case "array"> <#assign arrayField = field.asArrayField().orElseThrow()> + <#assign typedField = field.asTypedField().orElseThrow()> <#assign elementTypeReference=arrayField.type.elementTypeReference> # Array field (${helper.camelCaseToSnakeCase(arrayField.name)}) <#-- Only update curPos if the length expression uses it --> @@ -103,7 +104,7 @@ class ${type.name}: <@emitImport import="from plc4py.api.value.PlcValue import PlcValue" /> ${helper.camelCaseToSnakeCase(arrayField.name)}: List[PlcValue] = [] 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>, ))) + ${helper.camelCaseToSnakeCase(arrayField.name)}.append(${helper.getPlcValueTypeForTypeReference(elementTypeReference)}(${helper.getLanguageTypeNameForTypeReference(elementTypeReference, false)}(<#if elementTypeReference.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "", arrayField)}${helper.getFieldOptions(typedField, parserArguments)}))<#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>, ))) <#-- In all other cases do we have to work with a list, that is later converted to an array --> <#else> @@ -118,7 +119,7 @@ class ${type.name}: while read_buffer.get_pos() < ${helper.camelCaseToSnakeCase(arrayField.name)}_end_pos): value.append(<@compress single_line=true> <#if elementTypeReference.isSimpleTypeReference()> - ${helper.getPlcValueTypeForTypeReference(elementTypeReference)}(${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "", arrayField)}) + ${helper.getPlcValueTypeForTypeReference(elementTypeReference)}(${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "", arrayField)}${helper.getFieldOptions(typedField, parserArguments)})) <#else>${elementTypeReference.asNonSimpleTypeReference().orElseThrow().name}IO.static_parse(readBuffer <#if elementTypeReference.params.isPresent()>, <#list elementTypeReference.params.orElseThrow() as parserArgument> @@ -136,7 +137,7 @@ class ${type.name}: # Terminated array ${arrayField.name}: ${helper.getNonPrimitiveLanguageTypeNameForField(arrayField)} = new LinkedList<>() while not bool(${helper.camelCaseToSnakeCase(helper.toParseExpression(arrayField, helper.boolTypeReference, arrayField.loopExpression,parserArguments))})): - ${helper.camelCaseToSnakeCase(arrayField.name)}.append(<#if elementTypeReference.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "", arrayField)}<#else>${elementTypeReference.asComplexTypeReference().orElseThrow().name}IO.static_parse(readBuffer<#if arrayField.params.isPresent()>, <#list arrayField.params.orElseThrow() as parserArgument>(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(elementTypeReference, parserArgument?index), true)}) (${helper.toParseExpression(arrayField, parserArgument,parserArguments)})<#sep>, )) + ${helper.camelCaseToSnakeCase(arrayField.name)}.append(<#if elementTypeReference.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "", arrayField)}${helper.getFieldOptions(typedField, parserArguments)})<#else>${elementTypeReference.asComplexTypeReference().orElseThrow().name}IO.static_parse(readBuffer<#if arrayField.params.isPresent()>, <#list arrayField.params.orElseThrow() as parserArgument>(${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(elementTypeReference, parserArgument?index), true)}) (${helper.toParseExpression(arrayField, parserArgument,parserArguments)})<#sep>, )) <#-- After parsing, update the current position, but only if it's needed --> <#if arrayField.loopExpression.contains("curPos")> @@ -151,9 +152,10 @@ class ${type.name}: <#break> <#case "const"> <#assign constField=field.asConstField().orElseThrow()> + <#assign typedField = field.asTypedField().orElseThrow()> # Const Field (${helper.camelCaseToSnakeCase(constField.name)}) - ${helper.camelCaseToSnakeCase(constField.name)}: ${helper.getNonPrimitiveLanguageTypeNameForField(constField)} = ${helper.getReadBufferReadMethodCall(constField.type.asSimpleTypeReference().orElseThrow(), "", constField)} + ${helper.camelCaseToSnakeCase(constField.name)}: ${helper.getNonPrimitiveLanguageTypeNameForField(constField)} = ${helper.getReadBufferReadMethodCall(constField.type.asSimpleTypeReference().orElseThrow(), "", constField)}${helper.getFieldOptions(typedField, parserArguments)}) if ${helper.camelCaseToSnakeCase(constField.name)} != ${dataIoTypeDefinition.name}.${constField.name?upper_case}): <@emitImport import="from plc4py.api.exceptions.exceptions import ParseException" /> raise ParseException("Expected constant value " + ${dataIoTypeDefinition.name}.${constField.name?upper_case} + " but got " + ${helper.camelCaseToSnakeCase(constField.name)}) @@ -164,9 +166,10 @@ class ${type.name}: <#break> <#case "enum"> <#assign enumField=field.asEnumField().orElseThrow()> + <#assign typedField = field.asTypedField().orElseThrow()> # Enum field (${helper.camelCaseToSnakeCase(enumField.name)}) - ${helper.camelCaseToSnakeCase(enumField.name)}: ${helper.getNonPrimitiveLanguageTypeNameForField(enumField)} = ${helper.getNonPrimitiveLanguageTypeNameForField(enumField)}.enum_for_value(${helper.getReadBufferReadMethodCall(helper.getEnumBaseTypeReference(enumField.type.asSimpleTypeReference().orElseThrow()), "", enumField)}) + ${helper.camelCaseToSnakeCase(enumField.name)}: ${helper.getNonPrimitiveLanguageTypeNameForField(enumField)} = ${helper.getNonPrimitiveLanguageTypeNameForField(enumField)}.enum_for_value(${helper.getReadBufferReadMethodCall(helper.getEnumBaseTypeReference(enumField.type.asSimpleTypeReference().orElseThrow()), "", enumField)}${helper.getFieldOptions(typedField, parserArguments)})) <#if enumField.name == "value"> <#assign valueDefined=true> @@ -182,22 +185,24 @@ class ${type.name}: <#break> <#case "reserved"> <#assign reservedField=field.asReservedField().orElseThrow()> + <#assign typedField = field.asTypedField().orElseThrow()> # Reserved Field (Compartmentalized so the "reserved" variable can't leak) - reserved: ${helper.getLanguageTypeNameForField(field)} = ${helper.getReadBufferReadMethodCall(reservedField.type.asSimpleTypeReference().orElseThrow(), "", reservedField)} + reserved: ${helper.getLanguageTypeNameForField(field)} = ${helper.getReadBufferReadMethodCall(reservedField.type.asSimpleTypeReference().orElseThrow(), "", reservedField)}${helper.getFieldOptions(typedField, parserArguments)}) if reserved != ${helper.getReservedValue(reservedField)}: <@emitImport import="import logging" /> logging.warning("Expected constant value " + str(${reservedField.referenceValue}) + " but got " + str(reserved) + " for reserved field.") <#break> <#case "simple"> <#assign simpleField=field.asSimpleField().orElseThrow()> + <#assign typedField = field.asTypedField().orElseThrow()> <#if helper.isEnumField(simpleField)> # Enum field (${simpleField.name}) - ${helper.camelCaseToSnakeCase(simpleField.name)}: ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)} = ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)}(${helper.getReadBufferReadMethodCall(helper.getEnumBaseTypeReference(simpleField.type), "", simpleField)}) + ${helper.camelCaseToSnakeCase(simpleField.name)}: ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)} = ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)}(${helper.getReadBufferReadMethodCall(helper.getEnumBaseTypeReference(simpleField.type), "", simpleField)}${helper.getFieldOptions(typedField, parserArguments)})) <#else> # Simple Field (${simpleField.name}) - ${helper.camelCaseToSnakeCase(simpleField.name)}: ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)} = <#if simpleField.type.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(simpleField.type.asSimpleTypeReference().orElseThrow(), "", simpleField)}<#else>${simpleField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer<#if simpleField.params.isPresent()>, <#list field.params.orElseThrow() as parserArgument>${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(simpleField.type, parserArgument?index), true)}(${helper.toParseExpression(simpleField, simpleField.type, parserArgument,parserArguments)})<#sep>, ) + ${helper.camelCaseToSnakeCase(simpleField.name)}: ${helper.getNonPrimitiveLanguageTypeNameForField(simpleField)} = <#if simpleField.type.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(simpleField.type.asSimpleTypeReference().orElseThrow(), "", simpleField)}${helper.getFieldOptions(typedField, parserArguments)})<#else>${simpleField.type.asComplexTypeReference().orElseThrow().name}IO.staticParse(readBuffer<#if simpleField.params.isPresent()>, <#list field.params.orElseThrow() as parserArgument>${helper.getLanguageTypeNameForTypeReference(helper.getArgumentType(simpleField.type, parserArgument?index), true)}(${helper.toParseExpression(simpleField, simpleField.type, parserArgument,parserArguments)})<#sep>, ${helper.getFieldOptions(typedField, parserArguments)}) <#if case.name == "Struct" || ((case.name == "DATE_AND_TIME") && ((simpleField.name == "year") || (simpleField.name == "month") || (simpleField.name == "day") || (simpleField.name == "hour") || (simpleField.name == "minutes") || (simpleField.name == "seconds"))) || @@ -289,7 +294,8 @@ class ${type.name}: <#break> <#case "DATE"> <#if helper.hasFieldsWithNames(case.fields, "year", "month", "day")> - value: LocalDate = LocalDate(int(year), (month == 0) ? 1 : int(month), (day == 0) ? 1 : int(day)) + <@emitImport import="import datetime" /> + value: datetime = datetime.datetime(int(year), int(month), int(day)) <@emitImport import="from plc4py.spi.values.PlcValues import PlcDATE" /> return PlcDATE(value) @@ -304,12 +310,10 @@ class ${type.name}: return PlcTIME_OF_DAY(value) <#break> <#case "DATE_AND_TIME"> - <#if helper.hasFieldsWithNames(case.fields, "year", "month", "day", "hour", "minutes", "seconds", "nanos")> - value: LocalDateTime = LocalDateTime(int(year), (month == 0) ? 1 : int(month), (day == 0) ? 1 : int(day), int(hour), int(minutes), int(seconds), int(nanos)) + <#if helper.hasFieldsWithNames(case.fields, "year", "month", "day", "hour", "minutes", "seconds", "microseconds")> + value: datetime = datetime.datetime(int(year), int(month), int(day), int(hour), int(minutes), int(seconds), int(microseconds)) <#elseif helper.hasFieldsWithNames(case.fields, "year", "month", "day", "hour", "minutes", "seconds")> - value: LocalDateTime = LocalDateTime(int(year), (month == 0) ? 1 : int(month), (day == 0) ? 1 : int(day), int(hour), int(minutes), int(seconds)) - <#elseif helper.hasFieldsWithNames(case.fields, "secondsSinceEpoch")> - value: LocalDateTime = LocalDateTime.ofEpochSecond(secondsSinceEpoch, 0, ZoneOffset.UTC) + value: datetime = datetime.datetime(int(year), int(month), int(day), int(hour), int(minutes), int(seconds)) <@emitImport import="from plc4py.spi.values.PlcValues import PlcDATE_AND_TIME" /> return PlcDATE_AND_TIME(value) @@ -368,7 +372,7 @@ class ${type.name}: for val in values.getStruct().get("${arrayField.name}").get_list(): <#if elementTypeReference.isByteBased()> value: ${helper.getLanguageTypeNameForField(arrayField)} = val.get_raw() - write_buffer.write_byte_array("", value) + write_buffer.write_byte(value, 8, "") <#else> value: ${helper.getLanguageTypeNameForField(arrayField)} = val.get_${helper.camelCaseToSnakeCase(helper.getLanguageTypeNameForField(arrayField)?cap_first)}() ${helper.getWriteBufferWriteMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "value", arrayField)} @@ -378,7 +382,7 @@ class ${type.name}: <#if elementTypeReference.isByteBased()> <@emitImport import="from typing import List" /> value: ${helper.getLanguageTypeNameForField(arrayField)} = val.get_raw() - write_buffer.write_byte_array("", value) + write_buffer.write_byte(value, 8, "") <#else> value: ${helper.getLanguageTypeNameForTypeReference(elementTypeReference)} = val.get_${helper.camelCaseToSnakeCase(helper.getLanguageTypeNameForTypeReference(elementTypeReference)?cap_first)}() ${helper.getWriteBufferWriteMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(), "(" + arrayField.name + ")", arrayField)} @@ -406,7 +410,9 @@ class ${type.name}: <#case "manual"> <#assign manualField=field.asManualField().orElseThrow()> # Manual Field (${manualField.name}) - ${helper.toSerializationExpression(manualField, manualField.type, manualField.serializeExpression, type.parserArguments.orElse(null))} + <@emitImport import="from plc4py.protocols.${protocolName} import StaticHelper" /> + value: PlcValue = _value + StaticHelper.${helper.toSerializationExpression(manualField, manualField.type, manualField.serializeExpression, type.parserArguments.orElse(null))} <#break> <#case "reserved"> diff --git a/code-generation/language-python/src/test/resources/plc4py/pom.xml b/code-generation/language-python/src/test/resources/plc4py/pom.xml index 059fea2df21..f9b131f540b 100644 --- a/code-generation/language-python/src/test/resources/plc4py/pom.xml +++ b/code-generation/language-python/src/test/resources/plc4py/pom.xml @@ -53,7 +53,7 @@ . ${project.basedir}/../../../../../plc4py/ - setup.* + pyproject.toml true @@ -219,35 +219,36 @@ - python-black + python-test-compile process-sources exec - ${python.venv.bin}${python.exe.bin} + ${python.venv.bin}pip3 - -m - black - . + install + .[dev] - python-test-compile - test-compile + python-black + process-sources exec - ${python.venv.bin}pip3 + ${python.venv.bin}${python.exe.bin} - install - .[dev] + -m + black + . + diff --git a/plc4py/plc4py/api/messages/PlcResponse.py b/plc4py/plc4py/api/messages/PlcResponse.py index 807dba5d43d..f61457bd965 100644 --- a/plc4py/plc4py/api/messages/PlcResponse.py +++ b/plc4py/plc4py/api/messages/PlcResponse.py @@ -22,7 +22,7 @@ from plc4py.api.messages.PlcField import PlcTag from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.api.value.PlcValue import PlcResponseCode, PlcValue -from plc4py.spi.messages.utils.ResponseItem import ResponseItem +from plc4py.spi.messages.utils.ResponseItem import ResponseItem, PlcBrowseItem @dataclass @@ -75,7 +75,7 @@ class PlcWriteResponse(PlcTagResponse): @dataclass class PlcQueryResponse(PlcResponse): - tags: Dict[str, List[ResponseItem[PlcTag]]] + tags: Dict[str, List[PlcBrowseItem[PlcTag]]] @property def tag_names(self): @@ -84,5 +84,6 @@ def tag_names(self): @dataclass class PlcBrowseResponse(PlcQueryResponse): - def get_tags(self) -> Dict[str, List[ResponseItem[PlcTag]]]: + + def get_tags(self) -> Dict[str, List[PlcBrowseItem[PlcTag]]]: return self.tags diff --git a/plc4py/plc4py/drivers/modbus/ModbusConnection.py b/plc4py/plc4py/drivers/modbus/ModbusConnection.py index d12b437f6a3..dc915464010 100644 --- a/plc4py/plc4py/drivers/modbus/ModbusConnection.py +++ b/plc4py/plc4py/drivers/modbus/ModbusConnection.py @@ -104,7 +104,7 @@ async def create(url: str) -> "ModbusConnection": host=config.host, port=config.port, ), - 10, + 15, ) except asyncio.TimeoutError: # If the creation times out, cancel the Future diff --git a/plc4py/plc4py/drivers/modbus/ModbusDevice.py b/plc4py/plc4py/drivers/modbus/ModbusDevice.py index b52993027ad..056d3798bd2 100644 --- a/plc4py/plc4py/drivers/modbus/ModbusDevice.py +++ b/plc4py/plc4py/drivers/modbus/ModbusDevice.py @@ -209,6 +209,7 @@ async def write( message_future = loop.create_future() values = request.values[request.tag_names[0]] if isinstance(tag, ModbusTagCoil): + values = self._serialize_data_items(tag, values) pdu = ModbusPDUWriteMultipleCoilsRequest(tag.address, tag.quantity, values) elif isinstance(tag, ModbusTagDiscreteInput): raise PlcRuntimeException( diff --git a/plc4py/plc4py/drivers/umas/UmasConnection.py b/plc4py/plc4py/drivers/umas/UmasConnection.py index 4257b3c3d24..ca71a6faf90 100644 --- a/plc4py/plc4py/drivers/umas/UmasConnection.py +++ b/plc4py/plc4py/drivers/umas/UmasConnection.py @@ -29,6 +29,8 @@ PlcReadRequest, PlcRequest, ReadRequestBuilder, + WriteRequestBuilder, + PlcWriteRequest, ) from plc4py.api.messages.PlcResponse import ( PlcResponse, @@ -46,12 +48,16 @@ from plc4py.spi.messages.PlcRequest import ( DefaultBrowseRequestBuilder, DefaultReadRequestBuilder, + DefaultWriteRequestBuilder, ) from plc4py.spi.transport.Plc4xBaseTransport import Plc4xBaseTransport from plc4py.spi.transport.TCPTransport import TCPTransport +from plc4py.spi.messages.PlcWriter import DefaultPlcWriter -class UmasConnection(PlcConnection, DefaultPlcReader, DefaultPlcBrowser): +class UmasConnection( + PlcConnection, DefaultPlcReader, DefaultPlcWriter, DefaultPlcBrowser +): """ Umas TCP PLC connection implementation """ @@ -115,6 +121,12 @@ def read_request_builder(self) -> ReadRequestBuilder: """ return DefaultReadRequestBuilder(UmasTagBuilder) + def write_request_builder(self) -> WriteRequestBuilder: + """ + :return: write request builder. + """ + return DefaultWriteRequestBuilder(UmasTagBuilder) + def browse_request_builder(self) -> BrowseRequestBuilder: """ :return: browse request builder. @@ -133,6 +145,9 @@ async def execute(self, request: PlcRequest) -> PlcResponse: if isinstance(request, PlcReadRequest): return await self._read(request) + if isinstance(request, PlcWriteRequest): + return await self._write(request) + if isinstance(request, PlcBrowseRequest): return await self._browse(request) diff --git a/plc4py/plc4py/drivers/umas/UmasDevice.py b/plc4py/plc4py/drivers/umas/UmasDevice.py index 5eb104143e5..cb3ad770759 100644 --- a/plc4py/plc4py/drivers/umas/UmasDevice.py +++ b/plc4py/plc4py/drivers/umas/UmasDevice.py @@ -19,8 +19,11 @@ import asyncio from asyncio import AbstractEventLoop, Transport from dataclasses import dataclass, field +from logging import raiseExceptions from typing import Dict, List, Tuple, cast +from plc4py.drivers.umas.UmasVariables import UmasCustomVariable, UmasArrayVariable +from plc4py.api.exceptions.exceptions import PlcFieldParseException, PlcRuntimeException from plc4py.api.messages.PlcRequest import ( PlcBrowseRequest, PlcReadRequest, @@ -33,8 +36,12 @@ ) from plc4py.api.value.PlcValue import PlcResponseCode, PlcValue from plc4py.drivers.umas.UmasConfiguration import UmasConfiguration -from plc4py.drivers.umas.UmasTag import UmasTag -from plc4py.drivers.umas.UmasVariables import UmasVariable, UmasVariableBuilder +from plc4py.drivers.umas.UmasTag import UmasTag, UmasTagBuilder +from plc4py.drivers.umas.UmasVariables import ( + UmasVariable, + UmasVariableBuilder, + UmasElementryVariable, +) from plc4py.protocols.umas.readwrite import ( UmasPDUReadUnlocatedVariableResponse, ) @@ -98,8 +105,23 @@ VariableReadRequestReference, ) from plc4py.spi.generation.ReadBuffer import ReadBufferByteBased -from plc4py.spi.messages.utils.ResponseItem import ResponseItem +from plc4py.spi.messages.utils.ResponseItem import ( + ResponseItem, + PlcBrowseItem, + ArrayInfo, +) from plc4py.utils.GenericTypes import ByteOrder +from plc4py.protocols.umas.readwrite.UmasPDUWriteVariableRequest import ( + UmasPDUWriteVariableRequestBuilder, +) +from plc4py.protocols.umas.readwrite.UmasPDUWriteVariableResponse import ( + UmasPDUWriteVariableResponse, +) +from plc4py.protocols.umas.readwrite.VariableWriteRequestReference import ( + VariableWriteRequestReference, +) +from plc4py.spi.values.PlcValues import PlcNull +from plc4py.protocols.umas.readwrite.UmasDataType import UmasDataType @dataclass @@ -171,7 +193,7 @@ def _generate_variable_tree( return_dict = {} for kea, tag in tags.items(): temp_variable = UmasVariableBuilder( - kea, tag, data_types, data_type_children, base_offset=tag.base_offset + kea, tag, data_types, data_type_children, base_offset=0 ).build() if temp_variable is not None: return_dict[kea] = temp_variable @@ -403,9 +425,15 @@ async def _send_read_variable_request( else: quantity = 1 + if request_tag.data_type == None: + data_type = UmasDataType(self.variables[request_tag.tag_name].data_type) + else: + data_type = UmasDataType[request_tag.data_type] + + value = DataItem.static_parse(read_buffer, data_type, request_tag.quantity) response_item = ResponseItem( PlcResponseCode.OK, - DataItem.static_parse(read_buffer, request_tag.data_type, quantity), + value, ) values[key] = response_item @@ -413,6 +441,78 @@ async def _send_read_variable_request( response = PlcReadResponse(PlcResponseCode.OK, values) return response + async def _send_write_variable_request( + self, + transport: Transport, + loop: AbstractEventLoop, + request, + sorted_tags, + ): + message_future = loop.create_future() + + sorted_variable_list: List[VariableWriteRequestReference] = [ + variable_reference[1] for variable_reference in sorted_tags + ] + request_pdu = UmasPDUWriteVariableRequestBuilder( + crc=self.project_crc, + variable_count=len(sorted_variable_list), + variables=sorted_variable_list, + ).build(0, 0) + + protocol = transport.protocol + protocol.write_wait_for_response( + request_pdu, + transport, + message_future, + ) + + await message_future + variable_name_response: UmasPDUWriteVariableResponse = message_future.result() + + values: Dict[str, ResponseItem[PlcValue]] = {} + for key, tag in sorted_tags: + if variable_name_response.umas_request_function_key != 254: + response_item = ResponseItem( + PlcResponseCode.OK, + PlcNull(None), + ) + else: + response_item = ResponseItem( + PlcResponseCode.REMOTE_ERROR, + PlcNull(None), + ) + + values[key] = response_item + + response = PlcReadResponse(PlcResponseCode.OK, values) + return response + + def _get_internal_words(self, tag) -> UmasElementryVariable: + system_word_block = 0x002B + area = tag[1:3].upper() + if area == "SW": + area_offset = 80 + word_number = (int(tag[3:]) * 2) + area_offset + data_type = UmasDataType.INT.value + base_offset = word_number // 0x100 + offset = word_number % 0x100 + return UmasElementryVariable( + tag, data_type, system_word_block, base_offset, offset + ) + + area = tag[1:2].upper() + if area == "S": + area_offset = 50 + word_number = (int(tag[3:]) * 2) + area_offset + data_type = UmasDataType.BOOL.value + base_offset = word_number // 0x100 + offset = word_number % 0x100 + return UmasElementryVariable( + tag, data_type, system_word_block, base_offset, offset + ) + else: + raise PlcFieldParseException("Unable to read non system addresses") + def _sort_tags_based_on_memory_address(self, request): tag_list: List[List[Tuple[str, VariableReadRequestReference]]] = [[]] current_list_index = 0 @@ -421,7 +521,10 @@ def _sort_tags_based_on_memory_address(self, request): for kea, tag in request.tags.items(): umas_tag = cast(UmasTag, tag) base_tag_name = umas_tag.tag_name.split(".")[0] - variable = self.variables[base_tag_name] + if base_tag_name[:1] != "%": + variable = self.variables[base_tag_name] + else: + variable = self._get_internal_words(base_tag_name) if byte_count + variable.get_byte_length() > self.max_frame_size: current_list_index += 1 @@ -432,7 +535,7 @@ def _sort_tags_based_on_memory_address(self, request): current_list.append( ( kea, - variable.get_variable_reference(umas_tag.tag_name), + variable.get_read_variable_reference(umas_tag.tag_name), ) ) @@ -446,6 +549,44 @@ def _sort_tags_based_on_memory_address(self, request): return sorted_tag_lists + def _sort_write_tags_based_on_memory_address(self, request): + tag_list: List[List[Tuple[str, VariableWriteRequestReference]]] = [[]] + current_list_index = 0 + current_list = tag_list[current_list_index] + byte_count: int = 0 + for kea, tag in request.tags.items(): + umas_tag = cast(UmasTag, tag) + base_tag_name = umas_tag.tag_name.split(".")[0] + if base_tag_name[:1] != "%": + variable = self.variables[base_tag_name] + else: + variable = self._get_internal_words(base_tag_name) + + if byte_count + variable.get_byte_length() > self.max_frame_size: + current_list_index += 1 + tag_list.append([]) + current_list = tag_list[current_list_index] + byte_count = 0 + byte_count += variable.get_byte_length() + variable.data_type + current_list.append( + ( + kea, + variable.get_write_variable_reference( + umas_tag.tag_name, request.values[kea] + ), + ) + ) + + sorted_tag_lists: List[List[Tuple[str, VariableWriteRequestReference]]] = [] + for request in tag_list: + sorted_tags = sorted( + request, + key=lambda x: (x[1].block * 100000) + x[1].base_offset + x[1].offset, + ) + sorted_tag_lists.append(sorted_tags) + + return sorted_tag_lists + async def read( self, request: PlcReadRequest, transport: Transport ) -> PlcReadResponse: @@ -470,7 +611,77 @@ async def write( """ Writes one field from the UMAS Device """ - pass + loop = asyncio.get_running_loop() + await self._update_plc_project_info(transport, loop) + sorted_tag_list = self._sort_write_tags_based_on_memory_address(request) + response = PlcWriteResponse(PlcResponseCode.OK, {}) + for sorted_tags in sorted_tag_list: + response_chunk = await self._send_write_variable_request( + transport, loop, request, sorted_tags + ) + response.code = response_chunk.response_code + response.tags = {**response.tags, **response_chunk.tags} + return response + + def generate_browse_tree(self, tag) -> PlcBrowseItem[UmasTag]: + builder = UmasTagBuilder() + if isinstance(tag, UmasElementryVariable): + plc_tag = builder.create( + tag.variable_name + + ":" + + str(UmasDataType(tag.data_type).data_type_conversion) + ) + + return PlcBrowseItem[UmasTag]( + plc_tag, tag.variable_name, True, True, False, False, False + ) + + elif isinstance(tag, UmasCustomVariable): + plc_tag = builder.create(tag.variable_name) + children: Dict[str, "PlcBrowseItem"] = {} + for tag_name, child in tag.children.items(): + children[tag_name] = self.generate_browse_tree(child) + return PlcBrowseItem[UmasTag]( + plc_tag, + tag.variable_name, + True, + True, + False, + False, + False, + [], + children, + ) + elif isinstance(tag, UmasArrayVariable): + plc_tag = builder.create( + tag.variable_name + + ":" + + str(UmasDataType(tag.data_type).data_type_conversion) + + "[" + + str(tag.end_index) + + "]" + ) + + return PlcBrowseItem[UmasTag]( + plc_tag, + tag.variable_name, + True, + True, + False, + False, + True, + [ + ArrayInfo( + tag.end_index - tag.start_index + 1, + tag.start_index, + tag.end_index, + ) + ], + ) + else: + raise PlcRuntimeException( + f"Variable of type {type(tag).__name__} is not supported" + ) async def browse( self, request: PlcBrowseRequest, transport: Transport @@ -481,9 +692,9 @@ async def browse( loop = asyncio.get_running_loop() await self._update_plc_project_info(transport, loop) response_items = {} + for key, query in request.queries.items(): - response_items[key] = [ - ResponseItem[UmasTag](PlcResponseCode.OK, tag) - for tag in self.variables.values() - ] + response_items[key] = [] + for tag in self.variables.values(): + response_items[key].append(self.generate_browse_tree(tag)) return PlcBrowseResponse(PlcResponseCode.OK, response_items) diff --git a/plc4py/plc4py/drivers/umas/UmasTag.py b/plc4py/plc4py/drivers/umas/UmasTag.py index 480a41dc85a..69ab09202b0 100644 --- a/plc4py/plc4py/drivers/umas/UmasTag.py +++ b/plc4py/plc4py/drivers/umas/UmasTag.py @@ -27,7 +27,7 @@ class UmasTag(PlcTag): _ADDRESS_PATTERN: str = ( - "^(?P[%a-zA-Z_.0-9]+\\[?[0-9]*]?):?(?P[A-Z]*):?(?P[0-9]*)" + "^(?P[%a-zA-Z_.0-9]+):?(?P[A-Z_]*)(\[(?P[0-9]*)\])?" ) _ADDRESS_COMPILED: Pattern[AnyStr] = re.compile(_ADDRESS_PATTERN) @@ -36,7 +36,7 @@ class UmasTag(PlcTag): def __init__(self, tag_name: str, quantity: int, data_type: UmasDataType): self.tag_name: str = tag_name self.quantity: int = quantity - self.data_type: UmasDataType = data_type + self.data_type: str = data_type @classmethod def matches(cls, address_string: str): @@ -60,11 +60,11 @@ def create(cls, address_string): else 1 ) data_type = ( - UmasDataType[matcher.group("dataType")] + matcher.group("dataType") if "dataType" in matcher.groupdict() and matcher.group("dataType") is not None and len(matcher.group("dataType")) is not 0 - else cls._DEFAULT_DATA_TYPE + else None ) return cls(tag_name, quantity, data_type) diff --git a/plc4py/plc4py/drivers/umas/UmasVariables.py b/plc4py/plc4py/drivers/umas/UmasVariables.py index 421173bee33..8a22194eaf7 100644 --- a/plc4py/plc4py/drivers/umas/UmasVariables.py +++ b/plc4py/plc4py/drivers/umas/UmasVariables.py @@ -20,6 +20,7 @@ from dataclasses import dataclass from typing import AnyStr, Dict, List, Pattern, Union +from plc4py.api.value.PlcValue import PlcValue from plc4py.api.exceptions.exceptions import PlcDataTypeNotFoundException from plc4py.protocols.umas.readwrite.UmasDataType import UmasDataType from plc4py.protocols.umas.readwrite.UmasDatatypeReference import ( @@ -32,6 +33,12 @@ from plc4py.protocols.umas.readwrite.VariableReadRequestReference import ( VariableReadRequestReference, ) +from plc4py.protocols.umas.readwrite.VariableWriteRequestReference import ( + VariableWriteRequestReference, +) +from plc4py.protocols.umas.readwrite.DataItem import DataItem +from plc4py.spi.generation.WriteBuffer import WriteBufferByteBased +from plc4py.utils.GenericTypes import ByteOrder @dataclass @@ -42,7 +49,14 @@ class UmasVariable: base_offset: int offset: int - def get_variable_reference(self, address: str) -> VariableReadRequestReference: + def get_read_variable_reference(self, address: str) -> VariableReadRequestReference: + raise NotImplementedError( + f"UmasVariable subclass not implemented for variable {self.variable_name}" + ) + + def get_write_variable_reference( + self, address: str, value: PlcValue + ) -> VariableWriteRequestReference: raise NotImplementedError( f"UmasVariable subclass not implemented for variable {self.variable_name}" ) @@ -55,14 +69,16 @@ def get_byte_length(self) -> int: @dataclass class UmasElementryVariable(UmasVariable): - def get_variable_reference(self, address: str) -> VariableReadRequestReference: + def get_read_variable_reference(self, address: str) -> VariableReadRequestReference: + base_offset = self.base_offset + (self.offset // 0x100) + offset = self.offset % 0x100 if self.data_type == UmasDataType.STRING.value: return VariableReadRequestReference( is_array=1, data_size_index=UmasDataType(self.data_type).request_size, block=self.block_no, - base_offset=self.base_offset, - offset=self.offset, + base_offset=base_offset, + offset=offset, array_length=16, ) else: @@ -70,9 +86,43 @@ def get_variable_reference(self, address: str) -> VariableReadRequestReference: is_array=0, data_size_index=UmasDataType(self.data_type).request_size, block=self.block_no, - base_offset=self.base_offset, - offset=self.offset, + base_offset=base_offset, + offset=offset, + array_length=None, + ) + + def get_write_variable_reference( + self, address: str, value: PlcValue + ) -> VariableWriteRequestReference: + write_buffer = WriteBufferByteBased( + UmasDataType(self.data_type).data_type_size, ByteOrder.LITTLE_ENDIAN + ) + DataItem.static_serialize( + write_buffer, + value, + UmasDataType(self.data_type), + 1, + ByteOrder.LITTLE_ENDIAN, + ) + if self.data_type == UmasDataType.STRING.value: + return VariableWriteRequestReference( + is_array=1, + data_size_index=1, + block=self.block_no, + base_offset=self.offset, + offset=self.base_offset, + array_length=len(value.value), + record_data=bytearray(write_buffer.bb), + ) + else: + return VariableWriteRequestReference( + is_array=0, + data_size_index=UmasDataType(self.data_type).request_size, + block=self.block_no, + base_offset=self.offset, + offset=self.base_offset, array_length=None, + record_data=bytearray(write_buffer.bb), ) def get_byte_length(self) -> int: @@ -83,17 +133,30 @@ def get_byte_length(self) -> int: class UmasCustomVariable(UmasVariable): children: Dict[str, UmasVariable] - def get_variable_reference(self, address: str) -> VariableReadRequestReference: + def get_read_variable_reference(self, address: str) -> VariableReadRequestReference: split_tag_address: List[str] = address.split(".") child_index = None if len(split_tag_address) > 1: child_index = split_tag_address[1] - return self.children[child_index].get_variable_reference( + return self.children[child_index].get_read_variable_reference( ".".join(split_tag_address[1:]) ) else: raise NotImplementedError("Unable to read structures of UDT's") + def get_write_variable_reference( + self, address: str, value: PlcValue + ) -> VariableWriteRequestReference: + split_tag_address: List[str] = address.split(".") + child_index = None + if len(split_tag_address) > 1: + child_index = split_tag_address[1] + return self.children[child_index].get_write_variable_reference( + ".".join(split_tag_address[1:]), value + ) + else: + raise NotImplementedError("Unable to write structures of UDT's") + def get_byte_length(self) -> int: byte_count = 0 for key, child in self.children.items(): @@ -106,7 +169,7 @@ class UmasArrayVariable(UmasVariable): start_index: int end_index: int - def get_variable_reference(self, address: str) -> VariableReadRequestReference: + def get_read_variable_reference(self, address: str) -> VariableReadRequestReference: split_tag_address: List[str] = address.split(".") address_index = None if len(split_tag_address) > 1: @@ -132,6 +195,60 @@ def get_variable_reference(self, address: str) -> VariableReadRequestReference: array_length=self.end_index - self.start_index + 1, ) + def get_write_variable_reference( + self, address: str, value: PlcValue + ) -> VariableWriteRequestReference: + split_tag_address: List[str] = address.split(".") + address_index = None + if len(split_tag_address) > 1: + address_index = int(split_tag_address[1]) + data_type_enum = UmasDataType(self.data_type) + + if address_index: + write_buffer = WriteBufferByteBased( + UmasDataType(self.data_type).data_type_size * address_index, + ByteOrder.LITTLE_ENDIAN, + ) + DataItem.static_serialize( + write_buffer, + value, + UmasDataType(self.data_type), + address_index, + ByteOrder.LITTLE_ENDIAN, + ) + return VariableWriteRequestReference( + is_array=0, + data_size_index=data_type_enum.request_size, + block=self.block_no, + base_offset=self.base_offset, + offset=self.offset + + (address_index - self.start_index) * data_type_enum.data_type_size, + array_length=address_index, + record_data=bytearray(write_buffer.bb), + ) + else: + write_buffer = WriteBufferByteBased( + UmasDataType(self.data_type).data_type_size + * (self.end_index - self.start_index + 1), + ByteOrder.LITTLE_ENDIAN, + ) + DataItem.static_serialize( + write_buffer, + value, + UmasDataType(self.data_type), + self.end_index - self.start_index + 1, + ByteOrder.LITTLE_ENDIAN, + ) + return VariableWriteRequestReference( + is_array=1, + data_size_index=data_type_enum.request_size, + block=self.block_no, + base_offset=self.base_offset, + offset=self.offset, + array_length=self.end_index - self.start_index + 1, + record_data=bytearray(write_buffer.bb), + ) + def get_byte_length(self) -> int: return 9 diff --git a/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py b/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py index 2eec7384365..50fbafeefa1 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py @@ -425,7 +425,7 @@ def static_parse( if data_type == ModbusDataType.CHAR and number_of_values == int(1): # CHAR # Simple Field (value) - value: str = read_buffer.read_str(8, logical_name="", encoding="") + value: str = read_buffer.read_str(8, logical_name="", encoding='"UTF-8"') return PlcCHAR(value) if data_type == ModbusDataType.CHAR: # List @@ -436,7 +436,9 @@ def static_parse( for _ in range(item_count): value.append( PlcSTRING( - str(read_buffer.read_str(8, logical_name="", encoding="")) + str( + read_buffer.read_str(8, logical_name="", encoding='"UTF-8"') + ) ) ) @@ -444,7 +446,7 @@ def static_parse( if data_type == ModbusDataType.WCHAR and number_of_values == int(1): # WCHAR # Simple Field (value) - value: str = read_buffer.read_str(16, logical_name="", encoding="") + value: str = read_buffer.read_str(16, logical_name="", encoding='"UTF-16"') return PlcWCHAR(value) if data_type == ModbusDataType.WCHAR: # List @@ -455,7 +457,11 @@ def static_parse( for _ in range(item_count): value.append( PlcSTRING( - str(read_buffer.read_str(16, logical_name="", encoding="")) + str( + read_buffer.read_str( + 16, logical_name="", encoding='"UTF-16"' + ) + ) ) ) @@ -691,24 +697,24 @@ def static_serialize( elif data_type == ModbusDataType.CHAR and number_of_values == int(1): # CHAR # Simple Field (value) value: str = _value.get_str() - write_buffer.write_str((value), 8, "UTF-8", "value") + write_buffer.write_str((value), 8, "value", "UTF-8") elif data_type == ModbusDataType.CHAR: # List values: PlcList = cast(PlcList, _value) for val in values.get_list(): value: str = val.get_str() - write_buffer.write_str((value), 8, "UTF-8", "value") + write_buffer.write_str((value), 8, "value", "UTF-8") elif data_type == ModbusDataType.WCHAR and number_of_values == int(1): # WCHAR # Simple Field (value) value: str = _value.get_str() - write_buffer.write_str((value), 16, "UTF-16", "value") + write_buffer.write_str((value), 16, "value", "UTF-16") elif data_type == ModbusDataType.WCHAR: # List values: PlcList = cast(PlcList, _value) for val in values.get_list(): value: str = val.get_str() - write_buffer.write_str((value), 16, "UTF-16", "value") + write_buffer.write_str((value), 16, "value", "UTF-16") @staticmethod def get_length_in_bytes( diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusADU.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusADU.py index 7cac135246f..ec7864cbc79 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusADU.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusADU.py @@ -21,7 +21,6 @@ from abc import ABC from abc import abstractmethod -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import ParseException from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException @@ -29,6 +28,7 @@ from plc4py.protocols.modbus.readwrite.DriverType import DriverType from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusAsciiADU.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusAsciiADU.py index e18f5e9facd..8f78079ba2b 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusAsciiADU.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusAsciiADU.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -29,6 +28,7 @@ from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from plc4py.utils.GenericTypes import ByteOrder from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDU.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDU.py index d2fbad670b4..e03ec11f67e 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDU.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDU.py @@ -21,13 +21,13 @@ from abc import ABC from abc import abstractmethod -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import ParseException from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUDiagnosticRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUDiagnosticRequest.py index 9d8dc6ecbd6..92bab7e1f9e 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUDiagnosticRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUDiagnosticRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUDiagnosticResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUDiagnosticResponse.py index 5644e5cbd7e..dc618192201 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUDiagnosticResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUDiagnosticResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUError.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUError.py index 0d0625be68b..f7a542b19b1 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUError.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUError.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -27,6 +26,7 @@ from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventCounterRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventCounterRequest.py index eed5c85f3ac..1a8967bbba2 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventCounterRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventCounterRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventCounterResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventCounterResponse.py index ddb93a5e45d..0a857bb2478 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventCounterResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventCounterResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventLogRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventLogRequest.py index 61327aae3c1..68ca6e23df8 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventLogRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventLogRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventLogResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventLogResponse.py index 2717794df7d..f0fbcceb40e 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventLogResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUGetComEventLogResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUMaskWriteHoldingRegisterRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUMaskWriteHoldingRegisterRequest.py index 7f594f9608f..bf9ec7bfba2 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUMaskWriteHoldingRegisterRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUMaskWriteHoldingRegisterRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUMaskWriteHoldingRegisterResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUMaskWriteHoldingRegisterResponse.py index 8bddc99dcab..9b14ca9e609 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUMaskWriteHoldingRegisterResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUMaskWriteHoldingRegisterResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadCoilsRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadCoilsRequest.py index 9f95e4d164b..72b5180d94c 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadCoilsRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadCoilsRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadCoilsResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadCoilsResponse.py index c3ddcba5a1a..2c0133cc06b 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadCoilsResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadCoilsResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDeviceIdentificationRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDeviceIdentificationRequest.py index 21418393f26..00a81f26414 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDeviceIdentificationRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDeviceIdentificationRequest.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -29,6 +28,7 @@ from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDeviceIdentificationResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDeviceIdentificationResponse.py index 77fa52e63f3..558ffd12f06 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDeviceIdentificationResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDeviceIdentificationResponse.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -38,6 +37,7 @@ from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDiscreteInputsRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDiscreteInputsRequest.py index a93d3ca31af..11f544328af 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDiscreteInputsRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDiscreteInputsRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDiscreteInputsResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDiscreteInputsResponse.py index 8f86ea5ad47..54c20fd0ed1 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDiscreteInputsResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadDiscreteInputsResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadExceptionStatusRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadExceptionStatusRequest.py index 38b316eb947..da9b7c0736c 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadExceptionStatusRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadExceptionStatusRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadExceptionStatusResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadExceptionStatusResponse.py index b5641feeab6..6c845db1828 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadExceptionStatusResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadExceptionStatusResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFifoQueueRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFifoQueueRequest.py index 41a8615efa2..62617f9f60f 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFifoQueueRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFifoQueueRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFifoQueueResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFifoQueueResponse.py index 085c7744616..2b74fd2d174 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFifoQueueResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFifoQueueResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFileRecordRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFileRecordRequest.py index a0b948f08f1..04b340d5e7d 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFileRecordRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFileRecordRequest.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -30,6 +29,7 @@ from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer from plc4py.spi.values.Common import get_size_of_array +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFileRecordResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFileRecordResponse.py index 90173fa6bed..89e58c226e6 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFileRecordResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadFileRecordResponse.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -30,6 +29,7 @@ from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer from plc4py.spi.values.Common import get_size_of_array +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadHoldingRegistersRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadHoldingRegistersRequest.py index fae809b11f6..6a784d06514 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadHoldingRegistersRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadHoldingRegistersRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadHoldingRegistersResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadHoldingRegistersResponse.py index 2bc94d6ea89..35b9dc7760a 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadHoldingRegistersResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadHoldingRegistersResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadInputRegistersRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadInputRegistersRequest.py index e37e5aef01a..14f8d978c76 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadInputRegistersRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadInputRegistersRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadInputRegistersResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadInputRegistersResponse.py index 68782da420b..1fbefc6fd36 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadInputRegistersResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadInputRegistersResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadWriteMultipleHoldingRegistersRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadWriteMultipleHoldingRegistersRequest.py index c7889578520..8a669dbb3bf 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadWriteMultipleHoldingRegistersRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadWriteMultipleHoldingRegistersRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadWriteMultipleHoldingRegistersResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadWriteMultipleHoldingRegistersResponse.py index 7c493df65b9..a23dcdafec5 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadWriteMultipleHoldingRegistersResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReadWriteMultipleHoldingRegistersResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReportServerIdRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReportServerIdRequest.py index eec8b492611..213f265abd0 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReportServerIdRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReportServerIdRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReportServerIdResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReportServerIdResponse.py index 60a26aa2ecf..43725092e7f 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReportServerIdResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUReportServerIdResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteFileRecordRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteFileRecordRequest.py index ee142fb92b9..53e8fa097c7 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteFileRecordRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteFileRecordRequest.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -30,6 +29,7 @@ from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer from plc4py.spi.values.Common import get_size_of_array +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteFileRecordResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteFileRecordResponse.py index f1905c6395f..7a1cc065651 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteFileRecordResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteFileRecordResponse.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -30,6 +29,7 @@ from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer from plc4py.spi.values.Common import get_size_of_array +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleCoilsRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleCoilsRequest.py index bd95b6f942f..3f82c855f32 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleCoilsRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleCoilsRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleCoilsResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleCoilsResponse.py index d8b8b42150a..fb4ec735d01 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleCoilsResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleCoilsResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleHoldingRegistersRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleHoldingRegistersRequest.py index fdaec56e0dd..380debfe54d 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleHoldingRegistersRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleHoldingRegistersRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import Any from typing import ClassVar from typing import List diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleHoldingRegistersResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleHoldingRegistersResponse.py index 4c014d685aa..6898b495f6d 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleHoldingRegistersResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteMultipleHoldingRegistersResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleCoilRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleCoilRequest.py index 044baa61dfc..ddcba2e4346 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleCoilRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleCoilRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleCoilResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleCoilResponse.py index 670e698d030..2f1d45a1a99 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleCoilResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleCoilResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleRegisterRequest.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleRegisterRequest.py index 1303b8c5574..99482c8896d 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleRegisterRequest.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleRegisterRequest.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleRegisterResponse.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleRegisterResponse.py index f363b238b45..9e43b12b809 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleRegisterResponse.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusPDUWriteSingleRegisterResponse.py @@ -19,13 +19,13 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusRtuADU.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusRtuADU.py index 30cd9f37fc8..48b881c666b 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusRtuADU.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusRtuADU.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -29,6 +28,7 @@ from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from plc4py.utils.GenericTypes import ByteOrder from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/modbus/readwrite/ModbusTcpADU.py b/plc4py/plc4py/protocols/modbus/readwrite/ModbusTcpADU.py index b0dac480c58..e89c73d4878 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/ModbusTcpADU.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/ModbusTcpADU.py @@ -19,7 +19,6 @@ from dataclasses import dataclass -from distutils.util import strtobool from plc4py.api.exceptions.exceptions import PlcRuntimeException from plc4py.api.exceptions.exceptions import SerializationException from plc4py.api.messages.PlcMessage import PlcMessage @@ -28,6 +27,7 @@ from plc4py.protocols.modbus.readwrite.ModbusPDU import ModbusPDU from plc4py.spi.generation.ReadBuffer import ReadBuffer from plc4py.spi.generation.WriteBuffer import WriteBuffer +from plc4py.utils.ConnectionStringHandling import strtobool from plc4py.utils.GenericTypes import ByteOrder from typing import ClassVar import math diff --git a/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py b/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py index f5a2dc2bbf1..7ff9685f549 100644 --- a/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py +++ b/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py @@ -298,7 +298,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) if data_type == "CHAR" and number_of_values == int(1): # CHAR # Simple Field (value) - value: str = read_buffer.read_str(8, logical_name="", encoding="") + value: str = read_buffer.read_str(8, logical_name="", encoding='"UTF-8"') return PlcCHAR(value) if data_type == "CHAR": # List @@ -309,7 +309,9 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) for _ in range(item_count): value.append( PlcSTRING( - str(read_buffer.read_str(8, logical_name="", encoding="")) + str( + read_buffer.read_str(8, logical_name="", encoding='"UTF-8"') + ) ) ) @@ -317,7 +319,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) if data_type == "WCHAR" and number_of_values == int(1): # WCHAR # Simple Field (value) - value: str = read_buffer.read_str(16, logical_name="", encoding="") + value: str = read_buffer.read_str(16, logical_name="", encoding='"UTF-16"') return PlcWCHAR(value) if data_type == "WCHAR": # List @@ -328,7 +330,11 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) for _ in range(item_count): value.append( PlcSTRING( - str(read_buffer.read_str(16, logical_name="", encoding="")) + str( + read_buffer.read_str( + 16, logical_name="", encoding='"UTF-16"' + ) + ) ) ) @@ -336,13 +342,13 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) if data_type == "STRING": # STRING # Simple Field (value) - value: str = read_buffer.read_str(255, logical_name="", encoding="") + value: str = read_buffer.read_str(255, logical_name="", encoding='"UTF-8"') return PlcSTRING(value) if data_type == "WSTRING": # STRING # Simple Field (value) - value: str = read_buffer.read_str(255, logical_name="", encoding="") + value: str = read_buffer.read_str(255, logical_name="", encoding='"UTF-16"') return PlcSTRING(value) return None @@ -523,34 +529,34 @@ def static_serialize( elif data_type == "CHAR" and number_of_values == int(1): # CHAR # Simple Field (value) value: str = _value.get_str() - write_buffer.write_str((value), 8, "UTF-8", "value") + write_buffer.write_str((value), 8, "value", "UTF-8") elif data_type == "CHAR": # List values: PlcList = cast(PlcList, _value) for val in values.get_list(): value: str = val.get_str() - write_buffer.write_str((value), 8, "UTF-8", "value") + write_buffer.write_str((value), 8, "value", "UTF-8") elif data_type == "WCHAR" and number_of_values == int(1): # WCHAR # Simple Field (value) value: str = _value.get_str() - write_buffer.write_str((value), 16, "UTF-16", "value") + write_buffer.write_str((value), 16, "value", "UTF-16") elif data_type == "WCHAR": # List values: PlcList = cast(PlcList, _value) for val in values.get_list(): value: str = val.get_str() - write_buffer.write_str((value), 16, "UTF-16", "value") + write_buffer.write_str((value), 16, "value", "UTF-16") elif data_type == "STRING": # STRING # Simple Field (value) value: str = _value.get_str() - write_buffer.write_str((value), 255, "UTF-8", "value") + write_buffer.write_str((value), 255, "value", "UTF-8") elif data_type == "WSTRING": # STRING # Simple Field (value) value: str = _value.get_str() - write_buffer.write_str((value), 255, "UTF-16", "value") + write_buffer.write_str((value), 255, "value", "UTF-16") @staticmethod def get_length_in_bytes( diff --git a/plc4py/plc4py/protocols/umas/StaticHelper.py b/plc4py/plc4py/protocols/umas/StaticHelper.py index b6f7e153929..686acf1adf3 100644 --- a/plc4py/plc4py/protocols/umas/StaticHelper.py +++ b/plc4py/plc4py/protocols/umas/StaticHelper.py @@ -137,4 +137,9 @@ def parse_terminated_string_bytes(read_buffer: ReadBuffer, string_length) -> str def serialize_terminated_string(write_buffer, value, string_length): - pass + terminated_char_position = len(value.value) + for i in range(len(value.value)): + if value.value[i] == "": + terminated_char_position = i + break + write_buffer.write_str(value.value, (terminated_char_position + 1) * 8) diff --git a/plc4py/plc4py/protocols/umas/readwrite/DataItem.py b/plc4py/plc4py/protocols/umas/readwrite/DataItem.py index 800ec4d68db..0fa4e4827be 100644 --- a/plc4py/plc4py/protocols/umas/readwrite/DataItem.py +++ b/plc4py/plc4py/protocols/umas/readwrite/DataItem.py @@ -24,12 +24,17 @@ from plc4py.spi.generation.WriteBuffer import WriteBuffer from plc4py.spi.values.PlcValues import PlcBOOL from plc4py.spi.values.PlcValues import PlcBYTE +from plc4py.spi.values.PlcValues import PlcDATE +from plc4py.spi.values.PlcValues import PlcDATE_AND_TIME from plc4py.spi.values.PlcValues import PlcDINT from plc4py.spi.values.PlcValues import PlcDWORD from plc4py.spi.values.PlcValues import PlcINT from plc4py.spi.values.PlcValues import PlcList from plc4py.spi.values.PlcValues import PlcREAL +from plc4py.spi.values.PlcValues import PlcSINT from plc4py.spi.values.PlcValues import PlcSTRING +from plc4py.spi.values.PlcValues import PlcTIME +from plc4py.spi.values.PlcValues import PlcTIME_OF_DAY from plc4py.spi.values.PlcValues import PlcUDINT from plc4py.spi.values.PlcValues import PlcUINT from plc4py.spi.values.PlcValues import PlcULINT @@ -37,6 +42,7 @@ from plc4py.utils.GenericTypes import ByteOrder from typing import List from typing import cast +import datetime import logging import math @@ -63,28 +69,36 @@ def static_parse( value: bool = read_buffer.read_bit("") return PlcBOOL(value) - if data_type == UmasDataType.BOOL: # List - # Array field (value) - # Count array - item_count: int = int(number_of_values) - value: List[PlcValue] = [] - for _ in range(item_count): - value.append(PlcBOOL(bool(read_buffer.read_bit("")))) + if data_type == UmasDataType.EBOOL and number_of_values == int(1): # BOOL - return PlcList(value) + # Reserved Field (Compartmentalized so the "reserved" variable can't leak) + reserved: int = read_buffer.read_unsigned_short(7, logical_name="") + if reserved != int(0x0000): + logging.warning( + "Expected constant value " + + str(0x0000) + + " but got " + + str(reserved) + + " for reserved field." + ) + + # Simple Field (value) + value: bool = read_buffer.read_bit("") + + return PlcBOOL(value) if data_type == UmasDataType.BYTE and number_of_values == int(1): # BYTE # Simple Field (value) - value: int = read_buffer.read_unsigned_short(8, logical_name="") + value: int = read_buffer.read_byte("") return PlcBYTE(value) if data_type == UmasDataType.BYTE: # List # Array field (value) # Count array - item_count: int = int(number_of_values * int(8)) + item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcBOOL(bool(read_buffer.read_bit("")))) + value.append(PlcBYTE(int(read_buffer.read_byte("")))) return PlcList(value) if data_type == UmasDataType.WORD: # WORD @@ -200,6 +214,100 @@ def static_parse( ) return PlcList(value) + if data_type == UmasDataType.TIME and number_of_values == int(1): # TIME + + # Simple Field (value) + value: int = read_buffer.read_unsigned_long(32, logical_name="") + + return PlcTIME(value) + if data_type == UmasDataType.TIME: # List + # Array field (value) + # Count array + item_count: int = int(number_of_values) + value: List[PlcValue] = [] + for _ in range(item_count): + value.append( + PlcULINT(int(read_buffer.read_unsigned_long(32, logical_name=""))) + ) + + return PlcList(value) + if data_type == UmasDataType.DATE and number_of_values == int(1): # DATE + + # Simple Field (day) + day: int = read_buffer.read_unsigned_short( + 8, logical_name="", encoding="BCD" + ) + + # Simple Field (month) + month: int = read_buffer.read_unsigned_short( + 8, logical_name="", encoding="BCD" + ) + + # Simple Field (year) + year: int = read_buffer.read_unsigned_int( + 16, logical_name="", encoding="BCD" + ) + + value: datetime = datetime.datetime(int(year), int(month), int(day)) + return PlcDATE(value) + if data_type == UmasDataType.TOD and number_of_values == int(1): # TIME_OF_DAY + + # Simple Field (value) + value: int = read_buffer.read_unsigned_long(32, logical_name="") + + return PlcTIME_OF_DAY(value) + if data_type == UmasDataType.TOD: # List + # Array field (value) + # Count array + item_count: int = int(number_of_values) + value: List[PlcValue] = [] + for _ in range(item_count): + value.append( + PlcULINT(int(read_buffer.read_unsigned_long(32, logical_name=""))) + ) + + return PlcList(value) + if data_type == UmasDataType.DATE_AND_TIME and number_of_values == int( + 1 + ): # DATE_AND_TIME + + # Simple Field (unused) + unused: int = read_buffer.read_unsigned_short(8, logical_name="") + + # Simple Field (seconds) + seconds: int = read_buffer.read_unsigned_short( + 8, logical_name="", encoding="BCD" + ) + + # Simple Field (minutes) + minutes: int = read_buffer.read_unsigned_short( + 8, logical_name="", encoding="BCD" + ) + + # Simple Field (hour) + hour: int = read_buffer.read_unsigned_short( + 8, logical_name="", encoding="BCD" + ) + + # Simple Field (day) + day: int = read_buffer.read_unsigned_short( + 8, logical_name="", encoding="BCD" + ) + + # Simple Field (month) + month: int = read_buffer.read_unsigned_short( + 8, logical_name="", encoding="BCD" + ) + + # Simple Field (year) + year: int = read_buffer.read_unsigned_int( + 16, logical_name="", encoding="BCD" + ) + + value: datetime = datetime.datetime( + int(year), int(month), int(day), int(hour), int(minutes), int(seconds) + ) + return PlcDATE_AND_TIME(value) return None @staticmethod @@ -217,11 +325,12 @@ def static_serialize( value: bool = _value.get_bool() write_buffer.write_bit((value), "value") - elif data_type == UmasDataType.BOOL: # List - values: PlcList = cast(PlcList, _value) - for val in values.get_list(): - value: bool = val.get_bool() - write_buffer.write_bit((value), "value") + elif data_type == UmasDataType.EBOOL and number_of_values == int(1): # BOOL + # Reserved Field + write_buffer.write_byte(int(0x0000), 7, "int0x0000") + # Simple Field (value) + value: bool = _value.get_bool() + write_buffer.write_bit((value), "value") elif data_type == UmasDataType.BYTE and number_of_values == int(1): # BYTE # Simple Field (value) @@ -231,8 +340,8 @@ def static_serialize( elif data_type == UmasDataType.BYTE: # List values: PlcList = cast(PlcList, _value) for val in values.get_list(): - value: bool = val.get_bool() - write_buffer.write_bit((value), "value") + value: List[int] = val.get_raw() + write_buffer.write_byte(value, 8, "") elif data_type == UmasDataType.WORD: # WORD # Simple Field (value) @@ -301,13 +410,84 @@ def static_serialize( elif data_type == UmasDataType.STRING and number_of_values == int(1): # STRING # Manual Field (value) - serialize_terminated_string(write_buffer, self.value, self.number_of_values) + value: PlcValue = _value + StaticHelper.serialize_terminated_string( + write_buffer, value, number_of_values + ) elif data_type == UmasDataType.STRING: # List values: PlcList = cast(PlcList, _value) for val in values.get_list(): value: float = val.get_float() write_buffer.write_float((value), 32, "value") + elif data_type == UmasDataType.TIME and number_of_values == int(1): # TIME + # Simple Field (value) + value: int = _value.get_int() + write_buffer.write_unsigned_int((value), 32, "value") + + elif data_type == UmasDataType.TIME: # List + values: PlcList = cast(PlcList, _value) + for val in values.get_list(): + value: int = val.get_int() + write_buffer.write_unsigned_int((value), 32, "value") + + elif data_type == UmasDataType.DATE and number_of_values == int(1): # DATE + # Simple Field (day) + day: int = 0 + write_buffer.write_byte((day), 8, "day") + + # Simple Field (month) + month: int = 0 + write_buffer.write_byte((month), 8, "month") + + # Simple Field (year) + year: int = 0 + write_buffer.write_unsigned_short((year), 16, "year") + + elif data_type == UmasDataType.TOD and number_of_values == int( + 1 + ): # TIME_OF_DAY + # Simple Field (value) + value: int = _value.get_int() + write_buffer.write_unsigned_int((value), 32, "value") + + elif data_type == UmasDataType.TOD: # List + values: PlcList = cast(PlcList, _value) + for val in values.get_list(): + value: int = val.get_int() + write_buffer.write_unsigned_int((value), 32, "value") + + elif data_type == UmasDataType.DATE_AND_TIME and number_of_values == int( + 1 + ): # DATE_AND_TIME + # Simple Field (unused) + unused: int = 0 + write_buffer.write_byte((unused), 8, "unused") + + # Simple Field (seconds) + seconds: int = 0 + write_buffer.write_byte((seconds), 8, "seconds") + + # Simple Field (minutes) + minutes: int = 0 + write_buffer.write_byte((minutes), 8, "minutes") + + # Simple Field (hour) + hour: int = 0 + write_buffer.write_byte((hour), 8, "hour") + + # Simple Field (day) + day: int = 0 + write_buffer.write_byte((day), 8, "day") + + # Simple Field (month) + month: int = 0 + write_buffer.write_byte((month), 8, "month") + + # Simple Field (year) + year: int = 0 + write_buffer.write_unsigned_short((year), 16, "year") + @staticmethod def get_length_in_bytes( _value: PlcValue, data_type: UmasDataType, number_of_values: int @@ -329,15 +509,17 @@ def get_length_in_bits( size_in_bits += 7 # Simple Field (value) size_in_bits += 1 - elif data_type == UmasDataType.BOOL: # List - values: PlcList = cast(PlcList, _value) - size_in_bits += len(values.get_list()) * 1 + elif data_type == UmasDataType.EBOOL and number_of_values == int(1): # BOOL + # Reserved Field + size_in_bits += 7 + # Simple Field (value) + size_in_bits += 1 elif data_type == UmasDataType.BYTE and number_of_values == int(1): # BYTE # Simple Field (value) size_in_bits += 8 elif data_type == UmasDataType.BYTE: # List values: PlcList = cast(PlcList, _value) - size_in_bits += len(values.get_list()) * 1 + size_in_bits += len(values.get_list()) * 8 elif data_type == UmasDataType.WORD: # WORD # Simple Field (value) size_in_bits += 16 @@ -376,9 +558,47 @@ def get_length_in_bits( size_in_bits += len(values.get_list()) * 32 elif data_type == UmasDataType.STRING and number_of_values == int(1): # STRING # Manual Field (value) - size_in_bits += self.number_of_values * int(8) + size_in_bits += number_of_values * int(8) elif data_type == UmasDataType.STRING: # List values: PlcList = cast(PlcList, _value) size_in_bits += len(values.get_list()) * 32 + elif data_type == UmasDataType.TIME and number_of_values == int(1): # TIME + # Simple Field (value) + size_in_bits += 32 + elif data_type == UmasDataType.TIME: # List + values: PlcList = cast(PlcList, _value) + size_in_bits += len(values.get_list()) * 32 + elif data_type == UmasDataType.DATE and number_of_values == int(1): # DATE + # Simple Field (day) + size_in_bits += 8 + # Simple Field (month) + size_in_bits += 8 + # Simple Field (year) + size_in_bits += 16 + elif data_type == UmasDataType.TOD and number_of_values == int( + 1 + ): # TIME_OF_DAY + # Simple Field (value) + size_in_bits += 32 + elif data_type == UmasDataType.TOD: # List + values: PlcList = cast(PlcList, _value) + size_in_bits += len(values.get_list()) * 32 + elif data_type == UmasDataType.DATE_AND_TIME and number_of_values == int( + 1 + ): # DATE_AND_TIME + # Simple Field (unused) + size_in_bits += 8 + # Simple Field (seconds) + size_in_bits += 8 + # Simple Field (minutes) + size_in_bits += 8 + # Simple Field (hour) + size_in_bits += 8 + # Simple Field (day) + size_in_bits += 8 + # Simple Field (month) + size_in_bits += 8 + # Simple Field (year) + size_in_bits += 16 return size_in_bits diff --git a/plc4py/plc4py/protocols/umas/readwrite/UmasDataType.py b/plc4py/plc4py/protocols/umas/readwrite/UmasDataType.py index 69fcc90f31c..81366a22521 100644 --- a/plc4py/plc4py/protocols/umas/readwrite/UmasDataType.py +++ b/plc4py/plc4py/protocols/umas/readwrite/UmasDataType.py @@ -22,29 +22,29 @@ class UmasDataType(AutoNumberEnum): - _init_ = "value, request_size, data_type_size" - BOOL = (1, int(1), int(1)) - UNKNOWN2 = (2, int(1), int(1)) - UNKNOWN3 = (3, int(1), int(1)) - INT = (4, int(2), int(2)) - UINT = (5, int(2), int(2)) - DINT = (6, int(3), int(4)) - UDINT = (7, int(3), int(4)) - REAL = (8, int(3), int(4)) - STRING = (9, int(17), int(1)) - TIME = (10, int(3), int(4)) - UNKNOWN11 = (11, int(1), int(1)) - UNKNOWN12 = (12, int(1), int(1)) - UNKNOWN13 = (13, int(1), int(1)) - DATE = (14, int(3), int(4)) - TOD = (15, int(3), int(4)) - DT = (16, int(3), int(4)) - UNKNOWN17 = (17, int(1), int(1)) - UNKNOWN18 = (18, int(1), int(1)) - UNKNOWN19 = (19, int(1), int(1)) - UNKNOWN20 = (20, int(1), int(1)) - BYTE = (21, int(1), int(1)) - WORD = (22, int(2), int(2)) - DWORD = (23, int(3), int(4)) - UNKNOWN24 = (24, int(1), int(1)) - EBOOL = (25, int(1), int(1)) + _init_ = "value, data_type_conversion, request_size, data_type_size" + BOOL = (1, str("BOOL"), int(1), int(1)) + UNKNOWN2 = (2, str("BOOL"), int(1), int(1)) + UNKNOWN3 = (3, str("BOOL"), int(1), int(1)) + INT = (4, str("INT"), int(2), int(2)) + UINT = (5, str("UINT"), int(2), int(2)) + DINT = (6, str("DINT"), int(3), int(4)) + UDINT = (7, str("UDINT"), int(3), int(4)) + REAL = (8, str("REAL"), int(3), int(4)) + STRING = (9, str("STRING"), int(17), int(1)) + TIME = (10, str("TIME"), int(3), int(4)) + UNKNOWN11 = (11, str("BYTE"), int(1), int(1)) + UNKNOWN12 = (12, str("BYTE"), int(1), int(1)) + UNKNOWN13 = (13, str("BYTE"), int(1), int(1)) + DATE = (14, str("DATE"), int(3), int(4)) + TOD = (15, str("TIME_OF_DAY"), int(3), int(4)) + DATE_AND_TIME = (16, str("DATE_AND_TIME"), int(4), int(8)) + UNKNOWN17 = (17, str("BYTE"), int(1), int(1)) + UNKNOWN18 = (18, str("BYTE"), int(1), int(1)) + UNKNOWN19 = (19, str("BYTE"), int(1), int(1)) + UNKNOWN20 = (20, str("BYTE"), int(1), int(1)) + BYTE = (21, str("BYTE"), int(1), int(1)) + WORD = (22, str("WORD"), int(2), int(2)) + DWORD = (23, str("DWORD"), int(3), int(4)) + UNKNOWN24 = (24, str("BYTE"), int(1), int(1)) + EBOOL = (25, str("BOOL"), int(1), int(1)) diff --git a/plc4py/plc4py/protocols/umas/readwrite/UmasPDUReadUnlocatedVariableNames.py b/plc4py/plc4py/protocols/umas/readwrite/UmasPDUReadUnlocatedVariableNames.py deleted file mode 100644 index 3ab7e8882f9..00000000000 --- a/plc4py/plc4py/protocols/umas/readwrite/UmasPDUReadUnlocatedVariableNames.py +++ /dev/null @@ -1,153 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -import math -from dataclasses import dataclass -from typing import Any, ClassVar, List - -from plc4py.api.exceptions.exceptions import ( - PlcRuntimeException, - SerializationException, -) -from plc4py.api.messages.PlcMessage import PlcMessage -from plc4py.protocols.umas.readwrite.UmasPDUItem import UmasPDUItem -from plc4py.protocols.umas.readwrite.UmasUnlocatedVariableReference import ( - UmasUnlocatedVariableReference, -) -from plc4py.spi.generation.ReadBuffer import ReadBuffer -from plc4py.spi.generation.WriteBuffer import WriteBuffer -from plc4py.utils.GenericTypes import ByteOrder - - -@dataclass -class UmasPDUReadUnlocatedVariableNames(UmasPDUItem): - range: int - no_of_records: int - records: List[UmasUnlocatedVariableReference] - # Accessors for discriminator values. - umas_function_key: ClassVar[int] = 0xFE - umas_request_function_key: ClassVar[int] = 0x26 - - def serialize_umas_pdu_item_child(self, write_buffer: WriteBuffer): - write_buffer.push_context("UmasPDUReadUnlocatedVariableNames") - - # Simple Field (range) - write_buffer.write_unsigned_int(self.range, logical_name="range") - - # Simple Field (noOfRecords) - write_buffer.write_unsigned_int(self.no_of_records, logical_name="noOfRecords") - - # Array Field (records) - write_buffer.write_complex_array(self.records, logical_name="records") - - write_buffer.pop_context("UmasPDUReadUnlocatedVariableNames") - - def length_in_bytes(self) -> int: - return int(math.ceil(float(self.length_in_bits() / 8.0))) - - def length_in_bits(self) -> int: - length_in_bits: int = super().length_in_bits() - _value: UmasPDUReadUnlocatedVariableNames = self - - # Simple field (range) - length_in_bits += 32 - - # Simple field (noOfRecords) - length_in_bits += 32 - - # Array field - if self.records is not None: - for element in self.records: - length_in_bits += element.length_in_bits() - - return length_in_bits - - @staticmethod - def static_parse_builder(read_buffer: ReadBuffer, umas_request_function_key: int): - read_buffer.push_context("UmasPDUReadUnlocatedVariableNames") - - range: int = read_buffer.read_unsigned_int( - logical_name="range", - bit_length=32, - byte_order=ByteOrder.LITTLE_ENDIAN, - umas_request_function_key=umas_request_function_key, - ) - - no_of_records: int = read_buffer.read_unsigned_int( - logical_name="noOfRecords", - bit_length=32, - byte_order=ByteOrder.LITTLE_ENDIAN, - umas_request_function_key=umas_request_function_key, - ) - - records: List[Any] = read_buffer.read_array_field( - logical_name="records", - read_function=UmasUnlocatedVariableReference.static_parse, - count=no_of_records, - byte_order=ByteOrder.LITTLE_ENDIAN, - umas_request_function_key=umas_request_function_key, - ) - - read_buffer.pop_context("UmasPDUReadUnlocatedVariableNames") - # Create the instance - return UmasPDUReadUnlocatedVariableNamesBuilder(range, no_of_records, records) - - def equals(self, o: object) -> bool: - if self == o: - return True - - if not isinstance(o, UmasPDUReadUnlocatedVariableNames): - return False - - that: UmasPDUReadUnlocatedVariableNames = UmasPDUReadUnlocatedVariableNames(o) - return ( - (self.range == that.range) - and (self.no_of_records == that.no_of_records) - and (self.records == that.records) - and super().equals(that) - and True - ) - - def hash_code(self) -> int: - return hash(self) - - 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) - - # return "\n" + str(write_buffer_box_based.get_box()) + "\n" - - -@dataclass -class UmasPDUReadUnlocatedVariableNamesBuilder: - range: int - no_of_records: int - records: List[UmasUnlocatedVariableReference] - - def build(self, pairing_key) -> UmasPDUReadUnlocatedVariableNames: - umas_pdu_read_unlocated_variable_names: UmasPDUReadUnlocatedVariableNames = ( - UmasPDUReadUnlocatedVariableNames( - pairing_key, self.range, self.no_of_records, self.records - ) - ) - return umas_pdu_read_unlocated_variable_names diff --git a/plc4py/plc4py/protocols/umas/readwrite/UmasUnlocatedVariableReference.py b/plc4py/plc4py/protocols/umas/readwrite/UmasUnlocatedVariableReference.py index 9a7874957f2..00f1f9bdbad 100644 --- a/plc4py/plc4py/protocols/umas/readwrite/UmasUnlocatedVariableReference.py +++ b/plc4py/plc4py/protocols/umas/readwrite/UmasUnlocatedVariableReference.py @@ -31,10 +31,8 @@ @dataclass class UmasUnlocatedVariableReference: data_type: int - unknown1: int block: int offset: int - base_offset: int unknown4: int string_length: int value: str @@ -43,13 +41,8 @@ def serialize(self, write_buffer: WriteBuffer): write_buffer.push_context("UmasUnlocatedVariableReference") # Simple Field (dataType) - write_buffer.write_unsigned_byte( - self.data_type, bit_length=8, logical_name="dataType" - ) - - # Simple Field (unknown1) - write_buffer.write_unsigned_byte( - self.unknown1, bit_length=8, logical_name="unknown1" + write_buffer.write_unsigned_short( + self.data_type, bit_length=16, logical_name="dataType" ) # Simple Field (block) @@ -58,13 +51,8 @@ def serialize(self, write_buffer: WriteBuffer): ) # Simple Field (offset) - write_buffer.write_unsigned_byte( - self.offset, bit_length=8, logical_name="offset" - ) - - # Simple Field (baseOffset) - write_buffer.write_unsigned_byte( - self.base_offset, bit_length=8, logical_name="baseOffset" + write_buffer.write_unsigned_short( + self.offset, bit_length=16, logical_name="offset" ) # Simple Field (unknown4) @@ -94,19 +82,13 @@ def length_in_bits(self) -> int: _value: UmasUnlocatedVariableReference = self # Simple field (dataType) - length_in_bits += 8 - - # Simple field (unknown1) - length_in_bits += 8 + length_in_bits += 16 # Simple field (block) length_in_bits += 16 # Simple field (offset) - length_in_bits += 8 - - # Simple field (baseOffset) - length_in_bits += 8 + length_in_bits += 16 # Simple field (unknown4) length_in_bits += 16 @@ -127,24 +109,16 @@ def static_parse(read_buffer: ReadBuffer, **kwargs): def static_parse_context(read_buffer: ReadBuffer): read_buffer.push_context("UmasUnlocatedVariableReference") - data_type: int = read_buffer.read_unsigned_byte( - logical_name="data_type", bit_length=8 - ) - - unknown1: int = read_buffer.read_unsigned_byte( - logical_name="unknown1", bit_length=8 + data_type: int = read_buffer.read_unsigned_short( + logical_name="data_type", bit_length=16 ) block: int = read_buffer.read_unsigned_short( logical_name="block", bit_length=16 ) - offset: int = read_buffer.read_unsigned_byte( - logical_name="offset", bit_length=8 - ) - - base_offset: int = read_buffer.read_unsigned_byte( - logical_name="base_offset", bit_length=8 + offset: int = read_buffer.read_unsigned_short( + logical_name="offset", bit_length=16 ) unknown4: int = read_buffer.read_unsigned_short( @@ -166,14 +140,7 @@ def static_parse_context(read_buffer: ReadBuffer): # Create the instance _umas_unlocated_variable_reference: UmasUnlocatedVariableReference = ( UmasUnlocatedVariableReference( - data_type, - unknown1, - block, - offset, - base_offset, - unknown4, - string_length, - value, + data_type, block, offset, unknown4, string_length, value ) ) return _umas_unlocated_variable_reference @@ -188,10 +155,8 @@ def equals(self, o: object) -> bool: that: UmasUnlocatedVariableReference = UmasUnlocatedVariableReference(o) return ( (self.data_type == that.data_type) - and (self.unknown1 == that.unknown1) and (self.block == that.block) and (self.offset == that.offset) - and (self.base_offset == that.base_offset) and (self.unknown4 == that.unknown4) and (self.string_length == that.string_length) and (self.value == that.value) diff --git a/plc4py/plc4py/protocols/umas/readwrite/UmasVariableBlock.py b/plc4py/plc4py/protocols/umas/readwrite/UmasVariableBlock.py deleted file mode 100644 index 5a314a823d9..00000000000 --- a/plc4py/plc4py/protocols/umas/readwrite/UmasVariableBlock.py +++ /dev/null @@ -1,149 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -import math -from abc import ABC, abstractmethod -from dataclasses import dataclass - -from plc4py.api.exceptions.exceptions import ( - ParseException, - PlcRuntimeException, - SerializationException, -) -from plc4py.api.messages.PlcMessage import PlcMessage -from plc4py.spi.generation.ReadBuffer import ReadBuffer -from plc4py.spi.generation.WriteBuffer import WriteBuffer - - -@dataclass -class UmasVariableBlock(ABC, PlcMessage): - # Abstract accessors for discriminator values. - @property - def record_format(self) -> int: - pass - - @abstractmethod - def serialize_umas_variable_block_child(self, write_buffer: WriteBuffer) -> None: - pass - - def serialize(self, write_buffer: WriteBuffer): - write_buffer.push_context("UmasVariableBlock") - - # Switch field (Serialize the sub-type) - self.serialize_umas_variable_block_child(write_buffer) - - write_buffer.pop_context("UmasVariableBlock") - - def length_in_bytes(self) -> int: - return int(math.ceil(float(self.length_in_bits() / 8.0))) - - def length_in_bits(self) -> int: - length_in_bits: int = 0 - _value: UmasVariableBlock = self - - # Length of subtype elements will be added by sub-type... - - return length_in_bits - - @staticmethod - def static_parse(read_buffer: ReadBuffer, **kwargs): - if kwargs is None: - raise PlcRuntimeException( - "Wrong number of arguments, expected 1, but got None" - ) - - record_format: int = 0 - if isinstance(kwargs.get("record_format"), int): - record_format = int(kwargs.get("record_format")) - elif isinstance(kwargs.get("record_format"), str): - record_format = int(str(kwargs.get("record_format"))) - else: - raise PlcRuntimeException( - "Argument 0 expected to be of type int or a string which is parseable but was " - + kwargs.get("record_format").getClass().getName() - ) - - return UmasVariableBlock.static_parse_context(read_buffer, record_format) - - @staticmethod - def static_parse_context(read_buffer: ReadBuffer, record_format: int): - read_buffer.push_context("UmasVariableBlock") - - # Switch Field (Depending on the discriminator values, passes the instantiation to a sub-type) - builder: UmasVariableBlockBuilder = None - from plc4py.protocols.umas.readwrite.UmasPDUReadUnlocatedVariableNamesResponse import ( - UmasPDUReadUnlocatedVariableNamesResponse, - ) - - if record_format == int(0xDD02): - builder = UmasPDUReadUnlocatedVariableNamesResponse.static_parse_builder( - read_buffer, record_format - ) - from plc4py.protocols.umas.readwrite.UmasPDUReadDatatypeNamesResponse import ( - UmasPDUReadDatatypeNamesResponse, - ) - - if record_format == int(0xDD03): - builder = UmasPDUReadDatatypeNamesResponse.static_parse_builder( - read_buffer, record_format - ) - if builder is None: - raise ParseException( - "Unsupported case for discriminated type" - + " parameters [" - + "recordFormat=" - + str(record_format) - + "]" - ) - - read_buffer.pop_context("UmasVariableBlock") - # Create the instance - _umas_variable_block: UmasVariableBlock = builder.build() - return _umas_variable_block - - def equals(self, o: object) -> bool: - if self == o: - return True - - if not isinstance(o, UmasVariableBlock): - return False - - that: UmasVariableBlock = UmasVariableBlock(o) - return True - - def hash_code(self) -> int: - return hash(self) - - 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) - - # return "\n" + str(write_buffer_box_based.get_box()) + "\n" - - -@dataclass -class UmasVariableBlockBuilder: - def build( - self, - ) -> UmasVariableBlock: - pass diff --git a/plc4py/plc4py/protocols/umas/readwrite/VariableRequestReference.py b/plc4py/plc4py/protocols/umas/readwrite/VariableRequestReference.py deleted file mode 100644 index 7c43a744b87..00000000000 --- a/plc4py/plc4py/protocols/umas/readwrite/VariableRequestReference.py +++ /dev/null @@ -1,185 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -import math -from dataclasses import dataclass - -from plc4py.api.exceptions.exceptions import ( - PlcRuntimeException, - SerializationException, -) -from plc4py.api.messages.PlcMessage import PlcMessage -from plc4py.spi.generation.ReadBuffer import ReadBuffer -from plc4py.spi.generation.WriteBuffer import WriteBuffer - - -@dataclass -class VariableRequestReference: - is_array: int - data_size_index: int - block: int - base_offset: int - offset: int - array_length: int - UNKNOWN1: int = 0x01 - - def serialize(self, write_buffer: WriteBuffer): - write_buffer.push_context("VariableRequestReference") - - # Simple Field (isArray) - write_buffer.write_unsigned_byte( - self.is_array, bit_length=4, logical_name="isArray" - ) - - # Simple Field (dataSizeIndex) - write_buffer.write_unsigned_byte( - self.data_size_index, bit_length=4, logical_name="dataSizeIndex" - ) - - # Simple Field (block) - write_buffer.write_unsigned_short( - self.block, bit_length=16, logical_name="block" - ) - - # Const Field (unknown1) - write_buffer.write_unsigned_byte(self.UNKNOWN1, logical_name="unknown1") - - # Simple Field (baseOffset) - write_buffer.write_unsigned_short( - self.base_offset, bit_length=16, logical_name="baseOffset" - ) - - # Simple Field (offset) - write_buffer.write_unsigned_byte( - self.offset, bit_length=8, logical_name="offset" - ) - - # Optional Field (arrayLength) (Can be skipped, if the value is null) - write_buffer.write_unsigned_short(self.array_length, logical_name="arrayLength") - - write_buffer.pop_context("VariableRequestReference") - - def length_in_bytes(self) -> int: - return int(math.ceil(float(self.length_in_bits() / 8.0))) - - def length_in_bits(self) -> int: - length_in_bits: int = 0 - _value: VariableRequestReference = self - - # Simple field (isArray) - length_in_bits += 4 - - # Simple field (dataSizeIndex) - length_in_bits += 4 - - # Simple field (block) - length_in_bits += 16 - - # Const Field (unknown1) - length_in_bits += 8 - - # Simple field (baseOffset) - length_in_bits += 16 - - # Simple field (offset) - length_in_bits += 8 - - # Optional Field (arrayLength) - length_in_bits += 16 - - return length_in_bits - - @staticmethod - def static_parse(read_buffer: ReadBuffer, **kwargs): - return VariableRequestReference.static_parse_context(read_buffer) - - @staticmethod - def static_parse_context(read_buffer: ReadBuffer): - read_buffer.push_context("VariableRequestReference") - - is_array: int = read_buffer.read_unsigned_byte( - logical_name="isArray", bit_length=4 - ) - - data_size_index: int = read_buffer.read_unsigned_byte( - logical_name="dataSizeIndex", bit_length=4 - ) - - block: int = read_buffer.read_unsigned_short( - logical_name="block", bit_length=16 - ) - - UNKNOWN1: int = read_buffer.read_unsigned_byte(logical_name="unknown1") - - base_offset: int = read_buffer.read_unsigned_short( - logical_name="baseOffset", bit_length=16 - ) - - offset: int = read_buffer.read_unsigned_byte( - logical_name="offset", bit_length=8 - ) - - array_length: int = None - if self.is_array: - array_length = read_buffer.read_unsigned_short(logical_name="arrayLength") - - read_buffer.pop_context("VariableRequestReference") - # Create the instance - _variable_request_reference: VariableRequestReference = ( - VariableRequestReference( - is_array, - data_size_index, - block, - base_offset, - offset, - array_length, - ) - ) - return _variable_request_reference - - def equals(self, o: object) -> bool: - if self == o: - return True - - if not isinstance(o, VariableRequestReference): - return False - - that: VariableRequestReference = VariableRequestReference(o) - return ( - (self.is_array == that.is_array) - and (self.data_size_index == that.data_size_index) - and (self.block == that.block) - and (self.base_offset == that.base_offset) - and (self.offset == that.offset) - and (self.array_length == that.array_length) - and True - ) - - def hash_code(self) -> int: - return hash(self) - - 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) - - # return "\n" + str(write_buffer_box_based.get_box()) + "\n" diff --git a/plc4py/plc4py/protocols/umas/readwrite/VariableWriteRequestReference.py b/plc4py/plc4py/protocols/umas/readwrite/VariableWriteRequestReference.py index 055270c687b..30e34892d8d 100644 --- a/plc4py/plc4py/protocols/umas/readwrite/VariableWriteRequestReference.py +++ b/plc4py/plc4py/protocols/umas/readwrite/VariableWriteRequestReference.py @@ -38,7 +38,6 @@ class VariableWriteRequestReference: offset: int array_length: int record_data: List[int] - UNKNOWN1: int = 0x01 def serialize(self, write_buffer: WriteBuffer): write_buffer.push_context("VariableWriteRequestReference") @@ -58,17 +57,14 @@ def serialize(self, write_buffer: WriteBuffer): self.block, bit_length=16, logical_name="block" ) - # Const Field (unknown1) - write_buffer.write_unsigned_byte(self.UNKNOWN1, logical_name="unknown1") - # Simple Field (baseOffset) write_buffer.write_unsigned_short( self.base_offset, bit_length=16, logical_name="baseOffset" ) # Simple Field (offset) - write_buffer.write_unsigned_byte( - self.offset, bit_length=8, logical_name="offset" + write_buffer.write_unsigned_short( + self.offset, bit_length=16, logical_name="offset" ) # Optional Field (arrayLength) (Can be skipped, if the value is null) @@ -98,14 +94,11 @@ def length_in_bits(self) -> int: # Simple field (block) length_in_bits += 16 - # Const Field (unknown1) - length_in_bits += 8 - # Simple field (baseOffset) length_in_bits += 16 # Simple field (offset) - length_in_bits += 8 + length_in_bits += 16 # Optional Field (arrayLength) if self.is_array: @@ -137,14 +130,12 @@ def static_parse_context(read_buffer: ReadBuffer): logical_name="block", bit_length=16 ) - UNKNOWN1: int = read_buffer.read_unsigned_byte(logical_name="unknown1") - base_offset: int = read_buffer.read_unsigned_short( logical_name="base_offset", bit_length=16 ) - offset: int = read_buffer.read_unsigned_byte( - logical_name="offset", bit_length=8 + offset: int = read_buffer.read_unsigned_short( + logical_name="offset", bit_length=16 ) array_length: int = None diff --git a/plc4py/plc4py/spi/generation/ReadBuffer.py b/plc4py/plc4py/spi/generation/ReadBuffer.py index f250582b979..2a985cd7bad 100644 --- a/plc4py/plc4py/spi/generation/ReadBuffer.py +++ b/plc4py/plc4py/spi/generation/ReadBuffer.py @@ -24,7 +24,7 @@ from bitarray import bitarray from bitarray.util import ba2base, ba2int, zeros -from plc4py.api.exceptions.exceptions import SerializationException +from plc4py.api.exceptions.exceptions import SerializationException, ParseException from plc4py.api.messages.PlcMessage import PlcMessage from plc4py.utils.GenericTypes import ByteOrder, ByteOrderAware @@ -220,6 +220,7 @@ def read_unsigned_short( self, bit_length: int = 16, logical_name: str = "", **kwargs ) -> int: byte_order = kwargs.get("byte_order", self.byte_order) + encoding = kwargs.get("encoding", "") if bit_length <= 0: raise SerializationException("unsigned short must contain at least 1 bit") elif bit_length > 16: @@ -227,19 +228,41 @@ def read_unsigned_short( else: if byte_order == ByteOrder.LITTLE_ENDIAN: endian_string = "<" + padded = bitarray( + self.bb[self.position : self.position + bit_length] + ) + (16 - bit_length) * bitarray("0") else: endian_string = ">" - padded = (16 - bit_length) * bitarray("0") + bitarray( - self.bb[self.position : self.position + bit_length] - ) - result: int = struct.unpack(endian_string + "H", padded)[0] - self.position += bit_length - return result + padded = (16 - bit_length) * bitarray("0") + bitarray( + self.bb[self.position : self.position + bit_length] + ) + + if encoding == "BCD": + if bit_length % 4 != 0: + raise ParseException( + "'BCD' encoded fields must have a length that is a multiple of 4 bits long" + ) + result: int = 0 + for i in range(0, bit_length, 4): + digit: int = ba2int(padded[i : i + 4]) + if digit > 9: + raise ParseException( + "'BCD' encoded value is not a correctly encoded BCD value" + ) + multiplier = 10 ** ((int(bit_length - i) / 4) - 1) + result += multiplier * digit + self.position += bit_length + return result + else: + result: int = struct.unpack(endian_string + "H", padded)[0] + self.position += bit_length + return result def read_unsigned_int( self, bit_length: int = 32, logical_name: str = "", **kwargs ) -> int: byte_order = kwargs.get("byte_order", self.byte_order) + encoding = kwargs.get("encoding", "") if bit_length <= 0: raise SerializationException("unsigned int must contain at least 1 bit") elif bit_length > 32: @@ -247,14 +270,36 @@ def read_unsigned_int( else: if byte_order == ByteOrder.LITTLE_ENDIAN: endian_string = "<" + padded = bitarray( + self.bb[self.position : self.position + bit_length] + ) + (32 - bit_length) * bitarray("0") else: endian_string = ">" - padded = (32 - bit_length) * bitarray("0") + bitarray( - self.bb[self.position : self.position + bit_length] - ) - result: int = struct.unpack(endian_string + "I", padded)[0] - self.position += bit_length - return result + padded = (32 - bit_length) * bitarray("0") + bitarray( + self.bb[self.position : self.position + bit_length] + ) + if encoding == "BCD": + if bit_length % 4 != 0: + raise ParseException( + "'BCD' encoded fields must have a length that is a multiple of 4 bits long" + ) + if byte_order == ByteOrder.LITTLE_ENDIAN: + padded = padded[8:16] + padded[:8] + padded[24:32] + padded[16:24] + result: int = 0 + for i in range(0, bit_length, 4): + digit: int = ba2int(padded[i : i + 4]) + if digit > 9: + raise ParseException( + "'BCD' encoded value is not a correctly encoded BCD value" + ) + multiplier = 10 ** ((int(bit_length - i) / 4) - 1) + result += multiplier * digit + self.position += bit_length + return result + else: + result: int = struct.unpack(endian_string + "I", padded)[0] + self.position += bit_length + return result def read_unsigned_long( self, bit_length: int = 64, logical_name: str = "", **kwargs @@ -267,11 +312,14 @@ def read_unsigned_long( else: if byte_order == ByteOrder.LITTLE_ENDIAN: endian_string = "<" + padded = bitarray( + self.bb[self.position : self.position + bit_length] + ) + (64 - bit_length) * bitarray("0") else: endian_string = ">" - padded = (64 - bit_length) * bitarray("0") + bitarray( - self.bb[self.position : self.position + bit_length] - ) + padded = (64 - bit_length) * bitarray("0") + bitarray( + self.bb[self.position : self.position + bit_length] + ) result: int = struct.unpack(endian_string + "Q", padded)[0] self.position += bit_length return result @@ -301,11 +349,14 @@ def read_short(self, bit_length: int = 16, logical_name: str = "", **kwargs) -> else: if byte_order == ByteOrder.LITTLE_ENDIAN: endian_string = "<" + padded = bitarray( + self.bb[self.position : self.position + bit_length] + ) + (16 - bit_length) * bitarray("0") else: endian_string = ">" - padded = (16 - bit_length) * bitarray("0") + bitarray( - self.bb[self.position : self.position + bit_length] - ) + padded = (16 - bit_length) * bitarray("0") + bitarray( + self.bb[self.position : self.position + bit_length] + ) result: int = struct.unpack(endian_string + "h", padded)[0] self.position += bit_length return result @@ -319,11 +370,14 @@ def read_int(self, bit_length: int = 32, logical_name: str = "", **kwargs) -> in else: if byte_order == ByteOrder.LITTLE_ENDIAN: endian_string = "<" + padded = bitarray( + self.bb[self.position : self.position + bit_length] + ) + (32 - bit_length) * bitarray("0") else: endian_string = ">" - padded = (32 - bit_length) * bitarray("0") + bitarray( - self.bb[self.position : self.position + bit_length] - ) + padded = (32 - bit_length) * bitarray("0") + bitarray( + self.bb[self.position : self.position + bit_length] + ) if ( byte_order == ByteOrder.BIG_ENDIAN_BYTE_SWAP or byte_order == ByteOrder.LITTLE_ENDIAN_BYTE_SWAP @@ -342,11 +396,14 @@ def read_long(self, bit_length: int = 64, logical_name: str = "", **kwargs) -> i else: if byte_order == ByteOrder.LITTLE_ENDIAN: endian_string = "<" + padded = bitarray( + self.bb[self.position : self.position + bit_length] + ) + (64 - bit_length) * bitarray("0") else: endian_string = ">" - padded = (64 - bit_length) * bitarray("0") + bitarray( - self.bb[self.position : self.position + bit_length] - ) + padded = (64 - bit_length) * bitarray("0") + bitarray( + self.bb[self.position : self.position + bit_length] + ) if ( byte_order == ByteOrder.BIG_ENDIAN_BYTE_SWAP or byte_order == ByteOrder.LITTLE_ENDIAN_BYTE_SWAP diff --git a/plc4py/plc4py/spi/generation/WriteBuffer.py b/plc4py/plc4py/spi/generation/WriteBuffer.py index b6519f5ee40..2a5e0ec145b 100644 --- a/plc4py/plc4py/spi/generation/WriteBuffer.py +++ b/plc4py/plc4py/spi/generation/WriteBuffer.py @@ -195,8 +195,10 @@ def write_bit(self, value: bool, logical_name: str = "", **kwargs) -> None: self.bb[self.position] = value self.position += 1 - def write_byte(self, value: int, logical_name: str = "", **kwargs) -> None: - self.write_unsigned_byte(value, 8, logical_name, **kwargs) + def write_byte( + self, value: int, bit_length: int = 8, logical_name: str = "", **kwargs + ) -> None: + self.write_unsigned_byte(value, bit_length, logical_name, **kwargs) def write_byte_array( self, value: List[int], logical_name: str = "", **kwargs @@ -346,6 +348,20 @@ def write_double( raise SerializationException("Double can only contain max 64 bits") self._handle_numeric_encoding(value, bit_length, numeric_format="d", **kwargs) + def write_str( + self, + value: str, + bit_length: int = -1, + logical_name: str = "", + encoding: str = "UTF-8", + **kwargs, + ) -> None: + bit_order = kwargs.get("bit_order", ByteOrder.BIG_ENDIAN) + src = bitarray(endian=ByteOrder.get_short_name(bit_order)) + src.frombytes(value.encode(encoding)) + self.bb[self.position : self.position + bit_length] = src[:bit_length] + self.position += bit_length + def write_complex_array( self, value: List[PlcMessage], logical_name: str = "", **kwargs ) -> None: @@ -376,10 +392,12 @@ def _handle_numeric_encoding(self, value, bit_length: int, **kwargs): endianness = "<" if not isinstance(value, int): pass + result: bytes = struct.pack( endianness + numeric_format, value, ) + src.frombytes(result) if ( byte_order == ByteOrder.BIG_ENDIAN_BYTE_SWAP diff --git a/plc4py/plc4py/spi/messages/PlcWriter.py b/plc4py/plc4py/spi/messages/PlcWriter.py index f3d943c5fd7..fc9c36b56db 100644 --- a/plc4py/plc4py/spi/messages/PlcWriter.py +++ b/plc4py/plc4py/spi/messages/PlcWriter.py @@ -86,7 +86,7 @@ async def _write(self, request: PlcWriteRequest) -> PlcWriteResponse: # Send the write request to the device and wait for a response logging.debug("Sending write request to Device") response = await asyncio.wait_for( - self._device.write(request, self._transport), 5 + self._device.write(request, self._transport), 15 ) # Return the response return response diff --git a/plc4py/plc4py/spi/messages/utils/ResponseItem.py b/plc4py/plc4py/spi/messages/utils/ResponseItem.py index f62912773cf..ddf225d206e 100644 --- a/plc4py/plc4py/spi/messages/utils/ResponseItem.py +++ b/plc4py/plc4py/spi/messages/utils/ResponseItem.py @@ -17,9 +17,11 @@ # under the License. # from abc import ABC -from dataclasses import dataclass -from typing import Generic, TypeVar, Union +from dataclasses import dataclass, field +from typing import Generic, TypeVar, Union, Dict, List + +from plc4py.api.messages.PlcField import PlcTag from plc4py.api.messages.PlcResponse import PlcResponseCode from plc4py.api.value.PlcValue import PlcValue @@ -30,3 +32,27 @@ class ResponseItem(Generic[T], ABC): response_code: PlcResponseCode value: T + + +@dataclass +class ArrayInfo: + size: int + lower_bound: int + upper_bound: int + + +R = TypeVar("R", bound=Union[PlcTag, None]) + + +@dataclass +class PlcBrowseItem(Generic[R], ABC): + tag: R + name: str + readable: bool + writeable: bool + subscribable: bool + publishable: bool + array: bool + array_info: List[ArrayInfo] = field(default_factory=lambda: []) + children: Dict[str, "PlcBrowseItem"] = field(default_factory=lambda: {}) + options: Dict[str, PlcValue] = field(default_factory=lambda: {}) diff --git a/plc4py/plc4py/spi/values/PlcValues.py b/plc4py/plc4py/spi/values/PlcValues.py index 437c6a795f2..ed207cfb74d 100644 --- a/plc4py/plc4py/spi/values/PlcValues.py +++ b/plc4py/plc4py/spi/values/PlcValues.py @@ -17,9 +17,10 @@ # under the License. # from dataclasses import dataclass +from datetime import datetime from typing import Any, Dict, List -from ...api.value.PlcValue import PlcValue +from plc4py.api.value.PlcValue import PlcValue class PlcINT(PlcValue[int]): @@ -34,11 +35,11 @@ class PlcCHAR(PlcValue[str]): pass -class PlcDATE(PlcValue[int]): +class PlcDATE(PlcValue[datetime]): pass -class PlcDATE_AND_TIME(PlcValue[int]): +class PlcDATE_AND_TIME(PlcValue[datetime]): pass diff --git a/plc4py/plc4py/utils/ConnectionStringHandling.py b/plc4py/plc4py/utils/ConnectionStringHandling.py index c4954de32db..4968c5b7eaf 100644 --- a/plc4py/plc4py/utils/ConnectionStringHandling.py +++ b/plc4py/plc4py/utils/ConnectionStringHandling.py @@ -30,3 +30,11 @@ def get_protocol_code(url: str) -> str: """ parsed = urlparse(url) return parsed.scheme + + +def strtobool(value: str) -> bool: + """Credit goes to https://danielms.site/zet/2023/pythons-distutil-strtobool-replacement/""" + value = value.lower() + if value in ("y", "yes", "on", "1", "true", "t"): + return True + return False diff --git a/plc4py/pom.xml b/plc4py/pom.xml index a2133680572..4952326df9e 100644 --- a/plc4py/pom.xml +++ b/plc4py/pom.xml @@ -215,6 +215,22 @@ + + python-test-compile + process-sources + + exec + + + ${skipTests} + ${python.venv.bin}pip3 + + install + .[dev] + + + + python-black process-sources @@ -232,17 +248,19 @@ - python-test-compile + python-dependency-check test-compile exec - ${skipTests} - ${python.venv.bin}pip3 + ${python.venv.bin}${python.exe.bin} - install - .[dev] + -m + deptry + -ddg + dev + . diff --git a/plc4py/pyproject.toml b/plc4py/pyproject.toml new file mode 100644 index 00000000000..b58db1f1a81 --- /dev/null +++ b/plc4py/pyproject.toml @@ -0,0 +1,89 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +where = ["."] # list of folders that contain the packages (["."] by default) +include = ["plc4py*"] # package names should match these glob patterns (["*"] by default) +#exclude = ["my_package.tests*"] # exclude packages matching these glob patterns (empty by default) +namespaces = false # to disable scanning PEP 420 namespaces (true by default) + +[project] +name = "plc4py" +description = "Plc4Py The Python Industrial IOT Adapter" +version = "0.13" +readme = "README.md" +dependencies = [ + "aenum", + "bitarray", + "typing_extensions", + "pluggy", + "xsdata", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.8", + "Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator", +] +keywords=["modbus", "plc4x"] +authors=[ + {name='"Apache PLC4X <>"', email="dev@plc4x.apache.org"} +] + +[project.urls] +HomePage="https://plc4x.apache.org" +Repository="https://github.com/apache/plc4x" + +[project.optional-dependencies] +dev = [ + "pytest-asyncio>=0.18.3", + "pip-tools", + "black", + "pip", + "deptry", + "requires", + "pre-commit>=2.6.0", + "pytest-mock>=3.8.1", + "mock>=4.0.2", + "mypy>=0.942", + "flake8>=4.0.1", + "pytest-asyncio", + "xsdata", +] + +[project.entry-points."plc4py.drivers"] +mock = "plc4py.drivers.mock.MockConnection:MockDriverLoader" +modbus = "plc4py.drivers.modbus.ModbusConnection:ModbusDriverLoader" +umas = "plc4py.drivers.umas.UmasConnection:UmasDriverLoader" + +[project.entry-points."plc4py.transports"] +tcp = "plc4py.spi.transport.TCPTransport:TCPTransportLoader" +mock = "plc4py.spi.transport.MockTransport:MockTransportLoader" + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -q" +testpaths = [ + "tests", +] +asyncio_mode = "auto" +log_cli = "true" diff --git a/plc4py/pytest.ini b/plc4py/pytest.ini deleted file mode 100644 index 537563cdb10..00000000000 --- a/plc4py/pytest.ini +++ /dev/null @@ -1,21 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -[pytest] -log_cli = true \ No newline at end of file diff --git a/plc4py/requirements.txt b/plc4py/requirements.txt deleted file mode 100644 index 0b4e610f0af..00000000000 --- a/plc4py/requirements.txt +++ /dev/null @@ -1,54 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -aenum==3.1.15 -annotated-types==0.7.0 -bitarray==3.0.0 -black==24.10.0 -build==1.2.2.post1 -cfgv==3.4.0 -click==8.1.7 -coverage==7.6.4 -distlib==0.3.9 -filelock==3.16.1 -flake8==7.1.1 -funkify==0.4.5 -identify==2.6.1 -iniconfig==2.0.0 -mccabe==0.7.0 -mock==5.1.0 -mypy==1.13.0 -mypy-extensions==1.0.0 -nodeenv==1.9.1 -packaging==24.1 -pathspec==0.12.1 -pip-tools==7.4.1 -platformdirs==4.3.6 -pluggy==1.5.0 -pre-commit==4.0.1 -pycodestyle==2.12.1 -pyflakes==3.2.0 -pyproject_hooks==1.2.0 -pytest==8.3.3 -pytest-asyncio==0.24.0 -pytest-mock==3.14.0 -PyYAML==6.0.2 -requires==0.10.5 -typing_extensions==4.12.2 -virtualenv==20.27.0 -xtyping==0.8.2 diff --git a/plc4py/setup.cfg b/plc4py/setup.cfg deleted file mode 100644 index 812fb33cb26..00000000000 --- a/plc4py/setup.cfg +++ /dev/null @@ -1,22 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -[tool:pytest] -testpaths = tests -asyncio_mode=auto \ No newline at end of file diff --git a/plc4py/setup.py b/plc4py/setup.py deleted file mode 100644 index 9505736f533..00000000000 --- a/plc4py/setup.py +++ /dev/null @@ -1,74 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -from setuptools import setup, find_packages - -setup( - name="plc4py", - version="0.13", - description="Plc4py The Python Industrial IOT Adapter", - classifiers=[ - "Development Status :: 3 - Alpha", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.8", - "Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator", - ], - keywords="modbus plc4x", - url="https://plc4x.apache.org", - author='"Apache PLC4X <>"', - author_email="dev@plc4x.apache.org", - license="Apache 2.0", - packages=find_packages(include=["plc4py", "plc4py.*"]), - setup_requires=[ - "wheel", - ], - install_requires=[ - "pytest-asyncio>=0.18.3", - "pip-tools", - "black", - "pip", - "aenum", - "bitarray", - "typing_extensions", - "pluggy", - ], - extras_require={ - "dev": [ - "requires", - "pre-commit>=2.6.0", - "pytest-mock>=3.8.1", - "mock>=4.0.2", - "mypy>=0.942", - "flake8>=4.0.1", - "pytest-asyncio", - "xsdata", - ] - }, - entry_points={ - "plc4py.drivers": [ - "mock = plc4py.drivers.mock.MockConnection:MockDriverLoader", - "modbus = plc4py.drivers.modbus.ModbusConnection:ModbusDriverLoader", - "umas = plc4py.drivers.umas.UmasConnection:UmasDriverLoader", - ], - "plc4py.transports": [ - "tcp = plc4py.spi.transport.TCPTransport:TCPTransportLoader", - "mock = plc4py.spi.transport.MockTransport:MockTransportLoader", - ], - }, -) diff --git a/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py b/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py index f3d2ea13ad8..b957876e7f0 100644 --- a/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py +++ b/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py @@ -17,382 +17,170 @@ # under the License. # import time +from typing import AsyncGenerator from unittest import TestCase import pytest +import pytest_asyncio +from plc4py.api.PlcConnection import PlcConnection from plc4py.PlcDriverManager import PlcDriverManager from plc4py.api.value.PlcValue import PlcResponseCode import logging -from plc4py.spi.values.PlcValues import PlcINT, PlcREAL, PlcList +from plc4py.spi.values.PlcValues import PlcINT, PlcREAL, PlcList, PlcBOOL, PlcCHAR logger = logging.getLogger("testing") -TEST_SERVER_IP = "192.168.190.174" -@pytest.mark.asyncio -@pytest.mark.xfail -async def manual_test_plc_driver_modbus_connect(): - """ - Test the connection to a Modbus PLC using PlcDriverManager. - """ - # Initialize the PlcDriverManager +@pytest_asyncio.fixture +async def connection() -> AsyncGenerator[PlcConnection, None]: driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection(f"modbus://{TEST_SERVER_IP}") as connection: - # Check if the connection is successful - assert connection.is_connected() - - # Ensure the connection is closed after exiting the context manager - assert not connection.is_connected() + async with driver_manager.connection("modbus://127.0.0.1:502") as connection: + yield connection @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_coil(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) +async def test_plc_driver_modbus_connect(connection): + assert connection.is_connected - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "0x00001") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == True +async def write_read(connection, tag_name, tag_value): + tag_alias = "Some Random Alias" + with connection.write_request_builder() as builder: + builder.add_item(tag_alias, tag_name, tag_value) + write_request = builder.build() + with connection.read_request_builder() as builder: + builder.add_item(tag_alias, tag_name) + read_request = builder.build() + future = connection.execute(write_request) + response = await future + assert response.response_code == PlcResponseCode.OK + future = connection.execute(read_request) + response = await future + assert response.response_code == PlcResponseCode.OK -@pytest.mark.asyncio -@pytest.mark.xfail -async def test_plc_driver_modbus_read_coil_non_bool(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "0x00001:REAL") - request = builder.build() - TestCase.assertRaises( - await connection.execute(request), NotImplementedError - ) + value = response.tags[tag_alias].value + response_code = response.tags[tag_alias].response_code + assert value == tag_value + assert response_code == PlcResponseCode.OK @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_coil_array(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "0x00001[2]") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == [True, False] +async def test_plc_driver_modbus_read_boolean(connection): + tag_name = "0x00001" + await write_read(connection, tag_name, PlcBOOL(True)) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_contacts(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "1x00001") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == True +async def test_plc_driver_modbus_read_int(connection): + tag_name = "4x00001" + await write_read(connection, tag_name, PlcINT(83)) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_contact_array(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "1x00001[2]") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == [True, False] +async def test_plc_driver_modbus_read_real(connection): + tag_name = "4x00001" + await write_read(connection, tag_name, PlcINT(83)) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_input_register(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "3x00001") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == 333 +async def test_plc_driver_modbus_read_bool_array(connection): + tag_name = "0x00001[2]" + await write_read(connection, tag_name, PlcList([PlcBOOL(True), PlcBOOL(False)])) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_input_register_array(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "3x00001[2]") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == [333, 0] +async def test_plc_driver_modbus_read_contacts(connection): + tag_name = "1x00001" + await write_read(connection, tag_name, PlcBOOL(True)) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_holding(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "4x00001") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == 874 +async def test_plc_driver_modbus_read_bool_array_discrete_input(connection): + tag_name = "1x00001[2]" + await write_read(connection, tag_name, PlcList([PlcBOOL(True), PlcBOOL(False)])) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_holding(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "4x00001[2]") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == [874, 0] +async def test_plc_driver_modbus_read_input_register(connection): + tag_name = "3x00001" + await write_read(connection, tag_name, PlcINT(333)) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_holding_real(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502?byte_order=BIG_ENDIAN_BYTE_SWAP" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "4x00011:REAL[2]") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == [PlcREAL(value=874), PlcREAL(value=0.0)] +async def test_plc_driver_modbus_read_input_register_array(connection): + tag_name = "3x00001[2]" + await write_read(connection, tag_name, PlcList([PlcINT(333), PlcINT(0)])) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_holding_string_even(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "4x00041:CHAR[6]") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == [b"F", b"A", b"F", b"B", b"C", b"B"] +async def test_plc_driver_modbus_read_holding_register(connection): + tag_name = "4x00001" + await write_read(connection, tag_name, PlcINT(334)) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_read_holding_string_odd(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.read_request_builder() as builder: - builder.add_item("Random Tag", "4x00041:CHAR[5]") - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"].value - assert value == [b"F", b"A", b"F", b"B", b"C"] +async def test_plc_driver_modbus_read_holdiong_register_array(connection): + tag_name = "4x00001[2]" + await write_read(connection, tag_name, PlcList([PlcINT(334), PlcINT(0)])) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_write_holding_int(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.write_request_builder() as builder: - builder.add_item("Random Tag", "4x00001", PlcINT(874)) - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"] - assert value.response_code == PlcResponseCode.OK +async def test_plc_driver_modbus_read_holding_register_real(connection): + tag_name = "4x00011:REAL[2]" + await write_read( + connection, tag_name, PlcList([PlcREAL(value=874), PlcREAL(value=0.0)]) + ) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_write_holding_int_array(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502" - ) as connection: - with connection.write_request_builder() as builder: - builder.add_item( - "Random Tag", - "4x00001[5]", - PlcList([PlcINT(874), PlcINT(0), PlcINT(3), PlcINT(4), PlcINT(5)]), - ) - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"] - assert value.response_code == PlcResponseCode.OK +async def test_plc_driver_modbus_read_holding_string_even(connection): + tag_name = "4x00041:CHAR[6]" + await write_read( + connection, + tag_name, + PlcList( + [ + PlcCHAR(value=b"F"), + PlcCHAR(value=b"A"), + PlcCHAR(value=b"F"), + PlcCHAR(value=b"B"), + PlcCHAR(value=b"C"), + PlcCHAR(value=b"B"), + ] + ), + ) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_modbus_write_holding_real(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) - - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() - - # Establish a connection to the Modbus PLC - async with driver_manager.connection( - f"modbus://{TEST_SERVER_IP}:502?byte_order=BIG_ENDIAN_BYTE_SWAP" - ) as connection: - with connection.write_request_builder() as builder: - builder.add_item("Random Tag", "4x00011:REAL", PlcREAL(874)) - request = builder.build() - response = await connection.execute(request) - value = response.tags["Random Tag"] - assert value.response_code == PlcResponseCode.OK +async def test_plc_driver_modbus_read_holding_string_odd(connection): + tag_name = "4x00041:CHAR[5]" + await write_read( + connection, + tag_name, + PlcList( + [ + PlcCHAR(value=b"F"), + PlcCHAR(value=b"A"), + PlcCHAR(value=b"F"), + PlcCHAR(value=b"B"), + PlcCHAR(value=b"C"), + ] + ), + ) diff --git a/plc4py/tests/unit/plc4py/drivers/umas/test_umas_connection.py b/plc4py/tests/unit/plc4py/drivers/umas/test_umas_connection.py index 82a18f1f1fe..e03d4cdb9f6 100644 --- a/plc4py/tests/unit/plc4py/drivers/umas/test_umas_connection.py +++ b/plc4py/tests/unit/plc4py/drivers/umas/test_umas_connection.py @@ -17,50 +17,229 @@ # under the License. # import asyncio +import datetime import logging import time +from typing import AsyncGenerator import pytest +import pytest_asyncio +from plc4py.api.PlcConnection import PlcConnection +from plc4py.api.value.PlcValue import PlcResponseCode from plc4py.PlcDriverManager import PlcDriverManager +from plc4py.spi.values.PlcValues import ( + PlcBOOL, + PlcINT, + PlcREAL, + PlcDINT, + PlcSTRING, + PlcTIME, + PlcBYTE, + PlcDATE, + PlcDATE_AND_TIME, + PlcList, +) + + +@pytest_asyncio.fixture +async def connection() -> AsyncGenerator[PlcConnection, None]: + driver_manager = PlcDriverManager() + async with driver_manager.connection("umas://127.0.0.1:502") as connection: + yield connection @pytest.mark.asyncio @pytest.mark.xfail -async def manual_test_plc_driver_umas_connect(): - driver_manager = PlcDriverManager() - async with driver_manager.connection("umas://127.0.0.1:5555") as connection: - assert connection.is_connected() - assert not connection.is_connected() +async def test_plc_driver_umas_connect(connection): + assert connection.is_connected + + +async def write_read(connection, tag_name, tag_value): + tag_alias = "Some Random Alias" + with connection.write_request_builder() as builder: + builder.add_item(tag_alias, tag_name, tag_value) + write_request = builder.build() + with connection.read_request_builder() as builder: + builder.add_item(tag_alias, tag_name) + read_request = builder.build() + future = connection.execute(write_request) + response = await future + assert response.response_code == PlcResponseCode.OK + + future = connection.execute(read_request) + response = await future + assert response.response_code == PlcResponseCode.OK + + value = response.tags[tag_alias].value + response_code = response.tags[tag_alias].response_code + assert value == tag_value + assert response_code == PlcResponseCode.OK @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_umas_read(): - log = logging.getLogger(__name__) +async def test_plc_driver_umas_read_boolean(connection): + tag_name = "TESTING" + await write_read(connection, tag_name, PlcBOOL(True)) - driver_manager = PlcDriverManager() - async with driver_manager.connection("umas://192.168.190.174:502") as connection: - with connection.read_request_builder() as builder: - builder.add_item(f"Random Tag {1}", "blurbe:REAL") - request = builder.build() - future = connection.execute(request) - response = await future - value = response.tags["Random Tag 1"].value - assert value == 0.0 +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_boolean_with_data_type(connection): + tag_name = "TESTING:BOOL" + await write_read(connection, tag_name, PlcBOOL(True)) @pytest.mark.asyncio @pytest.mark.xfail -async def test_plc_driver_umas_browse(): - driver_manager = PlcDriverManager() - async with driver_manager.connection("umas://192.168.190.174:502") as connection: - with connection.browse_request_builder() as builder: - builder.add_query("All Tags", "*") - request = builder.build() - - future = connection.execute(request) - await future - response = future.result() - pass +async def test_plc_driver_umas_read_int(connection): + tag_name = "TESTING_INT" + await write_read(connection, tag_name, PlcINT(99)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_int_with_data_type(connection): + tag_name = "TESTING_INT:INT" + await write_read(connection, tag_name, PlcINT(99)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_dint(connection): + tag_name = "TESTING_DINT" + await write_read(connection, tag_name, PlcDINT(763539)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_dint_with_data_type(connection): + tag_name = "TESTING_DINT:DINT" + await write_read(connection, tag_name, PlcDINT(763539)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_ebool(connection): + tag_name = "TESTING_EBOOL" + await write_read(connection, tag_name, PlcBOOL(True)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_ebool_with_data_type(connection): + tag_name = "TESTING_EBOOL:BOOL" + await write_read(connection, tag_name, PlcBOOL(True)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_string(connection): + tag_name = "TESTING_STRING" + await write_read(connection, tag_name, PlcSTRING("Hello pyToddy!")) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_string_with_data_type(connection): + tag_name = "TESTING_STRING:STRING" + await write_read(connection, tag_name, PlcSTRING("Hello pyToddy!")) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_time(connection): + tag_name = "TESTING_TIME" + await write_read(connection, tag_name, PlcTIME(200000)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_time_with_data_type(connection): + tag_name = "TESTING_TIME:TIME" + await write_read(connection, tag_name, PlcTIME(200000)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_byte(connection): + tag_name = "TESTING_BYTE" + await write_read(connection, tag_name, PlcBYTE(253)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_byte_with_data_type(connection): + tag_name = "TESTING_BYTE:BYTE" + await write_read(connection, tag_name, PlcBYTE(253)) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_date(connection): + tag_name = "TESTING_DATE" + await write_read(connection, tag_name, PlcDATE(datetime.datetime(2024, 10, 25))) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_time_with_data_type(connection): + tag_name = "TESTING_DATE:DATE" + await write_read(connection, tag_name, PlcDATE(datetime.datetime(2025, 11, 22))) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_dt(connection): + tag_name = "TESTING_DT" + await write_read( + connection, tag_name, PlcDATE_AND_TIME(datetime.datetime(2000, 1, 10, 0, 40)) + ) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_dt_with_data_type(connection): + tag_alias = "Random Tag" + tag_name = "TESTING_DT:DATE_AND_TIME" + await write_read( + connection, tag_name, PlcDATE_AND_TIME(datetime.datetime(2002, 10, 31, 3, 38)) + ) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_read_array(connection): + tag_name = "TESTING_BYTE_ARRAY:BYTE[10]" + await write_read( + connection, + tag_name, + PlcList( + [ + PlcBYTE(1), + PlcBYTE(2), + PlcBYTE(3), + PlcBYTE(4), + PlcBYTE(5), + PlcBYTE(6), + PlcBYTE(7), + PlcBYTE(8), + PlcBYTE(9), + PlcBYTE(10), + ] + ), + ) + + +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_umas_browse(connection): + with connection.browse_request_builder() as builder: + builder.add_query("All Tags", "*") + request = builder.build() + + future = connection.execute(request) + response = await future + + pass diff --git a/protocols/umas/src/main/resources/protocols/umas/umas.mspec b/protocols/umas/src/main/resources/protocols/umas/umas.mspec index 1b5a32b0c12..cf65be4c060 100644 --- a/protocols/umas/src/main/resources/protocols/umas/umas.mspec +++ b/protocols/umas/src/main/resources/protocols/umas/umas.mspec @@ -196,19 +196,16 @@ [simple uint 4 isArray] [simple uint 4 dataSizeIndex] [simple uint 16 block] - [const uint 8 unknown1 0x01] [simple uint 16 baseOffset] - [simple uint 8 offset] + [simple uint 16 offset] [optional uint 16 arrayLength 'isArray'] - [array byte recordData length 'isArray == 1 ? dataSizeIndex * arrayLength : dataSizeIndex'] + [array byte recordData length 'isArray == 1 ? dataSizeIndex * arrayLength : dataSizeIndex'] ] [type UmasUnlocatedVariableReference - [simple uint 8 dataType] - [simple uint 8 unknown1] + [simple uint 16 dataType] [simple uint 16 block] - [simple uint 8 offset] - [simple uint 8 baseOffset] + [simple uint 16 offset] [simple uint 16 unknown4] [simple uint 16 stringLength] [manual vstring value 'STATIC_CALL("parseTerminatedString", readBuffer, stringLength)' 'STATIC_CALL("serializeTerminatedString", writeBuffer, value, stringLength)' '(stringLength * 8)'] @@ -238,24 +235,22 @@ [simple uint 32 memoryLength] ] - [dataIo DataItem(UmasDataType dataType, uint 16 numberOfValues) [typeSwitch dataType,numberOfValues ['BOOL','1' BOOL + [reserved uint 7 '0x0000' ] + [simple bit value ] + ] + ['EBOOL','1' BOOL // TODO: Possibly change the order of the bit and the reserved part. [reserved uint 7 '0x0000' ] [simple bit value ] ] - ['BOOL' List - // TODO: Handle adding some reserved bits at the end to fill up the last word. - [array bit value count 'numberOfValues' ] - ] ['BYTE','1' BYTE - [simple uint 8 value] + [simple byte value] ] ['BYTE' List - // TODO: If the number of values is odd, add a reserved byte - [array bit value count 'numberOfValues * 8' ] + [array byte value count 'numberOfValues' ] ] ['WORD' WORD [simple uint 16 value] @@ -294,40 +289,66 @@ [array float 32 value count 'numberOfValues'] ] ['STRING','1' STRING - [manual vstring value 'STATIC_CALL("parseTerminatedStringBytes", readBuffer, numberOfValues)' 'STATIC_CALL("serializeTerminatedString", writeBuffer, value, numberOfValues)' '(numberOfValues * 8)'] + [manual vstring value 'STATIC_CALL("parseTerminatedStringBytes", readBuffer, numberOfValues)' 'STATIC_CALL("serializeTerminatedString", writeBuffer, _value, numberOfValues)' '(numberOfValues * 8)'] ] ['STRING' List [array float 32 value count 'numberOfValues'] ] + ['TIME','1' TIME + [simple uint 32 value] + ] + ['TIME' List + [array uint 32 value count 'numberOfValues'] + ] + ['DATE','1' DATE + [simple uint 8 day encoding='BCD'] + [simple uint 8 month encoding='BCD'] + [simple uint 16 year encoding='BCD'] + ] + ['TOD','1' TIME_OF_DAY + [simple uint 32 value] + ] + ['TOD' List + [array uint 32 value count 'numberOfValues'] + ] + ['DATE_AND_TIME','1' DATE_AND_TIME + [simple uint 8 unused] + [simple uint 8 seconds encoding='BCD'] + [simple uint 8 minutes encoding='BCD'] + [simple uint 8 hour encoding='BCD'] + [simple uint 8 day encoding='BCD'] + [simple uint 8 month encoding='BCD'] + [simple uint 16 year encoding='BCD'] + ] ] ] -[enum uint 8 UmasDataType(uint 8 dataTypeSize, uint 8 requestSize) - ['1' BOOL ['1','1']] - ['2' UNKNOWN2 ['1','1']] - ['3' UNKNOWN3 ['1','1']] - ['4' INT ['2', '2']] - ['5' UINT ['2','2']] - ['6' DINT ['4','3']] - ['7' UDINT ['4','3']] - ['8' REAL ['4','3']] - ['9' STRING ['1','17']] - ['10' TIME ['4','3']] - ['11' UNKNOWN11 ['1','1']] - ['12' UNKNOWN12 ['1','1']] - ['13' UNKNOWN13 ['1','1']] - ['14' DATE ['4','3']] - ['15' TOD ['4','3']] - ['16' DT ['4','3']] - ['17' UNKNOWN17 ['1','1']] - ['18' UNKNOWN18 ['1','1']] - ['19' UNKNOWN19 ['1','1']] - ['20' UNKNOWN20 ['1','1']] - ['21' BYTE ['1','1']] - ['22' WORD ['2','2']] - ['23' DWORD ['4','3']] - ['24' UNKNOWN24 ['1','1']] - ['25' EBOOL ['1','1']] +[enum uint 8 UmasDataType(uint 8 dataTypeSize, uint 8 requestSize, vstring data_type_conversion ) + ['1' BOOL ['1','1','"BOOL"']] + ['2' UNKNOWN2 ['1','1','"BOOL"']] + ['3' UNKNOWN3 ['1','1','"BOOL"']] + ['4' INT ['2', '2','"INT"']] + ['5' UINT ['2','2','"UINT"']] + ['6' DINT ['4','3','"DINT"']] + ['7' UDINT ['4','3','"UDINT"']] + ['8' REAL ['4','3','"REAL"']] + ['9' STRING ['1','17','"STRING"']] + ['10' TIME ['4','3','"TIME"']] + ['11' UNKNOWN11 ['1','1','"BYTE"']] + ['12' UNKNOWN12 ['1','1','"BYTE"']] + ['13' UNKNOWN13 ['1','1','"BYTE"']] + ['14' DATE ['4','3','"DATE"']] + ['15' TOD ['4','3','"TIME_OF_DAY"']] + ['16' DATE_AND_TIME ['8','4','"DATE_AND_TIME"']] + ['17' UNKNOWN17 ['1','1','"BYTE"']] + ['18' UNKNOWN18 ['1','1','"BYTE"']] + ['19' UNKNOWN19 ['1','1','"BYTE"']] + ['20' UNKNOWN20 ['1','1','"BYTE"']] + ['21' BYTE ['1','1','"BYTE"']] + ['22' WORD ['2','2','"WORD"']] + ['23' DWORD ['4','3','"DWORD"']] + ['24' UNKNOWN24 ['1','1','"BYTE"']] + ['25' EBOOL ['1','1','"BOOL"']] ] [enum uint 8 ModbusErrorCode diff --git a/src/site/asciidoc/users/protocols/index.adoc b/src/site/asciidoc/users/protocols/index.adoc index 5ad3536941c..4bb5e16e541 100644 --- a/src/site/asciidoc/users/protocols/index.adoc +++ b/src/site/asciidoc/users/protocols/index.adoc @@ -153,6 +153,13 @@ |icon:check[role="green"] |icon:times[role="red"] +|UMAS +|icon:times[role="red"] +|icon:times[role="red"] +|icon:times[role="red"] +|icon:times[role="red"] +|icon:check[role="green"] + |=== Legend: @@ -379,6 +386,17 @@ The following table contains a list of operations and the protocols that support |icon:question[role="red"] |icon:question[role="red"] +|UMAS +|icon:question[role="red"] +|icon:question[role="red"] +|icon:check[role="green"] +|icon:check[role="green"] +|icon:check[role="green"] +|icon:question[role="red"] +|icon:question[role="red"] +|icon:question[role="red"] +|icon:question[role="red"] + |=== Legend: diff --git a/src/site/asciidoc/users/protocols/umas.adoc b/src/site/asciidoc/users/protocols/umas.adoc new file mode 100644 index 00000000000..3b188c97ee2 --- /dev/null +++ b/src/site/asciidoc/users/protocols/umas.adoc @@ -0,0 +1,118 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +:imagesdir: ../../images/users/protocols +:icons: font + +== UMAS (Schneider Electric PLCs) + +(Supported by Plc4Py Only) + +=== Connection String Options + +[cols="2,2a,2a,2a,4a"] +|=== +|Name |Type |Default Value |Required |Description +|Name 4+|UMAS +|Code 4+|`umas` +|Default Transport 4+|`tcp` +|Supported Transports 4+| + - `tcp` +5+|Config options: +|`request-timeout` |INT |5000| |Default timeout for all types of requests. +|`unit-identifier` |INT |1| |Unit-identifier or slave-id that identifies the target PLC. Defaults to 1. ++++ + +|=== +=== Supported Operations + +[cols="2,2a,5a"] +|=== +|Name |Value |Description + +3+|Supported Operations + +| +2+| `read` + +| +2+| `write` + +| +2+| `browse` +|=== + +=== Individual Resource Address Format + +==== Connection String + +UMAS has the following connection string format:- +---- +umas:{transport}://{ip-address}:{port}?{options} +---- +An example connection string would look like:- +---- +umas:tcp://127.0.0.1:502 +---- +Note the transport, port and option fields are optional. + + +==== General Format + +In general all UMAS addresses have this format: + +---- +{tag-name}.{child-name}.{child-name}:{data-type}[{array-size}] +---- + +Depending on the type of tag the child-name parameters are optional. +e.g. A tag with a BOOL data type could be 'TESTING_BOOL_1' whereas +if it is a UDT the tag name is followed by the child 'TESTING_UDT_1.START' which in itself could be a BOOL. + +If the array-size part is omitted, the size-default of `1` is assumed. + +If the data-type part is omitted, it defaults to the data type of the tag read from the PLC. + +==== Memory Areas + +Apart from tags defined in the PLC the driver is also able to access the %S and %SW +system memory areas. + +The specific address details of the data in these areas are outlined in the devices +manual. + +An example of the address format of these areas is %SW1 or %S20. + +==== Data Types + +The following data types are supported + +- BOOL (boolean) +- SINT (int 8) +- USINT (uint 8) +- BYTE (uint 8) +- INT (int 16) +- UINT (uint 16) +- WORD (uint 16) +- DINT (int 32) +- UDINT (uint 32) +- DWORD (uint 32) +- REAL (float) +- STRING (char) +- TIME +- DATE +- TOD (Time of Day) +- DATE_AND_TIME diff --git a/src/site/site.xml b/src/site/site.xml index 0e4d6cd09af..8f37821fd30 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -87,6 +87,7 @@ +