Browse Source

try optimize perfornance

Robert 11 months ago
parent
commit
d3bcb6d23e

+ 150 - 0
EVCB_OCPP.WSServer/Helper/GroupHandlerIO.cs

@@ -0,0 +1,150 @@
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Helper;
+
+public class GroupHandler<TI,TO> where TI : class
+{
+    public GroupHandler(
+        Func<BundleHandlerData<TI,TO>, Task> handleFunc,
+        ILogger logger, int workerCnt = 1, int maxRetry = 10)
+    {
+        this.handleFunc = handleFunc;
+        this.logger = logger;
+
+        this.maxRetry = maxRetry;
+        workersLock = new SemaphoreSlim(workerCnt);
+        //singleWorkLock = new(_WorkerCnt);
+    }
+
+    private readonly Func<BundleHandlerData<TI, TO>, Task> handleFunc;
+    private readonly ILogger logger;
+    private readonly int maxRetry;
+    private readonly ConcurrentQueue<WaitParam<TI, TO>> waitList = new();
+
+    private SemaphoreSlim workersLock;// = new SemaphoreSlim(1);
+
+    public async Task<TO> HandleAsync(TI param)
+    {
+        var waitData = new WaitParam<TI,TO>() { Data = param, Waiter = new SemaphoreSlim(0), Exception = null };
+        waitList.Enqueue(waitData);
+        TryStartHandler();
+        await waitData.Waiter.WaitAsync();
+        if (waitData.Exception is not null)
+        {
+            throw waitData.Exception;
+        }
+        return waitData.Result;
+    }
+
+    private void TryStartHandler()
+    {
+        if (!workersLock.Wait(0))
+        {
+            return;
+        }
+
+        if (waitList.Count == 0)
+        {
+            workersLock.Release();
+            return;
+        }
+
+        _ = StartHandleTask();
+    }
+
+    private async Task StartHandleTask()
+    {
+        var timer = Stopwatch.StartNew();
+        List<long> times = new();
+
+        var requests = new List<WaitParam<TI,TO>>();
+
+        while (waitList.TryDequeue(out var handle))
+        {
+            requests.Add(handle);
+        }
+        times.Add(timer.ElapsedMilliseconds);
+
+        int cnt = 0;
+        Exception lastException = null;
+        var datas = requests.Select(x => x.Data).ToList();
+
+        for (; cnt < maxRetry; cnt++)
+        {
+            var bundleHandledata = new BundleHandlerData<TI, TO>(datas);
+            try
+            {
+                await handleFunc(bundleHandledata);
+            }
+            catch (Exception e)
+            {
+                lastException = e;
+            }
+
+            var completedKeys = bundleHandledata.CompletedDatas.Select(x => x.Key).ToList();
+            var completedRequests = requests.Where(x => completedKeys.Any(y=> y == x.Data)).ToList();
+            foreach (var request in completedRequests)
+            {
+                request.Waiter.Release();
+            }
+
+            //datas = datas.Except(bundleHandledata.CompletedDatas).ToList();
+            datas = datas.Where(x => !completedKeys.Contains(x)).ToList();
+
+            if (datas == null || datas.Count == 0)
+            {
+                break;
+            }
+            logger.LogError(lastException?.Message);
+            times.Add(timer.ElapsedMilliseconds);
+        }
+
+        var uncompletedRequests = requests.Where(x => datas.Contains(x.Data)).ToList();
+        foreach (var request in uncompletedRequests)
+        {
+            request.Exception = lastException;
+            request.Waiter.Release();
+        }
+        workersLock.Release();
+
+        timer.Stop();
+        if (timer.ElapsedMilliseconds > 1000)
+        {
+            logger.LogWarning($"StartHandleTask {string.Join("/", times)}");
+        }
+
+        TryStartHandler();
+    }
+}
+
+public class BundleHandlerData<TI,TO>
+{
+    public List<TI> Datas { get; set; }
+    public List<KeyValuePair<TI,TO>> CompletedDatas { get; set; }
+
+    public BundleHandlerData(List<TI> datas)
+    {
+        Datas = datas;
+        CompletedDatas = new();
+    }
+
+    public void AddCompletedData(TI competedData, TO result)
+    {
+        CompletedDatas.Add(new KeyValuePair<TI, TO>(competedData, result)); ;
+    }
+}
+
+internal class WaitParam<TI,TO>
+{
+    public TI Data { get; init; }
+    public TO Result { get; init; }
+    public SemaphoreSlim Waiter { get; init; }
+    public Exception Exception { get; set; }
+}

+ 122 - 70
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -1544,76 +1544,128 @@ internal partial class ProfileHandler
 							GetConfigurationConfirmation _confirm = confirm as GetConfigurationConfirmation;
 							//  GetConfigurationRequest _request = _confirm.GetRequest() as GetConfigurationRequest;
 
-							using (var db = await maindbContextFactory.CreateDbContextAsync())
-							{
-								var configure = await db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToListAsync();
-
-								if (_confirm.configurationKey != null)
-								{
-									foreach (var item in _confirm.configurationKey)
-									{
-										string oldValue = string.Empty;
-										if (item.key == null)
-										{
-											logger.LogTrace("*********************");
-										}
-										var foundConfig = configure.Find(x => x.ConfigureName == item.key);
-
-
-										if (foundConfig != null)
-										{
-											if (foundConfig.ConfigureName == null)
-											{
-												logger.LogTrace("*********************");
-											}
-
-											if (foundConfig.ConfigureName == "SecurityProfile")
-											{
-												oldValue = foundConfig.ConfigureSetting;
-											}
-
-											foundConfig.ReadOnly = item.IsReadOnly;
-											foundConfig.ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value;
-										}
-										else
-										{
-											await db.MachineConfigurations.AddAsync(new MachineConfigurations()
-											{
-												ChargeBoxId = session.ChargeBoxId,
-												ConfigureName = item.key,
-												ReadOnly = item.IsReadOnly,
-												ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value,
-												Exists = true
-											});
-										}
-
-
-									}
-								}
-								if (_confirm.unknownKey != null)
-								{
-
-									foreach (var item in _confirm.unknownKey)
-									{
-										var foundConfig = configure.Find(x => x.ConfigureName == item);
-										if (foundConfig != null)
-										{
-											foundConfig.ReadOnly = true;
-											foundConfig.ConfigureSetting = string.Empty;
-											foundConfig.Exists = false;
-										}
-										else
-										{
-											await db.MachineConfigurations.AddAsync(new MachineConfigurations()
-											{
-												ChargeBoxId = session.ChargeBoxId,
-												ConfigureName = item
-											});
-										}
-									}
-								}
-
-								var operation = await db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+							List<Task> updateTasks = new List<Task>();
+                            List<MachineConfigurations> configure = await mainDbService.GetMachineConfiguration(session.ChargeBoxId);
+
+                            if (_confirm.configurationKey != null)
+                            {
+                                foreach (var item in _confirm.configurationKey)
+                                {
+                                    string oldValue = string.Empty;
+                                    if (item.key == null)
+                                    {
+                                        logger.LogTrace("*********************");
+                                    }
+                                    var foundConfig = configure.Find(x => x.ConfigureName == item.key);
+
+
+                                    if (foundConfig != null)
+                                    {
+                                        if (foundConfig.ConfigureName == null)
+                                        {
+                                            logger.LogTrace("*********************");
+                                        }
+
+                                        if (foundConfig.ConfigureName == "SecurityProfile")
+                                        {
+                                            oldValue = foundConfig.ConfigureSetting;
+                                        }
+
+										await mainDbService.UpdateMachineConfiguration(session.ChargeBoxId, item.key, item.value, item.IsReadOnly);
+                                    }
+                                    else
+                                    {
+										await mainDbService.AddMachineConfiguration(session.ChargeBoxId, item.key, item.value, item.IsReadOnly);
+                                    }
+                                }
+                            }
+                            if (_confirm.unknownKey != null)
+                            {
+
+                                foreach (var item in _confirm.unknownKey)
+                                {
+                                    var foundConfig = configure.Find(x => x.ConfigureName == item);
+                                    if (foundConfig != null)
+                                    {
+                                        await mainDbService.UpdateMachineConfiguration(session.ChargeBoxId, item, string.Empty, true, isExists: false);
+                                    }
+                                    else
+                                    {
+                                        await mainDbService.AddMachineConfiguration(session.ChargeBoxId, item, string.Empty, true, isExist: false);
+                                    }
+                                }
+                            }
+
+								//var configure = await db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToListAsync();
+
+								//if (_confirm.configurationKey != null)
+								//{
+								//	foreach (var item in _confirm.configurationKey)
+								//	{
+								//		string oldValue = string.Empty;
+								//		if (item.key == null)
+								//		{
+								//			logger.LogTrace("*********************");
+								//		}
+								//		var foundConfig = configure.Find(x => x.ConfigureName == item.key);
+
+
+								//		if (foundConfig != null)
+								//		{
+								//			if (foundConfig.ConfigureName == null)
+								//			{
+								//				logger.LogTrace("*********************");
+								//			}
+
+								//			if (foundConfig.ConfigureName == "SecurityProfile")
+								//			{
+								//				oldValue = foundConfig.ConfigureSetting;
+								//			}
+
+								//			foundConfig.ReadOnly = item.IsReadOnly;
+								//			foundConfig.ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value;
+								//		}
+								//		else
+								//		{
+								//			await db.MachineConfigurations.AddAsync(new MachineConfigurations()
+								//			{
+								//				ChargeBoxId = session.ChargeBoxId,
+								//				ConfigureName = item.key,
+								//				ReadOnly = item.IsReadOnly,
+								//				ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value,
+								//				Exists = true
+								//			});
+								//		}
+
+
+								//	}
+								//}
+								//if (_confirm.unknownKey != null)
+								//{
+
+								//	foreach (var item in _confirm.unknownKey)
+								//	{
+								//		var foundConfig = configure.Find(x => x.ConfigureName == item);
+								//		if (foundConfig != null)
+								//		{
+								//			foundConfig.ReadOnly = true;
+								//			foundConfig.ConfigureSetting = string.Empty;
+								//			foundConfig.Exists = false;
+								//		}
+								//		else
+								//		{
+								//			await db.MachineConfigurations.AddAsync(new MachineConfigurations()
+								//			{
+								//				ChargeBoxId = session.ChargeBoxId,
+								//				ConfigureName = item
+								//			});
+								//		}
+								//	}
+								//}
+
+                            using (var db = await maindbContextFactory.CreateDbContextAsync())
+                            {
+                                var operation = await db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
 							   x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefaultAsync();
 
 								if (operation != null)

+ 103 - 9
EVCB_OCPP.WSServer/Service/DbService/MainDbService.cs

@@ -46,6 +46,9 @@ public interface IMainDbService
     Task<string> GetMachineConnectorType(string chargeBoxId, CancellationToken token = default);
     Task SetMachineConnectionType(string chargeBoxId, int connectionType, CancellationToken token = default);
     Task UpdateServerMessageUpdateTime(int table_id);
+    Task AddMachineConfiguration(string chargeBoxId, string key, string value, bool isReadOnly, bool isExist = true);
+    Task UpdateMachineConfiguration(string chargeBoxId, string item, string empty, bool v, bool isExists = true);
+    Task<List<MachineConfigurations>> GetMachineConfiguration(string chargeBoxId);
 }
 
 public class MainDbService : IMainDbService
@@ -74,6 +77,7 @@ public class MainDbService : IMainDbService
         InitUpdateMachineBasicInfoHandler();
         InitAddServerMessageHandler();
         InitUpdateServerMessageUpdateOnHandler();
+        InitGetMachineConfigurationHandler();
     }
 
     private const string CustomerMemCacheKeyFromat = "Customer_{0}";
@@ -90,8 +94,9 @@ public class MainDbService : IMainDbService
     private readonly SemaphoreSlim opSemaphore;
     private GroupHandler<StatusNotificationParam> statusNotificationHandler;
     private GroupHandler<UpdateMachineBasicInfoParam> updateMachineBasicInfoHandler;
-    private GroupHandler<ServerMessage> addServerMessageHandler;
+    private GroupHandler<ServerMessage, string> addServerMessageHandler;
     private GroupHandler<int> updateServerMessageUpdateOnHandler;
+    private GroupHandler<string, List<MachineConfigurations>> getMachineConfigurationHandler;
 
     public async Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId, CancellationToken token = default)
     {
@@ -109,6 +114,11 @@ public class MainDbService : IMainDbService
         return new MachineAndCustomerInfo(machine.Id, machine.CustomerId, customerName);
     }
 
+    public Task<List<MachineConfigurations>> GetMachineConfiguration(string chargeBoxId)
+    {
+        return getMachineConfigurationHandler.HandleAsync(chargeBoxId);
+    }
+
     public async Task<string> GetMachineConfiguration(string ChargeBoxId, string configName, CancellationToken token = default)
     {
         using var semaphoreWrapper = await startupSemaphore.GetToken();
@@ -259,13 +269,13 @@ public class MainDbService : IMainDbService
         return SerialNo;
     }
 
-    public async Task<string> AddServerMessage(ServerMessage message)
+    public Task<string> AddServerMessage(ServerMessage message)
     {
         //return AddServerMessageEF(message);
-        //return addServerMessageHandler.HandleAsync(message);
-        var id = message.SerialNo;
-        await AddServerMessageDapper(message);
-        return id;
+        return addServerMessageHandler.HandleAsync(message);
+        //var id = message.SerialNo;
+        //await AddServerMessageDapper(message);
+        //return id;
     }
 
     public ValueTask<Customer> GetCustomer(string id, CancellationToken token = default)
@@ -336,6 +346,20 @@ public class MainDbService : IMainDbService
         return updateServerMessageUpdateOnHandler.HandleAsync(table_id);
     }
 
+    public async Task AddMachineConfiguration(string chargeBoxId, string key, string value, bool isReadOnly, bool isExists = true)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+
+        await db.MachineConfigurations.AddAsync(new MachineConfigurations()
+        {
+            ChargeBoxId = chargeBoxId,
+            ConfigureName = key,
+            ReadOnly = isReadOnly,
+            ConfigureSetting = string.IsNullOrEmpty(value) ? string.Empty : value,
+            Exists = isExists
+        });
+    }
+
     private async Task UpdateTransactionEF(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost)
     {
         using var db = await contextFactory.CreateDbContextAsync();
@@ -355,6 +379,20 @@ public class MainDbService : IMainDbService
         await db.SaveChangesAsync();
     }
 
+    public async Task UpdateMachineConfiguration(string chargeBoxId, string item, string value, bool isReadonly, bool isExists = true)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        var config = await db.MachineConfigurations.FirstOrDefaultAsync(x => x.ChargeBoxId == chargeBoxId && x.ConfigureName == item);
+        if (config is null)
+        {
+            return;
+        }
+        config.ConfigureSetting = value;
+        config.ReadOnly = isReadonly;
+        config.Exists = isExists;
+        await db.SaveChangesAsync();
+    }
+
     private async Task UpdateTransactionDapper(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost)
     {
         var parameters = new DynamicParameters();
@@ -416,6 +454,7 @@ public class MainDbService : IMainDbService
             logger: loggerFactory.CreateLogger("StatusNotificationHandler"),
             workerCnt: 1);
     }
+
     private void InitAddServerMessageHandler()
     {
         if (addServerMessageHandler is not null)
@@ -423,7 +462,7 @@ public class MainDbService : IMainDbService
             throw new Exception($"{nameof(InitAddServerMessageHandler)} should only called once");
         }
 
-        addServerMessageHandler = new GroupHandler<ServerMessage>(
+        addServerMessageHandler = new GroupHandler<ServerMessage, string>(
             handleFunc: BundleAddServerMessage,
             logger: loggerFactory.CreateLogger("AddServerMessageHandler"));
     }
@@ -453,6 +492,19 @@ public class MainDbService : IMainDbService
             logger: loggerFactory.CreateLogger("UpdateServerMessageUpdateOnHandler"),
             workerCnt: 10);
     }
+
+    private void InitGetMachineConfigurationHandler()
+    {
+        if (getMachineConfigurationHandler is not null)
+        {
+            throw new Exception($"{nameof(InitUpdateMachineBasicInfoHandler)} should only called once");
+        }
+
+        getMachineConfigurationHandler = new GroupHandler<string, List<MachineConfigurations>>(
+            handleFunc: BundelGetMachineConfiguration,
+            logger: loggerFactory.CreateLogger("GetMachineConfigurationHandler"),
+            workerCnt: 10);
+    }
     
 
     private async Task UpdateMachineBasicInfoEF(string chargeBoxId, Machine machine)
@@ -523,6 +575,26 @@ public class MainDbService : IMainDbService
         }
     }
 
+    private async Task BundelGetMachineConfiguration(BundleHandlerData<string, List<MachineConfigurations>> bundleHandlerData)
+    {
+        var chargeboxIds = bundleHandlerData.Datas;
+        var sql = """
+            SELECT [ChargeBoxId], [ConfigureName], [ConfigureSetting], [ReadOnly], [Exists]
+            FROM [dbo].[MachineConfigurations]
+            WHERE ChargeBoxId IN @ChargeBoxIds
+            """;
+        DynamicParameters parameters = new DynamicParameters();
+        parameters.Add("@ChargeBoxIds", chargeboxIds, direction: ParameterDirection.Input, size: 25);
+
+        using SqlConnection sqlConnection = await sqlConnectionFactory.CreateAsync();
+        var result = await sqlConnection.QueryAsync<MachineConfigurations>(sql, parameters);
+        var gReult = result.GroupBy(x => x.ChargeBoxId);
+        foreach (var g in gReult)
+        {
+            bundleHandlerData.AddCompletedData(g.Key, g.ToList());
+        }
+    }
+
     private async Task UpdateConnectorStatusEF(string Id, ConnectorStatus Status)
     {
         using var db = await contextFactory.CreateDbContextAsync();
@@ -738,8 +810,30 @@ public class MainDbService : IMainDbService
         }
     }
 
-    private async Task BundleAddServerMessage(BundleHandlerData<ServerMessage> bundleHandlerData)
+    private async Task BundleAddServerMessage(BundleHandlerData<ServerMessage, string> bundleHandlerData)
     {
+        //var sql = """
+        //    INSERT INTO [ServerMessage] ([ChargeBoxId], [CreatedBy], [CreatedOn], [InMessage], [OutAction], [OutRequest], [ReceivedOn], [SerialNo], [UpdatedOn])
+        //    OUTPUT INSERTED.Id
+        //    VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8)
+        //    """;
+
+        //using var conn = await sqlConnectionFactory.CreateAsync();
+
+        //foreach(var data in bundleHandlerData.Datas)
+        //{
+        //    var dymparam = new DynamicParameters();
+        //    dymparam.Add("@p0", data.ChargeBoxId);
+        //    dymparam.Add("@p1", data.CreatedBy);
+        //    dymparam.Add("@p2", data.CreatedOn);
+        //    dymparam.Add("@p3", data.InMessage);
+        //    dymparam.Add("@p4", data.OutAction);
+        //    dymparam.Add("@p5", data.OutRequest);
+        //    dymparam.Add("@p6", data.ReceivedOn);
+        //    dymparam.Add("@p7", data.SerialNo);
+        //    dymparam.Add("@p8", data.UpdatedOn);
+        //}
+
         using var db = await contextFactory.CreateDbContextAsync();
         using var trans = await db.Database.BeginTransactionAsync();
 
@@ -751,7 +845,7 @@ public class MainDbService : IMainDbService
         await db.SaveChangesAsync();
         await trans.CommitAsync();
 
-        bundleHandlerData.CompletedDatas.AddRange(bundleHandlerData.Datas);
+        bundleHandlerData.CompletedDatas.AddRange(bundleHandlerData.Datas.Select(x => new KeyValuePair<ServerMessage, string>(x, x.SerialNo)));
     }
 
     private async Task AddServerMessageEF(ServerMessage message)