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

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


The following commit(s) were added to refs/heads/develop by this push:
     new c66ee77880 feat: Got the first tests working with UMAS.
c66ee77880 is described below

commit c66ee778805a45a852f9faefa684dd1fa524aeb6
Author: Christofer Dutz <[email protected]>
AuthorDate: Thu Apr 2 17:23:09 2026 +0200

    feat: Got the first tests working with UMAS.
---
 plc4j/drivers/umas/pom.xml                         |   4 +
 .../apache/plc4x/java/umas/readwrite/DataItem.java |  20 +-
 ...nitorPlcRequest.java => MonitorPlcReadAll.java} |  84 +++-----
 .../umas/readwrite/MonitorPlcRegisterAction.java   |  55 +++++
 .../umas/readwrite/MonitorPlcRegisterAndRead.java  | 202 ++++++++++++++++++
 .../umas/readwrite/MonitorPlcRegisterVariable.java | 234 +++++++++++++++++++++
 ...MonitorPlcRequest.java => MonitorPlcReset.java} |  84 +++-----
 .../umas/readwrite/MonitorPlcSubOperation.java     | 159 ++++++++++++++
 .../java/umas/readwrite/UmasDatatypeReference.java |   9 +
 .../plc4x/java/umas/readwrite/UmasPDUItem.java     |   5 +
 .../umas/readwrite/UmasPDUMonitorPlcRequest.java   | 149 +++++++++++--
 ...Request.java => UmasPDUMonitorPlcResponse.java} |  72 ++++---
 .../readwrite/VariableWriteRequestReference.java   |  27 ++-
 .../readwrite/configuration/UmasConfiguration.java |  15 ++
 .../umas/readwrite/context/UmasDriverContext.java  |  27 +++
 .../umas/readwrite/protocol/UmasProtocolLogic.java | 188 +++++++++++++++--
 .../java/umas/readwrite/tag/SymbolicUmasTag.java   |   5 +-
 .../java/umas/readwrite/utils/StaticHelper.java    |  22 ++
 .../services/org.apache.plc4x.java.api.PlcDriver   |  18 ++
 .../plc4x/java/umas/manual/ManualUmasBrowse.java   |  18 ++
 .../java/umas/manual/ManualUmasDriverTest.java     |  61 ++++++
 .../main/resources/protocols/umas/v1/umas.mspec    | 113 +++++++---
 22 files changed, 1351 insertions(+), 220 deletions(-)

diff --git a/plc4j/drivers/umas/pom.xml b/plc4j/drivers/umas/pom.xml
index 40a11ba78e..268c49b863 100644
--- a/plc4j/drivers/umas/pom.xml
+++ b/plc4j/drivers/umas/pom.xml
@@ -110,6 +110,10 @@
       <groupId>io.netty</groupId>
       <artifactId>netty-buffer</artifactId>
     </dependency>
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-transport</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.slf4j</groupId>
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/DataItem.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/DataItem.java
index 61a0b8ff44..a1cf9888ea 100644
--- 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/DataItem.java
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/DataItem.java
@@ -61,11 +61,16 @@ public class DataItem {
       return new PlcBOOL(value);
     } else if (EvaluationHelper.equals(dataType, UmasDataType.BYTE)
         && EvaluationHelper.equals(numberOfValues, (int) 1)) { // BYTE
-      byte value = readSimpleField("value", readByte(readBuffer, 8));
+      short value = readSimpleField("value", readUnsignedShort(readBuffer, 8));
       return new PlcBYTE(value);
     } else if (EvaluationHelper.equals(dataType, UmasDataType.BYTE)) { // List
-      byte[] value = readBuffer.readByteArray("value", 
Math.toIntExact(numberOfValues));
-      return new PlcRawByteArray(value);
+      List<Short> _value =
+          readCountArrayField("value", readUnsignedShort(readBuffer, 8), 
numberOfValues);
+      List<PlcValue> value = new ArrayList<>(_value.size());
+      for (short _item : _value) {
+        value.add(new PlcUSINT(_item));
+      }
+      return new PlcList(value);
     } else if (EvaluationHelper.equals(dataType, UmasDataType.WORD)) { // WORD
       int value = readSimpleField("value", readUnsignedInt(readBuffer, 16));
       return new PlcWORD(value);
@@ -240,7 +245,7 @@ public class DataItem {
     } else if (EvaluationHelper.equals(dataType, UmasDataType.BYTE)) { // List
       // Array field
       if (_value != null) {
-        lengthInBits += 8 * _value.getRaw().length;
+        lengthInBits += 8 * _value.getList().size();
       }
     } else if (EvaluationHelper.equals(dataType, UmasDataType.WORD)) { // WORD
       // Simple field (value)
@@ -387,10 +392,13 @@ public class DataItem {
     } else if (EvaluationHelper.equals(dataType, UmasDataType.BYTE)
         && EvaluationHelper.equals(numberOfValues, (int) 1)) { // BYTE
       // Simple Field (value)
-      writeSimpleField("value", (byte) _value.getByte(), 
writeByte(writeBuffer, 8));
+      writeSimpleField("value", (short) _value.getShort(), 
writeUnsignedShort(writeBuffer, 8));
     } else if (EvaluationHelper.equals(dataType, UmasDataType.BYTE)) { // List
       // Array Field (value)
-      writeByteArrayField("value", _value.getRaw(), 
writeByteArray(writeBuffer, 8));
+      writeSimpleTypeArrayField(
+          "value",
+          
_value.getList().stream().map(PlcValue::getShort).collect(Collectors.toList()),
+          writeUnsignedShort(writeBuffer, 8));
     } else if (EvaluationHelper.equals(dataType, UmasDataType.WORD)) { // WORD
       // Simple Field (value)
       writeSimpleField("value", (int) _value.getInteger(), 
writeUnsignedInt(writeBuffer, 16));
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcReadAll.java
similarity index 56%
copy from 
plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
copy to 
plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcReadAll.java
index 7f78bb4416..887e55d777 100644
--- 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcReadAll.java
@@ -35,41 +35,29 @@ import org.apache.plc4x.java.spi.generation.*;
 
 // Code generated by code-generation. DO NOT EDIT.
 
-/** ---- FC 0x50: Monitor PLC ---- */
-public class UmasPDUMonitorPlcRequest extends UmasPDUItem implements Message {
+/**
+ * Read current values for all registered variables. Returns concatenated raw 
values in the response
+ * — the driver must know each variable's data type to parse the byte stream.
+ */
+public class MonitorPlcReadAll extends MonitorPlcSubOperation implements 
Message {
 
   // Accessors for discriminator values.
-  public Short getUmasFunctionKey() {
-    return (short) 0x50;
-  }
-
-  public Short getUmasRequestFunctionKey() {
-    return 0;
-  }
-
-  // Properties.
-  protected final byte[] data;
-
-  public UmasPDUMonitorPlcRequest(short pairingKey, byte[] data) {
-    super(pairingKey);
-    this.data = data;
+  public Short getOperationType() {
+    return (short) 0x07;
   }
 
-  public byte[] getData() {
-    return data;
+  public MonitorPlcReadAll() {
+    super();
   }
 
   @Override
-  protected void serializeUmasPDUItemChild(WriteBuffer writeBuffer) throws 
SerializationException {
+  protected void serializeMonitorPlcSubOperationChild(WriteBuffer writeBuffer)
+      throws SerializationException {
     PositionAware positionAware = writeBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
-    writeBuffer.pushContext("UmasPDUMonitorPlcRequest");
+    writeBuffer.pushContext("MonitorPlcReadAll");
 
-    // Array Field (data)
-    writeByteArrayField(
-        "data", data, writeByteArray(writeBuffer, 8), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
-
-    writeBuffer.popContext("UmasPDUMonitorPlcRequest");
+    writeBuffer.popContext("MonitorPlcReadAll");
   }
 
   @Override
@@ -80,45 +68,31 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
   @Override
   public int getLengthInBits() {
     int lengthInBits = super.getLengthInBits();
-    UmasPDUMonitorPlcRequest _value = this;
+    MonitorPlcReadAll _value = this;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
-    // Array field
-    if (data != null) {
-      lengthInBits += 8 * data.length;
-    }
-
     return lengthInBits;
   }
 
-  public static UmasPDUItemBuilder staticParseUmasPDUItemBuilder(
-      ReadBuffer readBuffer, Short umasRequestFunctionKey, Integer byteLength)
-      throws ParseException {
-    readBuffer.pullContext("UmasPDUMonitorPlcRequest");
+  public static MonitorPlcSubOperationBuilder 
staticParseMonitorPlcSubOperationBuilder(
+      ReadBuffer readBuffer) throws ParseException {
+    readBuffer.pullContext("MonitorPlcReadAll");
     PositionAware positionAware = readBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
-    byte[] data =
-        readBuffer.readByteArray(
-            "data", Math.toIntExact((byteLength) - (2)), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
-
-    readBuffer.closeContext("UmasPDUMonitorPlcRequest");
+    readBuffer.closeContext("MonitorPlcReadAll");
     // Create the instance
-    return new UmasPDUMonitorPlcRequestBuilderImpl(data);
+    return new MonitorPlcReadAllBuilderImpl();
   }
 
-  public static class UmasPDUMonitorPlcRequestBuilderImpl
-      implements UmasPDUItem.UmasPDUItemBuilder {
-    private final byte[] data;
+  public static class MonitorPlcReadAllBuilderImpl
+      implements MonitorPlcSubOperation.MonitorPlcSubOperationBuilder {
 
-    public UmasPDUMonitorPlcRequestBuilderImpl(byte[] data) {
-      this.data = data;
-    }
+    public MonitorPlcReadAllBuilderImpl() {}
 
-    public UmasPDUMonitorPlcRequest build(short pairingKey) {
-      UmasPDUMonitorPlcRequest umasPDUMonitorPlcRequest =
-          new UmasPDUMonitorPlcRequest(pairingKey, data);
-      return umasPDUMonitorPlcRequest;
+    public MonitorPlcReadAll build() {
+      MonitorPlcReadAll monitorPlcReadAll = new MonitorPlcReadAll();
+      return monitorPlcReadAll;
     }
   }
 
@@ -127,16 +101,16 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
     if (this == o) {
       return true;
     }
-    if (!(o instanceof UmasPDUMonitorPlcRequest)) {
+    if (!(o instanceof MonitorPlcReadAll)) {
       return false;
     }
-    UmasPDUMonitorPlcRequest that = (UmasPDUMonitorPlcRequest) o;
-    return (getData() == that.getData()) && super.equals(that) && true;
+    MonitorPlcReadAll that = (MonitorPlcReadAll) o;
+    return super.equals(that) && true;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(super.hashCode(), getData());
+    return Objects.hash(super.hashCode());
   }
 
   @Override
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterAction.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterAction.java
new file mode 100644
index 0000000000..7962dc1b1e
--- /dev/null
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterAction.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package org.apache.plc4x.java.umas.readwrite;
+
+import java.util.HashMap;
+import java.util.Map;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+public enum MonitorPlcRegisterAction {
+  DEREGISTER((short) 0x01),
+  REGISTER((short) 0x02);
+  private static final Map<Short, MonitorPlcRegisterAction> map;
+
+  static {
+    map = new HashMap<>();
+    for (MonitorPlcRegisterAction value : MonitorPlcRegisterAction.values()) {
+      map.put((short) value.getValue(), value);
+    }
+  }
+
+  private final short value;
+
+  MonitorPlcRegisterAction(short value) {
+    this.value = value;
+  }
+
+  public short getValue() {
+    return value;
+  }
+
+  public static MonitorPlcRegisterAction enumForValue(short value) {
+    return map.get(value);
+  }
+
+  public static Boolean isDefined(short value) {
+    return map.containsKey(value);
+  }
+}
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterAndRead.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterAndRead.java
new file mode 100644
index 0000000000..b16a7667dd
--- /dev/null
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterAndRead.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+package org.apache.plc4x.java.umas.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+/**
+ * Register a variable AND include its value in the response. Combines 
registration with an
+ * immediate read for that variable.
+ */
+public class MonitorPlcRegisterAndRead extends MonitorPlcSubOperation 
implements Message {
+
+  // Accessors for discriminator values.
+  public Short getOperationType() {
+    return (short) 0x09;
+  }
+
+  // Properties.
+  protected final short variableIndex;
+  protected final int block;
+  protected final int offset;
+
+  public MonitorPlcRegisterAndRead(short variableIndex, int block, int offset) 
{
+    super();
+    this.variableIndex = variableIndex;
+    this.block = block;
+    this.offset = offset;
+  }
+
+  public short getVariableIndex() {
+    return variableIndex;
+  }
+
+  public int getBlock() {
+    return block;
+  }
+
+  public int getOffset() {
+    return offset;
+  }
+
+  @Override
+  protected void serializeMonitorPlcSubOperationChild(WriteBuffer writeBuffer)
+      throws SerializationException {
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("MonitorPlcRegisterAndRead");
+
+    // Simple Field (variableIndex)
+    writeSimpleField(
+        "variableIndex",
+        variableIndex,
+        writeUnsignedShort(writeBuffer, 8),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Simple Field (block)
+    writeSimpleField(
+        "block",
+        block,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Simple Field (offset)
+    writeSimpleField(
+        "offset",
+        offset,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    writeBuffer.popContext("MonitorPlcRegisterAndRead");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = super.getLengthInBits();
+    MonitorPlcRegisterAndRead _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Simple field (variableIndex)
+    lengthInBits += 8;
+
+    // Simple field (block)
+    lengthInBits += 16;
+
+    // Simple field (offset)
+    lengthInBits += 16;
+
+    return lengthInBits;
+  }
+
+  public static MonitorPlcSubOperationBuilder 
staticParseMonitorPlcSubOperationBuilder(
+      ReadBuffer readBuffer) throws ParseException {
+    readBuffer.pullContext("MonitorPlcRegisterAndRead");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    short variableIndex =
+        readSimpleField(
+            "variableIndex",
+            readUnsignedShort(readBuffer, 8),
+            WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    int block =
+        readSimpleField(
+            "block", readUnsignedInt(readBuffer, 16), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    int offset =
+        readSimpleField(
+            "offset", readUnsignedInt(readBuffer, 16), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    readBuffer.closeContext("MonitorPlcRegisterAndRead");
+    // Create the instance
+    return new MonitorPlcRegisterAndReadBuilderImpl(variableIndex, block, 
offset);
+  }
+
+  public static class MonitorPlcRegisterAndReadBuilderImpl
+      implements MonitorPlcSubOperation.MonitorPlcSubOperationBuilder {
+    private final short variableIndex;
+    private final int block;
+    private final int offset;
+
+    public MonitorPlcRegisterAndReadBuilderImpl(short variableIndex, int 
block, int offset) {
+      this.variableIndex = variableIndex;
+      this.block = block;
+      this.offset = offset;
+    }
+
+    public MonitorPlcRegisterAndRead build() {
+      MonitorPlcRegisterAndRead monitorPlcRegisterAndRead =
+          new MonitorPlcRegisterAndRead(variableIndex, block, offset);
+      return monitorPlcRegisterAndRead;
+    }
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof MonitorPlcRegisterAndRead)) {
+      return false;
+    }
+    MonitorPlcRegisterAndRead that = (MonitorPlcRegisterAndRead) o;
+    return (getVariableIndex() == that.getVariableIndex())
+        && (getBlock() == that.getBlock())
+        && (getOffset() == that.getOffset())
+        && super.equals(that)
+        && true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), getVariableIndex(), getBlock(), 
getOffset());
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, 
true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterVariable.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterVariable.java
new file mode 100644
index 0000000000..9a98d0940c
--- /dev/null
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcRegisterVariable.java
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+package org.apache.plc4x.java.umas.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+/**
+ * Register or deregister a variable for monitoring. After registration, the 
variable's current
+ * value is returned in subsequent read (0x07) responses.
+ */
+public class MonitorPlcRegisterVariable extends MonitorPlcSubOperation 
implements Message {
+
+  // Accessors for discriminator values.
+  public Short getOperationType() {
+    return (short) 0x05;
+  }
+
+  // Properties.
+  protected final short variableIndex;
+  protected final int block;
+  protected final int offset;
+  protected final MonitorPlcRegisterAction action;
+
+  public MonitorPlcRegisterVariable(
+      short variableIndex, int block, int offset, MonitorPlcRegisterAction 
action) {
+    super();
+    this.variableIndex = variableIndex;
+    this.block = block;
+    this.offset = offset;
+    this.action = action;
+  }
+
+  public short getVariableIndex() {
+    return variableIndex;
+  }
+
+  public int getBlock() {
+    return block;
+  }
+
+  public int getOffset() {
+    return offset;
+  }
+
+  public MonitorPlcRegisterAction getAction() {
+    return action;
+  }
+
+  @Override
+  protected void serializeMonitorPlcSubOperationChild(WriteBuffer writeBuffer)
+      throws SerializationException {
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("MonitorPlcRegisterVariable");
+
+    // Simple Field (variableIndex)
+    writeSimpleField(
+        "variableIndex",
+        variableIndex,
+        writeUnsignedShort(writeBuffer, 8),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Simple Field (block)
+    writeSimpleField(
+        "block",
+        block,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Simple Field (offset)
+    writeSimpleField(
+        "offset",
+        offset,
+        writeUnsignedInt(writeBuffer, 16),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Simple Field (action)
+    writeSimpleEnumField(
+        "action",
+        "MonitorPlcRegisterAction",
+        action,
+        writeEnum(
+            MonitorPlcRegisterAction::getValue,
+            MonitorPlcRegisterAction::name,
+            writeUnsignedShort(writeBuffer, 8)),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    writeBuffer.popContext("MonitorPlcRegisterVariable");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = super.getLengthInBits();
+    MonitorPlcRegisterVariable _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Simple field (variableIndex)
+    lengthInBits += 8;
+
+    // Simple field (block)
+    lengthInBits += 16;
+
+    // Simple field (offset)
+    lengthInBits += 16;
+
+    // Simple field (action)
+    lengthInBits += 8;
+
+    return lengthInBits;
+  }
+
+  public static MonitorPlcSubOperationBuilder 
staticParseMonitorPlcSubOperationBuilder(
+      ReadBuffer readBuffer) throws ParseException {
+    readBuffer.pullContext("MonitorPlcRegisterVariable");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    short variableIndex =
+        readSimpleField(
+            "variableIndex",
+            readUnsignedShort(readBuffer, 8),
+            WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    int block =
+        readSimpleField(
+            "block", readUnsignedInt(readBuffer, 16), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    int offset =
+        readSimpleField(
+            "offset", readUnsignedInt(readBuffer, 16), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    MonitorPlcRegisterAction action =
+        readEnumField(
+            "action",
+            "MonitorPlcRegisterAction",
+            readEnum(MonitorPlcRegisterAction::enumForValue, 
readUnsignedShort(readBuffer, 8)),
+            WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    readBuffer.closeContext("MonitorPlcRegisterVariable");
+    // Create the instance
+    return new MonitorPlcRegisterVariableBuilderImpl(variableIndex, block, 
offset, action);
+  }
+
+  public static class MonitorPlcRegisterVariableBuilderImpl
+      implements MonitorPlcSubOperation.MonitorPlcSubOperationBuilder {
+    private final short variableIndex;
+    private final int block;
+    private final int offset;
+    private final MonitorPlcRegisterAction action;
+
+    public MonitorPlcRegisterVariableBuilderImpl(
+        short variableIndex, int block, int offset, MonitorPlcRegisterAction 
action) {
+      this.variableIndex = variableIndex;
+      this.block = block;
+      this.offset = offset;
+      this.action = action;
+    }
+
+    public MonitorPlcRegisterVariable build() {
+      MonitorPlcRegisterVariable monitorPlcRegisterVariable =
+          new MonitorPlcRegisterVariable(variableIndex, block, offset, action);
+      return monitorPlcRegisterVariable;
+    }
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof MonitorPlcRegisterVariable)) {
+      return false;
+    }
+    MonitorPlcRegisterVariable that = (MonitorPlcRegisterVariable) o;
+    return (getVariableIndex() == that.getVariableIndex())
+        && (getBlock() == that.getBlock())
+        && (getOffset() == that.getOffset())
+        && (getAction() == that.getAction())
+        && super.equals(that)
+        && true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), getVariableIndex(), getBlock(), 
getOffset(), getAction());
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, 
true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcReset.java
similarity index 56%
copy from 
plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
copy to 
plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcReset.java
index 7f78bb4416..a0a264e6aa 100644
--- 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcReset.java
@@ -35,41 +35,29 @@ import org.apache.plc4x.java.spi.generation.*;
 
 // Code generated by code-generation. DO NOT EDIT.
 
-/** ---- FC 0x50: Monitor PLC ---- */
-public class UmasPDUMonitorPlcRequest extends UmasPDUItem implements Message {
+/**
+ * Clear/reset monitoring state. Observed in Modicon M340 captures with no 
payload (single byte
+ * operation, like 0x07).
+ */
+public class MonitorPlcReset extends MonitorPlcSubOperation implements Message 
{
 
   // Accessors for discriminator values.
-  public Short getUmasFunctionKey() {
-    return (short) 0x50;
-  }
-
-  public Short getUmasRequestFunctionKey() {
-    return 0;
-  }
-
-  // Properties.
-  protected final byte[] data;
-
-  public UmasPDUMonitorPlcRequest(short pairingKey, byte[] data) {
-    super(pairingKey);
-    this.data = data;
+  public Short getOperationType() {
+    return (short) 0x0B;
   }
 
-  public byte[] getData() {
-    return data;
+  public MonitorPlcReset() {
+    super();
   }
 
   @Override
-  protected void serializeUmasPDUItemChild(WriteBuffer writeBuffer) throws 
SerializationException {
+  protected void serializeMonitorPlcSubOperationChild(WriteBuffer writeBuffer)
+      throws SerializationException {
     PositionAware positionAware = writeBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
-    writeBuffer.pushContext("UmasPDUMonitorPlcRequest");
+    writeBuffer.pushContext("MonitorPlcReset");
 
-    // Array Field (data)
-    writeByteArrayField(
-        "data", data, writeByteArray(writeBuffer, 8), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
-
-    writeBuffer.popContext("UmasPDUMonitorPlcRequest");
+    writeBuffer.popContext("MonitorPlcReset");
   }
 
   @Override
@@ -80,45 +68,31 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
   @Override
   public int getLengthInBits() {
     int lengthInBits = super.getLengthInBits();
-    UmasPDUMonitorPlcRequest _value = this;
+    MonitorPlcReset _value = this;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
-    // Array field
-    if (data != null) {
-      lengthInBits += 8 * data.length;
-    }
-
     return lengthInBits;
   }
 
-  public static UmasPDUItemBuilder staticParseUmasPDUItemBuilder(
-      ReadBuffer readBuffer, Short umasRequestFunctionKey, Integer byteLength)
-      throws ParseException {
-    readBuffer.pullContext("UmasPDUMonitorPlcRequest");
+  public static MonitorPlcSubOperationBuilder 
staticParseMonitorPlcSubOperationBuilder(
+      ReadBuffer readBuffer) throws ParseException {
+    readBuffer.pullContext("MonitorPlcReset");
     PositionAware positionAware = readBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
-    byte[] data =
-        readBuffer.readByteArray(
-            "data", Math.toIntExact((byteLength) - (2)), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
-
-    readBuffer.closeContext("UmasPDUMonitorPlcRequest");
+    readBuffer.closeContext("MonitorPlcReset");
     // Create the instance
-    return new UmasPDUMonitorPlcRequestBuilderImpl(data);
+    return new MonitorPlcResetBuilderImpl();
   }
 
-  public static class UmasPDUMonitorPlcRequestBuilderImpl
-      implements UmasPDUItem.UmasPDUItemBuilder {
-    private final byte[] data;
+  public static class MonitorPlcResetBuilderImpl
+      implements MonitorPlcSubOperation.MonitorPlcSubOperationBuilder {
 
-    public UmasPDUMonitorPlcRequestBuilderImpl(byte[] data) {
-      this.data = data;
-    }
+    public MonitorPlcResetBuilderImpl() {}
 
-    public UmasPDUMonitorPlcRequest build(short pairingKey) {
-      UmasPDUMonitorPlcRequest umasPDUMonitorPlcRequest =
-          new UmasPDUMonitorPlcRequest(pairingKey, data);
-      return umasPDUMonitorPlcRequest;
+    public MonitorPlcReset build() {
+      MonitorPlcReset monitorPlcReset = new MonitorPlcReset();
+      return monitorPlcReset;
     }
   }
 
@@ -127,16 +101,16 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
     if (this == o) {
       return true;
     }
-    if (!(o instanceof UmasPDUMonitorPlcRequest)) {
+    if (!(o instanceof MonitorPlcReset)) {
       return false;
     }
-    UmasPDUMonitorPlcRequest that = (UmasPDUMonitorPlcRequest) o;
-    return (getData() == that.getData()) && super.equals(that) && true;
+    MonitorPlcReset that = (MonitorPlcReset) o;
+    return super.equals(that) && true;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(super.hashCode(), getData());
+    return Objects.hash(super.hashCode());
   }
 
   @Override
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcSubOperation.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcSubOperation.java
new file mode 100644
index 0000000000..2d04ea8294
--- /dev/null
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/MonitorPlcSubOperation.java
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+package org.apache.plc4x.java.umas.readwrite;
+
+import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
+import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
+import static org.apache.plc4x.java.spi.generation.StaticHelper.*;
+
+import java.time.*;
+import java.util.*;
+import org.apache.plc4x.java.api.exceptions.*;
+import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.spi.codegen.*;
+import org.apache.plc4x.java.spi.codegen.fields.*;
+import org.apache.plc4x.java.spi.codegen.io.*;
+import org.apache.plc4x.java.spi.generation.*;
+
+// Code generated by code-generation. DO NOT EDIT.
+
+/**
+ * Sub-operation within an FC 0x50 MonitorPLC request. Discriminated by the 
first byte (operation
+ * type).
+ */
+public abstract class MonitorPlcSubOperation implements Message {
+
+  // Abstract accessors for discriminator values.
+  public abstract Short getOperationType();
+
+  public MonitorPlcSubOperation() {
+    super();
+  }
+
+  protected abstract void serializeMonitorPlcSubOperationChild(WriteBuffer 
writeBuffer)
+      throws SerializationException;
+
+  public void serialize(WriteBuffer writeBuffer) throws SerializationException 
{
+    PositionAware positionAware = writeBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+    writeBuffer.pushContext("MonitorPlcSubOperation");
+
+    // Discriminator Field (operationType) (Used as input to a switch field)
+    writeDiscriminatorField(
+        "operationType",
+        getOperationType(),
+        writeUnsignedShort(writeBuffer, 8),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Switch field (Serialize the sub-type)
+    serializeMonitorPlcSubOperationChild(writeBuffer);
+
+    writeBuffer.popContext("MonitorPlcSubOperation");
+  }
+
+  @Override
+  public int getLengthInBytes() {
+    return (int) Math.ceil((float) getLengthInBits() / 8.0);
+  }
+
+  @Override
+  public int getLengthInBits() {
+    int lengthInBits = 0;
+    MonitorPlcSubOperation _value = this;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    // Discriminator Field (operationType)
+    lengthInBits += 8;
+
+    // Length of sub-type elements will be added by sub-type...
+
+    return lengthInBits;
+  }
+
+  public static MonitorPlcSubOperation staticParse(ReadBuffer readBuffer) 
throws ParseException {
+    readBuffer.pullContext("MonitorPlcSubOperation");
+    PositionAware positionAware = readBuffer;
+    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
+
+    short operationType =
+        readDiscriminatorField(
+            "operationType",
+            readUnsignedShort(readBuffer, 8),
+            WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Switch Field (Depending on the discriminator values, passes the 
instantiation to a sub-type)
+    MonitorPlcSubOperationBuilder builder = null;
+    if (EvaluationHelper.equals(operationType, (short) 0x05)) {
+      builder = 
MonitorPlcRegisterVariable.staticParseMonitorPlcSubOperationBuilder(readBuffer);
+    } else if (EvaluationHelper.equals(operationType, (short) 0x07)) {
+      builder = 
MonitorPlcReadAll.staticParseMonitorPlcSubOperationBuilder(readBuffer);
+    } else if (EvaluationHelper.equals(operationType, (short) 0x09)) {
+      builder = 
MonitorPlcRegisterAndRead.staticParseMonitorPlcSubOperationBuilder(readBuffer);
+    } else if (EvaluationHelper.equals(operationType, (short) 0x0B)) {
+      builder = 
MonitorPlcReset.staticParseMonitorPlcSubOperationBuilder(readBuffer);
+    }
+    if (builder == null) {
+      throw new ParseException(
+          "Unsupported case for discriminated type"
+              + " parameters ["
+              + "operationType="
+              + operationType
+              + "]");
+    }
+
+    readBuffer.closeContext("MonitorPlcSubOperation");
+    // Create the instance
+    MonitorPlcSubOperation _monitorPlcSubOperation = builder.build();
+    return _monitorPlcSubOperation;
+  }
+
+  public interface MonitorPlcSubOperationBuilder {
+    MonitorPlcSubOperation build();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof MonitorPlcSubOperation)) {
+      return false;
+    }
+    MonitorPlcSubOperation that = (MonitorPlcSubOperation) o;
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash();
+  }
+
+  @Override
+  public String toString() {
+    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, 
true);
+    try {
+      writeBufferBoxBased.writeSerializable(this);
+    } catch (SerializationException e) {
+      throw new RuntimeException(e);
+    }
+    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
+  }
+}
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasDatatypeReference.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasDatatypeReference.java
index 0ae5972912..bfafe173bf 100644
--- 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasDatatypeReference.java
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasDatatypeReference.java
@@ -95,6 +95,9 @@ public class UmasDatatypeReference implements Message {
     // Simple Field (dataType)
     writeSimpleField("dataType", dataType, writeUnsignedShort(writeBuffer, 8));
 
+    // Reserved Field (reserved)
+    writeReservedField("reserved", (short) 0x00, 
writeUnsignedShort(writeBuffer, 8));
+
     // Manual Field (value)
     writeManualField(
         "value",
@@ -129,6 +132,9 @@ public class UmasDatatypeReference implements Message {
     // Simple field (dataType)
     lengthInBits += 8;
 
+    // Reserved Field (reserved)
+    lengthInBits += 8;
+
     // Manual Field (value)
     lengthInBits += (((STR_LEN(value)) + (1))) * (8);
 
@@ -148,6 +154,9 @@ public class UmasDatatypeReference implements Message {
 
     short dataType = readSimpleField("dataType", readUnsignedShort(readBuffer, 
8));
 
+    Short reservedField0 =
+        readReservedField("reserved", readUnsignedShort(readBuffer, 8), 
(short) 0x00);
+
     String value =
         readManualField(
             "value",
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUItem.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUItem.java
index d0219184af..d167dc71d0 100644
--- 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUItem.java
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUItem.java
@@ -300,6 +300,11 @@ public abstract class UmasPDUItem implements Message {
       builder =
           UmasPDUMonitorPlcRequest.staticParseUmasPDUItemBuilder(
               readBuffer, umasRequestFunctionKey, byteLength);
+    } else if (EvaluationHelper.equals(umasFunctionKey, (short) 0xFE)
+        && EvaluationHelper.equals(umasRequestFunctionKey, (short) 0x50)) {
+      builder =
+          UmasPDUMonitorPlcResponse.staticParseUmasPDUItemBuilder(
+              readBuffer, umasRequestFunctionKey, byteLength);
     } else if (EvaluationHelper.equals(umasFunctionKey, (short) 0x58)) {
       builder =
           UmasPDUCheckPlcRequest.staticParseUmasPDUItemBuilder(
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
index 7f78bb4416..d8049eaadd 100644
--- 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
@@ -35,7 +35,12 @@ import org.apache.plc4x.java.spi.generation.*;
 
 // Code generated by code-generation. DO NOT EDIT.
 
-/** ---- FC 0x50: Monitor PLC ---- */
+/**
+ * ---- FC 0x50: Monitor PLC ---- Used by Schneider OPC UA Server Expert to 
read variable values
+ * from M580 PLCs (instead of FC 0x22 ReadVariable which returns error 
0xA1A1). The request contains
+ * a list of sub-operations: register variables for monitoring, read all 
registered values, or
+ * deregister variables.
+ */
 public class UmasPDUMonitorPlcRequest extends UmasPDUItem implements Message {
 
   // Accessors for discriminator values.
@@ -48,15 +53,38 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
   }
 
   // Properties.
-  protected final byte[] data;
-
-  public UmasPDUMonitorPlcRequest(short pairingKey, byte[] data) {
+  protected final short subCommand;
+  protected final short unknown;
+  protected final short numberOfSubOperations;
+  protected final List<MonitorPlcSubOperation> subOperations;
+
+  public UmasPDUMonitorPlcRequest(
+      short pairingKey,
+      short subCommand,
+      short unknown,
+      short numberOfSubOperations,
+      List<MonitorPlcSubOperation> subOperations) {
     super(pairingKey);
-    this.data = data;
+    this.subCommand = subCommand;
+    this.unknown = unknown;
+    this.numberOfSubOperations = numberOfSubOperations;
+    this.subOperations = subOperations;
+  }
+
+  public short getSubCommand() {
+    return subCommand;
+  }
+
+  public short getUnknown() {
+    return unknown;
   }
 
-  public byte[] getData() {
-    return data;
+  public short getNumberOfSubOperations() {
+    return numberOfSubOperations;
+  }
+
+  public List<MonitorPlcSubOperation> getSubOperations() {
+    return subOperations;
   }
 
   @Override
@@ -65,9 +93,30 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
     writeBuffer.pushContext("UmasPDUMonitorPlcRequest");
 
-    // Array Field (data)
-    writeByteArrayField(
-        "data", data, writeByteArray(writeBuffer, 8), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+    // Simple Field (subCommand)
+    writeSimpleField(
+        "subCommand",
+        subCommand,
+        writeUnsignedShort(writeBuffer, 8),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Simple Field (unknown)
+    writeSimpleField(
+        "unknown",
+        unknown,
+        writeUnsignedShort(writeBuffer, 8),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Simple Field (numberOfSubOperations)
+    writeSimpleField(
+        "numberOfSubOperations",
+        numberOfSubOperations,
+        writeUnsignedShort(writeBuffer, 8),
+        WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    // Array Field (subOperations)
+    writeComplexTypeArrayField(
+        "subOperations", subOperations, writeBuffer, 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
 
     writeBuffer.popContext("UmasPDUMonitorPlcRequest");
   }
@@ -83,9 +132,22 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
     UmasPDUMonitorPlcRequest _value = this;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
+    // Simple field (subCommand)
+    lengthInBits += 8;
+
+    // Simple field (unknown)
+    lengthInBits += 8;
+
+    // Simple field (numberOfSubOperations)
+    lengthInBits += 8;
+
     // Array field
-    if (data != null) {
-      lengthInBits += 8 * data.length;
+    if (subOperations != null) {
+      int i = 0;
+      for (MonitorPlcSubOperation element : subOperations) {
+        ThreadLocalHelper.lastItemThreadLocal.set(++i >= subOperations.size());
+        lengthInBits += element.getLengthInBits();
+      }
     }
 
     return lengthInBits;
@@ -98,26 +160,57 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
     PositionAware positionAware = readBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
-    byte[] data =
-        readBuffer.readByteArray(
-            "data", Math.toIntExact((byteLength) - (2)), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+    short subCommand =
+        readSimpleField(
+            "subCommand",
+            readUnsignedShort(readBuffer, 8),
+            WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    short unknown =
+        readSimpleField(
+            "unknown", readUnsignedShort(readBuffer, 8), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    short numberOfSubOperations =
+        readSimpleField(
+            "numberOfSubOperations",
+            readUnsignedShort(readBuffer, 8),
+            WithOption.WithByteOrder("LITTLE_ENDIAN"));
+
+    List<MonitorPlcSubOperation> subOperations =
+        readCountArrayField(
+            "subOperations",
+            readComplex(() -> MonitorPlcSubOperation.staticParse(readBuffer), 
readBuffer),
+            numberOfSubOperations,
+            WithOption.WithByteOrder("LITTLE_ENDIAN"));
 
     readBuffer.closeContext("UmasPDUMonitorPlcRequest");
     // Create the instance
-    return new UmasPDUMonitorPlcRequestBuilderImpl(data);
+    return new UmasPDUMonitorPlcRequestBuilderImpl(
+        subCommand, unknown, numberOfSubOperations, subOperations);
   }
 
   public static class UmasPDUMonitorPlcRequestBuilderImpl
       implements UmasPDUItem.UmasPDUItemBuilder {
-    private final byte[] data;
-
-    public UmasPDUMonitorPlcRequestBuilderImpl(byte[] data) {
-      this.data = data;
+    private final short subCommand;
+    private final short unknown;
+    private final short numberOfSubOperations;
+    private final List<MonitorPlcSubOperation> subOperations;
+
+    public UmasPDUMonitorPlcRequestBuilderImpl(
+        short subCommand,
+        short unknown,
+        short numberOfSubOperations,
+        List<MonitorPlcSubOperation> subOperations) {
+      this.subCommand = subCommand;
+      this.unknown = unknown;
+      this.numberOfSubOperations = numberOfSubOperations;
+      this.subOperations = subOperations;
     }
 
     public UmasPDUMonitorPlcRequest build(short pairingKey) {
       UmasPDUMonitorPlcRequest umasPDUMonitorPlcRequest =
-          new UmasPDUMonitorPlcRequest(pairingKey, data);
+          new UmasPDUMonitorPlcRequest(
+              pairingKey, subCommand, unknown, numberOfSubOperations, 
subOperations);
       return umasPDUMonitorPlcRequest;
     }
   }
@@ -131,12 +224,22 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
       return false;
     }
     UmasPDUMonitorPlcRequest that = (UmasPDUMonitorPlcRequest) o;
-    return (getData() == that.getData()) && super.equals(that) && true;
+    return (getSubCommand() == that.getSubCommand())
+        && (getUnknown() == that.getUnknown())
+        && (getNumberOfSubOperations() == that.getNumberOfSubOperations())
+        && (getSubOperations() == that.getSubOperations())
+        && super.equals(that)
+        && true;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(super.hashCode(), getData());
+    return Objects.hash(
+        super.hashCode(),
+        getSubCommand(),
+        getUnknown(),
+        getNumberOfSubOperations(),
+        getSubOperations());
   }
 
   @Override
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcResponse.java
similarity index 63%
copy from 
plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
copy to 
plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcResponse.java
index 7f78bb4416..9a2bc4ec98 100644
--- 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcRequest.java
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/UmasPDUMonitorPlcResponse.java
@@ -35,41 +35,45 @@ import org.apache.plc4x.java.spi.generation.*;
 
 // Code generated by code-generation. DO NOT EDIT.
 
-/** ---- FC 0x50: Monitor PLC ---- */
-public class UmasPDUMonitorPlcRequest extends UmasPDUItem implements Message {
+/**
+ * Response payload is a flat byte array because the per-variable value sizes 
depend on the data
+ * types of the registered variables, which are only known at the driver layer 
(via the symbol
+ * table). The driver parses the raw bytes using DataItem and the registered 
variable types.
+ */
+public class UmasPDUMonitorPlcResponse extends UmasPDUItem implements Message {
 
   // Accessors for discriminator values.
   public Short getUmasFunctionKey() {
-    return (short) 0x50;
+    return (short) 0xFE;
   }
 
   public Short getUmasRequestFunctionKey() {
-    return 0;
+    return (short) 0x50;
   }
 
   // Properties.
-  protected final byte[] data;
+  protected final byte[] block;
 
-  public UmasPDUMonitorPlcRequest(short pairingKey, byte[] data) {
+  public UmasPDUMonitorPlcResponse(short pairingKey, byte[] block) {
     super(pairingKey);
-    this.data = data;
+    this.block = block;
   }
 
-  public byte[] getData() {
-    return data;
+  public byte[] getBlock() {
+    return block;
   }
 
   @Override
   protected void serializeUmasPDUItemChild(WriteBuffer writeBuffer) throws 
SerializationException {
     PositionAware positionAware = writeBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
-    writeBuffer.pushContext("UmasPDUMonitorPlcRequest");
+    writeBuffer.pushContext("UmasPDUMonitorPlcResponse");
 
-    // Array Field (data)
+    // Array Field (block)
     writeByteArrayField(
-        "data", data, writeByteArray(writeBuffer, 8), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+        "block", block, writeByteArray(writeBuffer, 8), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
 
-    writeBuffer.popContext("UmasPDUMonitorPlcRequest");
+    writeBuffer.popContext("UmasPDUMonitorPlcResponse");
   }
 
   @Override
@@ -80,12 +84,12 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
   @Override
   public int getLengthInBits() {
     int lengthInBits = super.getLengthInBits();
-    UmasPDUMonitorPlcRequest _value = this;
+    UmasPDUMonitorPlcResponse _value = this;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
     // Array field
-    if (data != null) {
-      lengthInBits += 8 * data.length;
+    if (block != null) {
+      lengthInBits += 8 * block.length;
     }
 
     return lengthInBits;
@@ -94,31 +98,33 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
   public static UmasPDUItemBuilder staticParseUmasPDUItemBuilder(
       ReadBuffer readBuffer, Short umasRequestFunctionKey, Integer byteLength)
       throws ParseException {
-    readBuffer.pullContext("UmasPDUMonitorPlcRequest");
+    readBuffer.pullContext("UmasPDUMonitorPlcResponse");
     PositionAware positionAware = readBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
 
-    byte[] data =
+    byte[] block =
         readBuffer.readByteArray(
-            "data", Math.toIntExact((byteLength) - (2)), 
WithOption.WithByteOrder("LITTLE_ENDIAN"));
+            "block",
+            Math.toIntExact((byteLength) - (2)),
+            WithOption.WithByteOrder("LITTLE_ENDIAN"));
 
-    readBuffer.closeContext("UmasPDUMonitorPlcRequest");
+    readBuffer.closeContext("UmasPDUMonitorPlcResponse");
     // Create the instance
-    return new UmasPDUMonitorPlcRequestBuilderImpl(data);
+    return new UmasPDUMonitorPlcResponseBuilderImpl(block);
   }
 
-  public static class UmasPDUMonitorPlcRequestBuilderImpl
+  public static class UmasPDUMonitorPlcResponseBuilderImpl
       implements UmasPDUItem.UmasPDUItemBuilder {
-    private final byte[] data;
+    private final byte[] block;
 
-    public UmasPDUMonitorPlcRequestBuilderImpl(byte[] data) {
-      this.data = data;
+    public UmasPDUMonitorPlcResponseBuilderImpl(byte[] block) {
+      this.block = block;
     }
 
-    public UmasPDUMonitorPlcRequest build(short pairingKey) {
-      UmasPDUMonitorPlcRequest umasPDUMonitorPlcRequest =
-          new UmasPDUMonitorPlcRequest(pairingKey, data);
-      return umasPDUMonitorPlcRequest;
+    public UmasPDUMonitorPlcResponse build(short pairingKey) {
+      UmasPDUMonitorPlcResponse umasPDUMonitorPlcResponse =
+          new UmasPDUMonitorPlcResponse(pairingKey, block);
+      return umasPDUMonitorPlcResponse;
     }
   }
 
@@ -127,16 +133,16 @@ public class UmasPDUMonitorPlcRequest extends UmasPDUItem 
implements Message {
     if (this == o) {
       return true;
     }
-    if (!(o instanceof UmasPDUMonitorPlcRequest)) {
+    if (!(o instanceof UmasPDUMonitorPlcResponse)) {
       return false;
     }
-    UmasPDUMonitorPlcRequest that = (UmasPDUMonitorPlcRequest) o;
-    return (getData() == that.getData()) && super.equals(that) && true;
+    UmasPDUMonitorPlcResponse that = (UmasPDUMonitorPlcResponse) o;
+    return (getBlock() == that.getBlock()) && super.equals(that) && true;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(super.hashCode(), getData());
+    return Objects.hash(super.hashCode(), getBlock());
   }
 
   @Override
diff --git 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/VariableWriteRequestReference.java
 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/VariableWriteRequestReference.java
index c745547211..6a50c85d20 100644
--- 
a/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/VariableWriteRequestReference.java
+++ 
b/plc4j/drivers/umas/src/main/generated/org/apache/plc4x/java/umas/readwrite/VariableWriteRequestReference.java
@@ -35,7 +35,11 @@ import org.apache.plc4x.java.spi.generation.*;
 
 // Code generated by code-generation. DO NOT EDIT.
 
-/** Variable write request reference — identifies a variable to write with 
data */
+/**
+ * Variable write request reference — identifies a variable to write with 
data. The dataSizeIndex is
+ * a size index (not a byte count): 1→1B, 2→2B, 3→4B, 4→8B. The formula 
2^(index-1) converts the
+ * index to the actual byte size.
+ */
 public class VariableWriteRequestReference implements Message {
 
   // Properties.
@@ -93,6 +97,12 @@ public class VariableWriteRequestReference implements 
Message {
     return recordData;
   }
 
+  public int getDataSize() {
+    return (int)
+        
(org.apache.plc4x.java.umas.readwrite.utils.StaticHelper.writeSizeIndexToByteCount(
+            getDataSizeIndex()));
+  }
+
   public void serialize(WriteBuffer writeBuffer) throws SerializationException 
{
     PositionAware positionAware = writeBuffer;
     boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
@@ -116,6 +126,10 @@ public class VariableWriteRequestReference implements 
Message {
     // Optional Field (arrayLength) (Can be skipped, if the value is null)
     writeOptionalField("arrayLength", arrayLength, 
writeUnsignedInt(writeBuffer, 16));
 
+    // Virtual field (doesn't serialize anything, just makes the value 
available)
+    int dataSize = getDataSize();
+    writeBuffer.writeVirtual("dataSize", dataSize);
+
     // Array Field (recordData)
     writeByteArrayField("recordData", recordData, writeByteArray(writeBuffer, 
8));
 
@@ -153,6 +167,8 @@ public class VariableWriteRequestReference implements 
Message {
       lengthInBits += 16;
     }
 
+    // A virtual field doesn't have any in- or output.
+
     // Array field
     if (recordData != null) {
       lengthInBits += 8 * recordData.length;
@@ -179,12 +195,17 @@ public class VariableWriteRequestReference implements 
Message {
 
     Integer arrayLength =
         readOptionalField("arrayLength", readUnsignedInt(readBuffer, 16), 
(isArray) != (0));
+    int dataSize =
+        readVirtualField(
+            "dataSize",
+            int.class,
+            
org.apache.plc4x.java.umas.readwrite.utils.StaticHelper.writeSizeIndexToByteCount(
+                dataSizeIndex));
 
     byte[] recordData =
         readBuffer.readByteArray(
             "recordData",
-            Math.toIntExact(
-                (((isArray) == (1)) ? (dataSizeIndex) * (arrayLength) : 
dataSizeIndex)));
+            Math.toIntExact((((isArray) == (1)) ? (dataSize) * (arrayLength) : 
dataSize)));
 
     readBuffer.closeContext("VariableWriteRequestReference");
     // Create the instance
diff --git 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/configuration/UmasConfiguration.java
 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/configuration/UmasConfiguration.java
index 6657512932..df9d38cf1a 100644
--- 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/configuration/UmasConfiguration.java
+++ 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/configuration/UmasConfiguration.java
@@ -21,6 +21,7 @@ package org.apache.plc4x.java.umas.readwrite.configuration;
 import org.apache.plc4x.java.spi.configuration.PlcConnectionConfiguration;
 import 
org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
 import org.apache.plc4x.java.spi.configuration.annotations.Description;
+import 
org.apache.plc4x.java.spi.configuration.annotations.defaults.BooleanDefaultValue;
 import 
org.apache.plc4x.java.spi.configuration.annotations.defaults.IntDefaultValue;
 
 public class UmasConfiguration implements PlcConnectionConfiguration {
@@ -64,12 +65,26 @@ public class UmasConfiguration implements 
PlcConnectionConfiguration {
         this.maxFrameSize = maxFrameSize;
     }
 
+    @ConfigurationParameter("browser-generate-array-nodes")
+    @BooleanDefaultValue(true)
+    @Description("Tells the browser to generate artificial child nodes 
representing individual array elements.")
+    private boolean browserGenerateArrayNodes;
+
+    public boolean isBrowserGenerateArrayNodes() {
+        return browserGenerateArrayNodes;
+    }
+
+    public void setBrowserGenerateArrayNodes(boolean 
browserGenerateArrayNodes) {
+        this.browserGenerateArrayNodes = browserGenerateArrayNodes;
+    }
+
     @Override
     public String toString() {
         return "UmasConfiguration{" +
             "unitIdentifier=" + unitIdentifier +
             ", requestTimeout=" + requestTimeout +
             ", maxFrameSize=" + maxFrameSize +
+            ", browserGenerateArrayNodes=" + browserGenerateArrayNodes +
             '}';
     }
 
diff --git 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/context/UmasDriverContext.java
 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/context/UmasDriverContext.java
index aa85ee5dc7..f13fa36da2 100644
--- 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/context/UmasDriverContext.java
+++ 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/context/UmasDriverContext.java
@@ -72,6 +72,9 @@ public class UmasDriverContext implements DriverContext, 
HasConfiguration<UmasCo
     // Data type table: type ID -> UmasDataType
     private final Map<Integer, UmasDataType> dataTypeTable = new 
ConcurrentHashMap<>();
 
+    // Data type sizes from DD03: type ID -> allocated byte size
+    private final Map<Integer, Integer> dataTypeSizes = new 
ConcurrentHashMap<>();
+
     @Override
     public void setConfiguration(UmasConfiguration configuration) {
         this.configuration = configuration;
@@ -213,4 +216,28 @@ public class UmasDriverContext implements DriverContext, 
HasConfiguration<UmasCo
         return Collections.unmodifiableMap(dataTypeTable);
     }
 
+    // --- Data type size operations (from DD03) ---
+
+    /**
+     * Stores the allocated byte size for a data type from the DD03 data 
dictionary.
+     * This is the total memory footprint, important for STRING and custom 
types
+     * where the size is not derivable from the UmasDataType enum alone.
+     *
+     * @param typeId   the data type identifier
+     * @param dataSize the allocated byte size from 
UmasDatatypeReference.dataSize
+     */
+    public void addDataTypeSize(int typeId, int dataSize) {
+        dataTypeSizes.put(typeId, dataSize);
+    }
+
+    /**
+     * Returns the allocated byte size for a data type, or empty if not 
registered.
+     *
+     * @param typeId the data type identifier
+     * @return the allocated byte size
+     */
+    public Optional<Integer> getDataTypeSize(int typeId) {
+        return Optional.ofNullable(dataTypeSizes.get(typeId));
+    }
+
 }
diff --git 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/protocol/UmasProtocolLogic.java
 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/protocol/UmasProtocolLogic.java
index 4514d99e8b..4f9e6e3895 100644
--- 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/protocol/UmasProtocolLogic.java
+++ 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/protocol/UmasProtocolLogic.java
@@ -38,7 +38,12 @@ import 
org.apache.plc4x.java.spi.messages.utils.DefaultPlcResponseItem;
 import org.apache.plc4x.java.spi.messages.utils.PlcResponseItem;
 import org.apache.plc4x.java.spi.model.DefaultArrayInfo;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
+import org.apache.plc4x.java.spi.values.PlcDATE;
+import org.apache.plc4x.java.spi.values.PlcDATE_AND_TIME;
 import org.apache.plc4x.java.spi.values.PlcRawByteArray;
+import org.apache.plc4x.java.spi.values.PlcSTRING;
+import org.apache.plc4x.java.spi.values.PlcTIME;
+import org.apache.plc4x.java.spi.values.PlcTIME_OF_DAY;
 import org.apache.plc4x.java.umas.readwrite.*;
 import org.apache.plc4x.java.umas.readwrite.UmasFunctionKeyTracker;
 import org.apache.plc4x.java.umas.readwrite.configuration.UmasConfiguration;
@@ -118,6 +123,18 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
             .thenCompose(v -> performProjectInfoAsync(context, (short) 4))
             .thenCompose(v -> performProjectInfoAsync(context, (short) 1))
             .thenCompose(v -> performProjectInfoAsync(context, (short) 3))
+            // Eagerly download data dictionary (types + symbols) so
+            // read/write/browse can work immediately without a separate 
browse call.
+            // Must run off the Netty event loop because the download methods 
use
+            // blocking get() calls that would deadlock the event loop.
+            .thenCompose(v -> CompletableFuture.runAsync(() -> {
+                try {
+                    loadDataDictionary();
+                } catch (Exception e) {
+                    LOGGER.warn("Failed to eagerly load data dictionary during 
connect: {}", e.getMessage());
+                    // Non-fatal: browse will download on first use
+                }
+            }))
             .thenAccept(v -> {
                 LOGGER.info("UMAS connection established to PLC: hostname={}, 
model={}, firmware={}",
                     umasDriverContext.getPlcHostname(), 
umasDriverContext.getPlcModel(),
@@ -189,11 +206,29 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
                 if (response instanceof UmasPDUReadMemoryBlockResponse 
readResponse) {
                     byte[] block = readResponse.getBlock();
                     LOGGER.info("{}: {} bytes received", stepName, block != 
null ? block.length : 0);
-                    if (blockNumber == 0x30 && block != null && block.length 
>= 9) {
+                    if (blockNumber == 0x30 && block != null && block.length 
>= 17) {
+                        // UmasMemoryBlockBasicInfo: range(2) + notSure(2) + 
index(1) + hardwareId(4) = 9 bytes
                         long hardwareId = (block[5] & 0xFFL) | ((block[6] & 
0xFFL) << 8)
                             | ((block[7] & 0xFFL) << 16) | ((block[8] & 0xFFL) 
<< 24);
                         umasDriverContext.setHardwareId(hardwareId);
-                        LOGGER.info("{}: extracted hardwareId=0x{}", stepName, 
String.format("%08X", hardwareId));
+
+                        // Block 0x30 layout after basic info (9 bytes): 
hash1(4) + hash2(4) + ...
+                        // The project CRC used in FC 0x22/0x23 read/write 
requests is the
+                        // SUM of hash1 and hash2 (discovered by comparing 
working Schneider
+                        // OPC UA Server traffic with the raw block 0x30 
values).
+                        long hash1 = (block[9] & 0xFFL) | ((block[10] & 0xFFL) 
<< 8)
+                            | ((block[11] & 0xFFL) << 16) | ((block[12] & 
0xFFL) << 24);
+                        long hash2 = (block[13] & 0xFFL) | ((block[14] & 
0xFFL) << 8)
+                            | ((block[15] & 0xFFL) << 16) | ((block[16] & 
0xFFL) << 24);
+                        long projectCrc = (hash1 + hash2) & 0xFFFFFFFFL;
+                        umasDriverContext.setProjectCrc(projectCrc);
+
+                        LOGGER.info("{}: hardwareId=0x{}, hash1=0x{}, 
hash2=0x{}, projectCRC=0x{}",
+                            stepName,
+                            String.format("%08X", hardwareId),
+                            String.format("%08X", hash1),
+                            String.format("%08X", hash2),
+                            String.format("%08X", projectCrc));
                     }
                 } else {
                     LOGGER.warn("{}: unexpected response type: {}", stepName, 
response.getClass().getSimpleName());
@@ -347,8 +382,29 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
         }
     }
 
+    private static final int DEFAULT_STRING_BUFFER_SIZE = 254;
+
     private VariableReadRequestReference 
buildReadReference(UmasUnlocatedVariableReference symbol) {
         int dataTypeId = symbol.getDataType();
+
+        // The symbol's 32-bit offset encodes two fields:
+        //   - lower 8 bits  → offset (uint 8 in VariableReadRequestReference)
+        //   - upper bits     → baseOffset (uint 16 in 
VariableReadRequestReference)
+        long symbolOffset = symbol.getOffset();
+        int baseOffset = (int) (symbolOffset >> 8);
+        short offset = (short) (symbolOffset & 0xFF);
+
+        // STRING: requestSize=17 doesn't fit in the 4-bit dataSizeIndex field.
+        // Read as a byte array instead: isArray=1, dataSizeIndex=1, 
arrayLength=bufferSize.
+        if (UmasDataType.isDefined((short) dataTypeId)
+                && UmasDataType.enumForValue((short) dataTypeId) == 
UmasDataType.STRING) {
+            int stringSize = umasDriverContext.getDataTypeSize(dataTypeId)
+                .orElse(DEFAULT_STRING_BUFFER_SIZE);
+            return new VariableReadRequestReference(
+                (byte) 1, (byte) 1, symbol.getBlock(),
+                baseOffset, offset, stringSize);
+        }
+
         byte dataSizeIndex;
         if (UmasDataType.isDefined((short) dataTypeId)) {
             UmasDataType umasType = UmasDataType.enumForValue((short) 
dataTypeId);
@@ -356,9 +412,10 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
         } else {
             dataSizeIndex = (byte) 3;
         }
+
         return new VariableReadRequestReference(
             (byte) 0, dataSizeIndex, symbol.getBlock(),
-            (int) symbol.getOffset(), (short) 0, null);
+            baseOffset, offset, null);
     }
 
     private PlcValue parseReadResponse(UmasUnlocatedVariableReference symbol, 
byte[] block) throws Exception {
@@ -368,8 +425,57 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
         int dataTypeId = symbol.getDataType();
         if (UmasDataType.isDefined((short) dataTypeId)) {
             UmasDataType umasType = UmasDataType.enumForValue((short) 
dataTypeId);
-            ReadBuffer readBuffer = new ReadBufferByteBased(block, 
ByteOrder.LITTLE_ENDIAN);
-            return DataItem.staticParse(readBuffer, umasType, 1);
+
+            // Types that need manual parsing because the generated DataItem 
code
+            // either doesn't return a PlcValue (temporal types) or uses the 
wrong
+            // read strategy (STRING).
+            switch (umasType) {
+                case STRING: {
+                    // STRING is read as a byte array; extract null-terminated 
content
+                    for (int i = 0; i < block.length; i++) {
+                        if (block[i] == 0x00) {
+                            return new PlcSTRING(new String(block, 0, i, 
StandardCharsets.UTF_8));
+                        }
+                    }
+                    return new PlcSTRING(new String(block, 
StandardCharsets.UTF_8));
+                }
+                case TIME: {
+                    // TIME is stored as uint32 milliseconds (little-endian)
+                    long millis = readUint32LE(block);
+                    return new PlcTIME(millis);
+                }
+                case DATE: {
+                    // DATE is BCD-encoded: day(1) + month(1) + year(2 LE)
+                    int day = decodeBcdByte(block[0]);
+                    int month = decodeBcdByte(block[1]);
+                    int year = decodeBcd16(block[2], block[3]);
+                    return new PlcDATE(java.time.LocalDate.of(year, month, 
day));
+                }
+                case TOD: {
+                    // TOD is BCD-encoded: centiseconds(1) + seconds(1) + 
minutes(1) + hours(1)
+                    int secs = decodeBcdByte(block[1]);
+                    int mins = decodeBcdByte(block[2]);
+                    int hours = decodeBcdByte(block[3]);
+                    long totalSeconds = hours * 3600L + mins * 60L + secs;
+                    return new PlcTIME_OF_DAY(totalSeconds);
+                }
+                case DATE_AND_TIME: {
+                    // DT is 8 bytes: reserved(1) + seconds(1 BCD) + minutes(1 
BCD)
+                    // + hour(1 BCD) + day(1 BCD) + month(1 BCD) + year(2 BCD 
LE)
+                    int seconds = decodeBcdByte(block[1]);
+                    int minutes = decodeBcdByte(block[2]);
+                    int hour = decodeBcdByte(block[3]);
+                    int dtDay = decodeBcdByte(block[4]);
+                    int dtMonth = decodeBcdByte(block[5]);
+                    int dtYear = decodeBcd16(block[6], block[7]);
+                    return new PlcDATE_AND_TIME(java.time.LocalDateTime.of(
+                        dtYear, dtMonth, dtDay, hour, minutes, seconds));
+                }
+                default: {
+                    ReadBuffer readBuffer = new ReadBufferByteBased(block, 
ByteOrder.LITTLE_ENDIAN);
+                    return DataItem.staticParse(readBuffer, umasType, 1);
+                }
+            }
         }
         return new PlcRawByteArray(block);
     }
@@ -458,16 +564,34 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
 
     private VariableWriteRequestReference 
buildWriteReference(UmasUnlocatedVariableReference symbol, byte[] data) {
         int dataTypeId = symbol.getDataType();
+
+        // The symbol's 32-bit offset encodes two fields:
+        //   - lower 8 bits  → offset (uint 16 in 
VariableWriteRequestReference)
+        //   - upper bits     → baseOffset (uint 16 in 
VariableWriteRequestReference)
+        long symbolOffset = symbol.getOffset();
+        int baseOffset = (int) (symbolOffset >> 8);
+        int offset = (int) (symbolOffset & 0xFF);
+
+        // STRING: requestSize=17 doesn't fit in 4-bit dataSizeIndex.
+        // Write as byte array: isArray=1, dataSizeIndex=1, 
arrayLength=data.length.
+        if (UmasDataType.isDefined((short) dataTypeId)
+                && UmasDataType.enumForValue((short) dataTypeId) == 
UmasDataType.STRING) {
+            return new VariableWriteRequestReference(
+                (byte) 1, (byte) 1, symbol.getBlock(),
+                baseOffset, offset, data.length, data);
+        }
+
         byte dataSizeIndex;
         if (UmasDataType.isDefined((short) dataTypeId)) {
             UmasDataType umasType = UmasDataType.enumForValue((short) 
dataTypeId);
-            dataSizeIndex = (byte) umasType.getDataTypeSize();
+            dataSizeIndex = (byte) umasType.getRequestSize();
         } else {
-            dataSizeIndex = (byte) data.length;
+            dataSizeIndex = (byte) 3;
         }
+
         return new VariableWriteRequestReference(
             (byte) 0, dataSizeIndex, symbol.getBlock(),
-            (int) symbol.getOffset(), 0, null, data);
+            baseOffset, offset, null, data);
     }
 
     private byte[] serializeValue(UmasUnlocatedVariableReference symbol, 
PlcValue value) throws PlcConnectionException {
@@ -615,23 +739,37 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
         return future;
     }
 
-    private List<PlcBrowseItem> executeBrowse() throws Exception {
-        // Phase 1: Download datatype names
+    /**
+     * Downloads the data dictionary (types + symbols) into the driver context.
+     * Called during handshake so the symbol table is available for read/write 
immediately.
+     */
+    private void loadDataDictionary() throws Exception {
+        LOGGER.info("Loading data dictionary (types + symbols)...");
+
         List<UmasDatatypeReference> datatypeRefs = downloadDatatypeNames();
-        LOGGER.info("Browse: downloaded {} datatype references", 
datatypeRefs.size());
+        LOGGER.info("Data dictionary: downloaded {} datatype references", 
datatypeRefs.size());
 
-        // Phase 2: Resolve custom types
         resolveCustomTypes(datatypeRefs);
 
-        // Phase 3: Download symbol table
         List<UmasUnlocatedVariableReference> symbols = downloadSymbolTable();
-        LOGGER.info("Browse: downloaded {} symbols", symbols.size());
+        LOGGER.info("Data dictionary: downloaded {} symbols", symbols.size());
 
         for (UmasUnlocatedVariableReference symbol : symbols) {
             umasDriverContext.addSymbol(symbol.getValue(), symbol);
         }
 
-        return convertToBrowseItems(symbols);
+        LOGGER.info("Data dictionary loaded: {} symbols", 
umasDriverContext.getSymbolCount());
+    }
+
+    private List<PlcBrowseItem> executeBrowse() throws Exception {
+        // If symbol table is empty, download data dictionary
+        if (umasDriverContext.getSymbolCount() == 0) {
+            loadDataDictionary();
+        }
+
+        // Convert cached symbols to browse items
+        return convertToBrowseItems(
+            new ArrayList<>(umasDriverContext.getSymbolTable().values()));
     }
 
     private List<UmasDatatypeReference> downloadDatatypeNames() throws 
Exception {
@@ -679,6 +817,10 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
             if (UmasDataType.isDefined(primitiveId)) {
                 umasDriverContext.addDataType(typeId, 
UmasDataType.enumForValue(primitiveId));
             }
+            // Store the allocated byte size from the data dictionary 
(important for
+            // STRING buffers and custom struct types where the size is not 
derivable
+            // from the UmasDataType enum alone)
+            umasDriverContext.addDataTypeSize(typeId, ref.getDataSize());
         }
 
         for (int i = 0; i < datatypeRefs.size(); i++) {
@@ -879,6 +1021,22 @@ public class UmasProtocolLogic extends 
Plc4xProtocolBase<ModbusTcpADU> implement
         return new ModbusTcpADU(transactionId, unitIdentifier, umasPdu);
     }
 
+    /** Reads a little-endian uint32 from the first 4 bytes of a block. */
+    private static long readUint32LE(byte[] block) {
+        return (block[0] & 0xFFL) | ((block[1] & 0xFFL) << 8)
+            | ((block[2] & 0xFFL) << 16) | ((block[3] & 0xFFL) << 24);
+    }
+
+    /** Decodes a single BCD-encoded byte (e.g. 0x25 → 25). */
+    private static int decodeBcdByte(byte b) {
+        return ((b >> 4) & 0x0F) * 10 + (b & 0x0F);
+    }
+
+    /** Decodes a BCD-encoded uint16 LE from 2 bytes (e.g. 0x20, 0x25 → 2025). 
*/
+    private static int decodeBcd16(byte lo, byte hi) {
+        return decodeBcdByte(hi) * 100 + decodeBcdByte(lo);
+    }
+
     private UmasPDUItem extractUmasResponse(ModbusTcpADU response, String 
stepName) throws PlcConnectionException {
         ModbusPDU pdu = response.getPdu();
         if (pdu instanceof ModbusPDUError errorPdu) {
diff --git 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/tag/SymbolicUmasTag.java
 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/tag/SymbolicUmasTag.java
index 4e477c4cfa..8b07cc0943 100644
--- 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/tag/SymbolicUmasTag.java
+++ 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/tag/SymbolicUmasTag.java
@@ -74,7 +74,10 @@ public class SymbolicUmasTag implements UmasTag {
 
     @Override
     public PlcValueType getPlcValueType() {
-        return dataType != null ? dataType : PlcValueType.NULL;
+        // Return null (not PlcValueType.NULL) when type is unknown so the
+        // DefaultPlcValueHandler preserves the original PlcValue on writes
+        // instead of discarding it as PlcNull.
+        return dataType;
     }
 
     @Override
diff --git 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/utils/StaticHelper.java
 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/utils/StaticHelper.java
index 38ffc0dc74..b98a0728fe 100644
--- 
a/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/utils/StaticHelper.java
+++ 
b/plc4j/drivers/umas/src/main/java/org/apache/plc4x/java/umas/readwrite/utils/StaticHelper.java
@@ -104,6 +104,28 @@ public class StaticHelper {
         serializeTerminatedString(writeBuffer, value.getString(), 
stringLength);
     }
 
+    /**
+     * Converts a UMAS write size index to the actual byte count.
+     * The UMAS protocol uses an index encoding for data sizes:
+     * 1 → 1 byte, 2 → 2 bytes, 3 → 4 bytes, 4 → 8 bytes (= 2^(index-1)).
+     * For STRING (index 17), returns 1 (per-character size; actual length
+     * is determined by the arrayLength field in the write reference).
+     *
+     * @param sizeIndex the UMAS size index (matches UmasDataType.requestSize)
+     * @return the byte count for this size index
+     */
+    public static int writeSizeIndexToByteCount(int sizeIndex) {
+        if (sizeIndex == 17) {
+            // STRING: per-character size is 1 byte
+            return 1;
+        }
+        if (sizeIndex >= 1 && sizeIndex <= 4) {
+            return 1 << (sizeIndex - 1);
+        }
+        // Fallback: treat index as direct byte count
+        return sizeIndex;
+    }
+
     /**
      * Parses a null-terminated string from the buffer as raw bytes, used by 
the STRING
      * data type in the DataItem dataIo. Reads up to {@code numberOfValues} 
bytes and
diff --git 
a/plc4j/drivers/umas/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
 
b/plc4j/drivers/umas/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
index 3998f7c828..39e82a5539 100644
--- 
a/plc4j/drivers/umas/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
+++ 
b/plc4j/drivers/umas/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
@@ -1 +1,19 @@
+#
+# 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.
+#
 org.apache.plc4x.java.umas.readwrite.UmasDriver
diff --git 
a/plc4j/drivers/umas/src/test/java/org/apache/plc4x/java/umas/manual/ManualUmasBrowse.java
 
b/plc4j/drivers/umas/src/test/java/org/apache/plc4x/java/umas/manual/ManualUmasBrowse.java
index 20ff46e1e5..6f2af5ff92 100644
--- 
a/plc4j/drivers/umas/src/test/java/org/apache/plc4x/java/umas/manual/ManualUmasBrowse.java
+++ 
b/plc4j/drivers/umas/src/test/java/org/apache/plc4x/java/umas/manual/ManualUmasBrowse.java
@@ -1,3 +1,21 @@
+/*
+ * 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.
+ */
 package org.apache.plc4x.java.umas.manual;
 
 import org.apache.plc4x.java.api.PlcConnection;
diff --git 
a/plc4j/drivers/umas/src/test/java/org/apache/plc4x/java/umas/manual/ManualUmasDriverTest.java
 
b/plc4j/drivers/umas/src/test/java/org/apache/plc4x/java/umas/manual/ManualUmasDriverTest.java
new file mode 100644
index 0000000000..e852193e45
--- /dev/null
+++ 
b/plc4j/drivers/umas/src/test/java/org/apache/plc4x/java/umas/manual/ManualUmasDriverTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+package org.apache.plc4x.java.umas.manual;
+
+import org.apache.plc4x.java.spi.values.*;
+import org.apache.plc4x.test.manual.ManualTest;
+
+import java.time.*;
+
+public class ManualUmasDriverTest extends ManualTest {
+
+    public ManualUmasDriverTest(String connectionString) {
+        super(connectionString, true, true, true, true, 100);
+    }
+
+    public static void main(String[] args) throws Exception {
+        String spsIp = "192.168.42.99";
+        String connectionString = String.format("umas://%s", spsIp);
+        ManualUmasDriverTest test = new ManualUmasDriverTest(connectionString);
+
+        // =========================================================
+        // 1) Scalars — all primitive PlcValueTypes that are supported on the 
UMAS device
+        // =========================================================
+        test.addTestCase("g_b1",      new PlcBOOL(true));
+        test.addTestCase("g_b8",      new PlcBYTE(0xAB));
+        test.addTestCase("g_b16",     new PlcWORD(0xBEEF));
+        test.addTestCase("g_s16",     new PlcINT(-1234));
+        test.addTestCase("g_u16",     new PlcUINT(54321));
+        test.addTestCase("g_b32",     new PlcDWORD(0xDEADBEEFL));
+        test.addTestCase("g_s32",     new PlcDINT(-12345678));
+        test.addTestCase("g_u32",     new PlcUDINT(305_419_896L)); // 
0x12345678
+        test.addTestCase("g_r32",     new PlcREAL(3.14159f));
+        test.addTestCase("g_tim",     new 
PlcTIME(java.time.Duration.parse("PT2.5S")));
+        test.addTestCase("g_dat",     new 
PlcDATE(java.time.LocalDate.parse("2025-11-12")));
+        test.addTestCase("g_timoday", new 
PlcTIME_OF_DAY(java.time.LocalTime.parse("14:33:21")));
+        test.addTestCase("g_dattim",  new 
PlcDATE_AND_TIME(java.time.LocalDateTime.parse("2025-11-12T14:33:21")));
+        test.addTestCase("g_str",     new PlcSTRING("Hello PLC4X"));
+
+        long startMillis = System.currentTimeMillis();
+        test.run();
+        long endMillis = System.currentTimeMillis();
+        System.out.println("Test executed in " + (endMillis - startMillis) + 
"ms");
+    }
+
+}
diff --git a/protocols/umas/src/main/resources/protocols/umas/v1/umas.mspec 
b/protocols/umas/src/main/resources/protocols/umas/v1/umas.mspec
index cf076f030f..27fea9f422 100644
--- a/protocols/umas/src/main/resources/protocols/umas/v1/umas.mspec
+++ b/protocols/umas/src/main/resources/protocols/umas/v1/umas.mspec
@@ -26,19 +26,16 @@
 // Key detail: Modbus/TCP MBAP header is big-endian, UMAS payload is 
little-endian.
 //
 // References:
-//   - Apache PLC4X umas.mspec
+//   - Version 0 of the umas.mspec
 //   - Kaspersky ICS CERT: "The secrets of Schneider Electric's UMAS protocol"
 //   - Wireshark Lua dissectors (zaltzman, yanissec, biero-el-corridor)
-//
-// Procedure of reverse Engineering:
-//   - Setup a Windows VM and installed Control Expert Classic
-//   - Configured an example program with all data-structures an default values
-//   - Setup a virtual PLC and installed the example program on that
-//   - Setup OPC UA Server Expert and connected it to the virtual PLC
-//   - Used WireShark to observe the traffic between the OPC UA Server and the 
PLC
-//   - Used Claude Code and existing public resources to make sense out of the
-//     observed traffic
-//
+//   - Recordings of the Schneider Electric OPC UA Server Expert communicating
+//     with a Simulated PLC (Tag loading)
+//   - Recordings of the Schneider Electric OPC UA Server Expert communicating
+//     with a Simulated PLC (Using UAExpert to produce subscription traffic)
+//   - Recordings of the Schneider Electric OPC UA Server Expert communicating
+//     with a Simulated PLC (Using Prosys OPC UA Browser to produce read/write
+//     traffic)
 ////////////////////////////////////////////////////////////////
 
 [constants
@@ -299,8 +296,22 @@
         ]
 
         // ---- FC 0x50: Monitor PLC ----
+        // Used by Schneider OPC UA Server Expert to read variable values from
+        // M580 PLCs (instead of FC 0x22 ReadVariable which returns error 
0xA1A1).
+        // The request contains a list of sub-operations: register variables 
for
+        // monitoring, read all registered values, or deregister variables.
         ['0x50'      UmasPDUMonitorPlcRequest
-            [array      byte           data count 'byteLength - 2']
+            [simple     uint 8          subCommand]
+            [simple     uint 8          unknown]
+            [simple     uint 8          numberOfSubOperations]
+            [array      MonitorPlcSubOperation  subOperations count 
'numberOfSubOperations']
+        ]
+        // Response payload is a flat byte array because the per-variable value
+        // sizes depend on the data types of the registered variables, which 
are
+        // only known at the driver layer (via the symbol table). The driver
+        // parses the raw bytes using DataItem and the registered variable 
types.
+        ['0xFE', '0x50'     UmasPDUMonitorPlcResponse
+            [array      byte            block count 'byteLength - 2']
         ]
 
         // ---- FC 0x58: Check PLC ----
@@ -341,33 +352,66 @@
 // Helper Types
 ////////////////////////////////////////////////////////////////
 
+// Sub-operation within an FC 0x50 MonitorPLC request.
+// Discriminated by the first byte (operation type).
+[discriminatedType MonitorPlcSubOperation byteOrder='"LITTLE_ENDIAN"'
+    [discriminator uint 8  operationType]
+    [typeSwitch operationType
+        // Register or deregister a variable for monitoring.
+        // After registration, the variable's current value is returned
+        // in subsequent read (0x07) responses.
+        ['0x05'  MonitorPlcRegisterVariable
+            [simple     uint 8                   variableIndex  ]
+            [simple     uint 16                  block          ]
+            [simple     uint 16                  offset         ]
+            [simple     MonitorPlcRegisterAction action         ]
+        ]
+        // Read current values for all registered variables.
+        // Returns concatenated raw values in the response — the driver
+        // must know each variable's data type to parse the byte stream.
+        ['0x07'  MonitorPlcReadAll
+        ]
+        // Register a variable AND include its value in the response.
+        // Combines registration with an immediate read for that variable.
+        ['0x09'  MonitorPlcRegisterAndRead
+            [simple     uint 8                   variableIndex  ]
+            [simple     uint 16                  block          ]
+            [simple     uint 16                  offset         ]
+        ]
+        // Clear/reset monitoring state. Observed in Modicon M340 captures
+        // with no payload (single byte operation, like 0x07).
+        ['0x0B'  MonitorPlcReset
+        ]
+    ]
+]
+
 // Memory block structure for specific block/offset combinations
 [type UmasMemoryBlock(uint 16 blockNumber, uint 16 offset)
     [typeSwitch blockNumber, offset
         ['0x30', '0x00' UmasMemoryBlockBasicInfo
-            [simple     uint 16          range]
-            [simple uint 16 notSure]
-            [simple uint 8  index]
-            [simple uint 32 hardwareId]
+            [simple     uint 16                  range          ]
+            [simple     uint 16                  notSure        ]
+            [simple     uint 8                   index          ]
+            [simple     uint 32                  hardwareId     ]
         ]
     ]
 ]
 
 // Parsed response for unlocated variable names (used by driver layer)
 [type UmasPDUReadUnlocatedVariableNamesResponse
-    [simple     uint 8          range]
-    [simple     uint 16         nextAddress]
-    [simple     uint 16         unknown1]
-    [simple     uint 16         noOfRecords]
-    [array      UmasUnlocatedVariableReference         records count 
'noOfRecords']
+    [simple     uint 8                           range                         
     ]
+    [simple     uint 16                          nextAddress                   
     ]
+    [simple     uint 16                          unknown1                      
     ]
+    [simple     uint 16                          noOfRecords                   
     ]
+    [array      UmasUnlocatedVariableReference   records        count 
'noOfRecords' ]
 ]
 
 // Parsed response for UDT definitions
 [type UmasPDUReadUmasUDTDefinitionResponse
-    [simple     uint 8          range]
-    [simple     uint 32         unknown1]
-    [simple     uint 16         noOfRecords]
-    [array      UmasUDTDefinition         records count 'noOfRecords']
+    [simple     uint 8              range                           ]
+    [simple     uint 32             unknown1                        ]
+    [simple     uint 16             noOfRecords                     ]
+    [array      UmasUDTDefinition   records     count 'noOfRecords' ]
 ]
 
 // Parsed response for datatype names
@@ -390,7 +434,9 @@
     [optional   uint 16          arrayLength 'isArray != 0']
 ]
 
-// Variable write request reference — identifies a variable to write with data
+// Variable write request reference — identifies a variable to write with data.
+// The dataSizeIndex is a size index (not a byte count): 1→1B, 2→2B, 3→4B, 
4→8B.
+// The formula 2^(index-1) converts the index to the actual byte size.
 [type VariableWriteRequestReference
     [simple     uint 4           isArray]
     [simple     uint 4           dataSizeIndex]
@@ -398,7 +444,8 @@
     [simple     uint 16          baseOffset]
     [simple     uint 16          offset]
     [optional   uint 16          arrayLength 'isArray != 0']
-    [array      byte             recordData     length  'isArray == 1 ? 
dataSizeIndex * arrayLength : dataSizeIndex']
+    [virtual    uint 16          dataSize    
'STATIC_CALL("writeSizeIndexToByteCount", dataSizeIndex)']
+    [array      byte             recordData     length  'isArray == 1 ? 
dataSize * arrayLength : dataSize']
 ]
 
 // Unlocated variable reference from the data dictionary
@@ -427,6 +474,8 @@
     [simple     uint 16          unknown1]
     [simple     uint 8           classIdentifier]
     [simple     uint 8           dataType]
+    // Padding byte before the null-terminated name (always 0x00 in observed 
captures)
+    [reserved   uint 8           '0x00']
     // Name is null-terminated (no length prefix) — uses -1 to signal "read 
until null"
     [manual vstring value  'STATIC_CALL("parseTerminatedString", readBuffer, 
-1)' 'STATIC_CALL("serializeTerminatedString", writeBuffer, value, -1)' 
'(STR_LEN(value) + 1) * 8']
 ]
@@ -455,10 +504,10 @@
             [simple   bit     value                            ]
         ]
         ['BYTE','1'  BYTE
-            [simple byte value]
+            [simple   uint 8  value]
         ]
         ['BYTE' List
-            [array    byte     value count 'numberOfValues' ]
+            [array    uint 8  value count 'numberOfValues' ]
         ]
         ['WORD'      WORD
             [simple   uint 16 value]
@@ -564,6 +613,12 @@
     ['25' EBOOL     ['1','1','"BOOL"']]
 ]
 
+// Action codes for MonitorPLC register sub-operation (type 0x05)
+[enum uint 8 MonitorPlcRegisterAction
+    ['0x01' DEREGISTER]
+    ['0x02' REGISTER  ]
+]
+
 // Standard Modbus error codes used in UMAS error responses
 [enum uint 8 ModbusErrorCode
     ['1'    ILLEGAL_FUNCTION]


Reply via email to