Автоматически заново создавать PollValues ​​<T> после потери соединения PL C или загрузки программы PL C - PullRequest
0 голосов
/ 02 марта 2020

Я пытаюсь написать C# приложение, в котором клиент ADS автоматически переподключает / обновляет значения для чтения / записи и подписки, когда приложение потеряло соединение с PL C или будет загружена новая версия программы PL C.

Я использую TwinCAT.Ads.Reactive v4.4.0 библиотека от NuGet.

Мой поток программ:

Подключение к серверу ADS (подключение успешно) =>

  1. Создать реактивное уведомление с ValueSymbolExtensions.WhenValueChanged
  2. Создать реактивный цикл c записать значение ValueSymbolExtensions.WriteValues ​​

    • 3 [a] Создание реактивных циклических c значений опроса AnyTypeExtensions.PollValues.T
    • 3 [b] Я также пробовал ValueSymbolExtensions. PollValues, которые пока не задокументированы на веб-сайте Beckhoff

До сих пор я обнаружил, что 1. и 2. работают, даже если я отключаю кабель rnet или загружаю новую программу PL C -> Wr iteValues ​​() и WhenValueChanged () внутренне обновляются

CODE:

// WhenValueChanged()
TreeViewSymbols = SymbolLoaderFactory.Create(_client, SymbolLoaderSettings.Default).Symbols;  // Load symbol tree from plc

IValueSymbol boolVal = (IValueSymbol)TreeViewSymbols["SomeBoolValue"];
boolVal.WhenValueChanged().Subscribe(Observer.Create<object>(val => ArchiveData((bool)val)));

// WriteValues()
IValueSymbol toggleBit = (IValueSymbol)TreeViewSymbols["toggle_bit"];
            toggleBit.WriteValues(
                  Observable.Interval(TimeSpan.FromSeconds(1.0)).Select(x => x % 2 == 0 ? false : (object)true),
                  e => Debug.WriteLine($"Error writing toggle bit")
            );

Я читаю в PollValues ​​() каждую вторую пользовательскую структуру данных. При нормальном запуске это работает нормально, но после того, как я изменил программу PL C и загрузил изменения в PL C или потерял соединение (неподключенный кабель rnet), эта подписка завершается сбоем и не восстанавливается, как указано выше.

IValueSymbol state = (IValueSymbol)TreeViewSymbols[Cfg.ModuleStateTag];
state.PollValues(TimeSpan.FromSeconds(1.0))
      .Subscribe(Observer.Create<object>(
       val => // val comes as byte[] array
      {
         var a = new ModuleStateData((byte[])val);
         Debug.WriteLine($"Status values Machine Mode:{a.MachineMode}");
      },
      e => Debug.WriteLine($"Error reading status"),
      () => Debug.WriteLine($"OnComplete???? reading status"))
      ).AddDisposableTo(_disposables);

_client.PollValues<ModuleStateData>(
                Cfg.ModuleStateTag,
                TimeSpan.FromSeconds(1.0)
                ).Subscribe(Observer.Create<object>(
                val =>
                {
                    Debug.WriteLine($"Status values Machine Mode:{val.MachineMode});
                },
                e => Debug.WriteLine($"Error reading status - {e.Message}"),
                () => Debug.WriteLine($"OnComplete???? reading status"))
            ).AddDisposableTo(_disposables);

ConnectionStateChanged

Кроме того, событие изменения состояния подключения вызывается только в том случае, если я вызываю Connect () / Disconnect () на клиенте рекламы, а не на проблемах с подключением. Любая идея, как я могу узнать, что есть проблема с подключением?

Ответы [ 2 ]

0 голосов
/ 10 марта 2020

Я сообщил об этой проблеме создателям пакета nuget.

Ответ 6.3.2020:

Спасибо за сообщение об этой проблеме, это важный аспект. Я кратко изучил ваше дело. К сожалению, автоматическое c воскрешение наблюдаемого на самом деле не поддерживается. Причина в том, что PollValues ​​внутренне использует дескрипторы Symbol, которые будут недействительными при обновлении программы PL C. Поэтому на самом деле ваше единственное решение - зарегистрироваться для события SymbolVersionChanged в TcAdsClient / AdsConnection (отправляется после загрузки / перезапуска) и воссоздать Observable.

https://infosys.beckhoff.de/content/1031/tc3_adsnetref/7313543307.html?id=2192955395989567903

Если вам целесообразно дождаться следующей версии пакета AdsClient / Ads.Reactive - должна быть возможность обработать эту ситуацию внутренне в коде PollValues ​​(так, как вы ожидаете). Теперь он есть в моем списке TODO.


Прежде чем я получил этот ответ, я отредактировал их реализацию и создал свой собственный метод расширения.

Реализация TwinCAT.Ads.Reactive v4.4.0

    public static IObservable<T> PollValues<T>(
      this IAdsConnection connection,
      string instancePath,
      int[] args,
      IObservable<Unit> trigger,
      Func<Exception, T> errorHandler)
    {
      DisposableHandleBag bag = new DisposableHandleBag(connection, (IList<string>) new string[1]
      {
        instancePath
      });
      Func<Unit, T> selector = (Func<Unit, T>) (o =>
      {
        try
        {
          return (T) connection.ReadAny(61445U, bag.GetHandle(instancePath), typeof (T), args);
        }
        catch (Exception ex)
        {
          if (errorHandler != null)
            return errorHandler(ex);
          throw;
        }
      });
      Action finallyAction = (Action) (() =>
      {
        bag.Dispose();
        bag = (DisposableHandleBag) null;
      });
      return trigger.Select<Unit, T>(selector).Finally<T>(finallyAction);
    }

Мое редактирование - где я создаю дескриптор для каждого дескриптора чтения и удаления после чтения

    public static IObservable<T> MyPollValues<T>(
            this IAdsConnection connection,
            string instancePath,
            int[] args,
            IObservable<Unit> trigger,
            Func<Exception, T> errorHandler)
        {
            Func<Unit, T> selector = (Func<Unit, T>)(o =>
            {
                try
                {
                    var handle = connection.CreateVariableHandle(instancePath);
                    var data = (T)connection.ReadAny(handle, typeof(T), args);
                    connection.DeleteVariableHandle(handle);
                    return data;
                }
                catch (Exception ex)
                {
                    if (errorHandler != null)
                        return errorHandler(ex);

                    throw;
                }
            });
            return trigger.Select<Unit, T>(selector);
        }
0 голосов
/ 04 марта 2020

Я нашел решение для половины моей проблемы, где наблюдаемое работает даже при ошибке подключения (неподключенный кабель rnet), но после загрузки новой программы PL C (изменена версия символа рекламы) она не воссоздает переменную с новой версией, поэтому он просто выдает ошибку.

Решение заключается в использовании другой перегрузки PollValues ​​, где я указываю Func<Exception, T> errorHandler. Этот обработчик работает как резервное значение в случае ошибки.

_client.PollValues<ModuleStateData>(
                Cfg.ModuleStateTag,
                TimeSpan.FromSeconds(1.0),
                e =>
                {
                    Debug.WriteLine($"Error reading status {Cfg.Name} - {e.Message}");
                    return new ModuleStateData()
                    {
                        // Set data in case of error
                    };     
                }
            );
...