|
@@ -0,0 +1,294 @@
|
|
|
|
+using Log;
|
|
|
|
+using ProtocalGeneral;
|
|
|
|
+using System.Collections.Concurrent;
|
|
|
|
+using TwinCAT.Ads.SumCommand;
|
|
|
|
+using TwinCAT.TypeSystem;
|
|
|
|
+
|
|
|
|
+namespace AdsCommunicatorNet8;
|
|
|
|
+
|
|
|
|
+//this Implementation has removed notification function, only get value and set value available
|
|
|
|
+public class AdsCommunicator(ILog log):IDisposable
|
|
|
|
+{
|
|
|
|
+ private bool _isConnected;
|
|
|
|
+ private string? _netID = null;
|
|
|
|
+ private int _port = 0;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private AdsClient? _adsClient;
|
|
|
|
+ private List<string[]>? _adsCsvInfo;
|
|
|
|
+ private ITcpConnectNority? _connectionNotify;
|
|
|
|
+ private bool disposedValue;
|
|
|
|
+ private readonly ConcurrentDictionary<string, IAdsSymbol> _symbols = [];
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ public bool IsConnected { get { return _isConnected; } }
|
|
|
|
+
|
|
|
|
+ public bool Initialize(ITcpConnectNority connectionNotify)
|
|
|
|
+ {
|
|
|
|
+ if (this._adsClient is not null)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ this._adsClient = new AdsClient();
|
|
|
|
+ this._adsCsvInfo = [];
|
|
|
|
+ this._connectionNotify = connectionNotify;
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public bool Open(string netID, int port, int retry)
|
|
|
|
+ {
|
|
|
|
+ log?.Info($"Try Open {netID} {port}");
|
|
|
|
+ if (this._adsClient is null)
|
|
|
|
+ {
|
|
|
|
+ log?.Warning("Not initialized before Open");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (this._adsClient.IsConnected)
|
|
|
|
+ {
|
|
|
|
+ log?.Warning($"Ads {netID} {port} already connected");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._adsClient.ConnectionStateChanged += AdsClient_ConnectionStateChanged;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ for (int i = 1; i <= retry; i++)
|
|
|
|
+ {
|
|
|
|
+ log?.Info($"Try Connected {i}");
|
|
|
|
+
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ this._adsClient.Connect(netID, port);
|
|
|
|
+ if (this._adsClient.TryReadState(out _) == AdsErrorCode.NoError)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ catch
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (i == retry)
|
|
|
|
+ {
|
|
|
|
+ log?.Error($"Reach Max Retry {i}, connect failed");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ Thread.Sleep(300);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._netID = netID;
|
|
|
|
+ this._port = port;
|
|
|
|
+
|
|
|
|
+ log?.Info($"Connect success");
|
|
|
|
+ this._isConnected = true;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public bool ReConnect()
|
|
|
|
+ {
|
|
|
|
+ if (string.IsNullOrEmpty(this._netID) || this._port == 0)
|
|
|
|
+ return false;
|
|
|
|
+ this._adsClient ??= new();
|
|
|
|
+ if (this._adsClient.IsConnected)
|
|
|
|
+ return true;
|
|
|
|
+
|
|
|
|
+ for (; ; )
|
|
|
|
+ {
|
|
|
|
+ log? .Info($"Try ReConnected");
|
|
|
|
+
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ this._adsClient.Connect(_netID, _port);
|
|
|
|
+ if (this._adsClient.TryReadState(out _) == AdsErrorCode.NoError)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ catch
|
|
|
|
+ {
|
|
|
|
+ Thread.Sleep(300);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public bool GetValue<T>(string symbolName, out T? value)
|
|
|
|
+ {
|
|
|
|
+ value = default;
|
|
|
|
+
|
|
|
|
+ if (string.IsNullOrEmpty(symbolName))
|
|
|
|
+ {
|
|
|
|
+ log?.Warning("Symbol Empty");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log?.Info($"Try get value {symbolName}");
|
|
|
|
+
|
|
|
|
+ if (this._adsClient is null)
|
|
|
|
+ {
|
|
|
|
+ log? .Error("PLC not connected");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!this._symbols.TryGetValue(symbolName, out IAdsSymbol? adsSymbol) || adsSymbol is null)
|
|
|
|
+ {
|
|
|
|
+ log?.Info($"{symbolName} not Created before try create symbol");
|
|
|
|
+ if (this._adsClient.TryReadSymbol(symbolName, out adsSymbol) != AdsErrorCode.NoError || adsSymbol is null)
|
|
|
|
+ {
|
|
|
|
+ log?.Error($"{symbolName} not exist in PLC");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._symbols[symbolName] = adsSymbol;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ value = (T)this._adsClient.ReadValue(adsSymbol);
|
|
|
|
+ log?.Info($"{symbolName} value {value}");
|
|
|
|
+ }
|
|
|
|
+ catch (Exception ex)
|
|
|
|
+ {
|
|
|
|
+ log?.Error($"{symbolName} Read failed: {ex.Message}");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public bool SetValue<T>(string symbolName, T value) where T : notnull
|
|
|
|
+ {
|
|
|
|
+ if (string.IsNullOrEmpty(symbolName))
|
|
|
|
+ {
|
|
|
|
+ log?.Warning("Set Value Symbol empty");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log?.Info($"Try set value {symbolName}");
|
|
|
|
+
|
|
|
|
+ if (this._adsClient is null)
|
|
|
|
+ {
|
|
|
|
+ log?.Error("PLC not connected");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (value is null)
|
|
|
|
+ {
|
|
|
|
+ log?.Warning("Value Empty");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log?.Info($"Value {value}");
|
|
|
|
+
|
|
|
|
+ if (!this._symbols.TryGetValue(symbolName, out IAdsSymbol? adsSymbol) || adsSymbol is null)
|
|
|
|
+ {
|
|
|
|
+ log?.Info($"{symbolName} not Created before try create symbol");
|
|
|
|
+ if (this._adsClient.TryReadSymbol(symbolName, out adsSymbol) != AdsErrorCode.NoError || adsSymbol is null)
|
|
|
|
+ {
|
|
|
|
+ log?.Error($"{symbolName} not exist in PLC");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._symbols[symbolName] = adsSymbol;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ this._adsClient.WriteValue(adsSymbol, value);
|
|
|
|
+ log?.Info($"Set {symbolName} value {value} succeeded");
|
|
|
|
+ }
|
|
|
|
+ catch (Exception ex)
|
|
|
|
+ {
|
|
|
|
+ log?.Error($"{symbolName} Set failed: {ex.Message}");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public bool SetBatchValue(IEnumerable<string> symbolCollection, IList<object> valueCollection)
|
|
|
|
+ {
|
|
|
|
+ if (this._adsClient is null)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (symbolCollection is null || valueCollection is null)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (symbolCollection.Count() != symbolCollection.Count())
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ List<ISymbol> symbols = [];
|
|
|
|
+ foreach (var item in symbolCollection)
|
|
|
|
+ {
|
|
|
|
+ if (!this._symbols.TryGetValue(item, out IAdsSymbol? symbol) || symbol is null)
|
|
|
|
+ {
|
|
|
|
+ log?.Warning($"BatchSend: {item} not Registered before");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ symbols.Add(symbol);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ SumSymbolWrite writes = new(this._adsClient, symbols);
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ writes.Write([.. valueCollection]);
|
|
|
|
+ }
|
|
|
|
+ catch (Exception ex)
|
|
|
|
+ {
|
|
|
|
+ log?.Warning(ex.Message);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void AdsClient_ConnectionStateChanged(object? sender, TwinCAT.ConnectionStateChangedEventArgs e)
|
|
|
|
+ {
|
|
|
|
+ if (this._adsClient is null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ log?.Info($"{e.NewState} {this._adsClient.Address.NetId} {this._adsClient.Address.Port}");
|
|
|
|
+
|
|
|
|
+ switch (e.NewState)
|
|
|
|
+ {
|
|
|
|
+ case TwinCAT.ConnectionState.Connected:
|
|
|
|
+ this._isConnected = true;
|
|
|
|
+ this._connectionNotify?.Connect(this._adsClient.Address.NetId.ToString(), this._adsClient.Address.Port);
|
|
|
|
+ break;
|
|
|
|
+ case TwinCAT.ConnectionState.Disconnected:
|
|
|
|
+ case TwinCAT.ConnectionState.Lost:
|
|
|
|
+ default:
|
|
|
|
+ this._isConnected = false;
|
|
|
|
+ this._connectionNotify?.DisConnect(this._adsClient.Address.NetId.ToString(), this._adsClient.Address.Port);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ #region Dispose
|
|
|
|
+ protected virtual void Dispose(bool disposing)
|
|
|
|
+ {
|
|
|
|
+ log?.Info("Disposing AdsCommunication");
|
|
|
|
+ if (!disposedValue)
|
|
|
|
+ {
|
|
|
|
+ if (disposing)
|
|
|
|
+ {
|
|
|
|
+ this._adsClient?.Dispose();
|
|
|
|
+ this._adsClient = null;
|
|
|
|
+ this._adsCsvInfo?.Clear();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ disposedValue = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void IDisposable.Dispose()
|
|
|
|
+ {
|
|
|
|
+ Dispose(disposing: true);
|
|
|
|
+ GC.SuppressFinalize(this);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #endregion
|
|
|
|
+}
|