跳到主要内容

Node

NodeNodeCollection 返回的轻量值对象, 表示远程一个节点. Lazy Load — 只有访问属性才触发 RPC.

前置阅读

公共方法

类别属性类型访问说明
标识 (无 RPC)node_id()&strR节点 ID 字符串
数据 (触发 RPC)value()Result<DataValue, OpcUaError>R当前值 — 触发 Read(Value)
display_name()Option<String>R显示名 — 触发 Read(DisplayName)
browse_name()Option<String>R浏览名 — 触发 Read(BrowseName)
description()Option<String>R描述 — 触发 Read(Description)
node_class()NodeClassR节点类别 — 触发 Read(NodeClass)
is_readable()boolRAccessLevel bit0 — 触发 Read(AccessLevel)
is_writable()boolRAccessLevel bit1 — 触发 Read(AccessLevel)
子节点 (触发 RPC)children()Result<Vec<Reference>, OpcUaError>R子节点列表 — 触发 Browse
child(browse_name)Option<Node>R按 BrowseName 取子节点 — 触发 Browse

公共方法

方法说明
read_attribute(attr)读指定 Attribute, 返回 Result<DataValue, OpcUaError>
write(&Variant)写 Value, 返回 Result<StatusCode, OpcUaError>
write_attribute(attr, &Variant)写指定 Attribute
resolve(path)相对本节点解析 BrowsePath, 返回 Option<Node>
call(parent_obj_id, &[Variant])当本节点是 Method, 调用之

例子

use darra_opcua::Variant;

// 1. 拿节点 (无 RPC)
let temp = s.nodes().get("ns=2;s=Temperature");

// 2. 读基本信息 (各 1 次 RPC)
println!("NodeId = {}", temp.node_id());
println!("DisplayName = {:?}", temp.display_name());
println!("NodeClass = {:?}", temp.node_class());

// 3. 读值
let dv = temp.value()?;
let val = dv.variant().try_get_f64().unwrap_or(0.0);
println!("Value = {} °C @ {:?}", val, dv.source_timestamp());

// 4. 写
let mut v = Variant::new();
v.set_f64(25.5);
temp.write(&v)?;

// 5. 浏览子节点
for child in temp.children()? {
println!(" {} -> {}", child.browse_name, child.node_id);
}

// 6. 链式访问 (Server.ServerStatus.CurrentTime)
if let Some(status) = s.nodes().server().child("ServerStatus") {
if let Some(time) = status.child("CurrentTime") {
println!("Server time = {:?}", time.value()?.source_timestamp());
}
}

Node 与 Reference 的区别

Reference 是 Browse 返回的字段快照 (BrowseName / DisplayName / NodeId / NodeClass), 不挂 Session 句柄. 通过 to_node(&session) 转可操作的 Node:

use darra_opcua::NodeClass;

for r in s.browse("ns=2;s=Boiler1", NodeClass::Unspecified)? {
let node = r.to_node(&s);
let dv = node.value()?;
println!("{} = {:?}", r.browse_name, dv.variant());
}

注意事项

  • 所有触发 RPC 的方法都是"读一次返回一次", 不缓存. 想缓存自己存变量
  • 链式访问 node.child("a").unwrap().child("b").unwrap() 每段都是一次 Browse RPC, 总计 N 次. 如果路径已知, 用 Session::resolve() / translate_browse_paths() 一次性解析更快
  • DataValue 离开作用域自动 Drop, 不需要手动释放
  • 跨线程使用 Node 安全 (Send + Sync)

与 Resolve 的对比

// 方式 A: 链式 child - 4 次 RPC
let time = s.nodes().server()
.child("ServerStatus").unwrap()
.child("CurrentTime").unwrap()
.value()?;
// 1次 2次 3次 4次 Read

// 方式 B: 路径解析 - 2 次 RPC
let node = s.resolve("/Objects/Server/ServerStatus/CurrentTime")?.unwrap(); // 1 次
let time = node.value()?; // 1 次

// 方式 C: 直接 NodeId - 1 次 RPC
let time = s.read("i=2258")?;

如果路径已知 → 方式 C; 探索式开发 → 方式 A.

下一步