From 735def28a1925888b2fe9181e37a8f3965361956 Mon Sep 17 00:00:00 2001 From: Christofer Dutz Date: Fri, 13 Feb 2026 14:12:33 +0100 Subject: [PATCH 1/8] chore: Added a comprehensive test against my S7-1500 devices and started fine-tuning the code to work with more and more of the test-cases. --- .../plc4x/java/opcua/readwrite/Variant.java | 16 +- .../java/opcua/readwrite/VariantBoolean.java | 4 +- .../java/opcua/readwrite/VariantByte.java | 4 +- .../opcua/readwrite/VariantByteString.java | 4 +- .../opcua/readwrite/VariantDataValue.java | 4 +- .../java/opcua/readwrite/VariantDateTime.java | 4 +- .../readwrite/VariantDiagnosticInfo.java | 4 +- .../java/opcua/readwrite/VariantDouble.java | 4 +- .../readwrite/VariantExpandedNodeId.java | 4 +- .../readwrite/VariantExtensionObject.java | 4 +- .../java/opcua/readwrite/VariantFloat.java | 4 +- .../java/opcua/readwrite/VariantGuid.java | 4 +- .../java/opcua/readwrite/VariantInt16.java | 4 +- .../java/opcua/readwrite/VariantInt32.java | 4 +- .../java/opcua/readwrite/VariantInt64.java | 4 +- .../opcua/readwrite/VariantLocalizedText.java | 4 +- .../java/opcua/readwrite/VariantNodeId.java | 4 +- .../java/opcua/readwrite/VariantNull.java | 4 +- .../opcua/readwrite/VariantQualifiedName.java | 4 +- .../java/opcua/readwrite/VariantSByte.java | 4 +- .../opcua/readwrite/VariantStatusCode.java | 4 +- .../java/opcua/readwrite/VariantString.java | 4 +- .../java/opcua/readwrite/VariantUInt16.java | 4 +- .../java/opcua/readwrite/VariantUInt32.java | 4 +- .../java/opcua/readwrite/VariantUInt64.java | 4 +- .../java/opcua/readwrite/VariantVariant.java | 4 +- .../opcua/readwrite/VariantXmlElement.java | 4 +- .../java/opcua/context/Conversation.java | 8 +- .../opcua/protocol/OpcuaProtocolLogic.java | 173 +++++++++++++----- .../opcua/ManualS71500NewFWDriverTest.java | 139 ++++++++++++++ .../protocols/opcua/opc-manual.mspec | 2 +- protocols/opcua/src/main/xslt/opc-manual.xsl | 2 +- 32 files changed, 334 insertions(+), 110 deletions(-) create mode 100644 plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualS71500NewFWDriverTest.java diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/Variant.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/Variant.java index 14397b3060d..58e46699439 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/Variant.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/Variant.java @@ -44,13 +44,13 @@ public abstract class Variant implements Message { protected final boolean arrayLengthSpecified; protected final boolean arrayDimensionsSpecified; protected final Integer noOfArrayDimensions; - protected final List arrayDimensions; + protected final List arrayDimensions; public Variant( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { super(); this.arrayLengthSpecified = arrayLengthSpecified; this.arrayDimensionsSpecified = arrayDimensionsSpecified; @@ -70,7 +70,7 @@ public Integer getNoOfArrayDimensions() { return noOfArrayDimensions; } - public List getArrayDimensions() { + public List getArrayDimensions() { return arrayDimensions; } @@ -99,7 +99,7 @@ public void serialize(WriteBuffer writeBuffer) throws SerializationException { writeOptionalField("noOfArrayDimensions", noOfArrayDimensions, writeSignedInt(writeBuffer, 32)); // Array Field (arrayDimensions) - writeSimpleTypeArrayField("arrayDimensions", arrayDimensions, writeBoolean(writeBuffer)); + writeSimpleTypeArrayField("arrayDimensions", arrayDimensions, writeSignedInt(writeBuffer, 32)); writeBuffer.popContext("Variant"); } @@ -133,7 +133,7 @@ public int getLengthInBits() { // Array field if (arrayDimensions != null) { - lengthInBits += 1 * arrayDimensions.size(); + lengthInBits += 32 * arrayDimensions.size(); } return lengthInBits; @@ -222,10 +222,10 @@ public static Variant staticParse(ReadBuffer readBuffer) throws ParseException { readOptionalField( "noOfArrayDimensions", readSignedInt(readBuffer, 32), arrayDimensionsSpecified); - List arrayDimensions = + List arrayDimensions = readCountArrayField( "arrayDimensions", - readBoolean(readBuffer), + readSignedInt(readBuffer, 32), (((noOfArrayDimensions) == (null)) ? 0 : noOfArrayDimensions)); readBuffer.closeContext("Variant"); @@ -241,7 +241,7 @@ Variant build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions); + List arrayDimensions); } @Override diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantBoolean.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantBoolean.java index 452894bc4d1..a89d6439686 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantBoolean.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantBoolean.java @@ -50,7 +50,7 @@ public VariantBoolean( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, byte[] value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -136,7 +136,7 @@ public VariantBoolean build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantBoolean variantBoolean = new VariantBoolean( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantByte.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantByte.java index d4bb68dfe94..0d488d2aa3e 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantByte.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantByte.java @@ -50,7 +50,7 @@ public VariantByte( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -138,7 +138,7 @@ public VariantByte build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantByte variantByte = new VariantByte( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantByteString.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantByteString.java index 7c5478e068b..f9b2af6a1b0 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantByteString.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantByteString.java @@ -50,7 +50,7 @@ public VariantByteString( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantByteString build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantByteString variantByteString = new VariantByteString( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDataValue.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDataValue.java index 424128db2a4..7ee036082ab 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDataValue.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDataValue.java @@ -50,7 +50,7 @@ public VariantDataValue( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantDataValue build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantDataValue variantDataValue = new VariantDataValue( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDateTime.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDateTime.java index 68feac13707..603b8da10f5 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDateTime.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDateTime.java @@ -50,7 +50,7 @@ public VariantDateTime( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -136,7 +136,7 @@ public VariantDateTime build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantDateTime variantDateTime = new VariantDateTime( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDiagnosticInfo.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDiagnosticInfo.java index 3d4cc55c091..eeebd81b3bd 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDiagnosticInfo.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDiagnosticInfo.java @@ -50,7 +50,7 @@ public VariantDiagnosticInfo( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantDiagnosticInfo build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantDiagnosticInfo variantDiagnosticInfo = new VariantDiagnosticInfo( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDouble.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDouble.java index 6c7f9f33a2b..1acb34c9382 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDouble.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantDouble.java @@ -50,7 +50,7 @@ public VariantDouble( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -136,7 +136,7 @@ public VariantDouble build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantDouble variantDouble = new VariantDouble( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantExpandedNodeId.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantExpandedNodeId.java index 5b4db90d26e..0ac9caaa763 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantExpandedNodeId.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantExpandedNodeId.java @@ -50,7 +50,7 @@ public VariantExpandedNodeId( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantExpandedNodeId build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantExpandedNodeId variantExpandedNodeId = new VariantExpandedNodeId( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantExtensionObject.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantExtensionObject.java index b30622ff806..b914e79d5bb 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantExtensionObject.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantExtensionObject.java @@ -50,7 +50,7 @@ public VariantExtensionObject( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -143,7 +143,7 @@ public VariantExtensionObject build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantExtensionObject variantExtensionObject = new VariantExtensionObject( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantFloat.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantFloat.java index 186aaed2a0c..4f8b1b76942 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantFloat.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantFloat.java @@ -50,7 +50,7 @@ public VariantFloat( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -136,7 +136,7 @@ public VariantFloat build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantFloat variantFloat = new VariantFloat( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantGuid.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantGuid.java index fc85b24505a..7011f02a82c 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantGuid.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantGuid.java @@ -50,7 +50,7 @@ public VariantGuid( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantGuid build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantGuid variantGuid = new VariantGuid( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt16.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt16.java index ce6f2cd30c2..8950ea2277d 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt16.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt16.java @@ -50,7 +50,7 @@ public VariantInt16( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -138,7 +138,7 @@ public VariantInt16 build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantInt16 variantInt16 = new VariantInt16( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt32.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt32.java index 8cc8a9051fa..ace173c9fcd 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt32.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt32.java @@ -50,7 +50,7 @@ public VariantInt32( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -136,7 +136,7 @@ public VariantInt32 build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantInt32 variantInt32 = new VariantInt32( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt64.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt64.java index f5bf1d90259..b11d03c2f71 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt64.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantInt64.java @@ -50,7 +50,7 @@ public VariantInt64( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -136,7 +136,7 @@ public VariantInt64 build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantInt64 variantInt64 = new VariantInt64( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantLocalizedText.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantLocalizedText.java index e7ac52373f1..7a3706b867c 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantLocalizedText.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantLocalizedText.java @@ -50,7 +50,7 @@ public VariantLocalizedText( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantLocalizedText build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantLocalizedText variantLocalizedText = new VariantLocalizedText( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantNodeId.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantNodeId.java index 215174c888e..e91da66e741 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantNodeId.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantNodeId.java @@ -50,7 +50,7 @@ public VariantNodeId( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantNodeId build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantNodeId variantNodeId = new VariantNodeId( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantNull.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantNull.java index cfc5715b590..d6d77484a82 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantNull.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantNull.java @@ -46,7 +46,7 @@ public VariantNull( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); } @@ -92,7 +92,7 @@ public VariantNull build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantNull variantNull = new VariantNull( arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantQualifiedName.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantQualifiedName.java index 550e91622ec..9ff7e799b73 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantQualifiedName.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantQualifiedName.java @@ -50,7 +50,7 @@ public VariantQualifiedName( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantQualifiedName build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantQualifiedName variantQualifiedName = new VariantQualifiedName( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantSByte.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantSByte.java index 9a5cdb3fa68..921899ecd39 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantSByte.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantSByte.java @@ -50,7 +50,7 @@ public VariantSByte( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, byte[] value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -136,7 +136,7 @@ public VariantSByte build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantSByte variantSByte = new VariantSByte( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantStatusCode.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantStatusCode.java index 55ae3cca886..538849d02ed 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantStatusCode.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantStatusCode.java @@ -50,7 +50,7 @@ public VariantStatusCode( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantStatusCode build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantStatusCode variantStatusCode = new VariantStatusCode( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantString.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantString.java index 31cc206039b..5a52922e828 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantString.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantString.java @@ -50,7 +50,7 @@ public VariantString( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantString build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantString variantString = new VariantString( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt16.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt16.java index 04bbc933d7c..93428581f43 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt16.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt16.java @@ -50,7 +50,7 @@ public VariantUInt16( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -138,7 +138,7 @@ public VariantUInt16 build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantUInt16 variantUInt16 = new VariantUInt16( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt32.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt32.java index c42a0c7acc0..4fd3a59060d 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt32.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt32.java @@ -50,7 +50,7 @@ public VariantUInt32( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -138,7 +138,7 @@ public VariantUInt32 build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantUInt32 variantUInt32 = new VariantUInt32( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt64.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt64.java index 054473830ce..29e5b7c1d17 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt64.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantUInt64.java @@ -51,7 +51,7 @@ public VariantUInt64( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -139,7 +139,7 @@ public VariantUInt64 build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantUInt64 variantUInt64 = new VariantUInt64( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantVariant.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantVariant.java index dd10e407d98..f5f552ca244 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantVariant.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantVariant.java @@ -50,7 +50,7 @@ public VariantVariant( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantVariant build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantVariant variantVariant = new VariantVariant( arrayLengthSpecified, diff --git a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantXmlElement.java b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantXmlElement.java index 4da3834ac95..7b93a5d04c5 100644 --- a/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantXmlElement.java +++ b/plc4j/drivers/opcua/src/main/generated/org/apache/plc4x/java/opcua/readwrite/VariantXmlElement.java @@ -50,7 +50,7 @@ public VariantXmlElement( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions, + List arrayDimensions, Integer arrayLength, List value) { super(arrayLengthSpecified, arrayDimensionsSpecified, noOfArrayDimensions, arrayDimensions); @@ -142,7 +142,7 @@ public VariantXmlElement build( boolean arrayLengthSpecified, boolean arrayDimensionsSpecified, Integer noOfArrayDimensions, - List arrayDimensions) { + List arrayDimensions) { VariantXmlElement variantXmlElement = new VariantXmlElement( arrayLengthSpecified, 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 c2ef7978eae..a9c9c2b176c 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 @@ -337,7 +337,13 @@ private CompletableFuture submit(ExtensionObjectDefinition requestDefini if (extensionObjectBody instanceof ServiceFault) { ServiceFault fault = (ServiceFault) extensionObjectBody; - future.completeExceptionally(toProtocolException(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. + future.complete(extensionObjectBody); + } else { + future.completeExceptionally(toProtocolException(fault)); + } } else { future.complete(extensionObjectBody); } 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 41d8c94cd70..887c5497cac 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 @@ -50,7 +50,6 @@ import org.apache.plc4x.java.spi.configuration.HasConfiguration; import org.apache.plc4x.java.spi.connection.PlcTagHandler; import org.apache.plc4x.java.spi.context.DriverContext; -import org.apache.plc4x.java.spi.generation.Message; import org.apache.plc4x.java.spi.messages.*; import org.apache.plc4x.java.spi.messages.utils.DefaultPlcResponseItem; import org.apache.plc4x.java.spi.messages.utils.PlcResponseItem; @@ -59,9 +58,7 @@ import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionTag; import org.apache.plc4x.java.spi.transaction.RequestTransactionManager; import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction; -import org.apache.plc4x.java.spi.values.LegacyPlcValueHandler; -import org.apache.plc4x.java.spi.values.PlcList; -import org.apache.plc4x.java.spi.values.PlcSTRING; +import org.apache.plc4x.java.spi.values.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -314,65 +311,147 @@ static PlcValue variantToPlcValue(PlcTag tag, Variant variant) { PlcValue value = null; if (variant instanceof VariantBoolean) { byte[] array = ((VariantBoolean) variant).getValue(); - int length = array.length; - Boolean[] tmpValue = new Boolean[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = array[i] != 0; + if (array.length == 1) { + value = new PlcBOOL(array[0] != 0); + } else { + List values = new ArrayList<>(array.length); + for (byte b : array) { + values.add(new PlcBOOL(b != 0)); + } + value = new PlcList(values); } - value = LegacyPlcValueHandler.of(tmpValue); } else if (variant instanceof VariantSByte) { byte[] array = ((VariantSByte) variant).getValue(); - value = LegacyPlcValueHandler.of(tag, array); + if (array.length == 1) { + value = new PlcSINT(array[0]); + } else { + List values = new ArrayList<>(array.length); + for (byte b : array) { + values.add(new PlcSINT(b)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantByte) { List array = ((VariantByte) variant).getValue(); - Short[] tmpValue = array.toArray(new Short[0]); - value = LegacyPlcValueHandler.of(tmpValue); + if (array.size() == 1) { + value = new PlcUSINT(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (Short s : array) { + values.add(new PlcUSINT(s)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantInt16) { List array = ((VariantInt16) variant).getValue(); - Short[] tmpValue = array.toArray(new Short[0]); - value = LegacyPlcValueHandler.of(tmpValue); + if (array.size() == 1) { + value = new PlcINT(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (Short s : array) { + values.add(new PlcINT(s)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantUInt16) { List array = ((VariantUInt16) variant).getValue(); - Integer[] tmpValue = array.toArray(new Integer[0]); - value = LegacyPlcValueHandler.of(tmpValue); + if (array.size() == 1) { + value = new PlcUINT(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (Integer i : array) { + values.add(new PlcUINT(i)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantInt32) { List array = ((VariantInt32) variant).getValue(); - Integer[] tmpValue = array.toArray(new Integer[0]); - value = LegacyPlcValueHandler.of(tmpValue); + if (array.size() == 1) { + value = new PlcDINT(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (Integer i : array) { + values.add(new PlcDINT(i)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantUInt32) { List array = ((VariantUInt32) variant).getValue(); - Long[] tmpValue = array.toArray(new Long[0]); - value = LegacyPlcValueHandler.of(tmpValue); + if (array.size() == 1) { + value = new PlcUDINT(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (Long l : array) { + values.add(new PlcUDINT(l)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantInt64) { List array = ((VariantInt64) variant).getValue(); - Long[] tmpValue = array.toArray(new Long[0]); - value = LegacyPlcValueHandler.of(tmpValue); + if (array.size() == 1) { + value = new PlcLINT(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (Long l : array) { + values.add(new PlcLINT(l)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantUInt64) { - value = LegacyPlcValueHandler.of(((VariantUInt64) variant).getValue()); + List array = ((VariantUInt64) variant).getValue(); + if (array.size() == 1) { + value = new PlcULINT(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (BigInteger bi : array) { + values.add(new PlcULINT(bi)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantFloat) { List array = ((VariantFloat) variant).getValue(); - Float[] tmpValue = array.toArray(new Float[0]); - value = LegacyPlcValueHandler.of(tmpValue); + if (array.size() == 1) { + value = new PlcREAL(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (Float f : array) { + values.add(new PlcREAL(f)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantDouble) { List array = ((VariantDouble) variant).getValue(); - Double[] tmpValue = array.toArray(new Double[0]); - value = LegacyPlcValueHandler.of(tmpValue); + if (array.size() == 1) { + value = new PlcLREAL(array.getFirst()); + } else { + List values = new ArrayList<>(array.size()); + for (Double d : array) { + values.add(new PlcLREAL(d)); + } + value = new PlcList(values); + } } else if (variant instanceof VariantString) { - int length = ((VariantString) variant).getValue().size(); List stringArray = ((VariantString) variant).getValue(); - String[] tmpValue = new String[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = stringArray.get(i).getStringValue(); + if (stringArray.size() == 1) { + value = new PlcSTRING(stringArray.getFirst().getStringValue()); + } else { + List values = new ArrayList<>(stringArray.size()); + for (PascalString ps : stringArray) { + values.add(new PlcSTRING(ps.getStringValue())); + } + value = new PlcList(values); } - value = LegacyPlcValueHandler.of(tmpValue); } else if (variant instanceof VariantDateTime) { List array = ((VariantDateTime) variant).getValue(); - int length = array.size(); - LocalDateTime[] tmpValue = new LocalDateTime[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(getDateTime(array.get(i))), ZoneOffset.UTC); + if (array.size() == 1) { + value = LegacyPlcValueHandler.of(LocalDateTime.ofInstant(Instant.ofEpochMilli(getDateTime(array.getFirst())), ZoneOffset.UTC)); + } else { + List values = new ArrayList<>(array.size()); + for (Long l : array) { + values.add(LegacyPlcValueHandler.of(LocalDateTime.ofInstant(Instant.ofEpochMilli(getDateTime(l)), ZoneOffset.UTC))); + } + value = new PlcList(values); } - value = LegacyPlcValueHandler.of(tmpValue); } else if (variant instanceof VariantGuid) { List array = ((VariantGuid) variant).getValue(); int length = array.size(); @@ -495,21 +574,21 @@ private Variant fromPlcValue(String tagName, OpcuaTag tag, PlcWriteRequest reque List plcValueList = valueObject.getList(); PlcValueType dataType = tag.getPlcValueType(); if (dataType.equals(PlcValueType.NULL)) { - if (plcValueList.get(0).getObject() instanceof Boolean) { + if (plcValueList.getFirst().getObject() instanceof Boolean) { dataType = PlcValueType.BOOL; - } else if (plcValueList.get(0).getObject() instanceof Byte) { + } else if (plcValueList.getFirst().getObject() instanceof Byte) { dataType = PlcValueType.SINT; - } else if (plcValueList.get(0).getObject() instanceof Short) { + } else if (plcValueList.getFirst().getObject() instanceof Short) { dataType = PlcValueType.INT; - } else if (plcValueList.get(0).getObject() instanceof Integer) { + } else if (plcValueList.getFirst().getObject() instanceof Integer) { dataType = PlcValueType.DINT; - } else if (plcValueList.get(0).getObject() instanceof Long) { + } else if (plcValueList.getFirst().getObject() instanceof Long) { dataType = PlcValueType.LINT; - } else if (plcValueList.get(0).getObject() instanceof Float) { + } else if (plcValueList.getFirst().getObject() instanceof Float) { dataType = PlcValueType.REAL; - } else if (plcValueList.get(0).getObject() instanceof Double) { + } else if (plcValueList.getFirst().getObject() instanceof Double) { dataType = PlcValueType.LREAL; - } else if (plcValueList.get(0).getObject() instanceof String) { + } else if (plcValueList.getFirst().getObject() instanceof String) { dataType = PlcValueType.STRING; } } @@ -802,7 +881,7 @@ private PlcWriteResponse writeResponse(DefaultPlcWriteRequest request, WriteResp @Override public CompletableFuture subscribe(PlcSubscriptionRequest subscriptionRequest) { List tagNames = new ArrayList<>(subscriptionRequest.getTagNames()); - long cycleTime = (subscriptionRequest.getTag(tagNames.get(0))).getDuration().orElse(Duration.ofMillis(1000)).toMillis(); + long cycleTime = (subscriptionRequest.getTag(tagNames.getFirst())).getDuration().orElse(Duration.ofMillis(1000)).toMillis(); CompletableFuture future = new CompletableFuture<>(); RequestTransaction transaction = tm.startRequest(); @@ -823,7 +902,7 @@ public CompletableFuture subscribe(PlcSubscriptionReque subscriptions.put(handle.getSubscriptionId(), handle); return handle; }) - .thenCompose(handle -> handle.onSubscribeCreateMonitoredItemsRequest()) + .thenCompose(OpcuaSubscriptionHandle::onSubscribeCreateMonitoredItemsRequest) .thenApply(handle -> { Map> values = new HashMap<>(); for (String tagName : subscriptionRequest.getTagNames()) { 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/ManualS71500NewFWDriverTest.java new file mode 100644 index 00000000000..768a3e255f8 --- /dev/null +++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualS71500NewFWDriverTest.java @@ -0,0 +1,139 @@ +/* + * 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; + +import org.apache.plc4x.java.spi.values.*; +import org.apache.plc4x.test.manual.ManualTest; + +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; + +public class ManualS71500NewFWDriverTest extends ManualTest { + + public ManualS71500NewFWDriverTest(String connectionString) { + super(connectionString, true, false, 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\"", new PlcTIME(2500)); // TODO: Is returned as Int32 +// test.addTestCase(/*"g_dat",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_dat\"", new PlcDATE(LocalDate.of(2025, 11, 12))); // TODO: Is returned as UInt16 +// test.addTestCase(/*"g_timoday",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_timoday\"", new PlcTIME_OF_DAY(LocalTime.of(14, 33, 21, 250000000))); // TODO: Is returned as UInt32 +// test.addTestCase(/*"g_dattim",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_dattim\"", 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")); + 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), +// new PlcBOOL(false), new PlcBOOL(false), new PlcBOOL(true), new PlcBOOL(false)) +// )); +// test.addTestCase(/*"g_arrByte",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrByte\"", new PlcList(List.of( +// new PlcBYTE(0xDE), new PlcBYTE(0xAD), new PlcBYTE(0xBE), new PlcBYTE(0xEF), +// new PlcBYTE(0x12), new PlcBYTE(0x34), new PlcBYTE(0x56), new PlcBYTE(0x78)) +// )); +// test.addTestCase(/*"g_arrInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrInt\"", new PlcList(List.of( +// new PlcINT(-3), new PlcINT(-1), new PlcINT(0), new PlcINT(1), new PlcINT(3)) +// )); +// test.addTestCase(/*"g_arrUInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrUInt\"", new PlcList(List.of( +// new PlcUINT(1), new PlcUINT(10), new PlcUINT(100), new PlcUINT(1000), new PlcUINT(10000)) +// )); +// test.addTestCase(/*"g_arrDInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrDInt\"", new PlcList(List.of( +// new PlcDINT(-1000), new PlcDINT(0), new PlcDINT(1000), new PlcDINT(2000000)) +// )); +// test.addTestCase(/*"g_arrUDInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrUDInt\"", new PlcList(List.of( +// new PlcUDINT(0), new PlcUDINT(1), new PlcUDINT(0xFFFF), new PlcUDINT(0x12345678)) +// )); +// test.addTestCase(/*"g_arrLReal",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrLReal\"", new PlcList(List.of( +// new PlcLREAL(1.5), new PlcLREAL(-2.0), new PlcLREAL(0.125)) +// )); +// test.addTestCase(/*"g_arrTime",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrTime\"", new PlcList(List.of( +// new PlcTIME(Duration.ofMillis(10)), new PlcTIME(Duration.ofSeconds(1)), new PlcTIME(Duration.ofSeconds(10))) +// )); +// test.addTestCase(/*"g_arrString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrString\"", new PlcList(List.of( +// new PlcSTRING("alpha"), new PlcSTRING("beta"), new PlcSTRING("gamma")) +// )); +// test.addTestCase(/*"g_arrWString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrWString\"", new PlcList(List.of( +// new PlcWSTRING("Äpfel"), new PlcWSTRING("Öl")) +// )); +// test.addTestCase(/*"g_matI16_2x3",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matI16_2x3\"", new PlcList(List.of( // TODO: Getting a class cast error, because OpcuaMessageResponse cannot be cast to OpcuaAPU +// new PlcList(List.of( +// new PlcINT(10), new PlcINT(11), new PlcINT(12) +// )), +// new PlcList(List.of( +// new PlcINT(-10), new PlcINT(-11), new PlcINT(-12) +// ))) +// )); + test.addTestCase(/*"g_matR32_3x2",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matR32_3x2\"", new PlcList(List.of( + new PlcList(List.of( + new PlcREAL(1.0), new PlcREAL(1.5) + )), + new PlcList(List.of( + new PlcREAL(2.0), new PlcREAL(2.5) + )), + new PlcList(List.of( + new PlcREAL(3.0), new PlcREAL(3.5) + ))) + )); +// test.addTestCase(/*"g_cubeU16_2x2x2",*/ "%DB42:466.0:INT[1;1..2][1;1..2][1;1..2]", new PlcList(List.of( +// new PlcList(List.of( +// new PlcList(List.of( +// new PlcINT(1), new PlcINT(2) +// )), +// new PlcList(List.of( +// new PlcINT(3), new PlcINT(4) +// )) +// )), +// new PlcList(List.of( +// new PlcList(List.of( +// new PlcINT(5), new PlcINT(6) +// )), +// new PlcList(List.of( +// new PlcINT(7), new PlcINT(8) +// )) +// ))) +// )); + } + + long start = System.currentTimeMillis(); + test.run(); + long end = System.currentTimeMillis(); + System.out.printf("Finished in %d ms", end - start); + } + +} 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 380da9f2b8b..b2cb1610b72 100644 --- a/protocols/opcua/src/main/generated/protocols/opcua/opc-manual.mspec +++ b/protocols/opcua/src/main/generated/protocols/opcua/opc-manual.mspec @@ -2736,7 +2736,7 @@ ] ] [optional int 32 noOfArrayDimensions 'arrayDimensionsSpecified'] - [array bit arrayDimensions count 'noOfArrayDimensions == null ? 0 : noOfArrayDimensions'] + [array int 32 arrayDimensions count 'noOfArrayDimensions == null ? 0 : noOfArrayDimensions'] ] // node type, with two leading reserved bytes diff --git a/protocols/opcua/src/main/xslt/opc-manual.xsl b/protocols/opcua/src/main/xslt/opc-manual.xsl index c0fd1267b83..f5ea6879df2 100644 --- a/protocols/opcua/src/main/xslt/opc-manual.xsl +++ b/protocols/opcua/src/main/xslt/opc-manual.xsl @@ -355,7 +355,7 @@ ] ] [optional int 32 noOfArrayDimensions 'arrayDimensionsSpecified'] - [array bit arrayDimensions count 'noOfArrayDimensions == null ? 0 : noOfArrayDimensions'] + [array int 32 arrayDimensions count 'noOfArrayDimensions == null ? 0 : noOfArrayDimensions'] ] // node type, with two leading reserved bytes From cc9d443478e8fa7cd4270381cc13110e65aa7602 Mon Sep 17 00:00:00 2001 From: Christofer Dutz Date: Fri, 13 Feb 2026 14:44:52 +0100 Subject: [PATCH 2/8] chore: Got all array tests passing too. --- .../opcua/protocol/OpcuaProtocolLogic.java | 284 ++++++++---------- .../opcua/ManualS71500NewFWDriverTest.java | 144 ++++----- 2 files changed, 201 insertions(+), 227 deletions(-) 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 887c5497cac..5a459a42166 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 @@ -311,232 +311,206 @@ static PlcValue variantToPlcValue(PlcTag tag, Variant variant) { PlcValue value = null; if (variant instanceof VariantBoolean) { byte[] array = ((VariantBoolean) variant).getValue(); - if (array.length == 1) { - value = new PlcBOOL(array[0] != 0); - } else { - List values = new ArrayList<>(array.length); - for (byte b : array) { - values.add(new PlcBOOL(b != 0)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.length); + for (byte b : array) { + values.add(new PlcBOOL(b != 0)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantSByte) { byte[] array = ((VariantSByte) variant).getValue(); - if (array.length == 1) { - value = new PlcSINT(array[0]); - } else { - List values = new ArrayList<>(array.length); - for (byte b : array) { - values.add(new PlcSINT(b)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.length); + for (byte b : array) { + values.add(new PlcSINT(b)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantByte) { List array = ((VariantByte) variant).getValue(); - if (array.size() == 1) { - value = new PlcUSINT(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (Short s : array) { - values.add(new PlcUSINT(s)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Short s : array) { + values.add(new PlcUSINT(s)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantInt16) { List array = ((VariantInt16) variant).getValue(); - if (array.size() == 1) { - value = new PlcINT(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (Short s : array) { - values.add(new PlcINT(s)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Short s : array) { + values.add(new PlcINT(s)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantUInt16) { List array = ((VariantUInt16) variant).getValue(); - if (array.size() == 1) { - value = new PlcUINT(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (Integer i : array) { - values.add(new PlcUINT(i)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Integer i : array) { + values.add(new PlcUINT(i)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantInt32) { List array = ((VariantInt32) variant).getValue(); - if (array.size() == 1) { - value = new PlcDINT(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (Integer i : array) { - values.add(new PlcDINT(i)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Integer i : array) { + values.add(new PlcDINT(i)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantUInt32) { List array = ((VariantUInt32) variant).getValue(); - if (array.size() == 1) { - value = new PlcUDINT(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (Long l : array) { - values.add(new PlcUDINT(l)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Long l : array) { + values.add(new PlcUDINT(l)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantInt64) { List array = ((VariantInt64) variant).getValue(); - if (array.size() == 1) { - value = new PlcLINT(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (Long l : array) { - values.add(new PlcLINT(l)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Long l : array) { + values.add(new PlcLINT(l)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantUInt64) { List array = ((VariantUInt64) variant).getValue(); - if (array.size() == 1) { - value = new PlcULINT(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (BigInteger bi : array) { - values.add(new PlcULINT(bi)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (BigInteger bi : array) { + values.add(new PlcULINT(bi)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantFloat) { List array = ((VariantFloat) variant).getValue(); - if (array.size() == 1) { - value = new PlcREAL(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (Float f : array) { - values.add(new PlcREAL(f)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Float f : array) { + values.add(new PlcREAL(f)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantDouble) { List array = ((VariantDouble) variant).getValue(); - if (array.size() == 1) { - value = new PlcLREAL(array.getFirst()); - } else { - List values = new ArrayList<>(array.size()); - for (Double d : array) { - values.add(new PlcLREAL(d)); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Double d : array) { + values.add(new PlcLREAL(d)); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantString) { List stringArray = ((VariantString) variant).getValue(); - if (stringArray.size() == 1) { - value = new PlcSTRING(stringArray.getFirst().getStringValue()); - } else { - List values = new ArrayList<>(stringArray.size()); - for (PascalString ps : stringArray) { - values.add(new PlcSTRING(ps.getStringValue())); - } - value = new PlcList(values); + List values = new ArrayList<>(stringArray.size()); + for (PascalString ps : stringArray) { + values.add(new PlcSTRING(ps.getStringValue())); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantDateTime) { List array = ((VariantDateTime) variant).getValue(); - if (array.size() == 1) { - value = LegacyPlcValueHandler.of(LocalDateTime.ofInstant(Instant.ofEpochMilli(getDateTime(array.getFirst())), ZoneOffset.UTC)); - } else { - List values = new ArrayList<>(array.size()); - for (Long l : array) { - values.add(LegacyPlcValueHandler.of(LocalDateTime.ofInstant(Instant.ofEpochMilli(getDateTime(l)), ZoneOffset.UTC))); - } - value = new PlcList(values); + List values = new ArrayList<>(array.size()); + for (Long l : array) { + values.add(LegacyPlcValueHandler.of(LocalDateTime.ofInstant(Instant.ofEpochMilli(getDateTime(l)), ZoneOffset.UTC))); } + value = structurePlcValues(values, variant); } else if (variant instanceof VariantGuid) { List array = ((VariantGuid) variant).getValue(); - int length = array.size(); - String[] tmpValue = new String[length]; - for (int i = 0; i < length; i++) { - //These two data section aren't little endian like the rest. - byte[] data4Bytes = array.get(i).getData4(); + List values = new ArrayList<>(array.size()); + for (GuidValue guidValue : array) { + //These two data sections aren't little endian like the rest. + byte[] data4Bytes = guidValue.getData4(); int data4 = 0; for (byte data4Byte : data4Bytes) { data4 = (data4 << 8) + (data4Byte & 0xff); } - byte[] data5Bytes = array.get(i).getData5(); + byte[] data5Bytes = guidValue.getData5(); long data5 = 0; for (byte data5Byte : data5Bytes) { data5 = (data5 << 8) + (data5Byte & 0xff); } - tmpValue[i] = Long.toHexString(array.get(i).getData1()) + "-" + Integer.toHexString(array.get(i).getData2()) + "-" + Integer.toHexString(array.get(i).getData3()) + "-" + Integer.toHexString(data4) + "-" + Long.toHexString(data5); + values.add(new PlcSTRING(Long.toHexString(guidValue.getData1()) + "-" + Integer.toHexString(guidValue.getData2()) + "-" + Integer.toHexString(guidValue.getData3()) + "-" + Integer.toHexString(data4) + "-" + Long.toHexString(data5))); } - value = LegacyPlcValueHandler.of(tmpValue); + value = structurePlcValues(values, variant); } else if (variant instanceof VariantXmlElement) { - int length = ((VariantXmlElement) variant).getValue().size(); List strings = ((VariantXmlElement) variant).getValue(); - String[] tmpValue = new String[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = strings.get(i).getStringValue(); + List values = new ArrayList<>(strings.size()); + for (PascalString ps : strings) { + values.add(new PlcSTRING(ps.getStringValue())); } - value = LegacyPlcValueHandler.of(tmpValue); + value = structurePlcValues(values, variant); } else if (variant instanceof VariantLocalizedText) { - int length = ((VariantLocalizedText) variant).getValue().size(); List strings = ((VariantLocalizedText) variant).getValue(); - String[] tmpValue = new String[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = ""; - tmpValue[i] += strings.get(i).getLocaleSpecified() ? strings.get(i).getLocale().getStringValue() + "|" : ""; - tmpValue[i] += strings.get(i).getTextSpecified() ? strings.get(i).getText().getStringValue() : ""; + List values = new ArrayList<>(strings.size()); + for (LocalizedText lt : strings) { + String s = ""; + s += lt.getLocaleSpecified() ? lt.getLocale().getStringValue() + "|" : ""; + s += lt.getTextSpecified() ? lt.getText().getStringValue() : ""; + values.add(new PlcSTRING(s)); } - value = LegacyPlcValueHandler.of(tmpValue); + value = structurePlcValues(values, variant); } else if (variant instanceof VariantQualifiedName) { - int length = ((VariantQualifiedName) variant).getValue().size(); List strings = ((VariantQualifiedName) variant).getValue(); - String[] tmpValue = new String[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = "ns=" + strings.get(i).getNamespaceIndex() + ";s=" + strings.get(i).getName().getStringValue(); + List values = new ArrayList<>(strings.size()); + for (QualifiedName qn : strings) { + values.add(new PlcSTRING("ns=" + qn.getNamespaceIndex() + ";s=" + qn.getName().getStringValue())); } - value = LegacyPlcValueHandler.of(tmpValue); + value = structurePlcValues(values, variant); } else if (variant instanceof VariantExtensionObject) { - int length = ((VariantExtensionObject) variant).getValue().size(); - List strings = ((VariantExtensionObject) variant).getValue(); - String[] tmpValue = new String[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = strings.get(i).toString(); + List objects = ((VariantExtensionObject) variant).getValue(); + List values = new ArrayList<>(objects.size()); + for (ExtensionObject eo : objects) { + values.add(new PlcSTRING(eo.toString())); } - value = LegacyPlcValueHandler.of(tmpValue); + value = structurePlcValues(values, variant); } else if (variant instanceof VariantNodeId) { - int length = ((VariantNodeId) variant).getValue().size(); - List strings = ((VariantNodeId) variant).getValue(); - String[] tmpValue = new String[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = strings.get(i).toString(); + List nodeIds = ((VariantNodeId) variant).getValue(); + List values = new ArrayList<>(nodeIds.size()); + for (NodeId nid : nodeIds) { + values.add(new PlcSTRING(nid.toString())); } - value = LegacyPlcValueHandler.of(tmpValue); + value = structurePlcValues(values, variant); } else if (variant instanceof VariantStatusCode) { - int length = ((VariantStatusCode) variant).getValue().size(); - List strings = ((VariantStatusCode) variant).getValue(); - String[] tmpValue = new String[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = strings.get(i).toString(); + List statusCodes = ((VariantStatusCode) variant).getValue(); + List values = new ArrayList<>(statusCodes.size()); + for (StatusCode sc : statusCodes) { + values.add(new PlcSTRING(sc.toString())); } - value = LegacyPlcValueHandler.of(tmpValue); + value = structurePlcValues(values, variant); } else if (variant instanceof VariantByteString) { - PlcList plcList = new PlcList(); List array = ((VariantByteString) variant).getValue(); + List values = new ArrayList<>(array.size()); for (ByteStringArray byteStringArray : array) { - int length = byteStringArray.getValue().size(); - Short[] tmpValue = new Short[length]; - for (int i = 0; i < length; i++) { - tmpValue[i] = byteStringArray.getValue().get(i); - } - plcList.add(LegacyPlcValueHandler.of(tmpValue)); + Short[] tmpValue = byteStringArray.getValue().toArray(new Short[0]); + values.add(LegacyPlcValueHandler.of(tmpValue)); } - value = plcList; + value = structurePlcValues(values, variant); } return value; } + /** + * Structures a flat list of PlcValues according to the variant's dimensionality: + * - Single value: returns the scalar PlcValue directly + * - 1D array (no array dimensions specified): returns a flat PlcList + * - Multi-dimensional array: returns nested PlcLists matching the declared dimensions + */ + private static PlcValue structurePlcValues(List values, Variant variant) { + if (values.size() == 1) { + return values.getFirst(); + } + List dimensions = variant.getArrayDimensions(); + if (dimensions == null || dimensions.isEmpty()) { + return new PlcList(values); + } + return buildMultiDimensionalList(values, dimensions); + } + + /** + * Recursively partitions a flat list of PlcValues into nested PlcLists + * according to the given dimensions. + * E.g., 6 values with dimensions [3, 2] produces a PlcList of 3 PlcLists of 2 values each. + */ + private static PlcValue buildMultiDimensionalList(List flatValues, List dimensions) { + if (dimensions.size() <= 1) { + return new PlcList(flatValues); + } + int currentDim = dimensions.getFirst(); + List remainingDims = dimensions.subList(1, dimensions.size()); + int chunkSize = flatValues.size() / currentDim; + List result = new ArrayList<>(currentDim); + for (int i = 0; i < currentDim; i++) { + result.add(buildMultiDimensionalList(flatValues.subList(i * chunkSize, (i + 1) * chunkSize), remainingDims)); + } + return new PlcList(result); + } + private static PlcResponseCode mapOpcStatusCode(long opcStatusCode, PlcResponseCode fallback) { if (!OpcuaStatusCode.isDefined(opcStatusCode)) { return PlcResponseCode.INTERNAL_ERROR; 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/ManualS71500NewFWDriverTest.java index 768a3e255f8..d49a884ecdf 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/ManualS71500NewFWDriverTest.java @@ -37,68 +37,68 @@ public ManualS71500NewFWDriverTest(String connectionString) { 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_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\"", new PlcTIME(2500)); // TODO: Is returned as Int32 // test.addTestCase(/*"g_dat",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_dat\"", new PlcDATE(LocalDate.of(2025, 11, 12))); // TODO: Is returned as UInt16 // test.addTestCase(/*"g_timoday",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_timoday\"", new PlcTIME_OF_DAY(LocalTime.of(14, 33, 21, 250000000))); // TODO: Is returned as UInt32 // test.addTestCase(/*"g_dattim",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_dattim\"", 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")); + 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")); 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), -// new PlcBOOL(false), new PlcBOOL(false), new PlcBOOL(true), new PlcBOOL(false)) -// )); -// test.addTestCase(/*"g_arrByte",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrByte\"", new PlcList(List.of( -// new PlcBYTE(0xDE), new PlcBYTE(0xAD), new PlcBYTE(0xBE), new PlcBYTE(0xEF), -// new PlcBYTE(0x12), new PlcBYTE(0x34), new PlcBYTE(0x56), new PlcBYTE(0x78)) -// )); -// test.addTestCase(/*"g_arrInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrInt\"", new PlcList(List.of( -// new PlcINT(-3), new PlcINT(-1), new PlcINT(0), new PlcINT(1), new PlcINT(3)) -// )); -// test.addTestCase(/*"g_arrUInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrUInt\"", new PlcList(List.of( -// new PlcUINT(1), new PlcUINT(10), new PlcUINT(100), new PlcUINT(1000), new PlcUINT(10000)) -// )); -// test.addTestCase(/*"g_arrDInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrDInt\"", new PlcList(List.of( -// new PlcDINT(-1000), new PlcDINT(0), new PlcDINT(1000), new PlcDINT(2000000)) -// )); -// test.addTestCase(/*"g_arrUDInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrUDInt\"", new PlcList(List.of( -// new PlcUDINT(0), new PlcUDINT(1), new PlcUDINT(0xFFFF), new PlcUDINT(0x12345678)) -// )); -// test.addTestCase(/*"g_arrLReal",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrLReal\"", new PlcList(List.of( -// new PlcLREAL(1.5), new PlcLREAL(-2.0), new PlcLREAL(0.125)) -// )); + 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), + new PlcBOOL(false), new PlcBOOL(false), new PlcBOOL(true), new PlcBOOL(false)) + )); + test.addTestCase(/*"g_arrByte",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrByte\"", new PlcList(List.of( + new PlcBYTE(0xDE), new PlcBYTE(0xAD), new PlcBYTE(0xBE), new PlcBYTE(0xEF), + new PlcBYTE(0x12), new PlcBYTE(0x34), new PlcBYTE(0x56), new PlcBYTE(0x78)) + )); + test.addTestCase(/*"g_arrInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrInt\"", new PlcList(List.of( + new PlcINT(-3), new PlcINT(-1), new PlcINT(0), new PlcINT(1), new PlcINT(3)) + )); + test.addTestCase(/*"g_arrUInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrUInt\"", new PlcList(List.of( + new PlcUINT(1), new PlcUINT(10), new PlcUINT(100), new PlcUINT(1000), new PlcUINT(10000)) + )); + test.addTestCase(/*"g_arrDInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrDInt\"", new PlcList(List.of( + new PlcDINT(-1000), new PlcDINT(0), new PlcDINT(1000), new PlcDINT(2000000)) + )); + test.addTestCase(/*"g_arrUDInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrUDInt\"", new PlcList(List.of( + new PlcUDINT(0), new PlcUDINT(1), new PlcUDINT(0xFFFF), new PlcUDINT(0x12345678)) + )); + test.addTestCase(/*"g_arrLReal",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrLReal\"", new PlcList(List.of( + new PlcLREAL(1.5), new PlcLREAL(-2.0), new PlcLREAL(0.125)) + )); // test.addTestCase(/*"g_arrTime",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrTime\"", new PlcList(List.of( // new PlcTIME(Duration.ofMillis(10)), new PlcTIME(Duration.ofSeconds(1)), new PlcTIME(Duration.ofSeconds(10))) // )); -// test.addTestCase(/*"g_arrString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrString\"", new PlcList(List.of( -// new PlcSTRING("alpha"), new PlcSTRING("beta"), new PlcSTRING("gamma")) -// )); -// test.addTestCase(/*"g_arrWString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrWString\"", new PlcList(List.of( -// new PlcWSTRING("Äpfel"), new PlcWSTRING("Öl")) -// )); -// test.addTestCase(/*"g_matI16_2x3",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matI16_2x3\"", new PlcList(List.of( // TODO: Getting a class cast error, because OpcuaMessageResponse cannot be cast to OpcuaAPU -// new PlcList(List.of( -// new PlcINT(10), new PlcINT(11), new PlcINT(12) -// )), -// new PlcList(List.of( -// new PlcINT(-10), new PlcINT(-11), new PlcINT(-12) -// ))) -// )); + test.addTestCase(/*"g_arrString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrString\"", new PlcList(List.of( + new PlcSTRING("alpha"), new PlcSTRING("beta"), new PlcSTRING("gamma")) + )); + test.addTestCase(/*"g_arrWString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrWString\"", new PlcList(List.of( + new PlcWSTRING("Äpfel"), new PlcWSTRING("Öl")) + )); + test.addTestCase(/*"g_matI16_2x3",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matI16_2x3\"", new PlcList(List.of( // TODO: Getting a class cast error, because OpcuaMessageResponse cannot be cast to OpcuaAPU + new PlcList(List.of( + new PlcINT(10), new PlcINT(11), new PlcINT(12) + )), + new PlcList(List.of( + new PlcINT(-10), new PlcINT(-11), new PlcINT(-12) + ))) + )); test.addTestCase(/*"g_matR32_3x2",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matR32_3x2\"", new PlcList(List.of( new PlcList(List.of( new PlcREAL(1.0), new PlcREAL(1.5) @@ -110,24 +110,24 @@ public static void main(String[] args) throws Exception { new PlcREAL(3.0), new PlcREAL(3.5) ))) )); -// test.addTestCase(/*"g_cubeU16_2x2x2",*/ "%DB42:466.0:INT[1;1..2][1;1..2][1;1..2]", new PlcList(List.of( -// new PlcList(List.of( -// new PlcList(List.of( -// new PlcINT(1), new PlcINT(2) -// )), -// new PlcList(List.of( -// new PlcINT(3), new PlcINT(4) -// )) -// )), -// new PlcList(List.of( -// new PlcList(List.of( -// new PlcINT(5), new PlcINT(6) -// )), -// new PlcList(List.of( -// new PlcINT(7), new PlcINT(8) -// )) -// ))) -// )); + test.addTestCase(/*"g_cubeU16_2x2x2",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_cubeU16_2x2x2\"", new PlcList(List.of( + new PlcList(List.of( + new PlcList(List.of( + new PlcUINT(1), new PlcUINT(2) + )), + new PlcList(List.of( + new PlcUINT(3), new PlcUINT(4) + )) + )), + new PlcList(List.of( + new PlcList(List.of( + new PlcUINT(5), new PlcUINT(6) + )), + new PlcList(List.of( + new PlcUINT(7), new PlcUINT(8) + )) + ))) + )); } long start = System.currentTimeMillis(); From fb73288afa4ceaa1992ed7f14aa9004878549097 Mon Sep 17 00:00:00 2001 From: Christofer Dutz Date: Fri, 13 Feb 2026 15:18:12 +0100 Subject: [PATCH 3/8] chore: Added the option to provide an optional type, telling the driver how to decode the temporal values. --- .../opcua/protocol/OpcuaProtocolLogic.java | 59 +++++++++++++++++++ .../opcua/ManualS71500NewFWDriverTest.java | 16 ++--- 2 files changed, 67 insertions(+), 8 deletions(-) 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 5a459a42166..9d50a2f922f 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 @@ -64,8 +64,10 @@ import java.math.BigInteger; import java.time.Duration; +import java.time.LocalDate; import java.time.Instant; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZoneOffset; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -87,6 +89,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase implements H new ExtensionObjectEncodingMask(false, false, false)); private static final long EPOCH_OFFSET = 116444736000000000L; //Offset between OPC UA epoch time and linux epoch time. + // IEC 61131-3 date types use 1990-01-01 as epoch, PlcDATE etc. use Unix epoch (1970-01-01). + private static final long IEC_DATE_EPOCH_OFFSET_DAYS = LocalDate.of(1990, 1, 1).toEpochDay(); private final Map subscriptions = new ConcurrentHashMap<>(); private final RequestTransactionManager tm = new RequestTransactionManager(); @@ -472,9 +476,64 @@ static PlcValue variantToPlcValue(PlcTag tag, Variant variant) { } value = structurePlcValues(values, variant); } + + // If the tag declares a specific type (via suffix like :TIME, :DATE, etc.), + // re-interpret the raw numeric value as the correct IEC 61131-3 type. + if (value != null && tag != null) { + PlcValueType targetType = tag.getPlcValueType(); + if (targetType != PlcValueType.NULL) { + value = applyTypeOverride(value, targetType); + } + } + return value; } + /** + * 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; + List converted = new ArrayList<>(list.getLength()); + for (PlcValue item : list.getList()) { + converted.add(applyTypeOverride(item, targetType)); + } + return new PlcList(converted); + } + long raw = value.getLong(); + switch (targetType) { + case TIME: + return new PlcTIME(raw); + case LTIME: + return 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: + // PlcLDATE expects seconds since 1970-01-01 + return 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: + // 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: + // 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: + // 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; + } + } + /** * Structures a flat list of PlcValues according to the variant's dimensionality: * - Single value: returns the scalar PlcValue directly 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/ManualS71500NewFWDriverTest.java index d49a884ecdf..3060f664dc0 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/ManualS71500NewFWDriverTest.java @@ -52,10 +52,10 @@ public static void main(String[] args) throws Exception { 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\"", new PlcTIME(2500)); // TODO: Is returned as Int32 -// test.addTestCase(/*"g_dat",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_dat\"", new PlcDATE(LocalDate.of(2025, 11, 12))); // TODO: Is returned as UInt16 -// test.addTestCase(/*"g_timoday",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_timoday\"", new PlcTIME_OF_DAY(LocalTime.of(14, 33, 21, 250000000))); // TODO: Is returned as UInt32 -// test.addTestCase(/*"g_dattim",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_dattim\"", 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_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")); if(testArrays) { @@ -82,16 +82,16 @@ public static void main(String[] args) throws Exception { test.addTestCase(/*"g_arrLReal",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrLReal\"", new PlcList(List.of( new PlcLREAL(1.5), new PlcLREAL(-2.0), new PlcLREAL(0.125)) )); -// test.addTestCase(/*"g_arrTime",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrTime\"", new PlcList(List.of( -// new PlcTIME(Duration.ofMillis(10)), new PlcTIME(Duration.ofSeconds(1)), new PlcTIME(Duration.ofSeconds(10))) -// )); + test.addTestCase(/*"g_arrTime",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrTime\";TIME", new PlcList(List.of( + new PlcTIME(Duration.ofMillis(10)), new PlcTIME(Duration.ofSeconds(1)), new PlcTIME(Duration.ofSeconds(10))) + )); test.addTestCase(/*"g_arrString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrString\"", new PlcList(List.of( new PlcSTRING("alpha"), new PlcSTRING("beta"), new PlcSTRING("gamma")) )); test.addTestCase(/*"g_arrWString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrWString\"", new PlcList(List.of( new PlcWSTRING("Äpfel"), new PlcWSTRING("Öl")) )); - test.addTestCase(/*"g_matI16_2x3",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matI16_2x3\"", new PlcList(List.of( // TODO: Getting a class cast error, because OpcuaMessageResponse cannot be cast to OpcuaAPU + test.addTestCase(/*"g_matI16_2x3",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matI16_2x3\"", new PlcList(List.of( new PlcList(List.of( new PlcINT(10), new PlcINT(11), new PlcINT(12) )), From 3212e68bd29ca5cf77087096ee0bf83f42c3e90a Mon Sep 17 00:00:00 2001 From: Christofer Dutz Date: Fri, 13 Feb 2026 15:21:33 +0100 Subject: [PATCH 4/8] chore: Reformatting. --- .../opcua/ManualS71500NewFWDriverTest.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) 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/ManualS71500NewFWDriverTest.java index 3060f664dc0..66295cb58b7 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/ManualS71500NewFWDriverTest.java @@ -37,27 +37,27 @@ public ManualS71500NewFWDriverTest(String connectionString) { 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")); + 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")); 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), From 7719c55e6f2c10570c8f98cd6b02a7195d863ade Mon Sep 17 00:00:00 2001 From: Christofer Dutz Date: Tue, 17 Feb 2026 18:20:37 +0100 Subject: [PATCH 5/8] chore: Switched to the javac compiler. --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index b286bf0d5fa..e425bc4952e 100644 --- a/pom.xml +++ b/pom.xml @@ -1036,11 +1036,11 @@ Use the eclipse compiler instead of the normal jdk one, as we were seeing problems on the CI server when compiling the BACnet java driver. --> - eclipse + ${java.version} - + - + From 58eca3c2f06a969deff5e8bbf676bc5303b5b6e2 Mon Sep 17 00:00:00 2001 From: Christofer Dutz Date: Tue, 17 Feb 2026 18:20:55 +0100 Subject: [PATCH 6/8] chore: Updated KNX manufacturer data --- .../bacnetip/readwrite/BACnetVendorId.java | 19 ++++++++-- .../knxnetip/readwrite/KnxManufacturer.java | 36 +++++++++++++++---- .../protocols/bacnetip/bacnet-vendorids.mspec | 17 +++++++-- .../protocols/knxnetip/knx-master-data.mspec | 31 ++++++++++++---- 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/plc4j/drivers/bacnet/src/main/generated/org/apache/plc4x/java/bacnetip/readwrite/BACnetVendorId.java b/plc4j/drivers/bacnet/src/main/generated/org/apache/plc4x/java/bacnetip/readwrite/BACnetVendorId.java index 5e5951da3a6..d0b436453f9 100644 --- a/plc4j/drivers/bacnet/src/main/generated/org/apache/plc4x/java/bacnetip/readwrite/BACnetVendorId.java +++ b/plc4j/drivers/bacnet/src/main/generated/org/apache/plc4x/java/bacnetip/readwrite/BACnetVendorId.java @@ -101,7 +101,7 @@ public enum BACnetVendorId { REGULATOR_AUSTRALIAPTY_LTD((int) 67, (int) 67, (String) "Regulator Australia PTY Ltd."), TOUCH_PLATE_LIGHTING_CONTROLS((int) 68, (int) 68, (String) "Touch-Plate Lighting Controls"), AMANN_GMBH((int) 69, (int) 69, (String) "Amann GmbH"), - RLE_TECHNOLOGIES((int) 70, (int) 70, (String) "RLE Technologies"), + PARAMETERLLC((int) 70, (int) 70, (String) "Parameter LLC"), CARDKEY_SYSTEMS((int) 71, (int) 71, (String) "Cardkey Systems"), SECOM_CO_LTD((int) 72, (int) 72, (String) "SECOM Co., Ltd."), ABB_GEBUDETECHNIKAG_BEREICH_NET_SERV( @@ -404,7 +404,7 @@ public enum BACnetVendorId { CEMTREX_INC((int) 326, (int) 326, (String) "Cemtrex, Inc."), PERFORMANCE_TECHNOLOGIES_INC((int) 327, (int) 327, (String) "Performance Technologies, Inc."), XTRALIS_AUST_PTY_LTD((int) 328, (int) 328, (String) "Xtralis (Aust) Pty Ltd"), - TROX_GMBH((int) 329, (int) 329, (String) "TROX GmbH"), + TROXSE((int) 329, (int) 329, (String) "TROX SE"), BEIJING_HYSINE_TECHNOLOGY_CO_LTD( (int) 330, (int) 330, (String) "Beijing Hysine Technology Co., Ltd"), RCK_CONTROLS_INC((int) 331, (int) 331, (String) "RCK Controls, Inc."), @@ -1814,6 +1814,21 @@ public enum BACnetVendorId { SYSTEM_MART_LTD((int) 1579, (int) 1579, (String) "System Mart Ltd."), SPOT_GMBH((int) 1580, (int) 1580, (String) "Spot7 GmbH"), INTEMPCO_CONTROLS_LTD((int) 1581, (int) 1581, (String) "Intempco Controls Ltd."), + ROSEMONT_ENGINEERING_INC((int) 1582, (int) 1582, (String) "Rosemont Engineering, Inc."), + MINIVEIL_INDUSTRIES_LTD((int) 1583, (int) 1583, (String) "Miniveil Industries Ltd."), + PROFCONAB((int) 1584, (int) 1584, (String) "Profcon AB"), + KE_AUTOMASJONAS((int) 1585, (int) 1585, (String) "KE Automasjon AS"), + NORDOMATIC_PROPERTY_TECHNOLOGIESAB( + (int) 1586, (int) 1586, (String) "Nordomatic Property Technologies AB"), + RUNWISE((int) 1587, (int) 1587, (String) "Runwise"), + KARELAVOJSKY((int) 1588, (int) 1588, (String) "Karel Čavojsky"), + ABBLV_INSTALLATION_MATERIALS_CO_LTD_BEIJING( + (int) 1589, (int) 1589, (String) "ABB LV Installation Materials Co., Ltd. Beijing"), + ENER_SEARCH_SOLAR_GMBH((int) 1590, (int) 1590, (String) "EnerSearch Solar GmbH"), + ZURN_ELKAY_WATER_SOLUTIONS((int) 1591, (int) 1591, (String) "Zurn Elkay Water Solutions"), + ANDIVI((int) 1592, (int) 1592, (String) "Andivi"), + CHYI_LEE_INDUSTRY_CO_LTD((int) 1593, (int) 1593, (String) "Chyi Lee Industry Co., Ltd."), + ALLBEEBV((int) 1594, (int) 1594, (String) "Allbee B.V."), UNKNOWN_VENDOR((int) 0xFFFF, (int) 0xFFFF, (String) "Unknown"); private static final Map map; diff --git a/plc4j/drivers/knxnetip/src/main/generated/org/apache/plc4x/java/knxnetip/readwrite/KnxManufacturer.java b/plc4j/drivers/knxnetip/src/main/generated/org/apache/plc4x/java/knxnetip/readwrite/KnxManufacturer.java index 631f3daf57c..f08ae50fd33 100644 --- a/plc4j/drivers/knxnetip/src/main/generated/org/apache/plc4x/java/knxnetip/readwrite/KnxManufacturer.java +++ b/plc4j/drivers/knxnetip/src/main/generated/org/apache/plc4x/java/knxnetip/readwrite/KnxManufacturer.java @@ -28,11 +28,11 @@ public enum KnxManufacturer { M_UNKNOWN((int) 0, (int) 0, (String) "Unknown Manufacturer"), M_SIEMENS((int) 1, (int) 1, (String) "Siemens"), - M_ABB_AG_STOTZ___KONTAKT((int) 2, (int) 2, (String) "ABB AG (Stotz - Kontakt)"), + M_ABB_AG___STOTZ_KONTAKT((int) 2, (int) 2, (String) "ABB AG - STOTZ-KONTAKT"), M_ALBRECHT_JUNG((int) 3, (int) 4, (String) "Albrecht Jung"), M_BTICINO((int) 4, (int) 5, (String) "Bticino"), M_BERKER((int) 5, (int) 6, (String) "Berker"), - M_ABB_AG_BUSCH___JAEGER((int) 6, (int) 7, (String) "ABB AG (Busch - Jaeger)"), + M_ABB_AG___BUSCH_JAEGER((int) 6, (int) 7, (String) "ABB AG - BUSCH-JAEGER"), M_GIRA_GIERSIEPEN((int) 7, (int) 8, (String) "GIRA Giersiepen"), M_HAGER_ELECTRO((int) 8, (int) 9, (String) "Hager Electro"), M_INSTA_GMBH((int) 9, (int) 10, (String) "Insta GmbH"), @@ -364,7 +364,7 @@ public enum KnxManufacturer { M_EVN_LICHTTECHNIK((int) 318, (int) 369, (String) "EVN-Lichttechnik"), M_RUTEC((int) 319, (int) 370, (String) "rutec"), M_FINDER((int) 320, (int) 371, (String) "Finder"), - M_FUJITSU_GENERAL_LIMITED((int) 321, (int) 372, (String) "Fujitsu General Limited"), + M_GENERAL_INC_((int) 321, (int) 372, (String) "GENERAL Inc."), M_ZF_FRIEDRICHSHAFEN_AG((int) 322, (int) 373, (String) "ZF Friedrichshafen AG"), M_CREALED((int) 323, (int) 374, (String) "Crealed"), M_MILES_MAGIC_AUTOMATION_PRIVATE_LIMITED( @@ -677,7 +677,7 @@ public enum KnxManufacturer { (int) 587, (int) 643, (String) "Hefei Chuang Yue Intelligent Technology Co.,LTD"), M_LARFE((int) 588, (int) 644, (String) "Larfe"), M_DONGGUAN_MUHCCI_ELECTRICAL((int) 589, (int) 645, (String) "Dongguan Muhcci Electrical"), - M_STEC((int) 590, (int) 646, (String) "STEC"), + M_SDEKE((int) 590, (int) 646, (String) "Sdeke"), M_ARIGO_SOFTWARE_GMBH((int) 591, (int) 647, (String) "ARIGO Software GmbH"), M_FEISHELEC((int) 592, (int) 648, (String) "Feishelec"), M_GORDIC((int) 593, (int) 649, (String) "GORDIC"), @@ -765,7 +765,7 @@ public enum KnxManufacturer { M_FOSHAN_HAYDN_TECHNOLOGY_LIGHTING_CO__LTD_( (int) 662, (int) 720, (String) "Foshan Haydn Technology Lighting Co.,Ltd."), M_KNXES_COMPANY_LIMITED((int) 663, (int) 721, (String) "KNXES COMPANY LIMITED"), - M_AKUVOX((int) 664, (int) 722, (String) "Akuvox"), + M_AKUBELA((int) 664, (int) 722, (String) "akubela"), M_NINGBO_YINZHOU_SHENGRUIJIE_ELECTRONICS_CO__LTD_( (int) 665, (int) 723, (String) "NingBo Yinzhou ShengRuiJie Electronics Co. Ltd."), M_SHENZHEN_HAIZHICHUANG_TECHNOLOGY_CO___LTD( @@ -904,9 +904,31 @@ public enum KnxManufacturer { M_OSPA_APPARATEBAU_PAUSER_GMBH_AND_CO__KG( (int) 766, (int) 827, (String) "Ospa Apparatebau Pauser GmbH & Co. KG"), M_PLH((int) 767, (int) 828, (String) "PLH"), - M_ABB___RESERVED((int) 768, (int) 43954, (String) "ABB - reserved"), + M_HANGZHOU_ZION_COMMUNICATION_CO__LTD( + (int) 768, (int) 829, (String) "HANGZHOU ZION COMMUNICATION CO.,LTD"), + M_WEIYI_TECHNOLOGY_ZHEJIANG_CO___LTD( + (int) 769, (int) 830, (String) "Weiyi Technology (Zhejiang) Co., Ltd"), + M_AUTERAS((int) 770, (int) 831, (String) "AUTERAS"), + M_OSUM_SMART((int) 771, (int) 832, (String) "OSUM Smart"), + M_TEKNOLINE((int) 772, (int) 833, (String) "TEKNOLINE"), + M_HIMEL_HONG_KONG_LIMITED((int) 773, (int) 834, (String) "HIMEL HONG KONG LIMITED"), + M_MICRONIC_KOREA_CO___LTD_((int) 774, (int) 835, (String) "Micronic Korea Co., Ltd."), + M_ROBUSTEL_LIMITED((int) 775, (int) 836, (String) "Robustel Limited"), + M_LEXA_LIFESTYLE((int) 776, (int) 837, (String) "Lexa LIFESTYLE"), + M_CASAMBI((int) 777, (int) 838, (String) "casambi"), + M_BELCAB_UK_LTD((int) 778, (int) 839, (String) "BELCAB UK LTD"), + M_CHANNEL_SMART_BUILDINGS((int) 779, (int) 840, (String) "Channel Smart Buildings"), + M_NANJING_KLEVEN_ELECTRONIC_TECHNOLOGY_CO___LTD( + (int) 780, (int) 841, (String) "Nanjing Kleven Electronic Technology Co., Ltd"), + M_ZHEJIANG_CHATNOW_NEW_MATERIAL_TECHNOLOGY_CO___LTD( + (int) 781, (int) 842, (String) "Zhejiang Chatnow New Material Technology Co., Ltd"), + M_OCCHIO_GMBH((int) 782, (int) 843, (String) "Occhio GmbH"), + M_INSTELL((int) 783, (int) 844, (String) "Instell"), + M_VOKA_VOGTLAENDISCHES_KABELWERK_GMBH( + (int) 784, (int) 845, (String) "VOKA Vogtländisches Kabelwerk GmbH"), + M_ABB___RESERVED((int) 785, (int) 43954, (String) "ABB - reserved"), M_BUSCH_JAEGER_ELEKTRO___RESERVED( - (int) 769, (int) 43959, (String) "Busch-Jaeger Elektro - reserved"); + (int) 786, (int) 43959, (String) "Busch-Jaeger Elektro - reserved"); private static final Map map; static { diff --git a/protocols/bacnetip/src/main/generated/protocols/bacnetip/bacnet-vendorids.mspec b/protocols/bacnetip/src/main/generated/protocols/bacnetip/bacnet-vendorids.mspec index 7d3975cfb08..bcf9e332918 100644 --- a/protocols/bacnetip/src/main/generated/protocols/bacnetip/bacnet-vendorids.mspec +++ b/protocols/bacnetip/src/main/generated/protocols/bacnetip/bacnet-vendorids.mspec @@ -91,7 +91,7 @@ ['67' REGULATOR_AUSTRALIAPTY_LTD ['67', '"Regulator Australia PTY Ltd."']] ['68' TOUCH_PLATE_LIGHTING_CONTROLS ['68', '"Touch-Plate Lighting Controls"']] ['69' AMANN_GMBH ['69', '"Amann GmbH"']] - ['70' RLE_TECHNOLOGIES ['70', '"RLE Technologies"']] + ['70' PARAMETERLLC ['70', '"Parameter LLC"']] ['71' CARDKEY_SYSTEMS ['71', '"Cardkey Systems"']] ['72' SECOM_CO_LTD ['72', '"SECOM Co., Ltd."']] ['73' ABB_GEBUDETECHNIKAG_BEREICH_NET_SERV ['73', '"ABB Gebäudetechnik AG Bereich NetServ"']] @@ -350,7 +350,7 @@ ['326' CEMTREX_INC ['326', '"Cemtrex, Inc."']] ['327' PERFORMANCE_TECHNOLOGIES_INC ['327', '"Performance Technologies, Inc."']] ['328' XTRALIS_AUST_PTY_LTD ['328', '"Xtralis (Aust) Pty Ltd"']] - ['329' TROX_GMBH ['329', '"TROX GmbH"']] + ['329' TROXSE ['329', '"TROX SE"']] ['330' BEIJING_HYSINE_TECHNOLOGY_CO_LTD ['330', '"Beijing Hysine Technology Co., Ltd"']] ['331' RCK_CONTROLS_INC ['331', '"RCK Controls, Inc."']] ['332' DISTECH_CONTROLSSAS ['332', '"Distech Controls SAS"']] @@ -1593,6 +1593,19 @@ ['1579' SYSTEM_MART_LTD ['1579', '"System Mart Ltd."']] ['1580' SPOT_GMBH ['1580', '"Spot7 GmbH"']] ['1581' INTEMPCO_CONTROLS_LTD ['1581', '"Intempco Controls Ltd."']] + ['1582' ROSEMONT_ENGINEERING_INC ['1582', '"Rosemont Engineering, Inc."']] + ['1583' MINIVEIL_INDUSTRIES_LTD ['1583', '"Miniveil Industries Ltd."']] + ['1584' PROFCONAB ['1584', '"Profcon AB"']] + ['1585' KE_AUTOMASJONAS ['1585', '"KE Automasjon AS"']] + ['1586' NORDOMATIC_PROPERTY_TECHNOLOGIESAB ['1586', '"Nordomatic Property Technologies AB"']] + ['1587' RUNWISE ['1587', '"Runwise"']] + ['1588' KARELAVOJSKY ['1588', '"Karel Čavojsky"']] + ['1589' ABBLV_INSTALLATION_MATERIALS_CO_LTD_BEIJING ['1589', '"ABB LV Installation Materials Co., Ltd. Beijing"']] + ['1590' ENER_SEARCH_SOLAR_GMBH ['1590', '"EnerSearch Solar GmbH"']] + ['1591' ZURN_ELKAY_WATER_SOLUTIONS ['1591', '"Zurn Elkay Water Solutions"']] + ['1592' ANDIVI ['1592', '"Andivi"']] + ['1593' CHYI_LEE_INDUSTRY_CO_LTD ['1593', '"Chyi Lee Industry Co., Ltd."']] + ['1594' ALLBEEBV ['1594', '"Allbee B.V."']] ['0xFFFF' UNKNOWN_VENDOR ['0xFFFF', '"Unknown"']] ] diff --git a/protocols/knxnetip/src/main/generated/protocols/knxnetip/knx-master-data.mspec b/protocols/knxnetip/src/main/generated/protocols/knxnetip/knx-master-data.mspec index 642aa45c4f2..4962ea9cfe1 100644 --- a/protocols/knxnetip/src/main/generated/protocols/knxnetip/knx-master-data.mspec +++ b/protocols/knxnetip/src/main/generated/protocols/knxnetip/knx-master-data.mspec @@ -754,11 +754,11 @@ [enum uint 16 KnxManufacturer(uint 16 number, string 8 name) ['0' M_UNKNOWN ['0', '"Unknown Manufacturer"']] ['1' M_SIEMENS ['1', '"Siemens"']] - ['2' M_ABB_AG_STOTZ___KONTAKT ['2', '"ABB AG (Stotz - Kontakt)"']] + ['2' M_ABB_AG___STOTZ_KONTAKT ['2', '"ABB AG - STOTZ-KONTAKT"']] ['3' M_ALBRECHT_JUNG ['4', '"Albrecht Jung"']] ['4' M_BTICINO ['5', '"Bticino"']] ['5' M_BERKER ['6', '"Berker"']] - ['6' M_ABB_AG_BUSCH___JAEGER ['7', '"ABB AG (Busch - Jaeger)"']] + ['6' M_ABB_AG___BUSCH_JAEGER ['7', '"ABB AG - BUSCH-JAEGER"']] ['7' M_GIRA_GIERSIEPEN ['8', '"GIRA Giersiepen"']] ['8' M_HAGER_ELECTRO ['9', '"Hager Electro"']] ['9' M_INSTA_GMBH ['10', '"Insta GmbH"']] @@ -1073,7 +1073,7 @@ ['318' M_EVN_LICHTTECHNIK ['369', '"EVN-Lichttechnik"']] ['319' M_RUTEC ['370', '"rutec"']] ['320' M_FINDER ['371', '"Finder"']] - ['321' M_FUJITSU_GENERAL_LIMITED ['372', '"Fujitsu General Limited"']] + ['321' M_GENERAL_INC_ ['372', '"GENERAL Inc."']] ['322' M_ZF_FRIEDRICHSHAFEN_AG ['373', '"ZF Friedrichshafen AG"']] ['323' M_CREALED ['374', '"Crealed"']] ['324' M_MILES_MAGIC_AUTOMATION_PRIVATE_LIMITED ['375', '"Miles Magic Automation Private Limited"']] @@ -1342,7 +1342,7 @@ ['587' M_HEFEI_CHUANG_YUE_INTELLIGENT_TECHNOLOGY_CO__LTD ['643', '"Hefei Chuang Yue Intelligent Technology Co.,LTD"']] ['588' M_LARFE ['644', '"Larfe"']] ['589' M_DONGGUAN_MUHCCI_ELECTRICAL ['645', '"Dongguan Muhcci Electrical"']] - ['590' M_STEC ['646', '"STEC"']] + ['590' M_SDEKE ['646', '"Sdeke"']] ['591' M_ARIGO_SOFTWARE_GMBH ['647', '"ARIGO Software GmbH"']] ['592' M_FEISHELEC ['648', '"Feishelec"']] ['593' M_GORDIC ['649', '"GORDIC"']] @@ -1416,7 +1416,7 @@ ['661' M_ARKLYFE ['719', '"Arklyfe"']] ['662' M_FOSHAN_HAYDN_TECHNOLOGY_LIGHTING_CO__LTD_ ['720', '"Foshan Haydn Technology Lighting Co.,Ltd."']] ['663' M_KNXES_COMPANY_LIMITED ['721', '"KNXES COMPANY LIMITED"']] - ['664' M_AKUVOX ['722', '"Akuvox"']] + ['664' M_AKUBELA ['722', '"akubela"']] ['665' M_NINGBO_YINZHOU_SHENGRUIJIE_ELECTRONICS_CO__LTD_ ['723', '"NingBo Yinzhou ShengRuiJie Electronics Co. Ltd."']] ['666' M_SHENZHEN_HAIZHICHUANG_TECHNOLOGY_CO___LTD ['724', '"Shenzhen Haizhichuang Technology Co., Ltd"']] ['667' M_SHENZHEN_EASYDETEK_TECHNOLOGY_CO_LTD ['725', '"Shenzhen easydetek technology Co.Ltd"']] @@ -1520,8 +1520,25 @@ ['765' M_SPECIALCAVI_BALDASSARI_SRL ['826', '"SPECIALCAVI BALDASSARI SRL"']] ['766' M_OSPA_APPARATEBAU_PAUSER_GMBH_AND_CO__KG ['827', '"Ospa Apparatebau Pauser GmbH & Co. KG"']] ['767' M_PLH ['828', '"PLH"']] - ['768' M_ABB___RESERVED ['43954', '"ABB - reserved"']] - ['769' M_BUSCH_JAEGER_ELEKTRO___RESERVED ['43959', '"Busch-Jaeger Elektro - reserved"']] + ['768' M_HANGZHOU_ZION_COMMUNICATION_CO__LTD ['829', '"HANGZHOU ZION COMMUNICATION CO.,LTD"']] + ['769' M_WEIYI_TECHNOLOGY_ZHEJIANG_CO___LTD ['830', '"Weiyi Technology (Zhejiang) Co., Ltd"']] + ['770' M_AUTERAS ['831', '"AUTERAS"']] + ['771' M_OSUM_SMART ['832', '"OSUM Smart"']] + ['772' M_TEKNOLINE ['833', '"TEKNOLINE"']] + ['773' M_HIMEL_HONG_KONG_LIMITED ['834', '"HIMEL HONG KONG LIMITED"']] + ['774' M_MICRONIC_KOREA_CO___LTD_ ['835', '"Micronic Korea Co., Ltd."']] + ['775' M_ROBUSTEL_LIMITED ['836', '"Robustel Limited"']] + ['776' M_LEXA_LIFESTYLE ['837', '"Lexa LIFESTYLE"']] + ['777' M_CASAMBI ['838', '"casambi"']] + ['778' M_BELCAB_UK_LTD ['839', '"BELCAB UK LTD"']] + ['779' M_CHANNEL_SMART_BUILDINGS ['840', '"Channel Smart Buildings"']] + ['780' M_NANJING_KLEVEN_ELECTRONIC_TECHNOLOGY_CO___LTD ['841', '"Nanjing Kleven Electronic Technology Co., Ltd"']] + ['781' M_ZHEJIANG_CHATNOW_NEW_MATERIAL_TECHNOLOGY_CO___LTD ['842', '"Zhejiang Chatnow New Material Technology Co., Ltd"']] + ['782' M_OCCHIO_GMBH ['843', '"Occhio GmbH"']] + ['783' M_INSTELL ['844', '"Instell"']] + ['784' M_VOKA_VOGTLAENDISCHES_KABELWERK_GMBH ['845', '"VOKA Vogtländisches Kabelwerk GmbH"']] + ['785' M_ABB___RESERVED ['43954', '"ABB - reserved"']] + ['786' M_BUSCH_JAEGER_ELEKTRO___RESERVED ['43959', '"Busch-Jaeger Elektro - reserved"']] ] From f80dc154cbe912798bc3c86c8a807d932ac07bee Mon Sep 17 00:00:00 2001 From: Christofer Dutz Date: Tue, 17 Feb 2026 22:54:51 +0100 Subject: [PATCH 7/8] fix: Worked on getting all various types of OPC-UA request working with my Siemens S7 OPC-UA Server. --- .../BinaryExtensionObjectWithMask.java | 23 +++-- .../java/opcua/context/Conversation.java | 20 ++-- .../opcua/protocol/OpcuaProtocolLogic.java | 92 +++++++++++++----- .../readwrite/UnknownExtensionObject.java | 93 +++++++++++++++++++ .../opcua/readwrite/utils/StaticHelper.java | 36 ++++++- ... => ManualOpcUaS71500NewFWDriverTest.java} | 53 +++++------ .../java/opcua/OpcuaParserSerializerTest.java | 2 +- .../protocol/OpcuaSubscriptionHandleTest.java | 5 +- .../opcua/src/test/resources/logback-test.xml | 2 +- .../plc4x/java/spi/Plc4xNettyWrapper.java | 4 +- .../spi/values/LegacyPlcValueHandler.java | 4 + .../protocols/opcua/opc-manual.mspec | 2 +- protocols/opcua/src/main/xslt/opc-manual.xsl | 2 +- 13 files changed, 258 insertions(+), 80 deletions(-) create mode 100644 plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/readwrite/UnknownExtensionObject.java rename plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/{ManualS71500NewFWDriverTest.java => ManualOpcUaS71500NewFWDriverTest.java} (69%) 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 c1db6b21db4..82fe0a7be8c 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 @@ protected void serializeExtensionObjectWithMaskChild(WriteBuffer writeBuffer) 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 int getLengthInBits() { // 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 static ExtensionObjectWithMaskBuilder staticParseExtensionObjectWithMaskB 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 a9c9c2b176c..f9804a57cae 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.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 @@ private CompletableFuture reques .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 submit(ExtensionObjectDefinition requestDefinition) { - Integer requestId = tm.getTransactionIdentifier(); + int requestId = tm.getTransactionIdentifier(); ExpandedNodeId expandedNodeId = new ExpandedNodeId( false, //Namespace Uri Specified @@ -303,7 +300,7 @@ private CompletableFuture submit(ExtensionObjectDefinition requestDefini .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 @@ private CompletableFuture submit(ExtensionObjectDefinition requestDefini 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 @@ private SendRequestContext sendRequest(MessagePDU messagePDU, Completa 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 mergeChunks(ChunkStorage chunkStorage, T source, SequenceHeader sequenceHeader, BiFunction producer) { @@ -419,7 +415,7 @@ static SecurityPolicy determineSecurityPolicy(OpcuaConfiguration configuration) } 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() : ""; 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 9d50a2f922f..1beed98139f 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 @@ static PlcValue variantToPlcValue(PlcTag tag, Variant variant) { List objects = ((VariantExtensionObject) variant).getValue(); List 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 @@ static PlcValue variantToPlcValue(PlcTag tag, Variant variant) { 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 converted = new ArrayList<>(list.getLength()); for (PlcValue item : list.getList()) { converted.add(applyTypeOverride(item, targetType)); @@ -504,34 +542,38 @@ private static PlcValue applyTypeOverride(PlcValue value, PlcValueType targetTyp 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 00000000000..b358a7a60ec --- /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 7ae036ec39f..3f67ecffb47 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 static int extensionId(ExpandedNodeId expandedNodeId) { 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 66295cb58b7..ee085fac176 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.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 8fefde7336a..4953b35091d 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 @@ 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 1339394a725..2e72af3e779 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 @@ private static Stream getTags() { 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 719321b8e43..3e3748c42bf 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 @@ - + 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 4c20b1390d7..33f188dbec0 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 @@ protected void decode(ChannelHandlerContext channelHandlerContext, T payload, Li 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 0343bc4434d..8666442a7cc 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 static PlcValue of(PlcTag tag, Object value) { 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 b2cb1610b72..bf6a048950a 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 f5ea6879df2..9bd6300a6c0 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'] From 778a5b710e6d7f4d5b5b4cc057b6811cdc7869e9 Mon Sep 17 00:00:00 2001 From: Christofer Dutz Date: Fri, 20 Feb 2026 12:13:24 +0100 Subject: [PATCH 8/8] chore: Removed no longer needed if statement as the root cause was elsewhere. --- .../org/apache/plc4x/java/opcua/context/Conversation.java | 8 +------- .../plc4x/java/opcua/OpcuaParserSerializerTest.java | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) 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 f9804a57cae..981ed439617 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 @@ -333,13 +333,7 @@ private CompletableFuture submit(ExtensionObjectDefinition requestDefini } 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. - future.complete(extensionObjectBody); - } else { - future.completeExceptionally(toProtocolException(fault)); - } + future.completeExceptionally(toProtocolException(fault)); } else { future.complete(extensionObjectBody); } 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 4953b35091d..76daae52cf0 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 @@ public class OpcuaParserSerializerTest extends ParserSerializerTestsuiteRunner { public OpcuaParserSerializerTest() { - super("/protocols/opcua/ParserSerializerTestsuite.xml", true); + super("/protocols/opcua/ParserSerializerTestsuite.xml", false); } }