Files
Mapo-IOB-WIN/IOB-WIN-BECKHOFF/TcAdsClient.cs
T
Samuele Locatelli 698e12f050 Prima bozza beckhoff
2025-01-22 15:37:00 +01:00

409 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using TwinCAT.Ads;
using TwinCAT.Ads.TypeSystem;
namespace IOB_WIN_BECKHOFF
{
/// <summary>
/// Client comunicazioni con PLC Beckhoff TwinCat
/// </summary>
public class TcAdsClient
{
#region Public Fields
public System.Collections.Concurrent.ConcurrentQueue<ComandiADS> CodaComandi;
public TwinCAT.Ads.TcAdsSymbolInfoLoader InfoLoader;
/// <summary>
/// Dizionario delle variabili monitorate (gestite ad evento x modifica), chiave = nome var, valore = symbol x gestione variabile
/// </summary>
public Dictionary<string, Symbol> MonitoredItems = new Dictionary<string, Symbol>();
public TwinCAT.TypeSystem.ISymbolLoader SymbolLoaderInstance;
public TcAdsSymbolInfoCollection Symbols;
#endregion Public Fields
#region Public Constructors
/// <summary>
/// Inizializza un oggetto ADS x gestione classe comunicazione con TwinCat
/// </summary>
/// <param name="MonitVars">
/// Lista dei nomi delle variabili da gestire ad eventChange (es stato macchina)
/// </param>
/// <param name="indirizzo">indirizzo tipo AmsNetId</param>
/// <param name="porta">
/// Porta comunicazione: Connect to local PLC - Runtime 1 - TwinCAT2 Port=801, TwinCAT3 Port=851
/// </param>
public TcAdsClient(List<string> MonitVars, string indirizzo = "local", int porta = 851)
{
_MonitVars = MonitVars;
MonitoredItems = new Dictionary<string, Symbol>();
notifyStream = new AdsStream();
newNotificationStream = new AdsStream();
addressList = new Dictionary<int, Tuple<int, int>>();
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;
#if false
notifyposition = 0;
#endif
cts = new CancellationTokenSource();
//adsClient.AdsNotification += new AdsNotificationEventHandler(adsClient_AdsNotification);
CodaComandi = new System.Collections.Concurrent.ConcurrentQueue<ComandiADS>();
cts = new CancellationTokenSource(); //Task require CancellationToken.cancel() to stop
Action<object> 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 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
#region Private Fields
/// <summary>
/// elenco delle variabili monitorate x change event
/// </summary>
private List<string> _MonitVars = new List<string>();
private int _status;
private List<int> addedSignalationList = new List<int>();
/// <summary>
/// Dizionario di conversione da indice a index group e index offset
/// </summary>
private Dictionary<int, Tuple<int, int>> addressList;
private TwinCAT.Ads.TcAdsClient adsClient;
private CancellationTokenSource cts;
private System.Threading.Tasks.Task dispatchertask;
#if false
//private Action<object> dispatcher;
private int eventHandle;
private int SegnalazioniADSEventHandle, StatusChangedEventHandle, MessageQueuedEventHandle;
private int notifyposition;
#endif
private object lockobj = new object();
private AdsStream newNotificationStream;
private AdsStream notifyStream;
private Symbol StatoMacchina;
#endregion Private Fields
#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
}
}