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 bool _isInitialized; private string? _netID = null; private int _port = 0; private AdsClient? _adsClient; private List? _adsCsvInfo; private ITcpConnectNority? _connectionNotify; private bool disposedValue; private readonly ConcurrentDictionary _symbols = []; public bool IsConnected => _isConnected; public bool IsInitialized => _isInitialized; public bool Initialize(ITcpConnectNority connectionNotify) { if (this._adsClient is not null) return false; this._adsClient = new AdsClient(); this._adsClient.ConnectionStateChanged += AdsClient_ConnectionStateChanged; this._adsCsvInfo = []; this._connectionNotify = connectionNotify; _isInitialized = true; disposedValue = false; return true; } public bool Open(string netID, int port, int retry=10) { 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; } 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"); 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(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(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 symbolCollection, IList 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 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) { if (this._adsClient is not null) { this._adsClient.Dispose(); this._adsClient.ConnectionStateChanged -= AdsClient_ConnectionStateChanged; this._adsClient = null; _isInitialized = false; } this._adsCsvInfo?.Clear(); } disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } #endregion }