状态回调 (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);
DARRA_OPCUA_API DarraUa_Status DARRA_OPCUA_CALL DarraUa_Session_SetStateCallback(
DarraUa_SessionHandle h,
DarraUa_OnSessionStateChange cb,
void* user_context);
| 字段 | 说明 |
|---|---|
new_state | 新状态 (DARRA_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 DARRA_UA_SESSION_CONNECTING:
printf("[%s] connecting...\n", tag); break;
case DARRA_UA_SESSION_CONNECTED:
printf("[%s] connected\n", tag); break;
case DARRA_UA_SESSION_RECONNECTING:
printf("[%s] reconnecting (last err 0x%08X = %s)\n",
tag, (unsigned)code, DarraUa_StatusName(code));
break;
case DARRA_UA_SESSION_DISCONNECTED:
printf("[%s] disconnected\n", tag); break;
case DARRA_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 (DARRA_OPCUA_CALL *DarraUa_LogCallback)(
DarraUa_LogLevel level,
const char* category,
const char* message,
void* user_context);
DARRA_OPCUA_API DarraUa_Status DARRA_OPCUA_CALL
DarraUa_SetLogCallback(DarraUa_LogCallback callback, void* user_context);
DARRA_OPCUA_API DarraUa_Status DARRA_OPCUA_CALL
DarraUa_SetLogLevel(DarraUa_LogLevel minimum_level);
DarraUa_LogLevel:
typedef enum {
DARRA_UA_LOG_TRACE = 0,
DARRA_UA_LOG_DEBUG = 1,
DARRA_UA_LOG_INFO = 2,
DARRA_UA_LOG_WARN = 3,
DARRA_UA_LOG_ERROR = 4,
DARRA_UA_LOG_FATAL = 5
} DarraUa_LogLevel;
用法
static void DARRA_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(DARRA_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);
注销后正在执行的回调仍会跑完, 之后再不触发.