diff --git a/code-generation/language-go/src/main/resources/templates/go/complex-type-template.go.ftlh b/code-generation/language-go/src/main/resources/templates/go/complex-type-template.go.ftlh index 66ec2bc4593..f81bde74061 100644 --- a/code-generation/language-go/src/main/resources/templates/go/complex-type-template.go.ftlh +++ b/code-generation/language-go/src/main/resources/templates/go/complex-type-template.go.ftlh @@ -199,7 +199,10 @@ type _${type.name} struct { ${type.parentType.orElseThrow().name}Contract <#if type.isDiscriminatedParentTypeDefinition()> - _SubType ${type.name} + _SubType interface { + ${type.name}Contract + ${type.name}Requirements + } <#-- Property fields are fields that require a property in the pojo --> <#if type.propertyFields?has_content> 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..839fa2477ea 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(<#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/plc4go/assets/testing/protocols/opcua/ParserSerializerTestsuite.xml b/plc4go/assets/testing/protocols/opcua/ParserSerializerTestsuite.xml index 9fc863cfd5b..488655b5b0b 100644 --- a/plc4go/assets/testing/protocols/opcua/ParserSerializerTestsuite.xml +++ b/plc4go/assets/testing/protocols/opcua/ParserSerializerTestsuite.xml @@ -1,24 +1,24 @@ + byteOrder="LITTLE_ENDIAN"> OPCUA @@ -65,7 +65,7 @@ OPC UA Anonymous Identity Token 09000000616e6f6e796d6f7573 - UserIdentityToken + ExtensionObjectDefinition 321 @@ -142,7 +142,7 @@ OPC UA Activate Session request 0500002000000084428062123302a3e7470f91899aa477deeab6ba1d83368c8a8e1a5bfffefa6de065de8f04edda010200000000000000ffffffff30750000000000ffffffffffffffff000000000000000001004101010d00000009000000616e6f6e796d6f7573ffffffffffffffff - ActivateSessionRequest + ExtensionObjectDefinition 467 @@ -311,7 +311,7 @@ OPC UA Publish Response Payload (data change) 20427b9004edda01350000000000000000ffffffff000000010000000100000001000000000100000020427b9004edda010100000001002b03012200000001000000010000000d0620000000f0cc098e04edda017089e98f04edda01000000000000000000000000 - PublishResponse + ExtensionObjectDefinition 829 @@ -484,7 +484,7 @@ OPC UA Publish Response Payload (event notification) a07f7459ddf3da016c0200000000000000ffffffff0000000300000001000000010000000001000000a07f7459ddf3da010100000001009403012301000003000000050000000b0000000000000f08000000000000000000001a0c06000000536572766572050100110100cd081503000000000d000000526566726573682053746172740d5040e158ddf3da01110100e30a110000050000000b00000001000100000f10000000000000000000001700000000000000120c070000004d794c6576656c05f40111030600070000004d794c6576656c1503000000000e0000004c6576656c2065786365656465640d20110558ddf3da011101000a25110306000d0000004d794c6576656c2e416c61726d050000000b0000000000000f08000000000000000000001b0c06000000536572766572050100110100cd081503000000000b0000005265667265736820456e640d6067e158ddf3da01110100e40a1100000100000000002880ffffffff - PublishResponse + ExtensionObjectDefinition 829 @@ -1244,7 +1244,7 @@ OPC UA Notification Message 010000008084ff018fedda010100000001002b03012200000001000000010000000d062000000000222ffb8eedda0120d592008fedda0100000000 - PublishResponse + ExtensionObjectDefinition 805 @@ -1427,7 +1427,7 @@ OPC UA Monitored Item Notification 010000000d062000000000222ffb8eedda0120d592008fedda01 - MonitoredItemNotification + ExtensionObjectDefinition 808 diff --git a/plc4go/go.mod b/plc4go/go.mod index 45435616cd5..5f69706f775 100644 --- a/plc4go/go.mod +++ b/plc4go/go.mod @@ -25,7 +25,7 @@ require ( github.com/IBM/netaddr v1.5.0 github.com/ajankovic/xdiff v0.0.1 github.com/cstockton/go-conv v1.0.0 - github.com/fatih/color v1.17.0 + github.com/fatih/color v1.18.0 github.com/google/uuid v1.6.0 github.com/gopacket/gopacket v1.3.0 github.com/icza/bitio v1.1.0 diff --git a/plc4go/go.sum b/plc4go/go.sum index bc5978f5d26..da770e69b49 100644 --- a/plc4go/go.sum +++ b/plc4go/go.sum @@ -11,8 +11,8 @@ github.com/cstockton/go-conv v1.0.0/go.mod h1:HuiHkkRgOA0IoBNPC7ysG7kNpjDYlgM7Kj github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/plc4go/protocols/abeth/readwrite/model/CIPEncapsulationPacket.go b/plc4go/protocols/abeth/readwrite/model/CIPEncapsulationPacket.go index a7c3a3361be..cd827ed3950 100644 --- a/plc4go/protocols/abeth/readwrite/model/CIPEncapsulationPacket.go +++ b/plc4go/protocols/abeth/readwrite/model/CIPEncapsulationPacket.go @@ -75,7 +75,10 @@ type CIPEncapsulationPacketRequirements interface { // _CIPEncapsulationPacket is the data-structure of this message type _CIPEncapsulationPacket struct { - _SubType CIPEncapsulationPacket + _SubType interface { + CIPEncapsulationPacketContract + CIPEncapsulationPacketRequirements + } SessionHandle uint32 Status uint32 SenderContext []uint8 diff --git a/plc4go/protocols/abeth/readwrite/model/DF1RequestCommand.go b/plc4go/protocols/abeth/readwrite/model/DF1RequestCommand.go index 853a12d318e..a5a59645724 100644 --- a/plc4go/protocols/abeth/readwrite/model/DF1RequestCommand.go +++ b/plc4go/protocols/abeth/readwrite/model/DF1RequestCommand.go @@ -65,7 +65,10 @@ type DF1RequestCommandRequirements interface { // _DF1RequestCommand is the data-structure of this message type _DF1RequestCommand struct { - _SubType DF1RequestCommand + _SubType interface { + DF1RequestCommandContract + DF1RequestCommandRequirements + } } var _ DF1RequestCommandContract = (*_DF1RequestCommand)(nil) diff --git a/plc4go/protocols/abeth/readwrite/model/DF1RequestMessage.go b/plc4go/protocols/abeth/readwrite/model/DF1RequestMessage.go index c686b71005c..5f3c5832896 100644 --- a/plc4go/protocols/abeth/readwrite/model/DF1RequestMessage.go +++ b/plc4go/protocols/abeth/readwrite/model/DF1RequestMessage.go @@ -73,7 +73,10 @@ type DF1RequestMessageRequirements interface { // _DF1RequestMessage is the data-structure of this message type _DF1RequestMessage struct { - _SubType DF1RequestMessage + _SubType interface { + DF1RequestMessageContract + DF1RequestMessageRequirements + } DestinationAddress uint8 SourceAddress uint8 Status uint8 diff --git a/plc4go/protocols/abeth/readwrite/model/DF1ResponseMessage.go b/plc4go/protocols/abeth/readwrite/model/DF1ResponseMessage.go index a31ba1c2b10..db417920650 100644 --- a/plc4go/protocols/abeth/readwrite/model/DF1ResponseMessage.go +++ b/plc4go/protocols/abeth/readwrite/model/DF1ResponseMessage.go @@ -75,7 +75,10 @@ type DF1ResponseMessageRequirements interface { // _DF1ResponseMessage is the data-structure of this message type _DF1ResponseMessage struct { - _SubType DF1ResponseMessage + _SubType interface { + DF1ResponseMessageContract + DF1ResponseMessageRequirements + } DestinationAddress uint8 SourceAddress uint8 Status uint8 diff --git a/plc4go/protocols/ads/discovery/readwrite/model/AdsDiscoveryBlock.go b/plc4go/protocols/ads/discovery/readwrite/model/AdsDiscoveryBlock.go index 0f605b642ec..3ec55530f1a 100644 --- a/plc4go/protocols/ads/discovery/readwrite/model/AdsDiscoveryBlock.go +++ b/plc4go/protocols/ads/discovery/readwrite/model/AdsDiscoveryBlock.go @@ -65,7 +65,10 @@ type AdsDiscoveryBlockRequirements interface { // _AdsDiscoveryBlock is the data-structure of this message type _AdsDiscoveryBlock struct { - _SubType AdsDiscoveryBlock + _SubType interface { + AdsDiscoveryBlockContract + AdsDiscoveryBlockRequirements + } } var _ AdsDiscoveryBlockContract = (*_AdsDiscoveryBlock)(nil) diff --git a/plc4go/protocols/ads/readwrite/model/AdsMultiRequestItem.go b/plc4go/protocols/ads/readwrite/model/AdsMultiRequestItem.go index 67bf6fc90a9..17f75f58f54 100644 --- a/plc4go/protocols/ads/readwrite/model/AdsMultiRequestItem.go +++ b/plc4go/protocols/ads/readwrite/model/AdsMultiRequestItem.go @@ -63,7 +63,10 @@ type AdsMultiRequestItemRequirements interface { // _AdsMultiRequestItem is the data-structure of this message type _AdsMultiRequestItem struct { - _SubType AdsMultiRequestItem + _SubType interface { + AdsMultiRequestItemContract + AdsMultiRequestItemRequirements + } } var _ AdsMultiRequestItemContract = (*_AdsMultiRequestItem)(nil) diff --git a/plc4go/protocols/ads/readwrite/model/AmsPacket.go b/plc4go/protocols/ads/readwrite/model/AmsPacket.go index a155bfc984e..9d70d26d5e1 100644 --- a/plc4go/protocols/ads/readwrite/model/AmsPacket.go +++ b/plc4go/protocols/ads/readwrite/model/AmsPacket.go @@ -91,7 +91,10 @@ type AmsPacketRequirements interface { // _AmsPacket is the data-structure of this message type _AmsPacket struct { - _SubType AmsPacket + _SubType interface { + AmsPacketContract + AmsPacketRequirements + } TargetAmsNetId AmsNetId TargetAmsPort uint16 SourceAmsNetId AmsNetId diff --git a/plc4go/protocols/bacnetip/readwrite/model/APDU.go b/plc4go/protocols/bacnetip/readwrite/model/APDU.go index b0d5638f609..7ce2c5fb182 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/APDU.go +++ b/plc4go/protocols/bacnetip/readwrite/model/APDU.go @@ -67,7 +67,10 @@ type APDURequirements interface { // _APDU is the data-structure of this message type _APDU struct { - _SubType APDU + _SubType interface { + APDUContract + APDURequirements + } // Arguments. ApduLength uint16 diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetApplicationTag.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetApplicationTag.go index 2861c24067f..d28be087fcb 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetApplicationTag.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetApplicationTag.go @@ -71,8 +71,11 @@ type BACnetApplicationTagRequirements interface { // _BACnetApplicationTag is the data-structure of this message type _BACnetApplicationTag struct { - _SubType BACnetApplicationTag - Header BACnetTagHeader + _SubType interface { + BACnetApplicationTagContract + BACnetApplicationTagRequirements + } + Header BACnetTagHeader } var _ BACnetApplicationTagContract = (*_BACnetApplicationTag)(nil) diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetCalendarEntry.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetCalendarEntry.go index 42b50352eac..67787aa7eb7 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetCalendarEntry.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetCalendarEntry.go @@ -69,7 +69,10 @@ type BACnetCalendarEntryRequirements interface { // _BACnetCalendarEntry is the data-structure of this message type _BACnetCalendarEntry struct { - _SubType BACnetCalendarEntry + _SubType interface { + BACnetCalendarEntryContract + BACnetCalendarEntryRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetChannelValue.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetChannelValue.go index dad32625877..1bd82d1bde6 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetChannelValue.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetChannelValue.go @@ -73,7 +73,10 @@ type BACnetChannelValueRequirements interface { // _BACnetChannelValue is the data-structure of this message type _BACnetChannelValue struct { - _SubType BACnetChannelValue + _SubType interface { + BACnetChannelValueContract + BACnetChannelValueRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetClientCOV.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetClientCOV.go index 4b459ed3da4..e7faf3594d0 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetClientCOV.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetClientCOV.go @@ -69,7 +69,10 @@ type BACnetClientCOVRequirements interface { // _BACnetClientCOV is the data-structure of this message type _BACnetClientCOV struct { - _SubType BACnetClientCOV + _SubType interface { + BACnetClientCOVContract + BACnetClientCOVRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequest.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequest.go index 0281e07bada..82748f545a2 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequest.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequest.go @@ -69,7 +69,10 @@ type BACnetConfirmedServiceRequestRequirements interface { // _BACnetConfirmedServiceRequest is the data-structure of this message type _BACnetConfirmedServiceRequest struct { - _SubType BACnetConfirmedServiceRequest + _SubType interface { + BACnetConfirmedServiceRequestContract + BACnetConfirmedServiceRequestRequirements + } // Arguments. ServiceRequestLength uint32 diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecord.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecord.go index b4d75216ea0..d9fd639fc4b 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecord.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecord.go @@ -73,7 +73,10 @@ type BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecordRequirements inter // _BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecord is the data-structure of this message type _BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecord struct { - _SubType BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecord + _SubType interface { + BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecordContract + BACnetConfirmedServiceRequestAtomicReadFileStreamOrRecordRequirements + } PeekedTagHeader BACnetTagHeader OpeningTag BACnetOpeningTag ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestConfirmedTextMessageMessageClass.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestConfirmedTextMessageMessageClass.go index 68f96722d30..ccfe8913912 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestConfirmedTextMessageMessageClass.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestConfirmedTextMessageMessageClass.go @@ -75,7 +75,10 @@ type BACnetConfirmedServiceRequestConfirmedTextMessageMessageClassRequirements i // _BACnetConfirmedServiceRequestConfirmedTextMessageMessageClass is the data-structure of this message type _BACnetConfirmedServiceRequestConfirmedTextMessageMessageClass struct { - _SubType BACnetConfirmedServiceRequestConfirmedTextMessageMessageClass + _SubType interface { + BACnetConfirmedServiceRequestConfirmedTextMessageMessageClassContract + BACnetConfirmedServiceRequestConfirmedTextMessageMessageClassRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestReadRangeRange.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestReadRangeRange.go index cb09db49954..e61bb5c4df0 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestReadRangeRange.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetConfirmedServiceRequestReadRangeRange.go @@ -73,7 +73,10 @@ type BACnetConfirmedServiceRequestReadRangeRangeRequirements interface { // _BACnetConfirmedServiceRequestReadRangeRange is the data-structure of this message type _BACnetConfirmedServiceRequestReadRangeRange struct { - _SubType BACnetConfirmedServiceRequestReadRangeRange + _SubType interface { + BACnetConfirmedServiceRequestReadRangeRangeContract + BACnetConfirmedServiceRequestReadRangeRangeRequirements + } PeekedTagHeader BACnetTagHeader OpeningTag BACnetOpeningTag ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetConstructedData.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetConstructedData.go index 0fdc67a4e9f..22100ee0ddb 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetConstructedData.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetConstructedData.go @@ -81,7 +81,10 @@ type BACnetConstructedDataRequirements interface { // _BACnetConstructedData is the data-structure of this message type _BACnetConstructedData struct { - _SubType BACnetConstructedData + _SubType interface { + BACnetConstructedDataContract + BACnetConstructedDataRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetContextTag.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetContextTag.go index 2af4f444c63..919fbafef00 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetContextTag.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetContextTag.go @@ -73,8 +73,11 @@ type BACnetContextTagRequirements interface { // _BACnetContextTag is the data-structure of this message type _BACnetContextTag struct { - _SubType BACnetContextTag - Header BACnetTagHeader + _SubType interface { + BACnetContextTagContract + BACnetContextTagRequirements + } + Header BACnetTagHeader // Arguments. TagNumberArgument uint8 diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetError.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetError.go index fe39213fb4c..646285eb95a 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetError.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetError.go @@ -63,7 +63,10 @@ type BACnetErrorRequirements interface { // _BACnetError is the data-structure of this message type _BACnetError struct { - _SubType BACnetError + _SubType interface { + BACnetErrorContract + BACnetErrorRequirements + } } var _ BACnetErrorContract = (*_BACnetError)(nil) diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetEventLogRecordLogDatum.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetEventLogRecordLogDatum.go index f7908bd6dd5..73d6203d3ed 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetEventLogRecordLogDatum.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetEventLogRecordLogDatum.go @@ -75,7 +75,10 @@ type BACnetEventLogRecordLogDatumRequirements interface { // _BACnetEventLogRecordLogDatum is the data-structure of this message type _BACnetEventLogRecordLogDatum struct { - _SubType BACnetEventLogRecordLogDatum + _SubType interface { + BACnetEventLogRecordLogDatumContract + BACnetEventLogRecordLogDatumRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetEventParameter.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetEventParameter.go index 5d34e2392de..1ebea8b5a05 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetEventParameter.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetEventParameter.go @@ -69,7 +69,10 @@ type BACnetEventParameterRequirements interface { // _BACnetEventParameter is the data-structure of this message type _BACnetEventParameter struct { - _SubType BACnetEventParameter + _SubType interface { + BACnetEventParameterContract + BACnetEventParameterRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetEventParameterChangeOfValueCivCriteria.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetEventParameterChangeOfValueCivCriteria.go index c73886bc0c3..fe06295f3a8 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetEventParameterChangeOfValueCivCriteria.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetEventParameterChangeOfValueCivCriteria.go @@ -75,7 +75,10 @@ type BACnetEventParameterChangeOfValueCivCriteriaRequirements interface { // _BACnetEventParameterChangeOfValueCivCriteria is the data-structure of this message type _BACnetEventParameterChangeOfValueCivCriteria struct { - _SubType BACnetEventParameterChangeOfValueCivCriteria + _SubType interface { + BACnetEventParameterChangeOfValueCivCriteriaContract + BACnetEventParameterChangeOfValueCivCriteriaRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameter.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameter.go index fa188be626a..2cdd07c75be 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameter.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameter.go @@ -69,7 +69,10 @@ type BACnetFaultParameterRequirements interface { // _BACnetFaultParameter is the data-structure of this message type _BACnetFaultParameter struct { - _SubType BACnetFaultParameter + _SubType interface { + BACnetFaultParameterContract + BACnetFaultParameterRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultExtendedParametersEntry.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultExtendedParametersEntry.go index 89afb963522..35467c1711a 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultExtendedParametersEntry.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultExtendedParametersEntry.go @@ -73,7 +73,10 @@ type BACnetFaultParameterFaultExtendedParametersEntryRequirements interface { // _BACnetFaultParameterFaultExtendedParametersEntry is the data-structure of this message type _BACnetFaultParameterFaultExtendedParametersEntry struct { - _SubType BACnetFaultParameterFaultExtendedParametersEntry + _SubType interface { + BACnetFaultParameterFaultExtendedParametersEntryContract + BACnetFaultParameterFaultExtendedParametersEntryRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultOutOfRangeMaxNormalValue.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultOutOfRangeMaxNormalValue.go index d48e06240f2..e4b7ecdc3c8 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultOutOfRangeMaxNormalValue.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultOutOfRangeMaxNormalValue.go @@ -75,7 +75,10 @@ type BACnetFaultParameterFaultOutOfRangeMaxNormalValueRequirements interface { // _BACnetFaultParameterFaultOutOfRangeMaxNormalValue is the data-structure of this message type _BACnetFaultParameterFaultOutOfRangeMaxNormalValue struct { - _SubType BACnetFaultParameterFaultOutOfRangeMaxNormalValue + _SubType interface { + BACnetFaultParameterFaultOutOfRangeMaxNormalValueContract + BACnetFaultParameterFaultOutOfRangeMaxNormalValueRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultOutOfRangeMinNormalValue.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultOutOfRangeMinNormalValue.go index 9ff68f44159..8af08eaacee 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultOutOfRangeMinNormalValue.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetFaultParameterFaultOutOfRangeMinNormalValue.go @@ -75,7 +75,10 @@ type BACnetFaultParameterFaultOutOfRangeMinNormalValueRequirements interface { // _BACnetFaultParameterFaultOutOfRangeMinNormalValue is the data-structure of this message type _BACnetFaultParameterFaultOutOfRangeMinNormalValue struct { - _SubType BACnetFaultParameterFaultOutOfRangeMinNormalValue + _SubType interface { + BACnetFaultParameterFaultOutOfRangeMinNormalValueContract + BACnetFaultParameterFaultOutOfRangeMinNormalValueRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetHostAddress.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetHostAddress.go index af7a1ceb526..241f2ab1f75 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetHostAddress.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetHostAddress.go @@ -69,7 +69,10 @@ type BACnetHostAddressRequirements interface { // _BACnetHostAddress is the data-structure of this message type _BACnetHostAddress struct { - _SubType BACnetHostAddress + _SubType interface { + BACnetHostAddressContract + BACnetHostAddressRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetLandingCallStatusCommand.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetLandingCallStatusCommand.go index a1da5ce03f7..e5a7d8e526c 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetLandingCallStatusCommand.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetLandingCallStatusCommand.go @@ -69,7 +69,10 @@ type BACnetLandingCallStatusCommandRequirements interface { // _BACnetLandingCallStatusCommand is the data-structure of this message type _BACnetLandingCallStatusCommand struct { - _SubType BACnetLandingCallStatusCommand + _SubType interface { + BACnetLandingCallStatusCommandContract + BACnetLandingCallStatusCommandRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetLogData.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetLogData.go index b8f499b74da..b6d655c5ec0 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetLogData.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetLogData.go @@ -75,7 +75,10 @@ type BACnetLogDataRequirements interface { // _BACnetLogData is the data-structure of this message type _BACnetLogData struct { - _SubType BACnetLogData + _SubType interface { + BACnetLogDataContract + BACnetLogDataRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetLogDataLogDataEntry.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetLogDataLogDataEntry.go index 8b8a397ac04..f88b80b7c7c 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetLogDataLogDataEntry.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetLogDataLogDataEntry.go @@ -69,7 +69,10 @@ type BACnetLogDataLogDataEntryRequirements interface { // _BACnetLogDataLogDataEntry is the data-structure of this message type _BACnetLogDataLogDataEntry struct { - _SubType BACnetLogDataLogDataEntry + _SubType interface { + BACnetLogDataLogDataEntryContract + BACnetLogDataLogDataEntryRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetLogRecordLogDatum.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetLogRecordLogDatum.go index a1783dd0bc9..98041c53247 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetLogRecordLogDatum.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetLogRecordLogDatum.go @@ -75,7 +75,10 @@ type BACnetLogRecordLogDatumRequirements interface { // _BACnetLogRecordLogDatum is the data-structure of this message type _BACnetLogRecordLogDatum struct { - _SubType BACnetLogRecordLogDatum + _SubType interface { + BACnetLogRecordLogDatumContract + BACnetLogRecordLogDatumRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParameters.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParameters.go index 3ff0d51d10b..e4203f0272d 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParameters.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParameters.go @@ -77,7 +77,10 @@ type BACnetNotificationParametersRequirements interface { // _BACnetNotificationParameters is the data-structure of this message type _BACnetNotificationParameters struct { - _SubType BACnetNotificationParameters + _SubType interface { + BACnetNotificationParametersContract + BACnetNotificationParametersRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParametersChangeOfDiscreteValueNewValue.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParametersChangeOfDiscreteValueNewValue.go index b173a3a37b8..3c944fccc54 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParametersChangeOfDiscreteValueNewValue.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParametersChangeOfDiscreteValueNewValue.go @@ -79,7 +79,10 @@ type BACnetNotificationParametersChangeOfDiscreteValueNewValueRequirements inter // _BACnetNotificationParametersChangeOfDiscreteValueNewValue is the data-structure of this message type _BACnetNotificationParametersChangeOfDiscreteValueNewValue struct { - _SubType BACnetNotificationParametersChangeOfDiscreteValueNewValue + _SubType interface { + BACnetNotificationParametersChangeOfDiscreteValueNewValueContract + BACnetNotificationParametersChangeOfDiscreteValueNewValueRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParametersChangeOfValueNewValue.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParametersChangeOfValueNewValue.go index 6e347b87aea..f7821bec9a9 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParametersChangeOfValueNewValue.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetNotificationParametersChangeOfValueNewValue.go @@ -75,7 +75,10 @@ type BACnetNotificationParametersChangeOfValueNewValueRequirements interface { // _BACnetNotificationParametersChangeOfValueNewValue is the data-structure of this message type _BACnetNotificationParametersChangeOfValueNewValue struct { - _SubType BACnetNotificationParametersChangeOfValueNewValue + _SubType interface { + BACnetNotificationParametersChangeOfValueNewValueContract + BACnetNotificationParametersChangeOfValueNewValueRequirements + } OpeningTag BACnetOpeningTag PeekedTagHeader BACnetTagHeader ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalBinaryPV.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalBinaryPV.go index 4b2f96b9a67..be99aa3d27c 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalBinaryPV.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalBinaryPV.go @@ -69,7 +69,10 @@ type BACnetOptionalBinaryPVRequirements interface { // _BACnetOptionalBinaryPV is the data-structure of this message type _BACnetOptionalBinaryPV struct { - _SubType BACnetOptionalBinaryPV + _SubType interface { + BACnetOptionalBinaryPVContract + BACnetOptionalBinaryPVRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalCharacterString.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalCharacterString.go index 41ca5e5040e..5dfe06ef3d1 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalCharacterString.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalCharacterString.go @@ -69,7 +69,10 @@ type BACnetOptionalCharacterStringRequirements interface { // _BACnetOptionalCharacterString is the data-structure of this message type _BACnetOptionalCharacterString struct { - _SubType BACnetOptionalCharacterString + _SubType interface { + BACnetOptionalCharacterStringContract + BACnetOptionalCharacterStringRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalREAL.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalREAL.go index dee9c82067a..d64b2ac554d 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalREAL.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalREAL.go @@ -69,7 +69,10 @@ type BACnetOptionalREALRequirements interface { // _BACnetOptionalREAL is the data-structure of this message type _BACnetOptionalREAL struct { - _SubType BACnetOptionalREAL + _SubType interface { + BACnetOptionalREALContract + BACnetOptionalREALRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalUnsigned.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalUnsigned.go index 4174c0ed86b..f7feabb43d0 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalUnsigned.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetOptionalUnsigned.go @@ -69,7 +69,10 @@ type BACnetOptionalUnsignedRequirements interface { // _BACnetOptionalUnsigned is the data-structure of this message type _BACnetOptionalUnsigned struct { - _SubType BACnetOptionalUnsigned + _SubType interface { + BACnetOptionalUnsignedContract + BACnetOptionalUnsignedRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetPriorityValue.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetPriorityValue.go index e5c29bb70e8..ac7a44cca99 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetPriorityValue.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetPriorityValue.go @@ -75,7 +75,10 @@ type BACnetPriorityValueRequirements interface { // _BACnetPriorityValue is the data-structure of this message type _BACnetPriorityValue struct { - _SubType BACnetPriorityValue + _SubType interface { + BACnetPriorityValueContract + BACnetPriorityValueRequirements + } PeekedTagHeader BACnetTagHeader // Arguments. diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetProcessIdSelection.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetProcessIdSelection.go index f3860b9d071..61d2db35584 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetProcessIdSelection.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetProcessIdSelection.go @@ -69,7 +69,10 @@ type BACnetProcessIdSelectionRequirements interface { // _BACnetProcessIdSelection is the data-structure of this message type _BACnetProcessIdSelection struct { - _SubType BACnetProcessIdSelection + _SubType interface { + BACnetProcessIdSelectionContract + BACnetProcessIdSelectionRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetPropertyAccessResultAccessResult.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetPropertyAccessResultAccessResult.go index 8b7f48c7164..1a46b638377 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetPropertyAccessResultAccessResult.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetPropertyAccessResultAccessResult.go @@ -75,7 +75,10 @@ type BACnetPropertyAccessResultAccessResultRequirements interface { // _BACnetPropertyAccessResultAccessResult is the data-structure of this message type _BACnetPropertyAccessResultAccessResult struct { - _SubType BACnetPropertyAccessResultAccessResult + _SubType interface { + BACnetPropertyAccessResultAccessResultContract + BACnetPropertyAccessResultAccessResultRequirements + } PeekedTagHeader BACnetTagHeader // Arguments. diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetPropertyStates.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetPropertyStates.go index f3a1b045dfc..45b2a4077f3 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetPropertyStates.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetPropertyStates.go @@ -69,7 +69,10 @@ type BACnetPropertyStatesRequirements interface { // _BACnetPropertyStates is the data-structure of this message type _BACnetPropertyStates struct { - _SubType BACnetPropertyStates + _SubType interface { + BACnetPropertyStatesContract + BACnetPropertyStatesRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetRecipient.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetRecipient.go index 671b61c9d86..bf9efa4e703 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetRecipient.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetRecipient.go @@ -69,7 +69,10 @@ type BACnetRecipientRequirements interface { // _BACnetRecipient is the data-structure of this message type _BACnetRecipient struct { - _SubType BACnetRecipient + _SubType interface { + BACnetRecipientContract + BACnetRecipientRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetScale.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetScale.go index 73212d2a3ed..10ae0b153ae 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetScale.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetScale.go @@ -69,7 +69,10 @@ type BACnetScaleRequirements interface { // _BACnetScale is the data-structure of this message type _BACnetScale struct { - _SubType BACnetScale + _SubType interface { + BACnetScaleContract + BACnetScaleRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetServiceAck.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetServiceAck.go index e7e12edc4e7..57a3f3ac3f6 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetServiceAck.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetServiceAck.go @@ -69,7 +69,10 @@ type BACnetServiceAckRequirements interface { // _BACnetServiceAck is the data-structure of this message type _BACnetServiceAck struct { - _SubType BACnetServiceAck + _SubType interface { + BACnetServiceAckContract + BACnetServiceAckRequirements + } // Arguments. ServiceAckLength uint32 diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetServiceAckAtomicReadFileStreamOrRecord.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetServiceAckAtomicReadFileStreamOrRecord.go index d2cea00d65e..d632ea20423 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetServiceAckAtomicReadFileStreamOrRecord.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetServiceAckAtomicReadFileStreamOrRecord.go @@ -73,7 +73,10 @@ type BACnetServiceAckAtomicReadFileStreamOrRecordRequirements interface { // _BACnetServiceAckAtomicReadFileStreamOrRecord is the data-structure of this message type _BACnetServiceAckAtomicReadFileStreamOrRecord struct { - _SubType BACnetServiceAckAtomicReadFileStreamOrRecord + _SubType interface { + BACnetServiceAckAtomicReadFileStreamOrRecordContract + BACnetServiceAckAtomicReadFileStreamOrRecordRequirements + } PeekedTagHeader BACnetTagHeader OpeningTag BACnetOpeningTag ClosingTag BACnetClosingTag diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetShedLevel.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetShedLevel.go index 4205d55f845..44a57c9b3b7 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetShedLevel.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetShedLevel.go @@ -69,7 +69,10 @@ type BACnetShedLevelRequirements interface { // _BACnetShedLevel is the data-structure of this message type _BACnetShedLevel struct { - _SubType BACnetShedLevel + _SubType interface { + BACnetShedLevelContract + BACnetShedLevelRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetSpecialEventPeriod.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetSpecialEventPeriod.go index 7d9851c6874..fc318d47c1d 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetSpecialEventPeriod.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetSpecialEventPeriod.go @@ -69,7 +69,10 @@ type BACnetSpecialEventPeriodRequirements interface { // _BACnetSpecialEventPeriod is the data-structure of this message type _BACnetSpecialEventPeriod struct { - _SubType BACnetSpecialEventPeriod + _SubType interface { + BACnetSpecialEventPeriodContract + BACnetSpecialEventPeriodRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetTimeStamp.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetTimeStamp.go index 477106cbf5a..9c44205e1b3 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetTimeStamp.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetTimeStamp.go @@ -69,7 +69,10 @@ type BACnetTimeStampRequirements interface { // _BACnetTimeStamp is the data-structure of this message type _BACnetTimeStamp struct { - _SubType BACnetTimeStamp + _SubType interface { + BACnetTimeStampContract + BACnetTimeStampRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetTimerStateChangeValue.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetTimerStateChangeValue.go index ddcab099d77..cbd8df0e8de 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetTimerStateChangeValue.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetTimerStateChangeValue.go @@ -75,7 +75,10 @@ type BACnetTimerStateChangeValueRequirements interface { // _BACnetTimerStateChangeValue is the data-structure of this message type _BACnetTimerStateChangeValue struct { - _SubType BACnetTimerStateChangeValue + _SubType interface { + BACnetTimerStateChangeValueContract + BACnetTimerStateChangeValueRequirements + } PeekedTagHeader BACnetTagHeader // Arguments. diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetUnconfirmedServiceRequest.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetUnconfirmedServiceRequest.go index e1b0edab542..97a326dc027 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetUnconfirmedServiceRequest.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetUnconfirmedServiceRequest.go @@ -67,7 +67,10 @@ type BACnetUnconfirmedServiceRequestRequirements interface { // _BACnetUnconfirmedServiceRequest is the data-structure of this message type _BACnetUnconfirmedServiceRequest struct { - _SubType BACnetUnconfirmedServiceRequest + _SubType interface { + BACnetUnconfirmedServiceRequestContract + BACnetUnconfirmedServiceRequestRequirements + } // Arguments. ServiceRequestLength uint16 diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetUnconfirmedServiceRequestWhoHasObject.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetUnconfirmedServiceRequestWhoHasObject.go index ea6d1c741d1..ca5e211be67 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetUnconfirmedServiceRequestWhoHasObject.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetUnconfirmedServiceRequestWhoHasObject.go @@ -69,7 +69,10 @@ type BACnetUnconfirmedServiceRequestWhoHasObjectRequirements interface { // _BACnetUnconfirmedServiceRequestWhoHasObject is the data-structure of this message type _BACnetUnconfirmedServiceRequestWhoHasObject struct { - _SubType BACnetUnconfirmedServiceRequestWhoHasObject + _SubType interface { + BACnetUnconfirmedServiceRequestWhoHasObjectContract + BACnetUnconfirmedServiceRequestWhoHasObjectRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BACnetValueSource.go b/plc4go/protocols/bacnetip/readwrite/model/BACnetValueSource.go index 71df50e7b55..3879b975dc8 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BACnetValueSource.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BACnetValueSource.go @@ -69,7 +69,10 @@ type BACnetValueSourceRequirements interface { // _BACnetValueSource is the data-structure of this message type _BACnetValueSource struct { - _SubType BACnetValueSource + _SubType interface { + BACnetValueSourceContract + BACnetValueSourceRequirements + } PeekedTagHeader BACnetTagHeader } diff --git a/plc4go/protocols/bacnetip/readwrite/model/BVLC.go b/plc4go/protocols/bacnetip/readwrite/model/BVLC.go index e61d8f794f2..681df9b8fab 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/BVLC.go +++ b/plc4go/protocols/bacnetip/readwrite/model/BVLC.go @@ -72,7 +72,10 @@ type BVLCRequirements interface { // _BVLC is the data-structure of this message type _BVLC struct { - _SubType BVLC + _SubType interface { + BVLCContract + BVLCRequirements + } } var _ BVLCContract = (*_BVLC)(nil) diff --git a/plc4go/protocols/bacnetip/readwrite/model/NLM.go b/plc4go/protocols/bacnetip/readwrite/model/NLM.go index 4bcdd0a7971..2f10f9747bd 100644 --- a/plc4go/protocols/bacnetip/readwrite/model/NLM.go +++ b/plc4go/protocols/bacnetip/readwrite/model/NLM.go @@ -71,7 +71,10 @@ type NLMRequirements interface { // _NLM is the data-structure of this message type _NLM struct { - _SubType NLM + _SubType interface { + NLMContract + NLMRequirements + } // Arguments. ApduLength uint16 diff --git a/plc4go/protocols/cbus/readwrite/model/AccessControlData.go b/plc4go/protocols/cbus/readwrite/model/AccessControlData.go index d84d87bbba0..e56650cff5d 100644 --- a/plc4go/protocols/cbus/readwrite/model/AccessControlData.go +++ b/plc4go/protocols/cbus/readwrite/model/AccessControlData.go @@ -73,7 +73,10 @@ type AccessControlDataRequirements interface { // _AccessControlData is the data-structure of this message type _AccessControlData struct { - _SubType AccessControlData + _SubType interface { + AccessControlDataContract + AccessControlDataRequirements + } CommandTypeContainer AccessControlCommandTypeContainer NetworkId byte AccessPointId byte diff --git a/plc4go/protocols/cbus/readwrite/model/AirConditioningData.go b/plc4go/protocols/cbus/readwrite/model/AirConditioningData.go index 224150f122b..8a0f8e6b969 100644 --- a/plc4go/protocols/cbus/readwrite/model/AirConditioningData.go +++ b/plc4go/protocols/cbus/readwrite/model/AirConditioningData.go @@ -69,7 +69,10 @@ type AirConditioningDataRequirements interface { // _AirConditioningData is the data-structure of this message type _AirConditioningData struct { - _SubType AirConditioningData + _SubType interface { + AirConditioningDataContract + AirConditioningDataRequirements + } CommandTypeContainer AirConditioningCommandTypeContainer } diff --git a/plc4go/protocols/cbus/readwrite/model/CALData.go b/plc4go/protocols/cbus/readwrite/model/CALData.go index 6a45e988a67..3ba9ff66c9e 100644 --- a/plc4go/protocols/cbus/readwrite/model/CALData.go +++ b/plc4go/protocols/cbus/readwrite/model/CALData.go @@ -77,7 +77,10 @@ type CALDataRequirements interface { // _CALData is the data-structure of this message type _CALData struct { - _SubType CALData + _SubType interface { + CALDataContract + CALDataRequirements + } CommandTypeContainer CALCommandTypeContainer AdditionalData CALData diff --git a/plc4go/protocols/cbus/readwrite/model/CALReply.go b/plc4go/protocols/cbus/readwrite/model/CALReply.go index a99708b4548..6028b320ad9 100644 --- a/plc4go/protocols/cbus/readwrite/model/CALReply.go +++ b/plc4go/protocols/cbus/readwrite/model/CALReply.go @@ -73,9 +73,12 @@ type CALReplyRequirements interface { // _CALReply is the data-structure of this message type _CALReply struct { - _SubType CALReply - CalType byte - CalData CALData + _SubType interface { + CALReplyContract + CALReplyRequirements + } + CalType byte + CalData CALData // Arguments. CBusOptions CBusOptions diff --git a/plc4go/protocols/cbus/readwrite/model/CBusCommand.go b/plc4go/protocols/cbus/readwrite/model/CBusCommand.go index 16600cd4bdd..ac9f01a4ca4 100644 --- a/plc4go/protocols/cbus/readwrite/model/CBusCommand.go +++ b/plc4go/protocols/cbus/readwrite/model/CBusCommand.go @@ -75,8 +75,11 @@ type CBusCommandRequirements interface { // _CBusCommand is the data-structure of this message type _CBusCommand struct { - _SubType CBusCommand - Header CBusHeader + _SubType interface { + CBusCommandContract + CBusCommandRequirements + } + Header CBusHeader // Arguments. CBusOptions CBusOptions diff --git a/plc4go/protocols/cbus/readwrite/model/CBusMessage.go b/plc4go/protocols/cbus/readwrite/model/CBusMessage.go index c3641c34dd6..1b6da58c982 100644 --- a/plc4go/protocols/cbus/readwrite/model/CBusMessage.go +++ b/plc4go/protocols/cbus/readwrite/model/CBusMessage.go @@ -67,7 +67,10 @@ type CBusMessageRequirements interface { // _CBusMessage is the data-structure of this message type _CBusMessage struct { - _SubType CBusMessage + _SubType interface { + CBusMessageContract + CBusMessageRequirements + } // Arguments. RequestContext RequestContext diff --git a/plc4go/protocols/cbus/readwrite/model/CBusPointToMultiPointCommand.go b/plc4go/protocols/cbus/readwrite/model/CBusPointToMultiPointCommand.go index e1e8c2c2ce1..df29a8ae452 100644 --- a/plc4go/protocols/cbus/readwrite/model/CBusPointToMultiPointCommand.go +++ b/plc4go/protocols/cbus/readwrite/model/CBusPointToMultiPointCommand.go @@ -69,7 +69,10 @@ type CBusPointToMultiPointCommandRequirements interface { // _CBusPointToMultiPointCommand is the data-structure of this message type _CBusPointToMultiPointCommand struct { - _SubType CBusPointToMultiPointCommand + _SubType interface { + CBusPointToMultiPointCommandContract + CBusPointToMultiPointCommandRequirements + } PeekedApplication byte // Arguments. diff --git a/plc4go/protocols/cbus/readwrite/model/CBusPointToPointCommand.go b/plc4go/protocols/cbus/readwrite/model/CBusPointToPointCommand.go index 8b82e1e90eb..ce6f918931e 100644 --- a/plc4go/protocols/cbus/readwrite/model/CBusPointToPointCommand.go +++ b/plc4go/protocols/cbus/readwrite/model/CBusPointToPointCommand.go @@ -73,7 +73,10 @@ type CBusPointToPointCommandRequirements interface { // _CBusPointToPointCommand is the data-structure of this message type _CBusPointToPointCommand struct { - _SubType CBusPointToPointCommand + _SubType interface { + CBusPointToPointCommandContract + CBusPointToPointCommandRequirements + } BridgeAddressCountPeek uint16 CalData CALData diff --git a/plc4go/protocols/cbus/readwrite/model/CBusPointToPointToMultiPointCommand.go b/plc4go/protocols/cbus/readwrite/model/CBusPointToPointToMultiPointCommand.go index 23096bb6c7d..42eaba46ffa 100644 --- a/plc4go/protocols/cbus/readwrite/model/CBusPointToPointToMultiPointCommand.go +++ b/plc4go/protocols/cbus/readwrite/model/CBusPointToPointToMultiPointCommand.go @@ -73,7 +73,10 @@ type CBusPointToPointToMultiPointCommandRequirements interface { // _CBusPointToPointToMultiPointCommand is the data-structure of this message type _CBusPointToPointToMultiPointCommand struct { - _SubType CBusPointToPointToMultiPointCommand + _SubType interface { + CBusPointToPointToMultiPointCommandContract + CBusPointToPointToMultiPointCommandRequirements + } BridgeAddress BridgeAddress NetworkRoute NetworkRoute PeekedApplication byte diff --git a/plc4go/protocols/cbus/readwrite/model/ClockAndTimekeepingData.go b/plc4go/protocols/cbus/readwrite/model/ClockAndTimekeepingData.go index a6277e725bf..9d917556ea0 100644 --- a/plc4go/protocols/cbus/readwrite/model/ClockAndTimekeepingData.go +++ b/plc4go/protocols/cbus/readwrite/model/ClockAndTimekeepingData.go @@ -73,7 +73,10 @@ type ClockAndTimekeepingDataRequirements interface { // _ClockAndTimekeepingData is the data-structure of this message type _ClockAndTimekeepingData struct { - _SubType ClockAndTimekeepingData + _SubType interface { + ClockAndTimekeepingDataContract + ClockAndTimekeepingDataRequirements + } CommandTypeContainer ClockAndTimekeepingCommandTypeContainer Argument byte } diff --git a/plc4go/protocols/cbus/readwrite/model/EncodedReply.go b/plc4go/protocols/cbus/readwrite/model/EncodedReply.go index 7958c6df83e..6b127f565e8 100644 --- a/plc4go/protocols/cbus/readwrite/model/EncodedReply.go +++ b/plc4go/protocols/cbus/readwrite/model/EncodedReply.go @@ -73,7 +73,10 @@ type EncodedReplyRequirements interface { // _EncodedReply is the data-structure of this message type _EncodedReply struct { - _SubType EncodedReply + _SubType interface { + EncodedReplyContract + EncodedReplyRequirements + } PeekedByte byte // Arguments. diff --git a/plc4go/protocols/cbus/readwrite/model/ErrorReportingData.go b/plc4go/protocols/cbus/readwrite/model/ErrorReportingData.go index e4a0d4895c9..5ec87b5f894 100644 --- a/plc4go/protocols/cbus/readwrite/model/ErrorReportingData.go +++ b/plc4go/protocols/cbus/readwrite/model/ErrorReportingData.go @@ -69,7 +69,10 @@ type ErrorReportingDataRequirements interface { // _ErrorReportingData is the data-structure of this message type _ErrorReportingData struct { - _SubType ErrorReportingData + _SubType interface { + ErrorReportingDataContract + ErrorReportingDataRequirements + } CommandTypeContainer ErrorReportingCommandTypeContainer } diff --git a/plc4go/protocols/cbus/readwrite/model/ErrorReportingSystemCategoryType.go b/plc4go/protocols/cbus/readwrite/model/ErrorReportingSystemCategoryType.go index 528dfb3b03f..8169e5e77ed 100644 --- a/plc4go/protocols/cbus/readwrite/model/ErrorReportingSystemCategoryType.go +++ b/plc4go/protocols/cbus/readwrite/model/ErrorReportingSystemCategoryType.go @@ -63,7 +63,10 @@ type ErrorReportingSystemCategoryTypeRequirements interface { // _ErrorReportingSystemCategoryType is the data-structure of this message type _ErrorReportingSystemCategoryType struct { - _SubType ErrorReportingSystemCategoryType + _SubType interface { + ErrorReportingSystemCategoryTypeContract + ErrorReportingSystemCategoryTypeRequirements + } } var _ ErrorReportingSystemCategoryTypeContract = (*_ErrorReportingSystemCategoryType)(nil) diff --git a/plc4go/protocols/cbus/readwrite/model/IdentifyReplyCommand.go b/plc4go/protocols/cbus/readwrite/model/IdentifyReplyCommand.go index b9d6e7ccbed..251d119c9a1 100644 --- a/plc4go/protocols/cbus/readwrite/model/IdentifyReplyCommand.go +++ b/plc4go/protocols/cbus/readwrite/model/IdentifyReplyCommand.go @@ -65,7 +65,10 @@ type IdentifyReplyCommandRequirements interface { // _IdentifyReplyCommand is the data-structure of this message type _IdentifyReplyCommand struct { - _SubType IdentifyReplyCommand + _SubType interface { + IdentifyReplyCommandContract + IdentifyReplyCommandRequirements + } // Arguments. NumBytes uint8 diff --git a/plc4go/protocols/cbus/readwrite/model/LevelInformation.go b/plc4go/protocols/cbus/readwrite/model/LevelInformation.go index af501184e28..2d1c549bc3a 100644 --- a/plc4go/protocols/cbus/readwrite/model/LevelInformation.go +++ b/plc4go/protocols/cbus/readwrite/model/LevelInformation.go @@ -85,8 +85,11 @@ type LevelInformationRequirements interface { // _LevelInformation is the data-structure of this message type _LevelInformation struct { - _SubType LevelInformation - Raw uint16 + _SubType interface { + LevelInformationContract + LevelInformationRequirements + } + Raw uint16 } var _ LevelInformationContract = (*_LevelInformation)(nil) diff --git a/plc4go/protocols/cbus/readwrite/model/LightingData.go b/plc4go/protocols/cbus/readwrite/model/LightingData.go index df20ef9a31f..81d8d9e0b44 100644 --- a/plc4go/protocols/cbus/readwrite/model/LightingData.go +++ b/plc4go/protocols/cbus/readwrite/model/LightingData.go @@ -69,7 +69,10 @@ type LightingDataRequirements interface { // _LightingData is the data-structure of this message type _LightingData struct { - _SubType LightingData + _SubType interface { + LightingDataContract + LightingDataRequirements + } CommandTypeContainer LightingCommandTypeContainer } diff --git a/plc4go/protocols/cbus/readwrite/model/MeasurementData.go b/plc4go/protocols/cbus/readwrite/model/MeasurementData.go index de7f8500216..df4f3d54181 100644 --- a/plc4go/protocols/cbus/readwrite/model/MeasurementData.go +++ b/plc4go/protocols/cbus/readwrite/model/MeasurementData.go @@ -69,7 +69,10 @@ type MeasurementDataRequirements interface { // _MeasurementData is the data-structure of this message type _MeasurementData struct { - _SubType MeasurementData + _SubType interface { + MeasurementDataContract + MeasurementDataRequirements + } CommandTypeContainer MeasurementCommandTypeContainer } diff --git a/plc4go/protocols/cbus/readwrite/model/MediaTransportControlData.go b/plc4go/protocols/cbus/readwrite/model/MediaTransportControlData.go index a7c7c03e17a..a7e31638da5 100644 --- a/plc4go/protocols/cbus/readwrite/model/MediaTransportControlData.go +++ b/plc4go/protocols/cbus/readwrite/model/MediaTransportControlData.go @@ -71,7 +71,10 @@ type MediaTransportControlDataRequirements interface { // _MediaTransportControlData is the data-structure of this message type _MediaTransportControlData struct { - _SubType MediaTransportControlData + _SubType interface { + MediaTransportControlDataContract + MediaTransportControlDataRequirements + } CommandTypeContainer MediaTransportControlCommandTypeContainer MediaLinkGroup byte } diff --git a/plc4go/protocols/cbus/readwrite/model/MeteringData.go b/plc4go/protocols/cbus/readwrite/model/MeteringData.go index 139d6ff172b..4000ac5e217 100644 --- a/plc4go/protocols/cbus/readwrite/model/MeteringData.go +++ b/plc4go/protocols/cbus/readwrite/model/MeteringData.go @@ -73,7 +73,10 @@ type MeteringDataRequirements interface { // _MeteringData is the data-structure of this message type _MeteringData struct { - _SubType MeteringData + _SubType interface { + MeteringDataContract + MeteringDataRequirements + } CommandTypeContainer MeteringCommandTypeContainer Argument byte } diff --git a/plc4go/protocols/cbus/readwrite/model/MonitoredSAL.go b/plc4go/protocols/cbus/readwrite/model/MonitoredSAL.go index 75dd4c9245c..69fb7c73cdd 100644 --- a/plc4go/protocols/cbus/readwrite/model/MonitoredSAL.go +++ b/plc4go/protocols/cbus/readwrite/model/MonitoredSAL.go @@ -69,8 +69,11 @@ type MonitoredSALRequirements interface { // _MonitoredSAL is the data-structure of this message type _MonitoredSAL struct { - _SubType MonitoredSAL - SalType byte + _SubType interface { + MonitoredSALContract + MonitoredSALRequirements + } + SalType byte // Arguments. CBusOptions CBusOptions diff --git a/plc4go/protocols/cbus/readwrite/model/ParameterValue.go b/plc4go/protocols/cbus/readwrite/model/ParameterValue.go index afed6512099..dba765ffb71 100644 --- a/plc4go/protocols/cbus/readwrite/model/ParameterValue.go +++ b/plc4go/protocols/cbus/readwrite/model/ParameterValue.go @@ -65,7 +65,10 @@ type ParameterValueRequirements interface { // _ParameterValue is the data-structure of this message type _ParameterValue struct { - _SubType ParameterValue + _SubType interface { + ParameterValueContract + ParameterValueRequirements + } // Arguments. NumBytes uint8 diff --git a/plc4go/protocols/cbus/readwrite/model/Reply.go b/plc4go/protocols/cbus/readwrite/model/Reply.go index 3fef6b24928..0418b3d229f 100644 --- a/plc4go/protocols/cbus/readwrite/model/Reply.go +++ b/plc4go/protocols/cbus/readwrite/model/Reply.go @@ -71,7 +71,10 @@ type ReplyRequirements interface { // _Reply is the data-structure of this message type _Reply struct { - _SubType Reply + _SubType interface { + ReplyContract + ReplyRequirements + } PeekedByte byte // Arguments. diff --git a/plc4go/protocols/cbus/readwrite/model/ReplyOrConfirmation.go b/plc4go/protocols/cbus/readwrite/model/ReplyOrConfirmation.go index de65b68dacf..53b4b997a01 100644 --- a/plc4go/protocols/cbus/readwrite/model/ReplyOrConfirmation.go +++ b/plc4go/protocols/cbus/readwrite/model/ReplyOrConfirmation.go @@ -75,7 +75,10 @@ type ReplyOrConfirmationRequirements interface { // _ReplyOrConfirmation is the data-structure of this message type _ReplyOrConfirmation struct { - _SubType ReplyOrConfirmation + _SubType interface { + ReplyOrConfirmationContract + ReplyOrConfirmationRequirements + } PeekedByte byte // Arguments. diff --git a/plc4go/protocols/cbus/readwrite/model/Request.go b/plc4go/protocols/cbus/readwrite/model/Request.go index 72b721bcd3c..52465e73dff 100644 --- a/plc4go/protocols/cbus/readwrite/model/Request.go +++ b/plc4go/protocols/cbus/readwrite/model/Request.go @@ -79,7 +79,10 @@ type RequestRequirements interface { // _Request is the data-structure of this message type _Request struct { - _SubType Request + _SubType interface { + RequestContract + RequestRequirements + } PeekedByte RequestType StartingCR *RequestType ResetMode *RequestType diff --git a/plc4go/protocols/cbus/readwrite/model/SALData.go b/plc4go/protocols/cbus/readwrite/model/SALData.go index 26d3a9a7d54..8b09aa9a257 100644 --- a/plc4go/protocols/cbus/readwrite/model/SALData.go +++ b/plc4go/protocols/cbus/readwrite/model/SALData.go @@ -67,8 +67,11 @@ type SALDataRequirements interface { // _SALData is the data-structure of this message type _SALData struct { - _SubType SALData - SalData SALData + _SubType interface { + SALDataContract + SALDataRequirements + } + SalData SALData } var _ SALDataContract = (*_SALData)(nil) diff --git a/plc4go/protocols/cbus/readwrite/model/SecurityData.go b/plc4go/protocols/cbus/readwrite/model/SecurityData.go index 384444e15aa..db3f4f412a1 100644 --- a/plc4go/protocols/cbus/readwrite/model/SecurityData.go +++ b/plc4go/protocols/cbus/readwrite/model/SecurityData.go @@ -73,7 +73,10 @@ type SecurityDataRequirements interface { // _SecurityData is the data-structure of this message type _SecurityData struct { - _SubType SecurityData + _SubType interface { + SecurityDataContract + SecurityDataRequirements + } CommandTypeContainer SecurityCommandTypeContainer Argument byte } diff --git a/plc4go/protocols/cbus/readwrite/model/StatusRequest.go b/plc4go/protocols/cbus/readwrite/model/StatusRequest.go index 7a6ab37ab9c..5f3037f8f5b 100644 --- a/plc4go/protocols/cbus/readwrite/model/StatusRequest.go +++ b/plc4go/protocols/cbus/readwrite/model/StatusRequest.go @@ -67,7 +67,10 @@ type StatusRequestRequirements interface { // _StatusRequest is the data-structure of this message type _StatusRequest struct { - _SubType StatusRequest + _SubType interface { + StatusRequestContract + StatusRequestRequirements + } StatusType byte } diff --git a/plc4go/protocols/cbus/readwrite/model/TelephonyData.go b/plc4go/protocols/cbus/readwrite/model/TelephonyData.go index 5344c44e760..e125919c194 100644 --- a/plc4go/protocols/cbus/readwrite/model/TelephonyData.go +++ b/plc4go/protocols/cbus/readwrite/model/TelephonyData.go @@ -73,7 +73,10 @@ type TelephonyDataRequirements interface { // _TelephonyData is the data-structure of this message type _TelephonyData struct { - _SubType TelephonyData + _SubType interface { + TelephonyDataContract + TelephonyDataRequirements + } CommandTypeContainer TelephonyCommandTypeContainer Argument byte } diff --git a/plc4go/protocols/cbus/readwrite/model/TriggerControlData.go b/plc4go/protocols/cbus/readwrite/model/TriggerControlData.go index 113eccaa440..ad53d0880eb 100644 --- a/plc4go/protocols/cbus/readwrite/model/TriggerControlData.go +++ b/plc4go/protocols/cbus/readwrite/model/TriggerControlData.go @@ -73,7 +73,10 @@ type TriggerControlDataRequirements interface { // _TriggerControlData is the data-structure of this message type _TriggerControlData struct { - _SubType TriggerControlData + _SubType interface { + TriggerControlDataContract + TriggerControlDataRequirements + } CommandTypeContainer TriggerControlCommandTypeContainer TriggerGroup byte } diff --git a/plc4go/protocols/df1/readwrite/model/DF1Command.go b/plc4go/protocols/df1/readwrite/model/DF1Command.go index e047da34e3e..22c48c3eaa3 100644 --- a/plc4go/protocols/df1/readwrite/model/DF1Command.go +++ b/plc4go/protocols/df1/readwrite/model/DF1Command.go @@ -69,7 +69,10 @@ type DF1CommandRequirements interface { // _DF1Command is the data-structure of this message type _DF1Command struct { - _SubType DF1Command + _SubType interface { + DF1CommandContract + DF1CommandRequirements + } Status uint8 TransactionCounter uint16 } diff --git a/plc4go/protocols/df1/readwrite/model/DF1Symbol.go b/plc4go/protocols/df1/readwrite/model/DF1Symbol.go index b1434ee5290..733c71ac8b7 100644 --- a/plc4go/protocols/df1/readwrite/model/DF1Symbol.go +++ b/plc4go/protocols/df1/readwrite/model/DF1Symbol.go @@ -70,7 +70,10 @@ type DF1SymbolRequirements interface { // _DF1Symbol is the data-structure of this message type _DF1Symbol struct { - _SubType DF1Symbol + _SubType interface { + DF1SymbolContract + DF1SymbolRequirements + } } var _ DF1SymbolContract = (*_DF1Symbol)(nil) diff --git a/plc4go/protocols/eip/readwrite/model/CipService.go b/plc4go/protocols/eip/readwrite/model/CipService.go index f27a71bbd46..9ad37c789c2 100644 --- a/plc4go/protocols/eip/readwrite/model/CipService.go +++ b/plc4go/protocols/eip/readwrite/model/CipService.go @@ -71,7 +71,10 @@ type CipServiceRequirements interface { // _CipService is the data-structure of this message type _CipService struct { - _SubType CipService + _SubType interface { + CipServiceContract + CipServiceRequirements + } // Arguments. ServiceLen uint16 diff --git a/plc4go/protocols/eip/readwrite/model/CommandSpecificDataItem.go b/plc4go/protocols/eip/readwrite/model/CommandSpecificDataItem.go index ad97c023a19..696d1e66cab 100644 --- a/plc4go/protocols/eip/readwrite/model/CommandSpecificDataItem.go +++ b/plc4go/protocols/eip/readwrite/model/CommandSpecificDataItem.go @@ -65,7 +65,10 @@ type CommandSpecificDataItemRequirements interface { // _CommandSpecificDataItem is the data-structure of this message type _CommandSpecificDataItem struct { - _SubType CommandSpecificDataItem + _SubType interface { + CommandSpecificDataItemContract + CommandSpecificDataItemRequirements + } } var _ CommandSpecificDataItemContract = (*_CommandSpecificDataItem)(nil) diff --git a/plc4go/protocols/eip/readwrite/model/DataSegmentType.go b/plc4go/protocols/eip/readwrite/model/DataSegmentType.go index 28a12c656af..83c19ac6ec8 100644 --- a/plc4go/protocols/eip/readwrite/model/DataSegmentType.go +++ b/plc4go/protocols/eip/readwrite/model/DataSegmentType.go @@ -65,7 +65,10 @@ type DataSegmentTypeRequirements interface { // _DataSegmentType is the data-structure of this message type _DataSegmentType struct { - _SubType DataSegmentType + _SubType interface { + DataSegmentTypeContract + DataSegmentTypeRequirements + } } var _ DataSegmentTypeContract = (*_DataSegmentType)(nil) diff --git a/plc4go/protocols/eip/readwrite/model/EipPacket.go b/plc4go/protocols/eip/readwrite/model/EipPacket.go index 36db6dda43d..73a7a264558 100644 --- a/plc4go/protocols/eip/readwrite/model/EipPacket.go +++ b/plc4go/protocols/eip/readwrite/model/EipPacket.go @@ -77,7 +77,10 @@ type EipPacketRequirements interface { // _EipPacket is the data-structure of this message type _EipPacket struct { - _SubType EipPacket + _SubType interface { + EipPacketContract + EipPacketRequirements + } SessionHandle uint32 Status uint32 SenderContext []byte diff --git a/plc4go/protocols/eip/readwrite/model/LogicalSegmentType.go b/plc4go/protocols/eip/readwrite/model/LogicalSegmentType.go index 6f9bfd17fa5..7bd4cf06ce4 100644 --- a/plc4go/protocols/eip/readwrite/model/LogicalSegmentType.go +++ b/plc4go/protocols/eip/readwrite/model/LogicalSegmentType.go @@ -65,7 +65,10 @@ type LogicalSegmentTypeRequirements interface { // _LogicalSegmentType is the data-structure of this message type _LogicalSegmentType struct { - _SubType LogicalSegmentType + _SubType interface { + LogicalSegmentTypeContract + LogicalSegmentTypeRequirements + } } var _ LogicalSegmentTypeContract = (*_LogicalSegmentType)(nil) diff --git a/plc4go/protocols/eip/readwrite/model/PathSegment.go b/plc4go/protocols/eip/readwrite/model/PathSegment.go index 27a4eabdd13..7417ea2244a 100644 --- a/plc4go/protocols/eip/readwrite/model/PathSegment.go +++ b/plc4go/protocols/eip/readwrite/model/PathSegment.go @@ -65,7 +65,10 @@ type PathSegmentRequirements interface { // _PathSegment is the data-structure of this message type _PathSegment struct { - _SubType PathSegment + _SubType interface { + PathSegmentContract + PathSegmentRequirements + } } var _ PathSegmentContract = (*_PathSegment)(nil) diff --git a/plc4go/protocols/eip/readwrite/model/PortSegmentType.go b/plc4go/protocols/eip/readwrite/model/PortSegmentType.go index a8431bf8595..df3b9823ba1 100644 --- a/plc4go/protocols/eip/readwrite/model/PortSegmentType.go +++ b/plc4go/protocols/eip/readwrite/model/PortSegmentType.go @@ -65,7 +65,10 @@ type PortSegmentTypeRequirements interface { // _PortSegmentType is the data-structure of this message type _PortSegmentType struct { - _SubType PortSegmentType + _SubType interface { + PortSegmentTypeContract + PortSegmentTypeRequirements + } } var _ PortSegmentTypeContract = (*_PortSegmentType)(nil) diff --git a/plc4go/protocols/eip/readwrite/model/TypeId.go b/plc4go/protocols/eip/readwrite/model/TypeId.go index 4932978b9dd..8c5a76883c8 100644 --- a/plc4go/protocols/eip/readwrite/model/TypeId.go +++ b/plc4go/protocols/eip/readwrite/model/TypeId.go @@ -65,7 +65,10 @@ type TypeIdRequirements interface { // _TypeId is the data-structure of this message type _TypeId struct { - _SubType TypeId + _SubType interface { + TypeIdContract + TypeIdRequirements + } } var _ TypeIdContract = (*_TypeId)(nil) diff --git a/plc4go/protocols/firmata/readwrite/model/FirmataCommand.go b/plc4go/protocols/firmata/readwrite/model/FirmataCommand.go index cbea6538bd3..2249f80e0f4 100644 --- a/plc4go/protocols/firmata/readwrite/model/FirmataCommand.go +++ b/plc4go/protocols/firmata/readwrite/model/FirmataCommand.go @@ -67,7 +67,10 @@ type FirmataCommandRequirements interface { // _FirmataCommand is the data-structure of this message type _FirmataCommand struct { - _SubType FirmataCommand + _SubType interface { + FirmataCommandContract + FirmataCommandRequirements + } // Arguments. Response bool diff --git a/plc4go/protocols/firmata/readwrite/model/FirmataMessage.go b/plc4go/protocols/firmata/readwrite/model/FirmataMessage.go index cfedb2f88a4..578817597fc 100644 --- a/plc4go/protocols/firmata/readwrite/model/FirmataMessage.go +++ b/plc4go/protocols/firmata/readwrite/model/FirmataMessage.go @@ -69,7 +69,10 @@ type FirmataMessageRequirements interface { // _FirmataMessage is the data-structure of this message type _FirmataMessage struct { - _SubType FirmataMessage + _SubType interface { + FirmataMessageContract + FirmataMessageRequirements + } // Arguments. Response bool diff --git a/plc4go/protocols/firmata/readwrite/model/SysexCommand.go b/plc4go/protocols/firmata/readwrite/model/SysexCommand.go index bb44f5d795c..6bcdb063baf 100644 --- a/plc4go/protocols/firmata/readwrite/model/SysexCommand.go +++ b/plc4go/protocols/firmata/readwrite/model/SysexCommand.go @@ -67,7 +67,10 @@ type SysexCommandRequirements interface { // _SysexCommand is the data-structure of this message type _SysexCommand struct { - _SubType SysexCommand + _SubType interface { + SysexCommandContract + SysexCommandRequirements + } } var _ SysexCommandContract = (*_SysexCommand)(nil) diff --git a/plc4go/protocols/knxnetip/readwrite/model/Apdu.go b/plc4go/protocols/knxnetip/readwrite/model/Apdu.go index e451e0ff7b6..d5e4e860509 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/Apdu.go +++ b/plc4go/protocols/knxnetip/readwrite/model/Apdu.go @@ -71,7 +71,10 @@ type ApduRequirements interface { // _Apdu is the data-structure of this message type _Apdu struct { - _SubType Apdu + _SubType interface { + ApduContract + ApduRequirements + } Numbered bool Counter uint8 diff --git a/plc4go/protocols/knxnetip/readwrite/model/ApduControl.go b/plc4go/protocols/knxnetip/readwrite/model/ApduControl.go index d7ea126a5f5..41cfdd99e34 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/ApduControl.go +++ b/plc4go/protocols/knxnetip/readwrite/model/ApduControl.go @@ -65,7 +65,10 @@ type ApduControlRequirements interface { // _ApduControl is the data-structure of this message type _ApduControl struct { - _SubType ApduControl + _SubType interface { + ApduControlContract + ApduControlRequirements + } } var _ ApduControlContract = (*_ApduControl)(nil) diff --git a/plc4go/protocols/knxnetip/readwrite/model/ApduData.go b/plc4go/protocols/knxnetip/readwrite/model/ApduData.go index ae24aff7f79..07cbe76560d 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/ApduData.go +++ b/plc4go/protocols/knxnetip/readwrite/model/ApduData.go @@ -67,7 +67,10 @@ type ApduDataRequirements interface { // _ApduData is the data-structure of this message type _ApduData struct { - _SubType ApduData + _SubType interface { + ApduDataContract + ApduDataRequirements + } // Arguments. DataLength uint8 diff --git a/plc4go/protocols/knxnetip/readwrite/model/ApduDataExt.go b/plc4go/protocols/knxnetip/readwrite/model/ApduDataExt.go index 4e98a3f0c3f..b2f25dcd016 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/ApduDataExt.go +++ b/plc4go/protocols/knxnetip/readwrite/model/ApduDataExt.go @@ -67,7 +67,10 @@ type ApduDataExtRequirements interface { // _ApduDataExt is the data-structure of this message type _ApduDataExt struct { - _SubType ApduDataExt + _SubType interface { + ApduDataExtContract + ApduDataExtRequirements + } // Arguments. Length uint8 diff --git a/plc4go/protocols/knxnetip/readwrite/model/CEMI.go b/plc4go/protocols/knxnetip/readwrite/model/CEMI.go index 3f450931967..367960e7227 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/CEMI.go +++ b/plc4go/protocols/knxnetip/readwrite/model/CEMI.go @@ -67,7 +67,10 @@ type CEMIRequirements interface { // _CEMI is the data-structure of this message type _CEMI struct { - _SubType CEMI + _SubType interface { + CEMIContract + CEMIRequirements + } // Arguments. Size uint16 diff --git a/plc4go/protocols/knxnetip/readwrite/model/CEMIAdditionalInformation.go b/plc4go/protocols/knxnetip/readwrite/model/CEMIAdditionalInformation.go index 1edf442c5fd..d2aaa985c43 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/CEMIAdditionalInformation.go +++ b/plc4go/protocols/knxnetip/readwrite/model/CEMIAdditionalInformation.go @@ -65,7 +65,10 @@ type CEMIAdditionalInformationRequirements interface { // _CEMIAdditionalInformation is the data-structure of this message type _CEMIAdditionalInformation struct { - _SubType CEMIAdditionalInformation + _SubType interface { + CEMIAdditionalInformationContract + CEMIAdditionalInformationRequirements + } } var _ CEMIAdditionalInformationContract = (*_CEMIAdditionalInformation)(nil) diff --git a/plc4go/protocols/knxnetip/readwrite/model/ComObjectTable.go b/plc4go/protocols/knxnetip/readwrite/model/ComObjectTable.go index 063aa2624da..d92747f7ba9 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/ComObjectTable.go +++ b/plc4go/protocols/knxnetip/readwrite/model/ComObjectTable.go @@ -63,7 +63,10 @@ type ComObjectTableRequirements interface { // _ComObjectTable is the data-structure of this message type _ComObjectTable struct { - _SubType ComObjectTable + _SubType interface { + ComObjectTableContract + ComObjectTableRequirements + } } var _ ComObjectTableContract = (*_ComObjectTable)(nil) diff --git a/plc4go/protocols/knxnetip/readwrite/model/ConnectionRequestInformation.go b/plc4go/protocols/knxnetip/readwrite/model/ConnectionRequestInformation.go index fa1bc0a6240..4e61b564b9e 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/ConnectionRequestInformation.go +++ b/plc4go/protocols/knxnetip/readwrite/model/ConnectionRequestInformation.go @@ -65,7 +65,10 @@ type ConnectionRequestInformationRequirements interface { // _ConnectionRequestInformation is the data-structure of this message type _ConnectionRequestInformation struct { - _SubType ConnectionRequestInformation + _SubType interface { + ConnectionRequestInformationContract + ConnectionRequestInformationRequirements + } } var _ ConnectionRequestInformationContract = (*_ConnectionRequestInformation)(nil) diff --git a/plc4go/protocols/knxnetip/readwrite/model/ConnectionResponseDataBlock.go b/plc4go/protocols/knxnetip/readwrite/model/ConnectionResponseDataBlock.go index 45e5b936200..f580dbcedc1 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/ConnectionResponseDataBlock.go +++ b/plc4go/protocols/knxnetip/readwrite/model/ConnectionResponseDataBlock.go @@ -65,7 +65,10 @@ type ConnectionResponseDataBlockRequirements interface { // _ConnectionResponseDataBlock is the data-structure of this message type _ConnectionResponseDataBlock struct { - _SubType ConnectionResponseDataBlock + _SubType interface { + ConnectionResponseDataBlockContract + ConnectionResponseDataBlockRequirements + } } var _ ConnectionResponseDataBlockContract = (*_ConnectionResponseDataBlock)(nil) diff --git a/plc4go/protocols/knxnetip/readwrite/model/KnxGroupAddress.go b/plc4go/protocols/knxnetip/readwrite/model/KnxGroupAddress.go index 6c1f79998f3..93fc916550f 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/KnxGroupAddress.go +++ b/plc4go/protocols/knxnetip/readwrite/model/KnxGroupAddress.go @@ -63,7 +63,10 @@ type KnxGroupAddressRequirements interface { // _KnxGroupAddress is the data-structure of this message type _KnxGroupAddress struct { - _SubType KnxGroupAddress + _SubType interface { + KnxGroupAddressContract + KnxGroupAddressRequirements + } } var _ KnxGroupAddressContract = (*_KnxGroupAddress)(nil) diff --git a/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go b/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go index e7e0e82b443..92fa8e6aad3 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go +++ b/plc4go/protocols/knxnetip/readwrite/model/KnxManufacturer.go @@ -734,8 +734,9 @@ const ( KnxManufacturer_M_ITALIANA_CONDUTTORI_SRL KnxManufacturer = 688 KnxManufacturer_M_XIAMEN_LEELEN_TECHNOLOGY_CO__LTD_ KnxManufacturer = 689 KnxManufacturer_M_LEDNX KnxManufacturer = 690 - KnxManufacturer_M_ABB___RESERVED KnxManufacturer = 691 - KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED KnxManufacturer = 692 + KnxManufacturer_M_EBELONG KnxManufacturer = 691 + KnxManufacturer_M_ABB___RESERVED KnxManufacturer = 692 + KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED KnxManufacturer = 693 ) var KnxManufacturerValues []KnxManufacturer @@ -1434,6 +1435,7 @@ func init() { KnxManufacturer_M_ITALIANA_CONDUTTORI_SRL, KnxManufacturer_M_XIAMEN_LEELEN_TECHNOLOGY_CO__LTD_, KnxManufacturer_M_LEDNX, + KnxManufacturer_M_EBELONG, KnxManufacturer_M_ABB___RESERVED, KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED, } @@ -4075,10 +4077,14 @@ func (e KnxManufacturer) Number() uint16 { } case 691: { /* '691' */ - return 43954 + return 749 } case 692: { /* '692' */ + return 43954 + } + case 693: + { /* '693' */ return 43959 } case 7: @@ -6865,10 +6871,14 @@ func (e KnxManufacturer) Name() string { } case 691: { /* '691' */ - return "ABB - reserved" + return "ebelong" } case 692: { /* '692' */ + return "ABB - reserved" + } + case 693: + { /* '693' */ return "Busch-Jaeger Elektro - reserved" } case 7: @@ -8337,8 +8347,10 @@ func KnxManufacturerByValue(value uint16) (enum KnxManufacturer, ok bool) { case 690: return KnxManufacturer_M_LEDNX, true case 691: - return KnxManufacturer_M_ABB___RESERVED, true + return KnxManufacturer_M_EBELONG, true case 692: + return KnxManufacturer_M_ABB___RESERVED, true + case 693: return KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED, true case 7: return KnxManufacturer_M_GIRA_GIERSIEPEN, true @@ -9728,6 +9740,8 @@ func KnxManufacturerByName(value string) (enum KnxManufacturer, ok bool) { return KnxManufacturer_M_EELECTRON, true case "M_LEDNX": return KnxManufacturer_M_LEDNX, true + case "M_EBELONG": + return KnxManufacturer_M_EBELONG, true case "M_ABB___RESERVED": return KnxManufacturer_M_ABB___RESERVED, true case "M_BUSCH_JAEGER_ELEKTRO___RESERVED": @@ -11192,6 +11206,8 @@ func (e KnxManufacturer) PLC4XEnumName() string { return "M_EELECTRON" case KnxManufacturer_M_LEDNX: return "M_LEDNX" + case KnxManufacturer_M_EBELONG: + return "M_EBELONG" case KnxManufacturer_M_ABB___RESERVED: return "M_ABB___RESERVED" case KnxManufacturer_M_BUSCH_JAEGER_ELEKTRO___RESERVED: diff --git a/plc4go/protocols/knxnetip/readwrite/model/KnxNetIpMessage.go b/plc4go/protocols/knxnetip/readwrite/model/KnxNetIpMessage.go index da6c595f595..43bdcf1a0cc 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/KnxNetIpMessage.go +++ b/plc4go/protocols/knxnetip/readwrite/model/KnxNetIpMessage.go @@ -70,7 +70,10 @@ type KnxNetIpMessageRequirements interface { // _KnxNetIpMessage is the data-structure of this message type _KnxNetIpMessage struct { - _SubType KnxNetIpMessage + _SubType interface { + KnxNetIpMessageContract + KnxNetIpMessageRequirements + } } var _ KnxNetIpMessageContract = (*_KnxNetIpMessage)(nil) diff --git a/plc4go/protocols/knxnetip/readwrite/model/LDataFrame.go b/plc4go/protocols/knxnetip/readwrite/model/LDataFrame.go index 0a13164ce40..d41d7fb339a 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/LDataFrame.go +++ b/plc4go/protocols/knxnetip/readwrite/model/LDataFrame.go @@ -77,7 +77,10 @@ type LDataFrameRequirements interface { // _LDataFrame is the data-structure of this message type _LDataFrame struct { - _SubType LDataFrame + _SubType interface { + LDataFrameContract + LDataFrameRequirements + } FrameType bool NotRepeated bool Priority CEMIPriority diff --git a/plc4go/protocols/knxnetip/readwrite/model/ServiceId.go b/plc4go/protocols/knxnetip/readwrite/model/ServiceId.go index e0b8f289d3f..ce1f2ec81c2 100644 --- a/plc4go/protocols/knxnetip/readwrite/model/ServiceId.go +++ b/plc4go/protocols/knxnetip/readwrite/model/ServiceId.go @@ -65,7 +65,10 @@ type ServiceIdRequirements interface { // _ServiceId is the data-structure of this message type _ServiceId struct { - _SubType ServiceId + _SubType interface { + ServiceIdContract + ServiceIdRequirements + } } var _ ServiceIdContract = (*_ServiceId)(nil) diff --git a/plc4go/protocols/modbus/readwrite/model/ModbusADU.go b/plc4go/protocols/modbus/readwrite/model/ModbusADU.go index c8033eac511..2d6b25f56a2 100644 --- a/plc4go/protocols/modbus/readwrite/model/ModbusADU.go +++ b/plc4go/protocols/modbus/readwrite/model/ModbusADU.go @@ -66,7 +66,10 @@ type ModbusADURequirements interface { // _ModbusADU is the data-structure of this message type _ModbusADU struct { - _SubType ModbusADU + _SubType interface { + ModbusADUContract + ModbusADURequirements + } // Arguments. Response bool diff --git a/plc4go/protocols/modbus/readwrite/model/ModbusPDU.go b/plc4go/protocols/modbus/readwrite/model/ModbusPDU.go index 11393507eea..d8840ce5774 100644 --- a/plc4go/protocols/modbus/readwrite/model/ModbusPDU.go +++ b/plc4go/protocols/modbus/readwrite/model/ModbusPDU.go @@ -69,7 +69,10 @@ type ModbusPDURequirements interface { // _ModbusPDU is the data-structure of this message type _ModbusPDU struct { - _SubType ModbusPDU + _SubType interface { + ModbusPDUContract + ModbusPDURequirements + } } var _ ModbusPDUContract = (*_ModbusPDU)(nil) diff --git a/plc4go/protocols/opcua/readwrite/model/ExtensionObjectDefinition.go b/plc4go/protocols/opcua/readwrite/model/ExtensionObjectDefinition.go index 74363c25dd4..42fd29c639e 100644 --- a/plc4go/protocols/opcua/readwrite/model/ExtensionObjectDefinition.go +++ b/plc4go/protocols/opcua/readwrite/model/ExtensionObjectDefinition.go @@ -63,7 +63,10 @@ type ExtensionObjectDefinitionRequirements interface { // _ExtensionObjectDefinition is the data-structure of this message type _ExtensionObjectDefinition struct { - _SubType ExtensionObjectDefinition + _SubType interface { + ExtensionObjectDefinitionContract + ExtensionObjectDefinitionRequirements + } } var _ ExtensionObjectDefinitionContract = (*_ExtensionObjectDefinition)(nil) diff --git a/plc4go/protocols/opcua/readwrite/model/MessagePDU.go b/plc4go/protocols/opcua/readwrite/model/MessagePDU.go index 429f4e097b2..b3ca4aad8e7 100644 --- a/plc4go/protocols/opcua/readwrite/model/MessagePDU.go +++ b/plc4go/protocols/opcua/readwrite/model/MessagePDU.go @@ -69,8 +69,11 @@ type MessagePDURequirements interface { // _MessagePDU is the data-structure of this message type _MessagePDU struct { - _SubType MessagePDU - Chunk ChunkType + _SubType interface { + MessagePDUContract + MessagePDURequirements + } + Chunk ChunkType } var _ MessagePDUContract = (*_MessagePDU)(nil) diff --git a/plc4go/protocols/opcua/readwrite/model/NodeIdTypeDefinition.go b/plc4go/protocols/opcua/readwrite/model/NodeIdTypeDefinition.go index 8b81409aaec..8958237c501 100644 --- a/plc4go/protocols/opcua/readwrite/model/NodeIdTypeDefinition.go +++ b/plc4go/protocols/opcua/readwrite/model/NodeIdTypeDefinition.go @@ -69,7 +69,10 @@ type NodeIdTypeDefinitionRequirements interface { // _NodeIdTypeDefinition is the data-structure of this message type _NodeIdTypeDefinition struct { - _SubType NodeIdTypeDefinition + _SubType interface { + NodeIdTypeDefinitionContract + NodeIdTypeDefinitionRequirements + } } var _ NodeIdTypeDefinitionContract = (*_NodeIdTypeDefinition)(nil) diff --git a/plc4go/protocols/opcua/readwrite/model/OpenChannelMessage.go b/plc4go/protocols/opcua/readwrite/model/OpenChannelMessage.go index 31bfcc871ee..faf89033f9c 100644 --- a/plc4go/protocols/opcua/readwrite/model/OpenChannelMessage.go +++ b/plc4go/protocols/opcua/readwrite/model/OpenChannelMessage.go @@ -63,7 +63,10 @@ type OpenChannelMessageRequirements interface { // _OpenChannelMessage is the data-structure of this message type _OpenChannelMessage struct { - _SubType OpenChannelMessage + _SubType interface { + OpenChannelMessageContract + OpenChannelMessageRequirements + } } var _ OpenChannelMessageContract = (*_OpenChannelMessage)(nil) diff --git a/plc4go/protocols/opcua/readwrite/model/Payload.go b/plc4go/protocols/opcua/readwrite/model/Payload.go index 7fa072452f5..e7380124826 100644 --- a/plc4go/protocols/opcua/readwrite/model/Payload.go +++ b/plc4go/protocols/opcua/readwrite/model/Payload.go @@ -69,7 +69,10 @@ type PayloadRequirements interface { // _Payload is the data-structure of this message type _Payload struct { - _SubType Payload + _SubType interface { + PayloadContract + PayloadRequirements + } SequenceHeader SequenceHeader // Arguments. diff --git a/plc4go/protocols/opcua/readwrite/model/UserIdentityTokenDefinition.go b/plc4go/protocols/opcua/readwrite/model/UserIdentityTokenDefinition.go index b08290150bd..112c5125254 100644 --- a/plc4go/protocols/opcua/readwrite/model/UserIdentityTokenDefinition.go +++ b/plc4go/protocols/opcua/readwrite/model/UserIdentityTokenDefinition.go @@ -63,7 +63,10 @@ type UserIdentityTokenDefinitionRequirements interface { // _UserIdentityTokenDefinition is the data-structure of this message type _UserIdentityTokenDefinition struct { - _SubType UserIdentityTokenDefinition + _SubType interface { + UserIdentityTokenDefinitionContract + UserIdentityTokenDefinitionRequirements + } } var _ UserIdentityTokenDefinitionContract = (*_UserIdentityTokenDefinition)(nil) diff --git a/plc4go/protocols/opcua/readwrite/model/Variant.go b/plc4go/protocols/opcua/readwrite/model/Variant.go index 19bab5f54a8..0efb24d2c57 100644 --- a/plc4go/protocols/opcua/readwrite/model/Variant.go +++ b/plc4go/protocols/opcua/readwrite/model/Variant.go @@ -75,7 +75,10 @@ type VariantRequirements interface { // _Variant is the data-structure of this message type _Variant struct { - _SubType Variant + _SubType interface { + VariantContract + VariantRequirements + } ArrayLengthSpecified bool ArrayDimensionsSpecified bool NoOfArrayDimensions *int32 diff --git a/plc4go/protocols/s7/readwrite/model/COTPPacket.go b/plc4go/protocols/s7/readwrite/model/COTPPacket.go index 636232487be..84c06f27d42 100644 --- a/plc4go/protocols/s7/readwrite/model/COTPPacket.go +++ b/plc4go/protocols/s7/readwrite/model/COTPPacket.go @@ -71,7 +71,10 @@ type COTPPacketRequirements interface { // _COTPPacket is the data-structure of this message type _COTPPacket struct { - _SubType COTPPacket + _SubType interface { + COTPPacketContract + COTPPacketRequirements + } Parameters []COTPParameter Payload S7Message diff --git a/plc4go/protocols/s7/readwrite/model/COTPParameter.go b/plc4go/protocols/s7/readwrite/model/COTPParameter.go index d2a386366f6..cb2986d34ce 100644 --- a/plc4go/protocols/s7/readwrite/model/COTPParameter.go +++ b/plc4go/protocols/s7/readwrite/model/COTPParameter.go @@ -67,7 +67,10 @@ type COTPParameterRequirements interface { // _COTPParameter is the data-structure of this message type _COTPParameter struct { - _SubType COTPParameter + _SubType interface { + COTPParameterContract + COTPParameterRequirements + } // Arguments. Rest uint8 diff --git a/plc4go/protocols/s7/readwrite/model/CycServiceItemType.go b/plc4go/protocols/s7/readwrite/model/CycServiceItemType.go index d4a6583f65d..dc1328301e9 100644 --- a/plc4go/protocols/s7/readwrite/model/CycServiceItemType.go +++ b/plc4go/protocols/s7/readwrite/model/CycServiceItemType.go @@ -72,7 +72,10 @@ type CycServiceItemTypeRequirements interface { // _CycServiceItemType is the data-structure of this message type _CycServiceItemType struct { - _SubType CycServiceItemType + _SubType interface { + CycServiceItemTypeContract + CycServiceItemTypeRequirements + } ByteLength uint8 SyntaxId uint8 } diff --git a/plc4go/protocols/s7/readwrite/model/S7Address.go b/plc4go/protocols/s7/readwrite/model/S7Address.go index 7e651060f15..1b3073ffa94 100644 --- a/plc4go/protocols/s7/readwrite/model/S7Address.go +++ b/plc4go/protocols/s7/readwrite/model/S7Address.go @@ -65,7 +65,10 @@ type S7AddressRequirements interface { // _S7Address is the data-structure of this message type _S7Address struct { - _SubType S7Address + _SubType interface { + S7AddressContract + S7AddressRequirements + } } var _ S7AddressContract = (*_S7Address)(nil) diff --git a/plc4go/protocols/s7/readwrite/model/S7DataAlarmMessage.go b/plc4go/protocols/s7/readwrite/model/S7DataAlarmMessage.go index eb532f2f50e..eed0881c7ce 100644 --- a/plc4go/protocols/s7/readwrite/model/S7DataAlarmMessage.go +++ b/plc4go/protocols/s7/readwrite/model/S7DataAlarmMessage.go @@ -69,7 +69,10 @@ type S7DataAlarmMessageRequirements interface { // _S7DataAlarmMessage is the data-structure of this message type _S7DataAlarmMessage struct { - _SubType S7DataAlarmMessage + _SubType interface { + S7DataAlarmMessageContract + S7DataAlarmMessageRequirements + } } var _ S7DataAlarmMessageContract = (*_S7DataAlarmMessage)(nil) diff --git a/plc4go/protocols/s7/readwrite/model/S7Message.go b/plc4go/protocols/s7/readwrite/model/S7Message.go index 8b8d88a4a63..85ab659384a 100644 --- a/plc4go/protocols/s7/readwrite/model/S7Message.go +++ b/plc4go/protocols/s7/readwrite/model/S7Message.go @@ -74,7 +74,10 @@ type S7MessageRequirements interface { // _S7Message is the data-structure of this message type _S7Message struct { - _SubType S7Message + _SubType interface { + S7MessageContract + S7MessageRequirements + } TpduReference uint16 Parameter S7Parameter Payload S7Payload diff --git a/plc4go/protocols/s7/readwrite/model/S7Parameter.go b/plc4go/protocols/s7/readwrite/model/S7Parameter.go index 2f7e06161be..fb26868d5ca 100644 --- a/plc4go/protocols/s7/readwrite/model/S7Parameter.go +++ b/plc4go/protocols/s7/readwrite/model/S7Parameter.go @@ -67,7 +67,10 @@ type S7ParameterRequirements interface { // _S7Parameter is the data-structure of this message type _S7Parameter struct { - _SubType S7Parameter + _SubType interface { + S7ParameterContract + S7ParameterRequirements + } } var _ S7ParameterContract = (*_S7Parameter)(nil) diff --git a/plc4go/protocols/s7/readwrite/model/S7ParameterUserDataItem.go b/plc4go/protocols/s7/readwrite/model/S7ParameterUserDataItem.go index 9608b095d3b..c812ca31867 100644 --- a/plc4go/protocols/s7/readwrite/model/S7ParameterUserDataItem.go +++ b/plc4go/protocols/s7/readwrite/model/S7ParameterUserDataItem.go @@ -65,7 +65,10 @@ type S7ParameterUserDataItemRequirements interface { // _S7ParameterUserDataItem is the data-structure of this message type _S7ParameterUserDataItem struct { - _SubType S7ParameterUserDataItem + _SubType interface { + S7ParameterUserDataItemContract + S7ParameterUserDataItemRequirements + } } var _ S7ParameterUserDataItemContract = (*_S7ParameterUserDataItem)(nil) diff --git a/plc4go/protocols/s7/readwrite/model/S7Payload.go b/plc4go/protocols/s7/readwrite/model/S7Payload.go index a54e7038eff..bfb79e6eaa1 100644 --- a/plc4go/protocols/s7/readwrite/model/S7Payload.go +++ b/plc4go/protocols/s7/readwrite/model/S7Payload.go @@ -67,7 +67,10 @@ type S7PayloadRequirements interface { // _S7Payload is the data-structure of this message type _S7Payload struct { - _SubType S7Payload + _SubType interface { + S7PayloadContract + S7PayloadRequirements + } // Arguments. Parameter S7Parameter diff --git a/plc4go/protocols/s7/readwrite/model/S7PayloadUserDataItem.go b/plc4go/protocols/s7/readwrite/model/S7PayloadUserDataItem.go index 4e626a2a938..d2b56fabe7d 100644 --- a/plc4go/protocols/s7/readwrite/model/S7PayloadUserDataItem.go +++ b/plc4go/protocols/s7/readwrite/model/S7PayloadUserDataItem.go @@ -77,7 +77,10 @@ type S7PayloadUserDataItemRequirements interface { // _S7PayloadUserDataItem is the data-structure of this message type _S7PayloadUserDataItem struct { - _SubType S7PayloadUserDataItem + _SubType interface { + S7PayloadUserDataItemContract + S7PayloadUserDataItemRequirements + } ReturnCode DataTransportErrorCode TransportSize DataTransportSize DataLength uint16 diff --git a/plc4go/protocols/s7/readwrite/model/S7VarRequestParameterItem.go b/plc4go/protocols/s7/readwrite/model/S7VarRequestParameterItem.go index 00a78bca6ac..0cc77c6e01d 100644 --- a/plc4go/protocols/s7/readwrite/model/S7VarRequestParameterItem.go +++ b/plc4go/protocols/s7/readwrite/model/S7VarRequestParameterItem.go @@ -65,7 +65,10 @@ type S7VarRequestParameterItemRequirements interface { // _S7VarRequestParameterItem is the data-structure of this message type _S7VarRequestParameterItem struct { - _SubType S7VarRequestParameterItem + _SubType interface { + S7VarRequestParameterItemContract + S7VarRequestParameterItemRequirements + } } var _ S7VarRequestParameterItemContract = (*_S7VarRequestParameterItem)(nil) diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcMetadataKeys.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcMetadataKeys.java new file mode 100644 index 00000000000..e88ca207f32 --- /dev/null +++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcMetadataKeys.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.api.messages; + +import org.apache.plc4x.java.api.metadata.Metadata.Key; +import org.apache.plc4x.java.api.metadata.time.TimeSource; + +/** + * High level definition of common metadata keys which can occur across multiple drivers. + */ +public interface PlcMetadataKeys { + + Key TIMESTAMP = Key.of("timestamp", Long.class); + Key TIMESTAMP_SOURCE = Key.of("timestamp_source", TimeSource.class); + + Key RECEIVE_TIMESTAMP = Key.of("receive_timestamp", Long.class); + +} diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcTagResponse.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcTagResponse.java index 3aa2d642deb..1feae9f3bbb 100644 --- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcTagResponse.java +++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcTagResponse.java @@ -18,6 +18,7 @@ */ package org.apache.plc4x.java.api.messages; +import org.apache.plc4x.java.api.metadata.Metadata; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.types.PlcResponseCode; @@ -38,4 +39,9 @@ public interface PlcTagResponse extends PlcResponse { PlcResponseCode getResponseCode(String name); + /** + * Returns tag level metadata information. + */ + Metadata getTagMetadata(String name); + } diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/Metadata.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/Metadata.java new file mode 100644 index 00000000000..484fad7148a --- /dev/null +++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/Metadata.java @@ -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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.api.metadata; + +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public interface Metadata { + + Metadata EMPTY = new Metadata() { + @Override + public Set> keys() { + return Set.of(); + } + + @Override + public Map, Object> entries() { + return Map.of(); + } + + @Override + public Object getValue(Key key) { + return null; + } + + }; + + class Key { + + private final String key; + private final Class type; + + protected Key(String key, Class type) { + this.key = key; + this.type = type; + } + + public String getKey() { + return key; + } + + public boolean validate(Object value) { + return type.isInstance(value); + } + + public static Key of(String key, Class type) { + return new Key<>(key, type); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Key)) { + return false; + } + Key key1 = (Key) o; + return Objects.equals(getKey(), key1.getKey()) && Objects.equals(type, key1.type); + } + + @Override + public int hashCode() { + return Objects.hash(getKey(), type); + } + } + + Set> keys(); + Map, Object> entries(); + Object getValue(Key key); +} diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/time/TimeSource.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/time/TimeSource.java new file mode 100644 index 00000000000..84e5843635d --- /dev/null +++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/time/TimeSource.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.api.metadata.time; + +public enum TimeSource { + + // Time information is assumed by PLC4X itself + ASSUMPTION, + // Time comes from software layer, kernel driver and similar + SOFTWARE, + // Time can is confronted through hardware i.e. microcontroller + HARDWARE, + // Other source of time which fall into separate truthiness category + OTHER + +} diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java index 08eaa141d06..7a58931ae7e 100644 --- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java +++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java @@ -34,6 +34,9 @@ import org.apache.plc4x.java.api.exceptions.PlcInvalidTagException; import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; import org.apache.plc4x.java.api.messages.*; +import org.apache.plc4x.java.api.metadata.Metadata; +import org.apache.plc4x.java.spi.metadata.DefaultMetadata; +import org.apache.plc4x.java.api.metadata.time.TimeSource; import org.apache.plc4x.java.api.model.*; import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.api.types.PlcSubscriptionType; @@ -718,8 +721,13 @@ protected CompletableFuture singleRead(PlcReadRequest readReque .check(userdata -> userdata.getInvokeId() == amsPacket.getInvokeId()) .only(AdsReadResponse.class) .handle(response -> { + // result metadata + Metadata metadata = new DefaultMetadata.Builder() + .put(PlcMetadataKeys.RECEIVE_TIMESTAMP, System.currentTimeMillis()) + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build(); if (response.getResult() == ReturnCode.OK) { - final PlcReadResponse plcReadResponse = convertToPlc4xReadResponse(readRequest, Map.of((AdsTag) readRequest.getTags().get(0), directAdsTag), response); + final PlcReadResponse plcReadResponse = convertToPlc4xReadResponse(readRequest, Map.of((AdsTag) readRequest.getTags().get(0), directAdsTag), response, metadata); // Convert the response from the PLC into a PLC4X Response ... future.complete(plcReadResponse); } else { @@ -791,8 +799,12 @@ protected CompletableFuture multiRead(PlcReadRequest readReques .check(userdata -> userdata.getInvokeId() == amsPacket.getInvokeId()) .only(AdsReadWriteResponse.class) .handle(response -> { + Metadata metadata = new DefaultMetadata.Builder() + .put(PlcMetadataKeys.RECEIVE_TIMESTAMP, System.currentTimeMillis()) + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build(); if (response.getResult() == ReturnCode.OK) { - final PlcReadResponse plcReadResponse = convertToPlc4xReadResponse(readRequest, resolvedTags, response); + final PlcReadResponse plcReadResponse = convertToPlc4xReadResponse(readRequest, resolvedTags, response, metadata); // Convert the response from the PLC into a PLC4X Response ... future.complete(plcReadResponse); } else if (response.getResult() == ReturnCode.ADSERR_DEVICE_INVALIDSIZE) { @@ -807,8 +819,9 @@ protected CompletableFuture multiRead(PlcReadRequest readReques return future; } - protected PlcReadResponse convertToPlc4xReadResponse(PlcReadRequest readRequest, Map resolvedTags, AmsPacket adsData) { + protected PlcReadResponse convertToPlc4xReadResponse(PlcReadRequest readRequest, Map resolvedTags, AmsPacket adsData, Metadata responseMetadata) { ReadBuffer readBuffer = null; + Map metadata = new HashMap<>(); Map responseCodes = new HashMap<>(); // Read the response codes first @@ -841,6 +854,7 @@ protected PlcReadResponse convertToPlc4xReadResponse(PlcReadRequest readRequest, if (readBuffer != null) { Map> values = new HashMap<>(); for (String tagName : readRequest.getTagNames()) { + metadata.put(tagName, new DefaultMetadata.Builder(responseMetadata).build()); // If the response-code was anything but OK, we don't need to parse the payload. if(responseCodes.get(tagName) != PlcResponseCode.OK) { values.put(tagName, new DefaultPlcResponseItem<>(responseCodes.get(tagName), null)); @@ -851,7 +865,7 @@ protected PlcReadResponse convertToPlc4xReadResponse(PlcReadRequest readRequest, values.put(tagName, parseResponseItem(directAdsTag, readBuffer)); } } - return new DefaultPlcReadResponse(readRequest, values); + return new DefaultPlcReadResponse(readRequest, values, metadata); } return null; } @@ -1071,8 +1085,13 @@ protected CompletableFuture singleWrite(PlcWriteRequest writeR .check(userdata -> userdata.getInvokeId() == amsPacket.getInvokeId()) .only(AdsWriteResponse.class) .handle(response -> { + // result metadata + Metadata eventMetadata = new DefaultMetadata.Builder() + .put(PlcMetadataKeys.RECEIVE_TIMESTAMP, System.currentTimeMillis()) + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build(); if (response.getResult() == ReturnCode.OK) { - final PlcWriteResponse plcWriteResponse = convertToPlc4xWriteResponse(writeRequest, Collections.singletonMap((AdsTag) writeRequest.getTag(tagName), directAdsTag), response); + final PlcWriteResponse plcWriteResponse = convertToPlc4xWriteResponse(writeRequest, Collections.singletonMap((AdsTag) writeRequest.getTag(tagName), directAdsTag), response, eventMetadata); // Convert the response from the PLC into a PLC4X Response ... future.complete(plcWriteResponse); } else { @@ -1149,8 +1168,14 @@ protected CompletableFuture multiWrite(PlcWriteRequest writeRe .check(userdata -> userdata.getInvokeId() == amsPacket.getInvokeId()) .only(AdsReadWriteResponse.class) .handle(response -> { + // result metadata + Metadata eventMetadata = new DefaultMetadata.Builder() + .put(PlcMetadataKeys.RECEIVE_TIMESTAMP, System.currentTimeMillis()) + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build(); + if (response.getResult() == ReturnCode.OK) { - final PlcWriteResponse plcWriteResponse = convertToPlc4xWriteResponse(writeRequest, resolvedTags, response); + final PlcWriteResponse plcWriteResponse = convertToPlc4xWriteResponse(writeRequest, resolvedTags, response, eventMetadata); // Convert the response from the PLC into a PLC4X Response ... future.complete(plcWriteResponse); } else { @@ -1244,8 +1269,9 @@ else if (!dataType.getChildren().isEmpty()) { } } - protected PlcWriteResponse convertToPlc4xWriteResponse(PlcWriteRequest writeRequest, Map resolvedTags, AmsPacket adsData) { + protected PlcWriteResponse convertToPlc4xWriteResponse(PlcWriteRequest writeRequest, Map resolvedTags, AmsPacket adsData, Metadata eventMtadata) { Map responseCodes = new HashMap<>(); + Map metadata = new HashMap<>(); if (adsData instanceof AdsWriteResponse) { AdsWriteResponse adsWriteResponse = (AdsWriteResponse) adsData; responseCodes.put(writeRequest.getTagNames().stream().findFirst().orElse(""), @@ -1256,6 +1282,9 @@ protected PlcWriteResponse convertToPlc4xWriteResponse(PlcWriteRequest writeRequ // When parsing a multi-item response, the error codes of each items come // in sequence and then come the values. for (String tagName : writeRequest.getTagNames()) { + // result metadata + metadata.put(tagName, eventMtadata); + AdsTag adsTag = (AdsTag) writeRequest.getTag(tagName); // Skip invalid addresses. if(resolvedTags.get(adsTag) == null) { @@ -1271,7 +1300,7 @@ protected PlcWriteResponse convertToPlc4xWriteResponse(PlcWriteRequest writeRequ } } - return new DefaultPlcWriteResponse(writeRequest, responseCodes); + return new DefaultPlcWriteResponse(writeRequest, responseCodes, metadata); } @Override @@ -1493,9 +1522,16 @@ protected void decode(ConversationContext context, AmsTCPPacket ms if (msg.getUserdata() instanceof AdsDeviceNotificationRequest) { AdsDeviceNotificationRequest notificationData = (AdsDeviceNotificationRequest) msg.getUserdata(); List stamps = notificationData.getAdsStampHeaders(); + long receiveTs = System.currentTimeMillis(); for (AdsStampHeader stamp : stamps) { // convert Windows FILETIME format to unix epoch long unixEpochTimestamp = stamp.getTimestamp().divide(BigInteger.valueOf(10000L)).longValue() - 11644473600000L; + // result metadata + Metadata eventMetadata = new DefaultMetadata.Builder() + .put(PlcMetadataKeys.RECEIVE_TIMESTAMP, receiveTs) + .put(PlcMetadataKeys.TIMESTAMP, unixEpochTimestamp) + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.SOFTWARE) + .build(); List samples = stamp.getAdsNotificationSamples(); for (AdsNotificationSample sample : samples) { long handle = sample.getNotificationHandle(); @@ -1503,10 +1539,12 @@ protected void decode(ConversationContext context, AmsTCPPacket ms for (PlcSubscriptionHandle subscriptionHandle : registration.getSubscriptionHandles()) { if (subscriptionHandle instanceof AdsSubscriptionHandle) { AdsSubscriptionHandle adsHandle = (AdsSubscriptionHandle) subscriptionHandle; - if (adsHandle.getNotificationHandle() == handle) - consumers.get(registration).accept( - new DefaultPlcSubscriptionEvent(Instant.ofEpochMilli(unixEpochTimestamp), - convertSampleToPlc4XResult(adsHandle, sample.getData()))); + if (adsHandle.getNotificationHandle() == handle) { + Map metadata = new HashMap<>(); + Instant timestamp = Instant.ofEpochMilli(unixEpochTimestamp); + DefaultPlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(timestamp, convertSampleToPlc4XResult(adsHandle, sample.getData(), metadata, eventMetadata)); + consumers.get(registration).accept(event); + } } } } @@ -1515,12 +1553,13 @@ protected void decode(ConversationContext context, AmsTCPPacket ms } } - private Map> convertSampleToPlc4XResult(AdsSubscriptionHandle subscriptionHandle, byte[] data) throws + private Map> convertSampleToPlc4XResult(AdsSubscriptionHandle subscriptionHandle, byte[] data, Map tagMetadata, Metadata metadata) throws ParseException { Map> values = new HashMap<>(); ReadBufferByteBased readBuffer = new ReadBufferByteBased(data, ByteOrder.LITTLE_ENDIAN); values.put(subscriptionHandle.getTagName(), new DefaultPlcResponseItem<>(PlcResponseCode.OK, DataItem.staticParse(readBuffer, getPlcValueTypeForAdsDataType(subscriptionHandle.getAdsDataType()), data.length))); + tagMetadata.put(subscriptionHandle.getTagName(), new DefaultMetadata.Builder(metadata).build()); return values; } diff --git a/plc4j/drivers/ctrlx/pom.xml b/plc4j/drivers/ctrlx/pom.xml index f3cfc64e70e..5e4b7af9cb4 100644 --- a/plc4j/drivers/ctrlx/pom.xml +++ b/plc4j/drivers/ctrlx/pom.xml @@ -187,7 +187,7 @@ org.jetbrains.kotlin kotlin-stdlib-jdk8 - 2.0.20 + 2.0.21 diff --git a/plc4j/drivers/opcua/pom.xml b/plc4j/drivers/opcua/pom.xml index fe4f92536b9..c63e139f1bf 100644 --- a/plc4j/drivers/opcua/pom.xml +++ b/plc4j/drivers/opcua/pom.xml @@ -212,7 +212,7 @@ org.testcontainers testcontainers - 1.20.2 + 1.20.3 test @@ -224,7 +224,7 @@ com.google.cloud.tools jib-core - 0.27.1 + 0.27.2 test diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcMetadataKeys.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcMetadataKeys.java new file mode 100644 index 00000000000..713081159ca --- /dev/null +++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcMetadataKeys.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.opcua; + +import org.apache.plc4x.java.api.metadata.Metadata; +import org.apache.plc4x.java.api.metadata.Metadata.Key; +import org.apache.plc4x.java.opcua.tag.OpcuaQualityStatus; + +/** + * OPC UA level metadata keys. + */ +public interface OpcMetadataKeys { + + Key QUALITY = Metadata.Key.of("opcua_quality", OpcuaQualityStatus.class); + + Key SERVER_TIMESTAMP = Metadata.Key.of("opcua_server_timestamp", Long.class); + Key SOURCE_TIMESTAMP = Metadata.Key.of("opcua_source_timestamp", Long.class); + +} diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java index 91112aef604..0ccf130a5b3 100644 --- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java +++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java @@ -26,18 +26,23 @@ import org.apache.plc4x.java.api.exceptions.PlcConnectionException; import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; import org.apache.plc4x.java.api.messages.*; +import org.apache.plc4x.java.api.metadata.Metadata; +import org.apache.plc4x.java.spi.metadata.DefaultMetadata; +import org.apache.plc4x.java.api.metadata.time.TimeSource; import org.apache.plc4x.java.api.model.PlcConsumerRegistration; import org.apache.plc4x.java.api.model.PlcSubscriptionHandle; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.api.types.PlcValueType; import org.apache.plc4x.java.api.value.PlcValue; +import org.apache.plc4x.java.opcua.OpcMetadataKeys; import org.apache.plc4x.java.opcua.config.OpcuaConfiguration; import org.apache.plc4x.java.opcua.context.Conversation; import org.apache.plc4x.java.opcua.context.OpcuaDriverContext; import org.apache.plc4x.java.opcua.context.SecureChannel; import org.apache.plc4x.java.opcua.readwrite.*; import org.apache.plc4x.java.opcua.tag.OpcuaPlcTagHandler; +import org.apache.plc4x.java.opcua.tag.OpcuaQualityStatus; import org.apache.plc4x.java.opcua.tag.OpcuaTag; import org.apache.plc4x.java.spi.ConversationContext; import org.apache.plc4x.java.spi.Plc4xProtocolBase; @@ -210,7 +215,7 @@ public CompletableFuture read(PlcReadRequest readRequest) { List readValueArray = new ArrayList<>(request.getTagNames().size()); Iterator iterator = request.getTagNames().iterator(); - Map tagMap = new HashMap<>(); + Map tagMap = new LinkedHashMap<>(); for (int i = 0; i < request.getTagNames().size(); i++) { String tagName = iterator.next(); // TODO: We need to check that the tag-return-code is OK as it could also be INVALID_TAG @@ -237,7 +242,13 @@ public CompletableFuture read(PlcReadRequest readRequest) { transaction.submit(() -> { conversation.submit(opcuaReadRequest, ReadResponse.class).whenComplete((response, error) -> bridge(transaction, future, response, error)); }); - return future.thenApply(response -> new DefaultPlcReadResponse(request, readResponse(request.getTagNames(), tagMap, response.getResults()))); + return future.thenApply(response -> { + Metadata responseMetadata = new DefaultMetadata.Builder() + .put(PlcMetadataKeys.RECEIVE_TIMESTAMP, System.currentTimeMillis()) + .build(); + Map metadata = new LinkedHashMap<>(); + return new DefaultPlcReadResponse(request, readResponse(tagMap, response.getResults(), metadata, responseMetadata), metadata); + }); } static NodeId generateNodeId(OpcuaTag tag) { @@ -262,15 +273,16 @@ static NodeId generateNodeId(OpcuaTag tag) { return nodeId; } - public Map> readResponse(LinkedHashSet tagNames, Map tagMap, List results) { + public Map> readResponse(Map tagMap, List results, Map metadata, Metadata responseMetadata) { PlcResponseCode responseCode = null; // initialize variable Map> response = new HashMap<>(); - int count = 0; - for (String tagName : tagNames) { + int index = 0; + for (String tagName : tagMap.keySet()) { PlcTag tag = tagMap.get(tagName); PlcValue value = null; - if (results.get(count).getValueSpecified()) { - Variant variant = results.get(count).getValue(); + DataValue dataValue = results.get(index++); + if (dataValue.getValueSpecified()) { + Variant variant = dataValue.getValue(); LOGGER.trace("Response of type {}", variant.getClass().toString()); if (variant instanceof VariantBoolean) { byte[] array = ((VariantBoolean) variant).getValue(); @@ -423,12 +435,20 @@ public Map> readResponse(LinkedHashSet responseCode = PlcResponseCode.OK; } } else { - StatusCode statusCode = results.get(count).getStatusCode(); + StatusCode statusCode = dataValue.getStatusCode(); responseCode = mapOpcStatusCode(statusCode.getStatusCode(), PlcResponseCode.UNSUPPORTED); - LOGGER.error("Error while reading value from OPC UA server error code: {}", results.get(count).getStatusCode().toString()); + LOGGER.error("Error while reading value from OPC UA server error code:- " + dataValue.getStatusCode().toString()); } - count++; + + Metadata tagMetadata = new DefaultMetadata.Builder(responseMetadata) + .put(OpcMetadataKeys.QUALITY, new OpcuaQualityStatus(dataValue.getStatusCode())) + .put(OpcMetadataKeys.SERVER_TIMESTAMP, dataValue.getServerTimestamp()) + .put(OpcMetadataKeys.SOURCE_TIMESTAMP, dataValue.getSourceTimestamp()) + .put(PlcMetadataKeys.TIMESTAMP, dataValue.getServerTimestamp()) + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.SOFTWARE) + .build(); response.put(tagName, new DefaultPlcResponseItem<>(responseCode, value)); + metadata.put(tagName, tagMetadata); } return response; } @@ -880,4 +900,5 @@ private static void bridge(RequestTransaction transaction, CompletableFuture transaction.endRequest(); } } + } diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandle.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandle.java index ab241b0411e..4889480f3cd 100644 --- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandle.java +++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandle.java @@ -24,8 +24,11 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.apache.plc4x.java.api.messages.PlcMetadataKeys; import org.apache.plc4x.java.api.messages.PlcSubscriptionEvent; import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest; +import org.apache.plc4x.java.api.metadata.Metadata; +import org.apache.plc4x.java.spi.metadata.DefaultMetadata; import org.apache.plc4x.java.api.model.PlcConsumerRegistration; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.value.PlcValue; @@ -34,7 +37,6 @@ import org.apache.plc4x.java.opcua.readwrite.*; import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionEvent; import org.apache.plc4x.java.spi.messages.utils.PlcResponseItem; -import org.apache.plc4x.java.spi.messages.utils.PlcTagItem; import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration; import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionTag; import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle; @@ -260,17 +262,22 @@ public void stopSubscriber() { * @param values - array of data values to be sent to the client. */ private void onSubscriptionValue(MonitoredItemNotification[] values) { - LinkedHashSet tagNameList = new LinkedHashSet<>(); + long receiveTs = System.currentTimeMillis(); + Metadata responseMetadata = new DefaultMetadata.Builder() + .put(PlcMetadataKeys.RECEIVE_TIMESTAMP, receiveTs) + .build(); + List dataValues = new ArrayList<>(values.length); Map tagMap = new LinkedHashMap<>(); for (MonitoredItemNotification value : values) { String tagName = tagNames.get((int) value.getClientHandle() - 1); - tagNameList.add(tagName); tagMap.put(tagName, subscriptionRequest.getTag(tagName).getTag()); dataValues.add(value.getValue()); } - Map> tags = plcSubscriber.readResponse(tagNameList, tagMap, dataValues); - final PlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(Instant.now(), tags); + + Map metadata = new HashMap<>(); + Map> tags = plcSubscriber.readResponse(tagMap, dataValues, metadata, responseMetadata); + final PlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent(Instant.now(), tags, metadata); consumers.forEach(plcSubscriptionEventConsumer -> plcSubscriptionEventConsumer.accept(event)); } diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/tag/OpcuaQualityStatus.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/tag/OpcuaQualityStatus.java new file mode 100644 index 00000000000..1500e210999 --- /dev/null +++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/tag/OpcuaQualityStatus.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.opcua.tag; + +import org.apache.plc4x.java.opcua.readwrite.StatusCode; + +public final class OpcuaQualityStatus { + + private static final long STATUS_MASK = 0xC0000000L; + private static final long STATUS_GOOD = 0x00000000L; + private static final long STATUS_UNCERTAIN = 0x40000000L; + private static final long STATUS_BAD = 0x80000000L; + + private final StatusCode statusCode; + + public OpcuaQualityStatus(StatusCode statusCode) { + this.statusCode = statusCode; + } + + public boolean isGood() { + return (statusCode.getStatusCode() & STATUS_MASK) == STATUS_GOOD; + } + + public boolean isBad() { + return (statusCode.getStatusCode() & STATUS_MASK) == STATUS_BAD; + } + + public boolean isUncertain() { + return (statusCode.getStatusCode() & STATUS_MASK) == STATUS_UNCERTAIN; + } + + @Override + public String toString() { + if (isGood()) { + return "good"; + } else if (isBad()) { + return "bad"; + } + return "uncertain"; + } +} diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/KeystoreGenerator.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/KeystoreGenerator.java new file mode 100644 index 00000000000..c16f0b80e04 --- /dev/null +++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/KeystoreGenerator.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.opcua; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Set; +import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder; +import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator; + +/** + * Utility to generate server certificate - based on Eclipse Milo stuff. + */ +public class KeystoreGenerator { + + private final String password; + private final KeyStore keyStore; + private final X509Certificate certificate; + + public KeystoreGenerator(String password, int length, String applicationUri) { + this(password, length, applicationUri, "server-ai", "Milo Server"); + } + + public KeystoreGenerator(String password, int length, String applicationUri, String entryName, String commonName) { + this.password = password; + try { + this.keyStore = generate(password, length, applicationUri, entryName, commonName); + + Key serverPrivateKey = keyStore.getKey(entryName, password.toCharArray()); + if (serverPrivateKey instanceof PrivateKey) { + this.certificate = (X509Certificate) keyStore.getCertificate(entryName); + } else { + throw new IllegalStateException("Unexpected keystore entry, expected private key"); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private KeyStore generate(String password, int length, String applicationUri, String entryName, String commonName) throws Exception { + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + keyStore.load(null, password.toCharArray()); + KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(length); + SelfSignedCertificateBuilder builder = (new SelfSignedCertificateBuilder( + keyPair)).setCommonName(commonName) + .setOrganization("Apache Software Foundation") + .setOrganizationalUnit("PLC4X") + .setLocalityName("PLC4J") + .setStateName("CA") + .setCountryCode("US") + .setApplicationUri(applicationUri); + + Set hostnames = Set.of("127.0.0.1"); + + for (String hostname : hostnames) { + if (hostname.startsWith("\\d+\\.")) { + builder.addIpAddress(hostname); + } else { + builder.addDnsName(hostname); + } + } + + X509Certificate certificate = builder.build(); + keyStore.setKeyEntry(entryName, keyPair.getPrivate(), password.toCharArray(), new X509Certificate[]{ certificate }); + return keyStore; + } + + public KeyStore getKeyStore() { + return keyStore; + } + + public X509Certificate getCertificate() { + return certificate; + } + + public void writeKeyStoreTo(OutputStream stream) throws IOException, GeneralSecurityException { + keyStore.store(stream, password.toCharArray()); + stream.flush(); + } + + public void writeCertificateTo(OutputStream stream) throws IOException, CertificateEncodingException { + String data = "-----BEGIN CERTIFICATE-----\n" + + Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(certificate.getEncoded()) + "\n" + + "-----END CERTIFICATE-----"; + + stream.write(data.getBytes()); + stream.flush(); + } + +} diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/MiloTestContainer.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/MiloTestContainer.java index db13a936aee..43989e2659e 100644 --- a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/MiloTestContainer.java +++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/MiloTestContainer.java @@ -36,8 +36,15 @@ public class MiloTestContainer extends GenericContainer { public MiloTestContainer() { super(IMAGE); - waitingFor(Wait.forLogMessage("Server started\\s*", 1)); + waitingFor(Wait.forLogMessage("Server started\\s*", 1)) + // Uncomment below to debug Milo server + //.withStartupTimeout(Duration.ofMinutes(10)) + ; addExposedPort(12686); + + // Uncomment below to enable server debug + //withEnv("JAVA_TOOL_OPTIONS", "-agentlib:jdwp=transport=dt_socket,address=*:8000,server=y,suspend=y"); + //addFixedExposedPort(8000, 8000); } private static ImageFromDockerfile inlineImage() { diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java index 01432165a00..2e6bf163ddb 100644 --- a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java +++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java @@ -18,6 +18,8 @@ */ package org.apache.plc4x.java.opcua; +import java.io.File; +import java.io.FileOutputStream; import java.lang.reflect.Array; import java.net.URLEncoder; import java.nio.charset.Charset; @@ -45,10 +47,8 @@ import org.apache.plc4x.java.opcua.security.MessageSecurity; import org.apache.plc4x.java.opcua.security.SecurityPolicy; import org.apache.plc4x.java.opcua.tag.OpcuaTag; -import org.apache.plc4x.test.DisableOnJenkinsFlag; import org.assertj.core.api.Condition; import org.assertj.core.api.SoftAssertions; -import org.eclipse.milo.examples.server.TestMiloServer; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -59,15 +59,9 @@ import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.concurrent.ExecutionException; import java.util.stream.Stream; -import org.testcontainers.containers.BindMode; -import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; -import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.images.builder.ImageFromDockerfile; -import org.testcontainers.jib.JibImage; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -75,17 +69,23 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -@DisableOnJenkinsFlag @Testcontainers(disabledWithoutDocker = true) public class OpcuaPlcDriverTest { private static final Logger LOGGER = LoggerFactory.getLogger(OpcuaPlcDriverTest.class); + private static final String APPLICATION_URI = "urn:org.apache:plc4x"; + private static final KeystoreGenerator SERVER_KEY_STORE_GENERATOR = new KeystoreGenerator("password", 2048, APPLICATION_URI); + private static final KeystoreGenerator CLIENT_KEY_STORE_GENERATOR = new KeystoreGenerator("changeit", 2048, APPLICATION_URI, "plc4x_plus_milo", "client"); + @Container - public final GenericContainer milo = new MiloTestContainer() + public final MiloTestContainer milo = new MiloTestContainer() //.withCreateContainerCmdModifier(cmd -> cmd.withHostName("test-opcua-server")) .withLogConsumer(new Slf4jLogConsumer(LOGGER)) - .withFileSystemBind("target/tmp/server/security", "/tmp/server/security", BindMode.READ_WRITE); + .withFileSystemBind(SECURITY_DIR.getAbsolutePath(), "/tmp/server/security") + //.withEnv("JAVA_TOOL_OPTIONS", "-agentlib:jdwp=transport=dt_socket,address=*:8000,server=y,suspend=y") +// .withCommand("java -cp '/opt/milo/*:/opt/milo/' org.eclipse.milo.examples.server.TestMiloServer") + ; // Read only variables of milo example server of version 3.6 private static final String BOOL_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Boolean"; @@ -142,8 +142,8 @@ public class OpcuaPlcDriverTest { //Tcp pattern of OPC UA private final String opcPattern = "opcua:tcp://"; - private final String paramSectionDivider = "?"; - private final String paramDivider = "&"; + private static final String PARAM_SECTION_DIVIDER = "?"; + private static final String PARAM_DIVIDER = "&"; private final String discoveryValidParamTrue = "discovery=true"; private final String discoveryValidParamFalse = "discovery=false"; @@ -155,35 +155,39 @@ public class OpcuaPlcDriverTest { final List discoveryParamValidSet = List.of(discoveryValidParamTrue, discoveryValidParamFalse); List discoveryParamCorruptedSet = List.of(discoveryCorruptedParamWrongValueNum, discoveryCorruptedParamWrongName); - - @BeforeEach - public void startUp() { - //System.out.println(milo.getMappedPort(12686)); - tcpConnectionAddress = String.format(opcPattern + miloLocalAddress, milo.getHost(), milo.getMappedPort(12686)) + "?endpoint-port=12686"; - connectionStringValidSet = List.of(tcpConnectionAddress); - } + private static File SECURITY_DIR; + private static File CLIENT_KEY_STORE; @BeforeAll - public static void setup() throws Exception { - // When switching JDK versions from a newer to an older version, - // this can cause the server to not start correctly. - // Deleting the directory makes sure the key-store is initialized correctly. -// Path securityBaseDir = Paths.get(System.getProperty("java.io.tmpdir"), "server", "security"); -// try { -// Files.delete(securityBaseDir); -// } catch (Exception e) { -// // Ignore this ... -// } -// -// exampleServer = new TestMiloServer(); -// exampleServer.startup().get(); + public static void prepare() throws Exception { + Path tempDirectory = Files.createTempDirectory("plc4x_opcua_client"); + + SECURITY_DIR = new File(tempDirectory.toFile().getAbsolutePath(), "server/security"); + File pkiDir = new File(SECURITY_DIR, "pki"); + File trustedCerts = new File(pkiDir, "trusted/certs"); + if (!pkiDir.mkdirs() || !trustedCerts.mkdirs()) { + throw new IllegalStateException("Could not start test - missing permissions to create temporary files"); + } + + // pre-provisioned server certificate + try (FileOutputStream bos = new FileOutputStream(new File(pkiDir.getParentFile(), "example-server.pfx"))) { + SERVER_KEY_STORE_GENERATOR.writeKeyStoreTo(bos); + } + + // pre-provisioned client certificate, doing it here because server might be slow in picking up them, and we don't want to wait with our tests + CLIENT_KEY_STORE = Files.createTempFile("plc4x_opcua_client_", ".p12").toAbsolutePath().toFile(); + try (FileOutputStream outputStream = new FileOutputStream(CLIENT_KEY_STORE)) { + CLIENT_KEY_STORE_GENERATOR.writeKeyStoreTo(outputStream); + } + try (FileOutputStream outputStream = new FileOutputStream(new File(trustedCerts, "plc4x.crt"))) { + CLIENT_KEY_STORE_GENERATOR.writeCertificateTo(outputStream); + } } - @AfterAll - public static void tearDown() throws Exception { -// if (exampleServer != null) { -// exampleServer.shutdown().get(); -// } + @BeforeEach + public void startUp() throws Exception { + tcpConnectionAddress = String.format(opcPattern + miloLocalAddress, milo.getHost(), milo.getMappedPort(12686)) + "?endpoint-port=12686"; + connectionStringValidSet = List.of(tcpConnectionAddress); } @Nested @@ -276,7 +280,7 @@ Stream connectionWithDiscoveryParam() throws Exception { return connectionStringValidSet.stream() .map(connectionAddress -> DynamicContainer.dynamicContainer(connectionAddress, () -> discoveryParamValidSet.stream().map(discoveryParam -> DynamicTest.dynamicTest(discoveryParam, () -> { - String connectionString = connectionAddress + paramDivider + discoveryParam; + String connectionString = connectionAddress + PARAM_DIVIDER + discoveryParam; PlcConnection opcuaConnection = new DefaultPlcDriverManager().getConnection(connectionString); Condition is_connected = new Condition<>(PlcConnection::isConnected, "is connected"); assertThat(opcuaConnection).is(is_connected); @@ -340,6 +344,39 @@ void connectionWithPlcAuthenticationOverridesUrlParam() throws Exception { assertThat(response.getResponseCode("String")).isEqualTo(PlcResponseCode.OK); } } + + @Test + void staticConfig() throws Exception { + DefaultPlcDriverManager driverManager = new DefaultPlcDriverManager(); + + File certificateFile = Files.createTempFile("plc4x_opcua_server_", ".crt").toAbsolutePath().toFile(); + try (FileOutputStream outputStream = new FileOutputStream(certificateFile)) { + SERVER_KEY_STORE_GENERATOR.writeCertificateTo(outputStream); + } + + String options = params( + entry("discovery", "false"), + entry("server-certificate-file", certificateFile.toString().replace("\\", "/")), + entry("key-store-file", CLIENT_KEY_STORE.toString().replace("\\", "/")), // handle windows paths + entry("key-store-password", "changeit"), + entry("key-store-type", "pkcs12"), + entry("security-policy", SecurityPolicy.Basic256Sha256.name()), + entry("message-security", MessageSecurity.SIGN.name()) + ); + try (PlcConnection opcuaConnection = driverManager.getConnection(tcpConnectionAddress + PARAM_DIVIDER + + options)) { + Condition is_connected = new Condition<>(PlcConnection::isConnected, "is connected"); + assertThat(opcuaConnection).is(is_connected); + + PlcReadRequest.Builder builder = opcuaConnection.readRequestBuilder() + .addTagAddress("String", STRING_IDENTIFIER_READ_WRITE); + + PlcReadRequest request = builder.build(); + PlcReadResponse response = request.execute().get(); + + assertThat(response.getResponseCode("String")).isEqualTo(PlcResponseCode.OK); + } + } } @Nested @@ -533,7 +570,7 @@ public void run() { assert !opcuaConnection.isConnected(); } - private String getConnectionString(SecurityPolicy policy, MessageSecurity messageSecurity) { + private String getConnectionString(SecurityPolicy policy, MessageSecurity messageSecurity) throws Exception { switch (policy) { case NONE: return tcpConnectionAddress; @@ -543,18 +580,15 @@ private String getConnectionString(SecurityPolicy policy, MessageSecurity messag case Basic256Sha256: case Aes128_Sha256_RsaOaep: case Aes256_Sha256_RsaPss: - // this file and its contents should be populated by milo container - Path keyStoreFile = Paths.get("target/tmp/server/security/example-server.pfx"); - String connectionParams = Stream.of( - entry("key-store-file", keyStoreFile.toAbsolutePath().toString().replace("\\", "/")), // handle windows paths - entry("key-store-password", "password"), - entry("security-policy", policy.name()), - entry("message-security", messageSecurity.name()) - ) - .map(tuple -> tuple.getKey() + "=" + URLEncoder.encode(tuple.getValue(), Charset.defaultCharset())) - .collect(Collectors.joining(paramDivider)); - - return tcpConnectionAddress + paramDivider + connectionParams; + String connectionParams = params( + entry("key-store-file", CLIENT_KEY_STORE.getAbsoluteFile().toString().replace("\\", "/")), // handle windows paths + entry("key-store-password", "changeit"), + entry("key-store-type", "pkcs12"), + entry("security-policy", policy.name()), + entry("message-security", messageSecurity.name()) + ); + + return tcpConnectionAddress + PARAM_DIVIDER + connectionParams; default: throw new IllegalStateException(); } @@ -582,4 +616,10 @@ private static Stream getConnectionSecurityPolicies() { Arguments.of(SecurityPolicy.Aes256_Sha256_RsaPss, MessageSecurity.SIGN_ENCRYPT) ); } + + private static String params(Entry ... entries) { + return Stream.of(entries) + .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), Charset.defaultCharset())) + .collect(Collectors.joining(PARAM_DIVIDER)); + } } diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java index 094e09bb3b1..37dec0d08f7 100644 --- a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java +++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java @@ -18,6 +18,7 @@ */ package org.apache.plc4x.java.opcua.protocol; +import java.io.ByteArrayOutputStream; import java.util.concurrent.CountDownLatch; import java.util.stream.Stream; import org.apache.plc4x.java.DefaultPlcDriverManager; @@ -27,8 +28,7 @@ import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.opcua.MiloTestContainer; import org.apache.plc4x.java.opcua.OpcuaPlcDriverTest; -import org.apache.plc4x.test.DisableOnJenkinsFlag; -import org.apache.plc4x.test.DisableOnParallelsVmFlag; +import org.apache.plc4x.java.opcua.KeystoreGenerator; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -36,13 +36,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.concurrent.TimeUnit; -import org.testcontainers.containers.BindMode; -import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.Transferable; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -56,18 +52,14 @@ // cdutz: I have done way more than my fair share on tracking down this issue and am simply giving up on it. // I tracked it down into the core of Milo several times now, but got lost in there. // It's not a big issue as the GitHub runners and the Apache Jenkins still run the test. -@DisableOnJenkinsFlag -@DisableOnParallelsVmFlag @Testcontainers(disabledWithoutDocker = true) public class OpcuaSubscriptionHandleTest { private static final Logger LOGGER = LoggerFactory.getLogger(OpcuaPlcDriverTest.class); @Container - public final GenericContainer milo = new MiloTestContainer() - //.withCreateContainerCmdModifier(cmd -> cmd.withHostName("test-opcua-server")) - .withLogConsumer(new Slf4jLogConsumer(LOGGER)) - .withFileSystemBind("target/tmp/server/security", "/tmp/server/security", BindMode.READ_WRITE); + public final MiloTestContainer milo = new MiloTestContainer() + .withLogConsumer(new Slf4jLogConsumer(LOGGER)); // Address of local milo server private static final String miloLocalAddress = "%s:%d/milo"; @@ -102,16 +94,7 @@ public void setup() throws Exception { // When switching JDK versions from a newer to an older version, // this can cause the server to not start correctly. // Deleting the directory makes sure the key-store is initialized correctly. - String tcpConnectionAddress = String.format(opcPattern + miloLocalAddress, milo.getHost(), milo.getMappedPort(12686)) + "?endpoint-port=12686"; - - Path securityBaseDir = Paths.get(System.getProperty("java.io.tmpdir"), "server", "security"); - try { - Files.delete(securityBaseDir); - } catch (Exception e) { - // Ignore this ... - } - //Connect opcuaConnection = new DefaultPlcDriverManager().getConnection(tcpConnectionAddress); assertThat(opcuaConnection).extracting(PlcConnection::isConnected).isEqualTo(true); diff --git a/plc4j/drivers/opcua/src/test/java/org/eclipse/milo/examples/server/TestMiloServer.java b/plc4j/drivers/opcua/src/test/java/org/eclipse/milo/examples/server/TestMiloServer.java index 872afbfb0fd..d886afb11c4 100644 --- a/plc4j/drivers/opcua/src/test/java/org/eclipse/milo/examples/server/TestMiloServer.java +++ b/plc4j/drivers/opcua/src/test/java/org/eclipse/milo/examples/server/TestMiloServer.java @@ -123,8 +123,6 @@ public TestMiloServer() throws Exception { ); DefaultTrustListManager trustListManager = new DefaultTrustListManager(pkiDir); - trustListManager.setTrustedCertificates(new ArrayList<>(certificateManager.getCertificates())); - DefaultServerCertificateValidator certificateValidator = new DefaultServerCertificateValidator(trustListManager); diff --git a/plc4j/drivers/opcua/src/test/resources/logback-test.xml b/plc4j/drivers/opcua/src/test/resources/logback-test.xml index 695109a5329..719321b8e43 100644 --- a/plc4j/drivers/opcua/src/test/resources/logback-test.xml +++ b/plc4j/drivers/opcua/src/test/resources/logback-test.xml @@ -32,4 +32,6 @@ + + diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7AlarmEvent.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7AlarmEvent.java index 931bfab0a85..5d08cc6104e 100644 --- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7AlarmEvent.java +++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7AlarmEvent.java @@ -18,6 +18,7 @@ */ package org.apache.plc4x.java.s7.events; +import java.util.Collections; import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; import org.apache.plc4x.java.api.messages.PlcReadRequest; import org.apache.plc4x.java.api.model.PlcTag; @@ -33,7 +34,7 @@ import java.util.List; import java.util.Map; -public class S7AlarmEvent implements S7Event { +public class S7AlarmEvent extends S7EventBase { public enum Fields { @@ -130,172 +131,19 @@ public enum Fields { } - private final Instant timeStamp; private final Map map; - public S7AlarmEvent(Object obj) { - this.map = new HashMap<>(); - if (obj instanceof S7PayloadAlarmAckInd) { - AlarmMessageAckPushType msg = ((S7PayloadAlarmAckInd) obj).getAlarmMessage(); - DateAndTime dt = msg.getTimeStamp(); - int year = (dt.getYear() >= 90) ? dt.getYear() + 1900 : dt.getYear() + 2000; - LocalDateTime ldt = LocalDateTime.of(year, - dt.getMonth(), - dt.getDay(), - dt.getHour(), - dt.getMinutes(), - dt.getSeconds(), - dt.getMsec() * 1000000); - this.timeStamp = ldt.toInstant(ZoneOffset.UTC); - map.put(S7SysEvent.Fields.TIMESTAMP.name(), this.timeStamp); - - List items = msg.getMessageObjects(); - for (AlarmMessageAckObjectPushType item : items) { - map.put(Fields.EVENT_ID.name(), item.getEventId()); - map.put(Fields.TYPE.name(), "ALARMACK_IND"); - map.put(Fields.ASSOCIATED_VALUES.name(), item.getNumberOfValues()); - - map.put(Fields.SIG_1_DATA_GOING.name(), item.getAckStateGoing().getSIG_1()); - map.put(Fields.SIG_2_DATA_GOING.name(), item.getAckStateGoing().getSIG_2()); - map.put(Fields.SIG_3_DATA_GOING.name(), item.getAckStateGoing().getSIG_3()); - map.put(Fields.SIG_4_DATA_GOING.name(), item.getAckStateGoing().getSIG_4()); - map.put(Fields.SIG_5_DATA_GOING.name(), item.getAckStateGoing().getSIG_5()); - map.put(Fields.SIG_6_DATA_GOING.name(), item.getAckStateGoing().getSIG_6()); - map.put(Fields.SIG_7_DATA_GOING.name(), item.getAckStateGoing().getSIG_7()); - map.put(Fields.SIG_8_DATA_GOING.name(), item.getAckStateGoing().getSIG_8()); - - map.put(Fields.SIG_1_DATA_COMING.name(), item.getAckStateComing().getSIG_1()); - map.put(Fields.SIG_2_DATA_COMING.name(), item.getAckStateComing().getSIG_2()); - map.put(Fields.SIG_3_DATA_COMING.name(), item.getAckStateComing().getSIG_3()); - map.put(Fields.SIG_4_DATA_COMING.name(), item.getAckStateComing().getSIG_4()); - map.put(Fields.SIG_5_DATA_COMING.name(), item.getAckStateComing().getSIG_5()); - map.put(Fields.SIG_6_DATA_COMING.name(), item.getAckStateComing().getSIG_6()); - map.put(Fields.SIG_7_DATA_COMING.name(), item.getAckStateComing().getSIG_7()); - map.put(Fields.SIG_8_DATA_COMING.name(), item.getAckStateComing().getSIG_8()); - } - - } else { - - AlarmMessagePushType msg = null; - - if (obj instanceof S7PayloadAlarm8) { - msg = ((S7PayloadAlarm8) obj).getAlarmMessage(); - } else if (obj instanceof S7PayloadNotify) { - msg = ((S7PayloadNotify) obj).getAlarmMessage(); - } else if (obj instanceof S7PayloadAlarmSQ) { - msg = ((S7PayloadAlarmSQ) obj).getAlarmMessage(); - } else if (obj instanceof S7PayloadAlarmS) { - msg = ((S7PayloadAlarmS) obj).getAlarmMessage(); - } else if (obj instanceof S7PayloadNotify8) { - msg = ((S7PayloadNotify8) obj).getAlarmMessage(); - } else { - throw new PlcRuntimeException("Unsupported type: " + obj.getClass().getName()); - } - - DateAndTime dt = msg.getTimeStamp(); - int year = (dt.getYear() >= 90) ? dt.getYear() + 1900 : dt.getYear() + 2000; - LocalDateTime ldt = LocalDateTime.of(year, - dt.getMonth(), - dt.getDay(), - dt.getHour(), - dt.getMinutes(), - dt.getSeconds(), - dt.getMsec() * 1000000); - this.timeStamp = ldt.toInstant(ZoneOffset.UTC); - map.put(S7SysEvent.Fields.TIMESTAMP.name(), this.timeStamp); - - List items = msg.getMessageObjects(); - for (AlarmMessageObjectPushType item : items) { - map.put(Fields.EVENT_ID.name(), item.getEventId()); - - if (obj instanceof S7PayloadAlarm8) - map.put(Fields.TYPE.name(), "ALARM8"); - if (obj instanceof S7PayloadNotify) - map.put(Fields.TYPE.name(), "NOTIFY"); - if (obj instanceof S7PayloadAlarmSQ) - map.put(Fields.TYPE.name(), "ALARMSQ"); - if (obj instanceof S7PayloadAlarmS) - map.put(Fields.TYPE.name(), "ALARMS"); - if (obj instanceof S7PayloadNotify8) - map.put(Fields.TYPE.name(), "NOTIFY8"); - - - map.put(Fields.ASSOCIATED_VALUES.name(), item.getNumberOfValues()); - - - map.put(Fields.SIG_1.name(), item.getEventState().getSIG_1()); - map.put(Fields.SIG_2.name(), item.getEventState().getSIG_2()); - map.put(Fields.SIG_3.name(), item.getEventState().getSIG_3()); - map.put(Fields.SIG_4.name(), item.getEventState().getSIG_4()); - map.put(Fields.SIG_5.name(), item.getEventState().getSIG_5()); - map.put(Fields.SIG_6.name(), item.getEventState().getSIG_6()); - map.put(Fields.SIG_7.name(), item.getEventState().getSIG_7()); - map.put(Fields.SIG_8.name(), item.getEventState().getSIG_8()); - - - map.put(Fields.SIG_1_STATE.name(), item.getLocalState().getSIG_1()); - map.put(Fields.SIG_2_STATE.name(), item.getLocalState().getSIG_2()); - map.put(Fields.SIG_3_STATE.name(), item.getLocalState().getSIG_3()); - map.put(Fields.SIG_4_STATE.name(), item.getLocalState().getSIG_4()); - map.put(Fields.SIG_5_STATE.name(), item.getLocalState().getSIG_5()); - map.put(Fields.SIG_6_STATE.name(), item.getLocalState().getSIG_6()); - map.put(Fields.SIG_7_STATE.name(), item.getLocalState().getSIG_7()); - map.put(Fields.SIG_8_STATE.name(), item.getLocalState().getSIG_8()); - - map.put(Fields.SIG_1_DATA_GOING.name(), item.getAckStateGoing().getSIG_1()); - map.put(Fields.SIG_2_DATA_GOING.name(), item.getAckStateGoing().getSIG_2()); - map.put(Fields.SIG_3_DATA_GOING.name(), item.getAckStateGoing().getSIG_3()); - map.put(Fields.SIG_4_DATA_GOING.name(), item.getAckStateGoing().getSIG_4()); - map.put(Fields.SIG_5_DATA_GOING.name(), item.getAckStateGoing().getSIG_5()); - map.put(Fields.SIG_6_DATA_GOING.name(), item.getAckStateGoing().getSIG_6()); - map.put(Fields.SIG_7_DATA_GOING.name(), item.getAckStateGoing().getSIG_7()); - map.put(Fields.SIG_8_DATA_GOING.name(), item.getAckStateGoing().getSIG_8()); - - map.put(Fields.SIG_1_DATA_COMING.name(), item.getAckStateComing().getSIG_1()); - map.put(Fields.SIG_2_DATA_COMING.name(), item.getAckStateComing().getSIG_2()); - map.put(Fields.SIG_3_DATA_COMING.name(), item.getAckStateComing().getSIG_3()); - map.put(Fields.SIG_4_DATA_COMING.name(), item.getAckStateComing().getSIG_4()); - map.put(Fields.SIG_5_DATA_COMING.name(), item.getAckStateComing().getSIG_5()); - map.put(Fields.SIG_6_DATA_COMING.name(), item.getAckStateComing().getSIG_6()); - map.put(Fields.SIG_7_DATA_COMING.name(), item.getAckStateComing().getSIG_7()); - map.put(Fields.SIG_8_DATA_COMING.name(), item.getAckStateComing().getSIG_8()); - - List values = item.getAssociatedValues(); - int i = 1; - int j = 0; - for (AssociatedValueType value : values) { - map.put("SIG_" + i + "_DATA_STATUS", value.getReturnCode().getValue()); - map.put("SIG_" + i + "_DATA_SIZE", value.getTransportSize().getValue()); - map.put("SIG_" + i + "_DATA_LENGTH", value.getValueLength()); - byte[] data = new byte[value.getData().size()]; - j = 0; - for (short s : value.getData()) { - data[j] = (byte) s; - j++; - } - map.put("SIG_" + i + "_DATA", data); - i++; - } - - } - - } - + S7AlarmEvent(Instant timestamp, Map obj) { + super(timestamp); + this.map = obj; } - ; - @Override public Map getMap() { return map; } - @Override - public Instant getTimestamp() { - return timeStamp; - } - @Override public PlcReadRequest getRequest() { throw new UnsupportedOperationException("Not supported yet."); @@ -671,5 +519,160 @@ public PlcResponseCode getResponseCode(String name) { throw new UnsupportedOperationException("Not supported yet."); } + public static S7AlarmEvent of(Object obj) { + if (obj instanceof S7PayloadAlarmAckInd) { + AlarmMessageAckPushType msg = ((S7PayloadAlarmAckInd) obj).getAlarmMessage(); + DateAndTime dt = msg.getTimeStamp(); + int year = (dt.getYear() >= 90) ? dt.getYear() + 1900 : dt.getYear() + 2000; + LocalDateTime ldt = LocalDateTime.of(year, + dt.getMonth(), + dt.getDay(), + dt.getHour(), + dt.getMinutes(), + dt.getSeconds(), + dt.getMsec() * 1000000); + Instant timeStamp = ldt.toInstant(ZoneOffset.UTC); + + Map map = new HashMap<>(); + map.put(S7SysEvent.Fields.TIMESTAMP.name(), timeStamp); + + List items = msg.getMessageObjects(); + for (AlarmMessageAckObjectPushType item : items) { + map.put(Fields.EVENT_ID.name(), item.getEventId()); + map.put(Fields.TYPE.name(), "ALARMACK_IND"); + map.put(Fields.ASSOCIATED_VALUES.name(), item.getNumberOfValues()); + + map.put(Fields.SIG_1_DATA_GOING.name(), item.getAckStateGoing().getSIG_1()); + map.put(Fields.SIG_2_DATA_GOING.name(), item.getAckStateGoing().getSIG_2()); + map.put(Fields.SIG_3_DATA_GOING.name(), item.getAckStateGoing().getSIG_3()); + map.put(Fields.SIG_4_DATA_GOING.name(), item.getAckStateGoing().getSIG_4()); + map.put(Fields.SIG_5_DATA_GOING.name(), item.getAckStateGoing().getSIG_5()); + map.put(Fields.SIG_6_DATA_GOING.name(), item.getAckStateGoing().getSIG_6()); + map.put(Fields.SIG_7_DATA_GOING.name(), item.getAckStateGoing().getSIG_7()); + map.put(Fields.SIG_8_DATA_GOING.name(), item.getAckStateGoing().getSIG_8()); + + map.put(Fields.SIG_1_DATA_COMING.name(), item.getAckStateComing().getSIG_1()); + map.put(Fields.SIG_2_DATA_COMING.name(), item.getAckStateComing().getSIG_2()); + map.put(Fields.SIG_3_DATA_COMING.name(), item.getAckStateComing().getSIG_3()); + map.put(Fields.SIG_4_DATA_COMING.name(), item.getAckStateComing().getSIG_4()); + map.put(Fields.SIG_5_DATA_COMING.name(), item.getAckStateComing().getSIG_5()); + map.put(Fields.SIG_6_DATA_COMING.name(), item.getAckStateComing().getSIG_6()); + map.put(Fields.SIG_7_DATA_COMING.name(), item.getAckStateComing().getSIG_7()); + map.put(Fields.SIG_8_DATA_COMING.name(), item.getAckStateComing().getSIG_8()); + } + return new S7AlarmEvent(timeStamp, map); + } else { + + AlarmMessagePushType msg = null; + + if (obj instanceof S7PayloadAlarm8) { + msg = ((S7PayloadAlarm8) obj).getAlarmMessage(); + } else if (obj instanceof S7PayloadNotify) { + msg = ((S7PayloadNotify) obj).getAlarmMessage(); + } else if (obj instanceof S7PayloadAlarmSQ) { + msg = ((S7PayloadAlarmSQ) obj).getAlarmMessage(); + } else if (obj instanceof S7PayloadAlarmS) { + msg = ((S7PayloadAlarmS) obj).getAlarmMessage(); + } else if (obj instanceof S7PayloadNotify8) { + msg = ((S7PayloadNotify8) obj).getAlarmMessage(); + } else { + throw new PlcRuntimeException("Unsupported type: " + obj.getClass().getName()); + } + + DateAndTime dt = msg.getTimeStamp(); + int year = (dt.getYear() >= 90) ? dt.getYear() + 1900 : dt.getYear() + 2000; + LocalDateTime ldt = LocalDateTime.of(year, + dt.getMonth(), + dt.getDay(), + dt.getHour(), + dt.getMinutes(), + dt.getSeconds(), + dt.getMsec() * 1000000); + Instant timeStamp = ldt.toInstant(ZoneOffset.UTC); + + Map map = new HashMap<>(); + map.put(S7SysEvent.Fields.TIMESTAMP.name(), timeStamp); + + List items = msg.getMessageObjects(); + for (AlarmMessageObjectPushType item : items) { + map.put(Fields.EVENT_ID.name(), item.getEventId()); + + if (obj instanceof S7PayloadAlarm8) { + map.put(Fields.TYPE.name(), "ALARM8"); + } + if (obj instanceof S7PayloadNotify) { + map.put(Fields.TYPE.name(), "NOTIFY"); + } + if (obj instanceof S7PayloadAlarmSQ) { + map.put(Fields.TYPE.name(), "ALARMSQ"); + } + if (obj instanceof S7PayloadAlarmS) { + map.put(Fields.TYPE.name(), "ALARMS"); + } + if (obj instanceof S7PayloadNotify8) { + map.put(Fields.TYPE.name(), "NOTIFY8"); + } + + map.put(Fields.ASSOCIATED_VALUES.name(), item.getNumberOfValues()); + + map.put(Fields.SIG_1.name(), item.getEventState().getSIG_1()); + map.put(Fields.SIG_2.name(), item.getEventState().getSIG_2()); + map.put(Fields.SIG_3.name(), item.getEventState().getSIG_3()); + map.put(Fields.SIG_4.name(), item.getEventState().getSIG_4()); + map.put(Fields.SIG_5.name(), item.getEventState().getSIG_5()); + map.put(Fields.SIG_6.name(), item.getEventState().getSIG_6()); + map.put(Fields.SIG_7.name(), item.getEventState().getSIG_7()); + map.put(Fields.SIG_8.name(), item.getEventState().getSIG_8()); + + + map.put(Fields.SIG_1_STATE.name(), item.getLocalState().getSIG_1()); + map.put(Fields.SIG_2_STATE.name(), item.getLocalState().getSIG_2()); + map.put(Fields.SIG_3_STATE.name(), item.getLocalState().getSIG_3()); + map.put(Fields.SIG_4_STATE.name(), item.getLocalState().getSIG_4()); + map.put(Fields.SIG_5_STATE.name(), item.getLocalState().getSIG_5()); + map.put(Fields.SIG_6_STATE.name(), item.getLocalState().getSIG_6()); + map.put(Fields.SIG_7_STATE.name(), item.getLocalState().getSIG_7()); + map.put(Fields.SIG_8_STATE.name(), item.getLocalState().getSIG_8()); + + map.put(Fields.SIG_1_DATA_GOING.name(), item.getAckStateGoing().getSIG_1()); + map.put(Fields.SIG_2_DATA_GOING.name(), item.getAckStateGoing().getSIG_2()); + map.put(Fields.SIG_3_DATA_GOING.name(), item.getAckStateGoing().getSIG_3()); + map.put(Fields.SIG_4_DATA_GOING.name(), item.getAckStateGoing().getSIG_4()); + map.put(Fields.SIG_5_DATA_GOING.name(), item.getAckStateGoing().getSIG_5()); + map.put(Fields.SIG_6_DATA_GOING.name(), item.getAckStateGoing().getSIG_6()); + map.put(Fields.SIG_7_DATA_GOING.name(), item.getAckStateGoing().getSIG_7()); + map.put(Fields.SIG_8_DATA_GOING.name(), item.getAckStateGoing().getSIG_8()); + + map.put(Fields.SIG_1_DATA_COMING.name(), item.getAckStateComing().getSIG_1()); + map.put(Fields.SIG_2_DATA_COMING.name(), item.getAckStateComing().getSIG_2()); + map.put(Fields.SIG_3_DATA_COMING.name(), item.getAckStateComing().getSIG_3()); + map.put(Fields.SIG_4_DATA_COMING.name(), item.getAckStateComing().getSIG_4()); + map.put(Fields.SIG_5_DATA_COMING.name(), item.getAckStateComing().getSIG_5()); + map.put(Fields.SIG_6_DATA_COMING.name(), item.getAckStateComing().getSIG_6()); + map.put(Fields.SIG_7_DATA_COMING.name(), item.getAckStateComing().getSIG_7()); + map.put(Fields.SIG_8_DATA_COMING.name(), item.getAckStateComing().getSIG_8()); + + List values = item.getAssociatedValues(); + int i = 1; + int j = 0; + for (AssociatedValueType value : values) { + map.put("SIG_" + i + "_DATA_STATUS", value.getReturnCode().getValue()); + map.put("SIG_" + i + "_DATA_SIZE", value.getTransportSize().getValue()); + map.put("SIG_" + i + "_DATA_LENGTH", value.getValueLength()); + byte[] data = new byte[value.getData().size()]; + j = 0; + for (short s : value.getData()) { + data[j] = (byte) s; + j++; + } + map.put("SIG_" + i + "_DATA", data); + i++; + } + } + + return new S7AlarmEvent(timeStamp, map); + } + + } } diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7CyclicEvent.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7CyclicEvent.java index 6b7c83d3bf2..9c0d0fcd83b 100644 --- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7CyclicEvent.java +++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7CyclicEvent.java @@ -17,12 +17,14 @@ * under the License. */ package org.apache.plc4x.java.s7.events; - import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import static io.netty.buffer.Unpooled.wrappedBuffer; +import org.apache.plc4x.java.api.messages.PlcMetadataKeys; import org.apache.plc4x.java.api.messages.PlcReadRequest; import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest; +import org.apache.plc4x.java.spi.metadata.DefaultMetadata; +import org.apache.plc4x.java.api.metadata.time.TimeSource; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.api.value.PlcValue; @@ -31,7 +33,6 @@ import org.apache.plc4x.java.s7.readwrite.S7PayloadUserDataItemCyclicServicesPush; import org.apache.plc4x.java.s7.readwrite.S7PayloadUserDataItemCyclicServicesSubscribeResponse; import org.apache.plc4x.java.s7.readwrite.utils.StaticHelper; - import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.Charset; @@ -43,8 +44,7 @@ import org.apache.plc4x.java.s7.readwrite.tag.S7Tag; import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionTag; import org.apache.plc4x.java.spi.values.DefaultPlcValueHandler; - -public class S7CyclicEvent implements S7Event { +public class S7CyclicEvent extends S7EventBase implements S7Event { public enum Fields { TYPE, @@ -57,110 +57,104 @@ public enum Fields { TRANSPORTSIZE_, DATA_ } - private final PlcSubscriptionRequest request; - - private final Instant timeStamp; private final Map map; - public S7CyclicEvent(PlcSubscriptionRequest request, short jobid, S7PayloadUserDataItemCyclicServicesPush event) { + super(Instant.now(), new DefaultMetadata.Builder() + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build() + ); this.map = new HashMap<>(); - this.timeStamp = Instant.now(); this.request = request; map.put(Fields.TYPE.name(), "CYCEVENT"); - map.put(Fields.TIMESTAMP.name(), this.timeStamp); + map.put(Fields.TIMESTAMP.name(), getTimestamp()); map.put(Fields.JOBID.name(), jobid); map.put(Fields.ITEMSCOUNT.name(), event.getItemsCount()); - int[] n = new int[1]; + int[] n = new int[1]; request.getTagNames().forEach(tagname -> { int i = n[0]; map.put(Fields.RETURNCODE_.name() + i, event.getItems().get(i).getReturnCode().getValue()); map.put(Fields.TRANSPORTSIZE_.name() + i, event.getItems().get(i).getTransportSize().getValue()); map.put(tagname, dataToPlcValue(tagname, request, event.getItems().get(i).getData())); - n[0]++; + n[0]++; }); - } - public S7CyclicEvent(PlcSubscriptionRequest request, short jobid, S7PayloadUserDataItemCyclicServicesChangeDrivenPush event) { + super(Instant.now(), new DefaultMetadata.Builder() + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build() + ); this.map = new HashMap<>(); - this.timeStamp = Instant.now(); this.request = request; map.put(Fields.TYPE.name(), "CYCEVENT"); - map.put(Fields.TIMESTAMP.name(), this.timeStamp); + map.put(Fields.TIMESTAMP.name(), getTimestamp()); map.put(Fields.JOBID.name(), jobid); map.put(Fields.ITEMSCOUNT.name(), event.getItemsCount()); int[] n = new int[1]; - + request.getTagNames().forEach(tagname -> { int i = n[0]; map.put(Fields.RETURNCODE_.name() + i, event.getItems().get(i).getReturnCode().getValue()); map.put(Fields.TRANSPORTSIZE_.name() + i, event.getItems().get(i).getTransportSize().getValue()); map.put(tagname, dataToPlcValue(tagname, request, event.getItems().get(i).getData())); - n[0]++; + n[0]++; }); - + } - public S7CyclicEvent(PlcSubscriptionRequest request, short jobid, S7PayloadUserDataItemCyclicServicesSubscribeResponse event) { + super(Instant.now(), new DefaultMetadata.Builder() + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build() + ); this.map = new HashMap<>(); - this.timeStamp = Instant.now(); this.request = request; map.put(Fields.TYPE.name(), "CYCEVENT"); - map.put(Fields.TIMESTAMP.name(), this.timeStamp); + map.put(Fields.TIMESTAMP.name(), getTimestamp()); map.put(Fields.JOBID.name(), jobid); map.put(Fields.ITEMSCOUNT.name(), event.getItemsCount()); int[] n = new int[1]; - request.getTagNames().forEach(tagname -> { int i = n[0]; map.put(Fields.RETURNCODE_.name() + i, event.getItems().get(i).getReturnCode().getValue()); map.put(Fields.TRANSPORTSIZE_.name() + i, event.getItems().get(i).getTransportSize().getValue()); map.put(tagname, dataToPlcValue(tagname, request, event.getItems().get(i).getData())); - n[0]++; - }); + n[0]++; + }); } - public S7CyclicEvent(PlcSubscriptionRequest request, short jobid, S7PayloadUserDataItemCyclicServicesChangeDrivenSubscribeResponse event) { + super(Instant.now(), new DefaultMetadata.Builder() + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build() + ); this.map = new HashMap<>(); - this.timeStamp = Instant.now(); this.request = request; map.put(Fields.TYPE.name(), "CYCEVENT"); - map.put(Fields.TIMESTAMP.name(), this.timeStamp); + map.put(Fields.TIMESTAMP.name(), getTimestamp()); map.put(Fields.JOBID.name(), jobid); map.put(Fields.ITEMSCOUNT.name(), event.getItemsCount()); int[] n = new int[1]; - + request.getTagNames().forEach(tagname -> { int i = n[0]; map.put(Fields.RETURNCODE_.name() + i, event.getItems().get(i).getReturnCode().getValue()); map.put(Fields.TRANSPORTSIZE_.name() + i, event.getItems().get(i).getTransportSize().getValue()); map.put(tagname, dataToPlcValue(tagname, request, event.getItems().get(i).getData())); - n[0]++; - }); + n[0]++; + }); } - @Override public Map getMap() { return this.map; } - - @Override - public Instant getTimestamp() { - return this.timeStamp; - } - @Override public PlcReadRequest getRequest() { throw new UnsupportedOperationException("Not supported yet."); } - @Override public PlcValue getAsPlcValue() { throw new UnsupportedOperationException("Not supported yet."); } - @Override public PlcValue getPlcValue(String name) { if (request.getTagNames().contains(name)) { @@ -170,33 +164,27 @@ public PlcValue getPlcValue(String name) { } return null; } - @Override public int getNumberOfValues(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public Object getObject(String name) { if ("REQUEST".equals(name)) return request; return null; } - @Override public Object getObject(String name, int index) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public Collection getAllObjects(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public boolean isValidBoolean(String name) { return isValidBoolean(name, 0); } - @Override public boolean isValidBoolean(String name, int index) { try { @@ -206,12 +194,10 @@ public boolean isValidBoolean(String name, int index) { return false; } } - @Override public Boolean getBoolean(String name) { return getBoolean(name, 0); } - @Override public Boolean getBoolean(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -220,17 +206,14 @@ public Boolean getBoolean(String name, int index) { ByteBuf byteBuf = Unpooled.wrappedBuffer((byte[]) map.get(name)); return byteBuf.getBoolean(index); } - @Override public Collection getAllBooleans(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public boolean isValidByte(String name) { return isValidByte(name, 0); } - @Override public boolean isValidByte(String name, int index) { try { @@ -240,12 +223,10 @@ public boolean isValidByte(String name, int index) { return false; } } - @Override public Byte getByte(String name) { return getByte(name, 0); } - @Override public Byte getByte(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -255,7 +236,6 @@ public Byte getByte(String name, int index) { int pos = index * Byte.BYTES; return byteBuf.getByte(pos); } - @Override public Collection getAllBytes(String name) { if (!(map.get(name) instanceof byte[])) { @@ -264,12 +244,10 @@ public Collection getAllBytes(String name) { byte[] array = (byte[]) map.get(name); return IntStream.range(0, array.length).mapToObj(i -> array[i]).collect(Collectors.toList()); } - @Override public boolean isValidShort(String name) { return isValidShort(name, 0); } - @Override public boolean isValidShort(String name, int index) { try { @@ -279,12 +257,10 @@ public boolean isValidShort(String name, int index) { return false; } } - @Override public Short getShort(String name) { return getShort(name, 0); } - @Override public Short getShort(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -294,7 +270,6 @@ public Short getShort(String name, int index) { int pos = index * Short.BYTES; return byteBuf.getShort(pos); } - @Override public Collection getAllShorts(String name) { if (!(map.get(name) instanceof byte[])) { @@ -307,12 +282,10 @@ public Collection getAllShorts(String name) { } return list; } - @Override public boolean isValidInteger(String name) { return isValidInteger(name, 0); } - @Override public boolean isValidInteger(String name, int index) { try { @@ -322,12 +295,10 @@ public boolean isValidInteger(String name, int index) { return false; } } - @Override public Integer getInteger(String name) { return getInteger(name, 0); } - @Override public Integer getInteger(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -337,7 +308,6 @@ public Integer getInteger(String name, int index) { int pos = index * Integer.BYTES; return byteBuf.getInt(pos); } - @Override public Collection getAllIntegers(String name) { if (!(map.get(name) instanceof byte[])) { @@ -350,37 +320,30 @@ public Collection getAllIntegers(String name) { } return list; } - @Override public boolean isValidBigInteger(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public boolean isValidBigInteger(String name, int index) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public BigInteger getBigInteger(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public BigInteger getBigInteger(String name, int index) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public Collection getAllBigIntegers(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public boolean isValidLong(String name) { return isValidLong(name, 0); } - @Override public boolean isValidLong(String name, int index) { try { @@ -390,12 +353,10 @@ public boolean isValidLong(String name, int index) { return false; } } - @Override public Long getLong(String name) { return getLong(name, 0); } - @Override public Long getLong(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -405,7 +366,6 @@ public Long getLong(String name, int index) { int pos = index * Long.BYTES; return byteBuf.getLong(pos); } - @Override public Collection getAllLongs(String name) { if (!(map.get(name) instanceof byte[])) { @@ -418,12 +378,10 @@ public Collection getAllLongs(String name) { } return list; } - @Override public boolean isValidFloat(String name) { return isValidFloat(name, 0); } - @Override public boolean isValidFloat(String name, int index) { try { @@ -433,12 +391,10 @@ public boolean isValidFloat(String name, int index) { return false; } } - @Override public Float getFloat(String name) { return getFloat(name, 0); } - @Override public Float getFloat(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -448,7 +404,6 @@ public Float getFloat(String name, int index) { int pos = index * Float.BYTES; return byteBuf.getFloat(pos); } - @Override public Collection getAllFloats(String name) { if (!(map.get(name) instanceof byte[])) { @@ -461,12 +416,10 @@ public Collection getAllFloats(String name) { } return list; } - @Override public boolean isValidDouble(String name) { return isValidDouble(name, 0); } - @Override public boolean isValidDouble(String name, int index) { try { @@ -476,12 +429,10 @@ public boolean isValidDouble(String name, int index) { return false; } } - @Override public Double getDouble(String name) { return getDouble(name, 0); } - @Override public Double getDouble(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -491,7 +442,6 @@ public Double getDouble(String name, int index) { int pos = index * Double.BYTES; return byteBuf.getDouble(pos); } - @Override public Collection getAllDoubles(String name) { if (!(map.get(name) instanceof byte[])) { @@ -504,37 +454,30 @@ public Collection getAllDoubles(String name) { } return list; } - @Override public boolean isValidBigDecimal(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public boolean isValidBigDecimal(String name, int index) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public BigDecimal getBigDecimal(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public BigDecimal getBigDecimal(String name, int index) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public Collection getAllBigDecimals(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public boolean isValidString(String name) { return isValidString(name, 0); } - @Override public boolean isValidString(String name, int index) { try { @@ -544,7 +487,6 @@ public boolean isValidString(String name, int index) { return false; } } - @Override public String getString(String name) { if (!(map.get(name) instanceof byte[])) { @@ -553,22 +495,18 @@ public String getString(String name) { ByteBuf byteBuf = Unpooled.wrappedBuffer((byte[]) map.get(name)); return byteBuf.toString(Charset.defaultCharset()); } - @Override public String getString(String name, int index) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public Collection getAllStrings(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public boolean isValidTime(String name) { return isValidTime(name, 0); } - @Override public boolean isValidTime(String name, int index) { try { @@ -578,12 +516,10 @@ public boolean isValidTime(String name, int index) { return false; } } - @Override public LocalTime getTime(String name) { return getTime(name, 0); } - /* * In S7, data type TIME occupies one double word. * The value is in milliseconds (ms). @@ -599,7 +535,6 @@ public LocalTime getTime(String name, int index) { Duration dr = StaticHelper.s7TimeToDuration(value); return LocalTime.of(dr.toHoursPart(), dr.toMinutesPart(), dr.toSecondsPart(), dr.toNanosPart()); } - @Override public Collection getAllTimes(String name) { if (!(map.get(name) instanceof byte[])) { @@ -613,12 +548,10 @@ public Collection getAllTimes(String name) { } return items; } - @Override public boolean isValidDate(String name) { return isValidDate(name, 0); } - @Override public boolean isValidDate(String name, int index) { try { @@ -628,12 +561,10 @@ public boolean isValidDate(String name, int index) { return false; } } - @Override public LocalDate getDate(String name) { return getDate(name, 0); } - @Override public LocalDate getDate(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -644,7 +575,6 @@ public LocalDate getDate(String name, int index) { short value = byteBuf.getShort(pos); return StaticHelper.s7DateToLocalDate(value); } - @Override public Collection getAllDates(String name) { if (!(map.get(name) instanceof byte[])) { @@ -658,12 +588,10 @@ public Collection getAllDates(String name) { } return items; } - @Override public boolean isValidDateTime(String name) { return isValidDateTime(name, 0); } - @Override public boolean isValidDateTime(String name, int index) { try { @@ -673,12 +601,10 @@ public boolean isValidDateTime(String name, int index) { return false; } } - @Override public LocalDateTime getDateTime(String name) { return getDateTime(name, 0); } - @Override public LocalDateTime getDateTime(String name, int index) { if (!(map.get(name) instanceof byte[])) { @@ -688,7 +614,6 @@ public LocalDateTime getDateTime(String name, int index) { int pos = index * Long.BYTES; return StaticHelper.s7DateTimeToLocalDateTime(byteBuf.slice(pos, Long.BYTES)); } - @Override public Collection getAllDateTimes(String name) { if (!(map.get(name) instanceof byte[])) { @@ -702,22 +627,18 @@ public Collection getAllDateTimes(String name) { } return items; } - @Override public Collection getTagNames() { throw new UnsupportedOperationException("Not supported yet."); } - @Override public PlcTag getTag(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public PlcResponseCode getResponseCode(String name) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public boolean equals(Object obj) { if (this == obj) { @@ -730,7 +651,7 @@ public boolean equals(Object obj) { return false; } final S7CyclicEvent other = (S7CyclicEvent) obj; - + for (String tag:request.getTagNames()) { final PlcValue othervalue = other.getPlcValue(tag); if (othervalue == null) return false; @@ -739,7 +660,7 @@ public boolean equals(Object obj) { return false; } }; - + return true; } @@ -747,23 +668,22 @@ public boolean equals(Object obj) { private static PlcValue dataToPlcValue(String tagname, PlcSubscriptionRequest request, List data){ int[] i = new int[1]; - + final byte[] buffer = new byte[data.size()]; - data.forEach( b -> { - buffer[i[0]] = b.byteValue(); + buffer[i[0]] = b.byteValue(); i[0]++; }); - + ByteBuf bb = wrappedBuffer(buffer); - - + + final DefaultPlcSubscriptionTag dpst = (DefaultPlcSubscriptionTag) request.getTag(tagname); final S7SubscriptionTag subTag = (S7SubscriptionTag) dpst.getTag(); final S7Tag[] s7Tags = subTag.getS7Tags(); - + PlcValue plcValue = null; - + switch(s7Tags[0].getDataType()){ case BOOL: Boolean[] bools = new Boolean[s7Tags[0].getNumberOfElements()]; @@ -781,11 +701,11 @@ private static PlcValue dataToPlcValue(String tagname, PlcSubscriptionRequest re plcValue = DefaultPlcValueHandler.of(s7Tags[0], bytes); break; case WORD: - break; + break; case DWORD: - break; + break; case LWORD: - break; + break; case INT: Short[] shorts = new Short[s7Tags[0].getNumberOfElements()]; for (int iter = 0; iter < s7Tags[0].getNumberOfElements(); iter ++) { @@ -794,11 +714,11 @@ private static PlcValue dataToPlcValue(String tagname, PlcSubscriptionRequest re plcValue = DefaultPlcValueHandler.of(s7Tags[0], shorts); break; case UINT: - break; + break; case SINT: - break; + break; case USINT: - break; + break; case DINT: // TODO: This looks suspicious Integer[] integers = new Integer[bb.capacity() / Integer.SIZE]; @@ -808,7 +728,7 @@ private static PlcValue dataToPlcValue(String tagname, PlcSubscriptionRequest re plcValue = DefaultPlcValueHandler.of(s7Tags[0], integers); break; case UDINT: - break; + break; case LINT: // TODO: This looks suspicious Long[] longs = new Long[bb.capacity() / Long.SIZE]; @@ -818,7 +738,7 @@ private static PlcValue dataToPlcValue(String tagname, PlcSubscriptionRequest re plcValue = DefaultPlcValueHandler.of(s7Tags[0], longs); break; case ULINT: - break; + break; case REAL: // TODO: This looks suspicious Float[] floats = new Float[bb.capacity() / Float.SIZE]; @@ -836,42 +756,41 @@ private static PlcValue dataToPlcValue(String tagname, PlcSubscriptionRequest re plcValue = DefaultPlcValueHandler.of(s7Tags[0], doubles); break; case CHAR: - break; + break; case WCHAR: - break; + break; case STRING: - break; + break; case WSTRING: - break; + break; case S5TIME: break; case TIME: - break; + break; case LTIME: - break; + break; case DATE: - break; + break; case TIME_OF_DAY: - break; + break; case TOD: - break; + break; case LTIME_OF_DAY: - break; + break; case LTOD: - break; + break; case DATE_AND_TIME: - break; + break; case DT: - break; + break; case DATE_AND_LTIME: - break; + break; case LDT: - break; + break; case DTL: - break; + break; } - + return plcValue; } - } diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7EventBase.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7EventBase.java new file mode 100644 index 00000000000..7580b72923d --- /dev/null +++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7EventBase.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.s7.events; + +import java.time.Instant; +import org.apache.plc4x.java.api.messages.PlcMetadataKeys; +import org.apache.plc4x.java.api.metadata.Metadata; +import org.apache.plc4x.java.spi.metadata.DefaultMetadata; +import org.apache.plc4x.java.api.metadata.time.TimeSource; + +public abstract class S7EventBase implements S7Event { + + private final Instant timestamp; + private final Metadata metadata; + + S7EventBase() { + this(Instant.now()); + } + + S7EventBase(Instant timestamp) { + this(timestamp, new DefaultMetadata.Builder() + .put(PlcMetadataKeys.TIMESTAMP, timestamp.getEpochSecond()) + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.HARDWARE) // event triggered by PLC itself + .build() + ); + } + + S7EventBase(Instant timestamp, Metadata metadata) { + this.timestamp = timestamp; + this.metadata = metadata; + } + + @Override + public Metadata getTagMetadata(String name) { + return metadata; + } + + @Override + public Instant getTimestamp() { + return timestamp; + } +} diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7ModeEvent.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7ModeEvent.java index b144ad8510f..4a9d53f204b 100644 --- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7ModeEvent.java +++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7ModeEvent.java @@ -18,7 +18,10 @@ */ package org.apache.plc4x.java.s7.events; +import org.apache.plc4x.java.api.messages.PlcMetadataKeys; import org.apache.plc4x.java.api.messages.PlcReadRequest; +import org.apache.plc4x.java.spi.metadata.DefaultMetadata; +import org.apache.plc4x.java.api.metadata.time.TimeSource; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.api.value.PlcValue; @@ -34,7 +37,7 @@ import java.util.HashMap; import java.util.Map; -public class S7ModeEvent implements S7Event { +public class S7ModeEvent extends S7EventBase implements S7Event { public enum Fields { @@ -46,17 +49,19 @@ public enum Fields { CURRENT_MODE } - private final Instant timeStamp; private final Map map; public S7ModeEvent(S7ParameterModeTransition parameter) { + super(Instant.now(), new DefaultMetadata.Builder() + .put(PlcMetadataKeys.TIMESTAMP_SOURCE, TimeSource.ASSUMPTION) + .build() + ); this.map = new HashMap<>(); map.put(Fields.TYPE.name(), "MODE"); map.put(Fields.METHOD.name(), parameter.getMethod()); map.put(Fields.FUNCTION.name(), parameter.getCpuFunctionType()); map.put(Fields.CURRENT_MODE.name(), parameter.getCurrentMode()); - this.timeStamp = Instant.now(); - map.put(Fields.TIMESTAMP.name(), this.timeStamp); + map.put(Fields.TIMESTAMP.name(), getTimestamp()); // TODO: Is this really correct, to put the map itself in itself? map.put(Fields.MAP.name(), map); } @@ -67,11 +72,6 @@ public Map getMap() { return map; } - @Override - public Instant getTimestamp() { - return timeStamp; - } - @Override public PlcReadRequest getRequest() { throw new UnsupportedOperationException("Not supported yet."); diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7SysEvent.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7SysEvent.java index 4fab9b97ac0..364adc443d9 100644 --- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7SysEvent.java +++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7SysEvent.java @@ -32,7 +32,7 @@ import java.util.HashMap; import java.util.Map; -public class S7SysEvent implements S7Event { +public class S7SysEvent extends S7EventBase implements S7Event { public enum Fields { TIMESTAMP, @@ -45,30 +45,11 @@ public enum Fields { INFO2 } - private final Instant timeStamp; protected final Map map; - public S7SysEvent(S7PayloadDiagnosticMessage payload) { - this.map = new HashMap(); - map.put(Fields.TYPE.name(), "SYS"); - map.put(Fields.EVENT_ID.name(), payload.getEventId()); - map.put(Fields.PRIORITY_CLASS.name(), payload.getPriorityClass()); - map.put(Fields.OB_NUMBER.name(), payload.getObNumber()); - map.put(Fields.DAT_ID.name(), payload.getDatId()); - map.put(Fields.INFO1.name(), payload.getInfo1()); - map.put(Fields.INFO2.name(), payload.getInfo2()); - - DateAndTime dt = payload.getTimeStamp(); - int year = (dt.getYear() >= 90) ? dt.getYear() + 1900 : dt.getYear() + 2000; - LocalDateTime ldt = LocalDateTime.of(year, - dt.getMonth(), - dt.getDay(), - dt.getHour(), - dt.getMinutes(), - dt.getSeconds(), - dt.getMsec() * 1000000); - this.timeStamp = ldt.toInstant(ZoneOffset.UTC); - map.put(Fields.TIMESTAMP.name(), this.timeStamp); + S7SysEvent(Instant instant, Map map) { + super(instant); + this.map = map; } @Override @@ -76,11 +57,6 @@ public Map getMap() { return map; } - @Override - public Instant getTimestamp() { - return timeStamp; - } - @Override public PlcReadRequest getRequest() { throw new UnsupportedOperationException("Not supported yet."); @@ -456,4 +432,27 @@ public PlcResponseCode getResponseCode(String name) { throw new UnsupportedOperationException("Not supported yet."); } + public static S7SysEvent of(S7PayloadDiagnosticMessage payload) { + Map map = new HashMap<>(); + map.put(Fields.TYPE.name(), "SYS"); + map.put(Fields.EVENT_ID.name(), payload.getEventId()); + map.put(Fields.PRIORITY_CLASS.name(), payload.getPriorityClass()); + map.put(Fields.OB_NUMBER.name(), payload.getObNumber()); + map.put(Fields.DAT_ID.name(), payload.getDatId()); + map.put(Fields.INFO1.name(), payload.getInfo1()); + map.put(Fields.INFO2.name(), payload.getInfo2()); + + DateAndTime dt = payload.getTimeStamp(); + int year = (dt.getYear() >= 90) ? dt.getYear() + 1900 : dt.getYear() + 2000; + LocalDateTime ldt = LocalDateTime.of(year, + dt.getMonth(), + dt.getDay(), + dt.getHour(), + dt.getMinutes(), + dt.getSeconds(), + dt.getMsec() * 1000000); + Instant timestamp = ldt.toInstant(ZoneOffset.UTC); + map.put(Fields.TIMESTAMP.name(), timestamp); + return new S7SysEvent(timestamp, map); + } } diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7UserEvent.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7UserEvent.java index d9f01d0dbdb..d5805fc25eb 100644 --- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7UserEvent.java +++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/events/S7UserEvent.java @@ -18,12 +18,21 @@ */ package org.apache.plc4x.java.s7.events; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; import org.apache.plc4x.java.s7.readwrite.S7PayloadDiagnosticMessage; public class S7UserEvent extends S7SysEvent { - public S7UserEvent(S7PayloadDiagnosticMessage payload) { - super(payload); + S7UserEvent(Instant instant, Map map) { + super(instant, map); + } + + public static S7UserEvent of(S7PayloadDiagnosticMessage payload) { + S7SysEvent event = S7SysEvent.of(payload); + Map map = new HashMap<>(event.getMap()); map.put(Fields.TYPE.name(), "USER"); + return new S7UserEvent(event.getTimestamp(), map); } } diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java index 8f4eee69c29..97aaa7835a9 100644 --- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java +++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java @@ -1570,10 +1570,10 @@ protected void decode(ConversationContext context, TPKTPacket msg) t if (item instanceof S7PayloadDiagnosticMessage) { final S7PayloadDiagnosticMessage pload = (S7PayloadDiagnosticMessage) item; if ((pload.getEventId() >= 0x0A000) & (pload.getEventId() <= 0x0BFFF)) { - S7UserEvent userEvent = new S7UserEvent(pload); + S7UserEvent userEvent = S7UserEvent.of(pload); eventQueue.add(userEvent); } else { - S7SysEvent sysEvent = new S7SysEvent(pload); + S7SysEvent sysEvent = S7SysEvent.of(pload); eventQueue.add(sysEvent); } } @@ -1589,7 +1589,7 @@ protected void decode(ConversationContext context, TPKTPacket msg) t (myParameter.getCpuSubfunction() == 0x16))) { //(04) payload.getItems().forEach(item ->{ - S7AlarmEvent alrmEvent = new S7AlarmEvent(item); + S7AlarmEvent alrmEvent = S7AlarmEvent.of(item); eventQueue.add(alrmEvent); }); diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcReadResponse.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcReadResponse.java index 5983e519f6f..f0ee410501b 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcReadResponse.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcReadResponse.java @@ -18,10 +18,13 @@ */ package org.apache.plc4x.java.spi.messages; +import java.util.Map.Entry; import org.apache.plc4x.java.api.exceptions.PlcInvalidTagException; import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; import org.apache.plc4x.java.api.messages.PlcReadRequest; import org.apache.plc4x.java.api.messages.PlcReadResponse; +import org.apache.plc4x.java.api.metadata.Metadata; +import org.apache.plc4x.java.spi.metadata.DefaultMetadata; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.spi.generation.SerializationException; @@ -46,11 +49,19 @@ public class DefaultPlcReadResponse implements PlcReadResponse, Serializable { private final PlcReadRequest request; private final Map> values; + private final Map metadata; public DefaultPlcReadResponse(PlcReadRequest request, Map> values) { + this(request, values, Collections.emptyMap()); + } + + public DefaultPlcReadResponse(PlcReadRequest request, + Map> values, + Map metadata) { this.request = request; this.values = values; + this.metadata = Collections.unmodifiableMap(metadata); } @Override @@ -58,6 +69,11 @@ public PlcReadRequest getRequest() { return request; } + @Override + public Metadata getTagMetadata(String tag) { + return metadata.getOrDefault(tag, DefaultMetadata.EMPTY); + } + @Override public PlcValue getAsPlcValue() { Map structMap = new HashMap<>(); @@ -669,6 +685,20 @@ public void serialize(WriteBuffer writeBuffer) throws SerializationException { } writeBuffer.popContext("values"); + if (metadata != null && !metadata.isEmpty()) { + writeBuffer.pushContext("metadata", WithRenderAsList(true)); + + for (Entry entry : metadata.entrySet()) { + if (entry.getValue() instanceof Serializable) { + writeBuffer.pushContext(entry.getKey()); + ((Serializable) entry.getValue()).serialize(writeBuffer); + writeBuffer.popContext(entry.getKey()); + } + } + + writeBuffer.popContext("metadata"); + } + writeBuffer.popContext("PlcReadResponse"); } diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcSubscriptionEvent.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcSubscriptionEvent.java index 2035f76e001..e4ef1835aa0 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcSubscriptionEvent.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcSubscriptionEvent.java @@ -18,7 +18,10 @@ */ package org.apache.plc4x.java.spi.messages; +import java.util.Collections; import org.apache.plc4x.java.api.messages.PlcSubscriptionEvent; +import org.apache.plc4x.java.api.metadata.Metadata; +import org.apache.plc4x.java.spi.metadata.DefaultMetadata; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.value.PlcValue; import org.apache.plc4x.java.spi.messages.utils.PlcResponseItem; @@ -32,8 +35,14 @@ public class DefaultPlcSubscriptionEvent extends DefaultPlcReadResponse implemen public final Instant timestamp; public DefaultPlcSubscriptionEvent(Instant timestamp, - Map> tags) { - super(null, tags); + Map> tags) { + this(timestamp, tags, Collections.emptyMap()); + } + + public DefaultPlcSubscriptionEvent(Instant timestamp, + Map> tags, + Map metadata) { + super(null, tags, metadata); this.timestamp = timestamp; } diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcWriteResponse.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcWriteResponse.java index 329792956dd..96ff7ad066e 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcWriteResponse.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcWriteResponse.java @@ -18,8 +18,11 @@ */ package org.apache.plc4x.java.spi.messages; +import java.util.Collections; +import java.util.Map.Entry; import org.apache.plc4x.java.api.messages.PlcWriteRequest; import org.apache.plc4x.java.api.messages.PlcWriteResponse; +import org.apache.plc4x.java.api.metadata.Metadata; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.spi.generation.SerializationException; @@ -36,11 +39,19 @@ public class DefaultPlcWriteResponse implements PlcWriteResponse, Serializable { private final PlcWriteRequest request; private final Map responseCodes; + private final Map metadata; public DefaultPlcWriteResponse(PlcWriteRequest request, Map responseCodes) { + this(request, responseCodes, Collections.emptyMap()); + } + + public DefaultPlcWriteResponse(PlcWriteRequest request, + Map responseCodes, + Map metadata) { this.request = request; this.responseCodes = responseCodes; + this.metadata = metadata; } @Override @@ -48,6 +59,11 @@ public PlcWriteRequest getRequest() { return request; } + @Override + public Metadata getTagMetadata(String tag) { + return metadata.getOrDefault(tag, Metadata.EMPTY); + } + @Override public Collection getTagNames() { return request.getTagNames(); @@ -83,6 +99,20 @@ public void serialize(WriteBuffer writeBuffer) throws SerializationException { } writeBuffer.popContext("responseCodes"); + if (metadata != null && !metadata.isEmpty()) { + writeBuffer.pushContext("metadata", WithRenderAsList(true)); + + for (Entry entry : metadata.entrySet()) { + if (entry.getValue() instanceof Serializable) { + writeBuffer.pushContext(entry.getKey()); + ((Serializable) entry.getValue()).serialize(writeBuffer); + writeBuffer.popContext(entry.getKey()); + } + } + + writeBuffer.popContext("metadata"); + } + writeBuffer.popContext("PlcWriteResponse"); } diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/metadata/DefaultMetadata.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/metadata/DefaultMetadata.java new file mode 100644 index 00000000000..55bd78b4227 --- /dev/null +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/metadata/DefaultMetadata.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.java.spi.metadata; + +import static org.apache.plc4x.java.spi.generation.WithReaderWriterArgs.WithRenderAsList; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.apache.plc4x.java.api.metadata.Metadata; +import org.apache.plc4x.java.spi.generation.SerializationException; +import org.apache.plc4x.java.spi.generation.WriteBuffer; +import org.apache.plc4x.java.spi.utils.Serializable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultMetadata implements Metadata, Serializable { + + private final Metadata parent; + private final Map, Object> values; + + DefaultMetadata(Map, Object> values) { + this(values, Metadata.EMPTY); + } + + public DefaultMetadata(Map, Object> values, Metadata parent) { + this.values = new LinkedHashMap<>(values); + this.parent = Objects.requireNonNull(parent, "Parent metadata must not be null"); + } + + @Override + public Set> keys() { + Set> keys = new LinkedHashSet<>(values.keySet()); + keys.addAll(parent.keys()); + return Collections.unmodifiableSet(keys); + } + + @Override + public Map, Object> entries() { + Map, Object> copy = new LinkedHashMap<>(parent.entries()); + copy.putAll(values); + return Map.copyOf(copy); + } + + @Override + public Object getValue(Key key) { + Object value = values.get(key); + if (value == null) { + return parent.getValue(key); + } + return value; + } + + @Override + public void serialize(WriteBuffer writeBuffer) throws SerializationException { + for (Key metadataKey : keys()) { + writeBuffer.pushContext("entry", WithRenderAsList(false)); + writeBuffer.writeString("key", metadataKey.getKey().length(), metadataKey.getKey()); + String value = "" + getValue(metadataKey); + writeBuffer.writeString("value", value.length(), value); + writeBuffer.popContext("entry"); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Metadata)) { + return false; + } + Metadata that = (Metadata) o; + return Objects.equals(entries(), that.entries()); + } + + @Override + public int hashCode() { + return Objects.hash(entries()); + } + + public static class Builder { + private final Logger logger = LoggerFactory.getLogger(Builder.class); + + private final Map, Object> values = new LinkedHashMap<>(); + private final Metadata parent; + + public Builder() { + this(DefaultMetadata.EMPTY); + } + + public Builder(Metadata parent) { + this.parent = parent; + } + + public Builder put(Key key, T value) { + if (!key.validate(value)) { + logger.debug("Ignore metadata value {}, it does not match constraints imposed by key {}", value, key); + return this; + } + + values.put(key, value); + return this; + } + + public Metadata build() { + return new DefaultMetadata(values, parent); + } + } + +} diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/internal/validator/ApiValidator.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/internal/validator/ApiValidator.java index ddf6439c34b..1d538223bd3 100644 --- a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/internal/validator/ApiValidator.java +++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/internal/validator/ApiValidator.java @@ -19,6 +19,8 @@ package org.apache.plc4x.test.driver.internal.validator; import org.apache.plc4x.test.driver.exceptions.DriverTestsuiteException; +import org.apache.plc4x.test.driver.xmlunit.SkipAttributeFilter; +import org.apache.plc4x.test.driver.xmlunit.SkipDifferenceEvaluator; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,12 +28,15 @@ import org.xmlunit.diff.Diff; public class ApiValidator { + private static final Logger LOGGER = LoggerFactory.getLogger(ApiValidator.class); public static void validateApiMessage(Element referenceXml, String apiMessage) throws DriverTestsuiteException { final String referenceXmlString = referenceXml.asXML(); final Diff diff = DiffBuilder.compare(referenceXmlString) .withTest(apiMessage).checkForSimilar().ignoreComments().ignoreWhitespace() + .withDifferenceEvaluator(new SkipDifferenceEvaluator()) + .withAttributeFilter(new SkipAttributeFilter()) .build(); if (diff.hasDifferences()) { LOGGER.warn("got\n{}", apiMessage); diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/xmlunit/SkipAttributeFilter.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/xmlunit/SkipAttributeFilter.java new file mode 100644 index 00000000000..d7f63abff17 --- /dev/null +++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/xmlunit/SkipAttributeFilter.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.test.driver.xmlunit; + +import org.w3c.dom.Attr; +import org.xmlunit.util.Predicate; + +/** + * SPI element needed to exclude our custom attributes from comparison of XML results. + */ +public class SkipAttributeFilter implements Predicate { + + public static final String IGNORE_ATTRIBUTE_NAME = "plc4x-skip-comparison"; + + @Override + public boolean test(Attr attr) { + return !IGNORE_ATTRIBUTE_NAME.equals(attr.getName()); + } + +} diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/xmlunit/SkipDifferenceEvaluator.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/xmlunit/SkipDifferenceEvaluator.java new file mode 100644 index 00000000000..8e5e0f7cabb --- /dev/null +++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/driver/xmlunit/SkipDifferenceEvaluator.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.plc4x.test.driver.xmlunit; + +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.xmlunit.diff.Comparison; +import org.xmlunit.diff.ComparisonResult; +import org.xmlunit.diff.DifferenceEvaluator; + +/** + * Evaluator of differences which allows to ignore differences for elements annotated with 'plc4x-skip-comparison' attribute. + */ +public class SkipDifferenceEvaluator implements DifferenceEvaluator { + + @Override + public ComparisonResult evaluate(Comparison comparison, ComparisonResult comparisonResult) { + if (comparisonResult != ComparisonResult.EQUAL) { + Node target = comparison.getControlDetails().getTarget(); + + // root element + if (target == null || target.getParentNode() == null) { + return comparisonResult; + } + + // verify parent element - help with text nodes + NamedNodeMap attributes = target.getParentNode().getAttributes(); + if (attributes != null) { + Node attribute = attributes.getNamedItem(SkipAttributeFilter.IGNORE_ATTRIBUTE_NAME); + if (attribute != null) { + String content = attribute.getTextContent(); + return Boolean.parseBoolean(content.trim()) ? ComparisonResult.EQUAL : comparisonResult; + } + } + } + + return comparisonResult; + } +} 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..df1ddb6fb8c 100644 --- a/plc4py/plc4py/drivers/modbus/ModbusDevice.py +++ b/plc4py/plc4py/drivers/modbus/ModbusDevice.py @@ -18,6 +18,7 @@ # import asyncio import logging +import math from asyncio import Transport from dataclasses import dataclass, field from math import ceil @@ -209,6 +210,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( @@ -220,7 +222,7 @@ async def write( ) elif isinstance(tag, ModbusTagHoldingRegister): values = self._serialize_data_items(tag, values) - quantity = tag.quantity * (tag.data_type.data_type_size / 2) + quantity = math.ceil(tag.quantity * (tag.data_type.data_type_size / 2)) pdu = ModbusPDUWriteMultipleHoldingRegistersRequestBuilder( tag.address, quantity, values ).build() @@ -258,15 +260,49 @@ async def write( return write_response def _serialize_data_items(self, tag: ModbusTag, values: PlcValue) -> List[int]: - length = tag.quantity * tag.data_type.data_type_size - write_buffer = WriteBufferByteBased(length, self._configuration.byte_order) - - DataItem.static_serialize( - write_buffer, - values, - tag.data_type, - tag.quantity, - True, - self._configuration.byte_order, - ) + if ( + isinstance(tag, ModbusTagCoil) + and (tag.data_type == ModbusDataType.BOOL) + and not isinstance(values, PlcList) + ): + # Booleans are writen different depending on what registers they are being written to + length = tag.quantity + write_buffer = WriteBufferByteBased(length, self._configuration.byte_order) + write_buffer.write_unsigned_short(int(0x0000), 7, "int0x0000") + # Simple Field (value) + value: bool = values.get_bool() + write_buffer.write_bit((value), "value") + elif ( + isinstance(tag, ModbusTagCoil) + and (tag.data_type == ModbusDataType.BOOL) + and isinstance(values, PlcList) + ): + length = math.ceil(tag.quantity / 8) + write_buffer = WriteBufferByteBased(length, self._configuration.byte_order) + for i in range(length): + if i == length - 1: + sub_length = tag.quantity % 8 + else: + sub_length = 8 + sub_array = values.value[i * 8 : i * 8 + sub_length] + sub_array.reverse() + for j in range(8 - sub_length): + write_buffer.write_bit(False, "value") + for item in sub_array: + # Simple Field (value) + value: bool = item.get_bool() + write_buffer.write_bit(value, "value") + else: + length = tag.quantity * tag.data_type.data_type_size + if (length % 2) == 1: + length += 1 + write_buffer = WriteBufferByteBased(length, self._configuration.byte_order) + DataItem.static_serialize( + write_buffer, + values, + tag.data_type, + tag.quantity, + True, + self._configuration.byte_order, + ) return list(write_buffer.get_bytes().tobytes()) 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..fa07b80e92f 100644 --- a/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py +++ b/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py @@ -33,7 +33,6 @@ 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 PlcUDINT from plc4py.spi.values.PlcValues import PlcUINT from plc4py.spi.values.PlcValues import PlcULINT @@ -114,7 +113,7 @@ def static_parse( 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(read_buffer.read_bit("")) return PlcList(value) if ( @@ -165,7 +164,7 @@ def static_parse( item_count: int = int(number_of_values * int(8)) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcBOOL(bool(read_buffer.read_bit("")))) + value.append(read_buffer.read_bit("")) return PlcList(value) if data_type == ModbusDataType.WORD: # WORD @@ -234,9 +233,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcSINT(int(read_buffer.read_signed_byte(8, logical_name=""))) - ) + value.append(read_buffer.read_signed_byte(8, logical_name="")) return PlcList(value) if data_type == ModbusDataType.INT and number_of_values == int(1): # INT @@ -251,7 +248,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcINT(int(read_buffer.read_short(16, logical_name="")))) + value.append(read_buffer.read_short(16, logical_name="")) return PlcList(value) if data_type == ModbusDataType.DINT and number_of_values == int(1): # DINT @@ -266,7 +263,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcDINT(int(read_buffer.read_int(32, logical_name="")))) + value.append(read_buffer.read_int(32, logical_name="")) return PlcList(value) if data_type == ModbusDataType.LINT and number_of_values == int(1): # LINT @@ -281,7 +278,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcLINT(int(read_buffer.read_long(64, logical_name="")))) + value.append(read_buffer.read_long(64, logical_name="")) return PlcList(value) if ( @@ -332,9 +329,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcUINT(int(read_buffer.read_unsigned_short(8, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_short(8, logical_name="")) return PlcList(value) if data_type == ModbusDataType.UINT and number_of_values == int(1): # UINT @@ -349,9 +344,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcUDINT(int(read_buffer.read_unsigned_int(16, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_int(16, logical_name="")) return PlcList(value) if data_type == ModbusDataType.UDINT and number_of_values == int(1): # UDINT @@ -366,9 +359,7 @@ def static_parse( 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=""))) - ) + value.append(read_buffer.read_unsigned_long(32, logical_name="")) return PlcList(value) if data_type == ModbusDataType.ULINT and number_of_values == int(1): # ULINT @@ -383,9 +374,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcLINT(int(read_buffer.read_unsigned_long(64, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_long(64, logical_name="")) return PlcList(value) if data_type == ModbusDataType.REAL and number_of_values == int(1): # REAL @@ -400,9 +389,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcREAL(float(read_buffer.read_float(32, logical_name=""))) - ) + value.append(read_buffer.read_float(32, logical_name="")) return PlcList(value) if data_type == ModbusDataType.LREAL and number_of_values == int(1): # LREAL @@ -417,15 +404,13 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcLREAL(float(read_buffer.read_double(64, logical_name=""))) - ) + value.append(read_buffer.read_double(64, logical_name="")) return PlcList(value) 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 @@ -435,16 +420,14 @@ def static_parse( value: List[PlcValue] = [] for _ in range(item_count): value.append( - PlcSTRING( - str(read_buffer.read_str(8, logical_name="", encoding="")) - ) + read_buffer.read_str(8, logical_name="", encoding='"UTF-8"') ) return PlcList(value) 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 @@ -454,9 +437,7 @@ def static_parse( value: List[PlcValue] = [] for _ in range(item_count): value.append( - PlcSTRING( - str(read_buffer.read_str(16, logical_name="", encoding="")) - ) + read_buffer.read_str(16, logical_name="", encoding='"UTF-16"') ) return PlcList(value) @@ -691,24 +672,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..8fad9e13a88 100644 --- a/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py +++ b/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py @@ -60,7 +60,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) 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(read_buffer.read_bit("")) return PlcList(value) if data_type == "BYTE" and number_of_values == int(1): # BYTE @@ -75,9 +75,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcUINT(int(read_buffer.read_unsigned_short(8, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_short(8, logical_name="")) return PlcList(value) if data_type == "WORD" and number_of_values == int(1): # WORD @@ -92,9 +90,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcUDINT(int(read_buffer.read_unsigned_int(16, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_int(16, logical_name="")) return PlcList(value) if data_type == "DWORD" and number_of_values == int(1): # DWORD @@ -109,9 +105,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) 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=""))) - ) + value.append(read_buffer.read_unsigned_long(32, logical_name="")) return PlcList(value) if data_type == "LWORD" and number_of_values == int(1): # LWORD @@ -126,9 +120,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcLINT(int(read_buffer.read_unsigned_long(64, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_long(64, logical_name="")) return PlcList(value) if data_type == "SINT" and number_of_values == int(1): # SINT @@ -143,9 +135,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcSINT(int(read_buffer.read_signed_byte(8, logical_name=""))) - ) + value.append(read_buffer.read_signed_byte(8, logical_name="")) return PlcList(value) if data_type == "INT" and number_of_values == int(1): # INT @@ -160,7 +150,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcINT(int(read_buffer.read_short(16, logical_name="")))) + value.append(read_buffer.read_short(16, logical_name="")) return PlcList(value) if data_type == "DINT" and number_of_values == int(1): # DINT @@ -175,7 +165,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcDINT(int(read_buffer.read_int(32, logical_name="")))) + value.append(read_buffer.read_int(32, logical_name="")) return PlcList(value) if data_type == "LINT" and number_of_values == int(1): # LINT @@ -190,7 +180,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcLINT(int(read_buffer.read_long(64, logical_name="")))) + value.append(read_buffer.read_long(64, logical_name="")) return PlcList(value) if data_type == "USINT" and number_of_values == int(1): # USINT @@ -205,9 +195,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcUINT(int(read_buffer.read_unsigned_short(8, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_short(8, logical_name="")) return PlcList(value) if data_type == "UINT" and number_of_values == int(1): # UINT @@ -222,9 +210,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcUDINT(int(read_buffer.read_unsigned_int(16, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_int(16, logical_name="")) return PlcList(value) if data_type == "UDINT" and number_of_values == int(1): # UDINT @@ -239,9 +225,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) 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=""))) - ) + value.append(read_buffer.read_unsigned_long(32, logical_name="")) return PlcList(value) if data_type == "ULINT" and number_of_values == int(1): # ULINT @@ -256,9 +240,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcLINT(int(read_buffer.read_unsigned_long(64, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_long(64, logical_name="")) return PlcList(value) if data_type == "REAL" and number_of_values == int(1): # REAL @@ -273,9 +255,7 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcREAL(float(read_buffer.read_float(32, logical_name=""))) - ) + value.append(read_buffer.read_float(32, logical_name="")) return PlcList(value) if data_type == "LREAL" and number_of_values == int(1): # LREAL @@ -290,15 +270,13 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcLREAL(float(read_buffer.read_double(64, logical_name=""))) - ) + value.append(read_buffer.read_double(64, logical_name="")) return PlcList(value) 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 @@ -308,16 +286,14 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) value: List[PlcValue] = [] for _ in range(item_count): value.append( - PlcSTRING( - str(read_buffer.read_str(8, logical_name="", encoding="")) - ) + read_buffer.read_str(8, logical_name="", encoding='"UTF-8"') ) return PlcList(value) 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 @@ -327,22 +303,20 @@ def static_parse(read_buffer: ReadBuffer, data_type: str, number_of_values: int) value: List[PlcValue] = [] for _ in range(item_count): value.append( - PlcSTRING( - str(read_buffer.read_str(16, logical_name="", encoding="")) - ) + read_buffer.read_str(16, logical_name="", encoding='"UTF-16"') ) return PlcList(value) 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 +497,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/pytest.ini b/plc4py/plc4py/protocols/simulated/readwrite/__init__.py similarity index 97% rename from plc4py/pytest.ini rename to plc4py/plc4py/protocols/simulated/readwrite/__init__.py index 537563cdb10..342be717994 100644 --- a/plc4py/pytest.ini +++ b/plc4py/plc4py/protocols/simulated/readwrite/__init__.py @@ -16,6 +16,3 @@ # specific language governing permissions and limitations # under the License. # - -[pytest] -log_cli = true \ No newline at end of file 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..c6d025054b3 100644 --- a/plc4py/plc4py/protocols/umas/readwrite/DataItem.py +++ b/plc4py/plc4py/protocols/umas/readwrite/DataItem.py @@ -24,19 +24,23 @@ 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 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 from plc4py.spi.values.PlcValues import PlcWORD from plc4py.utils.GenericTypes import ByteOrder from typing import List from typing import cast +import datetime import logging import math @@ -63,28 +67,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(read_buffer.read_byte("")) return PlcList(value) if data_type == UmasDataType.WORD: # WORD @@ -111,7 +123,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcINT(int(read_buffer.read_short(16, logical_name="")))) + value.append(read_buffer.read_short(16, logical_name="")) return PlcList(value) if data_type == UmasDataType.DINT and number_of_values == int(1): # DINT @@ -126,7 +138,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append(PlcDINT(int(read_buffer.read_int(32, logical_name="")))) + value.append(read_buffer.read_int(32, logical_name="")) return PlcList(value) if data_type == UmasDataType.UINT and number_of_values == int(1): # UINT @@ -141,9 +153,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcUDINT(int(read_buffer.read_unsigned_int(16, logical_name=""))) - ) + value.append(read_buffer.read_unsigned_int(16, logical_name="")) return PlcList(value) if data_type == UmasDataType.UDINT and number_of_values == int(1): # UDINT @@ -158,9 +168,7 @@ def static_parse( 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=""))) - ) + value.append(read_buffer.read_unsigned_long(32, logical_name="")) return PlcList(value) if data_type == UmasDataType.REAL and number_of_values == int(1): # REAL @@ -175,9 +183,7 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcREAL(float(read_buffer.read_float(32, logical_name=""))) - ) + value.append(read_buffer.read_float(32, logical_name="")) return PlcList(value) if data_type == UmasDataType.STRING and number_of_values == int(1): # STRING @@ -195,11 +201,99 @@ def static_parse( item_count: int = int(number_of_values) value: List[PlcValue] = [] for _ in range(item_count): - value.append( - PlcREAL(float(read_buffer.read_float(32, logical_name=""))) - ) + value.append(read_buffer.read_float(32, logical_name="")) 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(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(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 +311,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 +326,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 +396,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 +495,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 +544,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..39287abae8d 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 @@ -433,11 +490,13 @@ def read_complex_array(self, logical_name: str = "", **kwargs) -> List[Any]: def read_str(self, bit_length: int = -1, logical_name: str = "", **kwargs) -> str: byte_order = kwargs.get("byte_order", self.byte_order) - result: str = struct.unpack( + encoding = kwargs.get("encoding", "utf-8") + result: bytes = struct.unpack( str(int(bit_length / 8)) + "s", self.bb[self.position : self.position + bit_length], )[0] self.position += bit_length + result = result.decode(encoding=encoding, errors="strict") return result def read_array_field( 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/requirements.txt b/plc4py/requirements.txt deleted file mode 100644 index 3a284c8bc3f..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.12.1 -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..14ee33ce922 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,211 @@ # 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__) +async def simple_read(connection, tag_name, tag_value): + tag_alias = "Some Random Alias" + with connection.read_request_builder() as builder: + builder.add_item(tag_alias, tag_name) + read_request = builder.build() - # Initialize the PlcDriverManager - driver_manager = PlcDriverManager() + future = connection.execute(read_request) + response = await future + assert response.response_code == PlcResponseCode.OK - # 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] + 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_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_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_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_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_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_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_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_boolean_array(connection): + tag_name = "0x00001[2]" + await write_read(connection, tag_name, PlcList([PlcBOOL(False), 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_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_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_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_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_contacts(connection): + tag_name = "1x00001" + await simple_read(connection, tag_name, PlcBOOL(False)) @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_bool_array_discrete_input(connection): + tag_name = "1x00001[2]" + await simple_read(connection, tag_name, PlcList([PlcBOOL(False), PlcBOOL(False)])) @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__) +async def test_plc_driver_modbus_read_input_register(connection): + tag_name = "3x00001" + await simple_read(connection, tag_name, PlcINT(0)) - # 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"] +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_modbus_read_input_register_array(connection): + tag_name = "3x00001[2]" + await simple_read(connection, tag_name, PlcList([PlcINT(0), 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__) +async def test_plc_driver_modbus_read_holding_register(connection): + tag_name = "4x00001" + await write_read(connection, tag_name, PlcINT(334)) - # 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 +@pytest.mark.asyncio +@pytest.mark.xfail +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_array(): - """ - Test reading data from a Modbus PLC. - """ - log = logging.getLogger(__name__) +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)]) + ) - # 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 +@pytest.mark.asyncio +@pytest.mark.xfail +async def test_plc_driver_modbus_read_holding_char(connection): + tag_name = "4x00041:CHAR" + await write_read( + connection, + tag_name, + PlcCHAR("F"), + ) @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__) +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="F"), + PlcCHAR(value="A"), + PlcCHAR(value="F"), + PlcCHAR(value="B"), + PlcCHAR(value="C"), + PlcCHAR(value="B"), + ] + ), + ) - # 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 +@pytest.mark.asyncio +@pytest.mark.xfail +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/pom.xml b/pom.xml index 43c76009d43..00985ce5815 100644 --- a/pom.xml +++ b/pom.xml @@ -107,7 +107,7 @@ 4.2.2 1.4.3 - 1.78.1 + 1.79 1.15.3 3.30.2-b1 1.9.4 @@ -132,7 +132,7 @@ 3.0 4.5.14 4.4.16 - 2.18.0 + 2.18.1 7.0.0 4.0.4 @@ -140,21 +140,21 @@ 11.0.20 5.15.0 2.11.0 - 5.11.2 + 5.11.3 1.8.2 1.5.9 3.2.0 0.6.14 - 5.14.1 + 5.14.2 4.1.114.Final 1.8.2 2.0.16 - 0.10.4 + 0.10.5 2.10.0 2.2.6 - 2.5.13 + 2.5.11 2.3.1 false @@ -275,7 +275,7 @@ com.google.googlejavaformat google-java-format - 1.23.0 + 1.24.0 @@ -1175,7 +1175,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.5.0 + 3.5.1 org.junit.jupiter @@ -1273,7 +1273,7 @@ false en ${project.build.sourceEncoding} - ${project.reporting.outputencoding} + ${project.reporting.outputEncoding} false diff --git a/protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml b/protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml index 3770e9d267e..332722e461e 100644 --- a/protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml +++ b/protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml @@ -1239,6 +1239,18 @@ + + + + receive_timestamp + 0 + + + timestamp_source + ASSUMPTION + + + 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/apache/index.adoc b/src/site/asciidoc/apache/index.adoc index 0f7364ccb11..35e48fafb3e 100644 --- a/src/site/asciidoc/apache/index.adoc +++ b/src/site/asciidoc/apache/index.adoc @@ -17,4 +17,4 @@ :imagesdir: ../images/ :icons: font -== Apache += Apache diff --git a/src/site/asciidoc/developers/architecture.adoc b/src/site/asciidoc/developers/architecture.adoc index 0e95ebf2013..784d26a3910 100644 --- a/src/site/asciidoc/developers/architecture.adoc +++ b/src/site/asciidoc/developers/architecture.adoc @@ -15,9 +15,9 @@ // limitations under the License. // -== Architecture of PLC4X Connections += Architecture of PLC4X Connections -=== Simple case +== Simple case In this simple case, an application asks the DriverManager to forward a connection creation to the corresponding Driver implementation, which then creates both a Connection and a MessageCodec instance. The Connection is the logical link between the connection state and the MessageCodec. A MessageCodec uses a TransportInstance to communicate with the target device. @@ -84,13 +84,13 @@ note top of [Connection] : logical .... -=== Problems +== Problems Serial transports based on RS475 and UDP Transports currently don't allow sharing. That means only one connection instance can have access to one RS485 or one shared local UDP Port (Multiple UDP transport instances with different local ports however are possible). As soon as one connection is established and a second connection would try to access this, this would result in errors. However, multiple devices could be attached to the same RS458 port (Modbus RTU and Modbus ASCII explicitly supports this, however using different devices using different protocols over the same port is not possible) and in BACnet connecting to multiple remote BACnet devices would require one local UDP port to be used by multiple connections. -=== Protocols requiring us to use a fixed port on a non-broadcast address +== Protocols requiring us to use a fixed port on a non-broadcast address Some protocols, such as BACnet require remotes to send data to a fixed udp port on a non-broadcast address. This causes problems as soon as we want to connect to multiple BACnet devices from the same host as only one instance can get access to that port. @@ -157,6 +157,6 @@ note top of [Connection] : logical .... -=== Protocols only allowing one connection at a time +== Protocols only allowing one connection at a time diff --git a/src/site/asciidoc/developers/building.adoc b/src/site/asciidoc/developers/building.adoc index ce5aa78d50e..c2989fce7de 100644 --- a/src/site/asciidoc/developers/building.adoc +++ b/src/site/asciidoc/developers/building.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Building PLC4X += Building PLC4X PLC4X is built with `Apache Maven` and we have tried to make the build as simple as possible. @@ -27,7 +27,7 @@ More about these later down in this manual. For your convenience we also have provided a `Maven-Wrapper`, that should allow building of PLC4X with only `Java 11` or greater as requirement. -=== Requirements +== Requirements The only requirements to building PLC4X should be: @@ -35,7 +35,7 @@ The only requirements to building PLC4X should be: * Git (Even if you are building the source distribution, the Kafka plugin seems to require a `git` executable being available on the systems `PATH`) * Apache Maven (3.6.0 or newer) *(Optional)* (See next chapter) -=== Using the Maven-Wrapper +== Using the Maven-Wrapper The so-called `Maven-Wrapper` is used by calling the Maven-Wrapper scripts `mvnw` (Mac & Linux) or `mvnw.cmd` (Windows) instead of the default Maven commands `mvn` and `mvn.cmd`. @@ -45,7 +45,7 @@ If no suitable version can be found, it is automatically downloaded and installe After the script has ensured a suitable Maven version is available, this is used and all arguments and parameters are transparently forwarded to this. So simply adding the additional `w` to each of the Maven commands, there should be no difference to using a pre-installed Maven version. -=== Using Maven +== Using Maven This document can't provide you with all the details needed to get started with `Maven` itself. But there is a lot of good documentation out there. @@ -57,7 +57,7 @@ It should handle all the details needed to get a general understanding of Maven .Recording of a Maven Training for Apache Flex from 2016 https://vimeo.com/167857327 -=== Building PLC4X with Maven +== Building PLC4X with Maven As especially building the C++, and C# drivers requires building of some third party artifacts and increases build-time dramatically and requires setting up some additional third party tools, we have excluded these parts form the default Maven build. @@ -95,7 +95,7 @@ If you want to skip the running of tests (even if this is not encouraged) you ca This will not skip the compilation of tests, however. -=== Building the PLC4X Website with Maven +== Building the PLC4X Website with Maven The PLC4X Website is also part of the same GIT repository that contains the code and it is built by Maven as well. @@ -105,13 +105,13 @@ In order to build the website the following command should be sufficient: This is just a quick-start version of the site generation, for a fully detailed documentation please read the https://plc4x.apache.org/developers/infrastructure/website.html[Website] documentation page. -=== Some special Maven profiles +== Some special Maven profiles Maven supports so-called `profiles` for customizing the build in special cases. We have tried to keep the number of profiles as low as possible. So far there is only one profile. -==== `apache-release` profile +=== `apache-release` profile This profile is automatically enabled on a release-build and it automatically creates some additional artifacts: @@ -123,7 +123,7 @@ This profile is automatically enabled on a release-build and it automatically cr Generally it is not required to enable ths profile unless you are interested in these Artifacts. -==== `debug-pom` profile +=== `debug-pom` profile Especially for Maven beginners, it might be difficult to understand why a module builds the way it does. Maven contains a lot of concepts to inherit and override settings. @@ -141,7 +141,7 @@ Some tests of the PLC4X project do require quite a bit of time to run. Therefore we decided to disable these for a normal build on developer machines. If you want to run them locally and not rely on them being run on the CI servers, enable the `enable-all-checks` profile. -=== Use the compiled library with Gradle +== Use the compiled library with Gradle Compiling the library as explained here add the new version in the local Maven repository (i.e. usually under `~/.m2/repository` on linux like systems), if you would like to use Gradle as Build Tool for your project you have just to use a local repository in your Gradle `build.gradle` file. diff --git a/src/site/asciidoc/developers/code-gen/index.adoc b/src/site/asciidoc/developers/code-gen/index.adoc index 83019d265f4..5c0f3b96e48 100644 --- a/src/site/asciidoc/developers/code-gen/index.adoc +++ b/src/site/asciidoc/developers/code-gen/index.adoc @@ -16,7 +16,7 @@ // :imagesdir: ../../images/ -== Code Generation += Code Generation As hand-writing code for a lot of drivers in multiple languages would be quite a nightmare, we have invested a very large amount of time into finding a way to automate this. @@ -120,7 +120,7 @@ So for example in case of generating a `Siemens S7` Driver for `Java` this would The dark blue parts are the ones released externally, the turquoise ones are part of the main PLC4X repo. -=== Introduction +== Introduction The maven plugin is built up very modular. @@ -141,7 +141,7 @@ The downside was, that the PLC4X community regarded this XML format as pretty co In the end we came up with our own format which we called `MSpec` and is described in the link:protocol/mspec.html[MSpec Format description]. -=== Configuration +== Configuration The `plc4x-maven-plugin` has a very limited set of configuration options. @@ -295,11 +295,11 @@ and: The reason for why the dependencies are added as code-dependencies and why the scope is set the way it is, is described in the <> section. -=== Custom Modules +== Custom Modules The plugin uses the https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html[Java Serviceloader] mechanism to find modules. -==== Protocol Modules +=== Protocol Modules In order to provide a new protocol module, all that is required, it so create a module containing a `META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol` file referencing an implementation of the `org.apache.plc4x.plugins.codegenerator.protocol.Protocol` interface. @@ -346,7 +346,7 @@ As mentioned before, we support multiple versions of a protocol, so if `getVersi The most important method for the actual code-generation however is the `getTypeContext()` method, which returns a `TypeContext` type which generally contains a list of all parsed types for this given protocol. -==== Language Modules +=== Language Modules Analog to the <> the Language modules are constructed very similar. @@ -394,9 +394,9 @@ The `name` being used by the plugin to find the language output module defined b `supportedOptions` provides a list of `options` that the current language module is able to use and which can be passed in to the maven configuration using the `options` settings. -=== Problems with Maven +== Problems with Maven -==== Why are the 4 modules released separately? +=== Why are the 4 modules released separately? We mentioned in the introduction, that the first 4 modules are maintained and released from outside the main PLC4X repository. @@ -417,7 +417,7 @@ For this reason we have stripped down the plugin and its dependencies to an abso As soon as the tooling is released, the version is updated in the PLC4X build and the release version is used without any complications. -==== Why are the protocol and language dependencies done so strangely? +=== Why are the protocol and language dependencies done so strangely? It would certainly be a lot cleaner, if we provided the dependencies to protocol and language modules as plugin dependencies. diff --git a/src/site/asciidoc/developers/code-gen/language/freemarker.adoc b/src/site/asciidoc/developers/code-gen/language/freemarker.adoc index 5c53337b8f5..5851fcff0b0 100644 --- a/src/site/asciidoc/developers/code-gen/language/freemarker.adoc +++ b/src/site/asciidoc/developers/code-gen/language/freemarker.adoc @@ -16,7 +16,7 @@ // :imagesdir: ../../../images/ -== Apache Freemarker += Apache Freemarker For the Freemarker language output we are using an unmodified version of https://freemarker.apache.org[Apache Freemarker] to generate output. @@ -48,7 +48,7 @@ It will automatically create all needed intermediate directories and generate th If this line is empty, the output is skipped for this type. -=== Example `Java` output +== Example `Java` output .... package org.apache.plc4x.language.java; diff --git a/src/site/asciidoc/developers/code-gen/protocol/df1.adoc b/src/site/asciidoc/developers/code-gen/protocol/df1.adoc index ca52ae1738a..aba67938037 100644 --- a/src/site/asciidoc/developers/code-gen/protocol/df1.adoc +++ b/src/site/asciidoc/developers/code-gen/protocol/df1.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Example: DF1 MSpec += Example: DF1 MSpec The DF1 protocol has three basic messages: a command message, acknowledge and not acknowledge. A `0x10` is used as delimiter to differentiate between the messages and parts of the command message. diff --git a/src/site/asciidoc/developers/code-gen/protocol/mspec.adoc b/src/site/asciidoc/developers/code-gen/protocol/mspec.adoc index 8d19b56480e..c8b20a35e31 100644 --- a/src/site/asciidoc/developers/code-gen/protocol/mspec.adoc +++ b/src/site/asciidoc/developers/code-gen/protocol/mspec.adoc @@ -16,7 +16,7 @@ // :imagesdir: ../../../images/ -== The MSpec format += The MSpec format The `MSpec` format (Message Specification) was a result of a brainstorming session after evaluating a lot of other options. @@ -109,7 +109,7 @@ In general, we distinguish between two types of types used in field definitions: - simple types - complex types -=== Simple Types +== Simple Types Simple types are usually raw data the format is: @@ -138,7 +138,7 @@ There is currently one special type, reserved for string values, whose length is - *vstring*: The input is treated as a variable length string and requires an expression tp provide the number of bits to read. -=== Complex Types +== Complex Types In contrast to simple types, complex types reference other complex types (Root elements of the spec document). @@ -146,9 +146,9 @@ How the parser should interpret them is defined in the referenced types definiti In the example above, for example the `S7Parameter` is defined in another part of the spec. -=== Field Types and their Syntax +== Field Types and their Syntax -==== array Field +=== array Field An `array` field is exactly what you expect. It generates an field which is not a single-value element but an array or list of elements. @@ -166,7 +166,7 @@ Possible values are: - `length`: In this case a given number of bytes are being read. So if an element has been parsed and there are still bytes left, another element is parsed. - `terminated`: In this case the parser will continue reading elements until it encounters a termination sequence. -==== assert Field +=== assert Field An assert field is pretty much identical to a `const` field. The main difference however it how the case is handled, if the parsed value does not match the expected value. @@ -185,7 +185,7 @@ See also: - validation field: Similar to an `assert` field, however no parsing is done, and instead simply a condition is checked. - optional field: `optional` fields are aware of the types of parser errors produced by `assert` and `validation` fields -==== checksum Field +=== checksum Field A checksum field can only operate on simple types. @@ -205,7 +205,7 @@ This field doesn't keep any data in memory. See also: - implicit field: A checksum field is similar to an implicit field, however the `checksum-expression` is evaluated are parsing time and throws an exception if the values don't match. -==== const Field +=== const Field A const field simply reads a given simple type and compares to a given reference value. @@ -222,7 +222,7 @@ This field doesn't keep any data in memory. See also: - implicit field: A const field is similar to an implicit field, however it compares the parsed input to the reference value and throws an exception if the values don't match. -==== discriminator Field +=== discriminator Field Discriminator fields are only used in `discriminatedType`s. @@ -239,7 +239,7 @@ See also: - implicit field: A discriminator field is similar to an implicit field, however doesn't provide a serialization expression as it uses the discrimination constants of the type it is. - discriminated types -==== implicit Field +=== implicit Field Implicit types are fields that get their value implicitly from the data they contain. @@ -255,7 +255,7 @@ This type of field is generally used for fields that handle numbers of elements This field doesn't keep any data in memory. -==== manualArray Field +=== manualArray Field [manualArray {bit|byte} {name} {count|length|terminated} '{loop-expression}' '{serialization-expression}' '{deserialization-expression}' '{length-expression}'] @@ -263,7 +263,7 @@ This field doesn't keep any data in memory. [manualArray {complex-type} {name} {count|length|terminated} '{loop-expression}' '{serialization-expression}' '{deserialization-expression}' '{length-expression}'] -==== manual Field +=== manual Field [manual {bit|byte} {name} '{serialization-expression}' '{deserialization-expression}' '{length-expression}'] @@ -271,7 +271,7 @@ This field doesn't keep any data in memory. [manual {complex-type} {name} '{serialization-expression}' '{deserialization-expression}' '{length-expression}'] -==== optional Field +=== optional Field An optional field is a type of field that can also be `null`. @@ -293,7 +293,7 @@ See also: - `assert`: Assert fields are similar to `const` fields, but can abort parsing of an `optional` filed. - `validation`: If a validation field in any of the subtypes fails, this aborts parsing of the `optional` field. -==== padding Field +=== padding Field A padding field allows aligning of data blocks. It outputs additional padding data, given amount of times specified by padding expression. @@ -309,11 +309,11 @@ When serializing, the `times-padding` defines how often the `padding-value` shou This field doesn't keep any data in memory. -===== peek Field +==== peek Field // TODO: Implement -==== reserved Field +=== reserved Field Reserved fields are very similar to `const` fields, however they don't throw exceptions, but instead log messages if the values don't match. @@ -334,7 +334,7 @@ This field doesn't keep any data in memory. See also: - `const` field -==== simple Field +=== simple Field Simple fields are the most common types of fields. @@ -350,7 +350,7 @@ When parsing, the given type is parsed (can't be `null`) and saved in the corres When serializing it is serialized normally using either a simple type serializer or by delegating serialization to a complex type. -==== typeSwitch Field +=== typeSwitch Field // TODO: Finish this ... @@ -393,7 +393,7 @@ These arguments are then available for expressions or passing on in the subtypes See also: - `discriminatedType` -===== unknown Field +==== unknown Field // TODO: Finish this ... @@ -402,7 +402,7 @@ It allows parsing any type of information, storing and using it and serializing In general, it's something similar to a `simple` field, just explicitly states, that we don't yet quite know how to handle the content. -===== validation Field +==== validation Field As mentioned before, a `validation` field is not really a field, it's a check that is added to the type parser. @@ -414,7 +414,7 @@ If it finds one, it rewinds the parser to the position just before starting to p If there is no `optional` field up the stack, then parsing fails. -==== virtual Field +=== virtual Field Virtual fields have no impact on the input or output. They simply result in creating artificial get-methods in the generated model classes. @@ -427,7 +427,7 @@ They simply result in creating artificial get-methods in the generated model cla Instead of being bound to a property, the return value of a `virtual` property is created by evaluating the `value-expression`. -==== Parameters +=== Parameters Sometimes it is necessary to pass along additional parameters. @@ -459,13 +459,13 @@ Here comes an example of this in above snippet: [field S7Payload 'payload' ['messageType', 'parameter']] -==== Serializer and Parser-Arguments +=== Serializer and Parser-Arguments Arguments influence the way the parser or serializer operates. Wherever an parser-argument is used, this should also be valid in all subtypes the parser processes. -===== byteOrder +==== byteOrder A `byteOrder` argument can set or change the byte-order used by the parser. @@ -474,7 +474,7 @@ We currently support two variants: - BIG_ENDIAN - LITTLE_ENDIAN -===== encoding +==== encoding Each simple type has a default encoding, which is ok for a very high percentage of cases. diff --git a/src/site/asciidoc/developers/conferences.adoc b/src/site/asciidoc/developers/conferences.adoc index cde6fe09367..4be49a499bf 100644 --- a/src/site/asciidoc/developers/conferences.adoc +++ b/src/site/asciidoc/developers/conferences.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Conferences & Events += Conferences & Events On this page we are listing options to attend talks and events around PLC4X. diff --git a/src/site/asciidoc/developers/contributing.adoc b/src/site/asciidoc/developers/contributing.adoc index 09fc6a60cf2..368f9517722 100644 --- a/src/site/asciidoc/developers/contributing.adoc +++ b/src/site/asciidoc/developers/contributing.adoc @@ -16,9 +16,9 @@ // :imagesdir: ../images/ -== Contributing += Contributing -=== Forms of contribution +== Forms of contribution There are multiple forms in which you can become involved with the PLC4X project. @@ -32,13 +32,13 @@ These usually are, but are not limited to: We are a very friendly bunch and don't be afraid to step forward. -=== Commits +== Commits We make use of https://www.conventionalcommits.org/en/v1.0.0/[conventional commits]. As plc4x is a monolithic polyglot repository we usually define the scope as `...(plc4[language shortcut here]/subcomponent)` (e.g. a new feature in Bacnet in the Golang part would have a message of `feat(plc4go/bacnet): cool new feature for...`). -=== Pull-Requests +== Pull-Requests The simplest way to submit code changes, is via a GitHub pull-request. @@ -67,7 +67,7 @@ image::contributing-github-create-pull-request.png[] If you click on this, we will receive a notification on your changes and can review them. We also can discuss your changes and have you perfect your pull request before we accept and merge it into PLC4X. -==== Keeping your fork up to date +=== Keeping your fork up to date As we are continuously working on PLC4X and you created a copy of our repo, this will become out-of-date pretty soon. @@ -106,7 +106,7 @@ You can do this by executing the following command: (If no remote is provided, git will use `origin` per default) -=== Bug Reports +=== Bug Reports We use https://issues.apache.org/jira/projects/PLC4X[JIRA] as our Bug & Issue Tracker. @@ -120,14 +120,14 @@ So if you are considering to contribute more than just a one-time-patch, please If you want to be assigned to an issue because you want to work on it, please request to be added to the JIRA groups on our http://plc4x.apache.org/mailing-lists.html[developers mailing list] -=== Documentation +== Documentation As our documentation and website are generated as a side-product of our build, contributing to this technically the same as contributing to the code. All our content is written in Asciidoctor and is located in `src/site/asciidoc` directories. For a reference of the Asciidoctor syntax please have a look at the https://asciidoctor.org/docs/user-manual/#introduction-to-asciidoctor[Asciidoctor documentation]. -=== Branching model +== Branching model The PLC4X project uses the following branching model. diff --git a/src/site/asciidoc/developers/decisions.adoc b/src/site/asciidoc/developers/decisions.adoc index 61d181e9282..f4d43e2e7d0 100644 --- a/src/site/asciidoc/developers/decisions.adoc +++ b/src/site/asciidoc/developers/decisions.adoc @@ -16,9 +16,9 @@ // :imagesdir: ../images/ -== Decision Making += Decision Making -=== Introduction +== Introduction This document describes the roles and responsibilities of the project, who may vote, how voting works, how conflicts are resolved, etc. @@ -26,19 +26,19 @@ The https://www.apache.org/foundation/faq[Apache Foundation FAQ] and http://www. Apache has a http://www.apache.org/foundation/policies/conduct.html[code of conduct] that it expects its members to follow. -=== Roles and Responsibilities +== Roles and Responsibilities Apache projects define a set of https://www.apache.org/foundation/how-it-works.html#roles[roles] with associated rights and responsibilities. -==== Project Management Committee +=== Project Management Committee The http://www.apache.org/dev/pmc.html#what-is-a-pmc[PMC] has many https://www.apache.org/foundation/how-it-works.html#pmc[responsibilities] including complying with http://www.apache.org/dev/pmc.html#policy[ASF policies], https://www.apache.org/foundation/board/reporting[reporting to the board], https://www.apache.org/foundation/voting.html[approving releases] and adding new http://www.apache.org/dev/pmc.html#newcommitter[committers] and http://www.apache.org/dev/pmc.html#newpmc[PMC members]. -==== The Chair +=== The Chair The http://www.apache.org/dev/pmc.html#chair[chair] ensures board reports are submitted and that the project's roster is up to date. -=== Decision Making +== Decision Making Different decisions require different forms of approval but community consensus is always the goal. Voting when needed should be open for http://www.apache.org/legal/release-policy.html#release-approval[at least 72 hours]. diff --git a/src/site/asciidoc/developers/index.adoc b/src/site/asciidoc/developers/index.adoc index 33aea916a25..43540280f4f 100644 --- a/src/site/asciidoc/developers/index.adoc +++ b/src/site/asciidoc/developers/index.adoc @@ -17,11 +17,11 @@ :imagesdir: ../images/ :icons: font -== Developer Section += Developer Section This part of the Apache PLC4X dedicated to provide information to people wanting to build PLC4X and hopefully also start contributing to this awesome project. -=== Getting Started +== Getting Started We have tried to make the PLC4X build experience as smooth as possible and have tried to reduce the number of required third party tools to an absolute minimum. @@ -35,17 +35,17 @@ As part of the build we have an initial build step that will do a `prerequisite For details please have a look at the link:preparing/index.html[Preparing your Computer] page. -=== Building PLC4X +== Building PLC4X We have a dedicated page on link:building.html[Building PLC4X]. Please read this page on information about how to build Apache PLC4X. -=== Contributing +== Contributing If you want to work on Apache PLC4X in order to `fix things`, `add things` and start `contributing` in general, please have a look at our link:contributing.html[Contributing] page. It should contain all the information you need. -=== Getting Help +== Getting Help The primary source for getting help definitely is our project mailing list dev@plc4x.apache.org. diff --git a/src/site/asciidoc/developers/infrastructure/ci.adoc b/src/site/asciidoc/developers/infrastructure/ci.adoc index fcb0558cf64..0c72f6c87bf 100644 --- a/src/site/asciidoc/developers/infrastructure/ci.adoc +++ b/src/site/asciidoc/developers/infrastructure/ci.adoc @@ -16,7 +16,7 @@ // :imagesdir: ../images/ -== Continuous Integration += Continuous Integration We are currently using the following CI systems. @@ -30,7 +30,7 @@ GitHub Actions on the other side responsible for doing the main part of the test It not only builds and runs the tests on a matrix of operating systems as well as with a number of java versions. It also is configured to run the tests on pull-requests. -=== Structure of the Jenkins Pipeline build +== Structure of the Jenkins Pipeline build We are using the Jenkins `multi-branch pipeline plugin` to automatically setup build for branches based upon the build definition in the `Jenkinsfile` in the root of the project. diff --git a/src/site/asciidoc/developers/infrastructure/index.adoc b/src/site/asciidoc/developers/infrastructure/index.adoc index 7f48619a72b..fd9b4a41698 100644 --- a/src/site/asciidoc/developers/infrastructure/index.adoc +++ b/src/site/asciidoc/developers/infrastructure/index.adoc @@ -17,6 +17,6 @@ :imagesdir: ../../images/ :icons: font -== Infrastructure += Infrastructure In this section you can find information on the services the Apache PLC4X project makes use of. diff --git a/src/site/asciidoc/developers/infrastructure/issues.adoc b/src/site/asciidoc/developers/infrastructure/issues.adoc index 13b44a1f377..1000e363e31 100644 --- a/src/site/asciidoc/developers/infrastructure/issues.adoc +++ b/src/site/asciidoc/developers/infrastructure/issues.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Bug & Issue Tracker += Bug & Issue Tracker Our bug & issue tracker is Github-Issues. diff --git a/src/site/asciidoc/developers/infrastructure/sonar.adoc b/src/site/asciidoc/developers/infrastructure/sonar.adoc index 663d9199e6d..eae01fc9ca6 100644 --- a/src/site/asciidoc/developers/infrastructure/sonar.adoc +++ b/src/site/asciidoc/developers/infrastructure/sonar.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Code Analysis += Code Analysis We are using `SonarCloud` as the service for static code analysis. diff --git a/src/site/asciidoc/developers/infrastructure/vm.adoc b/src/site/asciidoc/developers/infrastructure/vm.adoc index a42a8d8b705..4bce1f2358d 100644 --- a/src/site/asciidoc/developers/infrastructure/vm.adoc +++ b/src/site/asciidoc/developers/infrastructure/vm.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== The PLC4X Project VM += The PLC4X Project VM As, especially for the raw socket functionality of PLC4X, our project had higher demands on the infrastructure as other projects. Apache Infra kindly provided us with a dedicated VM. @@ -28,7 +28,7 @@ On this machine we can even `sudo` to perform operations only available to `root Project members can request access to the machine. -=== Requesting access +== Requesting access In order to be able to log in, users need to add their SSH public key to their Apache ID first. @@ -50,7 +50,7 @@ Here make sure to select the Project `Infrastructure (INFRA)`. As soon as that's handled by the Infra team, you should be ready to log-in on the machine. -=== Login to the machine +== Login to the machine Using SSH we should now be able to log in to the VM. @@ -60,7 +60,7 @@ Be sure to use the username matching your Apache ID or the login will fail. If all went well you should now be able to log in to the machine using your apache user. -=== Doing things as `root` +== Doing things as `root` Apache Infra is great at keeping things safe. Providing a sudo password directly would increase the danger of having this intercepted, therefore they are using a tool called `opiepasswd`. @@ -94,7 +94,7 @@ https://selfserve.apache.org/otp-calculator.html TIP: More help can be found for Apache committers at https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=122916896 -=== Required software +== Required software For being able to build the charts and graphics as part of the site generation, we need to add some additional packages: diff --git a/src/site/asciidoc/developers/infrastructure/website.adoc b/src/site/asciidoc/developers/infrastructure/website.adoc index 5a390688d91..d53c7b3c89a 100644 --- a/src/site/asciidoc/developers/infrastructure/website.adoc +++ b/src/site/asciidoc/developers/infrastructure/website.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Generating the Website += Generating the Website We are currently using the normal `Maven` build to not only generate the project artifacts, but also the projects website. @@ -36,7 +36,7 @@ Beyond the basic goodies, the build is also configured to generate images from A This allows us to generate images like the ones on the http://plc4x.apache.org/protocols/s7/index.html[S7 Protocol Description page] -=== Providing new content +== Providing new content Within the `src/site` directory there is a file `site.xml` which generally controls the menu and the look of the site. @@ -51,7 +51,7 @@ So if we wanted to add a new page on some (hopefully non existent) `Wombat PLC P For example with this content: ``` -== Wombat PLC Protocol += Wombat PLC Protocol If you want to waste your money, brains and time, feel free to use a `Wombat PLC`. @@ -75,7 +75,7 @@ After the build, you would find a file `target/site/protocols/wombat/index.html` However you can link to this page from any other page, but it is not added ot the navigation menu. -=== Adding links to menus +== Adding links to menus In order to add links to the menus, you have to create or modify the `site.xml` for the module you want to add content to. @@ -158,7 +158,7 @@ If you want to insert the menu somewhere else, you will have to re-define the en The `menu ref` items hereby reference standard menus provided by the `Maven` build. -=== Deploying the Website +== Deploying the Website The PLC4X project uses Apache `gitpubsub` system for maintaining the website. diff --git a/src/site/asciidoc/developers/infrastructure/wiki.adoc b/src/site/asciidoc/developers/infrastructure/wiki.adoc index 7c72a1c894c..6afbb897e11 100644 --- a/src/site/asciidoc/developers/infrastructure/wiki.adoc +++ b/src/site/asciidoc/developers/infrastructure/wiki.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== WIKI += WIKI We use Apache's Confluence instance as Wiki, however most information is generally managed on this website. diff --git a/src/site/asciidoc/developers/jqassistant.adoc b/src/site/asciidoc/developers/jqassistant.adoc index edd69283fa7..dd924860ba7 100644 --- a/src/site/asciidoc/developers/jqassistant.adoc +++ b/src/site/asciidoc/developers/jqassistant.adoc @@ -16,7 +16,7 @@ // :imagesdir: ../images/ -== Using JQAssistant += Using JQAssistant In PLC4X we are using a tool called `https://jqassistant.org/[JQAssistant]` for enforcing quality assurance rules. @@ -58,11 +58,11 @@ These rules that are automatically checked during the build are defined in `src/ If defined in a reactor project, the rules apply to all modules of that (sub-)reactor. So if they are defined in the root module of the project, it applies to all modules. -=== Defining JQAssistant rules +== Defining JQAssistant rules TODO: Finish this "little detail" ... -=== Using the Web-UI +== Using the Web-UI The scan and analysis is automatically performed during a normal Maven build. In order to do ad-hoc queries against the project or develop new rules, the Web-UI is very helpful. @@ -93,7 +93,7 @@ So when visiting the anounced url with a browser, you can use the pretty useful image::neo4j-web-console.png[Neo4j Web Console] -=== Using IntelliJ Idea +== Using IntelliJ Idea IntelliJ comes with some interesting Neo4J support. Unfortunately this only supports `Neo4j 3`. diff --git a/src/site/asciidoc/developers/maturity.adoc b/src/site/asciidoc/developers/maturity.adoc index dbefed507ae..3cf0bc87508 100644 --- a/src/site/asciidoc/developers/maturity.adoc +++ b/src/site/asciidoc/developers/maturity.adoc @@ -17,15 +17,15 @@ :icons: font -== Apache Maturity Model Assessment for PLC4X += Apache Maturity Model Assessment for PLC4X -=== Overview +== Overview This is an assessment of the PLC4X project's maturity, meant to help inform the decision (of the mentors, community, Incubator PMC and ASF Board of Directors) to graduate it as a top-level Apache project. It is based on the ASF project maturity model at https://community.apache.org/apache-way/apache-project-maturity-model.html -=== Maturity model assessment +== Maturity model assessment Community members are encouraged to contribute to this page and comment on it, the following table summarizes project’s self-assessment against the Apache Maturity Model. diff --git a/src/site/asciidoc/developers/preparing/index.adoc b/src/site/asciidoc/developers/preparing/index.adoc index 460f2affa7f..98d8475b513 100644 --- a/src/site/asciidoc/developers/preparing/index.adoc +++ b/src/site/asciidoc/developers/preparing/index.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Preparing your Computer += Preparing your Computer Building a project like Apache PLC4X on multiple target operating-systems is quite a challenge, but I think we managed to make it as simple as possible. diff --git a/src/site/asciidoc/developers/preparing/linux.adoc b/src/site/asciidoc/developers/preparing/linux.adoc index 84925717534..fb08eb59a4a 100644 --- a/src/site/asciidoc/developers/preparing/linux.adoc +++ b/src/site/asciidoc/developers/preparing/linux.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Linux += Linux As tracking down issues which result from missing or outdated third party tools such as compilers are always hard do diagnose, we have extended the build of PLC4X with a `prerequisiteCheck` that automatically checks if required tools are installed and if a required minimum version is available. @@ -26,7 +26,7 @@ So for example the availability and version of the C compiler is only checked if If the check is reporting any issues, please feel free to follow the corresponding steps in this guide to install the tools. -=== Git +== Git Checking: @@ -42,7 +42,7 @@ Yum based systems: sudo yum install git -=== Java +== Java Checking: @@ -54,11 +54,11 @@ Apt based systems: sudo apt install openjdk-21-jdk -=== Optional and other language support +== Optional and other language support Git an Java should be all you need for building the Java part of PLC4X. -==== LibPCAP (For raw-ethernet support) +=== LibPCAP (For raw-ethernet support) Apt-based systems: @@ -81,7 +81,7 @@ Sometimes I had to set the uid to execute the java executable with the permissio sudo chmod 4755 /path/to/java -==== gcc (For PLC4C) +=== gcc (For PLC4C) NOTE: It seems that when running Linux on `aarch64` (Apple's Silicon Chips), that there are issues ... we're working on that. Feel free to watch https://github.com/apache/plc4x/issues/1582 on updates to this. @@ -99,7 +99,7 @@ Yum based systems: sudo yum install gcc -==== dotnet (For PLC4Net) +=== dotnet (For PLC4Net) Checking: @@ -125,7 +125,7 @@ If, when checking the version again after installing, you are getting an error: Then please have a look at this Stackoverflow post (the accepted solution) https://stackoverflow.com/questions/73753672/a-fatal-error-occurred-the-folder-usr-share-dotnet-host-fxr-does-not-exist -==== python (For PLC4Py) +=== python (For PLC4Py) Checking: @@ -141,7 +141,7 @@ Yum based systems: yum intall python3 -==== Python venv (For PLC4Py) +=== Python venv (For PLC4Py) Checking: diff --git a/src/site/asciidoc/developers/preparing/macos.adoc b/src/site/asciidoc/developers/preparing/macos.adoc index d61bc261981..289030dced6 100644 --- a/src/site/asciidoc/developers/preparing/macos.adoc +++ b/src/site/asciidoc/developers/preparing/macos.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Mac OS += Mac OS As tracking down issues which result from missing or outdated third party tools such as compilers are always hard do diagnose, we have extended the build of PLC4X with a `prerequisiteCheck` that automatically checks if required tools are installed and if a required minimum version is available. @@ -30,7 +30,7 @@ Make sure `Homebrew` ist installed in order to install most of these. /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -=== Git +== Git Checking: @@ -45,7 +45,7 @@ If you get a response that indicates that git needs to be installed, please exec brew install git -=== Java +== Java Checking: @@ -57,11 +57,11 @@ Apt based systems: brew install openjdk -=== Optional and other language support +== Optional and other language support Git an Java should be all you need for building the Java part of PLC4X. -==== LibPCAP (For raw-ethernet support) +=== LibPCAP (For raw-ethernet support) The libpcap version bundled with macOS is currently 1.9.1. This version causes exceptions. @@ -69,7 +69,7 @@ So it's best to update to a newer version using brew: brew install libpcap -==== gcc (For PLC4C) +=== gcc (For PLC4C) Checking: @@ -79,7 +79,7 @@ If you get any successful output, you probably don't need to do anything. It seems macOS comes with a version of gcc which is good enough for our use cases. -==== dotnet (For PLC4Net) +=== dotnet (For PLC4Net) Checking: @@ -93,7 +93,7 @@ Alternatively you can also install it via homebrew: brew install --cask dotnet-sdk -==== python (For PLC4Py) +=== python (For PLC4Py) Checking: diff --git a/src/site/asciidoc/developers/preparing/windows.adoc b/src/site/asciidoc/developers/preparing/windows.adoc index f602b1cc589..8b1b1164abf 100644 --- a/src/site/asciidoc/developers/preparing/windows.adoc +++ b/src/site/asciidoc/developers/preparing/windows.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Windows += Windows As tracking down issues which result from missing or outdated third party tools such as compilers are always hard do diagnose, we have extended the build of PLC4X with a `prerequisiteCheck` that automatically checks if required tools are installed and if a required minimum version is available. @@ -28,7 +28,7 @@ As in the past keeping the documentation up to date has been quite challenging, So first be sure to install Chocolatey from here: https://community.chocolatey.org/ and follow the most recent instructions on that page in order to install it. -=== Git +== Git Checking: @@ -40,7 +40,7 @@ If above command is not successful, simply install it via Chocolatey: choco install git -=== Java +== Java Checking: @@ -52,11 +52,11 @@ If you need to install or update Java, unfortunately this doesn't seem to be ava For Oracles OpenJDK 21 this would be from here: https://learn.microsoft.com/de-de/java/openjdk/download If you are using a Windows VM on `aarch64` (Apple M1 or M2 virtual machine), the download available from Microsoft build seem to be one of the few options you have. When installing make sure to select the option to configure the "JAVA_HOME" environment variable (deactivated per default). -=== Optional and other language support +== Optional and other language support Git an Java should be all you need for building the Java part of PLC4X. -==== LibPCAP (For raw-ethernet support) +=== LibPCAP (For raw-ethernet support) In order to use the raw ethernet transport capabilities of PLC4X, we need to ensure the NPcap library is installed. @@ -74,7 +74,7 @@ In order to check if NPcap is installed, check the following directories: If none of these can be found, install it by downloading the installer from https://npcap.com/#download -==== gcc (For PLC4C) +=== gcc (For PLC4C) Checking: @@ -92,7 +92,7 @@ https://repo.maven.apache.org/maven2/com/googlecode/cmake-maven-project/cmake-bi Deploy as: https://repo.maven.apache.org/maven2/com/googlecode/cmake-maven-project/cmake-binaries/3.27.7-b1/cmake-binaries-3.27.7-b1-windows-arm64.jar -==== dotnet (For PLC4Net) +=== dotnet (For PLC4Net) Checking: @@ -104,7 +104,7 @@ Usually this is already installed on Windows machines. Download the installer from https://dotnet.microsoft.com/en-us/download[here] -==== python (For PLC4Py) +=== python (For PLC4Py) Checking: diff --git a/src/site/asciidoc/developers/protocols/ads/protocol.adoc b/src/site/asciidoc/developers/protocols/ads/protocol.adoc index d71aa790360..79d9e73c1ee 100644 --- a/src/site/asciidoc/developers/protocols/ads/protocol.adoc +++ b/src/site/asciidoc/developers/protocols/ads/protocol.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Beckhoff ADS Protocol += Beckhoff ADS Protocol // https://plantuml.com/de/activity-diagram-legacy // https://deepu.js.org/svg-seq-diagram/Reference_Guide.pdf diff --git a/src/site/asciidoc/developers/protocols/eip/protocol.adoc b/src/site/asciidoc/developers/protocols/eip/protocol.adoc index 1cf6bbbc4c3..ddf2120ae31 100644 --- a/src/site/asciidoc/developers/protocols/eip/protocol.adoc +++ b/src/site/asciidoc/developers/protocols/eip/protocol.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== EIP Protocol += EIP Protocol // https://plantuml.com/de/activity-diagram-legacy // https://deepu.js.org/svg-seq-diagram/Reference_Guide.pdf diff --git a/src/site/asciidoc/developers/protocols/index.adoc b/src/site/asciidoc/developers/protocols/index.adoc index c3aa351f8bb..c0b0b010b01 100644 --- a/src/site/asciidoc/developers/protocols/index.adoc +++ b/src/site/asciidoc/developers/protocols/index.adoc @@ -15,9 +15,9 @@ // limitations under the License. // -== Usage of protocols += Usage of protocols -=== Currently documented are: +== Currently documented are: - link:ads/protocol.html[Beckhoff/ ADS] - link:eip/protocol.html[EIP] diff --git a/src/site/asciidoc/developers/release/build-tools.adoc b/src/site/asciidoc/developers/release/build-tools.adoc index 4375c95ca2c..f865ee51f64 100644 --- a/src/site/asciidoc/developers/release/build-tools.adoc +++ b/src/site/asciidoc/developers/release/build-tools.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Releasing PLC4X Build-Tools += Releasing PLC4X Build-Tools In contrast to the main project, the `plc4x-build-tools` repository contains a loose collection of sub-projects. @@ -30,7 +30,7 @@ So please check link:release.html[here] (Chapters: `Preparing your system for be The rest of the steps are a lot simpler than those of the main project as there aren't any profiles involved. -=== Creating a release branch (For the code-generation module) +== Creating a release branch (For the code-generation module) According to SemVer, we have: Major, Minor and Bugfix releases. @@ -68,7 +68,7 @@ This step now should perform quite quickly as no build and no tests are involved However in the end the versions of the `develop` branch are updated and a new `releases/code-generation/{code-generation-short-version}` branch is created. -=== Preparing `develop` for the next iteration +== Preparing `develop` for the next iteration Now is a good time to add a new section to the `RELEASE_NOTES` document for the new `SNAPSHOT` version. @@ -99,7 +99,7 @@ Also be sure to do a quick full-text-search to check if the version was updated WARNING: If you find anything here, you will need to pay attention during the release. -=== Release stabilization phase +== Release stabilization phase Now usually comes a phase in which last tests and checks should be performed. @@ -107,7 +107,7 @@ If any problems are found they, have to be fixed in the release branch. Changes should either be re applied in `develop` or `cherry-picked`, however merging things back can cause a lot of problems, and we no longer have the same versions. -=== Preparing a release +== Preparing a release Before you start preparing the release, it is important to manually make the `RELEASE_NOTES` reflect the version we are planning on releasing. @@ -180,7 +180,7 @@ And it will change the versions back and commit and push things. However, it will not delete the tag in GIT (locally and remotely). So you have to do that manually or use a different tag next time. -=== Performing a release +== Performing a release This is done by executing another goal of the `maven-release-plugin`: @@ -217,7 +217,7 @@ This contains all sources of the project and will be what's actually the release This file will also be signed and `SHA512` hashes will be created. -=== Staging a release +== Staging a release Each new release and release-candidate has to be staged in the Apache SVN under: @@ -257,7 +257,7 @@ The three `*-source-release.zip*` artifacts should be located in the directory: So, after committing these files to SVN, you are ready to start the vote. -=== Starting a vote on the mailing list +== Starting a vote on the mailing list After staging the release candidate in the Apache SVN, it is time to actually call out the vote. @@ -335,7 +335,7 @@ After the 72-our minimum wait period is over, and we have fulfilled the requirem Chris ---- -=== Releasing after a successful vote +== Releasing after a successful vote As soon as the votes are finished, and the results were in favor of a release, the staged artifacts can be released. This is done by moving them inside the Apache SVN. @@ -351,7 +351,7 @@ This will make the release artifacts available and will trigger them being copie This is also the reason why you should wait at least 24 hours before sending out the release notification emails. -=== Cleaning up older release versions +== Cleaning up older release versions As a lot of mirrors are serving our releases, it is the Apache policy to clean old releases from the repo if newer versions are released. @@ -361,7 +361,7 @@ This can be done like this: After this, https://dist.apache.org/repos/dist/release/plc4x should only contain the latest release directory. -=== Releasing the Maven artifacts +== Releasing the Maven artifacts The probably simplest part is releasing the Maven artifacts. @@ -371,7 +371,7 @@ This will move all artifacts into the Apache release repository and delete the s All release artifacts released to the Apache release repo, will automatically be synced to Maven central. -=== Merge back release version to `release` branch +== Merge back release version to `release` branch The `release branch should always point to the last released version. This has to be done with git diff --git a/src/site/asciidoc/developers/release/index.adoc b/src/site/asciidoc/developers/release/index.adoc index 473cddb5b6b..311fe0ca3a5 100644 --- a/src/site/asciidoc/developers/release/index.adoc +++ b/src/site/asciidoc/developers/release/index.adoc @@ -17,4 +17,4 @@ :imagesdir: ../../images/ :icons: font -== Releasing and Validating Releases += Releasing and Validating Releases diff --git a/src/site/asciidoc/developers/release/release.adoc b/src/site/asciidoc/developers/release/release.adoc index 4e7ae206740..25de5fa27f2 100644 --- a/src/site/asciidoc/developers/release/release.adoc +++ b/src/site/asciidoc/developers/release/release.adoc @@ -17,9 +17,9 @@ :imagesdir: ../../images/ :icons: font -== Releasing PLC4X += Releasing PLC4X -=== TL/DR +== TL/DR IMPORTANT: Please be sure to execute the release with a Java version 11 or the Karaf feature tests will not run. @@ -57,7 +57,7 @@ IMPORTANT: Please be sure to execute the release with a Java version 11 or the K * [ ] Merge back release version to `release` branch * [ ] Send announce email -=== Preparing your system for being able to release +== Preparing your system for being able to release NOTE: Be sure you are using a JDK and not a JRE, or the release will fail because the release can't execute the `javadoc` executable. @@ -130,7 +130,7 @@ NOTE: On some systems (mainly Mac) gpg signing can result in errors like: gpg: signing failed: Inappropriate ioctl for device``` In this case adding the following helps: `export GPG_TTY=$(tty)` -=== Preparing the codebase for a release +== Preparing the codebase for a release Usually you will have to update the RELEASE_NOTES document to the new version. I would suggest doing this prior to the branch as otherwise you will definitely have to port it back to `develop`. @@ -139,7 +139,7 @@ So remove the `SNAPSHOT` and `(Unreleased)` markers from the file and add missin Also, if you are doing the first release in a new year, it is advisable to search for the old year and check if any occurrences are ok the way they are. Usually the `NOTICE` file has to be adjusted. -=== Creating a release branch +== Creating a release branch According to SemVer, we have: Major, Minor and Bugfix releases. @@ -180,7 +180,7 @@ This step now should perform quite quickly as no build and no tests are involved However, in the end the versions of the `develop` branch are updated and a new `rel/{current-short-version}` branch is created. -=== Preparing `develop` for the next iteration +== Preparing `develop` for the next iteration Now is a good time to add a new section to the `RELEASE_NOTES` document for the new `SNAPSHOT` version. @@ -209,7 +209,7 @@ Also be sure to do a quick full-text-search to check if the version was updated WARNING: If you find anything here, you will need to pay attention during the release. -=== Release stabilization phase +== Release stabilization phase Now usually comes a phase in which last tests and checks should be performed. @@ -217,7 +217,7 @@ If any problems are found, they have to be fixed in the release branch. Changes should either be re applied in `develop` or `cherry-picked`, however merging things back can cause a lot of problems, and we no longer have the same versions. -=== Preparing a release +== Preparing a release Same as with creating the branch it is important to enable all profiles when creating the branch as only this way will all modules versions be updated. Otherwise, the non-default modules on develop will reference the old version which will cause problems when building. @@ -287,7 +287,7 @@ Also, should you check if you have any uncommitted changes (as our code-generati However, it will not delete the tag in GIT (locally and remotely). So you have to do that manually or use a different tag next time. -=== Performing a release +== Performing a release This is done by executing another goal of the `maven-release-plugin`: @@ -328,7 +328,7 @@ This contains all sources of the project and will be what's actually the release This file will also be signed and `SHA512` hashes will be created. -=== Staging a release +== Staging a release Each new release and release-candidate has to be staged in the Apache SVN under: @@ -368,7 +368,7 @@ All three `*-source-relese.zip*` artifacts should be located in the directory: ` After committing these files to SVN you are ready to start the vote. -=== Starting a vote on the mailing list +== Starting a vote on the mailing list After staging the release candidate in the Apache SVN, it is time to actually call out the vote. @@ -442,7 +442,7 @@ Message: So, the vote passes with {number of +1 votes} +1 votes by PMC members {number of +1 votes from non-pmc members} +1 vote by a non PMC member. ---- -=== Releasing after a successful vote +== Releasing after a successful vote As soon as the votes are finished, and the results were in favor of a release, the staged artifacts can be released. This is done by moving them inside the Apache SVN. @@ -458,7 +458,7 @@ This will make the release artifacts available and will trigger them being copie This is also the reason why you should wait at least 24 hours before sending out the release notification emails. -=== Going back for a new release candidate +== Going back for a new release candidate If however for some reason it is needed to prepare a new RC for the release. Please follow these steps: @@ -486,7 +486,7 @@ If however for some reason it is needed to prepare a new RC for the release. Ple After this you should be ready to start a new RC. -=== Cleaning up older release versions +== Cleaning up older release versions As a lot of mirrors are serving our releases, it is the Apache policy to clean old releases from the repo if newer versions are released. @@ -496,7 +496,7 @@ This can be done like this: After this https://dist.apache.org/repos/dist/release/plc4x should only contain the latest release directory. -=== Releasing the Maven artifacts +== Releasing the Maven artifacts Probably the simplest part is releasing the Maven artifacts. @@ -506,7 +506,7 @@ This will move all artifacts into the Apache release repository and delete the s All release artifacts released to the Apache release repo, will automatically be synced to Maven central. -=== Add the version to the DOAP file +== Add the version to the DOAP file Now that the release is out, in the `develop` branch, update the `DOAP` file for plc4x. @@ -518,7 +518,7 @@ Please add the just released version to the top of the versions. This file is needed for Apache's tooling to automatically keep track of project release activity, and we use this internally too to automatically update the documentation to always reference the latest released version automatically. -=== Merge back release version to `release` branch +== Merge back release version to `release` branch The `release` branch should always point to the last released version. This has to be done with git @@ -539,12 +539,12 @@ git merge -X theirs v{current-full-version} Possibly a manual conflict resolution has to be done afterwards. After that, changes need to be pushed. -=== Updating Jira +== Updating Jira 1. Set the released version to "released" and set the "release-date" 2. Add the next version to the versions. -=== Update the download site +== Update the download site The URL http://plc4x.apache.org/users/download.html has to be changed, and the current release has to be listed there. This is done by changing the `download.adoc` under `src/site/users/` (**in the develop branch, as this is where the site is generated from!**) @@ -557,7 +557,7 @@ _Note: Please add an anchor for the toc_ _Note: Transfer all to ascii-doc notation to ensure correct rendering of the site_ _Also remove the JIRA TICKET ids in Front_ -=== Notifying the world +== Notifying the world Make sure you have given the Apache mirrors time to fetch the release files by waiting at least 24 hours after moving the release candidate to the release part of the SVN. diff --git a/src/site/asciidoc/developers/release/validation.adoc b/src/site/asciidoc/developers/release/validation.adoc index e5f572b49af..d16222e368d 100644 --- a/src/site/asciidoc/developers/release/validation.adoc +++ b/src/site/asciidoc/developers/release/validation.adoc @@ -17,7 +17,7 @@ :imagesdir: ../images/ :icons: font -== Validating a staged release += Validating a staged release TIP: On MacOS and Linux the first 4 steps can be automated. For details please read the section about tooling after this chapter. @@ -69,7 +69,7 @@ find . -type f -name 'pom.xml' -exec grep -l "SNAPSHOT" {} \; * [ ] Build the project according to the information in the README.md file. * [ ] [RM] Build the project with all `with-xyz` profiles and tests enabled and an empty maven local repo: by appending `-Dmaven.repo.local=../.m2` (On windows use `-D"maven.repo.local"="../.m2"`). -=== Using RAT +== Using RAT Even if we are using RAT to ensure sources have headers in place, still the project can contain exclusions that hide things from the check. @@ -86,7 +86,7 @@ java -jar apache-rat-0.13.jar apache-plc4x-{current-full-version}-source-release By piping the result into a text file gives you the chance to investigate the content more easily. -=== Release Tooling +== Release Tooling In the `tools` directory we have a little script that can help with downloading and checking the hashes and signatures automatically. @@ -103,7 +103,7 @@ After that it will calculate the SHA512 hash and compare it with the staged hash Last not least it will validate the PGP key and print out some information on it. -=== Template for the email for voting +== Template for the email for voting ---- +1/-1 (binding) diff --git a/src/site/asciidoc/developers/team.adoc b/src/site/asciidoc/developers/team.adoc index 787754f55bd..42a9482e593 100644 --- a/src/site/asciidoc/developers/team.adoc +++ b/src/site/asciidoc/developers/team.adoc @@ -17,12 +17,12 @@ :imagesdir: ../images/ :icons: font -== Team += Team Sorted by first name: [width="100%",cols="2,4,1,1",options="header"] -|========================================================= +|=== |Name |Bio | | | *Ben Hutcheson* @@ -88,3 +88,4 @@ Engineer pragmatic industries GmbH Nürtingen |Electrical engineer (HW, SW and interface-stuff) from passion and very new to Apache projects. The more he is involved in open-source the more he likes it, focusing on PLC4X at first. | a|image::team/tmitsch.png[tmitsch, 240, 263] +|=== \ No newline at end of file diff --git a/src/site/asciidoc/developers/tools.adoc b/src/site/asciidoc/developers/tools.adoc index 825229d5f72..c4a68cea013 100644 --- a/src/site/asciidoc/developers/tools.adoc +++ b/src/site/asciidoc/developers/tools.adoc @@ -17,7 +17,7 @@ :imagesdir: ../images/ :icons: font -== Tools += Tools In order to be able to work on PLC4X some tools have kindly been made available to Apache PLC4X committers. diff --git a/src/site/asciidoc/developers/tutorials/index.adoc b/src/site/asciidoc/developers/tutorials/index.adoc index 21ded0ee2cc..2016e68c531 100644 --- a/src/site/asciidoc/developers/tutorials/index.adoc +++ b/src/site/asciidoc/developers/tutorials/index.adoc @@ -17,4 +17,4 @@ :imagesdir: ../../images/ :icons: font -== Tutorials += Tutorials diff --git a/src/site/asciidoc/developers/tutorials/testing-serializers-and-parsers.adoc b/src/site/asciidoc/developers/tutorials/testing-serializers-and-parsers.adoc index 5b55e170498..4a6e1268b2c 100644 --- a/src/site/asciidoc/developers/tutorials/testing-serializers-and-parsers.adoc +++ b/src/site/asciidoc/developers/tutorials/testing-serializers-and-parsers.adoc @@ -18,7 +18,7 @@ = Testing Serializers and Parsers -Currently the build generates the serializers and parsers from a provided `mspec` specification. +Currently, the build generates the serializers and parsers from a provided `mspec` specification. A typical full round-trip test for the model, parsers and serializers would look as follows: diff --git a/src/site/asciidoc/developers/tutorials/writing-driver.adoc b/src/site/asciidoc/developers/tutorials/writing-driver.adoc index dcf572775b1..583187d992d 100644 --- a/src/site/asciidoc/developers/tutorials/writing-driver.adoc +++ b/src/site/asciidoc/developers/tutorials/writing-driver.adoc @@ -16,7 +16,7 @@ // :imagesdir: ../../images/ -== Basic Building Blocs of a Driver / Protocol += Basic Building Blocs of a Driver / Protocol The general pipeline for a Protocol looks like the following: diff --git a/src/site/asciidoc/protocols/ab-eth/index.adoc b/src/site/asciidoc/protocols/ab-eth/index.adoc index 759a709457b..67ba35fcee2 100644 --- a/src/site/asciidoc/protocols/ab-eth/index.adoc +++ b/src/site/asciidoc/protocols/ab-eth/index.adoc @@ -15,12 +15,12 @@ // limitations under the License. // -== Allen-Bradley Ethernet += Allen-Bradley Ethernet The Allen-Bradley Ethernet protocol encapsulates a DF1 protocol body to transfer data. The PLC4X driver currently only supports the protected typed logical read with a limited number of data types. -=== Connection +== Connection The connection string looks as follows: `ab-eth:///` diff --git a/src/site/asciidoc/protocols/ads/index.adoc b/src/site/asciidoc/protocols/ads/index.adoc index f586586a6d9..64498ae3580 100644 --- a/src/site/asciidoc/protocols/ads/index.adoc +++ b/src/site/asciidoc/protocols/ads/index.adoc @@ -15,11 +15,11 @@ // limitations under the License. // -== Beckhoff ADS += Beckhoff ADS Introduction -=== Links +== Links diff --git a/src/site/asciidoc/protocols/canopen/index.adoc b/src/site/asciidoc/protocols/canopen/index.adoc index 7d9d2b2472e..5e61e1ccd3e 100644 --- a/src/site/asciidoc/protocols/canopen/index.adoc +++ b/src/site/asciidoc/protocols/canopen/index.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== CANopen += CANopen CANopen is communication protocol built on top of CAN. CAN is a popular link layer standard which allows exchanging data between nodes and build multi-node applications. @@ -31,7 +31,7 @@ Please refer the official materials for more details on protocol and its constru Apache PLC4X is open source project. It is not a certified product. -=== Links +== Links - https://www.can-cia.org/canopen[CAN in Automation] organization managing specification. - Public materials published by CAN in Automation: https://www.can-cia.org/en/download/ diff --git a/src/site/asciidoc/protocols/delta-v/index.adoc b/src/site/asciidoc/protocols/delta-v/index.adoc index 791ece131fa..1ffebaf7a1e 100644 --- a/src/site/asciidoc/protocols/delta-v/index.adoc +++ b/src/site/asciidoc/protocols/delta-v/index.adoc @@ -16,14 +16,14 @@ // :imagesdir: ../../images/ -== DeltaV Industrial Ethernet Communication += DeltaV Industrial Ethernet Communication The DeltaV protocol is used in the Emerson DeltaV system. In contrast to most other systems, this is a combination of PLC and Control System. As Emerson seems to insist on the devices not being PLCs, but controllers, well call them this way in this document. Same with the Control System, these seem to be called OS (Operator System). -=== Disclaimer +== Disclaimer As we had absolutely no information on the details of the protocol, we started by taking an existing DeltaV training system and replaced the network switch with a hub and used this to take network captures of the entire network traffic of the DeltaV network. @@ -37,7 +37,7 @@ Due to this uncertainty we are only implementing a `promiscuous mode` driver, wh Read link:reverse-engineering.html[here] for a document on how we proceeded with reverse engineering this protocol. -=== The Wrapper Protocol +== The Wrapper Protocol All communication seems to be done using the `UDP` protocol on port `18507`. @@ -98,11 +98,11 @@ However we currently have no idea on how this is calculated and we haven't inves UDP being connectionless the DeltaV network protocol seems to require acknowledging. This is done by sending a packet back to the originator, but with a length of `0x0000` and the same `type` and mesasge-id` as the packet that is acknowledged. -=== High Level View of the Protocol +== High Level View of the Protocol In this section we'll describe the general structure of how the communication looks like - which message types are sent when and in which sequence. -==== Connecting an OS to a Controller +=== Connecting an OS to a Controller [seqdiag,deltav-connect] .... @@ -125,7 +125,7 @@ In this section we'll describe the general structure of how the communication lo } .... -==== Regular sync +=== Regular sync It seems that every 15 seconds two packets are exchanged. diff --git a/src/site/asciidoc/protocols/delta-v/read-data.adoc b/src/site/asciidoc/protocols/delta-v/read-data.adoc index 7793bad8f57..a7b9072f797 100644 --- a/src/site/asciidoc/protocols/delta-v/read-data.adoc +++ b/src/site/asciidoc/protocols/delta-v/read-data.adoc @@ -16,6 +16,8 @@ // :imagesdir: ../../images/ += Data retrieval + == 0x0403 (before registration) Before a subscription is done, regularly (about once a minute) the following packets come in. diff --git a/src/site/asciidoc/protocols/delta-v/reverse-engineering.adoc b/src/site/asciidoc/protocols/delta-v/reverse-engineering.adoc index 271c31b8e39..b38c17f380c 100644 --- a/src/site/asciidoc/protocols/delta-v/reverse-engineering.adoc +++ b/src/site/asciidoc/protocols/delta-v/reverse-engineering.adoc @@ -16,13 +16,13 @@ // :imagesdir: ../../images/ -== Reverse Engineering the DeltaV protocol += Reverse Engineering the DeltaV protocol This document should describe what we did in order to reverse-engineer the DeltaV protocol. The sole purpose of this document, is to write document our path in order to eventually protect ourselves against accusation of using illegal measures in gaining the information we got. -=== Starting point +== Starting point We kindly were provided with access to a DeltaV system used for training. @@ -42,7 +42,7 @@ With this we let the system run and did a pretty long network capture. This 29MB WireShark capture was what we started working with. -=== Identifying the protocol +== Identifying the protocol As WireShark didn't have a DeltaV disector, we had to trace it down ourselves. @@ -50,7 +50,7 @@ When looking at the capture, we very quickly filtered out the usual network prot Special with all of these was that they all started with a payload of `0xFACE`. -=== First steps in understanding the protocol +== First steps in understanding the protocol Here it appeared that `UDP` packets are sent with some sort of payload, which are obviously responded to by packets with a fixed and very small size (64 bytes). @@ -68,7 +68,7 @@ When filtering only the `UDP` packets on port `18507` we could scroll though the In order to prove any assumption we made, we used little programs that used Pcap4J to programmatically check assumptions. -=== Decoding the wrapper packet header +== Decoding the wrapper packet header As mentioned before, it appeared obvious that for almost identical packets the first two short values were very similar too, which led us to the assumption that these are `type` and `sub-type'. So we wrote a little program, that scanned all the packets and counted how many types we had. @@ -135,7 +135,7 @@ So we'll just keep that in mind an not worry about interpreting a meaning into t The last two bytes of the wrapper packet header are simply constantly `0x8000` in every packet. Here too, we'll just live with knowing that and not wondering what it could mean. -=== Decoding the internal packet +== Decoding the internal packet After having decoded what we think is the wrapper-protocol packet structure, we knew decoding the internal protocol would be a bigger challenge. @@ -153,7 +153,7 @@ The first two bytes of the payload seem again to relate to a type as similar pac Especially we did a lot of separate captures for all sorts of different operations. During this we managed to reverse engineer the connection on the wrapper-protocol layer as well how the communication looks inside the internal protocol. -==== Connecting +=== Connecting When an OS is booted, the following communication could be observed: @@ -172,7 +172,7 @@ So it seems that In these packets each participant compares it's software state This does make sense. When connecting to a remote system, checking the version compatibility is surely an important thing to do. -==== Normal operation +=== Normal operation When just starting the system and not changing anything, we did notice those previously mentioned `0x0006` type messages we think are responsible for keeping the connections in sync. @@ -183,7 +183,7 @@ It also appears that there is some upper bound for the size of packets. As when seeing a lot of alarms being sent in 0x0304 messages, sometimes these are split up into multiple messages sent in very short intervals. The last of these usually being smaller than the rest. -==== Changing values +=== Changing values So now we started changing values. First we started changing valid values into other valid values. @@ -197,7 +197,7 @@ Also did we notice the `0x0403` packets seem to contain String constants that re We assume payload-types starting with `0x04` relate to normal data exchange and ones with `0x03` relating to events and alarms. -==== Decoding value changes +=== Decoding value changes After coming to the conclusion that wrapper-type `0x0002` messages with a payload-type `0x0403` seem to relate to value changes, we started filtering for exactly these packages. @@ -242,7 +242,7 @@ Right now we are assuming that in one of the other `0x04` packets a subscription We will hopefully be able to decode this addressing problem in one of our next reverse-engineering sessions. -==== Decoding Strings +=== Decoding Strings As we mentioned before, that we could see content in the packages that were sometimes readable from just looking at the payload, we decided to have another look at these. diff --git a/src/site/asciidoc/protocols/ehtercat/index.adoc b/src/site/asciidoc/protocols/ehtercat/index.adoc index 8fecaa9b8a3..4f533f54f99 100644 --- a/src/site/asciidoc/protocols/ehtercat/index.adoc +++ b/src/site/asciidoc/protocols/ehtercat/index.adoc @@ -15,11 +15,11 @@ // limitations under the License. // -== EtherCAT += EtherCAT Introduction -=== Links +== Links https://www.ethercat.org/default.htm https://www.ethercat.org/download/documents/EtherCAT_EAP_EN.pdf diff --git a/src/site/asciidoc/protocols/ethernet-ip/index.adoc b/src/site/asciidoc/protocols/ethernet-ip/index.adoc index f0c705bd979..ca16129867c 100644 --- a/src/site/asciidoc/protocols/ethernet-ip/index.adoc +++ b/src/site/asciidoc/protocols/ethernet-ip/index.adoc @@ -15,11 +15,11 @@ // limitations under the License. // -== EtherNet/IP += EtherNet/IP Introduction -=== Links +== Links As of November 2008, The EtherNet/IP Specification consists of three parts: The Common Industrial Protocol (Volume 1 of the CIP Networks Library), the EtherNet/IP Adaptation of CIP (Volume 2), and the diff --git a/src/site/asciidoc/protocols/features.adoc b/src/site/asciidoc/protocols/features.adoc index 2c20eafe903..8f41b8afb26 100644 --- a/src/site/asciidoc/protocols/features.adoc +++ b/src/site/asciidoc/protocols/features.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Features += Features :icons: font The following table contains a list of operations and the protocols that support them: diff --git a/src/site/asciidoc/protocols/index.adoc b/src/site/asciidoc/protocols/index.adoc index da8c5308bc6..b6314097de9 100644 --- a/src/site/asciidoc/protocols/index.adoc +++ b/src/site/asciidoc/protocols/index.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Protocols += Protocols - link:ab-eth/index.html[AB-Ethernet] - link:ads/index.html[DeltaV] @@ -26,14 +26,14 @@ - link:opc-ua/index.html[OPC-UA] - link:s7/index.html[S7] -=== Links +== Links Apache 2.0 licensed JNI library for accessing raw IPv4 and IPv6 sockets. Might be the ideal starting point for implementing protocols below TCP & UDP. https://www.savarese.org/software/rocksaw/ Links to different WireShark captures: https://github.com/automayt/ICS-pcap -==== BACNet +=== BACNet Used in the building automation sector. http://www.bacnet.org/Addenda/Add-135-2008t.pdf @@ -41,7 +41,7 @@ http://www.bacnet.org/Addenda/Add-135-2008t.pdf AKA: ISO 16484-5: The official specification can be purchased here: https://www.iso.org/standard/71935.html -==== IEC 61850 +=== IEC 61850 Used by the IDS SAS (station automation system) diff --git a/src/site/asciidoc/protocols/modbus/index.adoc b/src/site/asciidoc/protocols/modbus/index.adoc index 7a1092eba37..58e2c65e2d2 100644 --- a/src/site/asciidoc/protocols/modbus/index.adoc +++ b/src/site/asciidoc/protocols/modbus/index.adoc @@ -15,11 +15,11 @@ // limitations under the License. // -== Modbus += Modbus Introduction -=== Links +== Links - Main Landing Page: http://www.modbus.org/tech.php - Modbus Protocol Spec: http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf diff --git a/src/site/asciidoc/protocols/opc-ua/index.adoc b/src/site/asciidoc/protocols/opc-ua/index.adoc index 82973c0edc1..01766965876 100644 --- a/src/site/asciidoc/protocols/opc-ua/index.adoc +++ b/src/site/asciidoc/protocols/opc-ua/index.adoc @@ -15,11 +15,11 @@ // limitations under the License. // -== OPC-UA += OPC-UA Introduction -=== Links +== Links https://opcfoundation.org/developer-tools/specifications-unified-architecture/part-1-overview-and-concepts diff --git a/src/site/asciidoc/protocols/s7/index.adoc b/src/site/asciidoc/protocols/s7/index.adoc index bb17ec7a4fa..0b734e4b648 100644 --- a/src/site/asciidoc/protocols/s7/index.adoc +++ b/src/site/asciidoc/protocols/s7/index.adoc @@ -16,14 +16,14 @@ // :imagesdir: ../../images/ -== S7 Communication += S7 Communication When communicating with S7 Devices there is a whole family of protocols, that can be used. In general you can divide them into `Profinet` protocols and `S7 Comm` protocols. The later are far simpler in structure, but also far less documented. The `S7 Comm` protocols are generally split up into two flavours: The classic `S7 Comm` and a newer version unofficially called `S7 Comm Plus`. -=== Overview of the Protocols +== Overview of the Protocols [ditaa,protocols-s7-osi] .... @@ -83,7 +83,7 @@ The `S7 Comm` protocols are generally split up into two flavours: The classic `S - - - - - - - - - - +-------------------------------------------------------------------------------------------------+ - - .... -=== Protocol Descriptions +== Protocol Descriptions |=== |Name |ISO |RFC |Link @@ -96,7 +96,7 @@ The `S7 Comm` protocols are generally split up into two flavours: The classic `S |DCOM |- |- | https://msdn.microsoft.com/library/cc201989.aspx |=== -=== Interaction with an S7 PLC +== Interaction with an S7 PLC Currently we are concentrating on implementing the TCP-based variants of the `S7 Comm` and `S7 Comm Plus` protocols. Both are transferred using `ISO TP` which is wrapped by `ISO on TCP`. @@ -140,7 +140,7 @@ Each message is called a `TPDU` (Transport Protocol Data Unit): Notice: There is no `Disconnect Response` in `ISO TP: Class 0`. -==== Connection Request TPDU +=== Connection Request TPDU // len (length of bits - use instead of explicit byte count - requires "*" as first element) // label @@ -198,11 +198,11 @@ Legend: - [protocolId]#Part of the packet that identifies the type of request# - [protocolParameter]#Variable Parts of the ISO Transport Protocol Packet Header# -==== Connection Response TPDU +=== Connection Response TPDU The `Connection Response` is identical to the `Connection Request` with the only difference that the `TPDU-Code` has a code of `0xD0`. -==== Data TPDU +=== Data TPDU // len (length of bits - use instead of explicit byte count - requires "*" as first element) // label diff --git a/src/site/asciidoc/protocols/s7/s7comm-plus.adoc b/src/site/asciidoc/protocols/s7/s7comm-plus.adoc index 529500a5f15..2c1f8fa7757 100644 --- a/src/site/asciidoc/protocols/s7/s7comm-plus.adoc +++ b/src/site/asciidoc/protocols/s7/s7comm-plus.adoc @@ -16,7 +16,7 @@ // :imagesdir: ../../images/ -== S7 Comm Plus (0x72) += S7 Comm Plus (0x72) The `S7 Comm Plus` protocol is a new version of the original `S7 Comm` protocol. While a `S7 Comm` packet is identified, by the magic byte `0x32`, the `S7 Comm Plus` packet uses the magic byte `0x72`. diff --git a/src/site/asciidoc/protocols/s7/s7comm.adoc b/src/site/asciidoc/protocols/s7/s7comm.adoc index 3b97a43ef39..3d25f6d9aaf 100644 --- a/src/site/asciidoc/protocols/s7/s7comm.adoc +++ b/src/site/asciidoc/protocols/s7/s7comm.adoc @@ -16,9 +16,9 @@ // :imagesdir: ../../images/ -== S7 Comm (0x32) += S7 Comm (0x32) -=== General +== General While a lot of information was available on the general structure of S7 communication, only little information was available on the constant values this protocol uses. If information was available, this was mostly provided with a GPL license and therefore was disqualified for being used in this project. @@ -30,7 +30,7 @@ As soon as a valid value was found the tool then output the detected constant va The tool for generating this is located in the `plc4j/protocols/s7-utils` project. -=== Structure of a Setup Communication Request +== Structure of a Setup Communication Request [packetdiag,s7-setup-communication-request,svg] .... @@ -78,7 +78,7 @@ Legend: - [protocolId]#Part of the packet that identifies the type of request# - [protocolParameter]#Variable Parts of the ISO Transport Protocol Packet Header# -=== Structure of a Setup Communication Response +== Structure of a Setup Communication Response The `Setup Communication Response` is identical to the `Setup Communication Request` with the only difference that the `Message Type` has an ACK_DATA code of `0x03`. @@ -88,7 +88,7 @@ The values might be lower than in the request, but never higher. TIP: One thing about `Setup Communication Responses` which is kind of strange, is that usually S7 response messages have additional `error class` and `error code` fields, which this type of response doesn't seem to have. -=== Sizes of requests +== Sizes of requests During the connection to a S7 PLC the client and PLC agree on 3 important parameters: @@ -134,7 +134,7 @@ Same should sometimes be possible in the other direction. So if we wanted to read 300 bytes with a 256 byte TPDU size, the driver would split this up into two smaller byte arrays and internally merge them. However when reading Real (Number) values there could be issues with this. However in this case the driver has to completely take care of this optimization. -=== Links +== Links Providing some additional information without directly being used: diff --git a/src/site/asciidoc/users/adopters.adoc b/src/site/asciidoc/users/adopters.adoc index da17829a85f..4b2d61652de 100644 --- a/src/site/asciidoc/users/adopters.adoc +++ b/src/site/asciidoc/users/adopters.adoc @@ -17,7 +17,7 @@ :imagesdir: ../images/ :icons: font -== Companies using Apache PLC4X += Companies using Apache PLC4X image::toddy-loves-apache.png[width=200,float=left] diff --git a/src/site/asciidoc/users/blogs-videos-and-slides.adoc b/src/site/asciidoc/users/blogs-videos-and-slides.adoc index bd591e4bfb4..dc0f1e524bd 100644 --- a/src/site/asciidoc/users/blogs-videos-and-slides.adoc +++ b/src/site/asciidoc/users/blogs-videos-and-slides.adoc @@ -15,15 +15,15 @@ // limitations under the License. // -== Blogs, Videos and Slides on Apache PLC4X += Blogs, Videos and Slides on Apache PLC4X -=== Blog posts +== Blog posts - https://medium.com/@megachucky/apache-kafka-ksql-and-apache-plc4x-for-iiot-data-integration-and-processing-472c2de6700b[Apache Kafka, KSQL and Apache PLC4X for IIoT Data Integration and Processing] - https://blog.codecentric.de/2018/06/edge-computing-industrial-iot-apache-edgent-apache-plc4x/[Edge Computing und Industrial IoT mit Apache Edgent und Apache PLC4X] - https://riot.community/examples/http-plc4x.html[Advanced: Exposing a PLC as JSON Web Services (using PLC4X)] -=== Videos & Webinars +== Videos & Webinars - https://youtu.be/1vf3zw7HQws[High Security Iiot Communication With Apache Plc4X, Apache Asia Con, August 2021] - https://youtu.be/ogsFTnQhtIU[Apache Plc4X For Can Bus And Canopen, Apache Asia Con, August 2021] @@ -39,7 +39,7 @@ - https://aceu19.apachecon.com/session/idea-apache-tlp[From an idea to an Apache TLP] - https://mediathek.hhu.de/watch/6014a3fd-aadf-4bcf-adf6-3134162aef1b[Wie wir mit Apache PLC4X die Silos in der Automatisierungsindustrie aufbechen] -=== Slides +== Slides - https://de.slideshare.net/KaiWaehner/iiot-industry-40-with-apache-kafka-connect-ksql-apache-plc4x[Apache Kafka, KSQL and Apache PLC4X for IIoT Data Integration and Processing] - https://de.slideshare.net/ChristoferDutz/episode-iv-a-new-hope-229731756[Episode iv a new hope - Industry 4.0 done our way] diff --git a/src/site/asciidoc/users/commercial-support.adoc b/src/site/asciidoc/users/commercial-support.adoc index ed80b9e0af6..f6adaf3e5c8 100644 --- a/src/site/asciidoc/users/commercial-support.adoc +++ b/src/site/asciidoc/users/commercial-support.adoc @@ -17,7 +17,7 @@ :imagesdir: ../images/ :icons: font -== Commercial support offerings for Apache PLC4X += Commercial support offerings for Apache PLC4X image::toddy-loves-apache.png[width=200,float=left] @@ -40,11 +40,11 @@ a|image::users/companies/logo-timecho.png[timecho, 200, 200] |https://www.timec |=== -=== Who can be added to this list? +== Who can be added to this list? Anyone who provides `Apache PLC4X` related services can be added to this list (e.g. training, consulting, custom software development, support, installation or related services). -=== How can I get added to this list? +== How can I get added to this list? Please create a Pull-Request on GitHub as described https://plc4x.apache.org/developers/contributing.html[here]. The resource requiring editing can be found https://github.com/apache/plc4x/blob/develop/src/site/asciidoc/users/commercial-support.adoc[here] diff --git a/src/site/asciidoc/users/download.adoc b/src/site/asciidoc/users/download.adoc index cf1edee462e..c0436e5b65f 100644 --- a/src/site/asciidoc/users/download.adoc +++ b/src/site/asciidoc/users/download.adoc @@ -15,11 +15,11 @@ // limitations under the License. // -== Download += Download Be sure to verify your downloads by these https://www.apache.org/info/verification[procedures] using these https://downloads.apache.org/plc4x/KEYS[KEYS] for any Apache release. -=== Current Releases +== Current Releases ==== 0.12.0 Official https://www.apache.org/dyn/closer.lua/plc4x/0.12.0/apache-plc4x-0.12.0-source-release.zip[source release] [ https://downloads.apache.org/plc4x/0.12.0/apache-plc4x-0.12.0-source-release.zip.sha512[SHA512] ] [ https://downloads.apache.org/plc4x/0.12.0/apache-plc4x-0.12.0-source-release.zip.asc[ASC] ] @@ -32,7 +32,7 @@ This release was mainly a release containing many bugfixes. We literally halved - Which of these configuration options are required? - The same set of information is also available for the transports a driver is using. -===== New Features +==== New Features - API: Made several bits of information available via the API allowing tools to provide more content assist when dealing @@ -45,7 +45,7 @@ and Simulated drivers. - The OPC-UA Java driver now support certificate-based authentication and encryption. -===== Incompatible changes +==== Incompatible changes - Java 8 is no longer officially supported and Java 11 is the new base-line. @@ -60,7 +60,7 @@ new base-line. config options in the connection-string was updated to be now prefixed with the transport name the option belongs to. -===== Bug Fixes +==== Bug Fixes - S7: Several bugs and issues regarding supporting various duration, date and time data-types. @@ -71,7 +71,7 @@ numbers. improved. - Core: Fixed several leaks of open threads. -=== Previous Releases +== Previous Releases ==== 0.11.0 Official https://archive.apache.org/dist/plc4x/0.11.0/apache-plc4x-0.11.0-source-release.zip[source release] [ https://downloads.apache.org/plc4x/0.11.0/apache-plc4x-0.11.0-source-release.zip.sha512[SHA512] ] [ https://downloads.apache.org/plc4x/0.11.0/apache-plc4x-0.10.0-source-release.zip.asc[ASC] ] @@ -79,7 +79,7 @@ The APIs have been streamlined in a preparation for a hopefully soon 1.0.0 relea Many drivers have been re-implemented with much more features. Integration modules have been improved. -===== New Features +==== New Features - Implemented a `PLC4X-Server` and `PLC4X-Driver` that allows using the server as a proxy for communicating with PLCs. @@ -92,7 +92,7 @@ communication, which however can be tunneled through a - The KNX driver in Java now supports reading "knxproj" files exported from the new ETS version 6. -===== Bug Fixes +==== Bug Fixes - The name of the Modbus TCP driver was changed from "modbus" to "modbus-tcp". @@ -121,7 +121,7 @@ This is a bugfix release aiming at directly fixing CVE-2021-43083 as well as upd Please note that CVE-2021-43083 only affects the PLC4C part of PLC4X. -===== Bug Fixes +==== Bug Fixes CVE-2021-43083 Apache PLC4X 0.9.0 Buffer overflow in PLC4C via crafted server response @@ -132,7 +132,7 @@ This is an ordinary PLC4X release, containing changes that accumulated over time. It doesn't have an explicit focus on a particular topic. -===== New Features +==== New Features - The OPC UA driver has been replaced with a native driver. Previously Eclipse Milo was being used. @@ -143,9 +143,9 @@ Eclipse Milo was being used. - Major cleanup of PLC4C - S7 Driver now supports event and alarm handling on some S7 models -===== Incompatible changes +==== Incompatible changes -===== Bug Fixes +==== Bug Fixes PLC4X-200 OPC-UA Driver not connecting if params string is not provided PLC4X-201 OPC-UA PlcList underlying type not compatible with Eclipse Milo @@ -168,7 +168,7 @@ This is an ordinary PLC4X release, containing changes that accumulated over time. It doesn't have an explicit focus on a particular topic. -===== New Features +==== New Features - The KNXnet/IP Driver now supports writing of values. - The Modbus driver now supports more common notations of Modbus addresses using a pure-numeric notation. @@ -177,7 +177,7 @@ a particular topic. - Integration with the Milo OPC UA Server is now available. - Kafka Connect workers have been updated source and sink connectors are now included. -===== Incompatible changes +==== Incompatible changes - The syntax of the S7 addresses changed slightly allowing to provide a string length. Without this, a STRING datatype will read 254 characters, by adding the size in round brackets to the type name will use the specified number. @@ -191,7 +191,7 @@ a particular topic. - The PLCValue types have been refactored to align with the types defined in IEC 61131-3 (https://en.wikipedia.org/wiki/IEC_61131-3) directly using the older Java types (PlcBoolean) is no longer possible. -===== Bug Fixes +==== Bug Fixes A lot of testing was done regarding the IEC 61131-3 data-types. This resulted in numerous bugfixes in many protocols. @@ -226,7 +226,7 @@ core. All previous driver versions are now considered deprecated and have been replaced by versions using the new driver structure and generated driver codebase. -===== New Features +==== New Features - Drivers now support structured types using PlcValues - The EIP (EtherNet/IP) driver no longer requires an external @@ -242,7 +242,7 @@ significant performance gains when writing multiple vlaues) to be able to use them in an OSGi container. - New Firmata protocol driver -===== Incompatible changes +==== Incompatible changes - Due to the refactoring of the driver core there might be issues running drivers built against older core versions. @@ -255,7 +255,7 @@ https://plc4x.apache.org/users/protocols/s7.html - The karaf-feature modules are removed as the drivers now all provide both a feature.xml as well as a `kar` bundled archive -===== Bug Fixes +==== Bug Fixes - PLC4X-174 UDP Transport does not accept ports containing 0 - PLC4X-134 S7 is terminating the connection during handshake @@ -271,16 +271,16 @@ most users have switched to 0.7 and above (with generated drivers). If you are using the S7 Driver you should update to this Version as the critical (memory leak) bug PLC4X-163 is fixed. -===== New Features +==== New Features - PLC4X-168 A shorter S7 Field Syntax is Introduced. This release contains no further features and mostly stabilization. -===== Incompatible changes +==== Incompatible changes - Moved the C++, C# and Python drivers into the `sandbox` -===== Bug Fixes +==== Bug Fixes - Fixed Promise Chain for InternalPlcWriteRequest - PLC4X-45 Add float support to Modbus Protocol @@ -294,7 +294,7 @@ This release contains no further features and mostly stabilization. This is the first release containing our new generated drivers (AB-ETH) -===== New Features +==== New Features - Implemented a new Apache Kafka Connect integration module - Implemented a new Apache NiFi integration module @@ -307,9 +307,9 @@ Sandbox (Beta-Features) - Implemented a new BACnet/IP passive mode driver - Implemented a new Serial DF1 driver -===== Incompatible changes +==== Incompatible changes -===== Bug Fixes +==== Bug Fixes - PLC4X-104 S7 Driver Datatype TIME_OF_DAY causes ArrayOutOfBoundException - PLC4X-134 S7 is terminating the connection during handshake @@ -322,7 +322,7 @@ Sandbox (Beta-Features) This is the first release of Apache PLC4X as top-level project. -===== New Features +==== New Features - The PlcConnection now supports a `ping` method to allow checking if an existing connection is still alive. - Support of the OPC-UA protocol with the `opc-ua-driver`. @@ -331,25 +331,25 @@ This is the first release of Apache PLC4X as top-level project. -- Added first versions of a Python PLC4X API (`plc4py`) - Added an Interop server which allows to relay requests from other languages to a Java Server -===== Incompatible changes +==== Incompatible changes - ElasticSearch example was updated to use ElasticSearch 7.0.1, this might cause problems with older Kibana versions. -===== Bug Fixes +==== Bug Fixes -=== Incubating Releases +== Incubating Releases ==== 0.3.1 (incubating) Official https://archive.apache.org/dist/incubator/plc4x/0.3.1-incubating/apache-plc4x-incubating-0.3.1-source-release.zip[source release] [ https://archive.apache.org/dist/incubator/plc4x/0.3.1-incubating/apache-plc4x-incubating-0.3.1-source-release.zip.sha512[SHA512] ] [ https://archive.apache.org/dist/incubator/plc4x/0.3.1-incubating/apache-plc4x-incubating-0.3.1-source-release.zip.asc[ASC] ] -===== New Features +==== New Features - No new features -===== Incompatible changes +==== Incompatible changes - No incompatible changes. -===== Bug Fixes +==== Bug Fixes - The S7 driver didn't correctly handle "fill-bytes" in multi-item read-responses and multi-item write-requests - Fixed NPE when reading odd-length array of one-byte base types @@ -359,7 +359,7 @@ This is the first release of Apache PLC4X as top-level project. [#release-0_3_0] ==== 0.3.0 (incubating) Official https://archive.apache.org/dist/incubator/plc4x/0.3.0-incubating/apache-plc4x-incubating-0.3.0-source-release.zip[source release] [ https://archive.apache.org/dist/incubator/plc4x/0.3.0-incubating/apache-plc4x-incubating-0.3.0-source-release.zip.sha512[SHA512] ] [ https://archive.apache.org/dist/incubator/plc4x/0.3.0-incubating/apache-plc4x-incubating-0.3.0-source-release.zip.asc[ASC] ] -===== New Features +==== New Features - Object PLC Mapping (OPM) now has a Alias Registry to allow variable substitution at runtime and write support @@ -368,7 +368,7 @@ This is the first release of Apache PLC4X as top-level project. - New integration `apache-karaf` to enable plc4j in a karaf runtime environment -===== Incompatible changes +==== Incompatible changes - The 'plc4j-core' module has been merged into 'plc4j-api'. So there is no 'plc4j-core' module anymore. Just remove that @@ -377,7 +377,7 @@ This is the first release of Apache PLC4X as top-level project. a `plc4j-protocol-{name}` you now need to change this to `plc4j-driver-{name}` -===== Bug Fixes +==== Bug Fixes - Fixing dependency to the wrap url-handler - When receiving responses with more than 512 byte, the IsoOnTcp protocol doesn't work @@ -388,7 +388,7 @@ This is the first release of Apache PLC4X as top-level project. [#release-0_2_0] ==== 0.2.0 (incubating) Official https://archive.apache.org/dist/incubator/plc4x/0.2.0-incubating/apache-plc4x-incubating-0.2.0-source-release.zip[source release] [ https://archive.apache.org/dist/incubator/plc4x/0.2.0-incubating/apache-plc4x-incubating-0.2.0-source-release.zip.sha512[SHA512] ] [ https://archive.apache.org/dist/incubator/plc4x/0.2.0-incubating/apache-plc4x-incubating-0.2.0-source-release.zip.asc[ASC] ] -===== Changes: +==== Changes: * Changed API: instead of passing request object to `read({read-request})`, `write({write-request})` or `subscribe({subscribe-request})` methods now the `execute()` method is called on the request itself * New Connection Pool component diff --git a/src/site/asciidoc/users/getting-started/general-concepts.adoc b/src/site/asciidoc/users/getting-started/general-concepts.adoc index 0df85ea25c9..bb5d70e1757 100644 --- a/src/site/asciidoc/users/getting-started/general-concepts.adoc +++ b/src/site/asciidoc/users/getting-started/general-concepts.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== General Concepts += General Concepts On this page we'll give a short introduction to the most important concepts that will help you understand Apache PLC4X better. @@ -33,7 +33,7 @@ Both are pretty dependent on the type of device you are planning to communicate If you are familiar with `JDBC` or `ODBC`, you will easily understand the concepts in PLC4X, as these were a great inspiration for the creation of Apache PLC4X. -=== Connections +== Connections In general a connection is a physical or logical connection between two endpoints. @@ -65,7 +65,7 @@ From a driver point of view there is actually no difference between a `Raw Socke The `Test` transport is generally built for being used inside the PLC4X test framework as it allows fine-grained access to the input and output of the drivers. With the test transport we can explicitly control which data is passed into and retrieved from drivers and to validate this in unit- and integration-tests. -==== Connection Strings +=== Connection Strings A fully qualified PLC4X connection string would look like this: @@ -103,7 +103,26 @@ The shortest version of a fully qualified connection string would look something For more information on the default settings for a given protocol or transport, please check the corresponding drivers documentation. -=== Individual Resource Addresses (Tags) +== Individual Resource Addresses (Tags) Addresses for individual tags on a PLC are extremely dependent on the used protocol. As we usually decided to stick to the address formats that are used in those particular environments, please check the `Protocol Documentation` on details about these address formats link:../protocols/index.html[here]. + +The tag syntax is fairly generic and can be summarized as `type:address`. +Some protocols might support tag attributes which are specified as key-value pairs after primary tag address. +For example `coil:1{unit-id: 10}`. +Tag attributes are additional elements which depend on actual protocol. + +== Tag metadata + +Starting from Apache PLC4X release 0.13 an experimental support for result set metadata is provided in plc4j. +This metadata is dedicated to provide additional information which might be available at protocol (sample timestamp) or driver level (i.e. packet receive time). +Consult again protocol documentation on specifics of this feature. + +The common metadata keys currently defined are: + +* timestamp - timestamp for tag value provided by other communication party. +* timestamp_source - source of timestamp or receive_timestamp field, if any of these is provided. +* receive_timestamp - timestamp assumed upon receiving of packet with data. + + diff --git a/src/site/asciidoc/users/getting-started/index.adoc b/src/site/asciidoc/users/getting-started/index.adoc index 41f0a5b72ee..c634db80e17 100644 --- a/src/site/asciidoc/users/getting-started/index.adoc +++ b/src/site/asciidoc/users/getting-started/index.adoc @@ -15,14 +15,14 @@ // limitations under the License. // -== Getting Started += Getting Started Depending on the programming language, the usage will differ, therefore please go to the `Getting Started` version of the language of choice. -=== Go/Golang +== Go/Golang For guides on how to write PLC4X applications with Go, please go to the link:plc4go.html[Go Getting Started] -=== Java +== Java For guides on how to write PLC4X applications with Java, please go to the link:plc4j.html[Java Getting Started] \ No newline at end of file diff --git a/src/site/asciidoc/users/getting-started/opcua-client-certificate.adoc b/src/site/asciidoc/users/getting-started/opcua-client-certificate.adoc new file mode 100644 index 00000000000..76205d19ed1 --- /dev/null +++ b/src/site/asciidoc/users/getting-started/opcua-client-certificate.adoc @@ -0,0 +1,128 @@ +// +// 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/tutorials/opcua/kse +:icons: font +:experimental: + += OPC UA : Client certificate creation + +Below tutorial will teach you how to create client certificate for use within production environments. +This tutorial focuses on preparation of configuration. +It does not dive in X.509 and PKI details nor specifics of any OPC-UA server. + +The Apache PLC4X client, as many other UA clients is able to create ad-hoc certificate for communication. +While it softens entry bearer for many, it creates also a gap when with secured environments, which control certificate chains. + +There are several ways on how to organize certificates. +In this little tutorial we will use open source tool called https://github.com/kaikramer/keystore-explorer[KeyStore Explorer], referred herein as KSE. +This tool allows to create files which can be used as a cryptographic keystore for Java-based programs but not only. +One of nice things which Java runtime introduced, was change of default keystore format from JKS (Java KeyStore) to https://en.wikipedia.org/wiki/PKCS_12[PKCS#12]. + +== Creating self-signed certificate using KSE +Install the tool using the way which is valid for your operating system, then open the tool. + +Entire process can be observed in below short screen capture: + +image::kse_self_signed_ca_certificate.gif["Creation of self-signed certificate authority using KeyStore Explorer"] + +Step by step operations are. + +. Navigate to menu:File[New] or press kbd:[CTRL+N] to create new keystore. ++ +image::kse_1.png["Create new keystore"] + +. Pick PKCS#12 as desired store format. ++ +image::kse_2.png["Use PKCS#12 format"] + +. Navigate to menu:Tools[Generate key pair] or press kbd:[CTRL+G]: ++ +image::kse_3.png["Create new key pair"] + +. Decide on key type (RSA, DSA, EC) and its size. RSA is a fairly common, confirm key size and click btn:[OK] ++ +image::kse_4.png["Pick key type and size"] + +. KSE will ask you about certificate details ++ +image::kse_5.png["Certificate settings"] + +. Go to `Name` field and click little phone book icon and click it. +You will be able to specify common name (CN), organization unit (OU) and other fields, and confirm with btn:[OK] ++ +image::kse_6.png["Certificate name details"] + +. Click btn:[Add Extensions], which is located below `Name` field (you will again see step 5 window), it will open next popup. ++ +image::kse_7.png["Add extension popup"] + +. Click btn:[Use Standard Template], select CA, then click btn:[OK]. ++ +image::kse_8.png["Use CA template"] + +. This will fill extensions with few rows, but do not close this window yet. ++ +image::kse_9.png["CA certificate extensions details"] + +. Click btn:[+] next to the list, and select `Subject Alternative Name`, then click btn:[OK]. ++ +image::kse_10.png["Add subject name"] + +. You will see again window with list, click btn:[+] next to it. ++ +image::kse_11.png["Subject alternative names list"] + +. Select `URI`, and type client identifier in `General Name Value` field (i.e. `urn:my:plc:client`), and confirm via btn:[OK]. ++ +image::kse_12.png["Add subject alternative name"] + +. Click btn:[OK] to close certificate extensions prompt. +. Click btn:[OK] to finish certificate creation. +. Enter key pair alias and confirm with btn:[OK]. ++ +image::kse_13.png["Specify key pair alias"] + +. Enter private key password, confirm it in second field and click btn:[OK]. ++ +image::kse_14.png["Specify private key password"] + +. Upon completion of these steps you should be presented with "Key Pair Generation Successful" message. ++ +image::kse_15.png["Completion of key generation"] + +. Navigate to menu:File[Save] or press kbd:[CTRL+S] to save keystore. ++ +image::kse_16.png["Save keystore"] + +. Enter keystore password, for use within Java **it must be same as private key password**. ++ +image::kse_17.png["Save keystore"] + +. Specify file location. ++ +image::kse_17.png["Save keystore"] + +. Close KSE, your client private key and certificate is ready for use. + +== Usage within OPC UA PLC4X client + +For detailed use of options used to configure client please refer to documentation of Apache PLC4X OPC-UA driver. +Please remember that keystore must be readable by your program. +In case if you are not certain what is working directory of your program, specify full path to keystore. + + + diff --git a/src/site/asciidoc/users/getting-started/plc4c.adoc b/src/site/asciidoc/users/getting-started/plc4c.adoc index 358f781bb27..99327ed3d4a 100644 --- a/src/site/asciidoc/users/getting-started/plc4c.adoc +++ b/src/site/asciidoc/users/getting-started/plc4c.adoc @@ -15,4 +15,4 @@ // limitations under the License. // -== Getting Started with C \ No newline at end of file += Getting Started with C \ No newline at end of file diff --git a/src/site/asciidoc/users/getting-started/plc4cs.adoc b/src/site/asciidoc/users/getting-started/plc4cs.adoc index 465c1a44364..f476a8b89a7 100644 --- a/src/site/asciidoc/users/getting-started/plc4cs.adoc +++ b/src/site/asciidoc/users/getting-started/plc4cs.adoc @@ -15,4 +15,4 @@ // limitations under the License. // -== Getting Started with C# \ No newline at end of file += Getting Started with C# \ No newline at end of file diff --git a/src/site/asciidoc/users/getting-started/plc4go.adoc b/src/site/asciidoc/users/getting-started/plc4go.adoc index 105b2245dfc..672ae5b48d4 100644 --- a/src/site/asciidoc/users/getting-started/plc4go.adoc +++ b/src/site/asciidoc/users/getting-started/plc4go.adoc @@ -15,9 +15,9 @@ // limitations under the License. // -== Getting Started with Go += Getting Started with Go -=== Initializing a dummy project +== Initializing a dummy project Just in case you want to get started with `Go`. In this part we'll setup a new `Go` project. If you are familiar with this, you can go to the next chapter. @@ -58,7 +58,7 @@ You will execute your first `Go` progran ... however the output is rather underw You're now ready to continue. -=== Using the PLC4Go API directly +== Using the PLC4Go API directly In order to write a valid PLC4X Go application, all you need, is to add a dependency to the `plc4go module`. Now all you need to do, is execute the following command: @@ -77,7 +77,7 @@ Perhaps we'll change this in the future, but for now all comes in one bundle. Now you're generally set to start writing your first `PLC4Go` program. -==== Connecting to a PLC +=== Connecting to a PLC In contrast to PLC4J, which uses the service lookup to find the `transports` and the `drivers` automatically, in `PLC4Go` they need to be manually registered at the driver manager. diff --git a/src/site/asciidoc/users/getting-started/plc4j.adoc b/src/site/asciidoc/users/getting-started/plc4j.adoc index c98d7feda19..5f4a3eeff7a 100644 --- a/src/site/asciidoc/users/getting-started/plc4j.adoc +++ b/src/site/asciidoc/users/getting-started/plc4j.adoc @@ -15,9 +15,9 @@ // limitations under the License. // -== Getting Started with Java += Getting Started with Java -=== Using the PLC4J API directly +== Using the PLC4J API directly In order to write a valid PLC4X Java application, all you need, is to add a dependency to the `api module`. When using Maven, all you need to do is add this dependency: @@ -79,7 +79,7 @@ But there are some cases in which we can't simulate or features are simply disab Therefore, we use metadata to check programmatically, if a given feature is available. -==== Reading Data +=== Reading Data ---- // Check if this connection support reading of data. @@ -185,7 +185,7 @@ PLC4X provides getters and setters for a wide variety of Java types and automati However, when for example trying to get a long-value as a byte and the long-value exceeds the range supported by the smaller type, a `RuntimeException` of type `PlcIncompatibleDatatypeException`. In order to avoid causing this exception to be thrown, however there are `isValid{TypeName}` methods that you can use to check if the value is compatible. -==== Writing Data +=== Writing Data In general the structure of code for writing data is extremely similar to that of reading data. @@ -241,7 +241,7 @@ for (String tagName : response.getTagNames()) { } ---- -==== Subscribing to Data +=== Subscribing to Data Subscribing to data can be considered similar to reading data, at least the subscription itself if very similar to reading of data. diff --git a/src/site/asciidoc/users/getting-started/plc4py.adoc b/src/site/asciidoc/users/getting-started/plc4py.adoc index f2e0411e3f2..1d19bf1db7d 100644 --- a/src/site/asciidoc/users/getting-started/plc4py.adoc +++ b/src/site/asciidoc/users/getting-started/plc4py.adoc @@ -15,9 +15,9 @@ // limitations under the License. // -== Getting Started with Python += Getting Started with Python -=== Using the PLC4PY API directly +== Using the PLC4PY API directly Currently, you need to install PLC4Py from the GitHub repository instead of pypi. Once we have decided that PLC4Py is in a position to release we will publish to pypi. @@ -54,10 +54,10 @@ async def communicate_with_plc(): builder.add_item("Random Tag", "4x00001[10]") request = builder.build() print(f"Request built") - print(f"Executing request") - response = await connection.execute(request) - print(f"Request executed") - print(f"Response code: {response.response_code}") + print(f"Executing request") + response = await connection.execute(request) + print(f"Request executed") + print(f"Response code: {response.response_code}") asyncio.run(communicate_with_plc()) ---- @@ -69,11 +69,12 @@ The basic functions supported by PLCs and therefore supported by PLC4X are: * Discover Devices (Not yet available for PLC4Py) * List resources in the PLC * Read data -* Write data (Not yet available for PLC4Py) +* Write data * Subscribe for data (Not yet available for PLC4Py) In general, we will try to offer as many features as possible. -So if a protocol doesn't support subscription based communication it is our goal to simulate this by polling in the background, so it is transparent for the users (This simulation feature hasn't been implemented yet though, but it's on our roadmap). +So if a protocol doesn't support subscription based communication it is our goal to simulate this by polling in the background, +so it is transparent for the users (This simulation feature hasn't been implemented yet though, but it's on our roadmap). But there are some cases in which we can't simulate or features are simply disabled intentionally: @@ -81,12 +82,12 @@ But there are some cases in which we can't simulate or features are simply disab Therefore, we use metadata to check programmatically, if a given feature is available. -==== Reading Data +=== Reading Data ---- # Check if this connection support reading of data. -if connection.is_read_supported(): +if not connection.is_read_supported(): logger.error("This connection doesn't support reading.") ---- @@ -105,7 +106,8 @@ with connection.read_request_builder() as builder: So, as you can see, you prepare a request, by adding tag addresses to the request and in the end by calling the `build` method. -If you are using the `BrowseApi` you might also have been provided with `Tag` objects. In that case simply use `addTag` and pass in the `Tag` object instead of the address string. +If you are using the `BrowseApi` you might also have been provided with `Tag` objects. In that case simply use `add_tag` and +pass in the `PlcTag` object instead of the address string. The request is sent to the PLC by issuing the `execute` method on the request object: @@ -120,7 +122,8 @@ except ... ---- In general, all requests are executed asynchronously. -As soon as the request is fully processed, the callback gets called and will contain a `ReadResponse`, if everything went right or an excception if there were problems. +As soon as the request is fully processed, the callback gets called and will contain a `ReadResponse`, if everything went right or +an excception if there were problems. The following example will demonstrate some of the options you have: @@ -143,7 +146,8 @@ for tag_name in response.tag_names: ---- In the for-loop, we are demonstrating how the user can iterate over the tag aliases in the response. -In case of an ordinary read request, this will be predefined by the items in the request, however in case of a subscription response, the response might only contain some of the items that were subscribed. +In case of an ordinary read request, this will be predefined by the items in the request, however in case of a subscription +response, the response might only contain some of the items that were subscribed. Before accessing the data, it is advisable to check if an item was correctly returned. This is done by the `response_code` property for a given alias. @@ -158,12 +162,13 @@ If this is `PlcResponseCode.OK`, everything is ok, however it could be one of th Assuming the return code was `OK`, we can continue accessing the data. -As all PlcValue items support the len property, the user can check how many items of a given type are returned by calling len(response.tags[tag_name].value) +As all PlcValue items support the len property, the user can check how many items of a given type are returned +by calling len(response.tags[tag_name].value) You can then treat the values in the PlcList as a list using response.tags[tag_name].value.get_list() -==== Writing Data +=== Writing Data In general the structure of code for writing data is extremely similar to that of reading data. @@ -173,7 +178,7 @@ So first it is advisable to check if this connection is even able to write data: // Check if this connection support writing of data. if not plc_connection.is_write_supported(): logger.error("This connection doesn't support writing.") - return + ---- As soon as we are sure that we can write, we create a new `PlcWriteRequest.Builder`: @@ -183,8 +188,8 @@ As soon as we are sure that we can write, we create a new `PlcWriteRequest.Build // - Give the single item requested an alias name // - Pass in the data you want to write (for arrays, pass in a list of values) with connection.write_request_builder() as builder: - builder.add_item("Random Tag 1", "4x00001[2]", [1, 2]) - builder.add_item("Random Tag 2", "4x00011", 1) + builder.add_item("Random Tag 1", "4x00001[2]", PlcList([PlcINT(1), PlcINT(2)])) + builder.add_item("Random Tag 2", "4x00011", PlcINT(1)) request = builder.build() ---- @@ -210,6 +215,6 @@ for tag_name in response.tag_names: logger.error("Error[" + tag_name + "]: " + response.tags[tag_name].name()) ---- -==== Subscribing to Data +=== Subscribing to Data Coming Soon diff --git a/src/site/asciidoc/users/getting-started/using-snapshots.adoc b/src/site/asciidoc/users/getting-started/using-snapshots.adoc index b6a29a126d8..3044ef838b8 100644 --- a/src/site/asciidoc/users/getting-started/using-snapshots.adoc +++ b/src/site/asciidoc/users/getting-started/using-snapshots.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Using SNAPSHOT versions += Using SNAPSHOT versions Especially when it comes to trying to verify if an issue you are facing has already been fixed in the development version, you might want to or be asked to try out the absolute latest version of PLC4X. You can generally do this by adding "-SNAPSHOT" to the version number of PLC4X. diff --git a/src/site/asciidoc/users/getting-started/virtual-modbus.adoc b/src/site/asciidoc/users/getting-started/virtual-modbus.adoc index 5da29851b7b..8aa9b815e60 100644 --- a/src/site/asciidoc/users/getting-started/virtual-modbus.adoc +++ b/src/site/asciidoc/users/getting-started/virtual-modbus.adoc @@ -16,7 +16,7 @@ // :imagesdir: ../../images/ -== Playing around with Apache PLC4X with a virtual Modbus PLC += Playing around with Apache PLC4X with a virtual Modbus PLC If you want to get started with Apache PLC4X, but don't have any PLC at hand, this tutorial will demonstrate how you can use a virtual `Modbus Slave` software to simulate communication with `Modbus` enabled PLCs. @@ -24,7 +24,7 @@ Such a fully open-source software is `ModbusPal` which is available from http:// All you need, is to download the file called `ModbusPal.jar`. -=== Setting up the virtual Modbus Slave +== Setting up the virtual Modbus Slave In order to run the software, you just need to execute the following command in the same directory you downloaded the Jar to: diff --git a/src/site/asciidoc/users/index.adoc b/src/site/asciidoc/users/index.adoc index 53694a20227..004459b733b 100644 --- a/src/site/asciidoc/users/index.adoc +++ b/src/site/asciidoc/users/index.adoc @@ -17,7 +17,7 @@ :imagesdir: ../images/ :icons: font -== Users += Users This part of the PLC4X website is dedicated to people wanting to use Apache PLC4X. diff --git a/src/site/asciidoc/users/industry40.adoc b/src/site/asciidoc/users/industry40.adoc index a918c8cbd3d..5c6243df457 100644 --- a/src/site/asciidoc/users/industry40.adoc +++ b/src/site/asciidoc/users/industry40.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Industry 4.0 with Apache += Industry 4.0 with Apache Since the introduction of programmable logic controllers in the production industry in the early 80s, they have been the core of almost every piece of production machinery. @@ -32,7 +32,7 @@ Most of the biggest changes in how we create modern IT systems is a result of th Unfortunately the production industry has been missing a lot of this innovation. Only a small number of companies today use open source software in their production systems. -=== Benefits of using open source +== Benefits of using open source The benefit of using open source could be huge: @@ -42,21 +42,21 @@ The benefit of using open source could be huge: * Improved Security * Great Cost reduction -==== Increased Flexibility +=== Increased Flexibility If a company had decided to use PLCs and control systems of a certain vendor, it is almost impossible to change this decision. This reduces the options available when adding new machinery or replacing existing ones. Technologically speaking, also the company can only use the options and solution it's vendor is able to provide. -==== Increased Stability +=== Increased Stability Current control systems are usually based on the concept of "backup systems". If the main control system fails, all activity is switched to the standby system. When using modern public- or private cloud systems, there is no need for a backup system, because the cluster is designed in a way that it can live with the failure of most of its hosts before loosing the ability to function. -==== Increased Extendability +=== Increased Extendability From the perspective of designing and scaling the IT infrastructure: If a control system was designed to handle the current size of plant, for cost reasons the IT infrastructure isn't designed to handle much more than that. @@ -65,14 +65,14 @@ Now if the plant should be extended in the future, extending it's control-system By utilizing modern virtualization frameworks, extending the existing cloud solution, would only require adding more compute resources, by adding more systems to the cluster and it should be possible to extend the existing system without problems. If the company decided to utilize a public cloud provider, it makes things even simpler, as it would only require booking more resources. -==== Improved Security +=== Improved Security This is probably one of the most concerning aspects of modern production control systems. Right now, in order to run these systems, a lot of the most popular solutions require companies to run not up to date systems. If applying all updates, the company is risking either loss of commercial support or even loss of functionality. Therefore an attacker can probably be certain to be able to exploit certain vulnerabilities just by knowing the type and version of the used control system. -==== Cost Reduction +=== Cost Reduction Well the probably biggest and most obvious cost reduction factor is definitely, that if the software you are using is free, you will not have to pay for it. @@ -80,7 +80,7 @@ Additionally, the ability to get the computing power of one insanely expensive s Being freed of the requirement to stick to the products of one vendor alone and to be able to choose the technology and the vendor of used systems freely will definitely also reduce costs. -=== Options to communicating with PLCs +== Options to communicating with PLCs In general there are two options for communicating with industrial PLCs: diff --git a/src/site/asciidoc/users/integrations/apache-calcite.adoc b/src/site/asciidoc/users/integrations/apache-calcite.adoc index e8a37d02ea7..83c0d31c21c 100644 --- a/src/site/asciidoc/users/integrations/apache-calcite.adoc +++ b/src/site/asciidoc/users/integrations/apache-calcite.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Apache Calcite += Apache Calcite https://calcite.apache.org/[Apache Calcite] Standard SQL diff --git a/src/site/asciidoc/users/integrations/apache-camel.adoc b/src/site/asciidoc/users/integrations/apache-camel.adoc index b65f751ef99..31e0026e70e 100644 --- a/src/site/asciidoc/users/integrations/apache-camel.adoc +++ b/src/site/asciidoc/users/integrations/apache-camel.adoc @@ -17,6 +17,6 @@ :imagesdir: ../../images/ :icons: font -== Apache Camel += Apache Camel The Camel Component for PLC4X allows you to create routes using the PLC4X API to read from a PLC device or write to it. This component is now maintained within the https://camel.apache.org/components/next/plc4x-component.html[Apache Camel] Project. diff --git a/src/site/asciidoc/users/integrations/apache-edgent.adoc b/src/site/asciidoc/users/integrations/apache-edgent.adoc index bfc713e1c4f..3c90fb4ab43 100644 --- a/src/site/asciidoc/users/integrations/apache-edgent.adoc +++ b/src/site/asciidoc/users/integrations/apache-edgent.adoc @@ -17,4 +17,4 @@ :imagesdir: ../../images/ :icons: font -== Apache Edgent (Retired) += Apache Edgent (Retired) diff --git a/src/site/asciidoc/users/integrations/apache-iotdb.adoc b/src/site/asciidoc/users/integrations/apache-iotdb.adoc index 8c069968098..5e1a431f637 100644 --- a/src/site/asciidoc/users/integrations/apache-iotdb.adoc +++ b/src/site/asciidoc/users/integrations/apache-iotdb.adoc @@ -17,13 +17,13 @@ :imagesdir: ../../images/ :icons: font -== Apache IotDB += Apache IotDB Apache IoTDB is database for storing time serie data. Therefore, it can be a good solution for managing the data which is collected by PLC4x. -=== Data Model (Concept) +== Data Model (Concept) Given a PLC address and some fields, we can consider the PLC as a `device` in IoTDB, and each field as a `measurement` in IoTDB. A couple of PLCs form a `storage group`. @@ -32,7 +32,7 @@ For example, there is a virtual storage group `mi`, and a PLC `d1`, which has on Then, in IoTDB, we can get a time series like `root.mi.d1.foo` (or `root.mi.d1.RANDOM_foo_Integer`, as you like). Then, we can write data into IoTDB using JDBC with SQL or native API called session API. -=== Example +== Example https://github.com/apache/plc4x-extras/tree/develop/plc4j/examples/hello-integration-iotdb shows an example to collect data using PLC4x and then writing data to IoTDB. diff --git a/src/site/asciidoc/users/integrations/apache-kafka.adoc b/src/site/asciidoc/users/integrations/apache-kafka.adoc index 6a0a244573f..00469f8882f 100644 --- a/src/site/asciidoc/users/integrations/apache-kafka.adoc +++ b/src/site/asciidoc/users/integrations/apache-kafka.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== https://kafka.apache.org/[Apache Kafka] += https://kafka.apache.org/[Apache Kafka] Apache Kafka is an open-source distributed event streaming platform used by thousands of companies for high-performance data pipelines, streaming analytics, data integration, and diff --git a/src/site/asciidoc/users/integrations/apache-nifi.adoc b/src/site/asciidoc/users/integrations/apache-nifi.adoc index abe7bd365dd..97ba3709577 100644 --- a/src/site/asciidoc/users/integrations/apache-nifi.adoc +++ b/src/site/asciidoc/users/integrations/apache-nifi.adoc @@ -17,13 +17,13 @@ :imagesdir: ../../images/ :icons: font -== Apache NiFi += Apache NiFi Apache NiFi allows creating systems that process data around the concept of data-streams. Apache PLC4X provides both `Source` as well as `Sink` processors for accessing data in PLCs or writing data to them. -=== Setting Up NiFi +== Setting Up NiFi Even if the documentation of NiFi states it works with any Java version above 1.8, this is not quite true. @@ -40,7 +40,7 @@ As soon as you have started NiFi using the `nifi.sh run` or `run-nifi.bat` the W It might take a few seconds for the Web-UI to show up ... so if you're getting errors in the browser, give it some time to start. ==== -=== Enabling PLC4X Processors in NiFi +== Enabling PLC4X Processors in NiFi In order to enable `Apache PLC4X` support in `Apache NiFi` all you need to do, is to copy our `nar` archive into the Nifi installations `lib` directory. @@ -50,7 +50,7 @@ Or you can download a released version from Maven central: https://search.maven. image::integrations/nifi/empty-nifi-flow.png[] -=== Using a PLC4X Source Processor in NiFi +== Using a PLC4X Source Processor in NiFi Add a PLC4X Source processor to the canvas, click on the `Add processor` button and drag it into the canvas. @@ -136,7 +136,7 @@ Now you should see an increasing number at the `Out` of the PLC4X Source and on image::integrations/nifi/running-flow.png[] -=== Enabling debugging +== Enabling debugging In order to be able to debug the PLC4X, please edit the `bin/nifi.sh` (On Mac & Linux) and comment in the line: diff --git a/src/site/asciidoc/users/integrations/apache-streampipes.adoc b/src/site/asciidoc/users/integrations/apache-streampipes.adoc index 7bf1ae05a9d..4e854529194 100644 --- a/src/site/asciidoc/users/integrations/apache-streampipes.adoc +++ b/src/site/asciidoc/users/integrations/apache-streampipes.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Apache StreamPipes += Apache StreamPipes https://streampipes.apache.org/docs/pe/org.apache.streampipes.connect.iiot.adapters.plc4x.s7[PLC4X S7 Adapter,opts=nofollow] diff --git a/src/site/asciidoc/users/integrations/eclipse-ditto.adoc b/src/site/asciidoc/users/integrations/eclipse-ditto.adoc index 4413b819820..e70e744225d 100644 --- a/src/site/asciidoc/users/integrations/eclipse-ditto.adoc +++ b/src/site/asciidoc/users/integrations/eclipse-ditto.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Eclipse Ditto += Eclipse Ditto … where IoT devices and their digital twins get together https://www.eclipse.org/ditto/ diff --git a/src/site/asciidoc/users/integrations/eclipse-milo.adoc b/src/site/asciidoc/users/integrations/eclipse-milo.adoc index 6107c9d82f9..33b0b42557e 100644 --- a/src/site/asciidoc/users/integrations/eclipse-milo.adoc +++ b/src/site/asciidoc/users/integrations/eclipse-milo.adoc @@ -15,12 +15,12 @@ // limitations under the License. // -== Introduction += Introduction The PLC4X OPC UA server integration is based around the Eclipse Milo OPC UA server. It uses PLC4X to communicate with industrial devices effectively acting as a industrial OPC UA gateway. -== Building the server += Building the server The OPC UA server can be built using maven as part of the PLC4X build. ``` @@ -30,7 +30,7 @@ mvn install This creates a target directory within plc4x/plc4j/integrations/opcua-server containing a jar file which is the main java executable. -== Configuration File += Configuration File The config.yml file is used to configure the server. The following root level settings should be configured. @@ -93,7 +93,7 @@ tcpPort: 12687 httpPort: 8445 ``` -== Running += Running To run the java executable execute:- ``` diff --git a/src/site/asciidoc/users/integrations/index.adoc b/src/site/asciidoc/users/integrations/index.adoc index f9ac3beba90..6e50951096b 100644 --- a/src/site/asciidoc/users/integrations/index.adoc +++ b/src/site/asciidoc/users/integrations/index.adoc @@ -17,4 +17,4 @@ :imagesdir: ../../images/ :icons: font -== Integrations += Integrations diff --git a/src/site/asciidoc/users/issues.adoc b/src/site/asciidoc/users/issues.adoc index 13b44a1f377..1000e363e31 100644 --- a/src/site/asciidoc/users/issues.adoc +++ b/src/site/asciidoc/users/issues.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Bug & Issue Tracker += Bug & Issue Tracker Our bug & issue tracker is Github-Issues. diff --git a/src/site/asciidoc/users/preparing-issues.adoc b/src/site/asciidoc/users/preparing-issues.adoc index 1a73e204c7a..e94365f7f8f 100644 --- a/src/site/asciidoc/users/preparing-issues.adoc +++ b/src/site/asciidoc/users/preparing-issues.adoc @@ -15,5 +15,5 @@ // limitations under the License. // -== Preparing Issues & Bug Reports += Preparing Issues & Bug Reports diff --git a/src/site/asciidoc/users/protocols/ab-eth.adoc b/src/site/asciidoc/users/protocols/ab-eth.adoc index 391fea2b361..23a8c9583e7 100644 --- a/src/site/asciidoc/users/protocols/ab-eth.adoc +++ b/src/site/asciidoc/users/protocols/ab-eth.adoc @@ -17,8 +17,8 @@ :imagesdir: ../../images/users/protocols :icons: font -== AB-ETH += AB-ETH -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/ab-eth.adoc[] diff --git a/src/site/asciidoc/users/protocols/ads.adoc b/src/site/asciidoc/users/protocols/ads.adoc index db3aedcc3d1..44162573819 100644 --- a/src/site/asciidoc/users/protocols/ads.adoc +++ b/src/site/asciidoc/users/protocols/ads.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/users/protocols :icons: font -== ADS (Automation Device Specification) += ADS (Automation Device Specification) image::ads_banner.png[banner,512,167] The ADS (automation device specification) describes a device-independent and fieldbus independent interface for communication between Beckhoff automation devices running TwinCAT and other devices implementing this interface. https://www.home-assistant.io/integrations/ads/ Source (accessed 7 August 2022) @@ -26,13 +26,13 @@ ADS device concept: https://infosys.beckhoff.com/english.php?content=../content/ Specification for ADS devices: https://infosys.beckhoff.com/english.php?content=../content/1033/ams_nat/4275563275.html&id= Source (accessed 7 August 2022) -=== Structure AMS/TCP Packet +== Structure AMS/TCP Packet ADS (Automation Device Specification) is the TwinCAT communication protocol that specifies the interaction between two ADS devices. For example, it defines what operations can be executed on another ADS device, what parameters are necessary for that and what return value is sent after execution. AMS (Automation Message Specification) specifies the exchange of the ADS data. A major component of the communication protocol is the AmsNetId. This is specified in the AMS/ADS package for the source and target device. An ADS device can be explicitly addressed using the AmsNetId. Source https://infosys.beckhoff.com/english.php?content=../content/1033/ams_nat/4275563275.html&id= (accessed 7 August 2022) -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/ads.adoc[] @@ -56,7 +56,7 @@ include::../../../plc4j/drivers/all/src/site/generated/ads.adoc[] |=== -=== More details on +== More details on For details about the protocol look here: http://www.beckhoff.com/ & (German Handbook: https://download.beckhoff.com/download/Document/automation/twincat3/TwinCAT_3_ADS_INTRO_DE.pdf) diff --git a/src/site/asciidoc/users/protocols/bacnet.adoc b/src/site/asciidoc/users/protocols/bacnet.adoc index e49888e3ab1..b05a3d87f53 100644 --- a/src/site/asciidoc/users/protocols/bacnet.adoc +++ b/src/site/asciidoc/users/protocols/bacnet.adoc @@ -17,13 +17,13 @@ :imagesdir: ../../images/users/protocols :icons: font -== BACnet/IP += BACnet/IP image::bacnet_banner.png[banner,512,167] -=== BACnet (Building Automation and Control Networks) +== BACnet (Building Automation and Control Networks) -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/bacnet-ip.adoc[] @@ -47,6 +47,6 @@ include::../../../plc4j/drivers/all/src/site/generated/bacnet-ip.adoc[] |=== -=== More details on +== More details on http://www.bacnet.org/[BACnet - A Data Communication Protocol for Building Automation and Control Networks] \ No newline at end of file diff --git a/src/site/asciidoc/users/protocols/c-bus.adoc b/src/site/asciidoc/users/protocols/c-bus.adoc index a2ddbbd3450..478c65c1f11 100644 --- a/src/site/asciidoc/users/protocols/c-bus.adoc +++ b/src/site/asciidoc/users/protocols/c-bus.adoc @@ -17,8 +17,8 @@ :imagesdir: ../../images/users/protocols :icons: font -== C-Bus += C-Bus -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/c-bus.adoc[] \ No newline at end of file diff --git a/src/site/asciidoc/users/protocols/can.adoc b/src/site/asciidoc/users/protocols/can.adoc index 062dbaa2c98..159be9ec10a 100644 --- a/src/site/asciidoc/users/protocols/can.adoc +++ b/src/site/asciidoc/users/protocols/can.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/users/protocols :icons: font -== CAN Bus Driver Adapter += CAN Bus Driver Adapter image::can_banner.png[banner,512,167] @@ -25,7 +25,7 @@ The CAN Bus driver is a special kind of driver which does not bring any logic. Its responsibility is to combine link:../transports/can.html[CAN transport facade] and custom application layer protocols. In this way, the protocol can use its own "root frame" type which is not delivered from CAN bus frame. -=== More details on the driver +== More details on the driver The `CANDriverAdapter` is a full implementation of Apache PLC4X API. Under the hood adapter will forward all operations to delegate driver. diff --git a/src/site/asciidoc/users/protocols/canopen.adoc b/src/site/asciidoc/users/protocols/canopen.adoc index 1f7345f0a7c..ee4ad82a3c8 100644 --- a/src/site/asciidoc/users/protocols/canopen.adoc +++ b/src/site/asciidoc/users/protocols/canopen.adoc @@ -17,13 +17,14 @@ :imagesdir: ../../images/users/protocols :icons: font -== CANopen += CANopen image::can_banner.png[banner,512,167] -=== CAN in Automation + +== CAN in Automation CANopen is a specific protocol built on top of CAN bus. -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/canopen.adoc[] @@ -46,7 +47,7 @@ include::../../../plc4j/drivers/all/src/site/generated/canopen.adoc[] - It is possible to subscribe to CANopen NMT messages. |=== -=== More details on the driver +== More details on the driver CAN, despite (or due) to its popularity has ambiguous meaning. There are multiple articles and sources which attempts to give introduction, yet very few of them is consistent between each other. @@ -75,7 +76,7 @@ The CANopen specification defines Object Dictionary (OD). This driver does honor OD structure through usage of index and sub index for addressing fields. It does not ship Electronic Data Sheet (EDS) parser leaving it for applications who wish to utilize it. -=== Address Format +== Address Format CANopen specification defines several groups of addresses dedicated to certain kind of operations. Critical services and message exchanges related with them have lower identifiers making them wining eventual bus access. diff --git a/src/site/asciidoc/users/protocols/ctrlx.adoc b/src/site/asciidoc/users/protocols/ctrlx.adoc index 872b743a66e..e89db5f0eeb 100644 --- a/src/site/asciidoc/users/protocols/ctrlx.adoc +++ b/src/site/asciidoc/users/protocols/ctrlx.adoc @@ -17,8 +17,8 @@ :imagesdir: ../../images/users/protocols :icons: font -== CtlrX += CtlrX -=== Connection String Options +== Connection String Options //include::../../../plc4j/drivers/all/src/site/generated/ctrlx.adoc[] \ No newline at end of file diff --git a/src/site/asciidoc/users/protocols/deltav.adoc b/src/site/asciidoc/users/protocols/deltav.adoc index 2d3298de94c..456487d70a3 100644 --- a/src/site/asciidoc/users/protocols/deltav.adoc +++ b/src/site/asciidoc/users/protocols/deltav.adoc @@ -17,4 +17,4 @@ :imagesdir: ../../images/users/protocols :icons: font -== DeltaV += DeltaV diff --git a/src/site/asciidoc/users/protocols/df1.adoc b/src/site/asciidoc/users/protocols/df1.adoc index c93ff1d90e2..19e04d7b84b 100644 --- a/src/site/asciidoc/users/protocols/df1.adoc +++ b/src/site/asciidoc/users/protocols/df1.adoc @@ -17,4 +17,4 @@ :imagesdir: ../../images/users/protocols :icons: font -== DF1 += DF1 diff --git a/src/site/asciidoc/users/protocols/eip.adoc b/src/site/asciidoc/users/protocols/eip.adoc index f7bc9978792..0ae30b47d0d 100644 --- a/src/site/asciidoc/users/protocols/eip.adoc +++ b/src/site/asciidoc/users/protocols/eip.adoc @@ -17,13 +17,13 @@ :imagesdir: ../../images/users/protocols :icons: font -== EtherNet/IP += EtherNet/IP -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/eip.adoc[] -=== Address Format +== Address Format To read and write data to a PLC4X device, the EtherNet/IP driver uses symbolic segments. This is used to refer to objects through their symbolic names. This makes reading data a lot easier, as you do not @@ -44,7 +44,7 @@ To read and write data to a PLC4X device, the EtherNet/IP driver uses symbolic s |DataType (writing) |Specify the Data-type of the value you want to write (mandatory) |=== -==== Data Types +=== Data Types [cols="2" ,options="header"] |=== |To store a|Use this data type diff --git a/src/site/asciidoc/users/protocols/firmata.adoc b/src/site/asciidoc/users/protocols/firmata.adoc index dea5f9ec21a..42ef1c9601b 100644 --- a/src/site/asciidoc/users/protocols/firmata.adoc +++ b/src/site/asciidoc/users/protocols/firmata.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/users/protocols :icons: font -== Firmata += Firmata The Firmata protocol is based on the MIDI protocol used for communicating with musical equipment. @@ -25,7 +25,7 @@ It is also one of the most widely used protocols for communication with Arduino This driver is built to be compatible with the `StandardFirmata Arduino Sketch` which can be found https://github.com/firmata/arduino/blob/master/examples/StandardFirmata/StandardFirmata.ino[here] (Version last changed on August 17th, 2017) -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/firmata.adoc[] @@ -46,13 +46,13 @@ include::../../../plc4j/drivers/all/src/site/generated/firmata.adoc[] NOTE: When subscribing to pins, these are configured to become read pins. When writing to digital pins, these are configured to become output pins. However, writing to pins for which a subscription exists, an exception will be thrown. In order to write to previously subscribed pins, all subscriptions for this have to be cancelled first. -=== Individual Resource Address Format +== Individual Resource Address Format Similar to the Modbus protocol, the Firmata protocol support Boolean and Short values. Booleans are used for the digital IO pins and short values for the analog inputs. -==== Binary Addresses +=== Binary Addresses The full format for a digital address has the following format: @@ -77,7 +77,7 @@ A normal `Arduino Uno` is equipped with 14 digital inputs: 0-13 WARNING: However in case of using the serial port (which will always be the case when using this driver), the pins 0 and 1 are the `RX` and `TX` pins of the serial port and can't be used. -==== Analog Addresses +=== Analog Addresses The full format for an analog address is as follows: diff --git a/src/site/asciidoc/users/protocols/genericcan.adoc b/src/site/asciidoc/users/protocols/genericcan.adoc index 5633f2751f8..da07b93742a 100644 --- a/src/site/asciidoc/users/protocols/genericcan.adoc +++ b/src/site/asciidoc/users/protocols/genericcan.adoc @@ -17,16 +17,16 @@ :imagesdir: ../../images/users/protocols :icons: font -== Generic CAN += Generic CAN -=== CAN Bus semantics +== CAN Bus semantics This driver is a generic purpose driver. It allows implementing a basic CAN bus listening or writing scenarios. CAN bus open is a specific protocol built on top of CAN bus. -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/genericcan.adoc[] @@ -46,7 +46,7 @@ include::../../../plc4j/drivers/all/src/site/generated/genericcan.adoc[] |=== -=== More details on the driver +== More details on the driver Given popularity of CAN in multiple market segments there is variety of protocols which can't be published. Also, for many cases which are data acquisition oriented it is necessary to tap only parts of communications. @@ -55,7 +55,7 @@ This driver allows to model incoming and outgoing communication using plain Apac The written CAN data is constructed from fields submitted via write request builder. The receiving data is transformed in similar fashion, based on subscribed fields. -==== Address format +=== Address format [cols="1,1a,1a,2a"] |=== diff --git a/src/site/asciidoc/users/protocols/iec-60870.adoc b/src/site/asciidoc/users/protocols/iec-60870.adoc index 5a87891f45c..747c79cf4a1 100644 --- a/src/site/asciidoc/users/protocols/iec-60870.adoc +++ b/src/site/asciidoc/users/protocols/iec-60870.adoc @@ -17,8 +17,8 @@ :imagesdir: ../../images/users/protocols :icons: font -== IEC-60870 += IEC-60870 -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/iec-60870-5-104.adoc[] \ No newline at end of file diff --git a/src/site/asciidoc/users/protocols/index.adoc b/src/site/asciidoc/users/protocols/index.adoc index 5ad3536941c..4af0ec637fc 100644 --- a/src/site/asciidoc/users/protocols/index.adoc +++ b/src/site/asciidoc/users/protocols/index.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Current language support for protocols += Current language support for protocols :icons: font |=== |Protocol | C | C# | Go | Java | Python @@ -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: @@ -162,7 +169,7 @@ Legend: - icon:check[role="red"] Not implemented yet - icon:question[role="red"] Unsure -== Features += Features :icons: font The following table contains a list of operations and the protocols that support them: @@ -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/knxnetip.adoc b/src/site/asciidoc/users/protocols/knxnetip.adoc index 84d465031e7..4e2522668a9 100644 --- a/src/site/asciidoc/users/protocols/knxnetip.adoc +++ b/src/site/asciidoc/users/protocols/knxnetip.adoc @@ -17,14 +17,14 @@ :imagesdir: ../../images/users/protocols :icons: font -== KNXnet/IP += KNXnet/IP image::knx_banner.png[banner,512,167] -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/knxnet-ip.adoc[] -=== Individual Resource Address Format +== Individual Resource Address Format KNX Addresses usually have one of the following structures: diff --git a/src/site/asciidoc/users/protocols/logix.adoc b/src/site/asciidoc/users/protocols/logix.adoc index db60c5f32dd..77458b6b63b 100644 --- a/src/site/asciidoc/users/protocols/logix.adoc +++ b/src/site/asciidoc/users/protocols/logix.adoc @@ -17,15 +17,15 @@ :imagesdir: ../../images/users/protocols :icons: font -== Logix += Logix -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/logix.adoc[] -=== Individual Resource Address Format +== Individual Resource Address Format -==== Connection String +=== Connection String Logix has the following connection string format:- ---- @@ -38,7 +38,7 @@ logix:tcp://127.0.0.1:502?communicationPath=[1,1] Note the port and option fields are optional. -==== General Format +=== General Format In general all Logix addresses have this format: @@ -50,7 +50,7 @@ If the array-size part is omitted, the size-default of `1` is assumed. If the data-type part is omitted, the data type from the controller is used The address format matches that found in the controller. -==== Data Types +=== Data Types The following data types are supported diff --git a/src/site/asciidoc/users/protocols/modbus.adoc b/src/site/asciidoc/users/protocols/modbus.adoc index 5f210f50c65..4c0d2bd46bc 100644 --- a/src/site/asciidoc/users/protocols/modbus.adoc +++ b/src/site/asciidoc/users/protocols/modbus.adoc @@ -17,23 +17,23 @@ :imagesdir: ../../images/users/protocols :icons: font -== Modbus (TCP/UDP/Serial) += Modbus (TCP/UDP/Serial) -=== Connection String Options +== Connection String Options -==== Modbus TCP +=== Modbus TCP include::../../../plc4j/drivers/all/src/site/generated/modbus-tcp.adoc[] -==== Modbus RTU +=== Modbus RTU include::../../../plc4j/drivers/all/src/site/generated/modbus-rtu.adoc[] -==== Modbus ASCII +=== Modbus ASCII include::../../../plc4j/drivers/all/src/site/generated/modbus-ascii.adoc[] -=== Supported Operations +== Supported Operations [cols="2,2a,5a"] |=== @@ -48,9 +48,9 @@ include::../../../plc4j/drivers/all/src/site/generated/modbus-ascii.adoc[] 2+| `write` |=== -=== Individual Resource Address Format +== Individual Resource Address Format -==== Connection String +=== Connection String Modbus has the following connection string format:- ---- @@ -63,7 +63,7 @@ modbus-tcp:tcp://127.0.0.1:502 Note the transport, port and option fields are optional. -==== General Format +=== General Format In general all Modbus addresses have this format: @@ -86,7 +86,7 @@ Specifying this value overrides value of `default-unit-id` parameter specified a ---- With this, can the default byte-order be overridden on a per-tag basis. If not provided the default-byte-order from the connection string is used, or BIG_ENDIAN, if this is also not provided. -==== Memory Areas +=== Memory Areas There are a number of memory areas defined in the Modbus specification. @@ -120,7 +120,7 @@ Address 610000 is then the first address in the second file record and so on. It Using the extended-register: format you are able to reference all of these, if the shorter format is used then it is limited to 699999. This memory area starts at address 0. -==== Data Types +=== Data Types The following data types are supported @@ -142,7 +142,7 @@ The following data types are supported - CHAR (char) - WCHAR (2 byte char) -==== Some useful tips +=== Some useful tips Most memory areas start at address 1, except for the extended register area which starts at 0. These are both mapped to 0x0000 when it is sent in the Modbus protocol. @@ -161,7 +161,7 @@ The following Modbus function codes are supported:- - 0x14 (Read File Record)(Extended Register Read) - 0x15 (Write File Record)(Extended Register Write) -==== Examples +=== Examples To read 10 holding registers starting at address 20 and parse as Unsigned Integers the following examples are all valid. diff --git a/src/site/asciidoc/users/protocols/opcua.adoc b/src/site/asciidoc/users/protocols/opcua.adoc index 4116697e745..dd5bc8dc469 100644 --- a/src/site/asciidoc/users/protocols/opcua.adoc +++ b/src/site/asciidoc/users/protocols/opcua.adoc @@ -17,9 +17,9 @@ :imagesdir: ../../images/users/protocols :icons: font -== OPC UA += OPC UA -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/opcua.adoc[] @@ -34,7 +34,7 @@ include::../../../plc4j/drivers/all/src/site/generated/opcua.adoc[] |=== -=== Connection String +== Connection String The OPC UA drivers uses the connection string @@ -54,7 +54,7 @@ opcua:tcp://127.0.0.1:12686?discovery=true&username=admin&password=password Note the transport, port and options fields are optional. -=== Secure communication +== Secure communication The secure channel implementation within Apache PLC4X project have been tested against existing open source server implementations. This includes Eclipse Milo (all modes) as well as OPC Foundation .NET server (except `Basic128Rsa15`). Manual tests proven that driver is able to communicate with OPC UA server launched on PLCs as well as commercial simulators. @@ -69,7 +69,7 @@ By default, this option is set to `SIGN_ENCRYPT` which imposes high security set In case when additional diagnostics is needed payloads has to be traced through TRACE level log entries. The `SIGN` mode gives possibility o browse packets in tools such wireshark. -==== Certificate verification +=== Certificate verification The OPC UA specification defines its own procedures for certificate validation. In order to simplify implementation by default server certificate validation is relaxed. Unless explicitly disabled through configuration of `trustStoreFile` all server certificates will be accepted without validation. @@ -77,7 +77,7 @@ Unless explicitly disabled through configuration of `trustStoreFile` all server In case when secure communication is enabled the `trustStoreFile` option might be used to point certificates which client should accept. The acceptance rely on regular TLS checks (expiry date, certificate path etc.), does not validate OPC UA specific parts such as application URI. -==== Negotiation procedure +=== Negotiation procedure Depending on settings driver might or might not attempt to discover endpoints from remote server. In case when `discovery` option is set to `true` driver will look up server certificate through connection attempt. The discovery option also enables checks of server endpoints for matching security settings. @@ -94,7 +94,7 @@ Usual values of `sendBufferSize` and `receiveBufferSize` PLC devices remain at 8 NOTE: Due to lack of complete implementation of negotiation and chunking logic the OPC UA driver prior Apache PLC4X 0.11 release could supply calls exceeding server limits. -=== Address Format +== Address Format To read, write and subscribe to data, the OPC UA driver uses the variable declaration string of the OPC UA server it is connecting to. It includes the namespace(`ns`) of the hierarchy tree followed by the type of identifier string(`s`), numeric(`i`), @@ -107,7 +107,7 @@ ns={namespace-index};[s|i|g|b]={Identifier};{Data Type} ---- -==== Data Types +=== Data Types The following data types are supported @@ -132,7 +132,7 @@ The following data types are supported - WSTRING (utf-16) -==== Example of a valid OPC UA address: +=== Example of a valid OPC UA address: The following are examples of valid addresses @@ -154,12 +154,12 @@ ns=2;g=09087e75-8e5e-499b-954f-f2a8624db28a;REAL Note the Identifiers `s`,`i`,`b` and `g` specify the format of the address not the data type of the returned value. -=== Some useful tips +== Some useful tips The namespace (e.g. `ns=2`) within the address is specific to the server you are connecting to. -=== More details on OPC UA +== More details on OPC UA https://opcfoundation.org/about/opc-technologies/opc-ua/[OPC UA] The OPC Unified Architecture (UA), released in 2008, is a platform independent service-oriented architecture that integrates all the functionality of the individual OPC Classic specifications into one extensible framework. diff --git a/src/site/asciidoc/users/protocols/open-protocol.adoc b/src/site/asciidoc/users/protocols/open-protocol.adoc index e9d87225792..eec5bbd7092 100644 --- a/src/site/asciidoc/users/protocols/open-protocol.adoc +++ b/src/site/asciidoc/users/protocols/open-protocol.adoc @@ -17,8 +17,8 @@ :imagesdir: ../../images/users/protocols :icons: font -== Open-Protocol (Torque-Tools) += Open-Protocol (Torque-Tools) -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/open-protocol.adoc[] \ No newline at end of file diff --git a/src/site/asciidoc/users/protocols/plc4x.adoc b/src/site/asciidoc/users/protocols/plc4x.adoc index 82cda1c070c..8a765cbf9e8 100644 --- a/src/site/asciidoc/users/protocols/plc4x.adoc +++ b/src/site/asciidoc/users/protocols/plc4x.adoc @@ -17,13 +17,13 @@ :imagesdir: ../../images/users/protocols :icons: font -== PLC4X (Proxy) (TCP) += PLC4X (Proxy) (TCP) -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/plc4x.adoc[] -=== Connection String Options +== Connection String Options [cols="2,2a,5a"] |=== @@ -39,9 +39,9 @@ include::../../../plc4j/drivers/all/src/site/generated/plc4x.adoc[] |=== -=== Individual Resource Address Format +== Individual Resource Address Format -==== Connection String +=== Connection String The `plc4x` protocol connection has the following connection string format:- ---- @@ -55,7 +55,7 @@ plc4x://127.0.0.1?remote-connection-string=simulated%3A%2F%2Flocalhost Note the transport, port and option fields are optional. The remote connection string: `simulated://localhost` is encoded as `simulated%3A%2F%2Flocalhost` -==== General Format +=== General Format The address format is simply the address format of the used remote connection. So if you specify a remote connection as `simulated`, please follow the address format of that driver. \ No newline at end of file diff --git a/src/site/asciidoc/users/protocols/profinet.adoc b/src/site/asciidoc/users/protocols/profinet.adoc index dcecdf256ef..0239d2dd3a4 100644 --- a/src/site/asciidoc/users/protocols/profinet.adoc +++ b/src/site/asciidoc/users/protocols/profinet.adoc @@ -17,12 +17,12 @@ :imagesdir: ../../images/users/protocols :icons: font -== Profinet (In Development) += Profinet (In Development) The PROFINET driver implements a class 3 real time controller. Which is able to communicate with multiple devices on the same network segment. -=== Connection String Options +== Connection String Options On linux as the Java executable won't have permission to capture raw packets, this needs to be enabled via:- ---- @@ -42,7 +42,7 @@ include::../../../plc4j/drivers/all/src/site/generated/profinet.adoc[] |=== -=== Connection String +== Connection String The Profinet driver uses the connection string @@ -66,7 +66,7 @@ profinet:raw://127.0.0.1?gsddirectory=/Profinet/GSD&devices=[[test-device-1,MOD_ Note the transport, port fields shouldn't have to be changed -=== Address Format +== Address Format The format of address will be in the format. ---- @@ -83,7 +83,7 @@ device-1.1.1.DIGITAL_INPUT.0.1:BOOL Note:- -==== Data Types +=== Data Types The following data types are supported @@ -108,7 +108,7 @@ The following data types are supported - WSTRING (utf-16) -=== Some useful tips +== Some useful tips Although the GSD file contains all the information needed to configure which data will be available from a device. The easiest approach is to use the browsing function of the Profinet driver to return a list of all available tags. diff --git a/src/site/asciidoc/users/protocols/s7.adoc b/src/site/asciidoc/users/protocols/s7.adoc index 2acd668d43b..650a07a4b60 100644 --- a/src/site/asciidoc/users/protocols/s7.adoc +++ b/src/site/asciidoc/users/protocols/s7.adoc @@ -21,10 +21,10 @@ //:coderay-linenums-mode: inline //:coderay-css: class -== S7 (Step7) += S7 (Step7) image::s7_banner.png[banner,512,167] -=== Executive Summary +== Executive Summary This version of the S7 driver is aimed at exploiting the advanced features of the S7-300 and S7-400 controllers, as well as basic reading and writing functions for the S7-1200 and S7-1500 devices (PUT/GET functions). We hope in a short period of time to have the S7-Plus version, which should exploit the asynchronous functions of the S7-1500. @@ -49,7 +49,7 @@ Although this driver is developed using Siemens Hardware, it should be functiona NOTE: When trying to connect to a Siemens `LOGO` device, it is important to add one connection option, as Siemens seems to have only partially implemented the protocol, the device simply terminates the connection as soon as our driver tried to read the SZL table in order to find out which type of S7 device it is talking to. This can be disabled by passing in the type of PLC. For a Siemens LOGO device therefore please add `?controller-type=LOGO` to the connection string. -=== Regarding the Support +== Regarding the Support It is typical within the decision-making cycle in an automation project to know who and how much the support of the tools that will be used in the control architecture will cost. @@ -57,7 +57,7 @@ PLC4X support is on our development list (dev@plc4x.apache.org) where we will gl If your company requires commercial support, companies that directly or indirectly support the drivers and tools developed in PLC4X are published on our page. -=== Record of revisions made to the driver +== Record of revisions made to the driver [cols="1, 2,2a,5a"] |=== @@ -67,9 +67,9 @@ If your company requires commercial support, companies that directly or indirect |=== -=== Connecting as easy as 1-2-3. +== Connecting as easy as 1-2-3. -==== ONE +=== ONE In PLC4X the URL philosophy is used as the data source for the connection for the specification of the driver and its connection parameters, this is almost a standard in network applications (pointing to the best practices). It is also possible to create an instance of the driver directly and assign its parameters with the typical "set" methods. @@ -89,7 +89,7 @@ image::s7_url.png[s7_url,,,,align="center"] The SCHEMA and DOMAINE NAME are almost standard for any URL and do not require further explanation. The PARAMETERS that define the behavior of the driver are defined in the following table. -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/s7.adoc[] @@ -114,7 +114,7 @@ include::../../../plc4j/drivers/all/src/site/generated/s7.adoc[] |=== -==== TWO +=== TWO After defining the URL, the connection is made. Driver selection from the URL is done via PLC4X's SPI support, so driver instantiation and mapping originating from the URL is done transparently by the Java SPI services. @@ -127,8 +127,8 @@ Any inconsistency in the URL definition will generate an exception that must be . . try { - PlcConnection connection = new DefaultPlcDriverManager().getConnection("s7://10.10.1.33?remote-rack=0&remote-slot=3&controller-type=S7_400"); //(2.1) - final PlcReadRequest.Builder subscription = connection.readRequestBuilder(); //(2.2) + PlcConnection connection new DefaultPlcDriverManager().getConnection("s7://10.10.1.33?remote-rack=0&remote-slot=3&controller-type=S7_400"); //(2.1) + final PlcReadRequest.Builder subscription connection.readRequestBuilder(); //(2.2) . . . @@ -140,7 +140,7 @@ In (2.1) the driver instance is created, you only have to ensure that the requir No problems? Then we are ready to configure and request the data that we require from the PLC. Let's go to step "three". -==== THREE +=== THREE By having the connection we can start building and executing our requests. @@ -151,9 +151,9 @@ By having the connection we can start building and executing our requests. . readrequest.addTagAddress("MySZL", "SZL_ID=16#0091;INDEX=16#0000"); //(3.1) - final PlcReadRequest rr = readrequest.build(); //(3.2) - final PlcReadResponse szlresponse = rr.execute().get(); //(3.3) - if (szlresponse.getResponseCode("MySZL") == PlcResponseCode.OK) {//(3.4) + final PlcReadRequest rr readrequest.build(); //(3.2) + final PlcReadResponse szlresponse rr.execute().get(); //(3.3) + if (szlresponse.getResponseCode("MySZL") = PlcResponseCode.OK) {//(3.4) } . . @@ -168,13 +168,13 @@ These steps are shown separately for ease of analysis, but can be simplified int A detailed explanation of the format for addressing PLCTags in the S7 driver will be given in the following sections. -=== Individual Resource Address Format +== Individual Resource Address Format When programming Siemens PLCs, usually the tool used to do that is called TIA Portal. The PLC4X S7 Driver is therefore sticking to the address format defined by this tool as it simplifies exchanging address information. -==== General Format +=== General Format In general all S7 addresses have this format: @@ -227,7 +227,7 @@ The shorter syntax looks like this: The S7 driver will handle both types of notation equally. -==== Memory Areas +=== Memory Areas The S7 driver currently allows access to the following memory areas. @@ -286,7 +286,7 @@ Not all S7 device types support the same full set of memory areas, so the last c |=== -==== Data Types +=== Data Types [cols="1,1,2,4,1,1"] |=== @@ -330,7 +330,7 @@ Not all S7 device types support the same full set of memory areas, so the last c -=== Actors participating in the communication process +== Actors participating in the communication process PLC programming in general is a Pandora's box! @@ -369,7 +369,7 @@ endbox . `CP`, the communications CP will depend on your architecture and requirements, for an S7-300 it will be a CP 343-1 or a CP 443-1 for an S7-400. -==== S7 Read/Write +=== S7 Read/Write @@ -408,7 +408,7 @@ OS --> PLC4X PLC4X --> App : to consumer .... -==== S7 Event Subscription +=== S7 Event Subscription The S7 driver allows the subscription to asynchronous events generated in the PLC. @@ -449,9 +449,9 @@ At the user application level `App`, you can use the PLC4X API to subscribe SCAN In the following sections we will describe in more detail the functionalities of each field. -==== SCAN Events +=== SCAN Events -==== Subscription to MODE events (S7ModeEvent). +=== Subscription to MODE events (S7ModeEvent). By subscribing to controller status changes or `MODE` events, the PLC status changes can be tracked. @@ -528,19 +528,19 @@ With the sequence diagram and the data structures that will be received by the a public class PLCEventModeSubscription { public static void main(String[] args) throws Exception { - try (PlcConnection connection = new PlcDriverManager() + try (PlcConnection connection new PlcDriverManager() .getConnection("s7://192.168.1.51?remote-rack=0&remote-slot=3&controller-type=S7_400")) { - final PlcSubscriptionRequest.Builder subscription = connection.subscriptionRequestBuilder(); // <01> + final PlcSubscriptionRequest.Builder subscription connection.subscriptionRequestBuilder(); // <01> subscription.addEventField("myMODE", "MODE"); - final PlcSubscriptionRequest sub = subscription.build(); + final PlcSubscriptionRequest sub subscription.build(); System.out.println("Query: " + sub.toString()); - final PlcSubscriptionResponse subresponse = sub.execute().get(); + final PlcSubscriptionResponse subresponse sub.execute().get(); - if (subresponse.getResponseCode("myMODE") == PlcResponseCode.OK) { //<04> + if (subresponse.getResponseCode("myMODE") = PlcResponseCode.OK) { //<04> PlcConsumerRegistration registerMode = subresponse .getSubscriptionHandle("myMODE") //<05> @@ -566,7 +566,7 @@ public class PLCEventModeSubscription { ---- -==== Subscription to SYS events (S7SysEvent) and USER events (S7UserEvent). +=== Subscription to SYS events (S7SysEvent) and USER events (S7UserEvent). System events allow to receive asynchronously any event that affects the operation of the controller, or any of its peripheral equipment that is capable of sending events through a PROFIBUS or Profinet fieldbus. @@ -711,17 +711,17 @@ Below is an example code for the subscription of events type *SYS*. [source,java] ---- public static void main(String[] args) throws Exception { - try (PlcConnection connection = new PlcDriverManager(). + try (PlcConnection connection new PlcDriverManager(). getConnection("s7://192.168.1.51?remote-rack=0&remote-slot=3&controller-type=S7_400")) { - final PlcSubscriptionRequest.Builder subscription = connection.subscriptionRequestBuilder(); //<01> + final PlcSubscriptionRequest.Builder subscription connection.subscriptionRequestBuilder(); //<01> subscription.addEventField("mySYS", "SYS"); - final PlcSubscriptionRequest sub = subscription.build(); + final PlcSubscriptionRequest sub subscription.build(); System.out.println("Query: " + sub.toString()); - final PlcSubscriptionResponse subresponse = sub.execute().get(); + final PlcSubscriptionResponse subresponse sub.execute().get(); PlcConsumerRegistration registerSys = subresponse @@ -751,17 +751,17 @@ And below is an example code for the subscription of events type *USR*. [source,java] ---- public static void main(String[] args) throws Exception { - try (PlcConnection connection = new PlcDriverManager(). + try (PlcConnection connection new PlcDriverManager(). getConnection("s7://192.168.1.51?remote-rack=0&remote-slot=3&controller-type=S7_400")) { - final PlcSubscriptionRequest.Builder subscription = connection.subscriptionRequestBuilder(); + final PlcSubscriptionRequest.Builder subscription connection.subscriptionRequestBuilder(); subscription.addEventField("myUSR", "USR"); - final PlcSubscriptionRequest sub = subscription.build(); + final PlcSubscriptionRequest sub subscription.build(); System.out.println("Query: " + sub.toString()); - final PlcSubscriptionResponse subresponse = sub.execute().get(); + final PlcSubscriptionResponse subresponse sub.execute().get(); PlcConsumerRegistration registerUsr = subresponse @@ -785,14 +785,14 @@ public static void main(String[] args) throws Exception { The Java code shows how to detect the type of event in an event type *SYS*. In the S7 driver, there is an enum object _S7DiagnosticEventId_(10) that allows us to identify which internal event of the *PLC(AS)* generated it and thus, through the interpretation of the INFO1 and INFO2 fields, determine the root cause of the event. -[NOTE, icon = s7_note.png] +[NOTE, icon s7_note.png] To date, the enum object _S7DiagnosticEventId_ contains a considerable amount of diagnostic values, it must be updated according to the new CPUs or firmware versions available. Unlike *SYS* events, *USR* events must be interpreted directly by the *App* application, so they are generally scheduled during the development phase of the *S7App* application. By having INFO1 and INFO2 in the *S7App* program, the user can transfer data associated with events, such as transitions between phases, events of diagnostic routines such as firts-out or the start or end of a batch process, all asynchronously. -==== Subscription to ALM type events (S7AlarmEvent). +=== Subscription to ALM type events (S7AlarmEvent). @@ -910,7 +910,7 @@ TODO: Field description TODO: Example code -==== TODO: Cyclic subscription (CYC). +=== TODO: Cyclic subscription (CYC). The cyclical subscription allows the acquisition of data in passive mode, that is, the data is sent from the PLC in a cyclical and synchronous way. @@ -943,7 +943,7 @@ The data transfer has three time bases: -==== SZL System Status List +=== SZL System Status List The system status list gives access to the operating data of the PLC, such as memory space, operating status, status of the control switches, as well as diagnostic data of expansion cards or decentralized peripherals, PROFIBUS or PROFINET . @@ -961,7 +961,7 @@ For a first approach to using system state lists a byte array to JSON notation p [NOTE,icon=s7_tip.png] Make use of the XXX document for a detailed explanation of each SZL, since as indicated, everything will depend on the hardware you have installed. -==== Notation for SZL request +=== Notation for SZL request The access to the SZL of the PLC is done as a read request, where the PLCTag is formed by two fields "SZL_ID" and "INDEX". @@ -1021,26 +1021,26 @@ public static void main(String[] args) throws Exception { System.out.println("URL: https://cache.industry.siemens.com/dl/files/604/44240604/att_67003/v1/s7sfc_en-EN.pdf"); System.out.println("******************************************************************************************"); - try (PlcConnection connection = new DefaultPlcDriverManager().getConnection("s7://10.10.1.33?remote-rack=0&remote-slot=3&controller-type=S7_400")) { //(01) + try (PlcConnection connection new DefaultPlcDriverManager().getConnection("s7://10.10.1.33?remote-rack=0&remote-slot=3&controller-type=S7_400")) { //(01) - final PlcReadRequest.Builder readrequest = connection.readRequestBuilder(); //(02) + final PlcReadRequest.Builder readrequest connection.readRequestBuilder(); //(02) readrequest.addTagAddress("MySZL", "SZL_ID=16#0012;INDEX=16#0000"); //(03) - final PlcReadRequest rr = readrequest.build(); //(04) - final PlcReadResponse szlresponse = rr.execute().get(); //(05) + final PlcReadRequest rr readrequest.build(); //(04) + final PlcReadResponse szlresponse rr.execute().get(); //(05) - if (szlresponse.getResponseCode("MySZL") == PlcResponseCode.OK){ //(06) + if (szlresponse.getResponseCode("MySZL") = PlcResponseCode.OK){ //(06) - Collection data = szlresponse.getAllBytes("MySZL"); //(07) - byte[] dbytes = ArrayUtils.toPrimitive(data.toArray(new Byte[data.size()])); //(08) + Collection data szlresponse.getAllBytes("MySZL"); //(07) + byte[] dbytes ArrayUtils.toPrimitive(data.toArray(new Byte[data.size()])); //(08) - SZL szl = SZL.valueOf(0x0012); //(09) - ByteBuf wb = wrappedBuffer(dbytes); //(10) + SZL szl SZL.valueOf(0x0012); //(09) + ByteBuf wb wrappedBuffer(dbytes); //(10) StringBuilder sb = szl.execute(wb); //(11) System.out.println(sb.toString()); //(12) - } else if (szlresponse.getResponseCode("MySZL") == PlcResponseCode.NOT_FOUND){ //(13) + } else if (szlresponse.getResponseCode("MySZL") = PlcResponseCode.NOT_FOUND){ //(13) System.out.println("SZL is not supported."); } @@ -1114,7 +1114,7 @@ From the obtained StringBuilder, you can use the JSON processor of your choice t -==== Some useful tips +=== Some useful tips Especially when it comes to the input- and output addresses for analog channels, the start addresses are configurable and hereby don't always start at the same address. In order to find out what addresses these ports have, please go to the `device setting` of your PLC in `TIA Portal` @@ -1134,7 +1134,7 @@ The analog inputs however start at address `64`. Each digital input and output can be addresses by a single bit-address (start-address and offset) or can be read in a block by reading a full byte starting at the given start address without providing a bit offset. -==== Resources +=== Resources 1. https://snap7.sourceforge.net/ 2. https://support.industry.siemens.com/cs/document/13649203/simatic-net-pc-software-s7-programming-interface?dti=0&dl=en&lc=es-ES 1. https://support.industry.siemens.com/cs/document/109797648/simatic-comparison-list-for-s7-300-s7-400-s7-1200-s7-1500?dti=0&lc=en-WW diff --git a/src/site/asciidoc/users/protocols/simulated.adoc b/src/site/asciidoc/users/protocols/simulated.adoc index 7f299119f2d..51675f03c9c 100644 --- a/src/site/asciidoc/users/protocols/simulated.adoc +++ b/src/site/asciidoc/users/protocols/simulated.adoc @@ -17,9 +17,9 @@ :imagesdir: ../../images/users/protocols :icons: font -== Simulated += Simulated -=== Connection String Options +== Connection String Options include::../../../plc4j/drivers/all/src/site/generated/simulated.adoc[] @@ -39,9 +39,9 @@ include::../../../plc4j/drivers/all/src/site/generated/simulated.adoc[] |=== -=== Individual Resource Address Format +== Individual Resource Address Format -==== Connection String +=== Connection String The simulated driver has the following connection string format:- ---- @@ -54,7 +54,7 @@ simulated://127.0.0.1 Note the transport and port fields are optional. -==== General Format +=== General Format The simulated addresses have this format: @@ -65,7 +65,7 @@ The simulated addresses have this format: If the array-size part is omitted, the default size of `1` is assumed. If the data-type part is omitted, it defaults to STRING. -==== Simulation Types +=== Simulation Types The simulation device supports 3 different simulation types @@ -74,13 +74,13 @@ should only be used in conjunction with a persistent connection. Once the connec - RANDOM - This provides a new random value for each read. When writing, a log message is recorded and the value is discarded. - STDOUT - Always returns a null value when reading. When writing, a log message is recorded and the value is discarded. -==== Alias +=== Alias Aliases are used to identify the different field addresses. They should only contain alpha-numeric and the full stop (.) character. For readability and language specific technical purposes they should be less than 256 characters. -==== Data Types +=== Data Types The following data types are supported:- @@ -103,12 +103,12 @@ The following data types are supported:- - WCHAR (2 byte char) - STRING (254 bytes) -==== Some useful tips +=== Some useful tips The simulation driver uses a lot of the same logic templates that is used for the other drivers. It is a good way to test PLC4X functionality without having a device to connect to. -==== Examples +=== Examples All of these address formats are valid:- diff --git a/src/site/asciidoc/users/protocols/umas.adoc b/src/site/asciidoc/users/protocols/umas.adoc new file mode 100644 index 00000000000..4e94f8540dc --- /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/asciidoc/users/security.adoc b/src/site/asciidoc/users/security.adoc index d9316052a27..a500de019d2 100644 --- a/src/site/asciidoc/users/security.adoc +++ b/src/site/asciidoc/users/security.adoc @@ -15,12 +15,12 @@ // limitations under the License. // -== Security Vulnerabilities += Security Vulnerabilities Please note that binary patches are not produced for individual vulnerabilities. To obtain the binary fix for a particular vulnerability you should upgrade to an Apache PLC4X version where that vulnerability has been fixed. For more information about reporting vulnerabilities, see the https://www.apache.org/security/[Apache Security Team] page. -=== Known Vulnerabilities +== Known Vulnerabilities No vulnerabilities have been reported. \ No newline at end of file diff --git a/src/site/asciidoc/users/tools/capture-replay.adoc b/src/site/asciidoc/users/tools/capture-replay.adoc index adb2ee0ed76..097536aaaff 100644 --- a/src/site/asciidoc/users/tools/capture-replay.adoc +++ b/src/site/asciidoc/users/tools/capture-replay.adoc @@ -15,7 +15,7 @@ // limitations under the License. // -== Capture Replay += Capture Replay Some times the problem with industry protocols is, that the most interesting protocols live in places that are not very welcoming to IT folks. @@ -29,7 +29,7 @@ It allows to replay recorded network traffic and to directly intercept this traf Possibly it could also work with non passive drivers, but I expect synchronization to be tricky. -=== Getting a Capture +== Getting a Capture In order to create a capture I usually connect a device running `WireShark` to the network. @@ -39,7 +39,7 @@ An alternative would be to run `WireShark` on one of the PCs/Servers having acce So if for example I wanted to work on a driver for control system `X`, capturing the traffic on one of the `X` servers is probably the simplest way to do it. If complicance rules prevent ths a third option would be to use a network tap to record the capture. -=== Replaying the Capture +== Replaying the Capture Now you need to copy the `pcapng` file ideally to your development system. diff --git a/src/site/asciidoc/users/tools/connection-cache.adoc b/src/site/asciidoc/users/tools/connection-cache.adoc index 3d92cf9e523..c717f6b5b46 100644 --- a/src/site/asciidoc/users/tools/connection-cache.adoc +++ b/src/site/asciidoc/users/tools/connection-cache.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -=== The Connection Cache concept +== The Connection Cache concept In some applications there might be multiple parts of the code that require access to a PLC connection. diff --git a/src/site/asciidoc/users/tools/connection-pool.adoc b/src/site/asciidoc/users/tools/connection-pool.adoc index 82f43611105..f47ea49755f 100644 --- a/src/site/asciidoc/users/tools/connection-pool.adoc +++ b/src/site/asciidoc/users/tools/connection-pool.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Connection Pool += Connection Pool NOTE: The plc4j-connection-pool module has been discontinued and has been removed from PLC4X stating with version 0.11.0 @@ -41,7 +41,7 @@ Per default the PLC connection has no means of automatically re-connecting. The `PooledPlcDriverManager` can help you with both of these scenarios. -=== The PooledPlcDriverManager +== The PooledPlcDriverManager The `PooledPlcDriverManager` is a wrapper around the normal `PlcDriverManager`. @@ -57,7 +57,7 @@ Another benefit of the `PooledPlcDriverManager` is that it will check a connecti So if a connection was terminated, it will detect this and create a new connection. -=== Example +== Example Here comes a little example program utilizing the `PooledPlcDriverManager`: diff --git a/src/site/asciidoc/users/tools/index.adoc b/src/site/asciidoc/users/tools/index.adoc index 4670d706076..a558327a17a 100644 --- a/src/site/asciidoc/users/tools/index.adoc +++ b/src/site/asciidoc/users/tools/index.adoc @@ -17,4 +17,4 @@ :imagesdir: ../../images/ :icons: font -== Tools += Tools diff --git a/src/site/asciidoc/users/tools/opm.adoc b/src/site/asciidoc/users/tools/opm.adoc index 9192300c4f8..06a60184063 100644 --- a/src/site/asciidoc/users/tools/opm.adoc +++ b/src/site/asciidoc/users/tools/opm.adoc @@ -15,10 +15,10 @@ // limitations under the License. // -== Object PLC Mapping += Object PLC Mapping -=== What is Object PLC Mapping +== What is Object PLC Mapping Object PLC Mapping (OPM) is heavily inspired by the Java Persistence API (JPA) [1]. One of the main goal of the PLC4X Project is to make it easy to communicate with PLC devices to enable the development @@ -29,7 +29,7 @@ This is exactly the reason why JPA was initialized many years ago to allow the i calling methods on POJOs (Plain old Java Object). This is exactly what the OPM Module is for, to enable PLC communication by simply interacting with a POJO. -=== Simple Example +== Simple Example The following short code snippet shows how to read one value from a PLC via OPM. First, a _PlcEntityManager_ is instantiated, then a *connected* entity is fetched for a given PLC connection address. @@ -64,10 +64,10 @@ public class MyEntity { } ---- -=== Annotations +== Annotations -=== More details +== More details -=== References +== References [1] https://www.oracle.com/technetwork/java/javaee/tech/persistence-jsp-140049.html \ No newline at end of file diff --git a/src/site/asciidoc/users/tools/scraper.adoc b/src/site/asciidoc/users/tools/scraper.adoc index 857c48f63b2..9944d669749 100644 --- a/src/site/asciidoc/users/tools/scraper.adoc +++ b/src/site/asciidoc/users/tools/scraper.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Scraper += Scraper While the Apache PLC4X API allows simple access to PLC resources, if you want to continuously monitor some values and have them retrieved in a pre-defined interval, the core PLC4X API method is a little bit uncomfortable. @@ -29,7 +29,7 @@ As we have encountered exactly the same problem for about every integration modu This tool automatically handles all of the tasks mentioned above. -=== Getting started with the `Scraper` +== Getting started with the `Scraper` The Scraper can be found in the Maven module: [subs=attributes+] @@ -49,13 +49,13 @@ In general, you need 3 parts to work with the `Scraper`: In the `Scraper` Configuration you define the so-called `jobs`. -==== Sources +=== Sources Sources define connections to PLCs using PLC4X drivers. Generally you can think of a `Source` as a PLC4X connection string, given an alias name. -==== Jobs +=== Jobs A `Job` defines which resources (PLC Addresses) should be collected from which `Sources` with a given `Trigger`. @@ -69,7 +69,7 @@ In the near future we're hoping that we will be able to support: But, as to now, this has not been implemented yet. -=== Configuration using the Java API +== Configuration using the Java API The core of the Scraper configuration is the `ScraperConfigurationTriggeredImplBuilder` class. Use this to build the configuration objects used to bootstrap the Scraper. @@ -138,7 +138,7 @@ As soon as we're done configuring jobs, we need to create the `Scraper` configur ScraperConfigurationTriggeredImpl scraperConfig = builder.build(); ---- -=== Running the `Scraper` +== Running the `Scraper` In order to run the `Scraper`, the following boilerplate code is needed. @@ -183,7 +183,7 @@ public interface ResultHandler { } ---- -=== Configuration using a `JSON` or `YAML` file +== Configuration using a `JSON` or `YAML` file As an alternative to using the Java API, the Scraper Configuration can also be read from a `JSON` or `YAML` document. diff --git a/src/site/asciidoc/users/tools/testing.adoc b/src/site/asciidoc/users/tools/testing.adoc index e4252c188c5..05396753086 100644 --- a/src/site/asciidoc/users/tools/testing.adoc +++ b/src/site/asciidoc/users/tools/testing.adoc @@ -15,9 +15,9 @@ // limitations under the License. // -== Testing (or using PLC4X without a PLC) += Testing (or using PLC4X without a PLC) -=== The Mock Driver +== The Mock Driver PLC4X has a _Mock Driver_ which was initially implemented to be used for Unit Tests and this still is its main purpose. But this driver is also very suitable to play around a bit with the PLC4X API if no _Hardware_ PLC is available. @@ -54,7 +54,7 @@ public interface MockDevice { } ``` -=== Simple Example +== Simple Example Imagine we have some Code which we cannot control or whose functionality we want to test. This can be done with the Mock Driver in the following way. @@ -89,7 +89,7 @@ MockDevice mockDevice = new MockDevice() { ``` This would just return a String Value `hello` for every request and print all read and write requests to the Console. -=== Unit Testing with the Mock Driver +== Unit Testing with the Mock Driver To use the Mock driver in Unit Tests the easiest way is to generate the `MockDriver` instance as Mockito (or any other Framework) Mock. Like in the following Example @@ -149,7 +149,7 @@ verify(mockDevice).read(eq("MyAdress")); The Snippet above shows that the part under test really has to share nothing with the test code except for the connection string. -=== Conclusion +== Conclusion The above examples show that the `MockDriver` driver can not only be used to play around with the API but is also a powerful tool to do unit testing of Code which uses the PLC4X API. diff --git a/src/site/asciidoc/users/transports/can.adoc b/src/site/asciidoc/users/transports/can.adoc index 6dcdda5b722..ffcbe0591e6 100644 --- a/src/site/asciidoc/users/transports/can.adoc +++ b/src/site/asciidoc/users/transports/can.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== CAN += CAN A CAN transport is a special category of transport which can bring CAN frame data to various drivers. All of these transports are meant to follow basic CAN frame data semantics. @@ -67,11 +67,11 @@ The CAN transport responsibility is to bring CAN data to driver implementer. This API does not enforce, require or promote a low level bus operations. In this regard, these operations can be made by library available for specific CAN adapter in use. -=== Developer notes +== Developer notes Usage of CAN transport APIs is recommended for portability reasons. Please have a look on link:../protocols/can.html[CAN] describe usage of CAN driver adapter with CAN transport facade. -== Compatible CAN transports += Compatible CAN transports - link:socketcan.html[SocketCAN] \ No newline at end of file diff --git a/src/site/asciidoc/users/transports/index.adoc b/src/site/asciidoc/users/transports/index.adoc index 59d0f411689..cbefddf052a 100644 --- a/src/site/asciidoc/users/transports/index.adoc +++ b/src/site/asciidoc/users/transports/index.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Transports += Transports - link:tcp.html[TCP] - link:udp.html[UDP] diff --git a/src/site/asciidoc/users/transports/pcap-replay.adoc b/src/site/asciidoc/users/transports/pcap-replay.adoc index 96df626959b..84163fc6b01 100644 --- a/src/site/asciidoc/users/transports/pcap-replay.adoc +++ b/src/site/asciidoc/users/transports/pcap-replay.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== PCAP Replay += PCAP Replay [cols="2,2a,5a"] |=== diff --git a/src/site/asciidoc/users/transports/raw-socket.adoc b/src/site/asciidoc/users/transports/raw-socket.adoc index 9ca68332f1d..53e56bb2471 100644 --- a/src/site/asciidoc/users/transports/raw-socket.adoc +++ b/src/site/asciidoc/users/transports/raw-socket.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Raw Socket += Raw Socket [cols="2,2a,5a"] |=== diff --git a/src/site/asciidoc/users/transports/serial.adoc b/src/site/asciidoc/users/transports/serial.adoc index 61b28867253..524c9032363 100644 --- a/src/site/asciidoc/users/transports/serial.adoc +++ b/src/site/asciidoc/users/transports/serial.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== Serial Port += Serial Port [cols="2,2a,5a"] |=== diff --git a/src/site/asciidoc/users/transports/socketcan.adoc b/src/site/asciidoc/users/transports/socketcan.adoc index c0c86a4a60d..0cd2c1db88a 100644 --- a/src/site/asciidoc/users/transports/socketcan.adoc +++ b/src/site/asciidoc/users/transports/socketcan.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== SocketCAN += SocketCAN [cols="2,2a,5a"] |=== diff --git a/src/site/asciidoc/users/transports/tcp.adoc b/src/site/asciidoc/users/transports/tcp.adoc index a5da3229e73..cf714d7bc8d 100644 --- a/src/site/asciidoc/users/transports/tcp.adoc +++ b/src/site/asciidoc/users/transports/tcp.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== TCP += TCP [cols="2,2a,5a"] |=== diff --git a/src/site/asciidoc/users/transports/udp.adoc b/src/site/asciidoc/users/transports/udp.adoc index 684cccd2825..fabfcd95f5c 100644 --- a/src/site/asciidoc/users/transports/udp.adoc +++ b/src/site/asciidoc/users/transports/udp.adoc @@ -17,7 +17,7 @@ :imagesdir: ../../images/ :icons: font -== UDP += UDP [cols="2,2a,5a"] |=== diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_1.png b/src/site/resources/images/tutorials/opcua/kse/kse_1.png new file mode 100644 index 00000000000..7585536127d Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_1.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_10.png b/src/site/resources/images/tutorials/opcua/kse/kse_10.png new file mode 100644 index 00000000000..1c8139524aa Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_10.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_11.png b/src/site/resources/images/tutorials/opcua/kse/kse_11.png new file mode 100644 index 00000000000..d3293091d7e Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_11.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_12.png b/src/site/resources/images/tutorials/opcua/kse/kse_12.png new file mode 100644 index 00000000000..57fcaf62e78 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_12.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_13.png b/src/site/resources/images/tutorials/opcua/kse/kse_13.png new file mode 100644 index 00000000000..203d8418dfd Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_13.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_14.png b/src/site/resources/images/tutorials/opcua/kse/kse_14.png new file mode 100644 index 00000000000..8e664ff1f48 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_14.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_15.png b/src/site/resources/images/tutorials/opcua/kse/kse_15.png new file mode 100644 index 00000000000..bba47e167d8 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_15.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_16.png b/src/site/resources/images/tutorials/opcua/kse/kse_16.png new file mode 100644 index 00000000000..875e67e47a8 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_16.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_17.png b/src/site/resources/images/tutorials/opcua/kse/kse_17.png new file mode 100644 index 00000000000..e75d53c15f2 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_17.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_18.png b/src/site/resources/images/tutorials/opcua/kse/kse_18.png new file mode 100644 index 00000000000..9161af650ee Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_18.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_2.png b/src/site/resources/images/tutorials/opcua/kse/kse_2.png new file mode 100644 index 00000000000..9a09aeaa901 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_2.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_3.png b/src/site/resources/images/tutorials/opcua/kse/kse_3.png new file mode 100644 index 00000000000..0c3be499ddb Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_3.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_4.png b/src/site/resources/images/tutorials/opcua/kse/kse_4.png new file mode 100644 index 00000000000..ba5db5fa1b1 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_4.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_5.png b/src/site/resources/images/tutorials/opcua/kse/kse_5.png new file mode 100644 index 00000000000..9ca2a411e53 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_5.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_6.png b/src/site/resources/images/tutorials/opcua/kse/kse_6.png new file mode 100644 index 00000000000..aa1910f5d74 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_6.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_7.png b/src/site/resources/images/tutorials/opcua/kse/kse_7.png new file mode 100644 index 00000000000..f375f3b6960 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_7.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_8.png b/src/site/resources/images/tutorials/opcua/kse/kse_8.png new file mode 100644 index 00000000000..919c693c60c Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_8.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_9.png b/src/site/resources/images/tutorials/opcua/kse/kse_9.png new file mode 100644 index 00000000000..d1be26bae48 Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_9.png differ diff --git a/src/site/resources/images/tutorials/opcua/kse/kse_self_signed_ca_certificate.gif b/src/site/resources/images/tutorials/opcua/kse/kse_self_signed_ca_certificate.gif new file mode 100644 index 00000000000..84cfdfa7dce Binary files /dev/null and b/src/site/resources/images/tutorials/opcua/kse/kse_self_signed_ca_certificate.gif differ diff --git a/src/site/site.xml b/src/site/site.xml index 0e4d6cd09af..4ed13d10f32 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -63,6 +63,7 @@ + @@ -87,6 +88,7 @@ +