QMonReceiver.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. using System.Collections.Concurrent;
  2. using System.IO.Abstractions;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. using System.Text.Json;
  7. using System.Text.Json.Serialization;
  8. using Quadarax.Foundation.Core.Logging;
  9. namespace qmonlib
  10. {
  11. public delegate void GeneralDataReceivedDelegate(MonGeneral general);
  12. public delegate void DataReceivedDelegate(MonData[] data);
  13. public class QMonReceiver : QMonHostBase<QMonReceiverConfiguration>
  14. {
  15. #region *** Properties ***
  16. protected override Uri UriGeneral => Configuration.SourceUriGeneral;
  17. protected override Uri UriData => Configuration.SourceUriData;
  18. protected override TimeSpan IntervalGeneral => Configuration.GeneralReceiverInterval;
  19. protected override TimeSpan? IntervalData => Configuration.DataCache.FlushCacheInterval;
  20. public MonGeneral[] AvailableGeneral => _generalCache.Values.Select(x=>x.Data).ToArray();
  21. #endregion
  22. #region *** Fields ***
  23. private GeneralDataReceivedDelegate _generalReceivedCallback;
  24. private DataReceivedDelegate _dataReceivedCallback;
  25. private bool _isReceivingGeneral;
  26. private CancellationTokenSource _dataReceiverCancel;
  27. private IDictionary<string, MonReceiverGeneralCahedItem> _generalCache = new ConcurrentDictionary<string, MonReceiverGeneralCahedItem>();
  28. private IList<MonData> _dataCache = new List<MonData>();
  29. private JsonSerializerOptions _jsonSerializerOptions;
  30. private DateTime _lastSavedTimestamp;
  31. private IFileSystem _fileSystem;
  32. #endregion
  33. #region *** Constructors ***
  34. public QMonReceiver(IFileSystem fileSystem, QMonReceiverConfiguration configuration, ILogger logger, GeneralDataReceivedDelegate generalDataReceivedCallback = null, DataReceivedDelegate liveDataReceivedCallback = null) : base(configuration, logger)
  35. {
  36. _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
  37. _lastSavedTimestamp = DateTime.Now;
  38. _generalReceivedCallback = generalDataReceivedCallback;
  39. _dataReceivedCallback = liveDataReceivedCallback;
  40. _jsonSerializerOptions = new JsonSerializerOptions
  41. {
  42. IgnoreNullValues = true,
  43. WriteIndented = false,
  44. PropertyNameCaseInsensitive = true,
  45. PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
  46. MaxDepth = 10
  47. };
  48. _jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
  49. Configuration.DataCache.Path = Configuration.DataCache.Path.Replace("%AppData%",
  50. Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
  51. if (!_fileSystem.Directory.Exists(Configuration.DataCache.Path))
  52. {
  53. _fileSystem.Directory.CreateDirectory(Configuration.DataCache.Path);
  54. Log(LogSeverityEnum.Debug,$"Data receiver created data cache directory '{Configuration.DataCache.Path}'");
  55. }
  56. }
  57. #endregion
  58. #region *** Private overrides ***
  59. protected override void OnOpen()
  60. {
  61. var _dataReceiverCancel = new CancellationTokenSource();
  62. new Thread(() =>
  63. {
  64. try
  65. {
  66. Log(LogSeverityEnum.Debug, "Data receiver thread started.");
  67. while (!_dataReceiverCancel.Token.IsCancellationRequested)
  68. OnThreadData(_dataReceiverCancel.Token);
  69. }
  70. catch (OperationCanceledException)
  71. {
  72. Log(LogSeverityEnum.Info, $"Data receiver thread on {UriData} unexpected stopped");
  73. }
  74. Log(LogSeverityEnum.Debug, "Data receiver thread stoped.");
  75. }).Start();
  76. Log(LogSeverityEnum.Info, $"Data receiver thread on {UriData} started");
  77. }
  78. protected override void OnClose()
  79. {
  80. _dataReceiverCancel?.Cancel();
  81. Log(LogSeverityEnum.Info, $"Data receiver thread on {UriData} stopped");
  82. }
  83. protected override void OnTimerData(object? state)
  84. {
  85. _lastSavedTimestamp = DateTime.Now;
  86. var itemsToSave = _dataCache.Where(x => x.Timestamp < _lastSavedTimestamp).ToArray();
  87. var instances = itemsToSave.Select(x => x.InstanceIdentifier).Distinct();
  88. foreach (var instance in instances)
  89. {
  90. var dirName = _fileSystem.Path.Combine(Configuration.DataCache.Path, instance);
  91. if (_fileSystem.Directory.Exists(dirName))
  92. {
  93. _fileSystem.Directory.CreateDirectory(dirName);
  94. Log(LogSeverityEnum.Debug,$"Data receiver created data cache directory '{dirName}'");
  95. }
  96. var batchData = itemsToSave.Where(x => x.InstanceIdentifier == instance).OrderBy(x=>x.Timestamp).ToArray();
  97. _dataReceivedCallback?.Invoke(batchData);
  98. var dfrom = batchData.Min(x => x.Timestamp).ToFileTimeUtc();
  99. var dto = batchData.Max(x => x.Timestamp).ToFileTimeUtc();
  100. var sbFileName = new StringBuilder();
  101. sbFileName.Append("mondata_").Append(dfrom).Append("-").Append(dto).Append(".json");
  102. var fileName = _fileSystem.Path.Combine(dirName, sbFileName.ToString());
  103. using (var streamWriter = File.CreateText(fileName))
  104. {
  105. JsonSerializer.Serialize(streamWriter.BaseStream, batchData, _jsonSerializerOptions);
  106. streamWriter.Flush();
  107. }
  108. Log(LogSeverityEnum.Trace, $"Data receiver created dump data file '{fileName}'");
  109. }
  110. foreach (var item in itemsToSave)
  111. _dataCache.Remove(item);
  112. }
  113. protected override void OnTimerGeneral(object? state)
  114. {
  115. if (state == null) throw new ArgumentNullException(nameof(state));
  116. var owner = (QMonReceiver)state;
  117. if (owner._isReceivingGeneral) return;
  118. owner._isReceivingGeneral = true;
  119. try
  120. {
  121. var remoteEndPoint = new IPEndPoint(IPAddress.Parse(owner.UriGeneral.DnsSafeHost), owner.UriGeneral.Port);
  122. var data = UdpClientGeneral.Receive(ref remoteEndPoint);
  123. var general = (MonGeneral) Binder.LoadFromString(Encoding.UTF8.GetString(data), typeof(MonGeneral));
  124. Log(LogSeverityEnum.Debug,
  125. $"[ReceiverGeneral] receive general data from '{remoteEndPoint}' [{general.InstanceIdentifier}] size={data.Length} bytes.");
  126. _generalCache.TryAdd(general.InstanceIdentifier, new MonReceiverGeneralCahedItem(DateTime.Now, remoteEndPoint, general));
  127. _generalReceivedCallback?.Invoke(general);
  128. }
  129. catch (Exception e)
  130. {
  131. Log(LogSeverityEnum.Error, $"[ReceiverGeneral] Error receiving general data", e);
  132. }
  133. finally
  134. {
  135. owner._isReceivingGeneral = false;
  136. }
  137. }
  138. private void OnThreadData(CancellationToken cancellation)
  139. {
  140. if (cancellation == null) throw new ArgumentNullException(nameof(cancellation));
  141. if (cancellation.IsCancellationRequested)
  142. {
  143. Log(LogSeverityEnum.Debug, $"[ReceiverData] Cancellation token detected on listener '{UriData}'.");
  144. }
  145. try
  146. {
  147. if (UdpClientData.Available==0) return;
  148. var remoteEndPoint = new IPEndPoint(IPAddress.Parse(this.UriData.DnsSafeHost), this.UriData.Port);
  149. var data = UdpClientData.Receive(ref remoteEndPoint);
  150. var monData = JsonSerializer.Deserialize<MonData>(data, _jsonSerializerOptions);
  151. if (monData == null)
  152. {
  153. Log(LogSeverityEnum.Warn, $"[ReceiverData] cannot deserialize monitor data from '{remoteEndPoint}' size={data.Length} bytes. Skipped.");
  154. return;
  155. }
  156. _dataCache.Add(monData);
  157. Log(LogSeverityEnum.Debug, $"[ReceiverData] receive monitor data from '{remoteEndPoint}' size={data.Length} bytes.");
  158. }
  159. catch (Exception e)
  160. {
  161. Log(LogSeverityEnum.Error, $"[ReceiverData] Error receiving monitor data", e);
  162. }
  163. }
  164. protected override UdpClient CreateUdpClient(Uri targetUri)
  165. {
  166. var udpClient = new UdpClient(targetUri.Port);
  167. return udpClient;
  168. }
  169. protected override void OnDisposing()
  170. {
  171. _dataReceiverCancel?.Cancel();
  172. _dataReceiverCancel?.Dispose();
  173. _dataReceiverCancel = null;
  174. base.OnDisposing();
  175. }
  176. #endregion
  177. }
  178. }