跳到主要内容

Read / Write

前置阅读

read

pub fn read(&self, node_id: &str) -> Result<DataValue, OpcUaError>;

读单节点的 Value 属性.

参数类型说明
node_id&strNodeId 字符串 (i=2258 / ns=2;s=...)

返回 DataValue (实现 Drop, 离开作用域自动释放 native).

错误: OpcUaError 携带 BAD_NODE_ID_UNKNOWN / BAD_NOT_READABLE / BAD_COMMUNICATION_ERROR / ...

let dv = s.read("ns=2;s=Temperature")?;
println!("Temperature = {} °C", dv.variant().try_get_f64().unwrap_or(0.0));

read_attr

pub fn read_attr(&self, node_id: &str, attr: AttributeId)
-> Result<DataValue, OpcUaError>;

读单节点指定 AttributeId.

use darra_opcua::AttributeId;

// 读 DisplayName
let dn = s.read_attr("ns=2;s=Temperature", AttributeId::DisplayName)?;
println!("name = {}", dn.variant().try_get_string().unwrap_or_default());

read_many

pub fn read_many(&self, node_ids: &[&str], attr: AttributeId)
-> Result<Vec<Option<DataValue>>, OpcUaError>;

一次 RPC 批量读多个节点的同一 Attribute. 返回 Vec<Option<DataValue>> — 长度等于 node_ids.len(), 失败项为 None.

let nodes = ["ns=2;s=T1", "ns=2;s=T2", "ns=2;s=T3"];
let values = s.read_many(&nodes, AttributeId::Value)?;
for (nid, dv) in nodes.iter().zip(values.iter()) {
match dv {
Some(d) => println!("{} = {:?}", nid, d.variant()),
None => println!("{} FAILED", nid),
}
}

性能: 100 个节点约 1 次 RPC (~10 ms), 比循环 read 快 ~50x (省 99 次 RPC 往返).


write

pub fn write(&self, node_id: &str, value: &Variant)
-> Result<StatusCode, OpcUaError>;

写单节点 Value 属性.

use darra_opcua::Variant;

let mut v = Variant::new();
v.set_f64(42.5);
let st = s.write("ns=2;s=Setpoint", &v)?;
if !st.is_good() {
println!("Write failed: {}", st);
}

返回 StatusCode: GOOD / BAD_NOT_WRITABLE / BAD_TYPE_MISMATCH / BAD_USER_ACCESS_DENIED / ...

业务级失败不返回 Err

write 不返回 Err (除非 transport 级错误), 业务级失败通过 Ok(StatusCode) 返回值判断. 必须检查 st.is_good(), 不要假定一定 Good.

write_attr

pub fn write_attr(&self, node_id: &str, attr: AttributeId, value: &Variant)
-> Result<StatusCode, OpcUaError>;

写指定 Attribute (一般只用 Value 默认, 通过 write 调用).


Variant 构造速查

use darra_opcua::Variant;

let mut v = Variant::new();
v.set_bool(true);
v.set_byte(255);
v.set_i16(42);
v.set_i32(42);
v.set_u32(42);
v.set_i64(42);
v.set_f32(3.14);
v.set_f64(3.14);
v.set_string("hello");
v.set_datetime(std::time::SystemTime::now());

详见 Variant 数据类型.


DataValue 字段速查

方法返回类型说明
variant()Variant实际值 (借用, 不释放)
status()StatusCodeGood / Bad* / Uncertain*
is_good()bool== status().is_good()
source_timestamp()Option<SystemTime>数据源时间戳
server_timestamp()Option<SystemTime>服务端打的时间戳
let dv = s.read("ns=2;s=T1")?;
if !dv.is_good() {
println!("Bad value: {}", dv.status());
return Ok(());
}
let value = dv.variant().try_get_f64().unwrap_or(0.0);
println!("{} @ {:?}", value, dv.source_timestamp());

AttributeId 常用值

Id名称说明
1NodeId节点 ID
2NodeClass节点类别
3BrowseName浏览名
4DisplayName显示名
5Description描述
13Value变量值 (默认)
14DataType数据类型 NodeId
17AccessLevel访问权限位
20Historizing是否在记录历史
21ExecutableMethod 是否可执行

最佳实践

  • 批量优先: 监控 100+ Tag 用 read_many, 不要循环 read (省 N-1 次 RPC)
  • Drop 自动释放: DataValue 离开作用域 Drop 自动释放 native, 不需要手动 dispose
  • write 检查返回值: 不要假定一定 Good, 用 st.is_good() / st.is_bad()
  • try_get_xxx: Variant 用 try_get_xxx() -> Option<T> 防止类型不匹配 panic
  • 实时变化用订阅: 不要 1 秒一次轮询 read, 改用 Subscription (服务端推送)

下一步