QMonClient.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. using System.ComponentModel.DataAnnotations;
  2. using System.IO.Abstractions;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Reflection;
  6. using System.Text;
  7. using Quadarax.Foundation.Core.Logging;
  8. using Quadarax.Foundation.Core.QMonitor.Attributes;
  9. using Quadarax.Foundation.Core.Reflection.Extensions;
  10. namespace Quadarax.Foundation.Core.QMonitor
  11. {
  12. public class QMonClient : QMonHostBase<QMonClientConfiuration>
  13. {
  14. #region *** Properties ***
  15. protected override Uri UriGeneral => Configuration.TargetUriGeneral;
  16. protected override Uri UriData => Configuration.TargetUriData;
  17. protected override TimeSpan IntervalGeneral => Configuration.EmitGeneralInterval;
  18. protected override TimeSpan? IntervalData => Configuration.EmitDataInterval;
  19. public string InstanceIdentifier { get; }
  20. #endregion
  21. #region *** Fields ***
  22. private IDictionary<string, MonItemGeneral> _generalCache = new Dictionary<string, MonItemGeneral>();
  23. private IList<MonData> _dataCache = new List<MonData>();
  24. #endregion
  25. #region *** Constructors ***
  26. public QMonClient(string instanceIdentifier, QMonClientConfiuration configuration, ILogger? externalLogger) : base(configuration, externalLogger)
  27. {
  28. InstanceIdentifier = instanceIdentifier ?? throw new ArgumentNullException(nameof(instanceIdentifier));
  29. ScanAssemblies();
  30. }
  31. private void ScanAssemblies()
  32. {
  33. var assemblies = AppDomain.CurrentDomain.GetAssemblies();
  34. foreach (var assembly in assemblies)
  35. {
  36. var types = assembly.GetTypesWithAttribute<MonitoredClassAttribute>(true);
  37. foreach (var type in types)
  38. {
  39. var monItemGeneral = new MonItemGeneral
  40. {
  41. Caption = type.GetCustomAttribute<MonitoredClassAttribute>()?.Caption ?? type.Name,
  42. Description = type.GetCustomAttribute<MonitoredClassAttribute>()?.Description ?? string.Empty,
  43. ViewType = MonItemGeneral.MonViewType.Item
  44. };
  45. var properties = type.GetPropertiesWithAttribute<MonitoredPropertyAttribute>(true);
  46. var keys = new List<Tuple<string, int>>();
  47. foreach (var property in properties)
  48. {
  49. var itemDescriptor = new MonItemGeneral.ItemDescriptor
  50. {
  51. Name = property.Name,
  52. TypeName = property.PropertyType.Name,
  53. Label = property.GetCustomAttribute<MonitoredPropertyAttribute>()?.Label ?? property.Name,
  54. Description = property.GetCustomAttribute<MonitoredPropertyAttribute>()?.Description ?? string.Empty,
  55. Order = property.GetCustomAttribute<MonitoredPropertyAttribute>()?.Order ?? 0,
  56. _propertyInfo = property
  57. };
  58. monItemGeneral.Items.Add(itemDescriptor);
  59. if (property.GetCustomAttribute<MonitoredPropertyKeyAttribute>() != null)
  60. {
  61. keys.Add(new Tuple<string, int>(property.Name, property.GetCustomAttribute<MonitoredPropertyKeyAttribute>()!.KeyOrder));
  62. }
  63. }
  64. foreach (var key in keys.OrderBy(x => x.Item2))
  65. {
  66. var ord = monItemGeneral.Items.FindIndex(x => x.Name == key.Item1);
  67. if (ord >= 0)
  68. monItemGeneral.KeyOrdinals.Add(ord);
  69. }
  70. _generalCache.Add(type.FullName!, monItemGeneral);
  71. }
  72. }
  73. }
  74. #endregion
  75. #region *** Public Operations ***
  76. public void Notify(object? monitoredItem)
  77. {
  78. if (monitoredItem == null) return;
  79. var fullName = monitoredItem.GetType().FullName;
  80. if (fullName != null && !_generalCache.ContainsKey(fullName)) throw new NotSupportedException($"Type {monitoredItem.GetType().FullName} is not flagged by MonitoredClassAttribute as monitored");
  81. var data = new MonData()
  82. {
  83. InstanceIdentifier = InstanceIdentifier,
  84. Name = fullName,
  85. Timestamp = DateTime.Now,
  86. Data = Binder.SaveToString(monitoredItem)
  87. };
  88. _dataCache.Add(data);
  89. }
  90. #endregion
  91. #region *** Protected Operations ***
  92. protected override void OnDisposing()
  93. {
  94. base.OnDisposing();
  95. _generalCache.Clear();
  96. }
  97. protected override void OnTimerData(object? state)
  98. {
  99. if (state == null) throw new ArgumentNullException(nameof(state));
  100. var owner = (QMonClient)state;
  101. if (_dataCache.Count == 0) return;
  102. try
  103. {
  104. var start = DateTime.Now;
  105. var itmCnt = 0;
  106. var declaredSize = 0;
  107. var sentSize = 0;
  108. var dataToSent = owner._dataCache.ToArray();
  109. foreach (var data in dataToSent)
  110. {
  111. var dataOut = Serialize(data);
  112. declaredSize += dataOut.Length;
  113. sentSize += owner.UdpClientData.Send(dataOut);
  114. _dataCache.Remove(data);
  115. itmCnt++;
  116. }
  117. Log(LogSeverityEnum.Trace, $"[{InstanceIdentifier}] Sent data blocks = {itmCnt} of delared {declaredSize} bytes/sent {sentSize} bytes @ {DateTime.Now.Subtract(start).TotalMilliseconds} ms.");
  118. }
  119. catch (Exception ex)
  120. {
  121. Log(LogSeverityEnum.Error, $"[{InstanceIdentifier}] Error sending general data", ex);
  122. }
  123. }
  124. protected override void OnTimerGeneral(object? state)
  125. {
  126. if (state == null) throw new ArgumentNullException(nameof(state));
  127. var owner = (QMonClient)state;
  128. try
  129. {
  130. var data = new MonGeneral()
  131. {
  132. InstanceIdentifier = owner.InstanceIdentifier,
  133. Items = new List<MonItemGeneral>()
  134. };
  135. data.Items.AddRange(owner._generalCache.Values);
  136. var dataOut = Serialize(data);
  137. UdpClientGeneral.Send(dataOut);
  138. Log(LogSeverityEnum.Trace, $"[{InstanceIdentifier}] Sent General packet size = {dataOut.Length} bytes");
  139. }
  140. catch (Exception ex)
  141. {
  142. Log(LogSeverityEnum.Error, $"[{InstanceIdentifier}] Error sending general data", ex);
  143. }
  144. }
  145. private byte[] Serialize(object data)
  146. {
  147. if (data == null) throw new ArgumentNullException(nameof(data));
  148. var json = Binder.SaveToString(data);
  149. return Encoding.UTF8.GetBytes(json);
  150. }
  151. protected override UdpClient CreateUdpClient(Uri targetUri)
  152. {
  153. var ep = new IPEndPoint(IPAddress.Parse(targetUri.DnsSafeHost), targetUri.Port);
  154. var udpClient = new UdpClient();
  155. udpClient.Connect(ep);
  156. return udpClient;
  157. }
  158. protected override void OnOpen()
  159. {
  160. Log(LogSeverityEnum.Info, $"Started.");
  161. }
  162. protected override void OnClose()
  163. {
  164. Log(LogSeverityEnum.Info, $"Closed.");
  165. }
  166. #endregion
  167. #region *** Nested Types ***
  168. #endregion
  169. }
  170. }