OpcUaNodeId — 强类型 NodeId
概述
OPC UA 节点 ID 在 SDK 里默认用字符串编码 ("ns=2;s=Foo" / "i=84" / "g={GUID}" / "b=base64")。字符串方便日志和配置文件, 但在编程时容易踩:
- 拼错前缀 (
s=写成S=) - ns 索引漂移 (服务端动态注册命名空间)
- GUID / Opaque 类型识别
OpcUaNodeId 是 readonly record struct, 显式带 NamespaceIndex / IdType / Identifier 三件套, 与字符串 100% 互转。
对应规范段: Part 6 §5.2.2 (NodeId 编码)。
API
| 成员 | 类别 | 读写 | 说明 |
|---|---|---|---|
| OpcUaNodeId.Null | 静态 | 读 | 空哨兵 (i=0) |
| OpcUaNodeId.FromNumeric(ns, uint id) | 工厂 | — | 构造数字型 |
| OpcUaNodeId.FromString(ns, string id) | 工厂 | — | 构造字符串型 |
| OpcUaNodeId.FromGuid(ns, Guid id) | 工厂 | — | 构造 GUID 型 |
| OpcUaNodeId.FromOpaque(ns, byte[] id) | 工厂 | — | 构造不透明型 |
| OpcUaNodeId.Parse(string s) | 静态 | — | 解析字符串, 失败 throw FormatException |
| OpcUaNodeId.TryParse(s, out nid) | 静态 | — | 安全解析 |
| nid.ToString() | 方法 | — | 序列化为 OPC UA 标准字符串 |
| nid.NamespaceIndex | 属性 | 读 | 命名空间索引 (ushort) |
| nid.IdType | 属性 | 读 | NodeIdType 枚举 |
| nid.Identifier | 属性 | 读 | object 实际类型按 IdType |
| nid.IsNull | 属性 | 读 | 是否为空 |
相关结构:
public enum NodeIdType
{
Numeric = 0, // i= 无符号 32 位整数 (最常见)
String = 1, // s= UTF-8 字符串
Guid = 2, // g= GUID {xxxx-...}
Opaque = 3 // b= base64 字节数组
}
代码示例
using DarraOpcUa_Client;
// 1) 工厂构造
var n1 = OpcUaNodeId.FromNumeric(0, 84); // i=84 (Root)
var n2 = OpcUaNodeId.FromString(2, "Boiler1.Temp"); // ns=2;s=Boiler1.Temp
var n3 = OpcUaNodeId.FromGuid(3, Guid.NewGuid()); // ns=3;g={...}
var n4 = OpcUaNodeId.FromOpaque(4, new byte[] {1,2,3});
// 2) 解析字符串
var n5 = OpcUaNodeId.Parse("ns=2;s=Demo.Tag");
Console.WriteLine($"ns={n5.NamespaceIndex}, type={n5.IdType}, id={n5.Identifier}");
if (OpcUaNodeId.TryParse("ns=99;i=foo", out var n6)) // false (foo 不是 uint)
Console.WriteLine("ok");
else
Console.WriteLine("invalid");
// 3) 序列化回字符串
string s = n2.ToString(); // "ns=2;s=Boiler1.Temp"
// 4) 与 SDK 互转 — SDK 接口仍接受字符串, ToString() 直接喂
using var dv = ua.Read(n2.ToString());
// 5) 比较 / 字典 key (record struct 自动 Equals / GetHashCode)
var dict = new Dictionary<OpcUaNodeId, string>();
dict[n2] = "锅炉一温度";
// 6) Null 哨兵
if (n2 == OpcUaNodeId.Null) Console.WriteLine("空");
Console.WriteLine($"IsNull={OpcUaNodeId.Null.IsNull}");
字符串编码规则
| 编码 | 说明 |
|---|---|
| i=NNN | 数字型, ns=0 时省略 ns= 前缀 |
| ns=N;i=NNN | 数字型, ns≠0 |
| ns=N;s=XXX | 字符串型, XXX 是 UTF-8 |
| ns=N;g={GUID} | GUID, 大括号包裹标准格式 |
| ns=N;b=base64== | 字节数组, base64 无 padding |
最佳实践
- 配置文件存字符串 — 持久化用
ToString(), 加载用Parse() - 代码内部用强类型 — 字典 key / Equals 比较等场景, 强类型更安全
- Numeric 优先 — i=NNN 最快 (uint 比较), 字符串型有 hash 开销
- 不要拼字符串 —
"ns=" + ns + ";s=" + id容易出错, 用FromString(ns, id).ToString() - TryParse 先于 Parse — 用户输入永远先 TryParse, 异常路径少写
跨语言对照
| C# | Python | Java | C++ | Rust | C |
|---|---|---|---|---|---|
| OpcUaNodeId | OpcUaNodeId | OpcUaNodeId | OpcUaNodeId | OpcUaNodeId | DarraUa_NodeId |
| Parse | parse | parse | Parse | parse | DarraUa_NodeId_Parse |
| FromNumeric | from_numeric | fromNumeric | FromNumeric | from_numeric | DarraUa_NodeId_FromNumeric |
| FromString | from_string | fromString | FromString | from_string | DarraUa_NodeId_FromString |