OPCFoundation / UA-.NETStandard

OPC Unified Architecture .NET Standard
Other
1.96k stars 948 forks source link

How to browse into an array node #2108

Closed VictorPieper closed 1 year ago

VictorPieper commented 1 year ago

Type of issue

Current Behavior

My test console program to browse into nodes gives me this output:

ServerInterfaces: 3:ServerInterfaces, Object
NameSpace+ Server interface_1: 4:Server interface_1, Object
Node+ read: 4:read, Variable, NodeId = ns=4;i=2, Value = (null)
Node+ IntValue1: 4:IntValue1, Variable, NodeId = ns=4;i=7, Value = 100

The node 'read' is an array of 4 Dints

Expected Behavior

This is what I expect:

ServerInterfaces: 3:ServerInterfaces, Object
NameSpace+ Server interface_1: 4:Server interface_1, Object
Node+ read: 4:read, Variable, NodeId = ns=4;i=2, Value = (null)
Node+ read: 4:read[0], Variable, NodeId = ns=4;i=3, Value = 1
Node+ read: 4:read[1], Variable, NodeId = ns=4;i=4, Value = 1
Node+ read: 4:read[2], Variable, NodeId = ns=4;i=5, Value = 1
Node+ read: 4:read[3], Variable, NodeId = ns=4;i=6, Value = 1
Node+ IntValue1: 4:IntValue1, Variable, NodeId = ns=4;i=7, Value = 100

I want the array nodes listed aswell

Steps To Reproduce

  1. Connect to an opc ua server in .net
  2. Use the code snippet from below
  3. Make an array in a serverinterface in the server you're connecting to
  4. Run the console program

Environment

- OS: Windows 11
- Environment: Visual Studio 2022 
- Runtime: .NET core 7
- Nuget Version:1.5.371.3-beta
- Component:Opc.Ua.Client
- Server: Siemens s7-1200
- Client: My C# Client

Anything else?

This code snippet is for step 2:

using (var session = Session.Create(config, new ConfiguredEndpoint(null, selectedEndpoint, EndpointConfiguration.Create(config)), false, "", 60000, null, null).GetAwaiter().GetResult())
            {
                Console.WriteLine("Step 3 - Browse the server namespace.");
                ReferenceDescriptionCollection refs;
                byte[] bts;
                session.Browse(null, null, ObjectIds.ObjectsFolder, 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, out bts, out refs);
                Console.WriteLine("DisplayName: BrowseName, NodeClass");
                foreach (var rd in refs)
                {
                    Console.WriteLine("{0}: {1}, {2}", rd.DisplayName, rd.BrowseName, rd.NodeClass);
                    ReferenceDescriptionCollection refs2;
                    byte[] bts2;
                    session.Browse(null, null, ExpandedNodeId.ToNodeId(rd.NodeId, session.NamespaceUris), 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, out bts2, out refs2);
                    foreach (var ref2 in refs2)
                    {
                        ReferenceDescriptionCollection refs3;
                        byte[] bts3;
                        Console.WriteLine("NameSpace+ {0}: {1}, {2}", ref2.DisplayName, ref2.BrowseName, ref2.NodeClass);
                        session.Browse(null, null, ExpandedNodeId.ToNodeId(ref2.NodeId, session.NamespaceUris), 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, out bts3, out refs3);
                        foreach (var ref3 in refs3)
                        {

                            NodeId nodeId = new NodeId(ref3.NodeId.ToString());
                            var value = session.ReadValue(nodeId);
                            Console.WriteLine("Node+ {0}: {1}, {2}, NodeId = {3}, Value = {4}", ref3.DisplayName, ref3.BrowseName, ref3.NodeClass, ref3.NodeId, value);

                        }
                    }
                }

Extra info: FWRzn

VictorPieper commented 1 year ago

I figured it out sort off so better answer are still appreciated (:

Using: var references = session.FetchReferences(nodeId); You can get all references from a node.

foreach (var ref3 in refs3.Where(n => n.DisplayName != "Icon"))
                    {

                        NodeId nodeId = new NodeId(ref3.NodeId.ToString());
                        var value = session.ReadValue(nodeId);
                        Console.WriteLine("----------------------");
                        Console.WriteLine("Node+ {0}: {1}, {2}, NodeId = {3}, Value = {4}", ref3.DisplayName, ref3.BrowseName, ref3.NodeClass, ref3.NodeId, value);
                        var references = session.FetchReferences(nodeId);
                        if (references != null)
                        {
                            Console.WriteLine("array");
                            foreach (var reference in references.Where(r => r.NodeClass.ToString() == "Variable"))
                            {
                                Console.WriteLine(reference.BrowseName + " NodeId: " + reference.NodeId.ToString());
                            }
                        }
                        Console.WriteLine("----------------------");
                    }

Output:

Node+ read: 4:read, Variable, NodeId = ns=4;i=2, Value = (null)
array
4:[0] NodeId: ns=4;i=3
4:[1] NodeId: ns=4;i=4
4:[2] NodeId: ns=4;i=5
4:[3] NodeId: ns=4;i=6
mregen commented 1 year ago

Please check the console samples how to browse and read values efficiently, especially how to browse or fetch the whole tree: https://github.com/OPCFoundation/UA-.NETStandard/blob/master/Applications/ConsoleReferenceClient/ClientSamples.cs Apparently there is a ReadValues API that reads the values in a single service call, that is preferred operation.