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

hutcheb pushed a commit to branch feat/umas_write
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feat/umas_write by this push:
     new 6f959f4a73 fix(plc4py/umas): Start testing all the data types.
6f959f4a73 is described below

commit 6f959f4a733998ddc9af3692611450e4a43e6db2
Author: hutcheb <[email protected]>
AuthorDate: Thu Oct 24 22:42:36 2024 +0800

    fix(plc4py/umas): Start testing all the data types.
---
 .../python/PythonLanguageTemplateHelper.java       |  14 +-
 .../templates/python/data-io-template.python.ftlh  |  32 +-
 plc4py/plc4py/drivers/umas/UmasDevice.py           |  56 +++-
 plc4py/plc4py/drivers/umas/UmasTag.py              |   2 +-
 plc4py/plc4py/drivers/umas/UmasVariables.py        |   2 +-
 .../plc4py/protocols/modbus/readwrite/DataItem.py  |  14 +-
 .../protocols/simulated/readwrite/DataItem.py      |  18 +-
 plc4py/plc4py/protocols/umas/readwrite/DataItem.py | 216 ++++++++++++-
 .../protocols/umas/readwrite/UmasDataType.py       |   2 +-
 plc4py/plc4py/spi/generation/ReadBuffer.py         | 107 +++++--
 plc4py/plc4py/spi/values/PlcValues.py              |   5 +-
 .../drivers/modbus/test_modbus_connection.py       |   2 +-
 .../plc4py/drivers/umas/test_umas_connection.py    | 335 ++++++++++++++++++---
 .../src/main/resources/protocols/umas/umas.mspec   |  33 +-
 src/site/asciidoc/users/protocols/index.adoc       |  18 ++
 src/site/asciidoc/users/protocols/umas.adoc        |  52 ++--
 src/site/site.xml                                  |   1 +
 17 files changed, 764 insertions(+), 145 deletions(-)

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 bd2370721b..c4054c61ce 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 class PythonLanguageTemplateHelper extends 
BaseFreemarkerLanguageTemplate
             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 class PythonLanguageTemplateHelper extends 
BaseFreemarkerLanguageTemplate
         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 class PythonLanguageTemplateHelper extends 
BaseFreemarkerLanguageTemplate
                 } 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 class PythonLanguageTemplateHelper extends 
BaseFreemarkerLanguageTemplate
                 } 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 class PythonLanguageTemplateHelper extends 
BaseFreemarkerLanguageTemplate
                     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 "";
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 029c485408..19ad41c98a 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
 elementTypeR [...]
+                
${helper.camelCaseToSnakeCase(arrayField.name)}.append(${helper.getPlcValueTypeForTypeReference(elementTypeReference)}(${helper.getLanguageTypeNameForTypeReference(elementTypeReference,
 false)}(<#if 
elementTypeReference.isSimpleTypeReference()>${helper.getReadBufferReadMethodCall(elementTypeReference.asSimpleTypeReference().orElseThrow(),
 "", arrayField)}${helper.getFieldOptions(typedField, 
parserArguments)}))<#else>${elementTypeReference.asComplexTypeReference().orElseTh
 [...]
 
             <#-- 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.getArgument 
[...]
+                ${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 [...]
 
                 <#-- 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>
                         </#if>
@@ -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.getLanguageType [...]
+            ${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.o [...]
                         </#if>
                         <#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))
                     </#if>
             <@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))
                     </#if>
             <@emitImport import="from plc4py.spi.values.PlcValues import 
PlcDATE_AND_TIME" />
             return PlcDATE_AND_TIME(value)
diff --git a/plc4py/plc4py/drivers/umas/UmasDevice.py 
b/plc4py/plc4py/drivers/umas/UmasDevice.py
index e5a135886a..593a4e8cd7 100644
--- a/plc4py/plc4py/drivers/umas/UmasDevice.py
+++ b/plc4py/plc4py/drivers/umas/UmasDevice.py
@@ -21,6 +21,7 @@ from asyncio import AbstractEventLoop, Transport
 from dataclasses import dataclass, field
 from typing import Dict, List, Tuple, cast
 
+from plc4py.api.exceptions.exceptions import PlcFieldParseException
 from plc4py.api.messages.PlcRequest import (
     PlcBrowseRequest,
     PlcReadRequest,
@@ -34,7 +35,11 @@ from plc4py.api.messages.PlcResponse import (
 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.UmasVariables import (
+    UmasVariable,
+    UmasVariableBuilder,
+    UmasElementryVariable,
+)
 from plc4py.protocols.umas.readwrite import (
     UmasPDUReadUnlocatedVariableResponse,
 )
@@ -110,6 +115,7 @@ from 
plc4py.protocols.umas.readwrite.VariableWriteRequestReference import (
     VariableWriteRequestReference,
 )
 from plc4py.spi.values.PlcValues import PlcNull
+from plc4py.protocols.umas.readwrite.UmasDataType import UmasDataType
 
 
 @dataclass
@@ -413,9 +419,17 @@ class UmasDevice:
             else:
                 quantity = 1
 
+            if request_tag.data_type == None:
+                request_tag.data_type = UmasDataType(
+                    self.variables[request_tag.tag_name].data_type
+                )
+
+            value = DataItem.static_parse(
+                read_buffer, request_tag.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
@@ -469,6 +483,32 @@ class UmasDevice:
         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
@@ -477,7 +517,10 @@ class UmasDevice:
         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
@@ -510,14 +553,17 @@ class UmasDevice:
         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
                 tag_list.append([])
                 current_list = tag_list[current_list_index]
                 byte_count = 0
-            byte_count += variable.get_byte_length()
+            byte_count += variable.get_byte_length() + variable.data_type
             current_list.append(
                 (
                     kea,
diff --git a/plc4py/plc4py/drivers/umas/UmasTag.py 
b/plc4py/plc4py/drivers/umas/UmasTag.py
index 480a41dc85..c6f48fdf92 100644
--- a/plc4py/plc4py/drivers/umas/UmasTag.py
+++ b/plc4py/plc4py/drivers/umas/UmasTag.py
@@ -64,7 +64,7 @@ class UmasTag(PlcTag):
             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 e730cbff16..92eb364b02 100644
--- a/plc4py/plc4py/drivers/umas/UmasVariables.py
+++ b/plc4py/plc4py/drivers/umas/UmasVariables.py
@@ -153,7 +153,7 @@ class UmasCustomVariable(UmasVariable):
         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:])
+                ".".join(split_tag_address[1:]), value
             )
         else:
             raise NotImplementedError("Unable to write structures of UDT's")
diff --git a/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py 
b/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py
index 2eec738436..9d76786554 100644
--- a/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py
+++ b/plc4py/plc4py/protocols/modbus/readwrite/DataItem.py
@@ -425,7 +425,7 @@ class DataItem:
         if data_type == ModbusDataType.CHAR and number_of_values == int(1):  # 
CHAR
 
             # Simple Field (value)
-            value: str = read_buffer.read_str(8, logical_name="", encoding="")
+            value: str = read_buffer.read_str(8, logical_name="", 
encoding='"UTF-8"')
 
             return PlcCHAR(value)
         if data_type == ModbusDataType.CHAR:  # List
@@ -436,7 +436,9 @@ class DataItem:
             for _ in range(item_count):
                 value.append(
                     PlcSTRING(
-                        str(read_buffer.read_str(8, logical_name="", 
encoding=""))
+                        str(
+                            read_buffer.read_str(8, logical_name="", 
encoding='"UTF-8"')
+                        )
                     )
                 )
 
@@ -444,7 +446,7 @@ class DataItem:
         if data_type == ModbusDataType.WCHAR and number_of_values == int(1):  
# WCHAR
 
             # Simple Field (value)
-            value: str = read_buffer.read_str(16, logical_name="", encoding="")
+            value: str = read_buffer.read_str(16, logical_name="", 
encoding='"UTF-16"')
 
             return PlcWCHAR(value)
         if data_type == ModbusDataType.WCHAR:  # List
@@ -455,7 +457,11 @@ class DataItem:
             for _ in range(item_count):
                 value.append(
                     PlcSTRING(
-                        str(read_buffer.read_str(16, logical_name="", 
encoding=""))
+                        str(
+                            read_buffer.read_str(
+                                16, logical_name="", encoding='"UTF-16"'
+                            )
+                        )
                     )
                 )
 
diff --git a/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py 
b/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py
index f5a2dc2bbf..02af3dea89 100644
--- a/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py
+++ b/plc4py/plc4py/protocols/simulated/readwrite/DataItem.py
@@ -298,7 +298,7 @@ class DataItem:
         if data_type == "CHAR" and number_of_values == int(1):  # CHAR
 
             # Simple Field (value)
-            value: str = read_buffer.read_str(8, logical_name="", encoding="")
+            value: str = read_buffer.read_str(8, logical_name="", 
encoding='"UTF-8"')
 
             return PlcCHAR(value)
         if data_type == "CHAR":  # List
@@ -309,7 +309,9 @@ class DataItem:
             for _ in range(item_count):
                 value.append(
                     PlcSTRING(
-                        str(read_buffer.read_str(8, logical_name="", 
encoding=""))
+                        str(
+                            read_buffer.read_str(8, logical_name="", 
encoding='"UTF-8"')
+                        )
                     )
                 )
 
@@ -317,7 +319,7 @@ class DataItem:
         if data_type == "WCHAR" and number_of_values == int(1):  # WCHAR
 
             # Simple Field (value)
-            value: str = read_buffer.read_str(16, logical_name="", encoding="")
+            value: str = read_buffer.read_str(16, logical_name="", 
encoding='"UTF-16"')
 
             return PlcWCHAR(value)
         if data_type == "WCHAR":  # List
@@ -328,7 +330,11 @@ class DataItem:
             for _ in range(item_count):
                 value.append(
                     PlcSTRING(
-                        str(read_buffer.read_str(16, logical_name="", 
encoding=""))
+                        str(
+                            read_buffer.read_str(
+                                16, logical_name="", encoding='"UTF-16"'
+                            )
+                        )
                     )
                 )
 
@@ -336,13 +342,13 @@ class DataItem:
         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
diff --git a/plc4py/plc4py/protocols/umas/readwrite/DataItem.py 
b/plc4py/plc4py/protocols/umas/readwrite/DataItem.py
index 83b54d806d..8e75e9bf42 100644
--- a/plc4py/plc4py/protocols/umas/readwrite/DataItem.py
+++ b/plc4py/plc4py/protocols/umas/readwrite/DataItem.py
@@ -24,12 +24,17 @@ from plc4py.spi.generation.ReadBuffer import ReadBuffer
 from plc4py.spi.generation.WriteBuffer import WriteBuffer
 from plc4py.spi.values.PlcValues import PlcBOOL
 from plc4py.spi.values.PlcValues import PlcBYTE
+from plc4py.spi.values.PlcValues import PlcDATE
+from plc4py.spi.values.PlcValues import PlcDATE_AND_TIME
 from plc4py.spi.values.PlcValues import PlcDINT
 from plc4py.spi.values.PlcValues import PlcDWORD
 from plc4py.spi.values.PlcValues import PlcINT
 from plc4py.spi.values.PlcValues import PlcList
 from plc4py.spi.values.PlcValues import PlcREAL
+from plc4py.spi.values.PlcValues import PlcSINT
 from plc4py.spi.values.PlcValues import PlcSTRING
+from plc4py.spi.values.PlcValues import PlcTIME
+from plc4py.spi.values.PlcValues import PlcTIME_OF_DAY
 from plc4py.spi.values.PlcValues import PlcUDINT
 from plc4py.spi.values.PlcValues import PlcUINT
 from plc4py.spi.values.PlcValues import PlcULINT
@@ -37,6 +42,7 @@ from plc4py.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
 
@@ -101,7 +107,7 @@ class DataItem:
         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
@@ -110,7 +116,7 @@ class DataItem:
             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(PlcBYTE(int(read_buffer.read_byte(""))))
 
             return PlcList(value)
         if data_type == UmasDataType.WORD:  # WORD
@@ -226,6 +232,98 @@ class DataItem:
                 )
 
             return PlcList(value)
+        if data_type == UmasDataType.TIME and number_of_values == int(1):  # 
TIME
+
+            # Simple Field (value)
+            value: int = read_buffer.read_unsigned_long(32, logical_name="")
+
+            return PlcTIME(value)
+        if data_type == UmasDataType.TIME:  # List
+            # Array field (value)
+            # Count array
+            item_count: int = int(number_of_values)
+            value: List[PlcValue] = []
+            for _ in range(item_count):
+                value.append(
+                    PlcULINT(int(read_buffer.read_unsigned_long(32, 
logical_name="")))
+                )
+
+            return PlcList(value)
+        if data_type == UmasDataType.DATE and number_of_values == int(1):  # 
DATE
+
+            # Simple Field (day)
+            day: int = read_buffer.read_unsigned_short(
+                8, logical_name="", encoding="BCD"
+            )
+
+            # Simple Field (month)
+            month: int = read_buffer.read_unsigned_short(
+                8, logical_name="", encoding="BCD"
+            )
+
+            # Simple Field (year)
+            year: int = read_buffer.read_unsigned_int(
+                16, logical_name="", encoding="BCD"
+            )
+
+            value: datetime = datetime.datetime(int(year), int(month), 
int(day))
+            return PlcDATE(value)
+        if data_type == UmasDataType.TOD and number_of_values == int(1):  # 
TIME_OF_DAY
+
+            # Simple Field (value)
+            value: int = read_buffer.read_unsigned_long(32, logical_name="")
+
+            return PlcTIME_OF_DAY(value)
+        if data_type == UmasDataType.TOD:  # List
+            # Array field (value)
+            # Count array
+            item_count: int = int(number_of_values)
+            value: List[PlcValue] = []
+            for _ in range(item_count):
+                value.append(
+                    PlcULINT(int(read_buffer.read_unsigned_long(32, 
logical_name="")))
+                )
+
+            return PlcList(value)
+        if data_type == UmasDataType.DT 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
@@ -265,13 +363,13 @@ class DataItem:
         elif data_type == UmasDataType.BYTE and number_of_values == int(1):  # 
BYTE
             # Simple Field (value)
             value: int = _value.get_int()
-            write_buffer.write_byte((value), 8, "value")
+            write_buffer.write_byte((value), "value")
 
         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_array("", value)
 
         elif data_type == UmasDataType.WORD:  # WORD
             # Simple Field (value)
@@ -347,6 +445,74 @@ class DataItem:
                 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.DT 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
@@ -384,7 +550,7 @@ class DataItem:
             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
@@ -427,5 +593,43 @@ class DataItem:
         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.DT 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 69fcc90f31..061afb668b 100644
--- a/plc4py/plc4py/protocols/umas/readwrite/UmasDataType.py
+++ b/plc4py/plc4py/protocols/umas/readwrite/UmasDataType.py
@@ -38,7 +38,7 @@ class UmasDataType(AutoNumberEnum):
     UNKNOWN13 = (13, int(1), int(1))
     DATE = (14, int(3), int(4))
     TOD = (15, int(3), int(4))
-    DT = (16, int(3), int(4))
+    DT = (16, int(4), int(8))
     UNKNOWN17 = (17, int(1), int(1))
     UNKNOWN18 = (18, int(1), int(1))
     UNKNOWN19 = (19, int(1), int(1))
diff --git a/plc4py/plc4py/spi/generation/ReadBuffer.py 
b/plc4py/plc4py/spi/generation/ReadBuffer.py
index f250582b97..2a985cd7ba 100644
--- a/plc4py/plc4py/spi/generation/ReadBuffer.py
+++ b/plc4py/plc4py/spi/generation/ReadBuffer.py
@@ -24,7 +24,7 @@ import aenum
 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 @@ class ReadBufferByteBased(ReadBuffer):
         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 @@ class ReadBufferByteBased(ReadBuffer):
         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 @@ class ReadBufferByteBased(ReadBuffer):
         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 @@ class ReadBufferByteBased(ReadBuffer):
         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 @@ class ReadBufferByteBased(ReadBuffer):
         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 @@ class ReadBufferByteBased(ReadBuffer):
         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 @@ class ReadBufferByteBased(ReadBuffer):
         else:
             if byte_order == ByteOrder.LITTLE_ENDIAN:
                 endian_string = "<"
+                padded = bitarray(
+                    self.bb[self.position : self.position + bit_length]
+                ) + (64 - bit_length) * bitarray("0")
             else:
                 endian_string = ">"
-            padded = (64 - bit_length) * bitarray("0") + bitarray(
-                self.bb[self.position : self.position + bit_length]
-            )
+                padded = (64 - bit_length) * bitarray("0") + bitarray(
+                    self.bb[self.position : self.position + bit_length]
+                )
             if (
                 byte_order == ByteOrder.BIG_ENDIAN_BYTE_SWAP
                 or byte_order == ByteOrder.LITTLE_ENDIAN_BYTE_SWAP
diff --git a/plc4py/plc4py/spi/values/PlcValues.py 
b/plc4py/plc4py/spi/values/PlcValues.py
index 3c2e5ca884..ed207cfb74 100644
--- a/plc4py/plc4py/spi/values/PlcValues.py
+++ b/plc4py/plc4py/spi/values/PlcValues.py
@@ -17,6 +17,7 @@
 # under the License.
 #
 from dataclasses import dataclass
+from datetime import datetime
 from typing import Any, Dict, List
 
 from plc4py.api.value.PlcValue import PlcValue
@@ -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/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py 
b/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py
index f3d2ea13ad..edf295d8e9 100644
--- a/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py
+++ b/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py
@@ -28,7 +28,7 @@ import logging
 from plc4py.spi.values.PlcValues import PlcINT, PlcREAL, PlcList
 
 logger = logging.getLogger("testing")
-TEST_SERVER_IP = "192.168.190.174"
+TEST_SERVER_IP = "192.168.190.152"
 
 
 @pytest.mark.asyncio
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 932456db1a..a7a37fb4d0 100644
--- a/plc4py/tests/unit/plc4py/drivers/umas/test_umas_connection.py
+++ b/plc4py/tests/unit/plc4py/drivers/umas/test_umas_connection.py
@@ -17,74 +17,329 @@
 # 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
 
 
+@pytest_asyncio.fixture
+async def connection() -> AsyncGenerator[PlcConnection, None]:
+    driver_manager = PlcDriverManager()
+    async with driver_manager.connection("umas://192.168.190.152: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
 
 
 @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_alias = "Random Tag"
+    tag_name = "TESTING"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == True
+    assert response_code == PlcResponseCode.OK
 
-    driver_manager = PlcDriverManager()
-    async with driver_manager.connection("umas://192.168.190.152:502") as 
connection:
-        with connection.read_request_builder() as builder:
-            #builder.add_item(f"Random Tag {1}", "TESTING_10:BOOL")
-            builder.add_item(f"Random Tag {2}", "TESTING_REAL:REAL")
 
-            request = builder.build()
[email protected]
[email protected]
+async def test_plc_driver_umas_read_boolean_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING:BOOL"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == True
+    assert response_code == PlcResponseCode.OK
+
 
-        future = connection.execute(request)
-        response = await future
-        value = response.tags["Random Tag 1"].value
-        assert value == 0.0
[email protected]
[email protected]
+async def test_plc_driver_umas_read_int(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_INT"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == 99
+    assert response_code == PlcResponseCode.OK
 
 
 @pytest.mark.asyncio
 @pytest.mark.xfail
-async def test_plc_driver_umas_write():
-    log = logging.getLogger(__name__)
+async def test_plc_driver_umas_read_int_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_INT:INT"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == 99
+    assert response_code == PlcResponseCode.OK
 
-    driver_manager = PlcDriverManager()
-    async with driver_manager.connection("umas://192.168.190.152:502") as 
connection:
-        with connection.write_request_builder() as builder:
-            # builder.add_item(f"Random Tag {1}", "TESTING_10:BOOL", 
PlcBOOL(True))
-            # builder.add_item(f"Random Tag {2}", "TESTING_INT:INT", 
PlcINT(10))
-            # builder.add_item(f"Random Tag {3}", "TESTING_EBOOL:BOOL", 
PlcBOOL(True))
-            builder.add_item(f"Random Tag {4}", "TESTING_REAL:REAL", 
PlcREAL(3.18))
-            request = builder.build()
 
-        future = connection.execute(request)
-        response = await future
-        value = response.tags["Random Tag 1"].response_code
-        assert value == PlcResponseCode.OK
[email protected]
[email protected]
+async def test_plc_driver_umas_read_dint(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_DINT"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == 763539
+    assert response_code == PlcResponseCode.OK
 
 
 @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()
+async def test_plc_driver_umas_read_dint_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_DINT:DINT"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == 763539
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_ebool(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_EBOOL"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == True
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_ebool_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_EBOOL:BOOL"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == True
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_string(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_STRING"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == "Hello World"
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_string_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_STRING:STRING"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == "Hello World"
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_time(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_TIME"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == 200000
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_time_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_TIME:TIME"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == 200000
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_byte(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_BYTE"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == 253
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_byte_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_BYTE:BYTE"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == 253
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_date(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_DATE"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == datetime.datetime(2024, 10, 25)
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_time_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_DATE:DATE"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == datetime.datetime(2024, 10, 25)
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_dt(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_DT"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == datetime.datetime(2000, 1, 10, 0, 40)
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+async def test_plc_driver_umas_read_dt_with_data_type(connection):
+    tag_alias = "Random Tag"
+    tag_name = "TESTING_DT:DATE_AND_TIME"
+    with connection.read_request_builder() as builder:
+        builder.add_item(tag_alias, tag_name)
+        request = builder.build()
+    future = connection.execute(request)
+    response = await future
+    value = response.tags[tag_alias].value
+    response_code = response.tags[tag_alias].response_code
+    assert value == datetime.datetime(2000, 1, 10, 0, 40)
+    assert response_code == PlcResponseCode.OK
+
+
[email protected]
[email protected]
+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
+    future = connection.execute(request)
+    response = await future
 
-        pass
+    pass
diff --git a/protocols/umas/src/main/resources/protocols/umas/umas.mspec 
b/protocols/umas/src/main/resources/protocols/umas/umas.mspec
index 878f64117c..de9079130a 100644
--- a/protocols/umas/src/main/resources/protocols/umas/umas.mspec
+++ b/protocols/umas/src/main/resources/protocols/umas/umas.mspec
@@ -257,11 +257,10 @@
             [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 * 8' ]
         ]
         ['WORD'      WORD
             [simple   uint 16 value]
@@ -305,6 +304,32 @@
         ['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']
+        ]
+        ['DT','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']
+        ]
     ]
 ]
 
@@ -324,7 +349,7 @@
     ['13' UNKNOWN13 ['1','1']]
     ['14' DATE ['4','3']]
     ['15' TOD ['4','3']]
-    ['16' DT ['4','3']]
+    ['16' DT ['8','4']]
     ['17' UNKNOWN17 ['1','1']]
     ['18' UNKNOWN18 ['1','1']]
     ['19' UNKNOWN19 ['1','1']]
diff --git a/src/site/asciidoc/users/protocols/index.adoc 
b/src/site/asciidoc/users/protocols/index.adoc
index 5ad3536941..4bb5e16e54 100644
--- a/src/site/asciidoc/users/protocols/index.adoc
+++ b/src/site/asciidoc/users/protocols/index.adoc
@@ -153,6 +153,13 @@
 |icon:check[role="green"]
 |icon:times[role="red"]
 
+|UMAS
+|icon:times[role="red"]
+|icon:times[role="red"]
+|icon:times[role="red"]
+|icon:times[role="red"]
+|icon:check[role="green"]
+
 |===
 
 Legend:
@@ -379,6 +386,17 @@ The following table contains a list of operations and the 
protocols that support
 |icon:question[role="red"]
 |icon:question[role="red"]
 
+|UMAS
+|icon:question[role="red"]
+|icon:question[role="red"]
+|icon:check[role="green"]
+|icon:check[role="green"]
+|icon:check[role="green"]
+|icon:question[role="red"]
+|icon:question[role="red"]
+|icon:question[role="red"]
+|icon:question[role="red"]
+
 |===
 
 Legend:
diff --git a/src/site/asciidoc/users/protocols/umas.adoc 
b/src/site/asciidoc/users/protocols/umas.adoc
index 18c2d00098..534ba8f08e 100644
--- a/src/site/asciidoc/users/protocols/umas.adoc
+++ b/src/site/asciidoc/users/protocols/umas.adoc
@@ -21,18 +21,20 @@
 
 === Connection String Options
 
-==== Modbus TCP
-
-include::../../../plc4j/drivers/all/src/site/generated/modbus-tcp.adoc[]
-
-==== Modbus RTU
-
-include::../../../plc4j/drivers/all/src/site/generated/modbus-rtu.adoc[]
-
-==== Modbus ASCII
-
-include::../../../plc4j/drivers/all/src/site/generated/modbus-ascii.adoc[]
+[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.
+|`default-unit-identifier` |INT |1| |Unit-identifier or slave-id that 
identifies the target PLC (On RS485 multiple Modbus Devices can be listening). 
Defaults to 1.
++++
 
+|===
 === Supported Operations
 
 [cols="2,2a,5a"]
@@ -46,19 +48,22 @@ 
include::../../../plc4j/drivers/all/src/site/generated/modbus-ascii.adoc[]
 
 |
 2+| `write`
+
+|
+2+| `browse`
 |===
 
 === Individual Resource Address Format
 
 ==== Connection String
 
-Modbus has the following connection string format:-
+UMAS has the following connection string format:-
 ----
-modbus-tcp:{transport}://{ip-address}:{port}?{options}
+umas:{transport}://{ip-address}:{port}?{options}
 ----
 An example connection string would look like:-
 ----
-modbus-tcp:tcp://127.0.0.1:502
+umas:tcp://127.0.0.1:502
 ----
 Note the transport, port and option fields are optional.
 
@@ -68,23 +73,14 @@ Note the transport, port and option fields are optional.
 In general all Modbus addresses have this format:
 
 ----
-{memory-Area}{start-address}:{data-type}[{array-size}]:{name-value-tag-options}
+{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 BOOL for Coils and Discrete 
Inputs and INT for input, holding and extended registers.
-If the name-value-tag-options part is omitted, simply no configuration 
fine-tuning is applied.
-
-Additionally address can contain tag configuration:
-----
-{unit-id: 123}
-----
-Specifying this value overrides value of `default-unit-id` parameter specified 
at the connection string.
-
-----
-{byte-order: 'LITTLE_ENDIAN'}
-----
-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.
+If the data-type part is omitted, it defaults to the data type of the tag read 
from the PLC.
 
 ==== Memory Areas
 
diff --git a/src/site/site.xml b/src/site/site.xml
index 0e4d6cd09a..8f37821fd3 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -87,6 +87,7 @@
         <item name="PROFINET" href="users/protocols/profinet.html"/>
         <item name="S7 (Step7)" href="users/protocols/s7.html"/>
         <item name="Simulated" href="users/protocols/simulated.html"/>
+        <item name="UMAS" href="users/protocols/umas.html"/>
       </item>
       <item name="Transports" href="users/transports/index.html">
         <item name="TCP" href="users/transports/tcp.html"/>

Reply via email to