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

cdutz pushed a commit to branch fix/opc-ua-fine-tuning
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit f80dc154cbe912798bc3c86c8a807d932ac07bee
Author: Christofer Dutz <[email protected]>
AuthorDate: Tue Feb 17 22:54:51 2026 +0100

    fix: Worked on getting all various types of OPC-UA request working with my 
Siemens S7 OPC-UA Server.
---
 .../readwrite/BinaryExtensionObjectWithMask.java   | 23 ++++--
 .../plc4x/java/opcua/context/Conversation.java     | 20 ++---
 .../java/opcua/protocol/OpcuaProtocolLogic.java    | 92 +++++++++++++++------
 .../opcua/readwrite/UnknownExtensionObject.java    | 93 ++++++++++++++++++++++
 .../java/opcua/readwrite/utils/StaticHelper.java   | 36 ++++++++-
 ....java => ManualOpcUaS71500NewFWDriverTest.java} | 53 ++++++------
 .../java/opcua/OpcuaParserSerializerTest.java      |  2 +-
 .../protocol/OpcuaSubscriptionHandleTest.java      |  5 +-
 .../opcua/src/test/resources/logback-test.xml      |  2 +-
 .../apache/plc4x/java/spi/Plc4xNettyWrapper.java   |  4 +-
 .../java/spi/values/LegacyPlcValueHandler.java     |  4 +
 .../generated/protocols/opcua/opc-manual.mspec     |  2 +-
 protocols/opcua/src/main/xslt/opc-manual.xsl       |  2 +-
 13 files changed, 258 insertions(+), 80 deletions(-)

diff --git 
a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/BinaryExtensionObjectWithMask.java
 
b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/BinaryExtensionObjectWithMask.java
index c1db6b21db..82fe0a7be8 100644
--- 
a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/BinaryExtensionObjectWithMask.java
+++ 
b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/BinaryExtensionObjectWithMask.java
@@ -73,8 +73,13 @@ public class BinaryExtensionObjectWithMask extends 
ExtensionObjectWithMask imple
     int bodyLength = (int) ((((getBody()) == (null)) ? 0 : 
getBody().getLengthInBytes()));
     writeImplicitField("bodyLength", bodyLength, writeSignedInt(writeBuffer, 
32));
 
-    // Simple Field (body)
-    writeSimpleField("body", body, writeComplex(writeBuffer));
+    // Manual Field (body)
+    writeManualField(
+        "body",
+        () ->
+            
org.apache.plc4x.java.opcua.readwrite.utils.StaticHelper.serializeExtensionObjectBody(
+                writeBuffer, body),
+        writeBuffer);
 
     writeBuffer.popContext("BinaryExtensionObjectWithMask");
   }
@@ -93,8 +98,8 @@ public class BinaryExtensionObjectWithMask extends 
ExtensionObjectWithMask imple
     // Implicit Field (bodyLength)
     lengthInBits += 32;
 
-    // Simple field (body)
-    lengthInBits += body.getLengthInBits();
+    // Manual Field (body)
+    lengthInBits += (((body) == (null)) ? 0 : body.getLengthInBits());
 
     return lengthInBits;
   }
@@ -109,11 +114,13 @@ public class BinaryExtensionObjectWithMask extends 
ExtensionObjectWithMask imple
     int bodyLength = readImplicitField("bodyLength", readSignedInt(readBuffer, 
32));
 
     ExtensionObjectDefinition body =
-        readSimpleField(
+        readManualField(
             "body",
-            readComplex(
-                () -> ExtensionObjectDefinition.staticParse(readBuffer, (int) 
(extensionId)),
-                readBuffer));
+            readBuffer,
+            () ->
+                (ExtensionObjectDefinition)
+                    (org.apache.plc4x.java.opcua.readwrite.utils.StaticHelper
+                        .parseExtensionObjectBody(readBuffer, extensionId, 
bodyLength)));
 
     readBuffer.closeContext("BinaryExtensionObjectWithMask");
     // Create the instance
diff --git 
a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/Conversation.java
 
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/Conversation.java
index a9c9c2b176..f9804a57ca 100644
--- 
a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/Conversation.java
+++ 
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/context/Conversation.java
@@ -75,7 +75,6 @@ import org.apache.plc4x.java.opcua.security.MessageSecurity;
 import org.apache.plc4x.java.opcua.security.SecurityPolicy;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.ConversationContext.SendRequestContext;
-import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -243,16 +242,14 @@ public class Conversation {
                     .unwrap(OpcuaAPU::getMessage)
                     .check(replyType::isInstance)
                     .unwrap(replyType::cast)
-                    .unwrap(msg -> encryptionHandler.decodeMessage(msg))
+                    .unwrap(encryptionHandler::decodeMessage)
                     .check(replyType::isInstance)
                     .unwrap(replyType::cast)
                     .check(reply -> requestId == 
sequenceHeaderExtractor.apply(reply).getRequestId())
                     .check(reply -> 
sequenceValidator.test(sequenceHeaderExtractor.apply(reply), future))
                     .check(msg -> accumulateChunkUntilFinal(chunkStorage, 
msg.getChunk(), chunkExtractor.apply(msg)))
                     .unwrap(msg -> mergeChunks(chunkStorage, msg, 
sequenceHeaderExtractor.apply(msg), chunkAssembler))
-                    .handle(response -> {
-                        future.complete(response);
-                    });
+                    .handle(future::complete);
             } else {
                 context.sendToWire(new OpcuaAPU(chunks.get(index)));
             }
@@ -270,7 +267,7 @@ public class Conversation {
     }
 
     private CompletableFuture<Object> submit(ExtensionObjectDefinition 
requestDefinition) {
-        Integer requestId = tm.getTransactionIdentifier();
+        int requestId = tm.getTransactionIdentifier();
 
         ExpandedNodeId expandedNodeId = new ExpandedNodeId(
             false,           //Namespace Uri Specified
@@ -303,7 +300,7 @@ public class Conversation {
                     .unwrap(OpcuaAPU::getMessage)
                     .check(OpcuaMessageResponse.class::isInstance)
                     .unwrap(OpcuaMessageResponse.class::cast)
-                    .unwrap(msg -> encryptionHandler.decodeMessage(msg))
+                    .unwrap(encryptionHandler::decodeMessage)
                     .check(OpcuaMessageResponse.class::isInstance)
                     .unwrap(OpcuaMessageResponse.class::cast)
                     .check(OpcuaMessageResponse.class::isInstance)
@@ -329,14 +326,13 @@ public class Conversation {
                                     BinaryPayload binary = (BinaryPayload) 
message;
                                     ReadBufferByteBased buffer = new 
ReadBufferByteBased(binary.getPayload(), 
org.apache.plc4x.java.spi.generation.ByteOrder.LITTLE_ENDIAN);
                                     extensionObjectBody = 
ExtensionObject.staticParse(buffer, false).getBody();
-                                } catch (ParseException e) {
+                                } catch (Exception e) {
                                     future.completeExceptionally(e);
                                     return;
                                 }
                             }
 
-                            if (extensionObjectBody instanceof ServiceFault) {
-                                ServiceFault fault = (ServiceFault) 
extensionObjectBody;
+                            if (extensionObjectBody instanceof ServiceFault 
fault) {
                                 // If we write the same data a tag already 
had, Siemens devices return an error.
                                 if 
(fault.getResponseHeader().getServiceResult().getStatusCode() == 
OpcuaStatusCode.BadNothingToDo.getValue()) {
                                     // TODO: Here we need to fake a 
WriteResponse.
@@ -361,7 +357,7 @@ public class Conversation {
         return context.sendRequest(new OpcuaAPU(messagePDU))
             .onError((req, err) -> future.completeExceptionally(err))
             .expectResponse(OpcuaAPU.class, Duration.ofMillis(timeout))
-            .onTimeout((e) -> future.completeExceptionally(e));
+            .onTimeout(future::completeExceptionally);
     }
 
     private <T> T mergeChunks(ChunkStorage chunkStorage, T source, 
SequenceHeader sequenceHeader, BiFunction<T, BinaryPayload, T> producer) {
@@ -419,7 +415,7 @@ public class Conversation {
     }
 
     static PlcProtocolException toProtocolException(ServiceFault fault) {
-        if (fault.getResponseHeader() instanceof ResponseHeader) {
+        if (fault.getResponseHeader() != null) {
             ResponseHeader responseHeader = (ResponseHeader) 
fault.getResponseHeader();
             long statusCode = 
responseHeader.getServiceResult().getStatusCode();
             String statusName = OpcuaStatusCode.isDefined(statusCode) ? 
OpcuaStatusCode.enumForValue(statusCode).name() : "<unknown>";
diff --git 
a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
 
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
index 9d50a2f922..1beed98139 100644
--- 
a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
+++ 
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
@@ -450,7 +450,8 @@ public class OpcuaProtocolLogic extends 
Plc4xProtocolBase<OpcuaAPU> implements H
             List<ExtensionObject> objects = ((VariantExtensionObject) 
variant).getValue();
             List<PlcValue> values = new ArrayList<>(objects.size());
             for (ExtensionObject eo : objects) {
-                values.add(new PlcSTRING(eo.toString()));
+                PlcValue eoValue = extensionObjectToPlcValue(eo);
+                values.add(eoValue != null ? eoValue : new 
PlcSTRING(eo.toString()));
             }
             value = structurePlcValues(values, variant);
         } else if (variant instanceof VariantNodeId) {
@@ -489,14 +490,51 @@ public class OpcuaProtocolLogic extends 
Plc4xProtocolBase<OpcuaAPU> implements H
         return value;
     }
 
+    /**
+     * Tries to convert an ExtensionObject with an unknown body type to a 
PlcValue.
+     * Handles vendor-specific types like Siemens TE_DTL (Date_And_Time_Long).
+     * Returns null if the type is not recognized.
+     */
+    private static PlcValue extensionObjectToPlcValue(ExtensionObject eo) {
+        ExtensionObjectDefinition body = eo.getBody();
+        if (!(body instanceof UnknownExtensionObject)) {
+            return null;
+        }
+        byte[] rawBytes = ((UnknownExtensionObject) body).getBodyBytes();
+        String typeId = eo.getTypeId().getNodeId().getIdentifier();
+
+        // Siemens DTL (Date_And_Time_Long): 12 bytes
+        // uint16 year, uint8 month, uint8 day, uint8 weekday,
+        // uint8 hour, uint8 minute, uint8 second, uint32 nanoseconds (LE)
+        if ("TE_DTL".equals(typeId) && rawBytes.length == 12) {
+            ByteBuffer buf = 
ByteBuffer.wrap(rawBytes).order(ByteOrder.LITTLE_ENDIAN);
+            int year = buf.getShort(0) & 0xFFFF;
+            int month = buf.get(2) & 0xFF;
+            int day = buf.get(3) & 0xFF;
+            // byte 4 = weekday, skip
+            int hour = buf.get(5) & 0xFF;
+            int minute = buf.get(6) & 0xFF;
+            int second = buf.get(7) & 0xFF;
+            int nanoseconds = buf.getInt(8);
+            return new PlcDATE_AND_LTIME(LocalDateTime.of(year, month, day, 
hour, minute, second, nanoseconds));
+        }
+
+        return null;
+    }
+
     /**
      * Recursively applies a type override to a PlcValue.
      * For PlcList values, each element is converted individually.
      * For scalar values, the raw numeric is re-interpreted as the target type.
      */
     private static PlcValue applyTypeOverride(PlcValue value, PlcValueType 
targetType) {
-        if (value instanceof PlcList) {
-            PlcList list = (PlcList) value;
+        // If the value is already a properly typed temporal value (e.g., from 
DTL
+        // conversion), skip the override to avoid corrupting it.
+        PlcValueType currentType = value.getPlcValueType();
+        if (currentType == targetType || isTemporalType(currentType)) {
+            return value;
+        }
+        if (value instanceof PlcList list) {
             List<PlcValue> converted = new ArrayList<>(list.getLength());
             for (PlcValue item : list.getList()) {
                 converted.add(applyTypeOverride(item, targetType));
@@ -504,34 +542,38 @@ public class OpcuaProtocolLogic extends 
Plc4xProtocolBase<OpcuaAPU> implements H
             return new PlcList(converted);
         }
         long raw = value.getLong();
-        switch (targetType) {
-            case TIME:
-                return new PlcTIME(raw);
-            case LTIME:
-                return new PlcLTIME(raw);
-            case DATE:
+        return switch (targetType) {
+            case TIME -> new PlcTIME(raw);
+            case LTIME -> new PlcLTIME(raw);
+            case DATE ->
                 // S7/IEC value is days since 1990-01-01, PlcDATE expects days 
since 1970-01-01
-                return new PlcDATE(raw + IEC_DATE_EPOCH_OFFSET_DAYS);
-            case LDATE:
+                new PlcDATE(raw + IEC_DATE_EPOCH_OFFSET_DAYS);
+            case LDATE ->
                 // PlcLDATE expects seconds since 1970-01-01
-                return new PlcLDATE(raw + IEC_DATE_EPOCH_OFFSET_DAYS * 86400L);
-            case TIME_OF_DAY:
+                new PlcLDATE(raw + IEC_DATE_EPOCH_OFFSET_DAYS * 86400L);
+            case TIME_OF_DAY ->
                 // S7/IEC value is milliseconds since midnight
-                return new PlcTIME_OF_DAY(LocalTime.ofNanoOfDay(raw * 
1_000_000L));
-            case LTIME_OF_DAY:
-                return new PlcLTIME_OF_DAY(raw);
-            case DATE_AND_TIME:
+                new PlcTIME_OF_DAY(LocalTime.ofNanoOfDay(raw * 1_000_000L));
+            case LTIME_OF_DAY -> new PlcLTIME_OF_DAY(raw);
+            case DATE_AND_TIME ->
                 // PlcDATE_AND_TIME expects seconds since 1970-01-01
-                return new PlcDATE_AND_TIME(raw + IEC_DATE_EPOCH_OFFSET_DAYS * 
86400L);
-            case DATE_AND_LTIME:
+                new PlcDATE_AND_TIME(raw + IEC_DATE_EPOCH_OFFSET_DAYS * 
86400L);
+            case DATE_AND_LTIME ->
                 // PlcDATE_AND_LTIME expects nanoseconds since 1970-01-01
-                return new PlcDATE_AND_LTIME(raw + IEC_DATE_EPOCH_OFFSET_DAYS 
* 86400L * 1_000_000_000L);
-            case LDATE_AND_TIME:
+                new PlcDATE_AND_LTIME(raw + IEC_DATE_EPOCH_OFFSET_DAYS * 
86400L * 1_000_000_000L);
+            case LDATE_AND_TIME ->
                 // PlcLDATE_AND_TIME expects milliseconds since 1970-01-01
-                return new PlcLDATE_AND_TIME(raw + IEC_DATE_EPOCH_OFFSET_DAYS 
* 86400L * 1000L);
-            default:
-                return value;
-        }
+                new PlcLDATE_AND_TIME(raw + IEC_DATE_EPOCH_OFFSET_DAYS * 
86400L * 1000L);
+            default -> value;
+        };
+    }
+
+    private static boolean isTemporalType(PlcValueType type) {
+        return switch (type) {
+            case TIME, LTIME, DATE, LDATE, TIME_OF_DAY, LTIME_OF_DAY, 
DATE_AND_TIME, DATE_AND_LTIME, LDATE_AND_TIME ->
+                true;
+            default -> false;
+        };
     }
 
     /**
diff --git 
a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/readwrite/UnknownExtensionObject.java
 
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/readwrite/UnknownExtensionObject.java
new file mode 100644
index 0000000000..b358a7a60e
--- /dev/null
+++ 
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/readwrite/UnknownExtensionObject.java
@@ -0,0 +1,93 @@
+/*
+ * 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.opcua.readwrite;
+
+import java.util.Arrays;
+import java.util.Objects;
+import org.apache.plc4x.java.spi.generation.SerializationException;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.generation.WriteBufferBoxBased;
+
+/**
+ * Represents an ExtensionObject whose type is not known to the parser.
+ * Stores the raw body bytes so they can be interpreted later based on context
+ * (e.g., the tag's dataType hint).
+ */
+public class UnknownExtensionObject extends ExtensionObjectDefinition {
+
+    private final byte[] bodyBytes;
+
+    public UnknownExtensionObject(byte[] bodyBytes) {
+        super();
+        this.bodyBytes = bodyBytes;
+    }
+
+    @Override
+    public Integer getExtensionId() {
+        return 0;
+    }
+
+    public byte[] getBodyBytes() {
+        return bodyBytes;
+    }
+
+    @Override
+    protected void serializeExtensionObjectDefinitionChild(WriteBuffer 
writeBuffer)
+        throws SerializationException {
+        writeBuffer.pushContext("UnknownExtensionObject");
+        writeBuffer.writeByteArray("bodyBytes", bodyBytes);
+        writeBuffer.popContext("UnknownExtensionObject");
+    }
+
+    @Override
+    public int getLengthInBytes() {
+        return (int) Math.ceil((float) getLengthInBits() / 8.0);
+    }
+
+    @Override
+    public int getLengthInBits() {
+        int lengthInBits = super.getLengthInBits();
+        lengthInBits += bodyBytes.length * 8;
+        return lengthInBits;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UnknownExtensionObject)) return false;
+        UnknownExtensionObject that = (UnknownExtensionObject) o;
+        return super.equals(that) && Arrays.equals(bodyBytes, that.bodyBytes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), Arrays.hashCode(bodyBytes));
+    }
+
+    @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/opcua/src/main/java/org/apache/plc4x/java/opcua/readwrite/utils/StaticHelper.java
 
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/readwrite/utils/StaticHelper.java
index 7ae036ec39..3f67ecffb4 100644
--- 
a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/readwrite/utils/StaticHelper.java
+++ 
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/readwrite/utils/StaticHelper.java
@@ -19,8 +19,13 @@
 package org.apache.plc4x.java.opcua.readwrite.utils;
 
 import java.nio.charset.StandardCharsets;
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.opcua.readwrite.ExpandedNodeId;
+import org.apache.plc4x.java.opcua.readwrite.ExtensionObjectDefinition;
+import org.apache.plc4x.java.opcua.readwrite.UnknownExtensionObject;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBuffer;
+import org.apache.plc4x.java.spi.generation.SerializationException;
+import org.apache.plc4x.java.spi.generation.WriteBuffer;
 
 public class StaticHelper {
 
@@ -40,7 +45,34 @@ public class StaticHelper {
         try {
             return 
Integer.parseInt(expandedNodeId.getNodeId().getIdentifier());
         } catch (NumberFormatException e) {
-            throw new PlcRuntimeException("Invalid node id, expected number, 
found " + expandedNodeId.getNodeId().getClass().getName());
+            // Non-numeric NodeIds (e.g., vendor-specific types like Siemens 
TE_DTL)
+            // are not known to the parser. Return 0 to signal an unknown type.
+            return 0;
         }
     }
+
+    public static ExtensionObjectDefinition parseExtensionObjectBody(
+            ReadBuffer readBuffer, int extensionId, int bodyLength) throws 
ParseException {
+        // Maintain the same "body" context that a simple field would produce,
+        // so the XML roundtrip tests stay consistent.
+        readBuffer.pullContext("body");
+        ExtensionObjectDefinition result;
+        if (extensionId < 1 && bodyLength > 0) {
+            // Unknown extension object type (e.g., vendor-specific like 
Siemens TE_DTL).
+            // Read the raw body bytes so the buffer position stays correct.
+            byte[] rawBytes = readBuffer.readByteArray("unknownBody", 
bodyLength);
+            result = new UnknownExtensionObject(rawBytes);
+        } else {
+            result = ExtensionObjectDefinition.staticParse(readBuffer, 
extensionId);
+        }
+        readBuffer.closeContext("body");
+        return result;
+    }
+
+    public static void serializeExtensionObjectBody(
+            WriteBuffer writeBuffer, ExtensionObjectDefinition body) throws 
SerializationException {
+        writeBuffer.pushContext("body");
+        body.serialize(writeBuffer);
+        writeBuffer.popContext("body");
+    }
 }
diff --git 
a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualS71500NewFWDriverTest.java
 
b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualOpcUaS71500NewFWDriverTest.java
similarity index 69%
rename from 
plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualS71500NewFWDriverTest.java
rename to 
plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualOpcUaS71500NewFWDriverTest.java
index 66295cb58b..ee085fac17 100644
--- 
a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualS71500NewFWDriverTest.java
+++ 
b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualOpcUaS71500NewFWDriverTest.java
@@ -28,36 +28,37 @@ import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.util.List;
 
-public class ManualS71500NewFWDriverTest extends ManualTest {
+public class ManualOpcUaS71500NewFWDriverTest extends ManualTest {
 
-    public ManualS71500NewFWDriverTest(String connectionString) {
-        super(connectionString, true, false, true, true, 100);
+    public ManualOpcUaS71500NewFWDriverTest(String connectionString) {
+        super(connectionString, true, true, true, true, 100);
     }
 
     public static void main(String[] args) throws Exception {
-        boolean testArrays = true;
-        ManualS71500NewFWDriverTest test = new 
ManualS71500NewFWDriverTest("opcua://192.168.23.28:4840");
-        test.addTestCase(/*"g_b1",*/            "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b1\"",                   new PlcBOOL(true));
-        test.addTestCase(/*"g_b8",*/            "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b8\"",                  new PlcBYTE(0xAB));
-        test.addTestCase(/*"g_s8",*/            "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_s8\"",                  new PlcSINT(-12));
-        test.addTestCase(/*"g_u8",*/            "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_u8\"",                  new PlcUSINT(250));
-        test.addTestCase(/*"g_b16",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b16\"",                 new PlcWORD(0xBEEF));
-        test.addTestCase(/*"g_s16",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_s16\"",                  new PlcINT(-1234));
-        test.addTestCase(/*"g_u16",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_u16\"",                  new PlcUINT(54321));
-        test.addTestCase(/*"g_b32",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b32\"",                  new PlcDWORD(0xDEADBEEFL));
-        test.addTestCase(/*"g_s32",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_s32\"",                  new PlcDINT(-12345678));
-        test.addTestCase(/*"g_u32",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_u32\"",                  new PlcUDINT(305419896));
-        test.addTestCase(/*"g_b64",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b64\"",                  new PlcLWORD(0x0123_4567_89AB_CDEFL));
-        test.addTestCase(/*"g_s64",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_s64\"",                  new PlcLINT(-9223372036854770000L));
-        test.addTestCase(/*"g_u64",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_u64\"",                  new PlcULINT(new 
BigDecimal("18446744073709551000")));
-        test.addTestCase(/*"g_r32",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_r32\"",                  new PlcREAL(3.14159));
-        test.addTestCase(/*"g_r64",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_r64\"",                  new PlcLREAL(2.71828182845905));
-        test.addTestCase(/*"g_tim",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_tim\";TIME",             new PlcTIME(2500)); // Is returned as Int32
-        test.addTestCase(/*"g_dat",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_dat\";DATE",             new PlcDATE(LocalDate.of(2025, 11, 12))); 
// Is returned as UInt16
-        test.addTestCase(/*"g_timoday",*/       "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_timoday\";TIME_OF_DAY",         new PlcTIME_OF_DAY(LocalTime.of(14, 
33, 21, 250000000))); // Is returned as UInt32
-//        test.addTestCase(/*"g_dattim",*/        "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_dattim\";LTIME",         new 
PlcDATE_AND_LTIME(LocalDateTime.of(2025, 11, 12, 14, 33, 21, 500_000_000))); // 
TODO: Getting a class cast error, because OpcuaMessageResponse cannot be cast 
to OpcuaAPU
-        test.addTestCase(/*"g_str",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_str\"",                  new PlcSTRING("Hello PLC4X"));
-        test.addTestCase(/*"g_wstr",*/          "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_wstr\"",                 new PlcWSTRING("Grüße von PLC4X"));
+        boolean testArrays = false;
+        ManualOpcUaS71500NewFWDriverTest test = new 
ManualOpcUaS71500NewFWDriverTest("opcua://192.168.24.66:4840");
+        test.addTestCase(/*"g_b1",*/            "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b1\";BOOL",                   new PlcBOOL(false));
+        test.addTestCase(/*"g_b8",*/            "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b8\";BYTE",                     new PlcBYTE(0xBC));
+        test.addTestCase(/*"g_s8",*/            "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_s8\";SINT",                     new PlcSINT(-14));
+        test.addTestCase(/*"g_u8",*/            "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_u8\";USINT",                    new PlcUSINT(251));
+//        test.addTestCase(/*"g_b16",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b16\";WORD",                  new PlcWORD(0xBEEF));
+        test.addTestCase(/*"g_s16",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_s16\";INT",                  new PlcINT(-1235));
+        test.addTestCase(/*"g_u16",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_u16\";UINT",                  new PlcUINT(54321));
+//        test.addTestCase(/*"g_b32",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b32\";DWORD",                  new PlcDWORD(0xDEADBEEFL));
+        test.addTestCase(/*"g_s32",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_s32\";DINT",                  new PlcDINT(-12345678));
+        test.addTestCase(/*"g_u32",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_u32\";UDINT",                  new PlcUDINT(305419896));
+//        test.addTestCase(/*"g_b64",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_b64\";LWORD",                  new PlcLWORD(0x0123_4567_89AB_CDEFL));
+        test.addTestCase(/*"g_s64",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_s64\";LINT",                  new PlcLINT(-9223372036854770000L));
+// TODO: This seems to write different values to the plc.
+//        test.addTestCase(/*"g_u64",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_u64\";ULINT",                  new PlcULINT(new 
BigDecimal("18446744073709551000")));
+        test.addTestCase(/*"g_r32",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_r32\";REAL",                  new PlcREAL(3.14159));
+        test.addTestCase(/*"g_r64",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_r64\";LREAL",                  new PlcLREAL(2.71828182845905));
+//        test.addTestCase(/*"g_tim",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_tim\";TIME",             new PlcTIME(2500)); // Is returned as Int32
+//        test.addTestCase(/*"g_dat",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_dat\";DATE",             new PlcDATE(LocalDate.of(2025, 11, 12))); 
// Is returned as UInt16
+//        test.addTestCase(/*"g_timoday",*/       "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_timoday\";TIME_OF_DAY",  new PlcTIME_OF_DAY(LocalTime.of(14, 33, 21, 
250000000))); // Is returned as UInt32
+//        test.addTestCase(/*"g_dattim",*/        "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_dattim\";DATE_AND_TIME",               new 
PlcDATE_AND_LTIME(LocalDateTime.of(2025, 11, 12, 14, 33, 21, 500_000_000)));
+        test.addTestCase(/*"g_str",*/           "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_str\";STRING",                  new PlcSTRING("Hello PLC4X"));
+//        test.addTestCase(/*"g_wstr",*/          "ns=3;s=\"OPC_UA_DB\".\"OPC 
Data\".\"g_wstr\";WSTRING",                 new PlcWSTRING("Grüße von PLC4X"));
         if(testArrays) {
             test.addTestCase(/*"g_arrBool",*/       
"ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrBool\"", new PlcList(List.of(
                 new PlcBOOL(true), new PlcBOOL(false), new PlcBOOL(true), new 
PlcBOOL(true),
diff --git 
a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaParserSerializerTest.java
 
b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaParserSerializerTest.java
index 8fefde7336..4953b35091 100644
--- 
a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaParserSerializerTest.java
+++ 
b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaParserSerializerTest.java
@@ -23,7 +23,7 @@ import 
org.apache.plc4x.test.parserserializer.ParserSerializerTestsuiteRunner;
 public class OpcuaParserSerializerTest extends ParserSerializerTestsuiteRunner 
{
 
     public OpcuaParserSerializerTest() {
-        super("/protocols/opcua/ParserSerializerTestsuite.xml");
+        super("/protocols/opcua/ParserSerializerTestsuite.xml", true);
     }
 
 }
diff --git 
a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
 
b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
index 1339394a72..2e72af3e77 100644
--- 
a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
+++ 
b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/protocol/OpcuaSubscriptionHandleTest.java
@@ -19,6 +19,7 @@
 package org.apache.plc4x.java.opcua.protocol;
 
 import java.io.ByteArrayOutputStream;
+import java.math.BigInteger;
 import java.util.concurrent.CountDownLatch;
 import java.util.stream.Stream;
 import org.apache.plc4x.java.DefaultPlcDriverManager;
@@ -259,11 +260,11 @@ public class OpcuaSubscriptionHandleTest {
             Arguments.of(INT32_IDENTIFIER_READ_WRITE, Integer.class),
             Arguments.of(INT64_IDENTIFIER_READ_WRITE, Long.class),
             Arguments.of(INTEGER_IDENTIFIER_READ_WRITE, Integer.class),
-            Arguments.of(SBYTE_IDENTIFIER_READ_WRITE, byte[].class),
+            Arguments.of(SBYTE_IDENTIFIER_READ_WRITE, Byte.class),
             Arguments.of(STRING_IDENTIFIER_READ_WRITE, String.class),
             Arguments.of(UINT16_IDENTIFIER_READ_WRITE, Integer.class),
             Arguments.of(UINT32_IDENTIFIER_READ_WRITE, Long.class),
-            Arguments.of(UINT64_IDENTIFIER_READ_WRITE, Long.class),
+            Arguments.of(UINT64_IDENTIFIER_READ_WRITE, BigInteger.class),
             Arguments.of(UINTEGER_IDENTIFIER_READ_WRITE, Long.class)
         );
     }
diff --git a/plc4j/drivers/opcua/src/test/resources/logback-test.xml 
b/plc4j/drivers/opcua/src/test/resources/logback-test.xml
index 719321b8e4..3e3748c42b 100644
--- a/plc4j/drivers/opcua/src/test/resources/logback-test.xml
+++ b/plc4j/drivers/opcua/src/test/resources/logback-test.xml
@@ -27,7 +27,7 @@
     </encoder>
   </appender>
 
-  <root level="warn">
+  <root level="info">
     <appender-ref ref="STDOUT" />
   </root>
 
diff --git 
a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xNettyWrapper.java 
b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xNettyWrapper.java
index 4c20b1390d..33f188dbec 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xNettyWrapper.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xNettyWrapper.java
@@ -198,7 +198,9 @@ public class Plc4xNettyWrapper<T> extends 
MessageToMessageCodec<T, Object> {
                     logger.trace("Failure while processing payload {} with 
handler {}", message, registration, e);
                     BiConsumer biConsumer = registration.getErrorConsumer();
                     if(biConsumer != null) {
-                        biConsumer.accept(message, e);
+                        // Use the original payload rather than the 
transformed message,
+                        // as the error consumer expects the original message 
type.
+                        biConsumer.accept(payload, e);
                     }
                     registration.confirmError();
                 }
diff --git 
a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/LegacyPlcValueHandler.java
 
b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/LegacyPlcValueHandler.java
index 0343bc4434..8666442a7c 100644
--- 
a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/LegacyPlcValueHandler.java
+++ 
b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/LegacyPlcValueHandler.java
@@ -123,6 +123,10 @@ public class LegacyPlcValueHandler implements 
PlcValueHandler {
 
     public static PlcValue of(PlcTag tag, Object[] values) {
         if (values.length == 1) {
+            if (values[0] instanceof PlcValue) {
+                return (PlcValue) values[0];
+            }
+
             Object value = values[0];
             if(tag.getPlcValueType() == null) {
                 // TODO: This is a hacky shortcut ..
diff --git 
a/protocols/opcua/src/main/generated/protocols/opcua/opc-manual.mspec 
b/protocols/opcua/src/main/generated/protocols/opcua/opc-manual.mspec
index b2cb1610b7..bf6a048950 100644
--- a/protocols/opcua/src/main/generated/protocols/opcua/opc-manual.mspec
+++ b/protocols/opcua/src/main/generated/protocols/opcua/opc-manual.mspec
@@ -176,7 +176,7 @@
             [typeSwitch encodingMask.xmlBody, encodingMask.binaryBody
                 ['false', 'true' BinaryExtensionObjectWithMask
                     [implicit int 32 bodyLength 'body == null ? 0 : 
body.lengthInBytes']
-                    [simple ExtensionObjectDefinition('extensionId') body]
+                    [manual ExtensionObjectDefinition body 
'STATIC_CALL("parseExtensionObjectBody", readBuffer, extensionId, bodyLength)' 
'STATIC_CALL("serializeExtensionObjectBody", writeBuffer, body)' 'body == null 
? 0 : body.lengthInBits']
                 ]
                 ['false', 'false' NullExtensionObjectWithMask
                     [virtual ExtensionObjectDefinition('0') body 'null']
diff --git a/protocols/opcua/src/main/xslt/opc-manual.xsl 
b/protocols/opcua/src/main/xslt/opc-manual.xsl
index f5ea6879df..9bd6300a6c 100644
--- a/protocols/opcua/src/main/xslt/opc-manual.xsl
+++ b/protocols/opcua/src/main/xslt/opc-manual.xsl
@@ -225,7 +225,7 @@
             [typeSwitch encodingMask.xmlBody, encodingMask.binaryBody
                 ['false', 'true' BinaryExtensionObjectWithMask
                     [implicit int 32 bodyLength 'body == null ? 0 : 
body.lengthInBytes']
-                    [simple ExtensionObjectDefinition('extensionId') body]
+                    [manual ExtensionObjectDefinition body 
'STATIC_CALL("parseExtensionObjectBody", readBuffer, extensionId, bodyLength)' 
'STATIC_CALL("serializeExtensionObjectBody", writeBuffer, body)' 'body == null 
? 0 : body.lengthInBits']
                 ]
                 ['false', 'false' NullExtensionObjectWithMask
                     [virtual ExtensionObjectDefinition('0') body 'null']


Reply via email to