using System; using System.Collections.Generic; using System.Diagnostics; using System.Net.NetworkInformation; using System.Threading; using TwinCAT; using TwinCAT.Ads; using TwinCAT.Ads.TypeSystem; using System.IO; using System.Text; using System.Linq; using System.Threading.Tasks; using TwinCAT.TypeSystem; namespace IOB_WIN_NEXT { /// /// Client comunicazioni con PLC Beckhoff TwinCat /// public class TcAdsClient { #region Private Fields /// /// elenco delle variabili monitorate x change event /// private List _MonitVars = new List(); private int _status; private List addedSignalationList = new List(); /// /// Dizionario di conversione da indice a index group e index offset /// private Dictionary> addressList; private TwinCAT.Ads.TcAdsClient adsClient; private CancellationTokenSource cts; //private Action dispatcher; private System.Threading.Tasks.Task dispatchertask; private int eventHandle; private object lockobj = new object(); private AdsStream newNotificationStream; private int notifyposition; private AdsStream notifyStream; private int SegnalazioniADSEventHandle, StatusChangedEventHandle, MessageQueuedEventHandle; private Symbol StatoMacchina; #endregion Private Fields #region Public Fields public System.Collections.Concurrent.ConcurrentQueue CodaComandi; public TwinCAT.Ads.TcAdsSymbolInfoLoader InfoLoader; /// /// Dizionario delle variabili monitorate (gestite ad evento x modifica), chiave = nome var, valore = symbol x gestione variabile /// public Dictionary MonitoredItems = new Dictionary(); public TwinCAT.TypeSystem.ISymbolLoader SymbolLoaderInstance; public TcAdsSymbolInfoCollection Symbols; #endregion Public Fields #region Public Constructors /// /// Inizializza un oggetto ADS x gestione classe comunicazione con TwinCat /// /// Lista dei nomi delle variabili da gestire ad eventChange (es stato macchina) /// indirizzo tipo AmsNetId /// Porta comunicazione: Connect to local PLC - Runtime 1 - TwinCAT2 Port=801, TwinCAT3 Port=851 public TcAdsClient(List MonitVars, string indirizzo = "local", int porta = 851) { _MonitVars = MonitVars; MonitoredItems = new Dictionary(); notifyStream = new AdsStream(); newNotificationStream = new AdsStream(); addressList = new Dictionary>(); bool ready = false; while (!ready) { try { //LETTURA DEL VETTORE DI INIZIALIZZAZIONE if (adsClient == null) adsClient = new TwinCAT.Ads.TcAdsClient(); // Connect to local PLC - Runtime 1 - TwinCAT2 Port=801, TwinCAT3 Port=851 if (indirizzo == "") { adsClient.Connect(porta); } else { if (adsClient.IsConnected == false) adsClient.Connect(indirizzo, porta); } SymbolLoaderInstance = SymbolLoaderFactory.Create(adsClient, SymbolLoaderSettings.Default); InfoLoader = adsClient.CreateSymbolInfoLoader(); Symbols = InfoLoader.GetSymbols(true); ready = true; } catch (Exception ex) { System.Threading.Thread.Sleep(100); ready = false; Debug.Print(ex.Message); } } // inizializzo dizionario delle variabili gestite foreach (var item in _MonitVars) { var currSymbol = (Symbol)SymbolLoaderInstance.Symbols[item]; currSymbol.NotificationSettings = new AdsNotificationSettings(AdsTransMode.OnChange, 100, 100); currSymbol.ValueChanged += MonItem_ValueChanged; // aggiungo al dict MonitoredItems.Add(item, currSymbol); } StatoMacchina = (Symbol)SymbolLoaderInstance.Symbols["VarADS.StatoMacchina"]; StatoMacchina.NotificationSettings = new AdsNotificationSettings(AdsTransMode.OnChange, 100, 100); StatoMacchina.ValueChanged += StatoMacchina_ValueChanged; notifyposition = 0; cts = new CancellationTokenSource(); //adsClient.AdsNotification += new AdsNotificationEventHandler(adsClient_AdsNotification); CodaComandi = new System.Collections.Concurrent.ConcurrentQueue(); cts = new CancellationTokenSource(); //Task require CancellationToken.cancel() to stop Action Azione = commandDispatcher; //Definisce e Crea un Task di base a priorità favorevole dispatchertask = new Task(Azione, cts.Token, TaskCreationOptions.PreferFairness); dispatchertask.Start(); } #endregion Public Constructors #region Public Delegates public delegate void CountChangedEventHandler(TcAdsClient sender, int newCount); public delegate void StatusChangedEventHandler(TcAdsClient sender, int newStatus); public delegate void ValueChangedEventHandler(TcAdsClient sender, string key, string value); #endregion Public Delegates #region Public Events public event CountChangedEventHandler CountChanged; public event StatusChangedEventHandler StatusChanged; public event ValueChangedEventHandler ValueChanged; #endregion Public Events #region Public Properties public TwinCAT.Ads.TcAdsClient Client { get { return adsClient; } } public bool Connected { get { bool answ = false; if (adsClient != null) { answ = adsClient.IsConnected; } return answ; } } public bool EnableEvents { get; set; } public int Status { get { var stato = ReadVariabile("VarADS.StatoMacchina"); if (stato != null) _status = (int)stato; else { throw new Exception("Errore lettura stato"); } return _status; } } #endregion Public Properties #region Private Methods private void commandDispatcher(object tk) { ComandiADS comando; Thread.CurrentThread.Name = "ADS Command Dispatcher"; CancellationToken chiudi = (CancellationToken)tk; while (!chiudi.IsCancellationRequested) { Thread.Sleep(1); if (CodaComandi.Count <= 0) { Thread.Sleep(1); continue; } if (CodaComandi.Count > 100) Debug.Print("CODA COMANDI! " + CodaComandi.Count.ToString()); if (!CodaComandi.TryDequeue(out comando)) continue; if (CodaComandi.Count > 1000) continue; if (comando.ComandoScrittua) //gestione scrittura { try { if (comando.SymbolName != "") { if (comando.Symbol == null) comando.Symbol = GetSymbolInfo(comando.SymbolName); } else { comando.SymbolName = comando.Symbol.Name; } if (comando.Value is int && comando.Symbol.Category == TwinCAT.TypeSystem.DataTypeCategory.Array) { var newvalue = new int[comando.Symbol.ArrayInfos[0].Elements]; newvalue[0] = (int)comando.Value; comando.Value = newvalue; } if (comando.Value is double && comando.Symbol.Category == TwinCAT.TypeSystem.DataTypeCategory.Array) { var newvalue = new double[comando.Symbol.ArrayInfos[0].Elements]; newvalue[0] = (double)comando.Value; comando.Value = newvalue; } adsClient.WriteSymbol(comando.Symbol, comando.Value); } catch (Exception err) { comando.Error = true; Debug.Print(comando.SymbolName + " Scrittura " + err.Message); } comando.Updating.Set(); } else // gestione lettura { try { if (comando.SymbolName != "") { if (comando.Symbol == null) comando.Symbol = GetSymbolInfo(comando.SymbolName); } else { comando.SymbolName = comando.Symbol.Name; } comando.Value = adsClient.ReadSymbol(comando.Symbol); } catch (Exception errore) { Debug.Print(errore.Message); comando.Error = true; Debug.Print("Error reading from ADS: VarName: " + comando.SymbolName); } comando.Updating.Set(); } } } private void MonItem_ValueChanged(object sender, TwinCAT.TypeSystem.ValueChangedArgs e) { string newStatus = $"{e.Value}"; if (ValueChanged != null) { ValueChanged(this, $"{sender}", newStatus); } } private object ReadVariabile(ComandiADS comando) { CodaComandi.Enqueue(comando); bool test = comando.Updating.Wait(3000); if (!test) Debug.Print("Errore attesa lettura: " + comando.SymbolName); if (comando.Value == null) Debug.Print("ADS Variabile non trovata: " + comando.SymbolName); return comando.Value; } private void StatoMacchina_ValueChanged(object sender, TwinCAT.TypeSystem.ValueChangedArgs e) { int newStatus = (int)e.Value; if (StatusChanged != null) StatusChanged(this, newStatus); } private bool WriteVariabile(ComandiADS comando, bool syncronous) { bool test = true; CodaComandi.Enqueue(comando); if (syncronous) test = comando.Updating.Wait(3000); if (!test) Debug.Print("Errore attesa lettura: " + comando.SymbolName); if (comando.Error) Debug.Print("Errore ADS durante la scrittura della variabile: " + comando.SymbolName); return !comando.Error; } #endregion Private Methods #region Public Methods public void dispose() { adsClient.Dispose(); } public TcAdsSymbolInfo GetSymbolInfo(string nome) { try { var symbol = InfoLoader.FindSymbol(nome); return symbol; } catch (Exception) { throw; } } public object ReadVariabile(ref TcAdsSymbolInfo variabile) { var comando = new ComandiADS { ComandoScrittua = false, Symbol = variabile }; return ReadVariabile(comando); } public object ReadVariabile(string symbolName, Type type = null) { var comando = new ComandiADS { ComandoScrittua = false, SymbolName = symbolName }; return ReadVariabile(comando); } public bool WriteVariabile(string symbolName, object value, bool syncronous = false) { var comando = new ComandiADS { Value = value, ComandoScrittua = true, SymbolName = symbolName }; return WriteVariabile(comando, syncronous); } public bool WriteVariabile(TcAdsSymbolInfo symbol, object value, bool syncronous = false) { var comando = new ComandiADS { Value = value, ComandoScrittua = true, Symbol = symbol }; return WriteVariabile(comando, syncronous); } #endregion Public Methods #region Public Classes public class ComandiADS { #region Public Fields public bool ComandoScrittua; public bool Error; public TcAdsSymbolInfo Symbol; public string SymbolName; public ManualResetEventSlim Updating; public object Value; #endregion Public Fields #region Public Constructors public ComandiADS() { Updating = new ManualResetEventSlim(false); } public ComandiADS(string name) { SymbolName = name; Updating = new ManualResetEventSlim(false); } public ComandiADS(TcAdsSymbolInfo info) { Symbol = info; Updating = new ManualResetEventSlim(false); } #endregion Public Constructors } #endregion Public Classes } }