OpcUaNode
OpcUaNode 是 NodeCollection.get 返回的包装对象, 表示远程一个节点. Lazy Load — 只有访问属性才触发 RPC.
前置阅读
- 入口请看 NodeCollection.
- 想避免链式索引的多次 RPC 请用 TranslateBrowsePaths 一次性解析路径.
公共方法
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 标识 (无 RPC) | getNodeId() | String | R | 节点 ID 字符串 |
| 数据 (触发 RPC) | getValue() | OpcUaDataValue | R | 当前值 — 触发 read(Value), 返回需 close |
getDisplayName() | String | R | 显示名 — 触发 read(DisplayName) | |
getBrowseName() | String | R | 浏览名 — 触发 read(BrowseName) | |
getDescription() | String | R | 描述 — 触发 read(Description) | |
getNodeClass() | Enums.NodeClass | R | 节点类别 — 触发 read(NodeClass) | |
getDataTypeNodeId() | String | R | 数据类型 NodeId — 触发 read(DataType) | |
isReadable() | boolean | R | AccessLevel bit0 — 触发 read(AccessLevel) | |
isWritable() | boolean | R | AccessLevel bit1 — 同上 | |
| 子节点 (触发 RPC) | getChildren() | List<Reference> | R | 子节点列表 — 触发 browse |
子节点访问
| 方法 | 返回 | 说明 |
|---|---|---|
node.get(String browseName) | OpcUaNode (找不到返回 null) | 按 BrowseName / DisplayName 取直接子节点 |
node.resolve(String path) | OpcUaNode | 按 BrowsePath 多层解析 |
操作方法
| 方法 | 说明 |
|---|---|
readAttribute(AttributeId attr) | 读指定 Attribute |
write(OpcUaVariant v) | 写 Value |
writeAttribute(AttributeId attr, OpcUaVariant v) | 写指定 Attribute |
call(String parentObjectNodeId, OpcUaVariant... inputs) | 把本节点作为方法调用 |
例子
// 1. 拿节点 (无 RPC)
OpcUaNode temp = ua.nodes.get("ns=2;s=Temperature");
// 2. 读基本信息 (各 1 次 RPC)
System.out.println("NodeId = " + temp.getNodeId());
System.out.println("DisplayName = " + temp.getDisplayName());
System.out.println("DataType = " + temp.getDataTypeNodeId());
System.out.println("NodeClass = " + temp.getNodeClass());
// 3. 读值
try (OpcUaDataValue dv = temp.getValue()) {
System.out.println("Value = " + dv.variant().asDouble() + " °C");
}
// 4. 写
try (OpcUaVariant v = new OpcUaVariant().setDouble(25.5)) {
temp.write(v);
}
// 5. 浏览子节点
for (Reference child : temp.getChildren()) {
System.out.println(" " + child.getBrowseName() + " -> " + child.getNodeId());
}
// 6. 链式访问 (Server.ServerStatus.CurrentTime)
try (OpcUaDataValue time = ua.nodes.server().get("ServerStatus").get("CurrentTime").getValue()) {
long ft = time.variant().asFileTime();
System.out.println("Server time FileTime = " + ft);
}
注意事项
- 所有触发 RPC 的方法都是"读一次返回一次", 不缓存. 想缓存自己存变量
- 链式访问
a.get("b").get("c")每段都是一次 Browse RPC, 总计 N 次 OpcUaDataValue必须 close, 否则泄漏 native 内存- 跨线程使用
OpcUaNode安全 (无状态), 但OpcUaDataValue不要跨线程 node.get(browseName)找不到返回 null (Java 风格), 不抛异常 — 上层显式 null 判断
与 resolve 的对比
// 方式 A: 链式索引 - 4 次 RPC
try (OpcUaDataValue time = ua.nodes.server().get("ServerStatus").get("CurrentTime").getValue()) {
// 4 次 Read: server() (无 RPC) + get("ServerStatus") + get("CurrentTime") + getValue()
// 实际 3 次 Browse + 1 次 Read
}
// 方式 B: 路径解析 - 2 次 RPC
OpcUaNode node = ua.resolve("/Objects/Server/ServerStatus/CurrentTime"); // 1 次
try (OpcUaDataValue time = node.getValue()) { /* 1 次 */ }
// 方式 C: 直接 NodeId - 1 次 RPC
try (OpcUaDataValue time = ua.read("i=2258")) { /* ... */ }
如果路径已知 → 方式 C; 探索式开发 → 方式 A.