You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by hu...@apache.org on 2021/01/05 11:33:24 UTC

[plc4x] branch feature/native_opua_client updated: Add read logic for DINT datatype.

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

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


The following commit(s) were added to refs/heads/feature/native_opua_client by this push:
     new fd85e5d  Add read logic for DINT datatype.
fd85e5d is described below

commit fd85e5ddb8b44b94e35cb121266b9e3d39425e7e
Author: hutcheb <be...@gmail.com>
AuthorDate: Tue Jan 5 06:32:09 2021 -0500

    Add read logic for DINT datatype.
    
    Next I'll generalize it to other data types.
---
 .../java/opcua/protocol/OpcuaProtocolLogic.java    | 134 +++++++-
 protocols/opcua/src/main/xslt/opc-types.xsl        | 344 +++++++--------------
 2 files changed, 244 insertions(+), 234 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 1e5e233..6e7aba7 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
@@ -59,6 +59,7 @@ import org.apache.plc4x.java.spi.messages.DefaultPlcWriteRequest;
 import org.apache.plc4x.java.spi.messages.DefaultPlcWriteResponse;
 import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
 import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
+import org.apache.plc4x.java.spi.values.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -112,6 +113,9 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
     private String endpoint;
     private AtomicInteger transactionIdentifierGenerator = new AtomicInteger(1);
+    private AtomicInteger requestHandleGenerator = new AtomicInteger(1);
+    private AtomicInteger tokenId = new AtomicInteger(1);
+    private AtomicInteger channelId = new AtomicInteger(1);
 
     @Override
     public void setConfiguration(OpcuaConfiguration configuration) {
@@ -216,8 +220,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
     public void onConnectCreateSessionRequest(ConversationContext<OpcuaAPU> context, OpcuaOpenResponse opcuaOpenResponse) throws PlcConnectionException {
         OpenSecureChannelResponse openSecureChannelResponse = (OpenSecureChannelResponse) opcuaOpenResponse.getMessage();
-        Integer tokenId = (int) openSecureChannelResponse.getSecurityToken().getTokenId();
-        Integer channelId = (int) openSecureChannelResponse.getSecurityToken().getChannelId();
+        tokenId.set((int) openSecureChannelResponse.getSecurityToken().getTokenId());
+        channelId.set((int) openSecureChannelResponse.getSecurityToken().getChannelId());
 
         int transactionId = transactionIdentifierGenerator.getAndIncrement();
         if(transactionIdentifierGenerator.get() == 0xFFFF) {
@@ -275,8 +279,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             0L);
 
         OpcuaMessageRequest messageRequest = new OpcuaMessageRequest(CHUNK,
-            channelId,
-            tokenId,
+            channelId.get(),
+            tokenId.get(),
             nextSequenceNumber,
             nextRequestId,
             createSessionRequest);
@@ -300,8 +304,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         CreateSessionResponse createSessionResponse = (CreateSessionResponse) opcuaMessageResponse.getMessage();
 
         authenticationToken = (NodeIdByteString) createSessionResponse.getAuthenticationToken();
-        Integer tokenId = (int) opcuaMessageResponse.getSecureTokenId();
-        Integer channelId = (int) opcuaMessageResponse.getSecureChannelId();
+        tokenId.set((int) opcuaMessageResponse.getSecureTokenId());
+        channelId.set((int) opcuaMessageResponse.getSecureChannelId());
 
         int transactionId = transactionIdentifierGenerator.getAndIncrement();
         if(transactionIdentifierGenerator.get() == 0xFFFF) {
@@ -316,9 +320,14 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             throw new PlcConnectionException("Sequence number isn't as expected, we might have missed a packet. - " +  transactionId + " != " + nextSequenceNumber);
         }
 
+        int requestHandle = requestHandleGenerator.getAndIncrement();
+        if(requestHandleGenerator.get() == 0xFFFF) {
+            requestHandleGenerator.set(1);
+        }
+
         RequestHeader requestHeader = new RequestHeader(authenticationToken,
             getCurrentDateTime(),
-            1L,
+            requestHandle,
             0L,
             NULL_STRING,
             10000L,
@@ -360,8 +369,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             clientSignature);
 
         OpcuaMessageRequest activateMessageRequest = new OpcuaMessageRequest(CHUNK,
-            channelId,
-            tokenId,
+            channelId.get(),
+            tokenId.get(),
             nextSequenceNumber,
             nextRequestId,
             activateSessionRequest);
@@ -373,9 +382,116 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             .handle(opcuaActivateResponse -> {
                 LOGGER.debug("Got Activate Session Response Connection Response");
 
+                ActivateSessionResponse activateMessageResponse = (ActivateSessionResponse) opcuaActivateResponse.getMessage();
+
+                long returnedRequestHandle = activateMessageResponse.getResponseHeader().getRequestHandle();
+                if (!(requestHandle == returnedRequestHandle)) {
+                    LOGGER.error("Request handle isn't as expected, we might have missed a packet. - " +  requestHandle + " != " + returnedRequestHandle);
+                }
+
+                // Send an event that connection setup is complete.
+                context.fireConnected();
             });
     }
 
+    @Override
+    public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
+        LOGGER.info("Reading Value");
+        CompletableFuture<PlcReadResponse> future = new CompletableFuture<>();
+        DefaultPlcReadRequest request = (DefaultPlcReadRequest) readRequest;
+
+        if(request.getFieldNames().size() == 1) {
+            String fieldName = request.getFieldNames().iterator().next();
+            OpcuaField field = (OpcuaField) request.getField(fieldName);
+
+            int requestHandle = requestHandleGenerator.getAndIncrement();
+            // If we've reached the max value for a 16 bit transaction identifier, reset back to 1
+            if(requestHandleGenerator.get() == 0xFFFF) {
+                requestHandleGenerator.set(1);
+            }
+
+            RequestHeader requestHeader = new RequestHeader(authenticationToken,
+                getCurrentDateTime(),
+                requestHandle,
+                0L,
+                NULL_STRING,
+                10000L,
+                NULL_EXTENSION_OBJECT);
+
+            ReadValueId[] readValueArray = new ReadValueId[1];
+
+            NodeIdString nodeId = new NodeIdString(NodeIdType.nodeIdTypeString, new StringNodeId(field.getNamespace(), new PascalString(field.getIdentifier().length(), field.getIdentifier())));
+
+            readValueArray[0] = new ReadValueId(nodeId,
+                0xD,
+                NULL_STRING,
+                new QualifiedName(0, NULL_STRING));
+
+            ReadRequest opcuaReadRequest = new ReadRequest((byte) 1,
+                (byte) 0,
+                requestHeader,
+                0.0d,
+                TimestampsToReturn.timestampsToReturnNeither,
+                readValueArray.length,
+                readValueArray);
+
+            int transactionIdentifier = transactionIdentifierGenerator.getAndIncrement();
+            // If we've reached the max value for a 16 bit transaction identifier, reset back to 1
+            if(transactionIdentifierGenerator.get() == 0xFFFF) {
+                transactionIdentifierGenerator.set(1);
+            }
+
+            OpcuaMessageRequest readMessageRequest = new OpcuaMessageRequest(CHUNK,
+                channelId.get(),
+                tokenId.get(),
+                transactionIdentifier,
+                transactionIdentifier,
+                opcuaReadRequest);
+
+            RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+            transaction.submit(() -> context.sendRequest(new OpcuaAPU(readMessageRequest))
+                .expectResponse(OpcuaAPU.class, REQUEST_TIMEOUT)
+                .onTimeout(future::completeExceptionally)
+                .onError((p, e) -> future.completeExceptionally(e))
+                .check(p -> p.getMessage() instanceof OpcuaMessageResponse)
+                .unwrap(p -> (OpcuaMessageResponse) p.getMessage())
+                .handle(opcuaResponse -> {
+                    // Try to decode the response data based on the corresponding request.
+                    ReadResponse readResponse = (ReadResponse) opcuaResponse.getMessage();
+
+                    //TODO;- Fix this
+                    DataValue[] results = readResponse.getResults();
+                    Integer value = null;
+                    if (results.length > 0) {
+                        Variant variant = results[0].getValue();
+                        LOGGER.info("Repsponse Include Variant of type " + variant.getClass().toString());
+                        if (variant instanceof VariantInt32) {
+                            value = ((VariantInt32) variant).getValue()[0];
+                        }
+                    }
+
+                    PlcValue plcValue = new PlcDINT(value);
+                    // Prepare the response.
+                    PlcReadResponse response = new DefaultPlcReadResponse(request,
+                        Collections.singletonMap(fieldName, new ResponseItem<>(PlcResponseCode.OK, plcValue)));
+
+                    // Pass the response back to the application.
+                    future.complete(response);
+
+                    // Finish the request-transaction.
+                    transaction.endRequest();
+                }));
+        } else {
+            future.completeExceptionally(new PlcRuntimeException("Modbus only supports single filed requests"));
+        }
+        return future;
+    }
+
+    @Override
+    protected void decode(ConversationContext<OpcuaAPU> context, OpcuaAPU msg) throws Exception {
+        super.decode(context, msg);
+    }
+
 
     private long getCurrentDateTime() {
         return (System.currentTimeMillis() * 10000) + epochOffset;
diff --git a/protocols/opcua/src/main/xslt/opc-types.xsl b/protocols/opcua/src/main/xslt/opc-types.xsl
index f3e54cf..4a74ae2 100644
--- a/protocols/opcua/src/main/xslt/opc-types.xsl
+++ b/protocols/opcua/src/main/xslt/opc-types.xsl
@@ -139,237 +139,131 @@
 ]
 
 [type 'DataValue'
-    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='DataValue']"/>
+    //A value with an associated timestamp, and quality..
+    [simple bit 'reserved2']
+    [simple bit 'reserved1']
+    [simple bit 'serverPicosecondsSpecified']
+    [simple bit 'sourcePicosecondsSpecified']
+    [simple bit 'serverTimestampSpecified']
+    [simple bit 'sourceTimestampSpecified']
+    [simple bit 'statusCodeSpecified']
+    [simple bit 'valueSpecified']
+    [optional Variant 'value' 'valueSpecified']
+    [optional StatusCode 'statusCode' 'statusCodeSpecified']
+    [optional int 64 'sourceTimestamp' 'sourceTimestampSpecified']
+    [optional uint 16 'sourcePicoseconds' 'sourcePicosecondsSpecified']
+    [optional int 64 'serverTimestamp' 'serverTimestampSpecified']
+    [optional uint 16 'serverPicoseconds' 'serverPicosecondsSpecified']
 ]
 
 [discriminatedType 'Variant'
+    [discriminator bit 'arrayLengthSpecified']
+    [simple bit 'arrayDimensionsSpecified']
     [discriminator uint 6 'VariantType']
-    [typeSwitch 'VariantType'
-        ['1' VariantBoolean
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array bit 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['2' VariantSByte
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array int 8 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['3' VariantByte
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array uint 8 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['4' VariantInt16
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array int 16 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['5' VariantUInt16
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array uint 16 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['6' VariantInt32
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array int 32 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['7' VariantUInt32
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array uint 32 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['8' VariantInt64
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array int 64 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['9' VariantUInt64
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array uint 64 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['10' VariantFloat
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array float 8.23 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['11' VariantDouble
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array float 11.52 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['12' VariantString
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array PascalString  'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['13' VariantDateTime
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array int 64 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['14' VariantGuid
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array string '-1' 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['15' VariantByteString
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array PascalString 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['16' VariantXmlElement
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array XmlElement 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['17' VariantNodeId
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array NodeId 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['18' VariantExpandedNodeId
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array ExpandedNodeId 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['19' VariantStatusCode
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array StatusCode 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['20' VariantQualifiedName
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array QualifiedName 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['21' VariantLocalizedText
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array LocalizedText 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['22' VariantExtensionObject
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array ExtensionObject 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['23' VariantDataValue
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array DataValue 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['24' VariantVariant
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array Variant 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
-        ]
-        ['25' VariantDiagnosticInfo
-
-            [simple bit 'ArrayDimensionsSpecified']
-            [simple bit 'ArrayLengthSpecified']
-            [simple int 32 'arrayLength']
-            [array DiagnosticInfo 'value' count 'arrayLength']
-            [simple int 32 'noOfArrayDimensions']
-            [array bit 'arrayDimensions' count 'noOfArrayDimensions']
+    [typeSwitch 'VariantType','arrayLengthSpecified'
+        ['1' VariantBoolean [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array bit 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['2' VariantSByte [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array int 8 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['3' VariantByte [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array uint 8 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['4' VariantInt16 [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array int 16 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['5' VariantUInt16 [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array uint 16 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['6' VariantInt32 [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array int 32 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['7' VariantUInt32 [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array uint 32 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['8' VariantInt64 [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array int 64 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['9' VariantUInt64 [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array uint 64 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['10' VariantFloat [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array float 8.23 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['11' VariantDouble [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array float 11.52 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['12' VariantString [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array PascalString 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['13' VariantDateTime [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array int 64 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['14' VariantGuid [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array string '-1' 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['15' VariantByteString [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array PascalString 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['16' VariantXmlElement [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array XmlElement 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['17' VariantNodeId [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array NodeId 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['18' VariantExpandedNodeId [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array ExpandedNodeId 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['19' VariantStatusCode [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array StatusCode 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['20' VariantQualifiedName [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array QualifiedName 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['21' VariantLocalizedText [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array LocalizedText 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['22' VariantExtensionObject [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array ExtensionObject 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['23' VariantDataValue [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array DataValue 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['24' VariantVariant [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array Variant 'value' count 'arrayLength == null ? 1 : arrayLength']
+        ]
+        ['25' VariantDiagnosticInfo [bit 'arrayLengthSpecified']
+            [optional int 32 'arrayLength' 'arrayLengthSpecified']
+            [array DiagnosticInfo 'value' count 'arrayLength == null ? 1 : arrayLength']
         ]
     ]
+    [optional int 32 'noOfArrayDimensions' 'arrayDimensionsSpecified']
+    [array bit 'arrayDimensions' count 'noOfArrayDimensions == null ? 0 : noOfArrayDimensions']
 ]
 
 [discriminatedType 'NodeId'