Robert hai 1 ano
pai
achega
95fa7015a2
Modificáronse 28 ficheiros con 1059 adicións e 427 borrados
  1. 2 0
      Dockerfile
  2. 1 0
      EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj
  3. 30 0
      EVCB_OCPP.WSServer/Fake/DummyMemoryCache.cs
  4. 13 1
      EVCB_OCPP.WSServer/GlobalConfig.cs
  5. 43 0
      EVCB_OCPP.WSServer/Helper/AddLogServcie.cs
  6. 95 10
      EVCB_OCPP.WSServer/Helper/AddPortalDbContext.cs
  7. 75 112
      EVCB_OCPP.WSServer/Helper/GroupSingleHandler.cs
  8. 6 4
      EVCB_OCPP.WSServer/Helper/MeterValueGroupSingleHandler.cs
  9. 5 1
      EVCB_OCPP.WSServer/HostedProtalServer.cs
  10. 64 61
      EVCB_OCPP.WSServer/Jobs/ServerMessageJob.cs
  11. 8 4
      EVCB_OCPP.WSServer/Jobs/ServerSetFeeJob.cs
  12. 8 4
      EVCB_OCPP.WSServer/Jobs/SmartChargingJob.cs
  13. 49 44
      EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs
  14. 8 6
      EVCB_OCPP.WSServer/Program.cs
  15. 61 41
      EVCB_OCPP.WSServer/ProtalServer.cs
  16. 21 6
      EVCB_OCPP.WSServer/Service/BusinessServiceFactory.cs
  17. 257 3
      EVCB_OCPP.WSServer/Service/ConnectionLogdbService.cs
  18. 17 15
      EVCB_OCPP.WSServer/Service/HttpClientService.cs
  19. 23 16
      EVCB_OCPP.WSServer/Service/LoadingBalanceService.cs
  20. 0 4
      EVCB_OCPP.WSServer/Service/LocalBusinessService.cs
  21. 197 45
      EVCB_OCPP.WSServer/Service/MainDbService.cs
  22. 26 28
      EVCB_OCPP.WSServer/Service/MeterValueDbService.cs
  23. 8 5
      EVCB_OCPP.WSServer/Service/MeterValueInsertHandler.cs
  24. 14 3
      EVCB_OCPP.WSServer/Service/OuterBusinessService.cs
  25. 9 4
      EVCB_OCPP.WSServer/Service/WebDbService.cs
  26. 9 8
      EVCB_OCPP.WSServer/appsettings.json
  27. 5 2
      SocketBase/AppServerBase.cs
  28. 5 0
      build.bat

+ 2 - 0
Dockerfile

@@ -1,6 +1,8 @@
 #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
 
 FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
+RUN sed -i 's/TLSv1.2/TLSv1/g' /etc/ssl/openssl.cnf
+RUN sed -i 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf
 EXPOSE 80
 EXPOSE 443
 EXPOSE 54088

+ 1 - 0
EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj

@@ -47,6 +47,7 @@
     <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
     <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="7.0.9" />
     <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
     <PackageReference Include="NLog" Version="5.1.4" />

+ 30 - 0
EVCB_OCPP.WSServer/Fake/DummyMemoryCache.cs

@@ -0,0 +1,30 @@
+using Microsoft.Extensions.Caching.Memory;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Fake;
+
+internal class DummyMemoryCache : IMemoryCache
+{
+    public ICacheEntry CreateEntry(object key)
+    {
+        return null;
+    }
+
+    public void Dispose()
+    {
+    }
+
+    public void Remove(object key)
+    {
+    }
+
+    public bool TryGetValue(object key, out object value)
+    {
+        value = default(object);
+        return false;
+    }
+}

+ 13 - 1
EVCB_OCPP.WSServer/GlobalConfig.cs

@@ -75,13 +75,25 @@ namespace EVCB_OCPP.WSServer
                             {
                                 var value = configuration[key];
 
-                                WS_Port = Int32.TryParse(value, out WS_Port) ? WS_Port : 0;
+                                if (string.IsNullOrEmpty(value))
+                                {
+                                    WS_Port = 80;
+                                    break;
+                                }
+
+                                WS_Port = Int32.TryParse(value, out WS_Port) ? WS_Port : 80;
                             }
                             break;
                         case "WSSPort":
                             {
                                 var value = configuration[key];
 
+                                if (string.IsNullOrEmpty(value))
+                                {
+                                    WSS_Ports = new List<int>();
+                                    break;
+                                }
+
                                 var ports = value.Split(',');
                                 WSS_Ports = new List<int>();
                                 foreach (var port in ports)

+ 43 - 0
EVCB_OCPP.WSServer/Helper/AddLogServcie.cs

@@ -0,0 +1,43 @@
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using NLog.Extensions.Logging;
+using NLog.Web;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Helper
+{
+    public static class AddLogServcieExtention
+    {
+        public static IHostBuilder AddLogServcie(this IHostBuilder hostBuilder)
+        {
+            string loggerSelectString = "";
+            hostBuilder.ConfigureLogging((context, loggingBuilder) => {
+                loggerSelectString = context.Configuration["LogProvider"];
+                loggingBuilder.ClearProviders();
+
+                if (loggerSelectString == "Azure")
+                {
+                    loggingBuilder.AddAzureWebAppDiagnostics();
+                    return;
+                }
+
+                if (loggerSelectString == "Console")
+                {
+                    loggingBuilder.AddConsole();
+                    return;
+                }
+
+                NLog.LogManager.Configuration = new NLogLoggingConfiguration(context.Configuration.GetSection("NLog"));
+            });
+            if (loggerSelectString != "Azure")
+            {
+                hostBuilder.UseNLog();
+            }
+            return hostBuilder;
+        }
+    }
+}

+ 95 - 10
EVCB_OCPP.WSServer/Helper/AddPortalDbContext.cs

@@ -1,9 +1,13 @@
 using EVCB_OCPP.Domain;
+using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
+using MongoDB.Driver.Core.Configuration;
 using System;
 using System.Collections.Generic;
+using System.Configuration;
+using System.Data.Entity.Infrastructure;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -19,7 +23,14 @@ public static class AddPortalDbContext
         const string DbPassKey = "MainDbPass";
         const string DbConnectionStringKey = "MainDBContext";
 
-        AddPortalDbContextInternal<MainDBContext>(services,configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey, logToConsole: false);
+        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
+
+        services.AddSingleton(
+            new SqlConnectionFactory<MainDBContext>()
+            {
+                ConnectionString = conneciotnString
+            });
+        AddPortalDbContextInternal<MainDBContext>(services, configuration, conneciotnString, logToConsole: false);
         return services;
     }
 
@@ -29,7 +40,13 @@ public static class AddPortalDbContext
         const string DbPassKey = "MeterValueDbPass";
         const string DbConnectionStringKey = "MeterValueDBContext";
 
-        AddPortalDbContextInternal<MeterValueDBContext>(services, configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey, logToConsole: false);
+        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
+        services.AddSingleton(
+            new SqlConnectionFactory<MeterValueDBContext>()
+            {
+                ConnectionString = conneciotnString
+            });
+        AddPortalDbContextInternal<MeterValueDBContext>(services, configuration, conneciotnString, logToConsole: false);
         return services;
     }
 
@@ -39,23 +56,55 @@ public static class AddPortalDbContext
         const string DbPassKey = "ConnectionLogDbPass";
         const string DbConnectionStringKey = "ConnectionLogDBContext";
 
-        AddPortalDbContextInternal<ConnectionLogDBContext>(services, configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
+        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
+        services.AddSingleton(
+            new SqlConnectionFactory<ConnectionLogDBContext>()
+            {
+                ConnectionString = conneciotnString
+            });
+        AddPortalDbContextInternal<ConnectionLogDBContext>(services, configuration, conneciotnString);
+        return services;
+    }
+
+    public static IServiceCollection AddWebDBConetext(this IServiceCollection services, IConfiguration configuration)
+    {
+        const string DbUserIdKey = "WebDbUserId";
+        const string DbPassKey = "WebDbPass";
+        const string DbConnectionStringKey = "WebDBContext";
+
+        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
+        services.AddSingleton(
+            new SqlConnectionFactory<WebDBConetext>()
+            {
+                ConnectionString = conneciotnString
+            });
+        return services;
+    }
+
+    public static IServiceCollection AddOnlineLogDBContext(this IServiceCollection services, IConfiguration configuration)
+    {
+        const string DbUserIdKey = "OnlineLogDbUserId";
+        const string DbPassKey = "OnlineLogDbPass";
+        const string DbConnectionStringKey = "OnlineLogDBContext";
+
+        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
+        services.AddSingleton(
+            new SqlConnectionFactory<OnlineLogDBContext>()
+            {
+                ConnectionString = conneciotnString
+            });
         return services;
     }
 
     private static void AddPortalDbContextInternal<T>(
         IServiceCollection services, IConfiguration configuration,
-        string UserIdKey,string DbPassKey, string ConnectionStringKey,bool logToConsole = false) where T : DbContext
+        string connectionString,bool logToConsole = false) where T : DbContext
     {
 
         var commandTimeout = int.TryParse(configuration[CommandTimeoutKey], out var temp) ? temp : 180;
-        string mainDbUserId = string.IsNullOrEmpty(configuration[UserIdKey]) ? string.Empty : $"user id={configuration[UserIdKey]};";
-        string mainDbUserPass = string.IsNullOrEmpty(configuration[DbPassKey]) ? string.Empty : $"password={configuration[DbPassKey]};";
-
+        
         services.AddPooledDbContextFactory<T>((serviceProvider, options) => {
-            var cString = configuration.GetConnectionString(ConnectionStringKey);
-            cString = $"{cString}{mainDbUserId}{mainDbUserPass}";
-            options.UseSqlServer(cString, dbOptions =>
+            options.UseSqlServer(connectionString, dbOptions =>
             {
                 dbOptions.CommandTimeout(commandTimeout);
             });
@@ -65,4 +114,40 @@ public static class AddPortalDbContext
             }
         });
     }
+
+    private static string GetConnectionString(IConfiguration configuration, string UserIdKey, string DbPassKey, string ConnectionStringKey ) 
+    {
+        string mainDbUserId = string.IsNullOrEmpty(configuration[UserIdKey]) ? string.Empty : $"user id={configuration[UserIdKey]};";
+        string mainDbUserPass = string.IsNullOrEmpty(configuration[DbPassKey]) ? string.Empty : $"password={configuration[DbPassKey]};";
+        return $"{configuration.GetConnectionString(ConnectionStringKey)}{mainDbUserId}{mainDbUserPass}";
+    }
 }
+
+public class SqlConnectionFactory<T> where T: DbContext
+{
+    public string ConnectionString { get; init; }
+    public SqlConnectionFactory() { }
+    public SqlConnection Create()
+    {
+        var sqlConnection = new SqlConnection(ConnectionString);
+        sqlConnection.Open();
+        return sqlConnection;
+    }
+
+    public async Task<SqlConnection> CreateAsync()
+    {
+        var sqlConnection = new SqlConnection(ConnectionString);
+        await sqlConnection.OpenAsync();
+        return sqlConnection;
+    }
+}
+
+/// <summary>
+/// Dummy
+/// </summary>
+public class WebDBConetext : DbContext {}
+
+/// <summary>
+/// Dummy
+/// </summary>
+public class OnlineLogDBContext : DbContext { }

+ 75 - 112
EVCB_OCPP.WSServer/Helper/GroupSingleHandler.cs

@@ -16,141 +16,104 @@ public class GroupSingleHandler<T>
         this.handleFunc = handleFunc;
         this.logger = logger;
 
-        //singleWorkLock = new (_WorkerCnt);
-        //singleHandleTask = StartHandleTask();
+        WorkerCnt = workerCnt;
+        //singleWorkLock = new(_WorkerCnt);
+    }
 
-        _handleTasks = new Task[workerCnt];
-        for (int cnt = 0; cnt < workerCnt; cnt++)
+    private int _WorkerCnt = 1;
+    public int WorkerCnt
+    {
+        get => _WorkerCnt;
+        set
         {
-            _handleTasks[cnt] = StartHandleTask();
+            if (IsStarted)
+            {
+                throw new Exception($"{nameof(WorkerCnt)} must not be changed afted {nameof(HandleAsync)} is called");
+            }
+
+            _WorkerCnt = value;
+            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 BlockingCollection<(T param, SemaphoreSlim waitLock)> blockQueue = new();
-    //private SemaphoreSlim singleWorkLock;// = new SemaphoreSlim(1);
+    private readonly ConcurrentQueue<(T param, SemaphoreSlim waitLock)> waitList = new();
+    private SemaphoreSlim singleWorkLock;// = new SemaphoreSlim(1);
     private bool IsStarted = false;
     private Task singleHandleTask;
-    private Task[] _handleTasks;
 
     public Task HandleAsync(T param)
     {
         IsStarted = true;
 
         SemaphoreSlim reqLock = new(0);
-        blockQueue.Add((param, reqLock));
-        //TryStartHandler();
+        waitList.Enqueue((param, reqLock));
+        TryStartHandler();
         return reqLock.WaitAsync();
     }
 
-    //private void TryStartHandler()
-    //{
-    //    if (!singleWorkLock.Wait(0))
-    //    {
-    //        return;
-    //    }
+    private void TryStartHandler()
+    {
+        if (!singleWorkLock.Wait(0))
+        {
+            return;
+        }
 
-    //    if (waitList.Count == 0)
-    //    {
-    //        singleWorkLock.Release();
-    //        return;
-    //    }
+        if (waitList.Count == 0)
+        {
+            singleWorkLock.Release();
+            return;
+        }
 
-    //    singleHandleTask = StartHandleTask();
-    //}
+        singleHandleTask = StartHandleTask();
+    }
 
-    private Task StartHandleTask()
+    private async Task StartHandleTask()
     {
-        return Task.Run(async () => {
-            while (true)
+        var timer = Stopwatch.StartNew();
+        long t0 = 0, t1 = 0, t2 = 0;
+
+        var handleList = new List<(T param, SemaphoreSlim waitLock)>();
+
+        while (waitList.TryDequeue(out var handle))
+        {
+            handleList.Add(handle);
+        }
+        t0 = timer.ElapsedMilliseconds;
+
+        int cnt = 0;
+        do
+        {
+            cnt++;
+            try
             {
-                var handleList = new List<(T param, SemaphoreSlim waitLock)>();
-                try
-                {
-                    var startData = blockQueue.Take();
-                    handleList.Add(startData);
-                }
-                catch (InvalidOperationException e)
-                {
-                    logger.LogError(e, "blockQueue.Take Error");
-                    break;
-                }
-
-                var watch = Stopwatch.StartNew();
-                long t0, t1, t2, t3;
-
-                while (blockQueue.TryTake(out var data))
-                {
-                    handleList.Add(data);
-                }
-                t0 = watch.ElapsedMilliseconds;
-                var task = handleFunc(handleList.Select(x => x.param).ToList());
-                t1 = watch.ElapsedMilliseconds;
-                await task.ConfigureAwait(false);
-                t2 = watch.ElapsedMilliseconds;
-
-                foreach (var handled in handleList)
-                {
-                    handled.waitLock.Release();
-                }
-                watch.Stop();
-                t3 = watch.ElapsedMilliseconds;
-                if (t3 > 1000)
-                {
-                    logger.LogWarning("StartHandleTask {0}/{1}/{2}/{3}", t0, t1, t2, t3);
-                }
+                var task = handleFunc(handleList.Select(x => x.param));
+                await task;
+                t1 = timer.ElapsedMilliseconds;
+                break;
             }
-        });
-    }
+            catch (Exception e)
+            {
+                logger.LogError(e, "Trying Cnt {0}", cnt);
+                logger.LogError(e.Message);
+            }
+        }
+        while (true);
 
-    //private async Task StartHandleTask()
-    //{
-    //    var handleList = new List<(T param, SemaphoreSlim waitLock)>();
-
-    //    while (waitList.TryDequeue(out var handle))
-    //    {
-    //        handleList.Add(handle);
-    //    }
-
-    //    int cnt = 0;
-    //    do
-    //    {
-    //        cnt++;
-    //        try
-    //        {
-    //            var task = handleFunc(handleList.Select(x => x.param));
-    //            await task;
-    //            break;
-    //        }
-    //        catch (Exception e)
-    //        {
-    //            logger.LogError(e, "Trying Cnt {0}", cnt);
-    //        }
-    //    }
-    //    while (true);
-
-    //    foreach (var handled in handleList)
-    //    {
-    //        handled.waitLock.Release();
-    //    }
-    //    singleWorkLock.Release();
-    //    TryStartHandler();
-    //}
+        foreach (var handled in handleList)
+        {
+            handled.waitLock.Release();
+        }
+        singleWorkLock.Release();
+
+        timer.Stop();
+        t2= timer.ElapsedMilliseconds;
+        if (t2 >1000)
+        {
+            logger.LogWarning("StartHandleTask {0}/{1}/{2}", t0, t1, t2);
+        }
+
+        TryStartHandler();
+    }
 }

+ 6 - 4
EVCB_OCPP.WSServer/Helper/MeterValueGroupSingleHandler.cs

@@ -20,16 +20,18 @@ public class MeterValueGroupSingleHandler
 {
     public MeterValueGroupSingleHandler(
         IDbContextFactory<MeterValueDBContext> meterValueDbContextFactory,
+        SqlConnectionFactory<MeterValueDBContext> connectionFactory,
         IConfiguration configuration,
         ILogger<MeterValueGroupSingleHandler> logger)
     {
         this.meterValueDbContextFactory = meterValueDbContextFactory;
+        this.connectionFactory = connectionFactory;
         this.configuration = configuration;
         this.logger = logger;
 
         //singleWorkLock = new (_WorkerCnt);
         //singleHandleTask = StartHandleTask();
-        this.meterValueConnectionString = configuration.GetConnectionString("MeterValueDBContext");
+        //this.meterValueConnectionString = configuration.GetConnectionString("MeterValueDBContext");
 
         var workerCnt = 20;
         _handleTasks = new Task[workerCnt];
@@ -40,11 +42,12 @@ public class MeterValueGroupSingleHandler
     }
 
     private readonly IDbContextFactory<MeterValueDBContext> meterValueDbContextFactory;
+    private readonly SqlConnectionFactory<MeterValueDBContext> connectionFactory;
     private readonly IConfiguration configuration;
 
     //private readonly Func<IEnumerable<T>, Task> handleFunc;
     private readonly ILogger logger;
-    private readonly string meterValueConnectionString;
+    //private readonly string meterValueConnectionString;
     private readonly BlockingCollection<(InsertMeterValueParam param, SemaphoreSlim waitLock)> blockQueue = new();
     private static Queue<string> _existTables = new();
     //private SemaphoreSlim singleWorkLock;// = new SemaphoreSlim(1);
@@ -151,8 +154,7 @@ public class MeterValueGroupSingleHandler
         t2 = watch.ElapsedMilliseconds;
         foreach (var group in gruopParams)
         {
-            using SqlConnection sqlConnection = new SqlConnection(meterValueConnectionString);
-            sqlConnection.Open();
+            using SqlConnection sqlConnection = connectionFactory.Create();
             using var tans = sqlConnection.BeginTransaction();
 
             var tableName = group.Key;

+ 5 - 1
EVCB_OCPP.WSServer/HostedProtalServer.cs

@@ -1,10 +1,12 @@
 using EVCB_OCPP.Domain;
+using EVCB_OCPP.WSServer.Fake;
 using EVCB_OCPP.WSServer.Helper;
 using EVCB_OCPP.WSServer.Jobs;
 using EVCB_OCPP.WSServer.Message;
 using EVCB_OCPP.WSServer.Service;
 using EVCB_OCPP.WSServer.SuperSocket;
 using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Quartz;
@@ -21,6 +23,7 @@ namespace EVCB_OCPP.WSServer
         public static void AddProtalServer(this IServiceCollection services, IConfiguration configuration)
         {
             services.AddMemoryCache();
+            //services.AddSingleton<IMemoryCache, DummyMemoryCache>();
 
             services.AddPortalServerDatabase(configuration);
             services.AddBusinessServiceFactory();
@@ -45,7 +48,8 @@ namespace EVCB_OCPP.WSServer
             services
                 .AddMainDbContext(configuration)
                 .AddMeterValueDbContext(configuration)
-                .AddConnectionLogDbContext(configuration);
+                .AddConnectionLogDbContext(configuration)
+                .AddWebDBConetext(configuration);
         }
 
         public static void AddProtalServerJob(this IServiceCollection services)

+ 64 - 61
EVCB_OCPP.WSServer/Jobs/ServerMessageJob.cs

@@ -54,10 +54,10 @@ public class ServerMessageJob : IJob
 
         BasicMessageHandler msgAnalyser = new BasicMessageHandler();
         using var db = maindbContextFactory.CreateDbContext();
-        var dateTimeNoew = DateTime.UtcNow;
-        DateTime startDt = dateTimeNoew.AddSeconds(-30);
+        var dateTimeNow = DateTime.UtcNow;
+        DateTime startDt = dateTimeNow.AddSeconds(-30);
         DateTime dt = new DateTime(1991, 1, 1);
-        DateTime currentTime = dateTimeNoew;
+        DateTime currentTime = dateTimeNow;
         var commandList = await db.ServerMessage.Where(c => c.ReceivedOn == dt && c.UpdatedOn == dt && c.CreatedOn >= startDt && c.CreatedOn <= currentTime).AsNoTracking().ToListAsync();
         var clientDic = protalServer.ClientDic;
 
@@ -73,10 +73,10 @@ public class ServerMessageJob : IJob
             ClientData session;
             if (clientDic.TryGetValue(resendItem.ChargePointSerialNumber, out session))
             {
-                if (dateTimeNoew.Subtract(resendItem.SentOn).TotalSeconds > 1)
+                if (dateTimeNow.Subtract(resendItem.SentOn).TotalSeconds > 1)
                 {
                     resendItem.SentTimes--;
-                    resendItem.SentOn = dateTimeNoew;
+                    resendItem.SentOn = dateTimeNow;
                     protalServer.SendMsg(session, resendItem.SentMessage, string.Format("{0} {1}", resendItem.SentAction, "Request"), "");
                 }
 
@@ -87,87 +87,90 @@ public class ServerMessageJob : IJob
         {
             ClientData session;
             string uuid = string.Empty;
-            if (clientDic.TryGetValue(charger_SN, out session))
+
+            if (!clientDic.TryGetValue(charger_SN, out session))
             {
-                //logger.LogDebug(string.Format("charger_SN:{0} startDt:{1} CreatedOn:{2}", charger_SN, startDt.ToString("yyyy/MM/dd HH:mm:ss"), DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss")));
+                continue;
+            }
 
-                if (session.IsCheckIn && !session.ISOCPP20)
-                {
-                    string rawRequest = string.Empty;
+            //logger.LogDebug(string.Format("charger_SN:{0} startDt:{1} CreatedOn:{2}", charger_SN, startDt.ToString("yyyy/MM/dd HH:mm:ss"), DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss")));
 
-                    var cmdList = commandList.Where(c => c.ChargeBoxId == charger_SN).ToList();
+            if (session.IsCheckIn && !session.ISOCPP20)
+            {
+                string rawRequest = string.Empty;
 
-                    var profiles = protalServer.Profiles;
+                var cmdList = commandList.Where(c => c.ChargeBoxId == charger_SN).ToList();
+
+                var profiles = protalServer.Profiles;
+
+                foreach (var item in cmdList)
+                {
+                    IRequest request = null;
+                    Actions action = Actions.None;
+                    Enum.TryParse(item.OutAction, out action);
+                    Type _RequestType = null;
 
-                    foreach (var item in cmdList)
+                    for (int i = 0; i < profiles.Count; i++)
                     {
-                        IRequest request = null;
-                        Actions action = Actions.None;
-                        Enum.TryParse(item.OutAction, out action);
-                        Type _RequestType = null;
+                        var feature = profiles[i].GetFeaturebyAction(item.OutAction);
 
-                        for (int i = 0; i < profiles.Count; i++)
+                        if (feature != null)
                         {
-                            var feature = profiles[i].GetFeaturebyAction(item.OutAction);
-
-                            if (feature != null)
-                            {
-                                _RequestType = feature.GetRequestType();
-                                break;
-                            }
+                            _RequestType = feature.GetRequestType();
+                            break;
                         }
+                    }
+
+                    if (_RequestType != null && item.CreatedBy != "Destroyer")
+                    {
+                        request = JsonConvert.DeserializeObject(item.OutRequest, _RequestType) as IRequest;
+                        uuid = session.queue.store(request);
+                        rawRequest = BasicMessageHandler.GenerateRequest(uuid, item.OutAction, request);
+                        protalServer.SendMsg(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
+                    }
+
 
-                        if (_RequestType != null && item.CreatedBy != "Destroyer")
+                    if (item.CreatedBy == "Destroyer")
+                    {
+
+                        if (_RequestType != null)
                         {
-                            request = JsonConvert.DeserializeObject(item.OutRequest, _RequestType) as IRequest;
+                            request = Activator.CreateInstance(_RequestType) as IRequest;
                             uuid = session.queue.store(request);
-                            rawRequest = BasicMessageHandler.GenerateRequest(uuid, item.OutAction, request);
+                            rawRequest = BasicMessageHandler.GenerateDestroyRequest(uuid, item.OutAction, item.OutRequest);
                             protalServer.SendMsg(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
-                        }
 
-
-                        if (item.CreatedBy == "Destroyer")
+                        }
+                        else
                         {
 
-                            if (_RequestType != null)
-                            {
-                                request = Activator.CreateInstance(_RequestType) as IRequest;
-                                uuid = session.queue.store(request);
-                                rawRequest = BasicMessageHandler.GenerateDestroyRequest(uuid, item.OutAction, item.OutRequest);
-                                protalServer.SendMsg(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
-
-                            }
-                            else
-                            {
-
-                                rawRequest = BasicMessageHandler.GenerateDestroyRequest(Guid.NewGuid().ToString(), item.OutAction, item.OutRequest);
-                                protalServer.SendMsg(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
+                            rawRequest = BasicMessageHandler.GenerateDestroyRequest(Guid.NewGuid().ToString(), item.OutAction, item.OutRequest);
+                            protalServer.SendMsg(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
 
-                            }
                         }
+                    }
 
-                        protalServer.AddConfirmMessage(charger_SN, item.Id, item.SerialNo, item.OutAction, uuid, item.CreatedBy, rawRequest);
-
-                        dateTimeNoew = DateTime.UtcNow;
-                        #region 更新資料表單一欄位
-                        var _UpdatedItem = new ServerMessage() { Id = item.Id, UpdatedOn = dateTimeNoew };
-                        //db.Configuration.AutoDetectChangesEnabled = false;//自動呼叫DetectChanges()比對所有的entry集合的每一個屬性Properties的新舊值
-                        //db.Configuration.ValidateOnSaveEnabled = false;// 因為Entity有些欄位必填,若不避開會有Validate錯誤
-                        // var _UpdatedItem = db.ServerMessage.Where(x => x.Id == item.Id).FirstOrDefault();
-                        db.ServerMessage.Attach(_UpdatedItem);
-                        _UpdatedItem.UpdatedOn = dateTimeNoew;
-                        db.Entry(_UpdatedItem).Property(x => x.UpdatedOn).IsModified = true;// 可以直接使用這方式強制某欄位要更新,只是查詢集合耗效能而己
+                    protalServer.AddConfirmMessage(charger_SN, item.Id, item.SerialNo, item.OutAction, uuid, item.CreatedBy, rawRequest);
 
-                        await db.SaveChangesAsync();
-                        db.ChangeTracker.Clear();
+                    dateTimeNow = DateTime.UtcNow;
+                    #region 更新資料表單一欄位
+                    var _UpdatedItem = new ServerMessage() { Id = item.Id, UpdatedOn = dateTimeNow };
+                    //db.Configuration.AutoDetectChangesEnabled = false;//自動呼叫DetectChanges()比對所有的entry集合的每一個屬性Properties的新舊值
+                    //db.Configuration.ValidateOnSaveEnabled = false;// 因為Entity有些欄位必填,若不避開會有Validate錯誤
+                    // var _UpdatedItem = db.ServerMessage.Where(x => x.Id == item.Id).FirstOrDefault();
+                    db.ServerMessage.Attach(_UpdatedItem);
+                    _UpdatedItem.UpdatedOn = dateTimeNow;
+                    db.Entry(_UpdatedItem).Property(x => x.UpdatedOn).IsModified = true;// 可以直接使用這方式強制某欄位要更新,只是查詢集合耗效能而己
 
-                        #endregion
+                    await db.SaveChangesAsync();
+                    db.ChangeTracker.Clear();
 
-                        await Task.Delay(100);
+                    #endregion
 
-                    }
+                    await Task.Delay(100);
 
                 }
+
             }
         }
 

+ 8 - 4
EVCB_OCPP.WSServer/Jobs/ServerSetFeeJob.cs

@@ -4,6 +4,7 @@ using EVCB_OCPP.Domain.Models.Database;
 using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages.Core;
 using EVCB_OCPP.WSServer.Dto;
+using EVCB_OCPP.WSServer.Helper;
 using EVCB_OCPP.WSServer.Message;
 using EVCB_OCPP.WSServer.Service;
 using Microsoft.Data.SqlClient;
@@ -25,20 +26,23 @@ namespace EVCB_OCPP.WSServer.Jobs;
 public class ServerSetFeeJob : IJob
 {
     private readonly ProtalServer protalServer;
+    private readonly SqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
     private readonly IMainDbService mainDbService;
     private readonly ILogger<ServerSetFeeJob> logger;
-    private readonly string webConnectionString;
+    //private readonly string webConnectionString;
 
     public ServerSetFeeJob(
         ProtalServer protalServer,
-        IConfiguration configuration,
+        //IConfiguration configuration,
+        SqlConnectionFactory<WebDBConetext> sqlConnectionFactory,
         IMainDbService mainDbService,
         ILogger<ServerSetFeeJob> logger)
     {
         this.protalServer = protalServer;
+        this.webDbConnectionFactory = sqlConnectionFactory;
         this.mainDbService = mainDbService;
         this.logger = logger;
-        this.webConnectionString = configuration.GetConnectionString("WebDBContext");
+        //this.webConnectionString = configuration.GetConnectionString("WebDBContext");
     }
 
     public async Task Execute(IJobExecutionContext context)
@@ -111,7 +115,7 @@ public class ServerSetFeeJob : IJob
 
         try
         {
-            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            using (SqlConnection conn = webDbConnectionFactory.Create())
             {
                 var parameters = new DynamicParameters();
                 parameters.Add("@MachineId", client.MachineId, DbType.String, ParameterDirection.Input, 36);

+ 8 - 4
EVCB_OCPP.WSServer/Jobs/SmartChargingJob.cs

@@ -11,6 +11,7 @@ using EVCB_OCPP.WSServer.Dto;
 using Microsoft.Extensions.Configuration;
 using Dapper;
 using Microsoft.Extensions.Logging;
+using EVCB_OCPP.WSServer.Helper;
 
 namespace EVCB_OCPP.WSServer.Jobs;
 
@@ -19,16 +20,19 @@ public class SmartChargingJob : IJob
 {
     public SmartChargingJob(
         ProtalServer protalServer,
-        IConfiguration configuration,
+        SqlConnectionFactory<WebDBConetext> webDbConnectionFactory,
+        //IConfiguration configuration,
         ILogger<SmartChargingJob> logger)
     {
-        this.webConnectionString = configuration.GetConnectionString("WebDBContext");
+        //this.webConnectionString = configuration.GetConnectionString("WebDBContext");
         this.protalServer = protalServer;
+        this.webDbConnectionFactory = webDbConnectionFactory;
         this.logger = logger;
     }
 
-    private readonly string webConnectionString;
+    //private readonly string webConnectionString;
     private readonly ProtalServer protalServer;
+    private readonly SqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
     private readonly ILogger<SmartChargingJob> logger;
     private static List<StationInfoDto> _StationInfo = new List<StationInfoDto>();
 
@@ -36,7 +40,7 @@ public class SmartChargingJob : IJob
     {
         //logger.LogDebug("{0} Started", nameof(SmartChargingJob));
         List<StationInfoDto> stations = null;
-        using (SqlConnection conn = new SqlConnection(webConnectionString))
+        using (SqlConnection conn = webDbConnectionFactory.Create())
         {
             string strSql = "SELECT[Id],[LBMode],[LBCurrent] as Availability FROM[StandardOCPP_Web].[dbo].[Station]" +
              "where LBMode = 1; ";

+ 49 - 44
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -29,6 +29,9 @@ using System.Linq;
 using System.Threading.Tasks;
 
 using Dapper;
+using Microsoft.AspNetCore.Http.HttpResults;
+using MongoDB.Driver.Core.Connections;
+using EVCB_OCPP20.Packet.DataTypes;
 
 namespace EVCB_OCPP.WSServer.Message;
 
@@ -73,8 +76,9 @@ public class ID_ReaderStatus
 internal partial class ProfileHandler
 {
     private readonly ILogger logger;
-    private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings[].ConnectionString;
+    //private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings[].ConnectionString;
     private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
+    private readonly SqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
     private readonly MeterValueDbService meterValueDbService;
 
     //private readonly IDbContextFactory<MeterValueDBContext> metervaluedbContextFactory;
@@ -85,16 +89,18 @@ internal partial class ProfileHandler
     public ProfileHandler(
         IConfiguration configuration,
         IDbContextFactory<MainDBContext> maindbContextFactory, 
+        SqlConnectionFactory<WebDBConetext> webDbConnectionFactory,
         //IDbContextFactory<MeterValueDBContext> metervaluedbContextFactory,
         MeterValueDbService meterValueDbService,
         IBusinessServiceFactory businessServiceFactory,
         IMainDbService mainDbService,
         ILogger<ProfileHandler> logger)
     {
-        webConnectionString = configuration.GetConnectionString("WebDBContext");
+        //webConnectionString = configuration.GetConnectionString("WebDBContext");
 
         this.logger = logger;
         this.maindbContextFactory = maindbContextFactory;
+        this.webDbConnectionFactory = webDbConnectionFactory;
         this.meterValueDbService = meterValueDbService;
         this.mainDbService = mainDbService;
         //this.metervaluedbContextFactory = metervaluedbContextFactory;
@@ -282,46 +288,29 @@ internal partial class ProfileHandler
 
                         if (_oldStatus == null)
                         {
-                            using (var db = maindbContextFactory.CreateDbContext())
-                            {
-                                var _currentStatus = new Domain.Models.Database.ConnectorStatus()
-                                {
-                                    ChargeBoxId = session.ChargeBoxId,
-                                    ConnectorId = (byte)_request.connectorId,
-                                    CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
-                                    Status = (int)_request.status,
-                                    ChargePointErrorCodeId = (int)_request.errorCode,
-                                    ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
-                                    VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId,
-                                    VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
-                                    Id = Guid.NewGuid().ToString()
-                                };
-                                db.ConnectorStatus.Add(_currentStatus);
-
-                                db.SaveChanges();
-                            }
+                            await mainDbService.AddConnectorStatus(
+                                ChargeBoxId: session.ChargeBoxId,
+                                ConnectorId: (byte)_request.connectorId,
+                                CreatedOn: _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
+                                Status: (int)_request.status,
+                                ChargePointErrorCodeId: (int)_request.errorCode,
+                                ErrorInfo: string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
+                                VendorId: string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId,
+                                VendorErrorCode: string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode);
                         }
                         s3 = statusNotificationTimer.ElapsedMilliseconds;
 
                         if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
                         {
-                            using (var db = maindbContextFactory.CreateDbContext())
-                            {
-                                db.MachineError.Add(new MachineError()
-                                {
-                                    ConnectorId = (byte)_request.connectorId,
-                                    CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
-                                    Status = (int)_request.status,
-                                    ChargeBoxId = session.ChargeBoxId,
-                                    ErrorCodeId = (int)_request.errorCode,
-                                    ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
-                                    PreStatus = _oldStatus == null ? -1 : preStatus,
-                                    VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
-                                    VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId
-                                });
-
-                                db.SaveChanges();
-                            }
+                            await mainDbService.AddMachineError(ConnectorId: (byte)_request.connectorId,
+                                    CreatedOn: _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
+                                    Status: (int)_request.status,
+                                    ChargeBoxId: session.ChargeBoxId,
+                                    ErrorCodeId: (int)_request.errorCode,
+                                    ErrorInfo: string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
+                                    PreStatus: _oldStatus == null ? -1 : preStatus,
+                                    VendorErrorCode: string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
+                                    VendorId: string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId);
                         }
 
                         s4 = statusNotificationTimer.ElapsedMilliseconds;
@@ -348,9 +337,9 @@ internal partial class ProfileHandler
                         result.Success = true;
 
                         statusNotificationTimer.Stop();
-                        if(statusNotificationTimer.ElapsedMilliseconds/1000 > 3)
+                        if(statusNotificationTimer.ElapsedMilliseconds/1000 > 1)
                         {
-                            logger.LogCritical(string.Format("StatusNotification took {0}/{1}/{2}/{3}/{4}", s1/1000, s2/1000, s3/1000, s4/1000, s5/1000));
+                            logger.LogCritical(string.Format("StatusNotification took {0}/{1}/{2}/{3}/{4}", s1, s2, s3, s4, s5));
                         }
                     }
                     break;
@@ -480,18 +469,22 @@ internal partial class ProfileHandler
                     break;
                 case Actions.StartTransaction:
                     {
+                        var timer = Stopwatch.StartNew();
+                        long t0 = 0, t1 = 0, t2 = 0, t3 = 0,t4 =0, t5 = 0;
 
                         StartTransactionRequest _request = request as StartTransactionRequest;
 
                         int _transactionId = -1;
 
                         var businessService = await businessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
+                        t0 = timer.ElapsedMilliseconds;
 
                         var _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
                         if (_request.idTag != "Backend")
                         {
                             var authorization_result = await businessService.Authorize(session.ChargeBoxId, _request.idTag);
                             _idTagInfo = authorization_result.IdTagInfo;
+                            t1 = timer.ElapsedMilliseconds;
 
                             if (_idTagInfo.status == AuthorizationStatus.Accepted && authorization_result.ChargePointFee != null)
                             {
@@ -525,7 +518,7 @@ internal partial class ProfileHandler
                         string accountBalance = "0";
                         if (session.CustomerId.ToString().ToUpper() == "10C7F5BD-C89A-4E2A-8611-B617E0B41A73")
                         {
-                            using (SqlConnection conn = new SqlConnection(webConnectionString))
+                            using (SqlConnection conn = webDbConnectionFactory.Create())
                             {
                                 var parameters = new DynamicParameters();
                                 parameters.Add("@IdTag", _request.idTag, DbType.String, ParameterDirection.Input, 50);
@@ -539,9 +532,12 @@ internal partial class ProfileHandler
                         {
                             var _CustomerId = await db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).Include(x => x.Customer).
                                  Select(x => x.CustomerId).FirstOrDefaultAsync();
+                            t2 = timer.ElapsedMilliseconds;
 
                             var _existedTx = await db.TransactionRecord.Where(x => x.CustomerId == _CustomerId && x.ChargeBoxId == session.ChargeBoxId
                                && x.ConnectorId == _request.connectorId && x.StartTime == _request.timestamp).Select(C => new { C.Id }).AsNoTracking().FirstOrDefaultAsync();
+                            t3 = timer.ElapsedMilliseconds;
+
                             TransactionRecord _newTransaction = new TransactionRecord();
 
 
@@ -573,6 +569,7 @@ internal partial class ProfileHandler
                                 db.TransactionRecord.Add(_newTransaction);
 
                                 db.SaveChanges();
+                                t4 = timer.ElapsedMilliseconds;
 
                                 _transactionId = _newTransaction.Id;
                                 logger.LogInformation("***************************************************** ");
@@ -596,6 +593,13 @@ internal partial class ProfileHandler
 
                         result.Message = confirm;
                         result.Success = true;
+
+                        timer.Stop();
+                        t5 = timer.ElapsedMilliseconds;
+                        if (t5 > 1000)
+                        {
+                            logger.Log(LogLevel.Critical, "{action} {sessisonId} time {t0}/{t1}/{t2}/{t3}/{t4}/{totalTime}", action.ToString(), session.SessionID, t0, t1, t2, t3, t4, t5);
+                        }
                     }
                     break;
                 case Actions.StopTransaction:
@@ -995,9 +999,10 @@ internal partial class ProfileHandler
 
                                     if (feedto.StopTime != GlobalConfig.DefaultNullTime)
                                     {
-                                        var customerInfo = await db.Customer
-                                            .Where(x => x.Id == session.CustomerId).Select(x => new { x.InstantStopTxReport, x.ApiUrl, x.ApiKey })
-                                            .FirstOrDefaultAsync();
+                                        //var customerInfo = await db.Customer
+                                        //    .Where(x => x.Id == session.CustomerId).Select(x => new { x.InstantStopTxReport, x.ApiUrl, x.ApiKey })
+                                        //    .FirstOrDefaultAsync();
+                                        var customerInfo = await mainDbService.GetCustomer(session.CustomerId);
 
                                         decimal accountBalance = 0;
                                         decimal.TryParse(feedto.Fee.Split('+')[1], out accountBalance);
@@ -1168,7 +1173,7 @@ internal partial class ProfileHandler
                                             unitId: -1,
                                             transactionId: feedto.Id);
 
-                                        using (SqlConnection conn = new SqlConnection(webConnectionString))
+                                        using (SqlConnection conn = webDbConnectionFactory.Create())
                                         {
                                             var parameters = new DynamicParameters();
                                             parameters.Add("@IdTag", tx.StartIdTag, DbType.String, ParameterDirection.Input, 50);

+ 8 - 6
EVCB_OCPP.WSServer/Program.cs

@@ -45,14 +45,16 @@ namespace EVCB_OCPP.WSServer
 
             IHost host = Host.CreateDefaultBuilder(args)
                 //.UseEnvironment("Development")
-                .ConfigureLogging((context, builder) => { 
-                    builder.ClearProviders();
-                    NLog.LogManager.Configuration = new NLogLoggingConfiguration(context.Configuration.GetSection("NLog"));
-                })
-                .UseNLog()
+                //.ConfigureLogging((context, builder) => { 
+                //    builder.ClearProviders();
+                //    builder.AddAzureWebAppDiagnostics();
+                //    NLog.LogManager.Configuration = new NLogLoggingConfiguration(context.Configuration.GetSection("NLog"));
+                //})
+                //.UseNLog()
+                .AddLogServcie()
                 .ConfigureServices((hostContext, services) =>
                 {
-                    services.AddSingleton<MeterValueGroupSingleHandler>();
+                    //services.AddSingleton<MeterValueGroupSingleHandler>();
                     services.AddProtalServer(hostContext.Configuration);
                 })
                 .Build();

+ 61 - 41
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -40,6 +40,7 @@ using Microsoft.Data.SqlClient;
 using System.Collections.ObjectModel;
 using System.Collections.Concurrent;
 using EVCB_OCPP.WSServer.SuperSocket;
+using Microsoft.Extensions.Logging;
 
 namespace EVCB_OCPP.WSServer
 {
@@ -60,13 +61,16 @@ namespace EVCB_OCPP.WSServer
 
     public class ProtalServer : IHostedService
     {
-        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        //static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
 
         public ProtalServer(
-            IConfiguration configuration
+            ILogger<ProtalServer> logger
+            ,IConfiguration configuration
             , IDbContextFactory<MainDBContext> maindbContextFactory
             , IMainDbService mainDbService
             , IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory
+            , SqlConnectionFactory<WebDBConetext> webDbConnectionFactory
+            , SqlConnectionFactory<MainDBContext> mainDbConnectionFactory
             , IHostEnvironment environment
             , IOCPPWSServerFactory ocppWSServerFactory
             , IConnectionLogdbService connectionLogdbService
@@ -74,19 +78,20 @@ namespace EVCB_OCPP.WSServer
             ,IServiceProvider serviceProvider)
         {
             _ct = _cts.Token;
-
+            this.logger = logger;
             this.configuration = configuration;
             this.maindbContextFactory = maindbContextFactory;
             this.mainDbService = mainDbService;
+            this.webDbConnectionFactory = webDbConnectionFactory;
             //this.connectionLogdbContextFactory = connectionLogdbContextFactory;
             this.ocppWSServerFactory = ocppWSServerFactory;
             this.connectionLogdbService = connectionLogdbService;
             this.webDbService = webDbService;
             isInDocker = !string.IsNullOrEmpty(configuration["DOTNET_RUNNING_IN_CONTAINER"]);
 
-            webConnectionString = configuration.GetConnectionString("WebDBContext");
+            // = configuration.GetConnectionString("WebDBContext");
             this.profileHandler = serviceProvider.GetService<ProfileHandler>();// new ProfileHandler(configuration, serviceProvider);
-            _loadingBalanceService = new LoadingBalanceService(configuration);
+            _loadingBalanceService = new LoadingBalanceService(mainDbConnectionFactory, webDbConnectionFactory );
 
             WarmUpLog();
         }
@@ -97,17 +102,19 @@ namespace EVCB_OCPP.WSServer
         private ConcurrentDictionary<string, ClientData> clientDic = new ConcurrentDictionary<string, ClientData>();
         //private readonly Object _lockClientDic = new object();
         private readonly Object _lockConfirmPacketList = new object();
+        private readonly ILogger<ProtalServer> logger;
         private readonly IConfiguration configuration;
         //private readonly IServiceProvider serviceProvider;
         private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
         private readonly IMainDbService mainDbService;
+        private readonly SqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
 
         //private readonly IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory;
         private readonly IOCPPWSServerFactory ocppWSServerFactory;
         private readonly IConnectionLogdbService connectionLogdbService;
         private readonly WebDbService webDbService;
         private readonly ProfileHandler profileHandler;//= new ProfileHandler();
-        private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
+        //private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
         private readonly bool isInDocker;
         private List<NeedConfirmMessage> needConfirmPacketList = new List<NeedConfirmMessage>();
         private DateTime checkUpdateDt = DateTime.UtcNow;
@@ -316,7 +323,7 @@ namespace EVCB_OCPP.WSServer
 
                             if (isTargetRule)
                             {
-                                rule.SetLoggingLevels(LogLevel.Warn, LogLevel.Off);
+                                rule.SetLoggingLevels(NLog.LogLevel.Warn, NLog.LogLevel.Off);
                             }
                         }
                         break;
@@ -343,7 +350,7 @@ namespace EVCB_OCPP.WSServer
 
                             if (isTargetRule)
                             {
-                                rule.SetLoggingLevels(LogLevel.Trace, LogLevel.Off);
+                                rule.SetLoggingLevels(NLog.LogLevel.Trace, NLog.LogLevel.Off);
                             }
                         }
                         break;
@@ -386,7 +393,7 @@ namespace EVCB_OCPP.WSServer
                 List<string> toReturn = new List<string>() { "Command List Clients" };
                 Dictionary<string, ClientData> _copyClientDic = null;
                 _copyClientDic = new Dictionary<string, ClientData>(clientDic);
-                var list = _copyClientDic.Select(c => c.Value).Where(x=>x.IsPending = false).ToList();
+                var list = _copyClientDic.Select(c => c.Value).ToList();
                 int i = 1;
                 foreach (var c in list)
                 {
@@ -422,7 +429,7 @@ namespace EVCB_OCPP.WSServer
 
                     if (isTargetRule)
                     {
-                        rule.SetLoggingLevels(LogLevel.Warn, LogLevel.Off);
+                        rule.SetLoggingLevels(NLog.LogLevel.Warn, NLog.LogLevel.Off);
                     }
                 }
                 return "Command silent";
@@ -440,12 +447,26 @@ namespace EVCB_OCPP.WSServer
 
                     if (isTargetRule)
                     {
-                        rule.SetLoggingLevels(LogLevel.Trace, LogLevel.Off);
+                        rule.SetLoggingLevels(NLog.LogLevel.Trace, NLog.LogLevel.Off);
                     }
                 }
                 return "Command show";
             });
 
+            app.MapGet("/threads", () => {
+                ThreadPool.GetMaxThreads(out var maxWorkerThread,out var maxCompletionPortThreads);
+                ThreadPool.GetAvailableThreads(out var avaliableWorkerThread, out var avaliableCompletionPortThreads);
+                return $"WorkerThread:{avaliableWorkerThread}/{maxWorkerThread} CompletionPortThreads{avaliableCompletionPortThreads}/{maxCompletionPortThreads}";
+            });
+
+            app.MapPost("/threads", (int min, int max) => {
+                ThreadPool.GetMaxThreads(out var maxWorkerThread, out var maxCompletionPortThreads);
+                ThreadPool.GetAvailableThreads(out var avaliableWorkerThread, out var avaliableCompletionPortThreads); 
+                ThreadPool.SetMinThreads(min, 0);
+                ThreadPool.SetMaxThreads(max, maxCompletionPortThreads);
+                return $"WorkerThread:{avaliableWorkerThread}/{maxWorkerThread} CompletionPortThreads{avaliableCompletionPortThreads}/{maxCompletionPortThreads}";
+            });
+
             app.Urls.Add("http://*:54088");
 
             _ = app.RunAsync();
@@ -453,10 +474,7 @@ namespace EVCB_OCPP.WSServer
 
         internal void Stop()
         {
-            if (_cts != null)
-            {
-                _cts.Cancel();
-            }
+            _cts?.Cancel();
         }
 
         private async void CheckEVSEConfigure(string chargeBoxId)
@@ -502,11 +520,12 @@ namespace EVCB_OCPP.WSServer
                 //Ip = "172.17.40.13",
                 MaxRequestLength = 204800,
                 //Security = serverSecurity,
-                Certificate = Certificate,
+                //Certificate = Certificate,
                 Listeners = listeners,
                 //  LogAllSocketException = true,
                 KeepAliveTime = 10,
                 // LogBasicSessionActivity = true
+                //Security = "None"
             };
 
             //Setup with listening port
@@ -537,7 +556,7 @@ namespace EVCB_OCPP.WSServer
 
         private async void AppServer_NewSessionConnected(ClientData session)
         {
-            logger.Debug(string.Format("{0} NewSessionConnected", session.Path));
+            logger.LogDebug(string.Format("{0} NewSessionConnected", session.Path));
 
             try
             {
@@ -578,7 +597,7 @@ namespace EVCB_OCPP.WSServer
             }
             catch (Exception ex)
             {
-                logger.Error(string.Format("NewSessionConnected Ex: {0}", ex.ToString()));
+                logger.LogError(string.Format("NewSessionConnected Ex: {0}", ex.ToString()));
             }
 
 
@@ -592,7 +611,7 @@ namespace EVCB_OCPP.WSServer
 
             if (completedTask != task)
             {
-                logger.Fatal("Process timeout: {0} ", rawdata);
+                logger.LogCritical("Process timeout: {0} ", rawdata);
                 await task;
                 return;
             }
@@ -613,7 +632,7 @@ namespace EVCB_OCPP.WSServer
 
                 if (session.ResetSecurityProfile)
                 {
-                    logger.Error(string.Format("[{0}] ChargeBoxId:{1} ResetSecurityProfile", DateTime.UtcNow, session.ChargeBoxId));
+                    logger.LogError(string.Format("[{0}] ChargeBoxId:{1} ResetSecurityProfile", DateTime.UtcNow, session.ChargeBoxId));
                     RemoveClient(session);
                     return;
                 }
@@ -671,7 +690,7 @@ namespace EVCB_OCPP.WSServer
                                     }
                                     catch (Exception e)
                                     {
-                                        logger.Error($"Processing {action} exception!");
+                                        logger.LogError($"Processing {action} exception!");
                                         throw;
                                     }
                                 }
@@ -717,7 +736,7 @@ namespace EVCB_OCPP.WSServer
                                             break;
                                         default:
                                             {
-                                                logger.Error(string.Format("We don't implement messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
+                                                logger.LogError(string.Format("We don't implement messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
                                             }
                                             break;
                                     }
@@ -797,7 +816,7 @@ namespace EVCB_OCPP.WSServer
                                             break;
                                         default:
                                             {
-                                                logger.Error(string.Format("We don't implement messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
+                                                logger.LogError(string.Format("We don't implement messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
                                             }
                                             break;
                                     }
@@ -818,7 +837,7 @@ namespace EVCB_OCPP.WSServer
                             break;
                         default:
                             {
-                                logger.Error(string.Format("Can't analyze messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
+                                logger.LogError(string.Format("Can't analyze messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
                             }
                             break;
 
@@ -833,13 +852,13 @@ namespace EVCB_OCPP.WSServer
 
                 if (ex.InnerException != null)
                 {
-                    logger.Error(string.Format("{0} **Inner Exception :{1} ", session.ChargeBoxId + rawdata, ex.ToString()));
+                    logger.LogError(string.Format("{0} **Inner Exception :{1} ", session.ChargeBoxId + rawdata, ex.ToString()));
 
 
                 }
                 else
                 {
-                    logger.Error(string.Format("{0} **Exception :{1} ", session.ChargeBoxId, ex.ToString()));
+                    logger.LogError(string.Format("{0} **Exception :{1} ", session.ChargeBoxId, ex.ToString()));
                 }
             }
         }
@@ -946,7 +965,7 @@ namespace EVCB_OCPP.WSServer
                             sendTimer.Stop();
                             if(sendTimer.ElapsedMilliseconds/1000 > 1)
                             {
-                                logger.Fatal("ProcessRequestMessage Send Cost {time} sec", sendTimer.ElapsedMilliseconds / 1000);
+                                logger.LogCritical("ProcessRequestMessage Send Cost {time} sec", sendTimer.ElapsedMilliseconds / 1000);
                             }
 
                             if (action == Actions.StartTransaction)
@@ -966,7 +985,7 @@ namespace EVCB_OCPP.WSServer
                                         }
                                         catch (Exception ex)
                                         {
-                                            logger.Error(string.Format("Set Profile Exception: {0}", ex.ToString()));
+                                            logger.LogError(string.Format("Set Profile Exception: {0}", ex.ToString()));
                                         }
 
                                     }
@@ -990,7 +1009,7 @@ namespace EVCB_OCPP.WSServer
                                         }
                                         catch (Exception ex)
                                         {
-                                            logger.Error(string.Format("Set Profile Exception: {0}", ex.ToString()));
+                                            logger.LogError(string.Format("Set Profile Exception: {0}", ex.ToString()));
                                         }
                                     }
                                 }
@@ -1048,7 +1067,7 @@ namespace EVCB_OCPP.WSServer
             outter_stopwatch.Stop();
             if (outter_stopwatch.ElapsedMilliseconds > 1000)
             {
-                logger.Fatal("ProcessRequestMessage {action} too long {time} sec", action.ToString(), outter_stopwatch.ElapsedMilliseconds / 1000);
+                logger.LogCritical("ProcessRequestMessage {action} too long {time} sec", action.ToString(), outter_stopwatch.ElapsedMilliseconds / 1000);
             }
         }
 
@@ -1109,7 +1128,7 @@ namespace EVCB_OCPP.WSServer
 
                 if (confirmResult == null || !confirmResult.Success)
                 {
-                    logger.Error(string.Format("Action:{0} MessageId:{1}  ExecuteConfirm Error:{2} ",
+                    logger.LogError(string.Format("Action:{0} MessageId:{1}  ExecuteConfirm Error:{2} ",
                         analysisResult.Action, analysisResult.UUID, confirmResult.Exception.ToString()));
                 }
             }
@@ -1193,7 +1212,7 @@ namespace EVCB_OCPP.WSServer
             }
             catch (Exception ex)
             {
-                logger.Error(string.Format("Send Ex:{0}", ex.ToString()));
+                logger.LogError(string.Format("Send Ex:{0}", ex.ToString()));
             }
 
 
@@ -1209,7 +1228,7 @@ namespace EVCB_OCPP.WSServer
             try
             {
 
-                using (SqlConnection conn = new SqlConnection(webConnectionString))
+                using (SqlConnection conn = webDbConnectionFactory.Create())
                 {
                     var parameters = new DynamicParameters();
                     parameters.Add("@MachineId", client.MachineId, DbType.String, ParameterDirection.Input, 36);
@@ -1263,7 +1282,7 @@ namespace EVCB_OCPP.WSServer
             }
             catch (Exception ex)
             {
-                logger.Error("SetDefaultFee", ex.ToString());
+                logger.LogError("SetDefaultFee", ex.ToString());
             }
 
             return displayPriceText;
@@ -1343,7 +1362,7 @@ namespace EVCB_OCPP.WSServer
                 }
                 else
                 {
-                    logger.Error(string.Format("Received no record Action:{0} MessageId:{1} ", analysisResult.Action, analysisResult.UUID));
+                    logger.LogError(string.Format("Received no record Action:{0} MessageId:{1} ", analysisResult.Action, analysisResult.UUID));
                 }
             }
 
@@ -1361,7 +1380,7 @@ namespace EVCB_OCPP.WSServer
             }
 
             if (!string.IsNullOrEmpty(session.MachineId))
-                logger.Trace("RemoveClient[" + session.ChargeBoxId + "]");
+                logger.LogTrace("RemoveClient[" + session.ChargeBoxId + "]");
 
             if (session.Connected)
             {
@@ -1377,7 +1396,7 @@ namespace EVCB_OCPP.WSServer
             catch (Exception ex)
             {
                 //logger.LogWarning("Close client socket error!!");
-                logger.Warn(string.Format("Close client socket error!! {0} Msg:{1}", session.ChargeBoxId, ex.Message));
+                logger.LogWarning(string.Format("Close client socket error!! {0} Msg:{1}", session.ChargeBoxId, ex.Message));
             }
 
             if (session != null)
@@ -1397,10 +1416,10 @@ namespace EVCB_OCPP.WSServer
             {
                 if (clientDic[session.ChargeBoxId].SessionID == session.SessionID)
                 {
-                    logger.Debug(String.Format("ChargeBoxId:{0} Remove SessionId:{1} Removed SessionId:{2}", session.ChargeBoxId, session.SessionID, clientDic[session.ChargeBoxId].SessionID));
+                    logger.LogDebug(String.Format("ChargeBoxId:{0} Remove SessionId:{1} Removed SessionId:{2}", session.ChargeBoxId, session.SessionID, clientDic[session.ChargeBoxId].SessionID));
 
                     clientDic.Remove(session.ChargeBoxId, out _);
-                    logger.Trace("RemoveClient ContainsKey " + session.ChargeBoxId);
+                    logger.LogTrace("RemoveClient ContainsKey " + session.ChargeBoxId);
                 }
 
             }
@@ -1420,14 +1439,15 @@ namespace EVCB_OCPP.WSServer
 
                 if (clientData.ChargeBoxId == null)
                 {
-                    logger.Fatal(clientData.Path + "]********************session ChargeBoxId null sessionId=" + clientData.SessionID);
+                    logger.LogCritical(clientData.Path + "]********************session ChargeBoxId null sessionId=" + clientData.SessionID);
                 }
 
                 connectionLogdbService.WriteMachineLog(clientData, data, messageType, errorMsg, isSent);
             }
             catch (Exception ex)
             {
-                Console.WriteLine(ex.ToString());
+                //Console.WriteLine(ex.ToString());
+                logger.LogError(ex,ex.Message);
             }
 
 

+ 21 - 6
EVCB_OCPP.WSServer/Service/BusinessServiceFactory.cs

@@ -36,14 +36,19 @@ public class BusinessServiceFactory : IBusinessServiceFactory
 
     public BusinessServiceFactory(
         IServiceProvider serviceProvider,
-        IDbContextFactory<MainDBContext> mainDBContextFactory)
+        IMainDbService mainDbService
+        //IDbContextFactory<MainDBContext> mainDBContextFactory
+        )
     {
         this.serviceProvider = serviceProvider;
-        this.mainDBContextFactory = mainDBContextFactory;
+        this.mainDbService = mainDbService;
+        //this.mainDBContextFactory = mainDBContextFactory;
     }
 
     private readonly IServiceProvider serviceProvider;
-    private readonly IDbContextFactory<MainDBContext> mainDBContextFactory;
+    private readonly IMainDbService mainDbService;
+
+    //private readonly IDbContextFactory<MainDBContext> mainDBContextFactory;
 
     public async Task<IBusinessService> CreateBusinessService(string customerId)
     {
@@ -52,10 +57,20 @@ public class BusinessServiceFactory : IBusinessServiceFactory
         //{
         //    isCallOut = await db.Customer.Where(x => x.Id == new Guid(customerId)).Select(x => x.CallPartnerApiOnSchedule).FirstOrDefaultAsync();
         //}
-        CustomerSignMaterial _customer;
-        using (var db = this.mainDBContextFactory.CreateDbContext())
+        CustomerSignMaterial _customer = null;
+        //using (var db = this.mainDBContextFactory.CreateDbContext())
+        //{
+        //    _customer = await db.Customer.Where(x => x.Id == new Guid(customerId)).Select(x => new CustomerSignMaterial() { Id = x.Id.ToString(), APIUrl = x.ApiUrl, SaltKey = x.ApiKey, CallsThirdParty = x.CallPartnerApiOnSchedule }).FirstOrDefaultAsync();
+        //}
+        var _customerDb = await mainDbService.GetCustomer(customerId);
+        if (_customerDb is not null)
         {
-            _customer = await db.Customer.Where(x => x.Id == new Guid(customerId)).Select(x => new CustomerSignMaterial() { Id = x.Id.ToString(), APIUrl = x.ApiUrl, SaltKey = x.ApiKey, CallsThirdParty = x.CallPartnerApiOnSchedule }).FirstOrDefaultAsync();
+            _customer = new CustomerSignMaterial() { 
+                Id = _customerDb.Id.ToString(),
+                APIUrl = _customerDb.ApiUrl,
+                SaltKey = _customerDb.ApiKey,
+                CallsThirdParty = _customerDb.CallPartnerApiOnSchedule
+            };
         }
         isCallOut = _customer != null && _customer.CallsThirdParty;
         //return isCallOut ? new OuterBusinessService(customerId) : new LocalBusinessService(customerId);

+ 257 - 3
EVCB_OCPP.WSServer/Service/ConnectionLogdbService.cs

@@ -1,13 +1,17 @@
-using EVCB_OCPP.Domain;
+using Dapper;
+using EVCB_OCPP.Domain;
 using EVCB_OCPP.WSServer.Helper;
+using log4net;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
+using NLog.Fluent;
 using OCPPServer.Protocol;
 using System;
 using System.Collections.Generic;
 using System.Data;
+using System.Diagnostics;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -27,18 +31,28 @@ public class ConnectionLogdbService : IConnectionLogdbService
 
     public ConnectionLogdbService(
         IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory,
+        SqlConnectionFactory<ConnectionLogDBContext> sqlConnectionFactory,
         ILogger<ConnectionLogdbService> logger,
         IConfiguration configuration)
     {
         this.connectionLogdbContextFactory = connectionLogdbContextFactory;
+        this.sqlConnectionFactory = sqlConnectionFactory;
         this.logger = logger;
+        //connectionLogdbConnectionString = configuration.GetConnectionString("MeterValueDBContext");
+
         var opLimit = GetLimit(configuration);
         this.queueHandler = new(WriteMachineLog, opLimit);
+
+        InitInsertConnectonLogHandler();
     }
 
     private readonly IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory;
+    private readonly SqlConnectionFactory<ConnectionLogDBContext> sqlConnectionFactory;
     private readonly ILogger<ConnectionLogdbService> logger;
     private readonly QueueHandler<MachineLog> queueHandler;
+    //private readonly string connectionLogdbConnectionString;
+    private readonly Queue<string> _existTables = new();
+    private GroupSingleHandler<MachineLog> insertConnectonLogHandler;
 
     public void WarmUpLog()
     {
@@ -58,11 +72,80 @@ public class ConnectionLogdbService : IConnectionLogdbService
     public void WriteMachineLog(ClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false)
     {
         var log = new MachineLog(clientData, data, messageType, errorMsg, isSent);
-        queueHandler.Enqueue(log);
+        //queueHandler.Enqueue(log);
+        //WriteMachineLog(log);
+        insertConnectonLogHandler.HandleAsync(log);
+        //InsertWithDapper(log);
+    }
+
+    private async Task InsertWithDapper(MachineLog log)
+    {
+        var watch = Stopwatch.StartNew();
+        long t0, t1, t2, t3;
+        var workTime = DateTime.UtcNow;
+        if (!await GetTableExist(workTime))
+        {
+            t0 = watch.ElapsedMilliseconds;
+            await WriteMachineLog(log);
+            watch.Stop();
+            t1 = watch.ElapsedMilliseconds;
+            if (t1 > 500)
+            {
+                logger.LogWarning("ConnectionLog InsertWithDapper {0}/{1}", t0, t1);
+            }
+            return;
+        }
+
+        t0 = watch.ElapsedMilliseconds;
+        var tableName = GetTableName(workTime);
+        string command = $"""
+            INSERT INTO {tableName} (CreatedOn, ChargeBoxId, MessageType, Data, Msg, IsSent, EVSEEndPoint, Session)
+            VALUES (@CreatedOn, @ChargeBoxId, @MessageType, @Data, @Msg, @IsSent, @EVSEEndPoint, @Session);
+            """;
+
+        var parameters = new DynamicParameters();
+        parameters.Add("CreatedOn", workTime, DbType.DateTime);
+        parameters.Add("ChargeBoxId", log.clientData.ChargeBoxId == null ? "unknown" : log.clientData.ChargeBoxId.Replace("'", "''"), DbType.String, size:50); ;
+        parameters.Add("MessageType", log.messageType.Replace("'", "''"), DbType.String, size: 50);
+        parameters.Add("Data", log.data.Replace("'", "''"), DbType.String);
+        parameters.Add("Msg", log.errorMsg.Replace("'", "''"), DbType.String, size: 200);
+        parameters.Add("IsSent", log.isSent, DbType.Boolean);
+        parameters.Add("EVSEEndPoint", log.clientData.RemoteEndPoint == null ? "123" : log.clientData.RemoteEndPoint.ToString(), DbType.String, size: 25);
+        parameters.Add("Session", log.clientData.SessionID == null ? "123" : log.clientData.SessionID, DbType.String, size: 36);
+
+        t1 = watch.ElapsedMilliseconds;
+        using var sqlConnection = sqlConnectionFactory.Create();
+        t2 = watch.ElapsedMilliseconds;
+        await sqlConnection.ExecuteAsync(command, parameters);
+
+        watch.Stop();
+        t3 = watch.ElapsedMilliseconds;
+        if (t3 > 1000)
+        {
+            logger.LogWarning("ConnectionLog Dapper {0}/{1}/{2}/{3}", t0, t1, t2, t3);
+        }
+    }
+
+    private void InitInsertConnectonLogHandler()
+    {
+        if (insertConnectonLogHandler is not null)
+        {
+            throw new Exception($"{nameof(InitInsertConnectonLogHandler)} should only called once");
+        }
+
+        insertConnectonLogHandler = new GroupSingleHandler<MachineLog>(
+            //BulkInsertWithBulkCopy,
+            BundleInsertWithDapper,
+            //loggerFactory.CreateLogger("InsertMeterValueHandler")
+            logger,
+            workerCnt: 1
+            );
     }
 
     private async Task WriteMachineLog(MachineLog log)
     {
+        var watcher = Stopwatch.StartNew();
+
         try
         {
             if (log.clientData == null || string.IsNullOrEmpty(log.data)) return;
@@ -93,9 +176,180 @@ public class ConnectionLogdbService : IConnectionLogdbService
         }
         catch (Exception ex)
         {
-            Console.WriteLine(ex.ToString());
+            logger.LogError(ex.ToString());
+        }
+
+        watcher.Stop();
+        if (watcher.ElapsedMilliseconds > 1000)
+        {
+            logger.LogWarning("WriteMachineLog too long {0}", watcher.ElapsedMilliseconds);
+        }
+    }
+
+    private async Task BundleInsertWithDapper(IEnumerable<MachineLog> parms)
+    {
+        var watch = Stopwatch.StartNew();
+        long t0, t1, t2, t3, t4;
+        var workTime = DateTime.UtcNow;
+
+        var parmsList = parms.ToList();
+
+        if (parmsList.Count == 0)
+        {
+            return;
+        }
+        var candidate = parmsList[0];
+        if (!await GetTableExist(workTime))
+        {
+            t0 = watch.ElapsedMilliseconds;
+            await WriteMachineLog(candidate);
+            watch.Stop();
+            t1 = watch.ElapsedMilliseconds;
+            if (t1 > 500)
+            {
+                logger.LogWarning("ConnectionLog InsertWithDapper {0}/{1}", t0, t1);
+            }
+            parmsList.Remove(candidate);
         }
+
+        t0 = watch.ElapsedMilliseconds;
+
+        t1 = watch.ElapsedMilliseconds;
+        using SqlConnection sqlConnection = sqlConnectionFactory.Create();
+        using var tans = sqlConnection.BeginTransaction();
+
+        t2 = watch.ElapsedMilliseconds;
+        var tableName = GetTableName(workTime);
+
+        string command = $"""
+            INSERT INTO {tableName} (CreatedOn, ChargeBoxId, MessageType, Data, Msg, IsSent, EVSEEndPoint, Session)
+            VALUES (@CreatedOn, @ChargeBoxId, @MessageType, @Data, @Msg, @IsSent, @EVSEEndPoint, @Session);
+            """;
+
+        foreach (var log in parmsList)
+        {
+            var parameters = new DynamicParameters();
+            parameters.Add("CreatedOn", workTime, DbType.DateTime);
+            parameters.Add("ChargeBoxId", log.clientData.ChargeBoxId == null ? "unknown" : log.clientData.ChargeBoxId.Replace("'", "''"), DbType.String, size: 50); ;
+            parameters.Add("MessageType", log.messageType.Replace("'", "''"), DbType.String, size: 50);
+            parameters.Add("Data", log.data.Replace("'", "''"), DbType.String);
+            parameters.Add("Msg", log.errorMsg.Replace("'", "''"), DbType.String, size: 200);
+            parameters.Add("IsSent", log.isSent, DbType.Boolean);
+            parameters.Add("EVSEEndPoint", log.clientData.RemoteEndPoint == null ? "123" : log.clientData.RemoteEndPoint.ToString(), DbType.String, size: 25);
+            parameters.Add("Session", log.clientData.SessionID == null ? "123" : log.clientData.SessionID, DbType.String, size: 36);
+
+            sqlConnection.Execute(command, parameters, tans);
+        }
+
+        t3 = watch.ElapsedMilliseconds;
+        tans.Commit();
+
+        watch.Stop();
+        t4 = watch.ElapsedMilliseconds;
+        if (t4 > 1000)
+        {
+            logger.LogWarning("MachineLog Bundle Dapper {0}/{1}/{2}/{3}/{4}/{5}", t0, t1, t2, t3, t4, parms.Count());
+        }
+    }
+
+    private async Task BulkInsertWithBulkCopy(IEnumerable<MachineLog> parms)
+    {
+        var watcher = Stopwatch.StartNew();
+        long t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
+
+        var parmsList = parms.ToList();
+        if (parmsList.Count == 0)
+        {
+            return;
+        }
+
+        var workTime = DateTime.UtcNow;
+        if (!await GetTableExist(workTime))
+        {
+            var candidate = parmsList.First();
+            await WriteMachineLog(candidate);
+            parmsList.Remove(candidate);
+        }
+
+        t0 = watcher.ElapsedMilliseconds;
+        var table = new DataTable();
+        table.Columns.Add("CreatedOn");
+        table.Columns.Add("ChargeBoxId");
+        table.Columns.Add("MessageType");
+        table.Columns.Add("Data");
+        table.Columns.Add("Msg");
+        table.Columns.Add("IsSent");
+        table.Columns.Add("EVSEEndPoint");
+        table.Columns.Add("Session");
+
+        foreach (var param in parmsList)
+        {
+            var row = table.NewRow();
+            row["CreatedOn"] = workTime;
+            row["ChargeBoxId"] = param.clientData.ChargeBoxId == null ? "unknown" : param.clientData.ChargeBoxId.Replace("'", "''");
+            row["MessageType"] = param.messageType.Replace("'", "''");
+            row["Data"] = param.data.Replace("'", "''");
+            row["Msg"] = param.errorMsg.Replace("'", "''");
+            row["IsSent"] = param.isSent;
+            row["EVSEEndPoint"] = param.clientData.RemoteEndPoint == null ? "123" : param.clientData.RemoteEndPoint.ToString();
+            row["Session"] = param.clientData.SessionID == null ? "123" : param.clientData.SessionID;
+
+            table.Rows.Add(row);
+        }
+        t1 = watcher.ElapsedMilliseconds;
+        using SqlConnection sqlConnection = await sqlConnectionFactory.CreateAsync();
+        using SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(sqlConnection);
+        t2 = watcher.ElapsedMilliseconds;
+        sqlBulkCopy.BatchSize = parmsList.Count();
+        sqlBulkCopy.DestinationTableName = GetTableName(workTime);
+
+        sqlBulkCopy.ColumnMappings.Add("CreatedOn", "CreatedOn");
+        sqlBulkCopy.ColumnMappings.Add("ChargeBoxId", "ChargeBoxId");
+        sqlBulkCopy.ColumnMappings.Add("MessageType", "MessageType");
+        sqlBulkCopy.ColumnMappings.Add("Data", "Data");
+        sqlBulkCopy.ColumnMappings.Add("Msg", "Msg");
+        sqlBulkCopy.ColumnMappings.Add("IsSent", "IsSent");
+        sqlBulkCopy.ColumnMappings.Add("EVSEEndPoint", "EVSEEndPoint");
+        sqlBulkCopy.ColumnMappings.Add("Session", "Session");
+        t3 = watcher.ElapsedMilliseconds;
+        await sqlBulkCopy.WriteToServerAsync(table);
+
+        watcher.Stop();
+        t4 = watcher.ElapsedMilliseconds;
+
+        if (t4 > 500)
+        {
+            logger.LogWarning("ConnectionLog BulkInsertWithBulkCopy Slow {0}/{1}/{2}/{3}/{4}/{5}", t0, t1, t2, t3, t4, parms.Count());
+        }
+    }
+
+    private async ValueTask<bool> GetTableExist(DateTime tableDateTime)
+    {
+        var tableName = GetTableName(tableDateTime);
+        if (_existTables.Contains(tableName))
+        {
+            return true;
+        }
+
+        FormattableString checkTableSql = $"SELECT Count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = {tableName}";
+
+        using var db = await connectionLogdbContextFactory.CreateDbContextAsync();
+        var resultList = db.Database.SqlQuery<int>(checkTableSql)?.ToList();
+
+        if (resultList is not null && resultList.Count > 0 && resultList[0] > 0)
+        {
+            _existTables.Enqueue(tableName);
+            if (_existTables.Count > 30)
+            {
+                _existTables.TryDequeue(out _);
+            }
+            return true;
+        }
+
+        return false;
     }
+    private static string GetTableName(DateTime dateTime)
+        => $"MachineConnectionLog{dateTime:yyMMdd}";
 
     private int GetLimit(IConfiguration configuration)
     {

+ 17 - 15
EVCB_OCPP.WSServer/Service/HttpClientService.cs

@@ -57,23 +57,25 @@ namespace EVCB_OCPP.WSServer.Service
 
             IServiceCollection _services = new ServiceCollection();
             _services.AddHttpClient("Default", c =>
-            {
-                if (!string.IsNullOrEmpty(baseAddress))
                 {
-                    c.BaseAddress = new Uri(baseAddress);
-                }
-                c.Timeout = TimeSpan.FromSeconds(_timeout);
-                c.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
-            })
-            .AddTypedClient<HttpClient>()
-            .SetHandlerLifetime(TimeSpan.FromDays(1))
-            .ConfigurePrimaryHttpMessageHandler((h =>
-            {
-                return new HttpClientHandler
+                    if (!string.IsNullOrEmpty(baseAddress))
+                    {
+                        c.BaseAddress = new Uri(baseAddress);
+                    }
+                    c.Timeout = TimeSpan.FromSeconds(_timeout);
+                    c.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
+                })
+                .AddTypedClient<HttpClient>()
+                .SetHandlerLifetime(TimeSpan.FromDays(1))
+                .ConfigurePrimaryHttpMessageHandler((h =>
                 {
-                    MaxConnectionsPerServer = _maxConnectionsPerServer,
-                };
-            }));
+                    var toReturn = new HttpClientHandler
+                    {
+                        MaxConnectionsPerServer = _maxConnectionsPerServer,
+                    };
+                    toReturn.ServerCertificateCustomValidationCallback = delegate { return true; };
+                    return toReturn;
+                }));
 
             _clientFactory = _services.BuildServiceProvider()
                      .GetRequiredService<IHttpClientFactory>();

+ 23 - 16
EVCB_OCPP.WSServer/Service/LoadingBalanceService.cs

@@ -1,4 +1,6 @@
 using Dapper;
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.WSServer.Helper;
 using Microsoft.Data.SqlClient;
 using Microsoft.Extensions.Configuration;
 using System;
@@ -25,19 +27,24 @@ namespace EVCB_OCPP.WSServer.Service
     {
         //ConcurrentDictionary<int, object> _lockDic = new ConcurrentDictionary<int, object>();
         ConcurrentDictionary<int, SemaphoreSlim> _semaphoreDic = new ConcurrentDictionary<int, SemaphoreSlim>();
-        private readonly string mainConnectionString;
-        private readonly string webConnectionString;
+        private readonly SqlConnectionFactory<MainDBContext> mainDbConnectionFactory;
+        private readonly SqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
 
-        public LoadingBalanceService(IConfiguration configuration)
+        //private readonly string mainConnectionString;
+        //private readonly string webConnectionString;
+
+        public LoadingBalanceService(SqlConnectionFactory<MainDBContext> mainDbConnectionFactory, SqlConnectionFactory<WebDBConetext> webDbConnectionFactory)
         {
-            mainConnectionString = configuration.GetConnectionString("MainDBContext");
-            webConnectionString = configuration.GetConnectionString("WebDBContext");
+            this.mainDbConnectionFactory = mainDbConnectionFactory;
+            this.webDbConnectionFactory = webDbConnectionFactory;
+            //mainConnectionString = configuration.GetConnectionString("MainDBContext");
+            //webConnectionString = configuration.GetConnectionString("WebDBContext");
         }
 
         public async Task<int> GetStationIdByMachineId(string machineId)
         {
             int stationId = 0;
-            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            using (SqlConnection conn = webDbConnectionFactory.Create())
             {
                 var parameters = new DynamicParameters();
                 parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input, 36);
@@ -100,7 +107,7 @@ namespace EVCB_OCPP.WSServer.Service
 
         private void CloseLoadbalanceRecord(int stationId)
         {
-            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            using (SqlConnection conn = mainDbConnectionFactory.Create())
             {
 
                 var parameters = new DynamicParameters();
@@ -115,7 +122,7 @@ namespace EVCB_OCPP.WSServer.Service
 
         private void UpdateLoadbalanceRecord(int stationId, string machineId, decimal power, DateTime? finishedOn, bool keepgoing = false)
         {
-            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            using (SqlConnection conn = mainDbConnectionFactory.Create())
             {
                 if (finishedOn.HasValue)
                 {
@@ -161,7 +168,7 @@ namespace EVCB_OCPP.WSServer.Service
         {
             bool result = false;
 
-            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            using (SqlConnection conn = mainDbConnectionFactory.Create())
             {
                 var parameters = new DynamicParameters();
                 parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
@@ -176,7 +183,7 @@ namespace EVCB_OCPP.WSServer.Service
         {
             decimal? result = (decimal?)null;
 
-            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            using (SqlConnection conn = mainDbConnectionFactory.Create())
             {
                 var parameters = new DynamicParameters();
                 parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input, 36);
@@ -212,7 +219,7 @@ namespace EVCB_OCPP.WSServer.Service
         async public Task<LoadBalanceSetting> GetLoadBalance(int stationId)
         {
             LoadBalanceSetting setting = null;
-            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            using (SqlConnection conn = webDbConnectionFactory.Create())
             {
                 var parameters = new DynamicParameters();
                 parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input);
@@ -227,7 +234,7 @@ namespace EVCB_OCPP.WSServer.Service
         async private Task<List<string>> GetIdsbyStationId(int stationId)
         {
             List<string> machineIds = new List<string>();
-            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            using (SqlConnection conn = webDbConnectionFactory.Create())
             {
                 var parameters = new DynamicParameters();
                 parameters.Add("@StationId", stationId, DbType.Int16, ParameterDirection.Input);
@@ -275,7 +282,7 @@ namespace EVCB_OCPP.WSServer.Service
             List<string> results = new List<string>();
             List<string> machineIds = await GetIdsbyStationId(stationId);
             List<string> chargeboxids = new List<string>();
-            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            using (SqlConnection conn = mainDbConnectionFactory.Create())
             {
                 string onlineChargerSql = "Select ChargeBoxId from [dbo].[Machine] where Id in @machineIds and [Online]=1; ";
                 var onlineResult = await conn.QueryAsync<string>(onlineChargerSql, new { machineIds = machineIds.ToArray() });
@@ -307,7 +314,7 @@ namespace EVCB_OCPP.WSServer.Service
             List<string> machineIds = GetIdsbyStationId(stationId).Result;
             List<string> result = new List<string>();
             int ratedPowers = 0;
-            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            using (SqlConnection conn = mainDbConnectionFactory.Create())
             {
 
                 string offlineChargerSql = "Select ChargeBoxId from [dbo].[Machine] where Id in @machineIds and [Online]=0; ";
@@ -332,7 +339,7 @@ namespace EVCB_OCPP.WSServer.Service
         private decimal GetRatedPowerbyChargeBoxId(string chargeBoxId)
         {
             decimal ratedPower = 0;
-            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            using (SqlConnection conn = mainDbConnectionFactory.Create())
             {
                 var parameters = new DynamicParameters();
                 parameters.Add("@machineId", chargeBoxId, DbType.String, ParameterDirection.Input, 36);
@@ -345,7 +352,7 @@ namespace EVCB_OCPP.WSServer.Service
         private decimal GetRatedPowerbyId(string machineId)
         {
             decimal ratedPower = 0;
-            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            using (SqlConnection conn = mainDbConnectionFactory.Create())
             {
                 var parameters = new DynamicParameters();
                 parameters.Add("@machineId", machineId, DbType.String, ParameterDirection.Input, 36);

+ 0 - 4
EVCB_OCPP.WSServer/Service/LocalBusinessService.cs

@@ -65,12 +65,8 @@ namespace EVCB_OCPP.WSServer.Service
                         {
                             ;
                         }
-
-
                     }
-
                 }
-
             }
             catch (Exception ex)
             {

+ 197 - 45
EVCB_OCPP.WSServer/Service/MainDbService.cs

@@ -1,23 +1,15 @@
-using EVCB_OCPP.Domain;
+using Dapper;
+using EVCB_OCPP.Domain;
 using EVCB_OCPP.Domain.Models.Database;
-using EVCB_OCPP.Packet.Messages.Core;
 using EVCB_OCPP.WSServer.Helper;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
-using MongoDB.Driver.Core.Connections;
 using Newtonsoft.Json;
 using OCPPPackage.Profiles;
-using System;
-using System.Collections.Generic;
 using System.Data;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace EVCB_OCPP.WSServer.Service;
 
@@ -32,24 +24,31 @@ public interface IMainDbService
     Task AddOCMF(OCMF oCMF);
     ValueTask<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId);
     Task UpdateConnectorStatus(string Id, ConnectorStatus connectorStatus);
+    ValueTask AddConnectorStatus(string ChargeBoxId, byte ConnectorId, DateTime CreatedOn, int Status,
+        int ChargePointErrorCodeId, string ErrorInfo, string VendorId, string VendorErrorCode);
     Task AddServerMessage(ServerMessage message);
     Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy = "", DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "");
+    ValueTask AddMachineError(byte ConnectorId, DateTime CreatedOn, int Status, string ChargeBoxId, int ErrorCodeId, string ErrorInfo, int PreStatus, string VendorErrorCode, string VendorId);
+    ValueTask<Customer> GetCustomer(string id);
+    ValueTask<Customer> GetCustomer(Guid id);
 }
 
 public class MainDbService : IMainDbService
 {
     public MainDbService(
         IDbContextFactory<MainDBContext> contextFactory,
+        SqlConnectionFactory<MainDBContext> sqlConnectionFactory,
         IMemoryCache memoryCache,
-        IConfiguration configuration, 
+        IConfiguration configuration,
         ILoggerFactory loggerFactory)
     {
         this.contextFactory = contextFactory;
+        this.sqlConnectionFactory = sqlConnectionFactory;
         this.memoryCache = memoryCache;
         this.loggerFactory = loggerFactory;
         var startupLimit = GetStartupLimit(configuration);
-        this.connectionString = configuration.GetConnectionString("MainDBContext");
-        this.startupSemaphore = new (startupLimit);
+        //this.connectionString = configuration.GetConnectionString("MainDBContext");
+        this.startupSemaphore = new(startupLimit);
 
         var opLimit = GetOpLimit(configuration);
         this.opSemaphore = new SemaphoreSlim(opLimit);
@@ -60,9 +59,10 @@ public class MainDbService : IMainDbService
     }
 
     private readonly IDbContextFactory<MainDBContext> contextFactory;
+    private readonly SqlConnectionFactory<MainDBContext> sqlConnectionFactory;
     private readonly IMemoryCache memoryCache;
     private readonly ILoggerFactory loggerFactory;
-    private string connectionString;
+    //private string connectionString;
     private readonly QueueSemaphore startupSemaphore;
     private readonly SemaphoreSlim opSemaphore;
     private GroupSingleHandler<StatusNotificationParam> statusNotificationHandler;
@@ -78,7 +78,9 @@ public class MainDbService : IMainDbService
         {
             return new MachineAndCustomerInfo(string.Empty, Guid.Empty, "Unknown");
         }
-        var customerName = await db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefaultAsync();
+        //var customerName = await db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefaultAsync();
+        var customer = await GetCustomer(machine.CustomerId);
+        var customerName = customer?.Name;
         return new MachineAndCustomerInfo(machine.Id, machine.CustomerId, customerName);
     }
 
@@ -134,6 +136,35 @@ public class MainDbService : IMainDbService
         await db.SaveChangesAsync();
     }
 
+    public async ValueTask AddConnectorStatus(
+        string ChargeBoxId, byte ConnectorId, DateTime CreatedOn, int Status,
+        int ChargePointErrorCodeId, string ErrorInfo, string VendorId, string VendorErrorCode)
+    {
+        using var db = contextFactory.CreateDbContext();
+        var _currentStatus = new Domain.Models.Database.ConnectorStatus()
+        {
+            ChargeBoxId = ChargeBoxId,
+            ConnectorId = ConnectorId,
+            CreatedOn = CreatedOn,
+            Status = Status,
+            ChargePointErrorCodeId = ChargePointErrorCodeId,
+            ErrorInfo = ErrorInfo,
+            VendorId = VendorId,
+            VendorErrorCode = VendorErrorCode,
+            Id = Guid.NewGuid().ToString()
+        };
+        db.ConnectorStatus.Add(_currentStatus);
+
+        db.SaveChanges();
+
+        Task.Run(() => { 
+        lock (memoryCache)
+        {
+            memoryCache.Set($"{ChargeBoxId}{ConnectorId}", _currentStatus, TimeSpan.FromHours(12));
+        }
+        });
+    }
+
     public async ValueTask<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId)
     {
         var key = $"{ChargeBoxId}{ConnectorId}";
@@ -144,44 +175,104 @@ public class MainDbService : IMainDbService
         using var db = contextFactory.CreateDbContext();
         var statusFromDb = await db.ConnectorStatus.Where(x => x.ChargeBoxId == ChargeBoxId
                             && x.ConnectorId == ConnectorId).AsNoTracking().FirstOrDefaultAsync();
-        memoryCache.Set(key, statusFromDb);
+
+        Task.Run(() => {
+        lock (memoryCache)
+        {
+            memoryCache.Set(key, statusFromDb, TimeSpan.FromHours(12));
+        }
+        });
         return statusFromDb;
     }
 
     public async Task UpdateConnectorStatus(string Id, ConnectorStatus Status)
     {
-        //using var db = await contextFactory.CreateDbContextAsync();
+        //await statusNotificationHandler.HandleAsync(new StatusNotificationParam(Id, Status));
+        //await UpdateConnectorStatusEF(Id, Status);
+        await UpdateConnectorStatusDapper(Id, Status);
 
-        //ConnectorStatus status = new() { Id = Id };
+        var key = $"{Status.ChargeBoxId}{Status.ConnectorId}";
+        Task.Run(() => {
+        lock (memoryCache)
+        {
+            memoryCache.Set(key, Status, TimeSpan.FromHours(12));
+        }
+        });
+        return;
+    }
+
+    private async Task UpdateConnectorStatusEF(string Id, ConnectorStatus Status)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
 
-        //db.ChangeTracker.AutoDetectChangesEnabled = false;
-        //db.ConnectorStatus.Attach(status);
+        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;
 
+        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));
-        var key = $"{Status.ChargeBoxId}{Status.ConnectorId}";
-        if (memoryCache.TryGetValue<ConnectorStatus>(key, out _))
+        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();
+    }
+
+    private async Task UpdateConnectorStatusDapper(string Id, ConnectorStatus Status)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@Id", Id, DbType.String, ParameterDirection.Input, 36);
+        parameters.Add("@CreatedOn", Status.CreatedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@Status", Status.Status, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@ChargePointErrorCodeId", Status.ChargePointErrorCodeId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@ErrorInfo", Status.ErrorInfo, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@VendorId", Status.VendorId, DbType.String, ParameterDirection.Input, 255);
+        parameters.Add("@VendorErrorCode", Status.VendorErrorCode, DbType.String, ParameterDirection.Input, 100);
+
+        using var conn = sqlConnectionFactory.Create();
+        await conn.ExecuteAsync("""
+            update ConnectorStatus
+            set
+            CreatedOn = @CreatedOn,
+            Status = @Status,
+            ChargePointErrorCodeId = @ChargePointErrorCodeId,
+            ErrorInfo = @ErrorInfo,
+            VendorId = @VendorId,
+            VendorErrorCode = @VendorErrorCode
+            where Id = @Id
+            """, parameters );
+    }
+
+    public async ValueTask AddMachineError(byte ConnectorId, DateTime CreatedOn, int Status, string ChargeBoxId,
+    int ErrorCodeId, string ErrorInfo, int PreStatus, string VendorErrorCode, string VendorId)
+    {
+        using var db = contextFactory.CreateDbContext();
+        db.MachineError.Add(new MachineError()
         {
-            memoryCache.Remove(key);
-        }
-        memoryCache.Set(key, Status);
-        return;
+            ConnectorId = ConnectorId,
+            CreatedOn = CreatedOn,
+            Status = Status,
+            ChargeBoxId = ChargeBoxId,
+            ErrorCodeId = ErrorCodeId,
+            ErrorInfo = ErrorInfo,
+            PreStatus = PreStatus,
+            VendorErrorCode = VendorErrorCode,
+            VendorId = VendorId
+        });
+
+        db.SaveChanges();
+
     }
 
     public Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy, DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "")
@@ -226,6 +317,35 @@ public class MainDbService : IMainDbService
         return addServerMessageHandler.HandleAsync(message);
     }
 
+    public ValueTask<Customer> GetCustomer(string id)
+        => GetCustomer(new Guid(id));
+    public async ValueTask<Customer> GetCustomer(Guid id)
+    {
+        var key = $"Customer{id}";
+        if (memoryCache.TryGetValue<Customer>(key, out var customer))
+        {
+            return customer;
+        }
+
+        Customer toReturn = null;
+        using (var db = contextFactory.CreateDbContext())
+        {
+            toReturn = await db.Customer.FirstOrDefaultAsync(x => x.Id == id);
+        }
+
+        if (toReturn is not null)
+        {
+            Task.Run(() => {
+            lock (memoryCache)
+            {
+                memoryCache.Set(key, toReturn, TimeSpan.FromSeconds(15));
+            }
+            });
+        }
+
+        return toReturn;
+    }
+
     private void InitUpdateMachineBasicInfoHandler()
     {
         if (updateMachineBasicInfoHandler is not null)
@@ -272,9 +392,9 @@ public class MainDbService : IMainDbService
         }
 
         statusNotificationHandler = new GroupSingleHandler<StatusNotificationParam>(
-            handleFunc: BundleUpdateConnectorStatus,
+            handleFunc: BundleUpdateConnectorStatusDapper,
             logger: loggerFactory.CreateLogger("StatusNotificationHandler"),
-            workerCnt: 10);
+            workerCnt: 1);
     }
 
     private async Task BundleUpdateConnectorStatus(IEnumerable<StatusNotificationParam> statusNotifications)
@@ -315,6 +435,39 @@ public class MainDbService : IMainDbService
         db.ChangeTracker.Clear();
     }
 
+
+
+    private Task BundleUpdateConnectorStatusDapper(IEnumerable<StatusNotificationParam> statusNotifications)
+    {
+        using var conn = sqlConnectionFactory.Create();
+
+        foreach (var status in statusNotifications)
+        {
+
+            var parameters = new DynamicParameters();
+            parameters.Add("@Id", status.Id, DbType.String, ParameterDirection.Input, 36);
+            parameters.Add("@CreatedOn", status.Status.CreatedOn, DbType.DateTime, ParameterDirection.Input);
+            parameters.Add("@Status", status.Status.Status, DbType.Int32, ParameterDirection.Input);
+            parameters.Add("@ChargePointErrorCodeId", status.Status.ChargePointErrorCodeId, DbType.Int32, ParameterDirection.Input);
+            parameters.Add("@ErrorInfo", status.Status.ErrorInfo, DbType.String, ParameterDirection.Input, 50);
+            parameters.Add("@VendorId", status.Status.VendorId, DbType.String, ParameterDirection.Input, 255);
+            parameters.Add("@VendorErrorCode", status.Status.VendorErrorCode, DbType.String, ParameterDirection.Input, 100);
+
+            conn.Execute("""
+                update ConnectorStatus
+                set
+                CreatedOn = @CreatedOn,
+                Status = @Status,
+                ChargePointErrorCodeId = @ChargePointErrorCodeId,
+                ErrorInfo = @ErrorInfo,
+                VendorId = @VendorId,
+                VendorErrorCode = @VendorErrorCode
+                where Id = @Id
+                """, parameters);
+        }
+        return Task.CompletedTask;
+    }
+
     private void InitAddServerMessageHandler()
     {
         if (addServerMessageHandler is not null)
@@ -323,7 +476,7 @@ public class MainDbService : IMainDbService
         }
 
         addServerMessageHandler = new GroupSingleHandler<ServerMessage>(
-            handleFunc: BulkInsertServerMessage,
+            handleFunc: BundleAddServerMessage,
             logger: loggerFactory.CreateLogger("AddServerMessageHandler"));
     }
 
@@ -371,8 +524,7 @@ public class MainDbService : IMainDbService
             table.Rows.Add(row);
         }
 
-        using SqlConnection sqlConnection = new SqlConnection(connectionString);
-        sqlConnection.Open();
+        using SqlConnection sqlConnection = sqlConnectionFactory.Create();
         using SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(sqlConnection);
 
         sqlBulkCopy.BatchSize = messages.Count();

+ 26 - 28
EVCB_OCPP.WSServer/Service/MeterValueDbService.cs

@@ -21,16 +21,18 @@ namespace EVCB_OCPP.WSServer.Service;
 public class MeterValueDbService
 {
     private readonly IDbContextFactory<MeterValueDBContext> meterValueDbContextFactory;
+    private readonly SqlConnectionFactory<MeterValueDBContext> sqlConnectionFactory;
     private readonly ILoggerFactory loggerFactory;
     private readonly MeterValueGroupSingleHandler meterValueGroupSingleHandler;
     private readonly QueueSemaphore insertSemaphore;
-    private readonly string meterValueConnectionString;
+    //private readonly string meterValueConnectionString;
     private readonly ILogger logger;
+    private readonly Queue<string> _existTables = new();
     private GroupSingleHandler<InsertMeterValueParam> insertMeterValueHandler;
-    private Queue<string> _existTables = new();
 
     public MeterValueDbService(
-        IDbContextFactory<MeterValueDBContext> meterValueDbContextFactory, 
+        IDbContextFactory<MeterValueDBContext> meterValueDbContextFactory,
+        SqlConnectionFactory<MeterValueDBContext> sqlConnectionFactory,
         ILogger<MeterValueDbService> logger,
         ILoggerFactory loggerFactory,
         IConfiguration configuration
@@ -38,9 +40,10 @@ public class MeterValueDbService
         )
     {
         this.meterValueDbContextFactory = meterValueDbContextFactory;
+        this.sqlConnectionFactory = sqlConnectionFactory;
         this.loggerFactory = loggerFactory;
         //this.meterValueGroupSingleHandler = meterValueGroupSingleHandler;
-        this.meterValueConnectionString = configuration.GetConnectionString("MeterValueDBContext");
+        //this.meterValueConnectionString = configuration.GetConnectionString("MeterValueDBContext");
         this.logger = logger;
 
         InitInsertMeterValueHandler();
@@ -77,8 +80,8 @@ public class MeterValueDbService
 
         //await db.Database.ExecuteSqlRawAsync(sp, parameter.ToArray());
 
-        return insertMeterValueHandler.HandleAsync(param);
-        //return InsertWithDapper(param);
+        //return insertMeterValueHandler.HandleAsync(param);
+        return InsertWithDapper(param);
         //return meterValueGroupSingleHandler.HandleAsync(param);
     }
 
@@ -90,10 +93,11 @@ public class MeterValueDbService
         }
 
         insertMeterValueHandler = new GroupSingleHandler<InsertMeterValueParam>(
+            //BulkInsertWithBulkCopy,
             BundleInsertWithDapper,
             //loggerFactory.CreateLogger("InsertMeterValueHandler")
             logger,
-            workerCnt:20
+            workerCnt:1
             );
     }
 
@@ -143,7 +147,7 @@ public class MeterValueDbService
         parameters.Add("TransactionId", param.transactionId, DbType.Int32);
 
         t1 = watch.ElapsedMilliseconds;
-        using var sqlConnection = new SqlConnection(meterValueConnectionString);
+        using var sqlConnection = sqlConnectionFactory.Create();
         t2 = watch.ElapsedMilliseconds;
         await sqlConnection.ExecuteAsync(command, parameters);
 
@@ -158,10 +162,9 @@ public class MeterValueDbService
     private async Task BundleInsertWithDapper(IEnumerable<InsertMeterValueParam> parms)
     {
         var watch = Stopwatch.StartNew();
-        long t0, t1, t2, t3;
+        long t0, t1, t2, t3, t4;
 
         var parmsList = parms.ToList();
-        t0 = watch.ElapsedMilliseconds;
         foreach (var param in parms)
         {
             if (!await GetTableExist(param.createdOn))
@@ -169,23 +172,18 @@ public class MeterValueDbService
                 await InsertWithStoredProcedure(param);
                 parmsList.Remove(param);
             }
-            t1 = watch.ElapsedMilliseconds;
-            watch.Stop();
-            if (t1 > 500)
-            {
-                logger.LogWarning("MeterValue InsertWithStoredProcedure {0}/{1}", t0, t1);
-            }
         }
 
-        t1 = watch.ElapsedMilliseconds;
+        t0 = watch.ElapsedMilliseconds;
         //logger.LogInformation("MeterValue bundle insert cnt {0}", parmsList.Count);
         var gruopParams = parmsList.GroupBy(x => GetTableName(x.createdOn));
 
-        t2 = watch.ElapsedMilliseconds;
-        using SqlConnection sqlConnection = new SqlConnection(meterValueConnectionString);
-        sqlConnection.Open();
+        t1 = watch.ElapsedMilliseconds;
+        using SqlConnection sqlConnection = sqlConnectionFactory.Create();
         using var tans = sqlConnection.BeginTransaction();
 
+        t2 = watch.ElapsedMilliseconds;
+
         foreach (var group in gruopParams)
         {
 
@@ -212,17 +210,18 @@ public class MeterValueDbService
             }
         }
 
+        t3 = watch.ElapsedMilliseconds;
         tans.Commit();
 
         watch.Stop();
-        t3 = watch.ElapsedMilliseconds;
-        if (t3 > 300)
+        t4 = watch.ElapsedMilliseconds;
+        if (t4 > 500)
         {
-            logger.LogWarning("MeterValue Dapper {0}/{1}/{2}/{3}", t0, t1, t2, t3);
+            logger.LogWarning("MeterValue Dapper {0}/{1}/{2}/{3}/{4}/{5}", t0, t1, t2, t3, t4, parms.Count());
         }
     }
 
-    private async Task BulkInsertWithCache(IEnumerable<InsertMeterValueParam> parms)
+    private async Task BulkInsertWithBulkCopy(IEnumerable<InsertMeterValueParam> parms)
     {
         var watcher = Stopwatch.StartNew();
         long t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
@@ -274,8 +273,7 @@ public class MeterValueDbService
                 table.Rows.Add(row);
             }
             t1 = watcher.ElapsedMilliseconds;
-            using SqlConnection sqlConnection = new SqlConnection(meterValueConnectionString);
-            sqlConnection.Open();
+            using SqlConnection sqlConnection = sqlConnectionFactory.Create();
             using SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(sqlConnection);
             t2 = watcher.ElapsedMilliseconds;
             sqlBulkCopy.BatchSize = group.Count();
@@ -293,14 +291,14 @@ public class MeterValueDbService
             sqlBulkCopy.ColumnMappings.Add("UnitId", "UnitId");
             sqlBulkCopy.ColumnMappings.Add("TransactionId", "TransactionId");
             t3 = watcher.ElapsedMilliseconds;
-            sqlBulkCopy.WriteToServer(table);
+            await sqlBulkCopy.WriteToServerAsync(table);
         }
         watcher.Stop();
         t4 = watcher.ElapsedMilliseconds;
 
         if (t4 > 500)
         {
-            logger.LogWarning("BulkInsertWithCache Slow {0}/{1}/{2}/{3}/{4}",t0,t1,t2,t3,t4);
+            logger.LogWarning("MeterValue BulkInsertWithBulkCopy Slow {0}/{1}/{2}/{3}/{4}/{5}", t0,t1,t2,t3,t4, parms.Count());
         }
     }
 

+ 8 - 5
EVCB_OCPP.WSServer/Service/MeterValueInsertHandler.cs

@@ -25,17 +25,21 @@ public class MeterValueInsertHandler : IHandler<InsertMeterValueParam>
     private static Queue<string> _existTables = new();
     private readonly IDbContextFactory<MeterValueDBContext> meterValueDbContextFactory;
     private readonly ILogger<MeterValueInsertHandler> logger;
-    private readonly string meterValueConnectionString;
+    private readonly SqlConnectionFactory<MeterValueDBContext> sqlConnectionFactory;
+
+    //private readonly string meterValueConnectionString;
 
     public MeterValueInsertHandler(
         IDbContextFactory<MeterValueDBContext> meterValueDbContextFactory,
         ILogger<MeterValueInsertHandler> logger,
-        IConfiguration configuration
+        SqlConnectionFactory<MeterValueDBContext> sqlConnectionFactory
+        //IConfiguration configuration
         )
     {
         this.meterValueDbContextFactory = meterValueDbContextFactory;
         this.logger = logger;
-        this.meterValueConnectionString = configuration.GetConnectionString("MeterValueDBContext");
+        this.sqlConnectionFactory = sqlConnectionFactory;
+        //this.meterValueConnectionString = configuration.GetConnectionString("MeterValueDBContext");
     }
 
     public void Handle(IEnumerable<InsertMeterValueParam> parms)
@@ -67,8 +71,7 @@ public class MeterValueInsertHandler : IHandler<InsertMeterValueParam>
         //t2 = watch.ElapsedMilliseconds;
         foreach (var group in gruopParams)
         {
-            using SqlConnection sqlConnection = new SqlConnection(meterValueConnectionString);
-            sqlConnection.Open();
+            using SqlConnection sqlConnection = sqlConnectionFactory.Create();
             using var tans = sqlConnection.BeginTransaction();
 
             var tableName = group.Key;

+ 14 - 3
EVCB_OCPP.WSServer/Service/OuterBusinessService.cs

@@ -54,6 +54,7 @@ namespace EVCB_OCPP.WSServer.Service
 
         private string _CustomerId = string.Empty;
         private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
+        private readonly IMainDbService mainDbService;
 
         public string CustomerId
         {
@@ -75,9 +76,11 @@ namespace EVCB_OCPP.WSServer.Service
             }
         }
 
-        public OuterBusinessService(IDbContextFactory<MainDBContext> maindbContextFactory)
+        public OuterBusinessService(IDbContextFactory<MainDBContext> maindbContextFactory,
+            IMainDbService mainDbService)
         {
             this.maindbContextFactory = maindbContextFactory;
+            this.mainDbService = mainDbService;
         }
 
 
@@ -241,9 +244,17 @@ namespace EVCB_OCPP.WSServer.Service
 
 
             //using (var db = new MainDBContext())
-            using (var db = maindbContextFactory.CreateDbContext())
+            //using (var db = maindbContextFactory.CreateDbContext())
+            //{
+            //    _customer = await db.Customer.Where(x => x.Id == Id).Select(x => new CustomerSignMaterial() { Id = x.Id.ToString(), APIUrl = x.ApiUrl, SaltKey = x.ApiKey, CallsThirdParty = x.CallPartnerApiOnSchedule }).FirstOrDefaultAsync();
+            //}
+            var _customerDb = await mainDbService.GetCustomer(Id);
+            if (_customerDb is not null)
             {
-                _customer = await db.Customer.Where(x => x.Id == Id).Select(x => new CustomerSignMaterial() { Id = x.Id.ToString(), APIUrl = x.ApiUrl, SaltKey = x.ApiKey, CallsThirdParty = x.CallPartnerApiOnSchedule }).FirstOrDefaultAsync();
+                _customer.Id = _customerDb.Id.ToString();
+                _customer.APIUrl = _customerDb.ApiUrl;
+                _customer.SaltKey = _customerDb.ApiKey;
+                _customer.CallsThirdParty = _customerDb.CallPartnerApiOnSchedule;
             }
             return _customer;
         }

+ 9 - 4
EVCB_OCPP.WSServer/Service/WebDbService.cs

@@ -1,4 +1,5 @@
 using Dapper;
+using EVCB_OCPP.WSServer.Helper;
 using Microsoft.Data.SqlClient;
 using Microsoft.Extensions.Configuration;
 using System;
@@ -11,16 +12,20 @@ namespace EVCB_OCPP.WSServer.Service;
 
 public class WebDbService
 {
-    public WebDbService(IConfiguration configuration)
+    private readonly SqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
+
+    public WebDbService(SqlConnectionFactory<WebDBConetext> webDbConnectionFactory)
     {
-        this.webConnectionString = configuration.GetConnectionString("WebDBContext");
+        this.webDbConnectionFactory = webDbConnectionFactory;
+        //this.webConnectionString = configuration.GetConnectionString("WebDBContext");
     }
 
-    private readonly string webConnectionString;
+    //private readonly string webConnectionString;
 
     public async Task<List<string>> GetDenyModelNames()
     {
-        using SqlConnection conn = new SqlConnection(webConnectionString) ;
+        return new List<string>() { "" };
+        using SqlConnection conn = webDbConnectionFactory.Create();
         string strSql = """
                 SELECT [Value] 
                 FROM [StandardOCPP_Web].[dbo].[KernelConfig]

+ 9 - 8
EVCB_OCPP.WSServer/appsettings.json

@@ -1,7 +1,8 @@
 {
   "WSPort": 80,
-  "WSSPort": 443,
+  "WSSPort": "",
   "LocalAuthAPI": "",
+  "LogProvider": "NLog",
   "OCPP20_WSUrl": "ws://ocpp.phihong.com.tw:5004",
   "OCPP20_WSSUrl": "ws://ocpp.phihong.com.tw:5004",
   "MaintainMode": 0,
@@ -36,12 +37,12 @@
       "async": true,
       "f": {
         "type": "File",
-        "fileName": "${basedir}/logs/${shortdate}.log",
+        "fileName": "${basedir}/logs/server/${shortdate}.log",
         "layout": "${longdate} ${uppercase:${level}} ${message}"
       },
       "ws": {
         "type": "File",
-        "fileName": "${basedir}/logs/WS_${shortdate}.log",
+        "fileName": "${basedir}/logs/server/WS_${shortdate}.log",
         "layout": "${longdate} ${uppercase:${level}} ${message}"
       },
       "Console": {
@@ -50,7 +51,7 @@
       },
       "auth": {
         "type": "File",
-        "fileName": "${basedir}/logs/Auth_${shortdate}.log",
+        "fileName": "${basedir}/logs/server/Auth_${shortdate}.log",
         "layout": "${longdate} ${callsite} ${uppercase:${level}} ${message}"
       }
     },
@@ -88,9 +89,9 @@
     ]
   },
   "ConnectionStrings": {
-    "ConnectionLogDBContext": "data source=172.1.0.131;initial catalog=Docker_ConnectionLog;;persist security info=True;user id=sa;password=B58Py42j/4cj84;MultipleActiveResultSets=False;App=EntityFramework;TrustServerCertificate=True;",
-    "MainDBContext": "data source=172.1.0.131;initial catalog=Docker_Main;;persist security info=True;user id=sa;password=B58Py42j/4cj84;MultipleActiveResultSets=False;App=EntityFramework;TrustServerCertificate=True;",
-    "MeterValueDBContext": "data source=172.1.0.131;initial catalog=Docker_MeterValue;;persist security info=True;user id=sa;password=B58Py42j/4cj84;MultipleActiveResultSets=False;App=EntityFramework;TrustServerCertificate=True;",
-    "WebDBContext": "data source=172.1.0.131;initial catalog=Docker_Web;;persist security info=True;user id=sa;password=B58Py42j/4cj84;MultipleActiveResultSets=False;App=EntityFramework;TrustServerCertificate=True;"
+    "ConnectionLogDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_ConnectionLog;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=200;Connection Lifetime=0;Pooling=true;",
+    "MainDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_Main;;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=1024;Connection Lifetime=0;Pooling=true;Min Pool Size=150;",
+    "MeterValueDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_MeterValue;;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=200;Connection Lifetime=0;Pooling=true;",
+    "WebDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_Web;;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=200;Connection Lifetime=0;Pooling=true;"
   }
 }

+ 5 - 2
SocketBase/AppServerBase.cs

@@ -816,6 +816,7 @@ namespace SuperSocket.SocketBase
                 SslProtocols configProtocol;
                 if (!config.Security.TryParseEnum<SslProtocols>(true, out configProtocol))
                 {
+                    Console.WriteLine("Failed to parse '{0}' to SslProtocol!", config.Security);
                     Logger.LogError("Failed to parse '{0}' to SslProtocol!", config.Security);
 
                     return false;
@@ -838,6 +839,7 @@ namespace SuperSocket.SocketBase
                 }
                 else if(BasicSecurity != SslProtocols.None)
                 {
+                    Console.WriteLine("Certificate is required in this security mode!");
                     Logger.LogError("Certificate is required in this security mode!");
 
                     return false;
@@ -846,6 +848,8 @@ namespace SuperSocket.SocketBase
             }
             catch (Exception e)
             {
+                Console.WriteLine("Failed to initialize certificate!");
+                Console.WriteLine(e.Message);
                 Logger.LogError(e , "Failed to initialize certificate!");
 
                 return false;
@@ -861,14 +865,13 @@ namespace SuperSocket.SocketBase
         /// <returns></returns>
         protected virtual X509Certificate GetCertificate(ICertificateConfig certificate)
         {
-            Logger.LogDebug($"{certificate.StoreName} {certificate.FilePath} {certificate.Password}");
-
             if (certificate == null)
             {
                 if (BasicSecurity != SslProtocols.None)
                     Logger.LogError("There is no certificate configured!");
                 return null;
             }
+            Logger.LogDebug($"{certificate.StoreName} {certificate.FilePath} {certificate.Password}");
 
             if (string.IsNullOrEmpty(certificate.FilePath) && string.IsNullOrEmpty(certificate.Thumbprint))
             {

+ 5 - 0
build.bat

@@ -0,0 +1,5 @@
+for /f %%i in ('git rev-parse --short HEAD') do set ssha=%%i
+docker build ./ -t 172.1.2.214:5000/server:test --label "git-commit=%ssha%"
+docker push 172.1.2.214:5000/server:test
+docker tag 172.1.2.214:5000/server:test evdevcontainerregistry.azurecr.io/server:test
+docker push evdevcontainerregistry.azurecr.io/server:test