跳到主要内容

Events — s.Events().*

Session::Events() 返回 SessionEvents&, 是 SDK 内部事件统一通道, 把会话生命周期 / 心跳 / 通讯异常都汇集到一组 std::function 回调字段上, 方便上层 UI 做日志 / 状态栏 / 调试.

与 OPC UA 协议事件区分
  • s.Events().on_* — SDK 内部事件 (本节, 与协议无关)
  • s.SubscribeEvents(...) — OPC UA 协议事件 (Alarms & Conditions), 见 事件订阅

SessionEvents 字段

字段式 std::function 回调, 直接赋值即可:

字段签名触发时机
on_connectedvoid(std::string const& url)首次连接成功
on_disconnectedvoid(std::string const& url, Status reason)主动 / 被动断开
on_reconnectingvoid(std::string const& url)检测到断线, 启动自动重连
on_reconnectedvoid(std::string const& url)自动重连成功
on_keepalivevoid(int64_t server_time_ft, Status server_status)KeepAlive 心跳成功
on_state_changedvoid(SessionState old, SessionState new, Status reason)任何 SessionState 变化
on_communication_errorvoid(Status code, std::string const& cat, std::string const& msg)通讯失败 (KeepAlive 失败 / Read 异常等)
on_anyvoid(OpcUaEventEntry const&)统一事件通道 (全部事件汇集)

OpcUaEventCategory 枚举

OpcUaEventEntry::category 字段, 完整清单:

enum class OpcUaEventCategory : int {
Unknown = 0,
Connected, Disconnected, Reconnecting, Reconnected,
KeepAlive, StateChanged,
SubscriptionCreated, SubscriptionLost, SubscriptionRestored,
MonitoredItemAdded, MonitoredItemRemoved, SubscriptionCleared,
DataChange, ServerEvent, Alarm,
Read, Write, Browse, Call, HistoryRead,
CommunicationError, SecurityError, ProtocolError,
Info, Diagnostic
};

OpcUaEventSeverity 枚举

enum class OpcUaEventSeverity : int {
Trace = 0, Debug = 1, Info = 2, Warn = 3, Error = 4, Fatal = 5
};

OpcUaEventEntry 结构

struct OpcUaEventEntry {
OpcUaEventCategory category = OpcUaEventCategory::Unknown;
OpcUaEventSeverity severity = OpcUaEventSeverity::Info;
std::string source;
std::string message;
Status status = Status::Good;
};

用法示例

通用日志钩子 (一行打印 SDK 内所有事件)

s.Events().on_any = [](OpcUaEventEntry const& e) {
std::cout << "[" << static_cast<int>(e.severity) << "] "
<< "[" << static_cast<int>(e.category) << "] "
<< e.source << ": " << e.message
<< " (0x" << std::hex << static_cast<uint32_t>(e.status) << ")\n";
};

调试期非常省事 — 不订阅具体事件, 一个 lambda 看全部.

状态栏绑定 (Qt 示例)

s.Events().on_state_changed = [&](SessionState old_s, SessionState new_s, Status reason) {
QMetaObject::invokeMethod(qApp, [&, new_s]() {
statusLabel->setText(QString("State=%1").arg(static_cast<int>(new_s)));
statusLabel->setStyleSheet(new_s == SessionState::Connected
? "color:green" : "color:red");
});
};

错误统计

std::atomic<int> error_count{0};
s.Events().on_communication_error = [&](Status, std::string const&, std::string const&) {
error_count.fetch_add(1);
};

KeepAlive 心跳输出服务端时间

constexpr int64_t WIN_EPOCH_OFFSET = 116444736000000000LL;
s.Events().on_keepalive = [](int64_t ft, Status status) {
time_t t = (ft - WIN_EPOCH_OFFSET) / 10000000;
std::cout << "KeepAlive @ " << t
<< " status=0x" << std::hex << static_cast<uint32_t>(status) << "\n";
};

线程模型

必须切回 UI 线程

回调在 C 层 Publish 线程KeepAlive 线程上调用, 不在调用方线程. UI 操作必须切回 UI 线程 (Qt QMetaObject::invokeMethod / WPF Dispatcher 等), 否则跨线程 UI 操作崩溃.

不要在事件里执行长操作 (>100 ms), 否则会阻塞 Publish 队列 / KeepAlive.

回调里的异常会被 SDK try-catch 吃掉 (避免 native 崩溃), 自己加 try-catch 看具体异常.

下一步