跳到主要内容

状态回调 (C)

C SDK 没有"统一事件聚合通道", 而是把不同事件分别暴露为 回调函数指针:

事件类别回调类型注册函数
Session 生命周期DarraUa_OnSessionStateChangeDarraUa_Session_SetStateCallback
数据变化 (订阅)DarraUa_OnDataChangeDarraUa_MonitoredItem_SetCallback / DarraUa_Subscription_AddNode
订阅生命周期DarraUa_OnSubscriptionStateDarraUa_Subscription_SetStateCallback
SDK 日志DarraUa_LogCallbackDarraUa_SetLogCallback
关联

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)NodeTypeChangedWrite 时命中 BadTypeMismatch
DarraUa_RegisterSessionLostHandler(ua, cb, ctx)SessionLostSession 失效预警
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);

详见 自动重连订阅自恢复

下一步