簡體   English   中英

自動重新創建 PollValues<T> 在失去 PLC 連接或 PLC 程序上傳后

[英]Automatically re-create PollValues<T> ​after a lost PLC connection or PLC program upload

我正在嘗試編寫 C# 應用程序,當應用程序與 PLC 失去連接或將下載新版本的 PLC 程序時,ADS 客戶端將自動重新連接/更新讀取/寫入值和訂閱。

我正在使用 NuGet 的TwinCAT.Ads.Reactive v4.4.0庫。

我的程序流程是:

連接ADS服務器(連接成功)=>

  1. 使用ValueSymbolExtensions.WhenValueChanged創建反應式通知
  2. 創建反應性循環寫入值ValueSymbolExtensions.WriteValues

    • 3[a] 創建反應式循環輪詢值AnyTypeExtensions.PollValues.T
    • 3[b] 我也試過 ValueSymbolExtensions.PollValues,它在 Beckhoff 網站上還沒有記錄

到目前為止,我發現 1. 和 2. 即使我拔掉以太網電纜或將新程序下載到 PLC 也能工作 -> WriteValues() 和 WhenValueChanged() 在內部自我更新

代碼:

// 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() 在正常運行期間,這工作正常,但在我更改 PLC 程序並將更改下載到 PLC 或連接丟失(拔下以太網電纜)后,此訂閱在內部失敗並且不會像上述那樣恢復。

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

連接狀態改變

此外,僅當我在廣告客戶端上調用 Connect()/Disconnect() 時才會觸發連接狀態更改事件,而不是在連接問題上。 知道如何發現連接問題嗎?

我已經找到了一半問題的解決方案,其中 observable 即使在連接錯誤(拔出以太網電纜)時也能正常工作,但是在下載新的 PLC 程序(廣告符號版本已更改)后,它不會使用新版本重新創建變量,因此它只會引發錯誤.

解決方案是使用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
                    };     
                }
            );

我已將此問題報告給 nuget 包的創建者。

回答 6.3.2020:

感謝您報告此問題,這是一個重要方面。 我對你的情況進行了簡短的調查。 不幸的是,實際上並不支持 observable 的自動復活。 原因是 PollValues 內部使用符號句柄,當現在的 PLC 程序更新時,符號句柄將失效。 所以實際上,您唯一的解決方案是在 TcAdsClient/AdsConnection 上注冊 SymbolVersionChanged 事件(這是在下載/重啟后發送的)並重新創建 Observable。

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

如果適合您等待 AdsClient / Ads.Reactive 包的下一個版本 - 應該可以在 PollValues 代碼中內部處理這種情況(以您期望的方式)。 我現在有它在我的待辦事項清單上。


在我得到這個答案之前,我已經編輯了他們的實現並創建了我自己的擴展方法。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM