跳到主要内容

状态回调 (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);

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);

注销后正在执行的回调仍会跑完, 之后再不触发.

下一步