添加 MonitoredItem
前置阅读
- 先创建 Subscription, 见 创建 Subscription.
- data_changed 字段请看 data_changed 事件.
- 改采样参数请看 Modify.
sub.add_node(...)
def add_node(self,
node_id: str,
callback: Optional[Callable[[DataChangeEventArgs], None]] = None,
attribute: AttributeId = AttributeId.Value,
sampling_interval_ms: float = -1.0) -> int: ...
| 参数 | 默认 | 说明 |
|---|---|---|
node_id | — | 监控的 NodeId |
callback | None | 该 MI 专用回调 (容器级 data_changed 仍会被触发) |
attribute | Value (13) | 监控的 Attribute |
sampling_interval_ms | -1 (跟订阅) | 服务端采样间隔. 0 = 尽快 |
返回: int MonitoredItem handle (本地句柄, 用于后续 modify / remove)
异常: OpcUaException (BadNodeIdUnknown 等)
h1 = sub.add_node("ns=2;s=Temperature")
h2 = sub.add_node("ns=2;s=Pressure", sampling_interval_ms=100)
# 带专用回调
def on_temp(e):
print(f"Temperature: {e.value_string}")
h3 = sub.add_node("ns=2;s=Temperature2", callback=on_temp)
add_many
一次 RPC 添加多个 MI, 比循环 add_node 快得多:
def add_many(self,
node_ids: List[str],
callback: Optional[Callable[[DataChangeEventArgs], None]] = None,
attribute: AttributeId = AttributeId.Value
) -> List[Tuple[int, StatusCode]]: ...
返回每项 (mi_handle, status) — 失败项 mi=0.
nodes = [f"ns=2;s=T{i}" for i in range(1, 101)]
results = sub.add_many(nodes)
for nid, (mi, st) in zip(nodes, results):
if st == StatusCode.Good:
print(f" {nid} -> handle={mi}")
else:
print(f" {nid} FAILED: {st.name}")
性能: 100 个 MI 用 add_many 约 1 次 RPC (~10 ms), 用循环 add_node 约 100 次 RPC (~500-1000 ms).
DataChangeEventArgs 字段
订阅的回调参数 DataChangeEventArgs (dataclass, 跨线程安全):
| 字段 | 类型 | 说明 |
|---|---|---|
monitored_item_handle | int | 触发的 MI 句柄 |
node_id | str | 该 MI 的 NodeId |
value_string | Optional[str] | 值的字符串表示 (预抽取, 跨线程安全) |
data_type_name | Optional[str] | 内置数据类型枚举名 (如 Double) |
status | StatusCode | DataValue 的 Status |
source_timestamp | Optional[datetime] | 数据源时间戳 (UTC) |
server_timestamp | Optional[datetime] | 服务端时间戳 (UTC) |
安全设计
value_string / data_type_name 已在 C 层 Publish 线程同步抽取为 Python str/枚举, 跨线程使用安全. 不再暴露原始 native DataValue 指针, 避免 use-after-free 闪退.
移除
sub.remove(handle)
sub.remove_by_node_id("ns=2;s=Temperature") # 按 NodeId 移除全部对应
不同 Attribute 的监控
除了默认监控 Value, 也能监控 Status / Quality:
sub.add_node("ns=2;s=T1", attribute=AttributeId.Value)
sub.add_node("ns=2;s=T1", attribute=AttributeId.StatusCode) # 只关心状态变化
DataChangeFilter (死区)
当前 SDK 默认不设 filter (任何变化都推). 如需 Absolute / Percent 死区, 走底层 API (后续版本会暴露).