状态回调 (C)
C SDK 没有"统一事件聚合通道", 而是把不同事件分别暴露为 回调函数指针:
| 事件类别 | 回调类型 | 注册函数 |
|---|---|---|
| Session 生命周期 | DarraUa_OnSessionStateChange | DarraUa_Session_SetStateCallback |
| 数据变化 (订阅) | DarraUa_OnDataChange | DarraUa_MonitoredItem_SetCallback / DarraUa_Subscription_AddNode |
| 订阅生命周期 | DarraUa_OnSubscriptionState | DarraUa_Subscription_SetStateCallback |
| SDK 日志 | DarraUa_LogCallback | DarraUa_SetLogCallback |
关联
- 数据变化事件请看 Subscription 事件.
- OPC UA 协议事件 (报警 / 条件) 请看 事件订阅.
- 心跳事件本质是周期 Read, 见 KeepAlive.
Session 状态回调
typedef void (*DarraUa_OnSessionStateChange)(
DarraUa_SessionHandle h,
DarraUa_SessionState new_state,
DarraUa_Status status,
void* user_context);
OPCUA_API DarraUa_Status OPCUA_CALL DarraUa_Session_SetStateCallback(
DarraUa_SessionHandle h,
DarraUa_OnSessionStateChange cb,
void* user_context);
| 字段 | 说明 |
|---|---|
| new_state | 新状态 (UA_SESSION_*) |
| status | 关联状态码 (例如重连失败的具体原因) |
| user_context | 注册时传入的上下文指针, 原样回传 |
用法
static void on_state(DarraUa_SessionHandle h, DarraUa_SessionState s,
DarraUa_Status code, void* ctx)
{
(void)h;
const char* tag = (const char*)ctx;
switch (s) {
case UA_SESSION_CONNECTING:
printf("[%s] connecting...\n", tag); break;
case UA_SESSION_CONNECTED:
printf("[%s] connected\n", tag); break;
case UA_SESSION_RECONNECTING:
printf("[%s] reconnecting (last err 0x%08X = %s)\n",
tag, (unsigned)code, DarraUa_StatusName(code));
break;
case UA_SESSION_DISCONNECTED:
printf("[%s] disconnected\n", tag); break;
case UA_SESSION_FAILED:
printf("[%s] FAILED (%s)\n", tag, DarraUa_StatusName(code));
break;
default:
break;
}
}
/* 注册 */
DarraUa_Session_SetStateCallback(h, on_state, (void*)"plant1");
SDK 日志回调
Stack 不落盘日志, 通过回调把日志交给上层:
typedef void (OPCUA_CALL *DarraUa_LogCallback)(
DarraUa_LogLevel level,
const char* category,
const char* message,
void* user_context);
OPCUA_API DarraUa_Status OPCUA_CALL
DarraUa_SetLogCallback(DarraUa_LogCallback callback, void* user_context);
OPCUA_API DarraUa_Status OPCUA_CALL
DarraUa_SetLogLevel(DarraUa_LogLevel minimum_level);
DarraUa_LogLevel:
typedef enum {
UA_LOG_TRACE = 0,
UA_LOG_DEBUG = 1,
UA_LOG_INFO = 2,
UA_LOG_WARN = 3,
UA_LOG_ERROR = 4,
UA_LOG_FATAL = 5
} DarraUa_LogLevel;
用法
static void OPCUA_CALL on_log(DarraUa_LogLevel level,
const char* cat, const char* msg, void* ctx)
{
(void)ctx;
static const char* tag[] = { "T","D","I","W","E","F" };
printf("[%s][%s] %s\n", tag[level], cat ? cat : "", msg ? msg : "");
}
/* 先注册再 Initialize, 这样初始化期日志也能收到 */
DarraUa_SetLogLevel(UA_LOG_INFO);
DarraUa_SetLogCallback(on_log, NULL);
DarraUa_Initialize();
线程模型
回调线程不是调用线程
状态回调与日志回调都在 Stack 内部线程 触发, 与 DarraUa_Session_Connect / _Read 等 API 调用线程不同.
- 共享数据访问必须加锁 / 用原子
- 回调里不要做长时间计算 / 阻塞 IO, 否则会拖慢 Stack 内部调度
- 上层若是 GUI / WPF, 必须 Marshal 到 UI 线程再操作控件
跨平台原子计数示例:
#if defined(_WIN32)
# include <intrin.h>
# define ATOMIC_INC(p) _InterlockedIncrement((volatile long*)(p))
#else
# define ATOMIC_INC(p) __sync_add_and_fetch((p), 1)
#endif
static volatile long g_state_change_count = 0;
static void on_state(DarraUa_SessionHandle h, DarraUa_SessionState s,
DarraUa_Status code, void* ctx)
{
(void)h; (void)s; (void)code; (void)ctx;
ATOMIC_INC(&g_state_change_count);
}
注销回调
调 _SetCallback 时传 NULL 即注销:
DarraUa_Session_SetStateCallback(h, NULL, NULL);
DarraUa_SetLogCallback(NULL, NULL);
注销后正在执行的回调仍会跑完, 之后再不触发.
DarraUa_EventCategory 枚举 (统一事件通道)
C SDK 同时提供"统一事件通道" — 任何 SDK 内部事件先汇集为 DarraUa_EventEntry, 业务可注册 DarraUa_RegisterAnyHandler 一行 hook 全部, 也可注册 6 个 typed handler 仅收特定类别。
typedef enum {
EC_UNKNOWN = 0,
EC_CONNECTED, EC_DISCONNECTED,
EC_RECONNECTING, EC_RECONNECTED,
EC_KEEP_ALIVE, EC_STATE_CHANGED,
EC_SUBSCRIPTION_CREATED, EC_SUBSCRIPTION_LOST,
EC_SUBSCRIPTION_RESTORED,
EC_MONITORED_ITEM_ADDED, EC_MONITORED_ITEM_REMOVED,
EC_SUBSCRIPTION_CLEARED,
EC_DATA_CHANGE, EC_SERVER_EVENT, EC_ALARM,
EC_READ, EC_WRITE, EC_BROWSE,
EC_CALL, EC_HISTORY_READ,
EC_COMMUNICATION_ERROR, EC_SECURITY_ERROR,
EC_PROTOCOL_ERROR,
EC_INFO, EC_DIAGNOSTIC,
/* 节点 / 会话级新增分类 (2026-04-25) */
EC_NODE_REMOVED = 30,
EC_NODE_ACCESS_DENIED = 31,
EC_NODE_TYPE_CHANGED = 32,
EC_SESSION_LOST = 33,
EC_SERVER_STATE_CHANGED = 34,
} DarraUa_EventCategory;
Typed 通道 (语法糖) — 6 个新事件
| 注册函数 | Category | 触发场景 |
|---|---|---|
| DarraUa_RegisterNodeRemovedHandler(ua, cb, ctx) | NodeRemoved | 命中 BadNodeIdUnknown |
| DarraUa_RegisterNodeAccessDeniedHandler(ua, cb, ctx) | NodeAccessDenied | 命中 BadUserAccessDenied |
| DarraUa_RegisterNodeTypeChangedHandler(ua, cb, ctx) | NodeTypeChanged | Write 时命中 BadTypeMismatch |
| DarraUa_RegisterSessionLostHandler(ua, cb, ctx) | SessionLost | Session 失效预警 |
| DarraUa_RegisterServerStateChangedHandler(ua, cb, ctx) | ServerStateChanged | 服务端状态切换 |
| DarraUa_RegisterSubRestoredHandler(ua, cb, ctx) | SubscriptionRestored | 订阅恢复成功 |
回调签名: void cb(const DarraUa_EventEntry* e, void* ctx)。
void on_node_removed(const DarraUa_EventEntry* e, void* ctx) {
printf("⚠ 节点已被服务端删除: %s\n", e->source);
tree_remove(ctx, e->source);
}
void on_session_lost(const DarraUa_EventEntry* e, void* ctx) {
fprintf(stderr, "⚠ 会话失效, 即将重连...\n");
}
DarraUa_RegisterNodeRemovedHandler(ua, on_node_removed, my_tree);
DarraUa_RegisterSessionLostHandler(ua, on_session_lost, NULL);