简体   繁体   English

Twincat Ads Reactive 奇怪的句柄行为

[英]Twincat Ads Reactive weird Handle behaviour

I'm working with TwincatAds.Reactive 6.0.190 in .NET 6 WPF Desktop application.我在 .NET 6 WPF 桌面应用程序中使用 TwincatAds.Reactive 6.0.190。
I'm also using MVVM pattern.我也在使用 MVVM 模式。
My goal is to create a Class that is going to observe for a PLC Variable changes, collect those variables to a dictionary, and later on use those values in the ViewModel.我的目标是创建一个 Class 来观察 PLC 变量的变化,将这些变量收集到字典中,然后在 ViewModel 中使用这些值。
Here's the method where I'm attaching the notification and action where I'm handling the notification.这是我附加通知的方法和处理通知的操作。

    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);
        }
    }

I'm using ValueNotification simply because, I'd like to use this pattern also for complex PLC Variables like Structs.我使用 ValueNotification 仅仅是因为,我也想将此模式用于复杂的 PLC 变量,如 Structs。
As You can see, in the WhenNotificationEx method I'm using UserData[] to provide some sort of identification of what Variable has changed when handling the change.如您所见,在 WhenNotificationEx 方法中,我使用 UserData[] 来提供某种标识,以识别在处理更改时变量已更改的内容。
My idea was to use Handle property from ValueNotification as an indexer in UserData[] to identify what variable I'm dealing with, but for some reason Handle starts from 2.我的想法是使用 ValueNotification 中的 Handle 属性作为 UserData[] 中的索引器来识别我正在处理的变量,但由于某种原因,Handle 从 2 开始。

My question is, is it expected behaviour, does the Handle value really always start from 2?我的问题是,它是预期的行为吗,Handle 值真的总是从 2 开始吗?

I've decided that relying on the Handle being index in the UserData array is quite unpredictable as Handle is being created by the Twincat Ads server.我已经决定依赖 Handle 作为 UserData 数组中的索引是非常不可预测的,因为 Handle 是由 Twincat Ads 服务器创建的。

Solved the issue by creating own extension method to the WhenNotificationEx.通过为WhenNotificationEx 创建自己的扩展方法解决了这个问题。 Turned out IDisposableHandleBag has exactly what I was looking for, which is SourceResultHandles property, where AnySymbolSpecifier and ResultHandle are both stored!原来 IDisposableHandleBag 正是我想要的,它是 SourceResultHandles 属性,其中 AnySymbolSpecifier 和 ResultHandle 都存储!

Here's created extension method这是创建的扩展方法
public static Dictionary<string, uint> Handles { get; public static Dictionary<string, uint> Handles { get; private set;私人套装; } = new(); } = 新的();

    public static IObservable<ValueNotification> WhenNotificationWithHandle(this IAdsConnection connection, IList<AnySymbolSpecifier> symbols, NotificationSettings settings)
    {
        IAdsConnection connection2 = connection;
        IList<AnySymbolSpecifier> symbols2 = symbols;
        NotificationSettings settings2 = settings;
        if (connection2 == null)
        {
            throw new ArgumentNullException("connection");
        }

        if (symbols2 == null)
        {
            throw new ArgumentNullException("symbols");
        }

        if (symbols2.Count == 0)
        {
            throw new ArgumentOutOfRangeException("symbols", "Symbol list is empty!");
        }

        IDisposableHandleBag<AnySymbolSpecifier> bag = null;
        EventLoopScheduler scheduler = new EventLoopScheduler();
        IObservable<int> whenSymbolChangeObserver = connection2.WhenSymbolVersionChanges(scheduler);
        IDisposable whenSymbolChanges = null;
        Action<EventHandler<AdsNotificationExEventArgs>> addHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
        {
            connection2.AdsNotificationEx += h;
            bag = ((IAdsHandleCacheProvider)connection2).CreateNotificationExHandleBag(symbols2, relaxSubErrors: false, settings2, null);
            bag.CreateHandles();

            // Collect Handles
            Handles.Clear();
            foreach (var item in bag.SourceResultHandles)
                Handles.Add(item.source.InstancePath, item.result.Handle);

            whenSymbolChanges = whenSymbolChangeObserver.Subscribe((Action<int>)delegate
            {
                bag.CreateHandles();
                Handles.Clear();
                foreach (var item in bag.SourceResultHandles)
                    Handles.Add(item.source.InstancePath, item.result.Handle);

            }, (Action<Exception>)delegate
            {
                TcTraceSource traceAds = AdsModule.TraceAds;
                DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(101, 1);
                defaultInterpolatedStringHandler.AppendLiteral("The AdsServer '");
                defaultInterpolatedStringHandler.AppendFormatted(connection2.Address);
                defaultInterpolatedStringHandler.AppendLiteral("' doesn't support SymbolVersionChanged Notifications! Handle recreation is not active!");
                traceAds.TraceInformation(defaultInterpolatedStringHandler.ToStringAndClear());
            });
        };
        Action<EventHandler<AdsNotificationExEventArgs>> removeHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
        {
            if (whenSymbolChanges != null)
            {
                whenSymbolChanges.Dispose();
            }

            scheduler.Dispose();
            if (bag != null)
            {
                bag.Dispose();
                bag = null;

                Handles.Clear();
            }

            connection2.AdsNotificationEx -= h;
        };

        return from ev in Observable.FromEventPattern<EventHandler<AdsNotificationExEventArgs>, AdsNotificationExEventArgs>(addHandler, removeHandler)
               where bag.Contains(ev.EventArgs.Handle)
               select new ValueNotification(ev.EventArgs, ev.EventArgs.Value);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM