Read / Write
前置阅读
- 数据类型 Variant 和 DataValue 是所有读写的基础.
- 高频访问相同节点请用 register_nodes 加速.
- 实时变化通知用 Subscription 而不是循环 read.
read
pub fn read(&self, node_id: &str) -> Result<DataValue, OpcUaError>;
读单节点的 Value 属性.
| 参数 | 类型 | 说明 |
|---|---|---|
node_id | &str | NodeId 字符串 (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() | StatusCode | Good / 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 | 名称 | 说明 |
|---|---|---|
| 1 | NodeId | 节点 ID |
| 2 | NodeClass | 节点类别 |
| 3 | BrowseName | 浏览名 |
| 4 | DisplayName | 显示名 |
| 5 | Description | 描述 |
| 13 | Value | 变量值 (默认) |
| 14 | DataType | 数据类型 NodeId |
| 17 | AccessLevel | 访问权限位 |
| 20 | Historizing | 是否在记录历史 |
| 21 | Executable | Method 是否可执行 |
最佳实践
- 批量优先: 监控 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(服务端推送)