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/13 10:34:10 UTC

[plc4x] branch feature/native_opua_client updated: Updated datatypes that can be read

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 a65ed1c  Updated datatypes that can be read
a65ed1c is described below

commit a65ed1c4c5b7476ae8b762935a773ef150cb809f
Author: hutcheb <be...@gmail.com>
AuthorDate: Wed Jan 13 04:19:44 2021 -0500

    Updated datatypes that can be read
    
    There is some support for all 25 Variant types however there are some
    that jutst get passed through as Strings.
---
 .../java/opcua/protocol/OpcuaProtocolLogic.java    | 106 ++++++++++++++++-----
 .../plc4x/java/opcua/OpcuaPlcDriverTest.java       |  23 ++++-
 protocols/opcua/src/main/xslt/opc-types.xsl        |  74 ++++++++++++--
 3 files changed, 170 insertions(+), 33 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 9d7f8de..de891f8 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
@@ -62,11 +62,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.UnsupportedEncodingException;
-import java.time.Duration;
+import java.time.*;
 import java.math.BigInteger;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -88,12 +85,14 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     private static final Logger LOGGER = LoggerFactory.getLogger(OpcuaProtocolLogic.class);
     public static final Duration REQUEST_TIMEOUT = Duration.ofMillis(1000000);
     public static final long REQUEST_TIMEOUT_LONG = 10000L;
-    private static final String CHUNK = "F";
-    private static final int VERSION = 0;
+
+    private static final int DEFAULT_CONNECTION_LIFETIME = 36000000;
+    private static final int DEFAULT_MAX_CHUNK_COUNT = 64;
+    private static final int DEFAULT_MAX_MESSAGE_SIZE = 2097152;
     private static final int DEFAULT_RECEIVE_BUFFER_SIZE = 65535;
     private static final int DEFAULT_SEND_BUFFER_SIZE = 65535;
-    private static final int DEFAULT_MAX_MESSAGE_SIZE = 2097152;
-    private static final int DEFAULT_MAX_CHUNK_COUNT = 64;
+    private static final int VERSION = 0;
+
     private NodeId authenticationToken = new NodeIdTwoByte(NodeIdType.nodeIdTypeTwoByte, new TwoByteNodeId((short) 0));
     private static final PascalString NULL_STRING = new PascalString(-1,null);
     private static ExpandedNodeId NULL_EXPANDED_NODEID = new ExpandedNodeIdTwoByte(false,
@@ -106,14 +105,15 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                                                                                 null,               //Body Length
                                                                                     null);               // Body
     private static final long epochOffset = 116444736000000000L;         //Offset between OPC UA epoch time and linux epoch time.
-    private static final int DEFAULT_CONNECTION_LIFETIME = 36000000;
+
+    private static final String CHUNK = "F";
     private static final String nameSpaceSecurityPolicyNone = "http://opcfoundation.org/UA/SecurityPolicy#None";
     private static final String applicationUri = "urn:apache:plc4x:client";
     private static final String productUri = "urn:apache:plc4x:client";
     private static final String applicationText = "OPCUA client for the Apache PLC4X:PLC4J project";
 
-    private String sessionName = "UaSession:" + applicationText + ":" + RandomStringUtils.random(20, true, true);
-    private String clientNonce = RandomStringUtils.random(40, true, true);
+    private final String sessionName = "UaSession:" + applicationText + ":" + RandomStringUtils.random(20, true, true);
+    private final String clientNonce = RandomStringUtils.random(40, true, true);
     private RequestTransactionManager tm;
 
     private String endpoint;
@@ -361,8 +361,6 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             noOfDiscoveryUrls,
             discoveryUrls);
 
-        clientNonce = RandomStringUtils.random(40, true, true);
-
         CreateSessionRequest createSessionRequest = new CreateSessionRequest((byte) 1,
             (byte) 0,
             requestHeader,
@@ -683,13 +681,30 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                     int length = array.length;
                     LocalDateTime[] tmpValue = new LocalDateTime[length];
                     for (int i = 0; i < length; i++) {
-                        tmpValue[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(array[i]), ZoneId.systemDefault());
+                        tmpValue[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(getDateTime(array[i])), ZoneOffset.UTC);
                     }
                     value = IEC61131ValueHandler.of(tmpValue);
                 } else if (variant instanceof VariantGuid) {
-                    int length = ((VariantGuid) variant).getValue().length;
-                    String[] stringArray = ((VariantGuid) variant).getValue();
-                    value = IEC61131ValueHandler.of(stringArray);
+                    GuidValue[] array = ((VariantGuid) variant).getValue();
+                    int length = array.length;
+                    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[i].getData4();
+                        int data4 = 0;
+                        for (int k = 0; k < data4Bytes.length; k++)
+                        {
+                            data4 = (data4 << 8) + (data4Bytes[k] & 0xff);
+                        }
+                        byte[] data5Bytes = array[i].getData5();
+                        long data5 = 0;
+                        for (int k = 0; k < data5Bytes.length; k++)
+                        {
+                            data5 = (data5 << 8) + (data5Bytes[k] & 0xff);
+                        }
+                        tmpValue[i] = Long.toHexString(array[i].getData1()) + "-" + Integer.toHexString(array[i].getData2()) + "-" + Integer.toHexString(array[i].getData3()) + "-" + Integer.toHexString(data4) + "-" + Long.toHexString(data5);
+                    }
+                    value = IEC61131ValueHandler.of(tmpValue);
                 } else if (variant instanceof VariantXmlElement) {
                     int length = ((VariantXmlElement) variant).getValue().length;
                     PascalString[] stringArray = ((VariantXmlElement) variant).getValue();
@@ -698,15 +713,60 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                         tmpValue[i] = stringArray[i].getStringValue();
                     }
                     value = IEC61131ValueHandler.of(tmpValue);
-                } else if (variant instanceof VariantByteString) {
-                    //TODO:- Looking into returning structures.
-                    ByteStringArray[] array = ((VariantByteString) variant).getValue();
-                    int length = array.length;
-                    Short[] tmpValue = new Short[length];
+                } else if (variant instanceof VariantLocalizedText) {
+                    int length = ((VariantLocalizedText) variant).getValue().length;
+                    LocalizedText[] stringArray = ((VariantLocalizedText) variant).getValue();
+                    String[] tmpValue = new String[length];
+                    for (int i = 0; i < length; i++) {
+                        tmpValue[i] = "";
+                        tmpValue[i] += stringArray[i].getLocaleSpecified() ? stringArray[i].getLocale().getStringValue() + "|" : "";
+                        tmpValue[i] += stringArray[i].getTextSpecified() ? stringArray[i].getText().getStringValue() : "";
+                    }
+                    value = IEC61131ValueHandler.of(tmpValue);
+                } else if (variant instanceof VariantQualifiedName) {
+                    int length = ((VariantQualifiedName) variant).getValue().length;
+                    QualifiedName[] stringArray = ((VariantQualifiedName) variant).getValue();
+                    String[] tmpValue = new String[length];
                     for (int i = 0; i < length; i++) {
-                        tmpValue[i] = array[i].getValue();
+                        tmpValue[i] = "ns=" + stringArray[i].getNamespaceIndex() + ";s=" + stringArray[i].getName().getStringValue();
                     }
                     value = IEC61131ValueHandler.of(tmpValue);
+                } else if (variant instanceof VariantExtensionObject) {
+                    int length = ((VariantExtensionObject) variant).getValue().length;
+                    ExtensionObject[] stringArray = ((VariantExtensionObject) variant).getValue();
+                    String[] tmpValue = new String[length];
+                    for (int i = 0; i < length; i++) {
+                        tmpValue[i] = stringArray[i].toString();
+                    }
+                    value = IEC61131ValueHandler.of(tmpValue);
+                } else if (variant instanceof VariantNodeId) {
+                    int length = ((VariantNodeId) variant).getValue().length;
+                    NodeId[] stringArray = ((VariantNodeId) variant).getValue();
+                    String[] tmpValue = new String[length];
+                    for (int i = 0; i < length; i++) {
+                        tmpValue[i] = stringArray[i].toString();
+                    }
+                    value = IEC61131ValueHandler.of(tmpValue);
+                }else if (variant instanceof VariantStatusCode) {
+                    int length = ((VariantStatusCode) variant).getValue().length;
+                    StatusCode[] stringArray = ((VariantStatusCode) variant).getValue();
+                    String[] tmpValue = new String[length];
+                    for (int i = 0; i < length; i++) {
+                        tmpValue[i] = stringArray[i].toString();
+                    }
+                    value = IEC61131ValueHandler.of(tmpValue);
+                } else if (variant instanceof VariantByteString) {
+                    PlcList plcList = new PlcList();
+                    ByteStringArray[] array = ((VariantByteString) variant).getValue();
+                    for (int k = 0; k < array.length; k++) {
+                        int length = array[k].getValue().length;
+                        Short[] tmpValue = new Short[length];
+                        for (int i = 0; i < length; i++) {
+                            tmpValue[i] = array[k].getValue()[i];
+                        }
+                        plcList.add(IEC61131ValueHandler.of(tmpValue));
+                    }
+                    value = plcList;
                 } else {
                     responseCode = PlcResponseCode.UNSUPPORTED;
                     LOGGER.error("Data type - " +  variant.getClass() + " is not supported ");
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java
index 3f676a6..e41954d 100644
--- a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/OpcuaPlcDriverTest.java
@@ -65,7 +65,7 @@ public class OpcuaPlcDriverTest {
     private static final String DATE_TIME_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/DateTime";
     private static final String DURATION_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Duration";
     private static final String GUID_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Guid";
-    private static final String LOCALISED_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/LocalizedText";
+    private static final String LOCALIZED_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/LocalizedText";
     private static final String NODE_ID_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/NodeId";
     private static final String QUALIFIED_NAM_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/QualifiedName";
     private static final String UTC_TIME_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/UtcTime";
@@ -87,6 +87,7 @@ public class OpcuaPlcDriverTest {
     private static final String UINT16_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/UInt16Array";
     private static final String UINT32_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/UInt32Array";
     private static final String UINT64_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/UInt64Array";
+    private static final String DATE_TIME_ARRAY_IDENTIFIER = "ns=2;s=HelloWorld/ArrayTypes/DateTimeArray";
 
 
     // Address of local milo server
@@ -194,6 +195,16 @@ public class OpcuaPlcDriverTest {
         builder.addItem("UInt32", UINT32_IDENTIFIER_READ_WRITE);
         builder.addItem("UInt64", UINT64_IDENTIFIER_READ_WRITE);
         builder.addItem("UInteger", UINTEGER_IDENTIFIER_READ_WRITE);
+        builder.addItem("ByteString", BYTE_STRING_IDENTIFIER_READ_WRITE);
+        builder.addItem("DateTime", DATE_TIME_READ_WRITE);
+        builder.addItem("Duration", DURATION_READ_WRITE);
+        builder.addItem("GUID", GUID_READ_WRITE);
+        builder.addItem("XmlElement", XML_ELEMENT_READ_WRITE);
+        builder.addItem("Variant", VARIANT_READ_WRITE);
+        builder.addItem("LocalizedText", LOCALIZED_READ_WRITE);
+        builder.addItem("QualifiedName", QUALIFIED_NAM_READ_WRITE);
+        builder.addItem("NodeId", NODE_ID_READ_WRITE);
+        builder.addItem("UtcTime", UTC_TIME_READ_WRITE);
 
         builder.addItem("BoolArray", BOOL_ARRAY_IDENTIFIER);
         builder.addItem("ByteStringArray", BYTE_STRING_ARRAY_IDENTIFIER);
@@ -208,6 +219,7 @@ public class OpcuaPlcDriverTest {
         builder.addItem("UInt16Array", UINT16_ARRAY_IDENTIFIER);
         builder.addItem("UInt32Array", UINT32_ARRAY_IDENTIFIER);
         builder.addItem("UInt64Array", UINT64_ARRAY_IDENTIFIER);
+        builder.addItem("DateTimeArray", DATE_TIME_ARRAY_IDENTIFIER);
 
         builder.addItem("DoesNotExists", DOES_NOT_EXIST_IDENTIFIER_READ_WRITE);
 
@@ -227,6 +239,14 @@ public class OpcuaPlcDriverTest {
         assert response.getResponseCode("UInt32").equals(PlcResponseCode.OK);
         assert response.getResponseCode("UInt64").equals(PlcResponseCode.OK);
         assert response.getResponseCode("UInteger").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("ByteString").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("DateTime").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Duration").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("GUID").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("XmlElement").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("Variant").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("LocalizedText").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("QualifiedName").equals(PlcResponseCode.OK);
 
         assert response.getResponseCode("BoolArray").equals(PlcResponseCode.OK);
         assert response.getResponseCode("ByteArray").equals(PlcResponseCode.OK);
@@ -240,6 +260,7 @@ public class OpcuaPlcDriverTest {
         assert response.getResponseCode("UInt16Array").equals(PlcResponseCode.OK);
         assert response.getResponseCode("UInt32Array").equals(PlcResponseCode.OK);
         assert response.getResponseCode("UInt64Array").equals(PlcResponseCode.OK);
+        assert response.getResponseCode("DateTimeArray").equals(PlcResponseCode.OK);
 
         assert response.getResponseCode("DoesNotExists").equals(PlcResponseCode.NOT_FOUND);
 
diff --git a/protocols/opcua/src/main/xslt/opc-types.xsl b/protocols/opcua/src/main/xslt/opc-types.xsl
index 43d5015..e3f2ac8 100644
--- a/protocols/opcua/src/main/xslt/opc-types.xsl
+++ b/protocols/opcua/src/main/xslt/opc-types.xsl
@@ -54,17 +54,25 @@
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='OpenSecureChannelResponse']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CreateSessionRequest']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CreateSessionResponse']"/>
+        <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CreateSubscriptionRequest']"/>
+        <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CreateSubscriptionResponse']"/>
+        <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CreateMonitoredItemsRequest']"/>
+        <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CreateMonitoredItemsRequest']"/>
+        <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='DeleteSubscriptionsRequest']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='ActivateSessionRequest']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='ActivateSessionResponse']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='ReadRequest']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='ReadResponse']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='WriteRequest']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='WriteResponse']"/>
-        ['473' CloseSessionRequest
-            [simple RequestHeader 'requestHeader']
-            [reserved uint 7 '0x00']
-            [simple bit 'deleteSubscriptions']
-        ]
+        <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='BrowseRequest']"/>
+        <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='BrowseResponse']"/>
+
+    ['473' CloseSessionRequest
+        [simple RequestHeader 'requestHeader']
+        [reserved uint 7 '0x00']
+        [simple bit 'deleteSubscriptions']
+    ]
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CloseSessionResponse']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CloseSecureChannelRequest']"/>
         <xsl:apply-templates select="$file/node:UANodeSet/node:UADataType[@BrowseName='CloseSecureChannelResponse']"/>
@@ -87,6 +95,46 @@
     <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='ChannelSecurityToken']"/>
 ]
 
+[type 'MonitoredItemCreateRequest'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='MonitoredItemCreateRequest']"/>
+]
+
+[type 'BrowseResult'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='BrowseResult']"/>
+]
+
+[type 'ViewDescription'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='ViewDescription']"/>
+]
+
+[type 'BrowseDescription'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='BrowseDescription']"/>
+]
+
+[type 'ReferenceDescription'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='ReferenceDescription']"/>
+]
+
+[enum int 32 'MonitoringMode'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:EnumeratedType[@Name='MonitoringMode']"/>
+]
+
+[type 'MonitoringParameters'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='MonitoringParameters']"/>
+]
+
+[enum int 32 'BrowseDirection'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:EnumeratedType[@Name='BrowseDirection']"/>
+]
+
+[type 'ReferenceDescription'
+    <xsl:apply-templates select="/opc:TypeDictionary/opc:StructuredType[@Name='ReferenceDescription']"/>
+]
+
+[enum int 32 'NodeClass'
+<xsl:apply-templates select="/opc:TypeDictionary/opc:EnumeratedType[@Name='NodeClass']"/>
+]
+
 [type 'DiagnosticInfo'
     [simple bit 'symbolicIdSpecified']
     [simple bit 'namespaceURISpecified']
@@ -170,6 +218,14 @@
     [array uint 8 'value' count 'arrayLength']
 ]
 
+[type 'GuidValue'
+    [simple uint 32 'data1']
+    [simple uint 16 'data2']
+    [simple uint 16 'data3']
+    [array int 8 'data4' count '2']
+    [array int 8 'data5' count '6']
+]
+
 [discriminatedType 'Variant'
     [discriminator bit 'arrayLengthSpecified']
     [simple bit 'arrayDimensionsSpecified']
@@ -229,7 +285,7 @@
         ]
         ['14' VariantGuid [bit 'arrayLengthSpecified']
             [optional int 32 'arrayLength' 'arrayLengthSpecified']
-            [array string '-1' 'value' count 'arrayLength == null ? 1 : arrayLength']
+            [array GuidValue 'value' count 'arrayLength == null ? 1 : arrayLength']
         ]
         ['15' VariantByteString [bit 'arrayLengthSpecified']
             [optional int 32 'arrayLength' 'arrayLengthSpecified']
@@ -423,7 +479,7 @@
                 <xsl:choose>
                     <xsl:when test="not(@BrowseName='Vector') and not(substring(@BrowseName,1,1) = '&lt;') and not(number(substring(@BrowseName,1,1)))">
     [type '<xsl:value-of select='@BrowseName'/>'
-                    <xsl:apply-templates select="$originaldoc/opc:TypeDictionary/opc:StructuredType[@Name=$browseName]"/>]
+        <xsl:apply-templates select="$originaldoc/opc:TypeDictionary/opc:StructuredType[@Name=$browseName]"/>]
                     </xsl:when>
                 </xsl:choose>
             </xsl:when>
@@ -516,9 +572,9 @@
 
         <xsl:choose>
             <xsl:when test="@LengthField">[array <xsl:value-of select="$dataType"/>  '<xsl:value-of select="$lowerCaseName"/>' count '<xsl:value-of select="$lowerCaseLengthField"/>']
-            </xsl:when>
+    </xsl:when>
             <xsl:otherwise>[<xsl:value-of select="$mspecType"/><xsl:text> </xsl:text><xsl:value-of select="$dataType"/> '<xsl:value-of select="$lowerCaseName"/>']
-        </xsl:otherwise>
+    </xsl:otherwise>
         </xsl:choose>
     </xsl:template>