MainDbService.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using EVCB_OCPP.Domain;
  2. using EVCB_OCPP.Domain.Models.Database;
  3. using EVCB_OCPP.Packet.Messages.Core;
  4. using EVCB_OCPP.WSServer.Helper;
  5. using Microsoft.EntityFrameworkCore;
  6. using Microsoft.Extensions.Configuration;
  7. using Microsoft.Extensions.DependencyInjection;
  8. using Microsoft.Extensions.Logging;
  9. using Newtonsoft.Json;
  10. using OCPPPackage.Profiles;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Linq;
  14. using System.Text;
  15. using System.Threading;
  16. using System.Threading.Tasks;
  17. namespace EVCB_OCPP.WSServer.Service;
  18. public interface IMainDbService
  19. {
  20. Task<string> GetMachineAuthorizationKey(string ChargeBoxId);
  21. Task<string> GetMachineConfiguration(string ChargeBoxId, string configName);
  22. Task<string> GetMachineHeartbeatInterval(string ChargeBoxId);
  23. Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId);
  24. Task<string> GetMachineSecurityProfile(string ChargeBoxId);
  25. Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine);
  26. Task AddOCMF(OCMF oCMF);
  27. Task<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId);
  28. Task UpdateConnectorStatus(string Id, ConnectorStatus connectorStatus);
  29. Task AddServerMessage(ServerMessage message);
  30. Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy = "", DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "");
  31. }
  32. public class MainDbService : IMainDbService
  33. {
  34. public MainDbService(IDbContextFactory<MainDBContext> contextFactory, IConfiguration configuration, ILoggerFactory loggerFactory)
  35. {
  36. this.contextFactory = contextFactory;
  37. this.loggerFactory = loggerFactory;
  38. var startupLimit = GetStartupLimit(configuration);
  39. this.startupSemaphore = new (startupLimit);
  40. var opLimit = GetOpLimit(configuration);
  41. this.opSemaphore = new SemaphoreSlim(opLimit);
  42. InitUpdateConnectorStatusHandler();
  43. InitUpdateMachineBasicInfoHandler();
  44. InitAddServerMessageHandler();
  45. }
  46. private readonly IDbContextFactory<MainDBContext> contextFactory;
  47. private readonly ILoggerFactory loggerFactory;
  48. private readonly QueueSemaphore startupSemaphore;
  49. private readonly SemaphoreSlim opSemaphore;
  50. private GroupSingleHandler<StatusNotificationParam> statusNotificationHandler;
  51. private GroupSingleHandler<UpdateMachineBasicInfoParam> updateMachineBasicInfoHandler;
  52. private GroupSingleHandler<ServerMessage> addServerMessageHandler;
  53. public async Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId)
  54. {
  55. using var semaphoreWrapper = await startupSemaphore.GetToken();
  56. using var db = contextFactory.CreateDbContext();
  57. var machine = await db.Machine.Where(x => x.ChargeBoxId == ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).AsNoTracking().FirstOrDefaultAsync();
  58. if (machine == null)
  59. {
  60. return new MachineAndCustomerInfo(string.Empty, Guid.Empty, "Unknown");
  61. }
  62. var customerName = await db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefaultAsync();
  63. return new MachineAndCustomerInfo(machine.Id, machine.CustomerId, customerName);
  64. }
  65. public async Task<string> GetMachineConfiguration(string ChargeBoxId, string configName)
  66. {
  67. using var semaphoreWrapper = await startupSemaphore.GetToken();
  68. using var db = contextFactory.CreateDbContext();
  69. return await db.MachineConfigurations
  70. .Where(x => x.ChargeBoxId == ChargeBoxId && x.ConfigureName == configName)
  71. .Select(x => x.ConfigureSetting).FirstOrDefaultAsync();
  72. }
  73. public async Task<string> GetMachineSecurityProfile(string ChargeBoxId)
  74. {
  75. return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.SecurityProfile);
  76. }
  77. public async Task<string> GetMachineAuthorizationKey(string ChargeBoxId)
  78. {
  79. return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.AuthorizationKey);
  80. }
  81. public async Task<string> GetMachineHeartbeatInterval(string ChargeBoxId)
  82. {
  83. return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.HeartbeatInterval);
  84. }
  85. public async Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine)
  86. {
  87. //using var semaphoreWrapper = await startupSemaphore.GetToken();
  88. //using var db = await contextFactory.CreateDbContextAsync();
  89. //var _machine = db.Machine.FirstOrDefault(x => x.ChargeBoxId == ChargeBoxId);
  90. //_machine.ChargeBoxSerialNumber = machine.ChargeBoxSerialNumber;
  91. //_machine.ChargePointSerialNumber = machine.ChargePointSerialNumber;
  92. //_machine.ChargePointModel = machine.ChargePointModel;
  93. //_machine.ChargePointVendor = machine.ChargePointVendor;
  94. //_machine.FW_CurrentVersion = machine.FW_CurrentVersion;
  95. //_machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
  96. //_machine.Imsi = machine.Imsi;
  97. //_machine.MeterSerialNumber = machine.MeterSerialNumber;
  98. //_machine.MeterType = machine.MeterType;
  99. //await db.SaveChangesAsync();
  100. //using var semaphoreWrapper = await startupSemaphore.GetToken();
  101. await updateMachineBasicInfoHandler.HandleAsync(new UpdateMachineBasicInfoParam(ChargeBoxId, machine));
  102. }
  103. public async Task AddOCMF(OCMF oCMF)
  104. {
  105. using var db = contextFactory.CreateDbContext();
  106. db.OCMF.Add(oCMF);
  107. await db.SaveChangesAsync();
  108. }
  109. public async Task<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId)
  110. {
  111. using var db = contextFactory.CreateDbContext();
  112. return await db.ConnectorStatus.Where(x => x.ChargeBoxId == ChargeBoxId
  113. && x.ConnectorId == ConnectorId).AsNoTracking().FirstOrDefaultAsync();
  114. }
  115. public async Task UpdateConnectorStatus(string Id, ConnectorStatus Status)
  116. {
  117. using var db = await contextFactory.CreateDbContextAsync();
  118. ConnectorStatus status = new() { Id = Id };
  119. db.ChangeTracker.AutoDetectChangesEnabled = false;
  120. db.ConnectorStatus.Attach(status);
  121. status.CreatedOn = Status.CreatedOn;
  122. status.Status = Status.Status;
  123. status.ChargePointErrorCodeId = Status.ChargePointErrorCodeId;
  124. status.ErrorInfo = Status.ErrorInfo;
  125. status.VendorId = Status.VendorId;
  126. status.VendorErrorCode = Status.VendorErrorCode;
  127. db.Entry(status).Property(x => x.CreatedOn).IsModified = true;
  128. db.Entry(status).Property(x => x.Status).IsModified = true;
  129. db.Entry(status).Property(x => x.ChargePointErrorCodeId).IsModified = true;
  130. db.Entry(status).Property(x => x.ErrorInfo).IsModified = true;
  131. db.Entry(status).Property(x => x.VendorId).IsModified = true;
  132. db.Entry(status).Property(x => x.VendorErrorCode).IsModified = true;
  133. await db.SaveChangesAsync();
  134. //await statusNotificationHandler.HandleAsync(new StatusNotificationParam(Id, Status));
  135. return;
  136. }
  137. public Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy, DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "")
  138. {
  139. if (string.IsNullOrEmpty(CreatedBy))
  140. {
  141. CreatedBy = "Server";
  142. }
  143. if (string.IsNullOrEmpty(SerialNo))
  144. {
  145. SerialNo = Guid.NewGuid().ToString();
  146. }
  147. var _CreatedOn = CreatedOn ?? DateTime.UtcNow;
  148. string _OutRequest = "";
  149. if (OutRequest is not null)
  150. {
  151. _OutRequest = JsonConvert.SerializeObject(
  152. OutRequest,
  153. new JsonSerializerSettings()
  154. {
  155. NullValueHandling = NullValueHandling.Ignore,
  156. Formatting = Formatting.None
  157. });
  158. }
  159. return AddServerMessage(new ServerMessage()
  160. {
  161. ChargeBoxId = ChargeBoxId,
  162. CreatedBy = CreatedBy,
  163. CreatedOn = _CreatedOn,
  164. OutAction = OutAction,
  165. OutRequest = _OutRequest,
  166. SerialNo = SerialNo,
  167. InMessage = InMessage
  168. });
  169. }
  170. public Task AddServerMessage(ServerMessage message)
  171. {
  172. return addServerMessageHandler.HandleAsync(message);
  173. }
  174. private void InitUpdateMachineBasicInfoHandler()
  175. {
  176. if (updateMachineBasicInfoHandler is not null)
  177. {
  178. throw new Exception($"{nameof(InitUpdateMachineBasicInfoHandler)} should only called once");
  179. }
  180. updateMachineBasicInfoHandler = new GroupSingleHandler<UpdateMachineBasicInfoParam>(
  181. handleFunc: BundelUpdateMachineBasicInfo,
  182. logger: loggerFactory.CreateLogger("UpdateMachineBasicInfoHandler"))
  183. {
  184. WorkerCnt = 10
  185. };
  186. }
  187. private async Task BundelUpdateMachineBasicInfo(IEnumerable<UpdateMachineBasicInfoParam> pams)
  188. {
  189. using var db = await contextFactory.CreateDbContextAsync();
  190. using var trans = await db.Database.BeginTransactionAsync();
  191. pams = pams.DistinctBy(x => x.ChargeBoxId);
  192. foreach (var pam in pams)
  193. {
  194. var _machine = db.Machine.FirstOrDefault(x => x.ChargeBoxId == pam.ChargeBoxId);
  195. _machine.ChargeBoxSerialNumber = pam.machine.ChargeBoxSerialNumber;
  196. _machine.ChargePointSerialNumber = pam.machine.ChargePointSerialNumber;
  197. _machine.ChargePointModel = pam.machine.ChargePointModel;
  198. _machine.ChargePointVendor = pam.machine.ChargePointVendor;
  199. _machine.FW_CurrentVersion = pam.machine.FW_CurrentVersion;
  200. _machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
  201. _machine.Imsi = pam.machine.Imsi;
  202. _machine.MeterSerialNumber = pam.machine.MeterSerialNumber;
  203. _machine.MeterType = pam.machine.MeterType;
  204. }
  205. db.SaveChanges();
  206. trans.Commit();
  207. }
  208. private void InitUpdateConnectorStatusHandler()
  209. {
  210. if (statusNotificationHandler is not null)
  211. {
  212. throw new Exception($"{nameof(InitUpdateConnectorStatusHandler)} should only called once");
  213. }
  214. statusNotificationHandler = new GroupSingleHandler<StatusNotificationParam>(
  215. handleFunc: BundleUpdateConnectorStatus,
  216. logger: loggerFactory.CreateLogger("StatusNotificationHandler"))
  217. {
  218. WorkerCnt = 10
  219. };
  220. }
  221. private async Task BundleUpdateConnectorStatus(IEnumerable<StatusNotificationParam> statusNotifications)
  222. {
  223. using var db = await contextFactory.CreateDbContextAsync();
  224. using var trans = await db.Database.BeginTransactionAsync();
  225. statusNotifications = statusNotifications.DistinctBy(x => x.Id);
  226. foreach (var param in statusNotifications)
  227. {
  228. ConnectorStatus status = new() { Id = param.Id };
  229. //db.ChangeTracker.AutoDetectChangesEnabled = false;
  230. db.ConnectorStatus.Attach(status);
  231. status.CreatedOn = param.Status.CreatedOn;
  232. status.Status = param.Status.Status;
  233. status.ChargePointErrorCodeId = param.Status.ChargePointErrorCodeId;
  234. status.ErrorInfo = param.Status.ErrorInfo;
  235. status.VendorId = param.Status.VendorId;
  236. status.VendorErrorCode = param.Status.VendorErrorCode;
  237. db.Entry(status).Property(x => x.CreatedOn).IsModified = true;
  238. db.Entry(status).Property(x => x.Status).IsModified = true;
  239. db.Entry(status).Property(x => x.ChargePointErrorCodeId).IsModified = true;
  240. db.Entry(status).Property(x => x.ErrorInfo).IsModified = true;
  241. db.Entry(status).Property(x => x.VendorId).IsModified = true;
  242. db.Entry(status).Property(x => x.VendorErrorCode).IsModified = true;
  243. //db.SaveChanges();
  244. }
  245. db.SaveChanges();
  246. trans.Commit();
  247. db.ChangeTracker.Clear();
  248. }
  249. private void InitAddServerMessageHandler()
  250. {
  251. if (addServerMessageHandler is not null)
  252. {
  253. throw new Exception($"{nameof(InitAddServerMessageHandler)} should only called once");
  254. }
  255. addServerMessageHandler = new GroupSingleHandler<ServerMessage>(
  256. handleFunc: BundleAddServerMessage,
  257. logger: loggerFactory.CreateLogger("AddServerMessageHandler"))
  258. {
  259. WorkerCnt = 1
  260. };
  261. }
  262. private async Task BundleAddServerMessage(IEnumerable<ServerMessage> messages)
  263. {
  264. using var db = await contextFactory.CreateDbContextAsync();
  265. using var trans = await db.Database.BeginTransactionAsync();
  266. foreach (var message in messages)
  267. {
  268. db.ServerMessage.Add(message);
  269. }
  270. db.SaveChanges();
  271. trans.Commit();
  272. db.ChangeTracker.Clear();
  273. }
  274. private int GetStartupLimit(IConfiguration configuration)
  275. {
  276. var limitConfig = configuration["MainDbStartupLimit"];
  277. int limit = 5;
  278. if (limitConfig != default)
  279. {
  280. int.TryParse(limitConfig, out limit);
  281. }
  282. return limit;
  283. }
  284. private int GetOpLimit(IConfiguration configuration)
  285. {
  286. var limitConfig = configuration["MainDbOpLimit"];
  287. int limit = 500;
  288. if (limitConfig != default)
  289. {
  290. int.TryParse(limitConfig, out limit);
  291. }
  292. return limit;
  293. }
  294. }
  295. public record MachineAndCustomerInfo (string MachineId, Guid CustomerId, string CustomerName);
  296. public record StatusNotificationParam(string Id, ConnectorStatus Status);
  297. public record UpdateMachineBasicInfoParam(string ChargeBoxId, Machine machine);