QMonClient.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. using System.Net;
  2. using System.Net.Sockets;
  3. using System.Reflection;
  4. using System.Text;
  5. using Quadarax.Foundation.Core.Collections;
  6. using Quadarax.Foundation.Core.Logging;
  7. using Quadarax.Foundation.Core.QMonitor.Attributes;
  8. using Quadarax.Foundation.Core.Reflection.Extensions;
  9. namespace Quadarax.Foundation.Core.QMonitor
  10. {
  11. public class QMonClient : QMonHostBase<QMonClientConfiuration>
  12. {
  13. #region *** Properties ***
  14. protected override Uri UriGeneral => Configuration.TargetUriGeneral;
  15. protected override Uri UriData => Configuration.TargetUriData;
  16. protected override TimeSpan IntervalGeneral => Configuration.EmitGeneralInterval;
  17. protected override TimeSpan? IntervalData => Configuration.EmitDataInterval;
  18. public string InstanceIdentifier { get; }
  19. #endregion
  20. #region *** Fields ***
  21. private readonly IDictionary<string, MonItemGeneral> _generalCache = new Dictionary<string, MonItemGeneral>();
  22. private readonly IList<MonData> _dataCache = new LockedList<MonData>();
  23. #endregion
  24. #region *** Constructors ***
  25. public QMonClient(string instanceIdentifier, QMonClientConfiuration configuration, ILogger? externalLogger) : base(configuration, externalLogger)
  26. {
  27. InstanceIdentifier = instanceIdentifier ?? throw new ArgumentNullException(nameof(instanceIdentifier));
  28. ScanAssemblies();
  29. }
  30. private void ScanAssemblies()
  31. {
  32. var assemblies = AppDomain.CurrentDomain.GetAssemblies();
  33. foreach (var assembly in assemblies)
  34. {
  35. var types = assembly.GetTypesWithAttribute<MonitoredClassAttribute>(true);
  36. foreach (var type in types)
  37. {
  38. var monItemGeneral = new MonItemGeneral
  39. {
  40. Caption = type.GetCustomAttribute<MonitoredClassAttribute>()?.Caption ?? type.Name,
  41. Description = type.GetCustomAttribute<MonitoredClassAttribute>()?.Description ?? string.Empty,
  42. ViewType = MonItemGeneral.MonViewType.Item,
  43. Name = type.FullName,
  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. TimestampFtUtc = DateTime.Now.ToFileTimeUtc(),
  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 async Task OnTimerData(QMonWorkerContext context)
  98. {
  99. if (_dataCache.Count == 0) return;
  100. try
  101. {
  102. var start = DateTime.Now;
  103. var itmCnt = 0;
  104. var declaredSize = 0;
  105. var sentSize = 0;
  106. var dataToSent = _dataCache.ToArray();
  107. foreach (var data in dataToSent)
  108. {
  109. var dataOut = Serialize(data);
  110. declaredSize += dataOut.Length;
  111. sentSize += await context.Client.SendAsync(dataOut, context.Cancel);
  112. _dataCache.Remove(data);
  113. itmCnt++;
  114. }
  115. Log(LogSeverityEnum.Trace, $"[{InstanceIdentifier}] Sent data blocks = {itmCnt} of declared {declaredSize} bytes/sent {sentSize} bytes @ {DateTime.Now.Subtract(start).TotalMilliseconds} ms.");
  116. }
  117. catch (Exception ex)
  118. {
  119. Log(LogSeverityEnum.Error, $"[{InstanceIdentifier}] Error sending general data", ex);
  120. }
  121. }
  122. protected override async Task OnTimerGeneral(QMonWorkerContext context)
  123. {
  124. try
  125. {
  126. var data = new MonGeneral()
  127. {
  128. InstanceIdentifier = InstanceIdentifier,
  129. Items = new List<MonItemGeneral>()
  130. };
  131. data.Items.AddRange(_generalCache.Values);
  132. var dataOut = Serialize(data);
  133. await context.Client.SendAsync(dataOut, context.Cancel);
  134. Log(LogSeverityEnum.Trace, $"[{InstanceIdentifier}] Sent General packet size = {dataOut.Length} bytes");
  135. }
  136. catch (Exception ex)
  137. {
  138. Log(LogSeverityEnum.Error, $"[{InstanceIdentifier}] Error sending general data", ex);
  139. }
  140. }
  141. private byte[] Serialize(object data)
  142. {
  143. if (data == null) throw new ArgumentNullException(nameof(data));
  144. var json = Binder.SaveToString(data);
  145. return Encoding.UTF8.GetBytes(json);
  146. }
  147. protected override UdpClient CreateUdpClient(Uri targetUri)
  148. {
  149. var ep = new IPEndPoint(IPAddress.Parse(targetUri.DnsSafeHost), targetUri.Port);
  150. var udpClient = new UdpClient();
  151. udpClient.Connect(ep);
  152. return udpClient;
  153. }
  154. protected override void OnOpen()
  155. {
  156. Log(LogSeverityEnum.Info, $"Started.");
  157. }
  158. protected override void OnClose()
  159. {
  160. Log(LogSeverityEnum.Info, $"Closed.");
  161. }
  162. #endregion
  163. #region *** Nested Types ***
  164. #endregion
  165. }
  166. }