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]