如何用OPC Foundation .NET浏览OPC UA服务器的数组节点?
问题描述
我修改了Stack Overflow上的代码,编写了测试程序以浏览Siemens S7-1200 OPC UA服务器的节点,代码如下:
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.Where(r => r.DisplayName == "ServerInterfaces")) { 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 nextRd in refs2) { ReferenceDescriptionCollection refs3; byte[] bts3; Console.WriteLine("NameSpace+ {0}: {1}, {2}", nextRd.DisplayName, nextRd.BrowseName, nextRd.NodeClass); session.Browse(null, null, ExpandedNodeId.ToNodeId(nextRd.NodeId, session.NamespaceUris), 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable | (uint)NodeClass.Object | (uint)NodeClass.Method, out bts3, out refs3); foreach (var nextRd2 in refs3.Where(n => n.DisplayName != "Icon")) { NodeId nodeId = new NodeId(nextRd2.NodeId.ToString()); var value = session.ReadValue(nodeId); Console.WriteLine("Node+ {0}: {1}, {2}, NodeId = {3}, Value = {4}", nextRd2.DisplayName, nextRd2.BrowseName, nextRd2.NodeClass, nextRd2.NodeId, value); } } } }
服务器中有一个INT类型数组,当前程序输出仅显示数组节点本身(Value为null),无法列出数组元素:
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
期望输出是能展示数组的每个元素及其值:
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
此外,另一程序可正常展示自动生成的数组元素,但自定义的'read'数组仍无法显示元素,请问如何修改代码实现期望输出?
解决方案
1. 核心修改思路
OPC UA中数组变量的处理需要分两种场景:一是直接读取数组完整值并解析元素,二是针对部分服务器将数组元素作为子节点暴露的情况,额外浏览数组节点的子节点。针对S7-1200自定义数组的特性,还可以利用其NodeId的生成规则直接定位元素节点。
2. 修改后的核心代码
替换原代码中遍历refs3的循环部分:
foreach (var nextRd2 in refs3.Where(n => n.DisplayName != "Icon")) { // 优化NodeId转换方式,避免字符串解析错误 NodeId nodeId = ExpandedNodeId.ToNodeId(nextRd2.NodeId, session.NamespaceUris); DataValue dataValue = session.ReadValue(nodeId); var value = dataValue.Value; Console.WriteLine("Node+ {0}: {1}, {2}, NodeId = {3}, Value = {4}", nextRd2.DisplayName, nextRd2.BrowseName, nextRd2.NodeClass, nextRd2.NodeId, value); // 处理数组类型变量 if (value != null && value.GetType().IsArray) { Array arrayValue = (Array)value; // 按S7-1200自定义数组的NodeId规则生成元素NodeId // 数组NodeId为ns=4;i=2时,元素[0]对应ns=4;i=3,以此类推 int baseId = int.Parse(nodeId.Identifier.ToString()) + 1; for (int i = 0; i < arrayValue.Length; i++) { Console.WriteLine("Node+ {0}: {1}[{2}], Variable, NodeId = ns=4;i={3}, Value = {4}", nextRd2.DisplayName, nextRd2.BrowseName, i, baseId + i, arrayValue.GetValue(i)); } } else { // 尝试浏览数组节点的子节点,兼容自动生成数组的场景 ReferenceDescriptionCollection arrayRefs; byte[] arrayBts; session.Browse(null, null, nodeId, 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable, out arrayBts, out arrayRefs); foreach (var elemRef in arrayRefs) { NodeId elemNodeId = ExpandedNodeId.ToNodeId(elemRef.NodeId, session.NamespaceUris); var elemValue = session.ReadValue(elemNodeId).Value; Console.WriteLine("Node+ {0}: {1}, Variable, NodeId = {2}, Value = {3}", elemRef.DisplayName, elemRef.BrowseName, elemRef.NodeId, elemValue); } } }
3. 关键修改说明
- 数组值解析:判断变量值是否为数组类型,强制转换后遍历输出每个元素,解决原代码中直接输出数组对象显示
null的问题。 - S7-1200自定义数组适配:利用S7-1200 OPC UA服务器的NodeId生成规则,在数组节点Id基础上递增得到元素节点Id,确保自定义数组元素能被正确定位。
- 子节点浏览兼容:保留浏览数组子节点的逻辑,适配自动生成数组的场景,确保两种数组类型都能正常显示元素。
- NodeId优化:将
new NodeId(nextRd2.NodeId.ToString())替换为ExpandedNodeId.ToNodeId,避免字符串解析导致的NodeId错误。
内容的提问来源于stack exchange,提问作者Victor Pieper
相关产品推荐
相关产品推荐

