Safe API — 永不抛异常变体
概述
UI 主线程 / 批处理不希望被一条 RPC 失败抛出的 OpcUaException 中断, SDK 为常用调用各提供一个 Safe 版本 — 任何异常折叠为 StatusCode, 调用方用 Result 包装类判读。
对应规范段: Browse Part 4 §5.8, Read §5.10.2, Write §5.10.4, Call §5.11.2, HistoryRead Part 11 §6.5。
API
| 方法 | 类别 | 读写 | 说明 |
|---|---|---|---|
| ua.browseSafe(nodeId, filter) | 浏览 | 读 | 返回 BrowseResult { status, references } |
| ua.readSafe(nodeId, attribute) | 读 | 读 | 返回 ReadResult { status, value }, 失败时 value=null |
| ua.writeSafe(nodeId, value, attribute) | 写 | 写 | 返回 StatusCode |
| ua.callSafe(objectId, methodId, inputs) | 方法 | 写 | 返回 CallResult { status, outputs } |
| ua.readHistorySafe(nodeId, t0, t1, max) | 历史 | 读 | 返回 HistoryResult { status, values } |
代码示例
import com.darra.opcua.*;
try (DarraOpcUa ua = new DarraOpcUa("opc.tcp://localhost:4840")) {
ua.connect();
// 1) 浏览 - 节点删除不让全树报错
BrowseResult br = ua.browseSafe("ns=2;s=Boiler1", NodeClass.Unspecified);
if (br.status != StatusCode.Good) {
System.out.println("Browse 失败 " + br.status);
} else {
for (OpcUaReference r : br.references)
System.out.println(r.browseName);
}
// 2) 批量轮询 - 单点失败不打断
for (String nid : nodeList) {
ReadResult rr = ua.readSafe(nid, AttributeId.Value);
if (rr.status == StatusCode.Good && rr.value != null) {
try (DataValue dv = rr.value) {
System.out.println(nid + " = " + dv.value);
}
} else {
System.out.println(nid + " = <" + rr.status + ">");
}
}
// 3) 批量下发参数
for (Map.Entry<String, Object> sp : setpoints.entrySet()) {
StatusCode rc = ua.writeSafe(sp.getKey(), new Variant(sp.getValue()));
if (rc != StatusCode.Good)
System.out.println("写 " + sp.getKey() + " 失败 " + rc);
}
// 4) 调用方法
CallResult cr = ua.callSafe("ns=2;s=Robot", "ns=2;s=Robot.Home");
if (cr.status != StatusCode.Good)
JOptionPane.showMessageDialog(null, "Home 失败: " + cr.status);
// 5) 历史读
HistoryResult hr = ua.readHistorySafe("ns=2;s=Temp", t0, t1, 5000);
}
与重试机制的关系
普通 read/write/browse/call/readHistory 内部都包了 retryOnSessionFault, session 级故障会自动重连一次。Safe 变体也受益于自动重连, 把"重连后仍失败"的异常折叠为 StatusCode。
最佳实践
- UI 主线程 (Swing/JavaFX) 一律用 Safe — 异常逃逸进 EDT 会让 UI 卡死
- 批处理 / 巡检循环用 Safe
- 业务关键路径仍用普通版本
跨语言对照
| 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 |