Safe API — 永不返回 Err 的变体
概述
Rust 默认就用 Result<T, OpcUaError> 处理失败。Safe 变体把 Err(OpcUaError) 折叠为 (StatusCode, T::default), 返回 tuple 而不是 Result, 让批量循环不必每条 ? 或 unwrap, 不打断执行流。
对应规范段: Browse Part 4 §5.8, Read §5.10.2, Write §5.10.4, Call §5.11.2, HistoryRead Part 11 §6.5。
API
| 方法 | 类别 | 读写 | 说明 |
|---|---|---|---|
| ua.browse_safe(node_id, filter) | 浏览 | 读 | 返回 (StatusCode, Vec<OpcUaReference>) |
| ua.read_safe(node_id, attribute) | 读 | 读 | 返回 (StatusCode, Option<DataValue>) |
| ua.write_safe(node_id, value, attribute) | 写 | 写 | 返回 StatusCode |
| ua.call_safe(object_id, method_id, &inputs) | 方法 | 写 | 返回 (StatusCode, Vec<Variant>) |
| ua.read_history_safe(node_id, t0, t1, max) | 历史 | 读 | 返回 (StatusCode, Vec<DataValue>) |
代码示例
use opcua::{DarraOpcUa, StatusCode, Variant, NodeClass, AttributeId};
let ua = DarraOpcUa::new("opc.tcp://localhost:4840")?;
ua.connect()?;
// 1) 浏览 - 节点删除不让全树报错
let (st, refs) = ua.browse_safe("ns=2;s=Boiler1", NodeClass::Unspecified);
if st != StatusCode::Good {
println!("Browse 失败 {:?}", st);
} else {
for r in &refs {
println!("{}", r.browse_name);
}
}
// 2) 批量轮询 - 单点失败不打断
for nid in &node_list {
let (rs, dv) = ua.read_safe(nid, AttributeId::Value);
match (rs, dv) {
(StatusCode::Good, Some(v)) => println!("{} = {:?}", nid, v.value),
_ => println!("{} = <{:?}>", nid, rs),
}
}
// 3) 批量下发参数
for (nid, val) in setpoints {
let rc = ua.write_safe(nid, Variant::from(val), AttributeId::Value);
if rc != StatusCode::Good { eprintln!("写 {} 失败 {:?}", nid, rc); }
}
// 4) 调用方法
let (cst, _outs) = ua.call_safe("ns=2;s=Robot", "ns=2;s=Robot.Home", &[]);
if cst != StatusCode::Good { eprintln!("Home 失败: {:?}", cst); }
// 5) 历史
let (hst, vals) = ua.read_history_safe("ns=2;s=Temp", t0, t1, 5000);
与重试机制的关系
普通 read/write/browse/call/read_history 内部都包了 retry_on_session_fault, session 级故障会自动重连一次。Safe 变体也受益于自动重连, 把"重连后仍失败"的 Err 折叠为 StatusCode。
最佳实践
- UI 主线程一律用 Safe —
?传播到 UI 任务会让线程退出 - 批处理 / 巡检循环用 Safe
- 业务关键路径仍用普通 Result 版本, 用
?显式错误传播
跨语言对照
| C# | Python | Java | C++ | Rust | C |
|---|---|---|---|---|---|
| BrowseSafe | browse_safe | browseSafe | BrowseSafe | browse_safe | DarraUa_Session_BrowseSafe |
| ReadSafe | read_safe | readSafe | ReadSafe | read_safe | DarraUa_Session_ReadSafe |
| WriteSafe | write_safe | writeSafe | WriteSafe | write_safe | DarraUa_Session_WriteSafe |
| CallSafe | call_safe | callSafe | CallSafe | call_safe | DarraUa_Session_CallSafe |
| ReadHistorySafe | read_history_safe | readHistorySafe | ReadHistorySafe | read_history_safe | DarraUa_Session_ReadHistorySafe |