ConfirmWaitingMessageSerevice.cs 8.7 KB

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