409 lines
14 KiB
C#
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
|
|
}
|
|
} |