浏览代码

optimize Add ServerMessage

Robert 1 年之前
父节点
当前提交
ab3a1c0879

+ 27 - 7
EVCB_OCPP.WSServer/Helper/GroupSingleHandler.cs

@@ -10,21 +10,41 @@ namespace EVCB_OCPP.WSServer.Helper;
 
 public class GroupSingleHandler<T>
 {
-    private readonly Func<IEnumerable<T>, Task> handleFunc;
-    private readonly ILogger logger;
-    private readonly ConcurrentQueue<(T param, SemaphoreSlim waitLock)> waitList = new();
-    //private readonly Dictionary<T, SemaphoreSlim> reqLockPaisrs = new();
-    private readonly SemaphoreSlim singleWorkLock = new SemaphoreSlim(1);
-    private Task singleHandleTask;
-
     public GroupSingleHandler(Func<IEnumerable<T>, Task> handleFunc, ILogger logger)
     {
         this.handleFunc = handleFunc;
         this.logger = logger;
+
+        singleWorkLock = new (_WorkerCnt);
+    }
+
+    private int _WorkerCnt = 1;
+    public int WorkerCnt
+    {
+        get => _WorkerCnt;
+        set
+        {
+            if (IsStarted)
+            {
+                throw new Exception($"{nameof(WorkerCnt)} must not be changed afted {nameof(HandleAsync)} is called");
+            }
+
+            _WorkerCnt = value;
+            singleWorkLock = new (_WorkerCnt);
+        }
     }
 
+    private readonly Func<IEnumerable<T>, Task> handleFunc;
+    private readonly ILogger logger;
+    private readonly ConcurrentQueue<(T param, SemaphoreSlim waitLock)> waitList = new();
+    private SemaphoreSlim singleWorkLock;// = new SemaphoreSlim(1);
+    private bool IsStarted = false;
+    private Task singleHandleTask;
+
     public Task HandleAsync(T param)
     {
+        IsStarted = true;
+
         SemaphoreSlim reqLock = new(0);
         waitList.Enqueue((param, reqLock));
         TryStartHandler();

+ 33 - 62
EVCB_OCPP.WSServer/Jobs/ServerSetFeeJob.cs

@@ -5,6 +5,7 @@ using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages.Core;
 using EVCB_OCPP.WSServer.Dto;
 using EVCB_OCPP.WSServer.Message;
+using EVCB_OCPP.WSServer.Service;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
@@ -24,18 +25,18 @@ namespace EVCB_OCPP.WSServer.Jobs;
 public class ServerSetFeeJob : IJob
 {
     private readonly ProtalServer protalServer;
-    private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
+    private readonly IMainDbService mainDbService;
     private readonly ILogger<ServerSetFeeJob> logger;
     private readonly string webConnectionString;
 
     public ServerSetFeeJob(
         ProtalServer protalServer,
         IConfiguration configuration,
-        IDbContextFactory<MainDBContext> maindbContextFactory,
-        ILogger<ServerSetFeeJob> logger) 
+        IMainDbService mainDbService,
+        ILogger<ServerSetFeeJob> logger)
     {
         this.protalServer = protalServer;
-        this.maindbContextFactory = maindbContextFactory;
+        this.mainDbService = mainDbService;
         this.logger = logger;
         this.webConnectionString = configuration.GetConnectionString("WebDBContext");
     }
@@ -43,9 +44,9 @@ public class ServerSetFeeJob : IJob
     public async Task Execute(IJobExecutionContext context)
     {
         //logger.LogDebug("{0} Started", nameof(ServerSetFeeJob));
-        BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+        //BasicMessageHandler msgAnalyser = new BasicMessageHandler();
         Dictionary<string, ClientData> _copyClientDic = protalServer.ClientDic;
-        using var db = maindbContextFactory.CreateDbContext();
+        //using var db = maindbContextFactory.CreateDbContext();
         foreach (var item in _copyClientDic)
         {
             try
@@ -64,65 +65,35 @@ public class ServerSetFeeJob : IJob
 
                 protalServer.UpdateClientDisplayPrice(item.Key, displayPriceText);
 
-                var _CheckFeeDt = DateTime.UtcNow;
-                    db.ServerMessage.Add(new ServerMessage()
-                    {
-                        ChargeBoxId = session.ChargeBoxId,
-                        CreatedBy = "Server",
-                        CreatedOn = _CheckFeeDt,
-                        OutAction = Actions.ChangeConfiguration.ToString(),
-                        OutRequest = JsonConvert.SerializeObject(
-                                new ChangeConfigurationRequest()
-                                {
-                                    key = "DefaultPrice",
-                                    value = displayPriceText
-                                },
-                                new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                        SerialNo = Guid.NewGuid().ToString(),
-                        InMessage = string.Empty
-
-                    });
-
-                    if (session.CustomerId == new Guid("10C7F5BD-C89A-4E2A-8611-B617E0B41A73"))
-                    {
-                        db.ServerMessage.Add(new ServerMessage()
+                await mainDbService.AddServerMessage(
+                        ChargeBoxId: session.ChargeBoxId,
+                        OutAction: Actions.ChangeConfiguration.ToString(),
+                        OutRequest: new ChangeConfigurationRequest()
                         {
-                            ChargeBoxId = session.ChargeBoxId,
-                            CreatedBy = "Server",
-                            CreatedOn = _CheckFeeDt,
-                            OutAction = Actions.ChangeConfiguration.ToString(),
-                            OutRequest = JsonConvert.SerializeObject(
-                               new ChangeConfigurationRequest()
-                               {
-                                   key = "ConnectionTimeOut",
-                                   value = "120"
-                               },
-                               new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                            SerialNo = Guid.NewGuid().ToString(),
-                            InMessage = string.Empty
-
+                            key = "DefaultPrice",
+                            value = displayPriceText
                         });
-                        db.ServerMessage.Add(new ServerMessage()
-                        {
-                            ChargeBoxId = session.ChargeBoxId,
-                            CreatedBy = "Server",
-                            CreatedOn = _CheckFeeDt,
-                            OutAction = Actions.ChangeConfiguration.ToString(),
-                            OutRequest = JsonConvert.SerializeObject(
-                               new ChangeConfigurationRequest()
-                               {
-                                   key = "MeterValueSampleInterval",
-                                   value = "3"
-                               },
-                               new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                            SerialNo = Guid.NewGuid().ToString(),
-                            InMessage = string.Empty
 
-                        });
-                    }
-
-                    db.SaveChanges();
-                    db.ChangeTracker.Clear();
+                if (session.CustomerId == new Guid("10C7F5BD-C89A-4E2A-8611-B617E0B41A73"))
+                {
+                    await mainDbService.AddServerMessage(
+                            ChargeBoxId: session.ChargeBoxId,
+                            OutAction: Actions.ChangeConfiguration.ToString(),
+                            OutRequest: new ChangeConfigurationRequest()
+                            {
+                                key = "ConnectionTimeOut",
+                                value = "120"
+                            });
+
+                    await mainDbService.AddServerMessage(
+                            ChargeBoxId: session.ChargeBoxId,
+                            OutAction: Actions.ChangeConfiguration.ToString(),
+                            OutRequest:  new ChangeConfigurationRequest()
+                            {
+                                key = "MeterValueSampleInterval",
+                                value = "3"
+                            });
+                }
             }
             catch (Exception ex)
             {

+ 62 - 99
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -436,29 +436,17 @@ internal partial class ProfileHandler
                             {
                                 if (session.IsBilling)
                                 {
-                                    using (var db = maindbContextFactory.CreateDbContext())
-                                    {
-                                        db.ServerMessage.Add(new ServerMessage()
-                                        {
-                                            ChargeBoxId = session.ChargeBoxId,
-                                            CreatedBy = "Server",
-                                            CreatedOn = DateTime.UtcNow,
-                                            OutAction = Actions.DataTransfer.ToString(),
-                                            OutRequest = JsonConvert.SerializeObject(
-                                                    new DataTransferRequest()
-                                                    {
-                                                        messageId = "ID_TxEnergy",
-                                                        vendorId = "Phihong Technology",
-                                                        data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = _request.connectorId })
-                                                    },
-                                                    new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                                            SerialNo = Guid.NewGuid().ToString(),
-                                            InMessage = string.Empty
-
-                                        });
-
-                                        db.SaveChanges();
-                                    }
+                                    await mainDbService.AddServerMessage(
+                                            ChargeBoxId: session.ChargeBoxId,
+                                            OutAction: Actions.DataTransfer.ToString(),
+                                            OutRequest: 
+                                                new DataTransferRequest()
+                                                {
+                                                    messageId = "ID_TxEnergy",
+                                                    vendorId = "Phihong Technology",
+                                                    data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = _request.connectorId })
+                                                }
+                                            );
                                 }
                             }
                             catch (Exception ex)
@@ -652,7 +640,13 @@ internal partial class ProfileHandler
                                 }
 
 
-                                if (transaction != null)
+                                if (transaction is null)
+                                {
+
+                                    result.Exception = new Exception("Can't find transactionId " + _request.transactionId);
+
+                                }
+                                else
                                 {
                                     var confirm = new StopTransactionConfirmation()
                                     {
@@ -698,40 +692,25 @@ internal partial class ProfileHandler
 
                                     if (session.IsBilling)
                                     {
-                                        db.ServerMessage.Add(new ServerMessage()
-                                        {
-                                            ChargeBoxId = session.ChargeBoxId,
-                                            CreatedBy = "Server",
-                                            CreatedOn = utcNow,
-                                            OutAction = Actions.DataTransfer.ToString(),
-                                            OutRequest = JsonConvert.SerializeObject(
-                                                       new DataTransferRequest()
-                                                       {
-                                                           messageId = "ID_TxEnergy",
-                                                           vendorId = "Phihong Technology",
-                                                           data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = transaction.ConnectorId })
-                                                       },
-                                                       new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                                            SerialNo = Guid.NewGuid().ToString(),
-                                            InMessage = string.Empty
-
-                                        });
-
-                                        //db.SaveChanges();
-                                        db.SaveChanges();
+                                        await mainDbService.AddServerMessage(
+                                            ChargeBoxId: session.ChargeBoxId,
+                                            OutAction: Actions.DataTransfer.ToString(),
+                                            OutRequest:
+                                                new DataTransferRequest()
+                                                {
+                                                    messageId = "ID_TxEnergy",
+                                                    vendorId = "Phihong Technology",
+                                                    data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = transaction.ConnectorId })
+                                                }
+                                            );
                                     }
 
                                     result.Message = confirm;
                                     result.Success = true;
                                 }
-                                else
-                                {
-
-                                    result.Exception = new Exception("Can't find transactionId " + _request.transactionId);
-
-                                }
                             }
                             dbOpTime = watch.ElapsedMilliseconds;
+
                             #region Save MeterValue
 
                             if (_request.transactionData != null &&
@@ -1130,41 +1109,34 @@ internal partial class ProfileHandler
                                         db.Entry(tx).Property(x => x.Receipt).IsModified = true;
                                         db.Entry(tx).Property(x => x.BillingDone).IsModified = true;
 
-                                        db.ServerMessage.Add(new ServerMessage()
-                                        {
-                                            ChargeBoxId = session.ChargeBoxId,
-                                            CreatedBy = "Server",
-                                            CreatedOn = DateTime.UtcNow,
-                                            OutAction = Actions.DataTransfer.ToString(),
-                                            OutRequest = JsonConvert.SerializeObject(
-                                                        new DataTransferRequest()
+                                        db.SaveChanges();
+
+                                        await mainDbService.AddServerMessage(
+                                            ChargeBoxId: session.ChargeBoxId,
+                                            OutAction: Actions.DataTransfer.ToString(),
+                                            OutRequest: 
+                                                new DataTransferRequest()
+                                                {
+                                                    messageId = "FinalCost",
+                                                    vendorId = "Phihong Technology",
+                                                    data = JsonConvert.SerializeObject(new
+                                                    {
+                                                        txId = txEnergy.TxId,
+                                                        description = JsonConvert.SerializeObject(new
                                                         {
-                                                            messageId = "FinalCost",
-                                                            vendorId = "Phihong Technology",
-                                                            data = JsonConvert.SerializeObject(new
-                                                            {
-                                                                txId = txEnergy.TxId,
-                                                                description = JsonConvert.SerializeObject(new
-                                                                {
-                                                                    chargedEnergy = chargedEnergy,
-                                                                    chargingFee = chargingCost,
-                                                                    parkTime = (int)stoptime.Subtract(starttime).TotalSeconds,
-                                                                    parkingFee = parkingCost,
-                                                                    currency = currency,
-                                                                    couponPoint = couponPoint,
-                                                                    accountBalance = accountBalance - tx.Cost,
-                                                                    farewellMessage = farewellMessage
-                                                                })
-                                                            })
-
-                                                        },
-                                                        new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                                            SerialNo = Guid.NewGuid().ToString(),
-                                            InMessage = string.Empty
-
-                                        }); ;
+                                                            chargedEnergy = chargedEnergy,
+                                                            chargingFee = chargingCost,
+                                                            parkTime = (int)stoptime.Subtract(starttime).TotalSeconds,
+                                                            parkingFee = parkingCost,
+                                                            currency = currency,
+                                                            couponPoint = couponPoint,
+                                                            accountBalance = accountBalance - tx.Cost,
+                                                            farewellMessage = farewellMessage
+                                                        })
+                                                    })
 
-                                        db.SaveChanges();
+                                                }
+                                                );
 
                                         await meterValueDbService.InsertAsync(
                                             chargeBoxId: session.ChargeBoxId,
@@ -1207,13 +1179,10 @@ internal partial class ProfileHandler
                                     }
                                     else
                                     {
-                                        db.ServerMessage.Add(new ServerMessage()
-                                        {
-                                            ChargeBoxId = session.ChargeBoxId,
-                                            CreatedBy = "Server",
-                                            CreatedOn = DateTime.UtcNow,
-                                            OutAction = Actions.DataTransfer.ToString(),
-                                            OutRequest = JsonConvert.SerializeObject(
+                                        await mainDbService.AddServerMessage(
+                                            ChargeBoxId: session.ChargeBoxId,
+                                            OutAction: Actions.DataTransfer.ToString(),
+                                            OutRequest: 
                                              new DataTransferRequest()
                                              {
                                                  messageId = "RunningCost",
@@ -1232,14 +1201,8 @@ internal partial class ProfileHandler
 
                                                  })
 
-                                             },
-                                             new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                                            SerialNo = Guid.NewGuid().ToString(),
-                                            InMessage = string.Empty
-
-                                        }); ;
-
-                                        db.SaveChanges();
+                                             }
+                                        );
 
                                         await meterValueDbService.InsertAsync(
                                             chargeBoxId: session.ChargeBoxId,

+ 8 - 12
EVCB_OCPP.WSServer/Message/FirmwareManagementProfileHandler.cs

@@ -65,20 +65,16 @@ namespace EVCB_OCPP.WSServer.Message
 
                                         });
 
-                                        db.ServerMessage.Add(new ServerMessage()
-                                        {
-                                            ChargeBoxId = session.ChargeBoxId,
-                                            CreatedBy = "Server",
-                                            CreatedOn = DateTime.UtcNow,
-                                            OutAction = _updateFWrequest.Action.ToString(),
-                                            OutRequest = JsonConvert.SerializeObject(_updateFWrequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                                            SerialNo = requestId,
-                                            InMessage = string.Empty
-
-                                        });
-
                                         db.SaveChanges();
 
+                                        await mainDbService.AddServerMessage(
+                                            ChargeBoxId: session.ChargeBoxId,
+                                            CreatedBy: "Server",
+                                            CreatedOn: DateTime.UtcNow,
+                                            OutAction: _updateFWrequest.Action.ToString(),
+                                            OutRequest: JsonConvert.SerializeObject(_updateFWrequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                            SerialNo: requestId);
+
                                         var clearMachine = db.Machine.Where(x => x.Id == machine.Id).FirstOrDefault();
                                         clearMachine.FW_AssignedVersion = null;
                                         db.SaveChanges();

+ 37 - 53
EVCB_OCPP.WSServer/Message/SmartChargingProfileHandler.cs

@@ -20,73 +20,57 @@ namespace EVCB_OCPP.WSServer.Message
 
         internal async void SetChargingProfile(string chargeBoxId, decimal value, ChargingRateUnitType unit)
         {
-            using (var db = maindbContextFactory.CreateDbContext())
+            var _setProfileRequest = new SetChargingProfileRequest()
             {
-                var _setProfileRequest = new SetChargingProfileRequest()
+                connectorId = 0,
+                csChargingProfiles = new Packet.Messages.SubTypes.csChargingProfiles()
                 {
-                    connectorId = 0,
-                    csChargingProfiles = new Packet.Messages.SubTypes.csChargingProfiles()
+                    chargingProfileId = 1,
+                    chargingProfileKind = Packet.Messages.SubTypes.ChargingProfileKindType.Recurring,
+                    chargingProfilePurpose = Packet.Messages.SubTypes.ChargingProfilePurposeType.ChargePointMaxProfile,
+                    chargingSchedule = new Packet.Messages.SubTypes.ChargingSchedule()
                     {
-                        chargingProfileId = 1,
-                        chargingProfileKind = Packet.Messages.SubTypes.ChargingProfileKindType.Recurring,
-                        chargingProfilePurpose = Packet.Messages.SubTypes.ChargingProfilePurposeType.ChargePointMaxProfile,
-                        chargingSchedule = new Packet.Messages.SubTypes.ChargingSchedule()
-                        {
-                            chargingRateUnit = unit,
-                            chargingSchedulePeriod = new List<Packet.Messages.SubTypes.ChargingSchedulePeriod>()
+                        chargingRateUnit = unit,
+                        chargingSchedulePeriod = new List<Packet.Messages.SubTypes.ChargingSchedulePeriod>()
                                                     {
                                                         new Packet.Messages.SubTypes.ChargingSchedulePeriod(){  startPeriod=0, limit=value}
                                                     },
-                            duration = 86400,
-
-                        },
-                        recurrencyKind = Packet.Messages.SubTypes.RecurrencyKindType.Daily,
-                        stackLevel = 1,
-
-                    }
-                };
-
-                db.ServerMessage.Add(new ServerMessage()
-                {
-                    ChargeBoxId = chargeBoxId,
-                    CreatedBy = "Server",
-                    CreatedOn = DateTime.UtcNow,
-                    OutAction = _setProfileRequest.Action.ToString(),
-                    OutRequest = JsonConvert.SerializeObject(_setProfileRequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                    SerialNo = Guid.NewGuid().ToString(),
-                    InMessage = string.Empty
-
-                });
-                db.SaveChanges();
-            }
+                        duration = 86400,
+
+                    },
+                    recurrencyKind = Packet.Messages.SubTypes.RecurrencyKindType.Daily,
+                    stackLevel = 1,
+
+                }
+            };
+
+            await mainDbService.AddServerMessage(
+                ChargeBoxId: chargeBoxId,
+                CreatedBy: "Server",
+                CreatedOn: DateTime.UtcNow,
+                OutAction: _setProfileRequest.Action.ToString(),
+                OutRequest: JsonConvert.SerializeObject(_setProfileRequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None })
+                );
         }
 
 
         internal async void ClearChargingProfile(string chargeBoxId)
         {
-            using (var db = maindbContextFactory.CreateDbContext())
+            var _clearProfileRequest = new ClearChargingProfileRequest()
             {
-                var _clearProfileRequest = new ClearChargingProfileRequest()
-                {
-                    connectorId = 0,
-                    chargingProfilePurpose = ChargingProfilePurposeType.ChargePointMaxProfile,
-
-                };
-
-                db.ServerMessage.Add(new ServerMessage()
-                {
-                    ChargeBoxId = chargeBoxId,
-                    CreatedBy = "Server",
-                    CreatedOn = DateTime.UtcNow,
-                    OutAction = _clearProfileRequest.Action.ToString(),
-                    OutRequest = JsonConvert.SerializeObject(_clearProfileRequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                    SerialNo = Guid.Empty.ToString(),
-                    InMessage = string.Empty
+                connectorId = 0,
+                chargingProfilePurpose = ChargingProfilePurposeType.ChargePointMaxProfile,
 
-                });
+            };
 
-                db.SaveChanges();
-            }
+            await mainDbService.AddServerMessage(new ServerMessage()
+            {
+                ChargeBoxId = chargeBoxId,
+                CreatedBy = "Server",
+                CreatedOn = DateTime.UtcNow,
+                OutAction = _clearProfileRequest.Action.ToString(),
+                OutRequest = JsonConvert.SerializeObject(_clearProfileRequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None })
+            });
         }
 
         internal async Task<MessageResult> ExecuteSmartChargingConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)

+ 50 - 80
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -65,6 +65,7 @@ namespace EVCB_OCPP.WSServer
         public ProtalServer(
             IConfiguration configuration
             , IDbContextFactory<MainDBContext> maindbContextFactory
+            , IMainDbService mainDbService
             , IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory
             , IHostEnvironment environment
             , IOCPPWSServerFactory ocppWSServerFactory
@@ -76,6 +77,7 @@ namespace EVCB_OCPP.WSServer
 
             this.configuration = configuration;
             this.maindbContextFactory = maindbContextFactory;
+            this.mainDbService = mainDbService;
             //this.connectionLogdbContextFactory = connectionLogdbContextFactory;
             this.ocppWSServerFactory = ocppWSServerFactory;
             this.connectionLogdbService = connectionLogdbService;
@@ -98,6 +100,8 @@ namespace EVCB_OCPP.WSServer
         private readonly IConfiguration configuration;
         //private readonly IServiceProvider serviceProvider;
         private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
+        private readonly IMainDbService mainDbService;
+
         //private readonly IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory;
         private readonly IOCPPWSServerFactory ocppWSServerFactory;
         private readonly IConnectionLogdbService connectionLogdbService;
@@ -458,29 +462,11 @@ namespace EVCB_OCPP.WSServer
         private async void CheckEVSEConfigure(string chargeBoxId)
         {
             if (string.IsNullOrEmpty(chargeBoxId)) return;
-            using (var db = maindbContextFactory.CreateDbContext())
-            {
-                db.ServerMessage.Add(new ServerMessage()
-                {
-                    ChargeBoxId = chargeBoxId,
-                    CreatedBy = "Server",
-                    CreatedOn = DateTime.UtcNow,
-                    OutAction = Actions.GetConfiguration.ToString(),
-                    OutRequest = JsonConvert.SerializeObject(
-                            new GetConfigurationRequest()
-                            {
-                                key = new List<string>()
-
-                            },
-                            new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                    SerialNo = Guid.NewGuid().ToString(),
-                    InMessage = string.Empty
-
-                }); ;
-
-                db.SaveChanges();
-
-            }
+            await mainDbService.AddServerMessage(
+                ChargeBoxId: chargeBoxId,
+                OutAction: Actions.GetConfiguration.ToString(),
+                OutRequest: new GetConfigurationRequest() { key = new List<string>() }
+                );
         }
 
         private void OpenNetwork()
@@ -575,7 +561,7 @@ namespace EVCB_OCPP.WSServer
                 }
 
                 //clientDic.add.Add(session.ChargeBoxId, session);
-                session.m_ReceiveData += new ClientData.OCPPClientDataEventHandler<ClientData, String>(ReceivedMessage);
+                session.m_ReceiveData += ReceivedMessageTimeLimited;
                 // logger.LogDebug("------------New " + (session == null ? "Oops" : session.ChargeBoxId));
                 WriteMachineLog(session, "NewSessionConnected", "Connection", "");
 
@@ -598,14 +584,31 @@ namespace EVCB_OCPP.WSServer
 
         }
 
-        async private void ReceivedMessage(ClientData session, string rawdata)
+        async private void ReceivedMessageTimeLimited(ClientData session, string rawdata)
+        {
+            CancellationTokenSource tokenSource = new();
+            var task = ReceivedMessage(session, rawdata);
+            var completedTask = await Task.WhenAny(task, Task.Delay(90_000, tokenSource.Token));
+
+            if (completedTask != task)
+            {
+                logger.Fatal("Process timeout: {0} ", rawdata);
+                await task;
+                return;
+            }
+
+            tokenSource.Cancel();
+            return;
+        }
+
+        async private Task ReceivedMessage(ClientData session, string rawdata)
         {
             try
             {
                 BasicMessageHandler msgAnalyser = new BasicMessageHandler();
                 MessageResult analysisResult = msgAnalyser.AnalysisReceiveData(session, rawdata);
 
-                WriteMachineLog(session, rawdata,
+                await WriteMachineLog(session, rawdata,
                      string.Format("{0} {1}", string.IsNullOrEmpty(analysisResult.Action) ? "unknown" : analysisResult.Action, analysisResult.Id == 2 ? "Request" : (analysisResult.Id == 3 ? "Confirmation" : "Error")), analysisResult.Exception == null ? "" : analysisResult.Exception.Message);
 
                 if (session.ResetSecurityProfile)
@@ -664,7 +667,7 @@ namespace EVCB_OCPP.WSServer
                                     Actions action = Convertor.GetAction(analysisResult.Action);
                                     try
                                     {
-                                        ProcessRequestMessage(analysisResult, session, action);
+                                        await ProcessRequestMessage(analysisResult, session, action);
                                     }
                                     catch (Exception e)
                                     {
@@ -841,7 +844,7 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        async private void ProcessRequestMessage(MessageResult analysisResult, ClientData session, Actions action)
+        private async Task ProcessRequestMessage(MessageResult analysisResult, ClientData session, Actions action)
         {
             Stopwatch outter_stopwatch = Stopwatch.StartNew();
             BasicMessageHandler msgAnalyser = new BasicMessageHandler();
@@ -875,30 +878,14 @@ namespace EVCB_OCPP.WSServer
                                         CheckEVSEConfigure(session.ChargeBoxId);
                                         if (session.CustomerId == new Guid("298918C0-6BB5-421A-88CC-4922F918E85E") || session.CustomerId == new Guid("9E6BFDCC-09FB-4DAB-A428-43FE507600A3"))
                                         {
-                                            using (var db = maindbContextFactory.CreateDbContext())
-                                            {
-                                                db.ServerMessage.Add(new ServerMessage()
+                                            await mainDbService.AddServerMessage(
+                                                ChargeBoxId: session.ChargeBoxId,
+                                                OutAction: Actions.ChangeConfiguration.ToString(),
+                                                OutRequest: new ChangeConfigurationRequest()
                                                 {
-                                                    ChargeBoxId = session.ChargeBoxId,
-                                                    CreatedBy = "Server",
-                                                    CreatedOn = DateTime.UtcNow,
-                                                    OutAction = Actions.ChangeConfiguration.ToString(),
-                                                    OutRequest = JsonConvert.SerializeObject(
-                                                   new ChangeConfigurationRequest()
-                                                   {
-                                                       key = "TimeOffset",
-                                                       value = "+08:00"
-
-                                                   },
-                                                   new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                                                    SerialNo = Guid.NewGuid().ToString(),
-                                                    InMessage = string.Empty
-
+                                                    key = "TimeOffset",
+                                                    value = "+08:00"
                                                 });
-
-                                                db.SaveChanges();
-                                            }
-
                                         }
                                     }
                                     else
@@ -926,35 +913,18 @@ namespace EVCB_OCPP.WSServer
                                     var authorizeRequest = (IRequest)analysisResult.Message as AuthorizeRequest;
                                     if (session.UserDisplayPrices.ContainsKey(authorizeRequest.idTag))
                                     {
-                                        using (var db = maindbContextFactory.CreateDbContext())
-                                        {
-                                            db.ServerMessage.Add(new ServerMessage()
-                                            {
-                                                ChargeBoxId = session.ChargeBoxId,
-                                                CreatedBy = "Server",
-                                                CreatedOn = DateTime.UtcNow,
-                                                OutAction = Actions.DataTransfer.ToString(),
-                                                OutRequest = JsonConvert.SerializeObject(
-                                                           new DataTransferRequest()
-                                                           {
-                                                               messageId = "SetUserPrice",
-                                                               vendorId = "Phihong Technology",
-                                                               data = JsonConvert.SerializeObject(
-                                                                   new
-                                                                   {
-                                                                       idToken = authorizeRequest.idTag,
-                                                                       price = session.UserDisplayPrices[authorizeRequest.idTag]
-
-                                                                   })
-                                                           },
-                                                           new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
-                                                SerialNo = Guid.NewGuid().ToString(),
-                                                InMessage = string.Empty
-
-                                            });
-
-                                            db.SaveChanges();
-                                        }
+                                        await mainDbService.AddServerMessage(
+                                            ChargeBoxId: session.ChargeBoxId,
+                                            OutAction: Actions.DataTransfer.ToString(),
+                                            OutRequest: new DataTransferRequest() { 
+                                                messageId = "SetUserPrice", 
+                                                vendorId = "Phihong Technology",
+                                                data = JsonConvert.SerializeObject(
+                                                    new {
+                                                        idToken = authorizeRequest.idTag, 
+                                                        price = session.UserDisplayPrices[authorizeRequest.idTag] 
+                                                    }) }
+                                            );
                                     }
                                 }
 
@@ -1400,7 +1370,7 @@ namespace EVCB_OCPP.WSServer
             RemoveClientDic(session);
             try
             {
-                session.m_ReceiveData -= new ClientData.OCPPClientDataEventHandler<ClientData, String>(ReceivedMessage);
+                session.m_ReceiveData -= ReceivedMessageTimeLimited;
                 // session.Close(CloseReason.ServerShutdown);
 
             }

+ 201 - 81
EVCB_OCPP.WSServer/Service/MainDbService.cs

@@ -1,10 +1,12 @@
 using EVCB_OCPP.Domain;
 using EVCB_OCPP.Domain.Models.Database;
+using EVCB_OCPP.Packet.Messages.Core;
 using EVCB_OCPP.WSServer.Helper;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
 using OCPPPackage.Profiles;
 using System;
 using System.Collections.Generic;
@@ -26,6 +28,8 @@ public interface IMainDbService
     Task AddOCMF(OCMF oCMF);
     Task<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId);
     Task UpdateConnectorStatus(string Id, ConnectorStatus connectorStatus);
+    Task AddServerMessage(ServerMessage message);
+    Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy = "", DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "");
 }
 
 public class MainDbService : IMainDbService
@@ -41,7 +45,8 @@ public class MainDbService : IMainDbService
         this.opSemaphore = new SemaphoreSlim(opLimit);
 
         InitUpdateConnectorStatusHandler();
-        //InitUpdateMachineBasicInfoHandler();
+        InitUpdateMachineBasicInfoHandler();
+        InitAddServerMessageHandler();
     }
 
     private readonly IDbContextFactory<MainDBContext> contextFactory;
@@ -49,7 +54,8 @@ public class MainDbService : IMainDbService
     private readonly QueueSemaphore startupSemaphore;
     private readonly SemaphoreSlim opSemaphore;
     private GroupSingleHandler<StatusNotificationParam> statusNotificationHandler;
-    //private GroupSingleHandler<UpdateMachineBasicInfoParam> updateMachineBasicInfoHandler;
+    private GroupSingleHandler<UpdateMachineBasicInfoParam> updateMachineBasicInfoHandler;
+    private GroupSingleHandler<ServerMessage> addServerMessageHandler;
 
     public async Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId)
     {
@@ -90,75 +96,149 @@ public class MainDbService : IMainDbService
 
     public async Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine)
     {
-        using var semaphoreWrapper = await startupSemaphore.GetToken();
-        using var db = await contextFactory.CreateDbContextAsync();
-
-        var _machine = db.Machine.FirstOrDefault(x => x.ChargeBoxId == ChargeBoxId);
-        _machine.ChargeBoxSerialNumber = machine.ChargeBoxSerialNumber;
-        _machine.ChargePointSerialNumber = machine.ChargePointSerialNumber;
-        _machine.ChargePointModel = machine.ChargePointModel;
-        _machine.ChargePointVendor = machine.ChargePointVendor;
-        _machine.FW_CurrentVersion = machine.FW_CurrentVersion;
-        _machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
-        _machine.Imsi = machine.Imsi;
-        _machine.MeterSerialNumber = machine.MeterSerialNumber;
-        _machine.MeterType = machine.MeterType;
-
-        await db.SaveChangesAsync();
         //using var semaphoreWrapper = await startupSemaphore.GetToken();
-        //await updateMachineBasicInfoHandler.HandleAsync(new UpdateMachineBasicInfoParam(ChargeBoxId, machine));
-    }
-
-    //private void InitUpdateMachineBasicInfoHandler()
-    //{
-    //    if (updateMachineBasicInfoHandler is not null)
-    //    {
-    //        throw new Exception($"{nameof(InitUpdateMachineBasicInfoHandler)} should only called once");
-    //    }
-
-    //    updateMachineBasicInfoHandler = new GroupSingleHandler<UpdateMachineBasicInfoParam>(async (pams) => {
-
-    //        using var db = await contextFactory.CreateDbContextAsync();
-    //        using var trans = await db.Database.BeginTransactionAsync();
-
-    //        foreach (var pam in pams)
-    //        {
-    //            var _machine = db.Machine.FirstOrDefault(x => x.ChargeBoxId == pam.ChargeBoxId);
-    //            _machine.ChargeBoxSerialNumber = pam.machine.ChargeBoxSerialNumber;
-    //            _machine.ChargePointSerialNumber = pam.machine.ChargePointSerialNumber;
-    //            _machine.ChargePointModel = pam.machine.ChargePointModel;
-    //            _machine.ChargePointVendor = pam.machine.ChargePointVendor;
-    //            _machine.FW_CurrentVersion = pam.machine.FW_CurrentVersion;
-    //            _machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
-    //            _machine.Imsi = pam.machine.Imsi;
-    //            _machine.MeterSerialNumber = pam.machine.MeterSerialNumber;
-    //            _machine.MeterType = pam.machine.MeterType;
-
-    //            await db.SaveChangesAsync();
-    //        }
-
-    //        await trans.CommitAsync();
-    //    },
-    //    loggerFactory.CreateLogger("UpdateMachineBasicInfoHandler"));
-    //}
+        //using var db = await contextFactory.CreateDbContextAsync();
+
+        //var _machine = db.Machine.FirstOrDefault(x => x.ChargeBoxId == ChargeBoxId);
+        //_machine.ChargeBoxSerialNumber = machine.ChargeBoxSerialNumber;
+        //_machine.ChargePointSerialNumber = machine.ChargePointSerialNumber;
+        //_machine.ChargePointModel = machine.ChargePointModel;
+        //_machine.ChargePointVendor = machine.ChargePointVendor;
+        //_machine.FW_CurrentVersion = machine.FW_CurrentVersion;
+        //_machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
+        //_machine.Imsi = machine.Imsi;
+        //_machine.MeterSerialNumber = machine.MeterSerialNumber;
+        //_machine.MeterType = machine.MeterType;
+
+        //await db.SaveChangesAsync();
+        //using var semaphoreWrapper = await startupSemaphore.GetToken();
+        await updateMachineBasicInfoHandler.HandleAsync(new UpdateMachineBasicInfoParam(ChargeBoxId, machine));
+    }
 
     public async Task AddOCMF(OCMF oCMF)
     {
-        using var db = contextFactory.CreateDbContext() ;
+        using var db = contextFactory.CreateDbContext();
         db.OCMF.Add(oCMF);
         await db.SaveChangesAsync();
     }
 
-    public async Task<ConnectorStatus> GetConnectorStatus(string ChargeBoxId,int ConnectorId)
+    public async Task<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId)
     {
         using var db = contextFactory.CreateDbContext();
         return await db.ConnectorStatus.Where(x => x.ChargeBoxId == ChargeBoxId
                             && x.ConnectorId == ConnectorId).AsNoTracking().FirstOrDefaultAsync();
     }
 
-    public Task UpdateConnectorStatus(string Id, ConnectorStatus connectorStatus)
+    public async Task UpdateConnectorStatus(string Id, ConnectorStatus Status)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+
+        ConnectorStatus status = new() { Id = Id };
+
+        db.ChangeTracker.AutoDetectChangesEnabled = false;
+        db.ConnectorStatus.Attach(status);
+
+
+        status.CreatedOn = Status.CreatedOn;
+        status.Status = Status.Status;
+        status.ChargePointErrorCodeId = Status.ChargePointErrorCodeId;
+        status.ErrorInfo = Status.ErrorInfo;
+        status.VendorId = Status.VendorId;
+        status.VendorErrorCode = Status.VendorErrorCode;
+
+
+        db.Entry(status).Property(x => x.CreatedOn).IsModified = true;
+        db.Entry(status).Property(x => x.Status).IsModified = true;
+        db.Entry(status).Property(x => x.ChargePointErrorCodeId).IsModified = true;
+        db.Entry(status).Property(x => x.ErrorInfo).IsModified = true;
+        db.Entry(status).Property(x => x.VendorId).IsModified = true;
+        db.Entry(status).Property(x => x.VendorErrorCode).IsModified = true;
+
+        await db.SaveChangesAsync();
+        //await statusNotificationHandler.HandleAsync(new StatusNotificationParam(Id, Status));
+        return;
+    }
+
+    public Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy, DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "")
+    {
+        if (string.IsNullOrEmpty(CreatedBy))
+        {
+            CreatedBy = "Server";
+        }
+
+        if (string.IsNullOrEmpty(SerialNo))
+        {
+            SerialNo = Guid.NewGuid().ToString();
+        }
+        var _CreatedOn = CreatedOn ?? DateTime.UtcNow;
+
+        string _OutRequest = "";
+        if (OutRequest is not null)
+        {
+            _OutRequest = JsonConvert.SerializeObject(
+                OutRequest,
+                new JsonSerializerSettings()
+                {
+                    NullValueHandling = NullValueHandling.Ignore,
+                    Formatting = Formatting.None
+                });
+        }
+
+        return AddServerMessage(new ServerMessage()
+        {
+            ChargeBoxId = ChargeBoxId,
+            CreatedBy = CreatedBy,
+            CreatedOn = _CreatedOn,
+            OutAction = OutAction,
+            OutRequest = _OutRequest,
+            SerialNo = SerialNo,
+            InMessage = InMessage
+        });
+    }
+
+    public Task AddServerMessage(ServerMessage message)
+    {
+        return addServerMessageHandler.HandleAsync(message);
+    }
+
+    private void InitUpdateMachineBasicInfoHandler()
     {
-        return statusNotificationHandler.HandleAsync(new StatusNotificationParam(Id, connectorStatus));
+        if (updateMachineBasicInfoHandler is not null)
+        {
+            throw new Exception($"{nameof(InitUpdateMachineBasicInfoHandler)} should only called once");
+        }
+
+        updateMachineBasicInfoHandler = new GroupSingleHandler<UpdateMachineBasicInfoParam>(
+            handleFunc: BundelUpdateMachineBasicInfo,
+            logger: loggerFactory.CreateLogger("UpdateMachineBasicInfoHandler"))
+            {
+                WorkerCnt = 10
+            };
+    }
+
+    private async Task BundelUpdateMachineBasicInfo(IEnumerable<UpdateMachineBasicInfoParam> pams)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        using var trans = await db.Database.BeginTransactionAsync();
+
+        pams = pams.DistinctBy(x => x.ChargeBoxId);
+
+        foreach (var pam in pams)
+        {
+            var _machine = db.Machine.FirstOrDefault(x => x.ChargeBoxId == pam.ChargeBoxId);
+            _machine.ChargeBoxSerialNumber = pam.machine.ChargeBoxSerialNumber;
+            _machine.ChargePointSerialNumber = pam.machine.ChargePointSerialNumber;
+            _machine.ChargePointModel = pam.machine.ChargePointModel;
+            _machine.ChargePointVendor = pam.machine.ChargePointVendor;
+            _machine.FW_CurrentVersion = pam.machine.FW_CurrentVersion;
+            _machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
+            _machine.Imsi = pam.machine.Imsi;
+            _machine.MeterSerialNumber = pam.machine.MeterSerialNumber;
+            _machine.MeterType = pam.machine.MeterType;
+        }
+
+        db.SaveChanges();
+        trans.Commit();
     }
 
     private void InitUpdateConnectorStatusHandler()
@@ -168,41 +248,81 @@ public class MainDbService : IMainDbService
             throw new Exception($"{nameof(InitUpdateConnectorStatusHandler)} should only called once");
         }
 
-        statusNotificationHandler = new GroupSingleHandler<StatusNotificationParam>(async (paramCollection) => {
-            using var db = await contextFactory.CreateDbContextAsync();
-            using var trans = await db.Database.BeginTransactionAsync();
+        statusNotificationHandler = new GroupSingleHandler<StatusNotificationParam>(
+            handleFunc: BundleUpdateConnectorStatus,
+            logger: loggerFactory.CreateLogger("StatusNotificationHandler"))
+        {
+            WorkerCnt = 10
+        };
+    }
 
-            foreach (var param in paramCollection)
-            {
-                ConnectorStatus status = new() { Id = param.Id };
+    private async Task BundleUpdateConnectorStatus(IEnumerable<StatusNotificationParam> statusNotifications)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        using var trans = await db.Database.BeginTransactionAsync();
 
-                db.ChangeTracker.AutoDetectChangesEnabled = false;
-                db.ConnectorStatus.Attach(status);
+        statusNotifications = statusNotifications.DistinctBy(x => x.Id);
+
+        foreach (var param in statusNotifications)
+        {
+            ConnectorStatus status = new() { Id = param.Id };
+
+            //db.ChangeTracker.AutoDetectChangesEnabled = false;
+            db.ConnectorStatus.Attach(status);
 
 
-                status.CreatedOn = param.Status.CreatedOn;
-                status.Status = param.Status.Status;
-                status.ChargePointErrorCodeId = param.Status.ChargePointErrorCodeId;
-                status.ErrorInfo = param.Status.ErrorInfo;
-                status.VendorId = param.Status.VendorId;
-                status.VendorErrorCode = param.Status.VendorErrorCode;
+            status.CreatedOn = param.Status.CreatedOn;
+            status.Status = param.Status.Status;
+            status.ChargePointErrorCodeId = param.Status.ChargePointErrorCodeId;
+            status.ErrorInfo = param.Status.ErrorInfo;
+            status.VendorId = param.Status.VendorId;
+            status.VendorErrorCode = param.Status.VendorErrorCode;
 
 
-                db.Entry(status).Property(x => x.CreatedOn).IsModified = true;
-                db.Entry(status).Property(x => x.Status).IsModified = true;
-                db.Entry(status).Property(x => x.ChargePointErrorCodeId).IsModified = true;
-                db.Entry(status).Property(x => x.ErrorInfo).IsModified = true;
-                db.Entry(status).Property(x => x.VendorId).IsModified = true;
-                db.Entry(status).Property(x => x.VendorErrorCode).IsModified = true;
+            db.Entry(status).Property(x => x.CreatedOn).IsModified = true;
+            db.Entry(status).Property(x => x.Status).IsModified = true;
+            db.Entry(status).Property(x => x.ChargePointErrorCodeId).IsModified = true;
+            db.Entry(status).Property(x => x.ErrorInfo).IsModified = true;
+            db.Entry(status).Property(x => x.VendorId).IsModified = true;
+            db.Entry(status).Property(x => x.VendorErrorCode).IsModified = true;
+
+            //db.SaveChanges();
+        }
 
-                //db.SaveChanges();
-                await db.SaveChangesAsync();
-            }
-            await trans.CommitAsync();
+        db.SaveChanges();
+        trans.Commit();
+        db.ChangeTracker.Clear();
+    }
+
+    private void InitAddServerMessageHandler()
+    {
+        if (addServerMessageHandler is not null)
+        {
+            throw new Exception($"{nameof(InitAddServerMessageHandler)} should only called once");
         }
-        , loggerFactory.CreateLogger("StatusNotificationHandler"));
+
+        addServerMessageHandler = new GroupSingleHandler<ServerMessage>(
+            handleFunc: BundleAddServerMessage,
+            logger: loggerFactory.CreateLogger("AddServerMessageHandler"))
+        {
+            WorkerCnt = 1
+        };
     }
 
+    private async Task BundleAddServerMessage(IEnumerable<ServerMessage> messages)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        using var trans = await db.Database.BeginTransactionAsync();
+
+        foreach (var message in messages)
+        {
+            db.ServerMessage.Add(message);
+        }
+
+        db.SaveChanges();
+        trans.Commit();
+        db.ChangeTracker.Clear();
+    }
 
     private int GetStartupLimit(IConfiguration configuration)
     {