添加 MonitoredItem
前置阅读
- 先创建 Subscription, 见 创建 Subscription.
- DataChanged 回调字段请看 DataChanged 回调.
- 改采样参数请看 Modify.
sub.add(...)
public int add(String nodeId, Consumer<DataChangeEventArgs> onChange);
public int add(String nodeId, Consumer<DataChangeEventArgs> onChange,
Enums.AttributeId attribute, double samplingIntervalMs);
| 参数 | 默认 | 说明 |
|---|---|---|
nodeId | — | 监控的 NodeId |
onChange | — | 单 MI 专属回调, 也会触发 sub.onDataChanged 列表里的全局回调 (传 null 跳过专属) |
attribute | Value (13) | 监控的 Attribute |
samplingIntervalMs | -1 (跟订阅) | 服务端采样间隔. 0 = 尽快 |
返回: int MonitoredItem handle (本地句柄, 用于后续 modify / remove)
异常: OpcUaException (BadNodeIdUnknown 等)
int h = sub.add("ns=2;s=Temperature", e ->
System.out.println(e.nodeId + " = " + e.valueString));
int h2 = sub.add("ns=2;s=Pressure", null,
Enums.AttributeId.Value, 100.0);
sub.addMany 批量添加
一次 RPC 添加多个 MI, 比循环 add 快得多:
public List<int[]> addMany(List<String> nodeIds,
Consumer<DataChangeEventArgs> onChange,
Enums.AttributeId attribute);
返回: List<int[]>, 每项是 { handle, statusCode } 二元组.
List<String> nodes = java.util.stream.IntStream.rangeClosed(1, 100)
.mapToObj(i -> "ns=2;s=T" + i)
.toList();
List<int[]> results = sub.addMany(nodes, e -> {
// 共享回调, 通过 e.nodeId / e.monitoredItemHandle 区分
System.out.println(e.nodeId + " = " + e.valueString);
}, Enums.AttributeId.Value);
for (int i = 0; i < nodes.size(); i++) {
int handle = results.get(i)[0];
int status = results.get(i)[1];
if (status == 0) {
System.out.println(" " + nodes.get(i) + " -> handle=" + handle);
} else {
System.out.println(" " + nodes.get(i) + " FAILED: " + Enums.StatusCode.fromCode(status));
}
}
性能: 100 个 MI 用 addMany 约 1 次 RPC (~10 ms), 用循环 add 约 100 次 RPC (~500-1000 ms).
DataChangeEventArgs 字段
回调参数 OpcUaSubscription.DataChangeEventArgs:
| 字段 | 类型 | 说明 |
|---|---|---|
monitoredItemHandle | int | 触发的 MI 句柄 |
nodeId | String | 该 MI 的 NodeId |
valueString | String | 值的字符串表示 (预抽取, 跨线程安全) |
dataTypeName | String | 内置数据类型枚举名 (如 Double) |
status | Enums.StatusCode | DataValue 的 Status |
sourceTimestampFt | long | 数据源 FileTime, 0 = null |
serverTimestampFt | long | 服务端 FileTime, 0 = null |
arrivedAtUtc | Instant | Java 端记录的到达时间 |
安全设计
valueString / dataTypeName 已在 native Publish 线程内同步抽取, 跨线程使用安全. 不再暴露原始 DataValue 指针, 避免 use-after-free 闪退.
JNA Callback 保活
add 内部为每个 MI 创建一个 JNA trampoline callback, 由 OpcUaSubscription.keepAlive 列表持有强引用防 GC. 用户不要手动销毁 callback, 只要不释放 OpcUaSubscription 就安全.
移除
sub.remove(handle);
sub.removeByNodeId("ns=2;s=Temperature"); // 按 NodeId 移除全部对应
不同 Attribute 的监控
除了默认监控 Value, 也能监控 Status / Quality:
sub.add("ns=2;s=T1", null, Enums.AttributeId.Value, -1);
sub.add("ns=2;s=T1", null, Enums.AttributeId.NodeClass, -1); // 只关心节点类别变化
DataChangeFilter (死区)
当前 SDK 默认不设 filter (任何变化都推). 如需 Absolute / Percent 死区, 走底层 API (后续版本会暴露).