Safe API — 永不异常变体
概述
C 没有异常机制, 所有调用都返回 DarraUa_StatusCode 或 Bad* 错误码。Safe 变体相当于普通版本, 但内部对网络层 / 反射层失败做兜底, 不让 BadInternalError (0x80020000) 之类的异常码暴露给业务侧, 把所有失败统一为 Bad* 标准状态码。
对应规范段: Browse Part 4 §5.8, Read §5.10.2, Write §5.10.4, Call §5.11.2, HistoryRead Part 11 §6.5。
API
| 函数 | 类别 | 读写 | 说明 |
|---|---|---|---|
| DarraUa_Session_BrowseSafe(ua, nid, filter, &out_refs, &out_count) | 浏览 | 读 | 返回 StatusCode, refs 通过 out 参数 |
| DarraUa_Session_ReadSafe(ua, nid, attr, &out_value) | 读 | 读 | 返回 StatusCode, value 通过 out 参数 |
| DarraUa_Session_WriteSafe(ua, nid, value, attr) | 写 | 写 | 返回 StatusCode |
| DarraUa_Session_CallSafe(ua, obj, mid, inputs, n_in, &out_outs, &out_count) | 方法 | 写 | 返回 StatusCode |
| DarraUa_Session_ReadHistorySafe(ua, nid, t0, t1, max, &out_vals, &out_count) | 历史 | 读 | 返回 StatusCode |
释放规则: out_refs / out_value / out_outs / out_vals 必须用对应的 Free* 函数释放。
代码示例
#include <opcua.h>
#include <stdio.h>
DarraUa_Session* ua;
DarraUa_StatusCode rc = DarraUa_Session_New("opc.tcp://localhost:4840", &ua);
DarraUa_Session_Connect(ua);
// 1) 浏览 - 节点删除不让全树报错
DarraUa_Reference* refs = NULL;
size_t n = 0;
DarraUa_StatusCode st = DarraUa_Session_BrowseSafe(ua, "ns=2;s=Boiler1",
NODE_CLASS_UNSPECIFIED, &refs, &n);
if (st != UA_GOOD) {
fprintf(stderr, "Browse 失败 0x%08X\n", st);
} else {
for (size_t i = 0; i < n; ++i)
printf("%s\n", refs[i].browse_name);
DarraUa_FreeReferences(refs, n);
}
// 2) 批量轮询 - 单点失败不打断
for (size_t i = 0; i < node_count; ++i) {
DarraUa_DataValue* dv = NULL;
DarraUa_StatusCode rs = DarraUa_Session_ReadSafe(ua, node_list[i],
ATTR_VALUE, &dv);
if (rs == UA_GOOD && dv) {
printf("%s = ...\n", node_list[i]);
DarraUa_FreeDataValue(dv);
} else {
printf("%s = <0x%08X>\n", node_list[i], rs);
}
}
// 3) 写
for (size_t i = 0; i < setpoint_count; ++i) {
DarraUa_Variant v;
DarraUa_Variant_SetDouble(&v, setpoints[i].value);
DarraUa_StatusCode rc = DarraUa_Session_WriteSafe(ua, setpoints[i].nid,
&v, ATTR_VALUE);
if (rc != UA_GOOD) fprintf(stderr, "写失败\n");
}
DarraUa_Session_Free(ua);
与重试机制的关系
普通 Read/Write/Browse/Call/ReadHistory 内部都包了 RetryOnSessionFault, session 级故障会自动重连一次。Safe 变体也受益于自动重连, 把"重连后仍失败"的内部异常折叠为标准 Bad* StatusCode。
最佳实践
- 主线程一律用 Safe — 不必处理
BadInternalError这种非标准状态码 - 批处理用 Safe — 单点失败不打断
- 内存释放: out 参数都需对应
Free*函数
跨语言对照
| 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 |