DataChangeFilter — 触发与死区过滤
概述
OPC UA Part 4 §7.17 DataChangeFilter 让客户端告诉服务端"什么时候才把数据 push 给我", 减少不必要的 Publish 流量:
- Trigger — 触发条件 (Status / StatusValue / StatusValueTimestamp)
- DeadbandType — 死区类型 (None / Absolute / Percent)
- DeadbandValue — 死区数值
API
| 类 / 字段 | 类别 | 读写 | 说明 |
|---|---|---|---|
| DataChangeFilter | 类 | — | 过滤器配置 (init-only) |
| .Trigger | 属性 | 读写 | DataChangeTrigger 枚举, 默认 StatusValue |
| .DeadbandType | 属性 | 读写 | DeadbandType 枚举, 默认 None |
| .DeadbandValue | 属性 | 读写 | double, 默认 0.0 |
| sub.Add(nodeId, attr, samplingMs, mode, queueSize, discardOldest, filter) | 方法 | 写 | 带 filter 的 MI 重载 |
相关结构:
public enum DataChangeTrigger
{
Status = 0, // 仅 StatusCode 变化时报告
StatusValue = 1, // StatusCode 或 Value 变化 (默认)
StatusValueTimestamp = 2 // StatusCode / Value / SourceTimestamp 任一变化
}
public enum DeadbandType
{
None = 0, // 无死区, 任何变化都报
Absolute = 1, // 绝对死区, |new - old| > DeadbandValue 才报
Percent = 2 // 百分比死区, |new - old| > EURange.Range × DeadbandValue / 100 才报
}
代码示例
using DarraOpcUa_Client;
using var ua = new DarraOpcUa("opc.tcp://localhost:4840");
ua.Connect();
using var sub = ua.CreateSubscription(publishingIntervalMs: 100);
// 1) 绝对死区 0.5 — 温度变化超过 0.5 度才上报
var filter1 = new DataChangeFilter
{
Trigger = DataChangeTrigger.StatusValue,
DeadbandType = DeadbandType.Absolute,
DeadbandValue = 0.5
};
uint mi1 = sub.Add("ns=2;s=Temp",
AttributeId.Value, samplingIntervalMs: 100,
mode: MonitoringMode.Reporting, queueSize: 1, discardOldest: true,
filter: filter1);
// 2) 百分比死区 2% — 流量在量程 2% 内不报
var filter2 = new DataChangeFilter
{
Trigger = DataChangeTrigger.StatusValueTimestamp,
DeadbandType = DeadbandType.Percent,
DeadbandValue = 2.0
};
uint mi2 = sub.Add("ns=2;s=Flow",
AttributeId.Value, 100,
MonitoringMode.Reporting, queueSize: 1, discardOldest: true,
filter: filter2);
// 3) 仅 Status 变化 — 看通讯链路状态翻转
var filter3 = new DataChangeFilter { Trigger = DataChangeTrigger.Status };
uint mi3 = sub.Add("ns=2;s=Comm.Status",
AttributeId.Value, 200, MonitoringMode.Reporting, 1, true, filter3);
sub.DataChanged += (s, e) =>
{
Console.WriteLine($"[mi={e.ClientHandle}] {e.Value.Value} ({e.Value.Status})");
};
实现说明
- Stack 不支持自定义过滤器时, SDK graceful 降级:
- 仍调底层 AddNode 注册 MI (filter 失效, 数据全量上报)
- filter 缓存到本地
_miCache.FilterDef, 重连/重建时仍可还原 - 推一条
Diagnostic类别 Warn 事件让 UI 能感知
- Percent 死区依赖目标节点有
EURangeProperty; 没有时服务端通常返回BadFilterNotAllowed
最佳实践
- 模拟量首选 Absolute 死区 — 工程单位直观, 例如 0.5°C / 0.1 bar
- 大量程信号用 Percent — 量程 0-1000 的传感器写 1% 比写 10 直观
- 数字量 (DI/DO) 不需要 filter — 状态翻转本来就稀疏
- 死区不要太大 — 趋势曲线会失真, 经验值 0.1%-2%
- filter 配 queueSize > 1 — 避免短脉冲被吞 (queueSize=10 + discardOldest=true 是常用)
跨语言对照
| C# | Python | Java | C++ | Rust | C |
|---|---|---|---|---|---|
| DataChangeFilter | DataChangeFilter | DataChangeFilter | DataChangeFilter | DataChangeFilter | DarraUa_DataChangeFilter |
| DataChangeTrigger | DataChangeTrigger | DataChangeTrigger | DataChangeTrigger | DataChangeTrigger | DarraUa_DataChangeTrigger |
| DeadbandType | DeadbandType | DeadbandType | DeadbandType | DeadbandType | DarraUa_DeadbandType |