Events — ua.events.*
OpcUaSession.events 是 SDK 内部事件统一通道, 把会话生命周期 / 订阅生命周期 / 数据变化 / 通讯异常都汇集到一个对象上, 方便上层 UI 做日志 / 状态栏 / 调试.
Java 没有原生 event 关键字, 用 List<Consumer<...>> (CopyOnWriteArrayList) 替代 — 用户直接 add(consumer) / remove(consumer).
ua.events.*— SDK 内部事件 (本节, 与协议无关)ua.subscribeEvents(...)— OPC UA 协议事件 (Alarms & Conditions), 见 事件订阅
完整事件清单
会话生命周期
events.onConnected(ConnectionEventArgs)— 首次连接成功events.onDisconnected(ConnectionEventArgs)— 主动 / 被动断开events.onReconnecting(ConnectionEventArgs)— 检测到断线, 启动自动重连events.onReconnected(ConnectionEventArgs)— 自动重连成功events.onKeepAlive(KeepAliveEventArgs)— KeepAlive 心跳成功events.onStateChanged(SessionStateChangedEventArgs)— 任何 SessionState 变化
异常
events.onCommunicationError(CommunicationErrorEventArgs)— 通讯失败 (KeepAlive 失败 / Read 异常等)events.onSubscriptionLost(SubscriptionLostEventArgs)— 订阅被服务端清理
统一通道
events.onAny(OpcUaEventEntry)— 所有事件汇集 (Connected / Disconnected / DataChange / Read / Write / Browse / Call / HistoryRead / Alarm / ...)
OpcUaEventEntry 字段: category (Enums.OpcUaEventCategory) + severity (Enums.OpcUaEventSeverity) + source + message + statusCode + timestampUtc + original.
OpcUaEventCategory 枚举
OpcUaEventEntry.category 字段, 完整清单:
Unknown,
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,
// 节点 / 会话级新增分类 (2026-04-25)
NodeRemoved = 30, // 服务端删除某节点 (BadNodeIdUnknown)
NodeAccessDenied = 31, // 当前用户对该节点权限不足
NodeTypeChanged = 32, // 节点 DataType 变了 (BadTypeMismatch)
SessionLost = 33, // Session 失效 (重连前预警)
ServerStateChanged = 34 // 服务端 Running → Failed/Shutdown 等
Typed 通道 (语法糖) — 6 个新事件
除了 onAny 统一通道, 6 个高频 Category 提供专属事件:
| 事件 | Category | 触发场景 |
|---|---|---|
| events.onNodeRemoved | NodeRemoved | 命中 BadNodeIdUnknown |
| events.onNodeAccessDenied | NodeAccessDenied | 命中 BadUserAccessDenied |
| events.onNodeTypeChanged | NodeTypeChanged | Write 时命中 BadTypeMismatch |
| events.onSessionLost | SessionLost | Session 失效预警 |
| events.onServerStateChanged | ServerStateChanged | 服务端状态切换 |
| events.onSubscriptionRestored | SubscriptionRestored | 订阅恢复成功 |
每个 typed 事件回调签名是 Consumer<OpcUaEventEntry>。
ua.events.onNodeRemoved(e -> {
System.out.println("⚠ 节点已被服务端删除: " + e.source);
treeView.removeNode(e.source);
});
ua.events.onNodeAccessDenied(e ->
System.out.println("⛔ 权限不足: " + e.source));
ua.events.onSessionLost(e ->
statusBar.setText("⚠ 会话失效, 即将重连..."));
ua.events.onServerStateChanged(e ->
System.out.println("服务端状态: " + e.message));
ua.events.onSubscriptionRestored(e ->
System.out.println("订阅已恢复: " + e.source));
OpcUaEventSeverity 枚举
Trace, Debug, Info, Warn, Error, Fatal
用法示例
通用日志钩子 (一行打印 SDK 内所有事件)
ua.events.onAny.add(e ->
System.out.printf("[%s] [%s] %s: %s (%s)%n",
e.severity, e.category, e.source, e.message, e.statusCode));
调试期非常省事 — 不订阅具体事件, 一行 hook 看全部.
状态栏绑定 (Swing)
import javax.swing.SwingUtilities;
ua.events.onStateChanged.add(e -> {
SwingUtilities.invokeLater(() -> {
statusLabel.setText(e.newState.toString());
statusLabel.setForeground(e.newState == Enums.SessionState.Connected
? Color.GREEN : Color.RED);
});
});
错误统计
java.util.concurrent.atomic.AtomicInteger errorCount = new java.util.concurrent.atomic.AtomicInteger();
ua.events.onCommunicationError.add(e -> errorCount.incrementAndGet());
与订阅 onDataChanged 的关系
订阅 sub.onDataChanged 仅收到该订阅的事件; ua.events.onAny (Category=DataChange) 收到 SDK 下所有订阅的事件 (统一通道). 两者并存, 上层按场景选用:
- 单订阅业务逻辑 →
sub.onDataChanged.add(...) - 全局日志 / 录波 / 调试 →
ua.events.onAny.add(...)
线程模型
事件回调在 C 层 Publish 线程上调用, 不在调用方线程. UI 操作必须 SwingUtilities.invokeLater (Swing) / Platform.runLater (JavaFX) 切回 UI 线程, 否则跨线程 UI 操作会崩.
不要在事件里执行长操作 (>100 ms), 否则会阻塞 Publish 队列.
回调里抛异常会被 SDK 吞掉 (try-catch ignored), 不会传播到 native, 不会让 Stack 闪退.