ConfirmWaitingMessageSerevice.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. using EVCB_OCPP.Domain;
  2. using EVCB_OCPP.Domain.Models.MainDb;
  3. using EVCB_OCPP.Packet.Features;
  4. using EVCB_OCPP.WSServer.Message;
  5. using Microsoft.EntityFrameworkCore;
  6. using Microsoft.EntityFrameworkCore.Query.Internal;
  7. using Microsoft.Extensions.Logging;
  8. using Microsoft.Identity.Client;
  9. using Newtonsoft.Json;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Threading.Tasks;
  15. namespace EVCB_OCPP.WSServer.Service
  16. {
  17. public enum ConfirmWaitingMessageRemoveReason
  18. {
  19. HitRetryLimit,
  20. Confirmed,
  21. }
  22. public class ConfirmWaitingMessageSerevice
  23. {
  24. private static readonly List<string> needConfirmActions = new List<string>()
  25. {
  26. "GetConfiguration",
  27. "ChangeConfiguration",
  28. "RemoteStartTransaction",
  29. "RemoteStopTransaction",
  30. "ChangeAvailability",
  31. "ClearCache",
  32. "DataTransfer",
  33. "Reset",
  34. "UnlockConnector",
  35. "TriggerMessage",
  36. "GetDiagnostics",
  37. "UpdateFirmware",
  38. "GetLocalListVersion",
  39. "SendLocalList",
  40. "SetChargingProfile",
  41. "ClearChargingProfile",
  42. "GetCompositeSchedule",
  43. "ReserveNow",
  44. "CancelReservation",
  45. "ExtendedTriggerMessage"
  46. };
  47. public static bool IsNeedConfirm(string action)
  48. {
  49. return needConfirmActions.Contains(action);
  50. }
  51. public ConfirmWaitingMessageSerevice(
  52. IDbContextFactory<MainDBContext> maindbContextFactory
  53. ,ILogger<ConfirmWaitingMessageSerevice> logger)
  54. {
  55. this.maindbContextFactory = maindbContextFactory;
  56. this.logger = logger;
  57. }
  58. public event EventHandler<ConfirmWaitingMessageRemoveReason> OnMessageRemoved;
  59. private readonly Object _lockConfirmPacketList = new object();
  60. private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
  61. private readonly ILogger<ConfirmWaitingMessageSerevice> logger;
  62. private readonly List<NeedConfirmMessage> needConfirmPacketList = new();
  63. private readonly Dictionary<string, MessageResultWaitObject> asyncWaitingTasks = new();
  64. internal async Task Add(string chargePointSerialNumber, int table_id, string requestId, string action, string msg_id, string createdBy, string sendMessage)
  65. {
  66. NeedConfirmMessage _needConfirmMsg = new NeedConfirmMessage();
  67. _needConfirmMsg.Id = table_id;
  68. _needConfirmMsg.SentAction = action;
  69. _needConfirmMsg.SentOn = DateTime.UtcNow;
  70. _needConfirmMsg.SentTimes = 4;
  71. _needConfirmMsg.ChargePointSerialNumber = chargePointSerialNumber;
  72. _needConfirmMsg.RequestId = requestId;
  73. _needConfirmMsg.SentUniqueId = msg_id;
  74. _needConfirmMsg.CreatedBy = createdBy;
  75. _needConfirmMsg.SentMessage = sendMessage;
  76. if (needConfirmActions.Contains(action))
  77. {
  78. lock (_lockConfirmPacketList)
  79. {
  80. needConfirmPacketList.Add(_needConfirmMsg);
  81. }
  82. }
  83. #region 更新資料表單一欄位
  84. var dateTimeNow = DateTime.UtcNow;
  85. var _UpdatedItem = new ServerMessage() { Id = table_id, UpdatedOn = dateTimeNow };
  86. //db.Configuration.AutoDetectChangesEnabled = false;//自動呼叫DetectChanges()比對所有的entry集合的每一個屬性Properties的新舊值
  87. //db.Configuration.ValidateOnSaveEnabled = false;// 因為Entity有些欄位必填,若不避開會有Validate錯誤
  88. // var _UpdatedItem = db.ServerMessage.Where(x => x.Id == item.Id).FirstOrDefault();
  89. using (var db = await maindbContextFactory.CreateDbContextAsync())
  90. {
  91. db.ServerMessage.Attach(_UpdatedItem);
  92. _UpdatedItem.UpdatedOn = dateTimeNow;
  93. db.Entry(_UpdatedItem).Property(x => x.UpdatedOn).IsModified = true;// 可以直接使用這方式強制某欄位要更新,只是查詢集合耗效能而己
  94. await db.SaveChangesAsync();
  95. db.ChangeTracker.Clear();
  96. }
  97. #endregion
  98. }
  99. internal List<NeedConfirmMessage> GetPendingMessages()
  100. {
  101. List<NeedConfirmMessage> sendMessages = new List<NeedConfirmMessage>();
  102. lock (_lockConfirmPacketList)
  103. {
  104. sendMessages = needConfirmPacketList.Where(x => x.SentTimes > 1 && x.CreatedBy == "Server").ToList();
  105. }
  106. return sendMessages;
  107. }
  108. internal async Task<bool> TryConfirmMessage(MessageResult analysisResult)
  109. {
  110. bool confirmed = false;
  111. //if (needConfirmActions.Contains(analysisResult.Action))
  112. if (!IsNeedConfirm(analysisResult.Action))
  113. {
  114. return confirmed;
  115. }
  116. NeedConfirmMessage foundRequest = null;
  117. lock (_lockConfirmPacketList)
  118. {
  119. foundRequest = needConfirmPacketList.Where(x => x.SentUniqueId == analysisResult.UUID).FirstOrDefault();
  120. }
  121. if (foundRequest != null && foundRequest.Id > 0)
  122. {
  123. RemoveWaitingMsg(foundRequest, ConfirmWaitingMessageRemoveReason.Confirmed, analysisResult);
  124. foundRequest.SentTimes = 0;
  125. foundRequest.SentInterval = 0;
  126. analysisResult.RequestId = foundRequest.RequestId;
  127. using (var db = await maindbContextFactory.CreateDbContextAsync())
  128. {
  129. var sc = await db.ServerMessage.Where(x => x.Id == foundRequest.Id).FirstOrDefaultAsync();
  130. sc.InMessage = JsonConvert.SerializeObject(analysisResult.Message, Formatting.None);
  131. sc.ReceivedOn = DateTime.UtcNow;
  132. await db.SaveChangesAsync();
  133. // Console.WriteLine(string.Format("Now:{0} ServerMessage Id:{1} ", DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss"), foundRequest.Id));
  134. }
  135. confirmed = true;
  136. }
  137. else if (analysisResult.Action == Actions.TriggerMessage.ToString())
  138. {
  139. confirmed = true;
  140. }
  141. else
  142. {
  143. logger.LogError(string.Format("Received no record Action:{0} MessageId:{1} ", analysisResult.Action, analysisResult.UUID));
  144. }
  145. return confirmed;
  146. }
  147. internal void SignalMessageSended(NeedConfirmMessage resendItem)
  148. {
  149. var dateTimeNow = DateTime.UtcNow;
  150. resendItem.SentTimes--;
  151. resendItem.SentOn = dateTimeNow;
  152. }
  153. internal void RemoveExpiredConfirmMessage()
  154. {
  155. var before10Mins = DateTime.UtcNow.AddMinutes(-10);
  156. var removeList = needConfirmPacketList.Where(x => x.SentTimes == 0 || x.SentOn < before10Mins).ToList();
  157. foreach (var item in removeList)
  158. {
  159. RemoveWaitingMsg(item, ConfirmWaitingMessageRemoveReason.HitRetryLimit);
  160. }
  161. }
  162. internal async Task<MessageResult> WaitResultAsync(string msgId, int maxWaitSec = 65000)
  163. {
  164. var waiObj = new MessageResultWaitObject();
  165. asyncWaitingTasks.Add(msgId, waiObj);
  166. var task = waiObj.Lock.WaitAsync();
  167. var completedTask = await Task.WhenAny(task, Task.Delay(650_000));
  168. return waiObj.Result;
  169. }
  170. private void RemoveWaitingMsg(
  171. NeedConfirmMessage msg,
  172. ConfirmWaitingMessageRemoveReason reason,
  173. MessageResult response = null)
  174. {
  175. lock (_lockConfirmPacketList)
  176. {
  177. needConfirmPacketList.Remove(msg);
  178. }
  179. if (asyncWaitingTasks.Keys.Contains(msg.RequestId))
  180. {
  181. asyncWaitingTasks[msg.RequestId].Result = response;
  182. asyncWaitingTasks[msg.RequestId].Lock.Release();
  183. }
  184. OnMessageRemoved?.Invoke(this, reason);
  185. }
  186. }
  187. internal class MessageResultWaitObject
  188. {
  189. public MessageResult Result { get; set; } = null;
  190. public SemaphoreSlim Lock { get; set; } = new SemaphoreSlim(0);
  191. }
  192. }