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 2022/09/15 20:19:29 UTC

[plc4x] branch plc4j/opcua-browse updated: chore(plc4x): Browse now browses each node. Still need to sort out how to create the BrowseItem Tree.

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

hutcheb pushed a commit to branch plc4j/opcua-browse
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/plc4j/opcua-browse by this push:
     new 018c27ed8 chore(plc4x): Browse now browses each node. Still need to sort out how to create the BrowseItem Tree.
018c27ed8 is described below

commit 018c27ed8bef9971cf774c5e6b380e4ca0d2f912
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 15 14:19:17 2022 -0600

    chore(plc4x): Browse now browses each node. Still need to sort out how to create the BrowseItem Tree.
---
 .../java/opcua/protocol/OpcuaProtocolLogic.java    | 142 +++++++++++++--------
 protocols/opcua/src/main/xslt/opc-common.xsl       |  19 +--
 2 files changed, 96 insertions(+), 65 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 5b3c75358..62924d546 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
@@ -39,6 +39,7 @@ import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
 import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration;
 import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionField;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
+import org.apache.plc4x.java.spi.values.PlcINT;
 import org.apache.plc4x.java.spi.values.PlcList;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -83,6 +84,9 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     private Map<Long, OpcuaSubscriptionHandle> subscriptions = new HashMap<>();
     private SecureChannel channel;
     private AtomicBoolean securedConnection = new AtomicBoolean(false);
+    private LinkedHashMap<String, ReferenceDescription> discoveredNodes= new LinkedHashMap<>();
+    private LinkedHashMap<String, ReferenceDescription> browsedNodes= new LinkedHashMap<>();
+    private LinkedHashMap<String, String> nodeChildren= new LinkedHashMap<>();
 
     @Override
     public void setConfiguration(OpcuaConfiguration configuration) {
@@ -128,8 +132,8 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
         channel.onDiscover(context);
     }
 
-    private CompletableFuture<List<PlcBrowseItem>> browseNode(NodeId nodeId) {
-        CompletableFuture<List<PlcBrowseItem>> future = new CompletableFuture<>();
+    private CompletableFuture<PlcBrowseItem> browseNode(ExpandedNodeId nodeId) {
+        CompletableFuture<PlcBrowseItem> future = new CompletableFuture<>();
         RequestHeader requestHeader = new RequestHeader(channel.getAuthenticationToken(),
             SecureChannel.getCurrentDateTime(),
             channel.getRequestHandle(),
@@ -140,7 +144,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
 
         List<ExtensionObjectDefinition> requestedValues = new ArrayList<>(1);
         requestedValues.add(new BrowseDescription(
-            nodeId,
+            new NodeId(nodeId.getNodeId()),
             BrowseDirection.browseDirectionForward,
             new NodeId(new NodeIdTwoByte((short) 33)),
             true,
@@ -176,13 +180,15 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
             Consumer<byte[]> consumer = opcuaResponse -> {
                 try {
                     ExtensionObjectDefinition reply = ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse, ByteOrder.LITTLE_ENDIAN), false).getBody();
+                    PlcBrowseItem value = null;
                     if (reply instanceof BrowseResponse) {
                         BrowseResponse response = (BrowseResponse) reply;
                         BrowseResult castResult = (BrowseResult) response.getResults().get(0);
-                        List<PlcBrowseItem> values = new ArrayList<>(response.getResults().size());
 
+                        ArrayList<PlcBrowseItem> children = new ArrayList<>(0);
                         for (ExtensionObjectDefinition result : castResult.getReferences()) {
                             ReferenceDescription referenceResult = (ReferenceDescription) result;
+                            referenceResult.getNodeId().getIdentifier();
                             String typeDefinition = referenceResult.getTypeDefinition().getIdentifier();
                             PlcValueType plcValue = null;
                             if (OpcuaDataType.isDefined(typeDefinition)) {
@@ -191,33 +197,36 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                             } else {
                                 plcValue = PlcValueType.Struct;
                             }
-                            ExpandedNodeId tempNodeId = ((ReferenceDescription) result).getNodeId();
-                            NodeId tempNode = new NodeId(
-                                new NodeIdString(tempNodeId tempNodeId.getIdentifier());
-                            )
-                            CompletableFuture<List<PlcBrowseItem>> childFuture = browseNode();
-                            List<PlcBrowseItem> list = childFuture.get(5000L, TimeUnit.SECONDS);
-                            if (list != null) {
-                                values.add(new DefaultPlcBrowseItem(
-                                    referenceResult.getBrowseName().getName().getStringValue(),
-                                    referenceResult.getDisplayName().getText().getStringValue(),
-                                    plcValue,
-                                    true,
-                                    true,
-                                    true, list, null));
-                            } else {
-                                values.add(new DefaultPlcBrowseItem(
-                                    referenceResult.getBrowseName().getName().getStringValue(),
-                                    referenceResult.getDisplayName().getText().getStringValue(),
-                                    plcValue,
-                                    true,
-                                    true,
-                                    true, new ArrayList<PlcBrowseItem>(0), null));
+                            String test = referenceResult.getNodeId().getIdentifier();
+                            if (referenceResult.getNodeClass() == NodeClass.nodeClassObject || referenceResult.getNodeClass() == NodeClass.nodeClassVariable) {
+                                nodeChildren.put(nodeId.getIdentifier(), referenceResult.getNodeId().getIdentifier());
+                                discoveredNodes.put(referenceResult.getNodeId().getIdentifier(), referenceResult);
                             }
                         }
-                        future.complete(values);
+                        value = new DefaultPlcBrowseItem(
+                            nodeId.getIdentifier(),
+                            nodeId.getIdentifier(),
+                            PlcValueType.INT,
+                            true,
+                            true,
+                            true,
+                            new ArrayList<>(0),
+                            new HashMap<>()
+                        );
+
+                        future.complete(value);
                     } else {
-                        List<PlcBrowseItem> values = new ArrayList<>(0);
+                        value = new DefaultPlcBrowseItem(
+                            nodeId.getIdentifier(),
+                            nodeId.getNamespaceURI().getStringValue(),
+                            PlcValueType.INT,
+                            false,
+                            false,
+                            false,
+                            new ArrayList<>(0),
+                            new HashMap<>()
+                        );
+
                         if (reply instanceof ServiceFault) {
                             ExtensionObjectDefinition header = ((ServiceFault) reply).getResponseHeader();
                             LOGGER.error("Browse request ended up with ServiceFault: {}", header);
@@ -225,17 +234,10 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
                             LOGGER.error("Remote party returned an error '{}'", reply);
                         }
 
-                        future.complete(values);
-                        return;
+                        future.complete(value);
                     }
                 } catch (ParseException e) {
                     future.completeExceptionally(new PlcRuntimeException(e));
-                } catch (ExecutionException e) {
-                    throw new RuntimeException(e);
-                } catch (InterruptedException e) {
-                    throw new RuntimeException(e);
-                } catch (TimeoutException e) {
-                    throw new RuntimeException(e);
                 }
             };
 
@@ -262,25 +264,61 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H
     @Override
     public CompletableFuture<PlcBrowseResponse> browse(PlcBrowseRequest browseRequest) {
         CompletableFuture<PlcBrowseResponse> future = new CompletableFuture<>();
+        boolean browsable = true;
+        PlcBrowseResponse response = null;
 
-        CompletableFuture<List<PlcBrowseItem>> childFuture = browseNode(new ExpandedNodeId(
-            false,
-            false,
-            new NodeIdTwoByte((short) 85),
-            null,
-            0L));
+        ReferenceDescription referenceDescription = new ReferenceDescription(
+            new NodeId(
+                new NodeIdTwoByte((short) 47)
+            ),
+            true,
+            new ExpandedNodeId(
+                false,
+                false,
+                new NodeIdTwoByte((short) 85),
+                null,
+                0L),
+            new QualifiedName(
+                0,
+                new PascalString("root")
+            ),
+            new LocalizedText(
+                true,
+                true,
+                new PascalString("en"),
+                new PascalString("root")),
+            NodeClass.nodeClassObject,
+            new ExpandedNodeId(
+                false,
+                false,
+                new NodeIdTwoByte((short) 2034),
+                null,
+                0L)
+        );
 
-        PlcBrowseResponse response = null;
-        try {
-            response = new DefaultPlcBrowseResponse(browseRequest, PlcResponseCode.OK, childFuture.get(1000L, TimeUnit.SECONDS));
-            future.complete(response);
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        } catch (ExecutionException e) {
-            throw new RuntimeException(e);
-        } catch (TimeoutException e) {
-            throw new RuntimeException(e);
+        discoveredNodes.put(referenceDescription.getNodeId().getIdentifier(), referenceDescription);
+
+        while (browsable) {
+            CompletableFuture<PlcBrowseItem> childFuture = browseNode(referenceDescription.getNodeId());
+
+            try {
+                PlcBrowseItem responseItem = childFuture.get(10L, TimeUnit.SECONDS);
+                browsedNodes.put(referenceDescription.getNodeId().getIdentifier(), referenceDescription);
+                discoveredNodes.remove(referenceDescription.getNodeId().getIdentifier());
+                Optional<String> first = discoveredNodes.keySet().stream().findFirst();
+                if (first.isPresent()) {
+                    referenceDescription = discoveredNodes.get(first.get());
+                }
+            } catch (TimeoutException | InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
+            if (discoveredNodes.isEmpty()) {
+                browsable = false;
+            }
         }
+
+        response = new DefaultPlcBrowseResponse(browseRequest, PlcResponseCode.OK, new ArrayList<PlcBrowseItem>());
+        future.complete(response);
         //new DefaultPlcBrowseResponse(browseRequest, PlcResponseCode.INTERNAL_ERROR, values)
         return future;
     }
diff --git a/protocols/opcua/src/main/xslt/opc-common.xsl b/protocols/opcua/src/main/xslt/opc-common.xsl
index 630feca92..73ca19b34 100644
--- a/protocols/opcua/src/main/xslt/opc-common.xsl
+++ b/protocols/opcua/src/main/xslt/opc-common.xsl
@@ -354,16 +354,15 @@
     <xsl:function name="plc4x:getDataTypeLength" as="xs:integer">
         <xsl:param name="lengthMap" as="map(xs:string, xs:int)"/>
         <xsl:param name="datatype"/>
-        <xsl:message>[DEBUG] Getting length of <xsl:value-of select="xs:string($datatype[@TypeName])"/></xsl:message>
         <xsl:choose>
-            <xsl:when test="map:contains($lengthMap, xs:string($datatype[@TypeName]))">
-                <xsl:message>[DEBUG] Bit Length <xsl:value-of select="$lengthMap(xs:string($datatype[@TypeName]))"/></xsl:message>
-                <xsl:value-of select="map:get($lengthMap, xs:string($datatype[@TypeName]))"/>
+            <xsl:when test="map:contains($lengthMap, xs:string($datatype/[@TypeName]))">
+                <xsl:message>[DEBUG] Bit Length <xsl:value-of select="$lengthMap(xs:string($datatype/[@TypeName]))"/></xsl:message>
+                <xsl:value-of select="map:get($lengthMap, xs:string($datatype/[@TypeName]))"/>
             </xsl:when>
-            <xsl:when test="($datatype[@TypeName] = 'opc:Bit') or ($datatype[@TypeName] = 'opc:Boolean')">
+            <xsl:when test="($datatype/[@TypeName] = 'opc:Bit') or ($datatype/[@TypeName] = 'opc:Boolean')">
                 <xsl:choose>
-                    <xsl:when test="$datatype[@Length] != ''">
-                        <xsl:value-of select="xs:int($datatype[@Length])"/>
+                    <xsl:when test="$datatype/[@Length] != ''">
+                        <xsl:value-of select="xs:int($datatype/[@Length])"/>
                     </xsl:when>
                     <xsl:otherwise>1</xsl:otherwise>
                 </xsl:choose>
@@ -379,7 +378,6 @@
         <xsl:param name="currentNodePosition" as="xs:int"/>
         <xsl:param name="currentBitPosition" as="xs:int"/>
         <xsl:param name="currentBytePosition" as="xs:int"/>
-        <xsl:message>[DEBUG] Recursively rearranging bit order in nodes,  Position - <xsl:value-of select="$currentNodePosition"/>, Bit Position - <xsl:value-of select="$currentBitPosition"/>, Byte Position - <xsl:value-of select="$currentBytePosition"/></xsl:message>
         <xsl:for-each select="$baseNode/opc:Field">
             <xsl:message>[DEBUG] <xsl:value-of select="position()"/> - <xsl:value-of select="@TypeName"/></xsl:message>
         </xsl:for-each>
@@ -390,7 +388,6 @@
                 <xsl:choose>
                     <xsl:when test="$currentBitPosition != 0">
                         <!-- Add a reserved field if we are halfway through a Byte.  -->
-                        <xsl:message>[DEBUG] Adding a reserved field</xsl:message>
                         <xsl:call-template name="plc4x:parseFields">
                             <xsl:with-param name="baseNode">
                                 <xsl:copy-of select="$baseNode/opc:Field[position() lt ($currentNodePosition - $currentBytePosition)]"/>
@@ -427,7 +424,6 @@
                         <xsl:choose>
                             <xsl:when test="$currentBitPosition=0">
                                 <!-- Put node into current position -->
-                                <xsl:message>[DEBUG] First Bit in Byte</xsl:message>
                                 <xsl:call-template name="plc4x:parseFields">
                                     <xsl:with-param name="baseNode">
                                         <xsl:copy-of select="$baseNode/opc:Field"/>
@@ -446,7 +442,6 @@
                             </xsl:when>
                             <xsl:otherwise>
                                 <!-- Put node into correct position based on bit and byte position -->
-                                <xsl:message>[DEBUG] Additional Bit in Byte</xsl:message>
                                 <xsl:call-template name="plc4x:parseFields">
                                     <xsl:with-param name="baseNode">
                                         <xsl:copy-of select="$baseNode/opc:Field[position() lt ($currentNodePosition - $currentBytePosition)]"/>
@@ -472,7 +467,6 @@
                         <xsl:choose>
                             <xsl:when test="$currentBitPosition != 0 and $currentBitPosition lt 8">
                                 <!-- Add a reserved field if we are halfway through a Byte.  -->
-                                <xsl:message>[DEBUG] Adding a reserved field</xsl:message>
                                 <xsl:call-template name="plc4x:parseFields">
                                     <xsl:with-param name="baseNode">
                                         <xsl:copy-of select="$baseNode/opc:Field[position() lt ($currentNodePosition - $currentBytePosition)]"/>
@@ -497,7 +491,6 @@
                             </xsl:when>
                             <xsl:otherwise>
                                 <!-- Put node into current position -->
-                                <xsl:message>[DEBUG] not a bit data type, just leave it in it's place</xsl:message>
                                 <xsl:call-template name="plc4x:parseFields">
                                     <xsl:with-param name="baseNode">
                                         <xsl:copy-of select="$baseNode/opc:Field"/>