如何用C#通过OPC UA获取Siemens TP1200 Comfort节点的值
解决方案
首先,OPC UA客户端不会自动加载根节点的所有子节点——你得主动发起**浏览(Browse)**操作,或者直接通过节点路径/ID来定位目标变量。西门子TP1200 Comfort的OPC UA服务器里,HMI标签(变量)都在特定命名空间下,下面给你具体步骤和代码:
1. 先确认目标节点的命名空间索引
西门子HMI的OPC UA命名空间一般是urn:Siemens:SimaticHMI,你可以在连接成功后通过Session获取它的索引:
var namespaceIndex = session.NamespaceUris.FindIndex("urn:Siemens:SimaticHMI");
如果不确定命名空间字符串,可以先遍历所有命名空间:
for (int i = 0; i < session.NamespaceUris.Count; i++) { Console.WriteLine($"索引{i}: {session.NamespaceUris[i]}"); }
2. 两种读取标签值的方法
方法一:通过节点路径读取
如果知道HMI标签的路径(比如Objects/Simatic HMI/TP1200/Tags/MyTag),可以用TranslateBrowsePathsToNodeIdsAsync把路径转成NodeId,再读值:
// 构造浏览路径 var browsePath = new BrowsePath(session.NamespaceUris.GetIndex("http://opcfoundation.org/UA/"), new[] { new QualifiedName("Objects", 0), new QualifiedName("Simatic HMI", namespaceIndex), new QualifiedName("TP1200", namespaceIndex), new QualifiedName("Tags", namespaceIndex), new QualifiedName("MyTag", namespaceIndex) }); // 转换路径到NodeId var translateResult = await session.TranslateBrowsePathsToNodeIdsAsync(null, new[] { browsePath }); if (translateResult.Results[0].StatusCode.IsGood) { var nodeId = translateResult.Results[0].TargetId.NodeId; // 读取值 var readResult = await session.ReadValueAsync(nodeId); Console.WriteLine($"标签值:{readResult.Value}"); }
方法二:直接通过NodeId读取
如果已经知道标签的NodeId(比如从HMI的OPC UA配置里获取,格式一般是ns=2;s=Tags.MyTag),可以直接读:
var nodeId = new NodeId("s=Tags.MyTag", namespaceIndex); // 替换成你的标签NodeId var value = await session.ReadValueAsync(nodeId); Console.WriteLine($"当前值:{value.Value}");
3. 为什么根节点看不到子节点?
根节点(RootFolder)的子节点是Objects、Types、Views这些顶层节点,但UA客户端默认不会自动加载它们的子节点。如果你要浏览整个节点树,可以用BrowseAsync方法:
var browseResult = await session.BrowseAsync(null, null, ObjectIds.RootFolder, 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Variable | (uint)NodeClass.Object); foreach (var reference in browseResult.References) { Console.WriteLine($"根节点子节点:{reference.DisplayName.Text}"); }
不过对于HMI标签,直接用路径/ID读取比全树浏览效率高得多。
4. 封装自己的GetNodeValue方法
如果你想要类似GetNodeValue(NodeName)的方法,可以自己封装一个:
public async Task<DataValue> GetNodeValueAsync(Session session, string tagName, int namespaceIndex) { var browsePath = new BrowsePath(session.NamespaceUris.GetIndex("http://opcfoundation.org/UA/"), new[] { new QualifiedName("Objects", 0), new QualifiedName("Simatic HMI", namespaceIndex), new QualifiedName("TP1200", namespaceIndex), new QualifiedName("Tags", namespaceIndex), new QualifiedName(tagName, namespaceIndex) }); var translateResult = await session.TranslateBrowsePathsToNodeIdsAsync(null, new[] { browsePath }); if (!translateResult.Results[0].StatusCode.IsGood) throw new Exception($"无法找到节点:{tagName}"); return await session.ReadValueAsync(translateResult.Results[0].TargetId.NodeId); }
调用的时候:
var tagValue = await GetNodeValueAsync(session, "MyTag", namespaceIndex); Console.WriteLine(tagValue.Value);
内容的提问来源于stack exchange,提问作者Álvaro García
相关产品推荐
相关产品推荐

