跳到主要内容

Safe API — 永不异常变体

概述

C 没有异常机制, 所有调用都返回 DarraUa_StatusCodeBad* 错误码。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#PythonJavaC++RustC
BrowseSafebrowse_safebrowseSafeBrowseSafebrowse_safeDarraUa_Session_BrowseSafe
ReadSaferead_safereadSafeReadSaferead_safeDarraUa_Session_ReadSafe
WriteSafewrite_safewriteSafeWriteSafewrite_safeDarraUa_Session_WriteSafe
CallSafecall_safecallSafeCallSafecall_safeDarraUa_Session_CallSafe
ReadHistorySaferead_history_safereadHistorySafeReadHistorySaferead_history_safeDarraUa_Session_ReadHistorySafe

下一步