AdsClientService.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. using TwinCAT;
  2. using TwinCAT.Ads;
  3. using TwinCAT.Ads.TypeSystem;
  4. namespace PLCIOPointTool.Services;
  5. public class AdsClientService : IClientService
  6. {
  7. private readonly ILogService _logService;
  8. private readonly AdsClient _adsClient;
  9. private readonly Timer _timer;
  10. private bool _previousCheckState = false;
  11. private bool disposedValue;
  12. public AdsClientService(ILogService logService)
  13. {
  14. _logService = logService;
  15. _adsClient = new AdsClient();
  16. _adsClient.ConnectionStateChanged += OnAdsClientConnectionStateChanged;
  17. _timer = new Timer(OnHealthCheck, null, Timeout.Infinite, Timeout.Infinite);
  18. }
  19. public event EventHandler<ConnectionState>? ConnectionStateChanged;
  20. public event EventHandler<bool>? HealthCheckingChanged;
  21. public async Task ConnectAsync(string netId, int port)
  22. {
  23. if (string.IsNullOrWhiteSpace(netId) || port < 0)
  24. {
  25. _logService.Warning("Invalid connection parameters");
  26. return;
  27. }
  28. try
  29. {
  30. await _adsClient.ConnectAsync(new AmsAddress(netId, port), new CancellationToken());
  31. if (_adsClient.TryReadState(out _) != AdsErrorCode.NoError)
  32. {
  33. OnAdsClientConnectionStateChanged(
  34. null,
  35. new ConnectionStateChangedEventArgs(
  36. ConnectionStateChangedReason.Established,
  37. ConnectionState.Disconnected,
  38. ConnectionState.Disconnected));
  39. _logService.Warning("Failed to connect ADS server, please try later");
  40. return;
  41. }
  42. _logService.Info("Succeeded to connect ADS server");
  43. _timer.Change(3000, 3000);
  44. }
  45. catch (Exception ex)
  46. {
  47. _logService.Error($"Got an exception while connecting; {ex}");
  48. }
  49. }
  50. public async Task DisconnectAsync()
  51. {
  52. try
  53. {
  54. await _adsClient.DisconnectAsync(new CancellationToken());
  55. _logService.Info("Succeeded to disconnect ADS server");
  56. }
  57. catch (Exception ex)
  58. {
  59. _logService.Error($"Got an exception while disconnecting; {ex}");
  60. }
  61. finally
  62. {
  63. _timer.Change(Timeout.Infinite, Timeout.Infinite);
  64. }
  65. }
  66. public async Task<object?> ReadValue(string symbolName)
  67. {
  68. if (string.IsNullOrWhiteSpace(symbolName))
  69. {
  70. _logService.Warning("Invalid variable path");
  71. return null;
  72. }
  73. try
  74. {
  75. var symbol = await _adsClient.ReadSymbolAsync(symbolName, new CancellationToken());
  76. if (symbol is null || symbol.Value is null || symbol.Succeeded == false)
  77. {
  78. _logService.Warning($"Failed to get symbol of {symbolName}");
  79. return null;
  80. }
  81. var symbolValue = await _adsClient.ReadValueAsync(symbol.Value, new CancellationToken());
  82. if (symbolValue is null || symbolValue.Value is null || symbolValue.Succeeded == false)
  83. {
  84. _logService.Warning($"Failed to get value of {symbolName}");
  85. return null;
  86. }
  87. _logService.Info($"Succeeded to get value of {symbolName}");
  88. return symbolValue.Value;
  89. }
  90. catch (Exception ex)
  91. {
  92. _logService.Error($"Got an exception while reading; {ex}");
  93. return null;
  94. }
  95. }
  96. public async Task WriteValue(string symbolName, object value)
  97. {
  98. if (string.IsNullOrWhiteSpace(symbolName) || value is null)
  99. {
  100. _logService.Warning("Invalid variable path or value");
  101. return;
  102. }
  103. try
  104. {
  105. var resultValue = await _adsClient.WriteSymbolAsync(symbolName, value, new CancellationToken());
  106. if (resultValue is null || resultValue.Succeeded == false)
  107. {
  108. _logService.Warning($"Failed to set value of {symbolName}");
  109. return;
  110. }
  111. _logService.Info($"Succeeded to set value of {symbolName}");
  112. }
  113. catch (Exception ex)
  114. {
  115. _logService.Error($"Got an exception while reading; {ex}");
  116. }
  117. }
  118. private void OnAdsClientConnectionStateChanged(object? sender, TwinCAT.ConnectionStateChangedEventArgs e)
  119. {
  120. try
  121. {
  122. ConnectionStateChanged?.Invoke(null, e.NewState);
  123. }
  124. catch (Exception ex)
  125. {
  126. _logService.Error($"Got an exception; {ex}");
  127. }
  128. }
  129. private void OnHealthCheck(object? state)
  130. {
  131. var errorCode = _adsClient.TryReadState(out _);
  132. if (errorCode != AdsErrorCode.NoError)
  133. {
  134. _logService.Warning("Health Checking Failed");
  135. if (_previousCheckState == true)
  136. {
  137. _previousCheckState = false;
  138. HealthCheckingChanged?.Invoke(null, false);
  139. }
  140. return;
  141. }
  142. if (_previousCheckState == false)
  143. {
  144. _previousCheckState = true;
  145. HealthCheckingChanged?.Invoke(null, true);
  146. }
  147. //_logService.Info("Health Checking succeeded");
  148. }
  149. #region Dispose
  150. protected virtual void Dispose(bool disposing)
  151. {
  152. if (!disposedValue)
  153. {
  154. if (disposing)
  155. {
  156. // TODO: dispose managed state (managed objects)
  157. }
  158. // TODO: free unmanaged resources (unmanaged objects) and override finalizer
  159. // TODO: set large fields to null
  160. _timer.Change(Timeout.Infinite, Timeout.Infinite);
  161. _timer.Dispose();
  162. _adsClient.Dispose();
  163. _adsClient.ConnectionStateChanged -= OnAdsClientConnectionStateChanged;
  164. disposedValue = true;
  165. }
  166. }
  167. // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
  168. // ~AdsClientService()
  169. // {
  170. // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  171. // Dispose(disposing: false);
  172. // }
  173. public void Dispose()
  174. {
  175. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  176. Dispose(disposing: true);
  177. GC.SuppressFinalize(this);
  178. }
  179. #endregion
  180. }