Twincat Ads Reactive中Handle起始值为2是否为预期行为?
关于TwincatAds.Reactive中ValueNotification.Handle起始值的疑问
我在.NET 6 WPF桌面应用中使用TwincatAds.Reactive 6.0.190,同时采用MVVM模式,目标是实现一个监控PLC变量变化的类,将变量收集到字典后供ViewModel使用。
我的绑定通知及处理代码如下:
public void AttachNotification(IEnumerable<(string key, Type type)> Symbols) { _observerValueNotification = Observer.Create<ValueNotification>(val => { // Does handle really start from 2? var handle = val.Handle; if (val.UserData is object[] objects) { string tag = objects[handle - 2].ToString(); if (!_values.Any(x => x.Key == tag)) _values.Add(new SymbolModel { Key = tag, Value = val.Value }); else { var symbol = _values.First(x => x.Key == tag); symbol.Value = val.Value; } } ValuesChanged?.Invoke(_values); }); if (_plcWrapper.AdsClient != null) { // Get Symbols from SymbolLoader List<AnySymbolSpecifier> list = new(); List<string> userData = new(); foreach (var (key, type) in Symbols) { list.Add(new AnySymbolSpecifier(key, new AnyTypeSpecifier(type))); userData.Add(key); } _subscription2 = _plcWrapper.AdsClient.WhenNotificationEx(list, NotificationSettings.ImmediatelyOnChange, userData.ToArray()) .Subscribe(_observerValueNotification); } }
我使用ValueNotification是为了兼容结构体这类复杂PLC变量,通过WhenNotificationEx传入的UserData[]来标识变化的变量,原本打算用ValueNotification.Handle作为UserData[]的索引定位变量,但发现Handle的值从2开始。想确认:
- 这是预期行为吗?
- Handle的值是否始终从2开始?
回答
Handle起始值为2是预期行为
TwincatAds.Reactive底层基于倍福原生ADS库实现,Handle对应ADS通知句柄。原生ADS库会预留部分句柄用于系统级操作(比如句柄1可能对应默认内部通知),因此用户创建的自定义通知句柄会从2开始分配。Handle的值并非始终固定从2开始
当前测试中Handle从2开始,但这个起始值不具备绝对固定性:- 若同一客户端实例下已创建过其他ADS通知,句柄会按递增顺序分配,新通知的Handle会从之前的最大句柄+1开始;
- 若之前的通知被取消订阅并释放句柄,ADS库可能复用空闲句柄,导致新句柄的起始值不固定。
更可靠的变量关联方案
直接依赖Handle作为UserData数组索引存在风险,推荐两种稳定方案:
- 将变量标识单独存入UserData:创建
AnySymbolSpecifier时,把对应的tag作为单个UserData传入,回调中直接从val.UserData获取tag,无需依赖Handle索引; - 用字典映射Handle与tag:订阅前单独创建每个通知并记录Handle与tag的映射关系,后续通过字典快速查找。
方案1代码示例
foreach (var (key, type) in Symbols) { list.Add(new AnySymbolSpecifier(key, new AnyTypeSpecifier(type))); // 每个符号对应单独的UserData(即tag) userData.Add(key); } // 回调中直接获取tag if (val.UserData is string tag) { if (!_values.Any(x => x.Key == tag)) _values.Add(new SymbolModel { Key = tag, Value = val.Value }); else { var symbol = _values.First(x => x.Key == tag); symbol.Value = val.Value; } }
方案2代码示例
Dictionary<int, string> handleToTagMap = new(); List<IDisposable> subscriptions = new(); foreach (var (key, type) in Symbols) { var specifier = new AnySymbolSpecifier(key, new AnyTypeSpecifier(type)); var notification = _plcWrapper.AdsClient.WhenNotificationEx(specifier, NotificationSettings.ImmediatelyOnChange, key); var handle = notification.Subscribe(val => { if (val.UserData is string tag) { // 处理变量更新逻辑 if (!_values.Any(x => x.Key == tag)) _values.Add(new SymbolModel { Key = tag, Value = val.Value }); else { var symbol = _values.First(x => x.Key == tag); symbol.Value = val.Value; } ValuesChanged?.Invoke(_values); } }); // 记录Handle与tag的映射 handleToTagMap.Add(notification.Handle, key); subscriptions.Add(notification); }
内容的提问来源于stack exchange,提问作者MIHOW
相关产品推荐
相关产品推荐

