Robert 1 年之前
父節點
當前提交
79c546cdbe

+ 3 - 0
EVCB_OCPP.WSServer/HostedProtalServer.cs

@@ -1,5 +1,6 @@
 using EVCB_OCPP.WSServer.Jobs;
 using Microsoft.Extensions.DependencyInjection;
+using OCPPServer.Protocol;
 using Quartz;
 using System;
 using System.Collections.Generic;
@@ -13,6 +14,8 @@ namespace EVCB_OCPP.WSServer
     {
         public static void AddProtalServer(this IServiceCollection services)
         {
+            services.AddTransient<OCPPWSServer>();
+            services.AddTransient<IOCPPWSServerFactory, OCPPWSServerFactory>();
             services.AddSingleton<ProtalServer>();
             services.AddHostedService<ProtalServer>(p => p.GetRequiredService<ProtalServer>());
             services.AddProtalServerJob();

+ 42 - 87
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -72,30 +72,29 @@ public class ID_ReaderStatus
 
 internal partial class ProfileHandler
 {
-    static private ILogger logger;
+    private readonly ILogger logger;
     private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings[].ConnectionString;
     private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
     private readonly IDbContextFactory<MeterValueDBContext> metervaluedbContextFactory;
-    private readonly IServiceProvider serviceProvider;
-    private readonly MainDbService mainDbService;
-    private readonly string mainDbConnectionString;
+    private readonly IBusinessServiceFactory businessServiceFactory;
+    private readonly IMainDbService mainDbService;
     private OuterHttpClient httpClient = new OuterHttpClient();
 
     public ProfileHandler(
         IConfiguration configuration,
         IDbContextFactory<MainDBContext> maindbContextFactory, 
         IDbContextFactory<MeterValueDBContext> metervaluedbContextFactory,
-        IServiceProvider serviceProvider,
-        MainDbService mainDbService)
+        IBusinessServiceFactory businessServiceFactory,
+        IMainDbService mainDbService,
+        ILogger<ProfileHandler> logger)
     {
         webConnectionString = configuration.GetConnectionString("WebDBContext");
-        logger = serviceProvider.GetService<ILogger<ProfileHandler>>();
 
+        this.logger = logger;
         this.maindbContextFactory = maindbContextFactory;
-        this.serviceProvider = serviceProvider;
         this.mainDbService = mainDbService;
         this.metervaluedbContextFactory = metervaluedbContextFactory;
-        this.mainDbConnectionString = configuration.GetConnectionString("MainDBContext");
+        this.businessServiceFactory = businessServiceFactory;
     }
 
     async internal Task<MessageResult> ExecuteCoreRequest(Actions action, ClientData session, IRequest request)
@@ -181,18 +180,12 @@ internal partial class ProfileHandler
 
                             logger.LogDebug("{0}\r\n{1}\r\n{2}", jo["txId"].Value<int>(), jo["dataString"].Value<string>(), jo["publicKey"].Value<string>());
 
-                            using (var db = maindbContextFactory.CreateDbContext())
+                            await mainDbService.AddOCMF(new OCMF()
                             {
-                                db.OCMF.Add(new OCMF()
-                                {
-                                    TransactionId = jo["txId"].Value<int>(),
-                                    DataString = jo["dataString"].Value<string>(),
-                                    PublicKey = jo["publicKey"].Value<string>()
-                                });
-
-                                db.SaveChanges();
-                            }
-
+                                TransactionId = jo["txId"].Value<int>(),
+                                DataString = jo["dataString"].Value<string>(),
+                                PublicKey = jo["publicKey"].Value<string>()
+                            });
 
                             confirm.status = DataTransferStatus.Accepted;
                             confirm.data = JsonConvert.SerializeObject(new { txId = jo["txId"].Value<int>(), msgId = _request.messageId });
@@ -220,18 +213,13 @@ internal partial class ProfileHandler
 
                         await mainDbService.UpdateMachineBasicInfo(session.ChargeBoxId, _machine);
 
-                        using (var db = maindbContextFactory.CreateDbContext())
-                        {
-                            var configVaule = await db.MachineConfigurations
-                                .Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.HeartbeatInterval)
-                                .Select(x => x.ConfigureSetting)
-                                .FirstOrDefaultAsync();
+                        var configValue = await mainDbService.GetMachineHeartbeatInterval(session.ChargeBoxId);
 
-                            if (configVaule != null)
-                            {
-                                int.TryParse(configVaule, out heartbeat_interval);
-                            }
+                        if (configValue != null)
+                        {
+                            int.TryParse(configValue, out heartbeat_interval);
                         }
+
                         var confirm = new BootNotificationConfirmation() { currentTime = DateTime.UtcNow, interval = session.IsPending ? heartbeat_interval : 5, status = session.IsPending ? Packet.Messages.SubTypes.RegistrationStatus.Accepted : RegistrationStatus.Pending };
 
                         session.IsPending = !session.IsPending;
@@ -248,64 +236,26 @@ internal partial class ProfileHandler
                         int preStatus = 0;
                         ConnectorStatus _oldStatus;
 
-                        using (var db = maindbContextFactory.CreateDbContext())
-                        {
-                            var con = db.Database.GetDbConnection();
-                            s1 = statusNotificationTimer.ElapsedMilliseconds;
-
-                            var sqlCommand = """
-                                select * from ConnectorStatus
-                                where ChargeBoxId = @ChargeBoxId and ConnectorId = @ConnectorId
-                                """;
-                            var param = new DynamicParameters();
-                            param.Add("@ChargeBoxId", session.ChargeBoxId, DbType.String, ParameterDirection.Input, 50);
-                            param.Add("@ConnectorId", _request.connectorId, DbType.Int16, ParameterDirection.Input);
-
-                            _oldStatus = con.QueryFirst<ConnectorStatus>(sqlCommand, param);
+                        s1 = statusNotificationTimer.ElapsedMilliseconds;
 
-                            s2 = statusNotificationTimer.ElapsedMilliseconds;
-                        }
-
-                        //using (var db = maindbContextFactory.CreateDbContext())
-                        //{
-                        //    s1 = statusNotificationTimer.ElapsedMilliseconds;
-
-                        //    _oldStatus = db.ConnectorStatus.Where(x => x.ChargeBoxId == session.ChargeBoxId
-                        //    && x.ConnectorId == _request.connectorId).AsNoTracking().FirstOrDefault();
+                        _oldStatus = await mainDbService.GetConnectorStatus(session.ChargeBoxId, _request.connectorId);
 
-                        //    s2 = statusNotificationTimer.ElapsedMilliseconds;
-                        //}
+                        s2 = statusNotificationTimer.ElapsedMilliseconds;
 
                         s4 = statusNotificationTimer.ElapsedMilliseconds;
 
                         if (_oldStatus != null && (_request.status != (ChargePointStatus)_oldStatus.Status || _request.status == ChargePointStatus.Faulted))
                         {
-                            using (var db = maindbContextFactory.CreateDbContext())
-                            {
-                                preStatus = _oldStatus.Status;
-
-                                db.ChangeTracker.AutoDetectChangesEnabled = false;
-                                db.ConnectorStatus.Attach(_oldStatus);
-
-
-                                _oldStatus.CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow;
-                                _oldStatus.Status = (int)_request.status;
-                                _oldStatus.ChargePointErrorCodeId = (int)_request.errorCode;
-                                _oldStatus.ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info;
-                                _oldStatus.VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId;
-                                _oldStatus.VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode;
-
-
-                                db.Entry(_oldStatus).Property(x => x.CreatedOn).IsModified = true;
-                                db.Entry(_oldStatus).Property(x => x.Status).IsModified = true;
-                                db.Entry(_oldStatus).Property(x => x.ChargePointErrorCodeId).IsModified = true;
-                                db.Entry(_oldStatus).Property(x => x.ErrorInfo).IsModified = true;
-                                db.Entry(_oldStatus).Property(x => x.VendorId).IsModified = true;
-                                db.Entry(_oldStatus).Property(x => x.VendorErrorCode).IsModified = true;
-
-                                //db.SaveChanges();
-                                db.SaveChanges();
-                            }
+                            preStatus = _oldStatus.Status;
+
+                            await mainDbService.UpdateConnectorStatus(_oldStatus.Id, new ConnectorStatus() {
+                                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
+                            });
                         }
 
                         if (_oldStatus == null)
@@ -356,7 +306,8 @@ internal partial class ProfileHandler
                         if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
                         {
                             //var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
-                            var businessService = await serviceProvider.GetService<BusinessServiceFactory>().CreateBusinessService(session.CustomerId.ToString());
+                            //var businessService = await serviceProvider.GetService<BusinessServiceFactory>().CreateBusinessService(session.CustomerId.ToString());
+                            var businessService = await businessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
                             var notification = businessService.NotifyFaultStatus(new ErrorDetails()
                             {
                                 ChargeBoxId = session.ChargeBoxId,
@@ -515,7 +466,7 @@ internal partial class ProfileHandler
 
                         int _transactionId = -1;
 
-                        var businessService = await serviceProvider.GetService<BusinessServiceFactory>().CreateBusinessService(session.CustomerId.ToString());
+                        var businessService = await businessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
 
                         var _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
                         if (_request.idTag != "Backend")
@@ -639,7 +590,7 @@ internal partial class ProfileHandler
 
                         var utcNow = DateTime.UtcNow;
                         getDateTimeTime = stopTrasactionTimer.ElapsedMilliseconds;
-                        var businessService = await serviceProvider.GetService<BusinessServiceFactory>().CreateBusinessService(session.CustomerId.ToString());
+                        var businessService = await businessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
                         getServiceTime = stopTrasactionTimer.ElapsedMilliseconds;
 
                         var _idTagInfo = string.IsNullOrEmpty(_request.idTag) ? null : (_request.idTag == "Backend" ?
@@ -808,8 +759,12 @@ internal partial class ProfileHandler
                         }
 
                         stopTrasactionTimer.Stop();
-                        logger.Log(LogLevel.Critical, "ExecuteCoreRequest {action} {sessisonId} took {time} sec", action.ToString(), session.SessionID, stopTrasactionTimer.ElapsedMilliseconds / 1000);
-                        logger.Log(LogLevel.Critical, "{action} {sessisonId} time {getDateTime}/{serviceTime}/{tagInfoTime}/{dbOpTime}", action.ToString(), session.SessionID, getDateTimeTime / 1000, getServiceTime / 1000, getTagInfoTime / 1000, dbOpTime / 1000);
+
+                        if (stopTrasactionTimer.ElapsedMilliseconds > 1000)
+                        {
+                            logger.Log(LogLevel.Critical, "ExecuteCoreRequest {action} {sessisonId} took {time} sec", action.ToString(), session.SessionID, stopTrasactionTimer.ElapsedMilliseconds / 1000);
+                            logger.Log(LogLevel.Critical, "{action} {sessisonId} time {getDateTime}/{serviceTime}/{tagInfoTime}/{dbOpTime}", action.ToString(), session.SessionID, getDateTimeTime / 1000, getServiceTime / 1000, getTagInfoTime / 1000, dbOpTime / 1000);
+                        }
 
                     }
                     break;
@@ -817,7 +772,7 @@ internal partial class ProfileHandler
                     {
                         AuthorizeRequest _request = request as AuthorizeRequest;
 
-                        var businessService = await serviceProvider.GetService<BusinessServiceFactory>().CreateBusinessService(session.CustomerId.ToString());
+                        var businessService = await businessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
                         var confirm = new AuthorizeConfirmation()
                         {
                             idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted }

+ 2 - 2
EVCB_OCPP.WSServer/Program.cs

@@ -86,10 +86,10 @@ namespace EVCB_OCPP.WSServer
                     services.AddScoped<OuterBusinessService>();
                     services.AddScoped<LocalBusinessService>();
                     services.AddScoped<CPOOuterResponse>();
-                    services.AddSingleton<BusinessServiceFactory>();
+                    services.AddSingleton<IBusinessServiceFactory, BusinessServiceFactory>();
 
                     //services.AddHostedService<ProtalServer>();
-                    services.AddSingleton<MainDbService>();
+                    services.AddSingleton<IMainDbService, MainDbService>();
 
                     services.AddProtalServer();
                 })

+ 6 - 5
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -65,13 +65,15 @@ namespace EVCB_OCPP.WSServer
             IConfiguration configuration
             , IServiceProvider serviceProvider
             , IDbContextFactory<MainDBContext> maindbContextFactory
-            , IHostEnvironment environment)
+            , IHostEnvironment environment
+            , IOCPPWSServerFactory ocppWSServerFactory)
         {
             _ct = _cts.Token;
 
             this.configuration = configuration;
             this.serviceProvider = serviceProvider;
             this.maindbContextFactory = maindbContextFactory;
+            this.ocppWSServerFactory = ocppWSServerFactory;
             isInDocker = !string.IsNullOrEmpty(configuration["DOTNET_RUNNING_IN_CONTAINER"]);
 
             webConnectionString = configuration.GetConnectionString("WebDBContext");
@@ -89,6 +91,7 @@ namespace EVCB_OCPP.WSServer
         private readonly IConfiguration configuration;
         private readonly IServiceProvider serviceProvider;
         private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
+        private readonly IOCPPWSServerFactory ocppWSServerFactory;
         private readonly ProfileHandler profileHandler;//= new ProfileHandler();
         private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
         private readonly bool isInDocker;
@@ -480,10 +483,8 @@ namespace EVCB_OCPP.WSServer
         {
 
             //載入OCPP Protocol
-            var appServer = new OCPPWSServer(
-                new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") }
-                ,serviceProvider
-                , serviceProvider.GetRequiredService<MainDbService>());
+            OCPPWSServer appServer = ocppWSServerFactory.Create(new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") });
+            //var appServer = new OCPPWSServer(new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") });
 
             List<IListenerConfig> llistener = new List<IListenerConfig>();
 

+ 5 - 3
EVCB_OCPP.WSServer/Service/BusinessServiceFactory.cs

@@ -20,9 +20,11 @@ namespace EVCB_OCPP.WSServer.Service
 
     }
 
-    public class BusinessServiceFactory
+    public class BusinessServiceFactory : IBusinessServiceFactory
     {
-        public BusinessServiceFactory(IServiceProvider serviceProvider, IDbContextFactory<MainDBContext> mainDBContextFactory)
+        public BusinessServiceFactory(
+            IServiceProvider serviceProvider,
+            IDbContextFactory<MainDBContext> mainDBContextFactory)
         {
             this.serviceProvider = serviceProvider;
             this.mainDBContextFactory = mainDBContextFactory;
@@ -40,7 +42,7 @@ namespace EVCB_OCPP.WSServer.Service
             }
 
             //return isCallOut ? new OuterBusinessService(customerId) : new LocalBusinessService(customerId);
-            if(isCallOut)
+            if (isCallOut)
             {
                 OuterBusinessService outerBusinessService = serviceProvider.GetService<OuterBusinessService>();
                 outerBusinessService.CustomerId = customerId;

+ 9 - 0
EVCB_OCPP.WSServer/Service/IBusinessServiceFactory.cs

@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Service
+{
+    public interface IBusinessServiceFactory
+    {
+        Task<IBusinessService> CreateBusinessService(string customerId);
+    }
+}

+ 106 - 15
EVCB_OCPP.WSServer/Service/MainDbService.cs

@@ -2,6 +2,7 @@
 using EVCB_OCPP.Domain.Models.Database;
 using EVCB_OCPP.WSServer.Helper;
 using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using OCPPPackage.Profiles;
 using System;
@@ -13,51 +14,76 @@ using System.Threading.Tasks;
 
 namespace EVCB_OCPP.WSServer.Service;
 
-public class MainDbService
+public interface IMainDbService
 {
-    public MainDbService(IDbContextFactory<MainDBContext> contextFactory)
+    Task<string> GetMachineAuthorizationKey(string ChargeBoxId);
+    Task<string> GetMachineConfiguration(string ChargeBoxId, string configName);
+    Task<string> GetMachineHeartbeatInterval(string ChargeBoxId);
+    Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId);
+    Task<string> GetMachineSecurityProfile(string ChargeBoxId);
+    Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine);
+    Task AddOCMF(OCMF oCMF);
+    Task<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId);
+    Task UpdateConnectorStatus(string Id, ConnectorStatus connectorStatus);
+}
+
+public class MainDbService : IMainDbService
+{
+    public MainDbService(IDbContextFactory<MainDBContext> contextFactory, IConfiguration configuration)
     {
         this.contextFactory = contextFactory;
 
-        this.semaphore = new SemaphoreSlim(5);
+        var startupLimit = GetStartupLimit(configuration);
+        this.startupSemaphore = new SemaphoreSlim(startupLimit);
+
+        var opLimit = GetOpLimit(configuration);
+        this.opSemaphore = new SemaphoreSlim(opLimit);
     }
 
     private readonly IDbContextFactory<MainDBContext> contextFactory;
-    private readonly SemaphoreSlim semaphore;
+    private readonly SemaphoreSlim startupSemaphore;
+    private readonly SemaphoreSlim opSemaphore;
 
     public async Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId)
     {
-        using var semaphoreWrapper = await SemaphoreWrapper.WaitAsync(semaphore);
+        using var semaphoreWrapper = await SemaphoreWrapper.WaitAsync(startupSemaphore);
         using var db = contextFactory.CreateDbContext();
         var machine = await db.Machine.Where(x => x.ChargeBoxId == ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).AsNoTracking().FirstOrDefaultAsync();
-        if (machine == null) {
+        if (machine == null)
+        {
             return new MachineAndCustomerInfo(string.Empty, Guid.Empty, "Unknown");
         }
         var customerName = await db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefaultAsync();
         return new MachineAndCustomerInfo(machine.Id, machine.CustomerId, customerName);
     }
 
-    public async Task<string> GetMachineSecurityProfile(string ChargeBoxId)
+    public async Task<string> GetMachineConfiguration(string ChargeBoxId, string configName)
     {
-        using var semaphoreWrapper = await SemaphoreWrapper.WaitAsync(semaphore);
+        using var semaphoreWrapper = await SemaphoreWrapper.WaitAsync(startupSemaphore);
         using var db = contextFactory.CreateDbContext();
         return await db.MachineConfigurations
-            .Where(x => x.ChargeBoxId == ChargeBoxId && x.ConfigureName == StandardConfiguration.SecurityProfile)
+            .Where(x => x.ChargeBoxId == ChargeBoxId && x.ConfigureName == configName)
             .Select(x => x.ConfigureSetting).FirstOrDefaultAsync();
     }
 
+    public async Task<string> GetMachineSecurityProfile(string ChargeBoxId)
+    {
+        return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.SecurityProfile);
+    }
+
     public async Task<string> GetMachineAuthorizationKey(string ChargeBoxId)
     {
-        using var semaphoreWrapper = await SemaphoreWrapper.WaitAsync(semaphore);
-        using var db = contextFactory.CreateDbContext();
-        return await db.MachineConfigurations
-            .Where(x => x.ChargeBoxId == ChargeBoxId && x.ConfigureName == StandardConfiguration.AuthorizationKey)
-            .Select(x => x.ConfigureSetting).FirstOrDefaultAsync();
+        return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.AuthorizationKey);
+    }
+
+    public async Task<string> GetMachineHeartbeatInterval(string ChargeBoxId)
+    {
+        return await GetMachineConfiguration(ChargeBoxId, StandardConfiguration.HeartbeatInterval);
     }
 
     public async Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine)
     {
-        using var semaphoreWrapper = await SemaphoreWrapper.WaitAsync(semaphore);
+        using var semaphoreWrapper = await SemaphoreWrapper.WaitAsync(startupSemaphore);
         using var db = contextFactory.CreateDbContext();
         var _machine = db.Machine.FirstOrDefault(x => x.ChargeBoxId == ChargeBoxId);
         _machine.ChargeBoxSerialNumber = machine.ChargeBoxSerialNumber;
@@ -73,6 +99,71 @@ public class MainDbService
 
         await db.SaveChangesAsync();
     }
+
+    public async Task AddOCMF(OCMF oCMF)
+    {
+        using var db = contextFactory.CreateDbContext() ;
+        db.OCMF.Add(oCMF);
+        await db.SaveChangesAsync();
+    }
+
+    public async Task<ConnectorStatus> GetConnectorStatus(string ChargeBoxId,int ConnectorId)
+    {
+        using var db = contextFactory.CreateDbContext();
+        return await db.ConnectorStatus.Where(x => x.ChargeBoxId == ChargeBoxId
+                            && x.ConnectorId == ConnectorId).AsNoTracking().FirstOrDefaultAsync();
+    }
+
+    public async Task UpdateConnectorStatus(string Id, ConnectorStatus connectorStatus)
+    {
+        using var db = contextFactory.CreateDbContext();
+
+        ConnectorStatus status = new();
+
+        db.ChangeTracker.AutoDetectChangesEnabled = false;
+        db.ConnectorStatus.Attach(status);
+
+
+        status.CreatedOn = connectorStatus.CreatedOn;
+        status.Status = connectorStatus.Status;
+        status.ChargePointErrorCodeId = connectorStatus.ChargePointErrorCodeId;
+        status.ErrorInfo = connectorStatus.ErrorInfo;
+        status.VendorId = connectorStatus.VendorId;
+        status.VendorErrorCode = connectorStatus.VendorErrorCode;
+
+
+        db.Entry(status).Property(x => x.CreatedOn).IsModified = true;
+        db.Entry(status).Property(x => x.Status).IsModified = true;
+        db.Entry(status).Property(x => x.ChargePointErrorCodeId).IsModified = true;
+        db.Entry(status).Property(x => x.ErrorInfo).IsModified = true;
+        db.Entry(status).Property(x => x.VendorId).IsModified = true;
+        db.Entry(status).Property(x => x.VendorErrorCode).IsModified = true;
+
+        //db.SaveChanges();
+        await db.SaveChangesAsync();
+    }
+
+
+    private int GetStartupLimit(IConfiguration configuration)
+    {
+        var limitConfig = configuration["MainDbStartupLimit"];
+        int limit = 5;
+        if (limitConfig != default)
+        {
+            int.TryParse(limitConfig, out limit);
+        }
+        return limit;
+    }
+    private int GetOpLimit(IConfiguration configuration)
+    {
+        var limitConfig = configuration["MainDbOpLimit"];
+        int limit = 500;
+        if (limitConfig != default)
+        {
+            int.TryParse(limitConfig, out limit);
+        }
+        return limit;
+    }
 }
 
 public record MachineAndCustomerInfo (string MachineId, Guid CustomerId, string CustomerName);

+ 165 - 168
EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPWSServer.cs

@@ -19,221 +19,218 @@ using System.Net.Security;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
 
-namespace OCPPServer.Protocol
+namespace OCPPServer.Protocol;
+
+public class OCPPWSServer : WebSocketServer<ClientData>
 {
-    public class OCPPWSServer : WebSocketServer<ClientData>
+
+    private readonly ILogger logger;
+    private readonly IConfiguration configuration;
+    private readonly IMainDbService mainDbService;
+
+    /// <summary>
+    /// 可允許連線Clinet數
+    /// </summary>
+    public int connectNum { get; set; }
+
+    /// <summary>
+    /// 是否限制連線Clinet數
+    /// </summary>
+    public bool beConnectLimit { get; set; }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+    /// </summary>
+    /// <param name="subProtocols">The sub protocols.</param>
+    public OCPPWSServer(
+        IConfiguration configuration,
+        IMainDbService mainDbService,
+        ILogger<OCPPWSServer> logger)
+        : base(new List<ISubProtocol<ClientData>>())
     {
+        this.configuration = configuration;
+        this.mainDbService = mainDbService;
+        this.logger = logger;
+    }
 
-        static private ILogger logger;
-        private readonly IConfiguration configuration;
-        private readonly IServiceProvider serviceProvider;
-        private readonly MainDbService mainDbService;
-
-        /// <summary>
-        /// 可允許連線Clinet數
-        /// </summary>
-        public int connectNum { get; set; }
-
-        /// <summary>
-        /// 是否限制連線Clinet數
-        /// </summary>
-        public bool beConnectLimit { get; set; }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
-        /// </summary>
-        /// <param name="subProtocols">The sub protocols.</param>
-        public OCPPWSServer(IEnumerable<ISubProtocol<ClientData>> subProtocols, IServiceProvider serviceProvider
-            ,MainDbService mainDbService)
-            : base(subProtocols)
+    /// <summary>
+    /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+    /// </summary>
+    /// <param name="subProtocol">The sub protocol.</param>
+    //public OCPPWSServer(ISubProtocol<ClientData> subProtocol, IServiceProvider serviceProvider)
+    //    : base(subProtocol)
+    //{
+    //    this.configuration = serviceProvider.GetService<IConfiguration>();
+
+    //    logger = serviceProvider.GetService<ILogger<OCPPWSServer>>();
+    //}
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+    /// </summary>
+    //public OCPPWSServer(IServiceProvider serviceProvider)
+    //    : base(new List<ISubProtocol<ClientData>>())
+    //{
+    //    this.configuration = serviceProvider.GetService<IConfiguration>();
+
+    //    logger = serviceProvider.GetService<ILogger<OCPPWSServer>>();
+    //}
+
+    protected override bool ValidateClientCertificate(ClientData session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+    {
+        //  Console.WriteLine(string.Format("{0} :{1}", session.ChargeBoxId + " ValidateClientCertificate", sslPolicyErrors));
+        return true;
+        // return base.ValidateClientCertificate(session, sender, certificate, chain, sslPolicyErrors);
+    }
+
+    protected override bool ValidateHandshake(ClientData session, string origin)
+    {
+        session.ISOCPP20 = session.SecWebSocketProtocol.ToLower().Contains("ocpp2.0");
+
+        int securityProfile = 0;
+        string authorizationKey = string.Empty;
+        if (string.IsNullOrEmpty(session.Path))
         {
-            this.configuration = serviceProvider.GetService<IConfiguration>();
-            this.serviceProvider = serviceProvider;
-            this.mainDbService = mainDbService;
-            logger = serviceProvider.GetService<ILogger<OCPPWSServer>>();
+            //logger.Log();
+            logger.LogWarning("===========================================");
+            logger.LogWarning("session.Path EMPTY");
+            logger.LogWarning("===========================================");
         }
 
-        /// <summary>
-        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
-        /// </summary>
-        /// <param name="subProtocol">The sub protocol.</param>
-        public OCPPWSServer(ISubProtocol<ClientData> subProtocol, IServiceProvider serviceProvider)
-            : base(subProtocol)
-        {
-            this.configuration = serviceProvider.GetService<IConfiguration>();
-            this.serviceProvider = serviceProvider;
+        string[] words = session.Path.Split('/');
+        session.ChargeBoxId = words.Last();
 
-            logger = serviceProvider.GetService<ILogger<OCPPWSServer>>();
+        if (configuration["MaintainMode"] == "1")
+        {
+            session.ChargeBoxId = session.ChargeBoxId + "_2";
         }
 
-        /// <summary>
-        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
-        /// </summary>
-        public OCPPWSServer(IServiceProvider serviceProvider)
-            : base(new List<ISubProtocol<ClientData>>())
+        logger.LogInformation(string.Format("ValidateHandshake: {0}", session.Path));
+        bool isExistedSN = false;
+        bool authorizated = false;
+
+        var info = mainDbService.GetMachineIdAndCustomerInfo(session.ChargeBoxId).Result;
+        //var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).AsNoTracking().FirstOrDefault();
+        //session.CustomerName = machine == null ? "Unknown" : db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefault();
+        //session.CustomerId = machine == null ? Guid.Empty : machine.CustomerId;
+        //session.MachineId = machine == null ? String.Empty : machine.Id;
+        //isExistedSN = machine == null ? false : true;
+        session.CustomerName = info.CustomerName;
+        session.CustomerId = info.CustomerId;
+        session.MachineId = info.MachineId;
+        isExistedSN = !string.IsNullOrEmpty(info.MachineId);// machine == null ? false : true;
+
+        if (!isExistedSN)
         {
-            this.configuration = serviceProvider.GetService<IConfiguration>();
-            this.serviceProvider = serviceProvider;
-
-            logger = serviceProvider.GetService<ILogger<OCPPWSServer>>();
+            StringBuilder responseBuilder = new StringBuilder();
+
+            responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+            (int)HttpStatusCode.NotFound, @"Not Found");
+              
+            responseBuilder.AppendWithCrCf();
+            string sb = responseBuilder.ToString();
+            byte[] data = Encoding.UTF8.GetBytes(sb);
+            ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
+
+            logger.LogInformation(sb);
+            return false;
         }
 
-        protected override bool ValidateClientCertificate(ClientData session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        //var configVaule = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.SecurityProfile)
+        //                  .Select(x => x.ConfigureSetting).FirstOrDefault();
+        var configVaule = mainDbService.GetMachineSecurityProfile(session.ChargeBoxId).Result;
+        int.TryParse(configVaule, out securityProfile);
+
+        if (session.ISOCPP20)
         {
-            //  Console.WriteLine(string.Format("{0} :{1}", session.ChargeBoxId + " ValidateClientCertificate", sslPolicyErrors));
-            return true;
-            // return base.ValidateClientCertificate(session, sender, certificate, chain, sslPolicyErrors);
+            // 1.6 server only support change server  function
+            securityProfile = 0;
         }
-
-        protected override bool ValidateHandshake(ClientData session, string origin)
+        
+        if (securityProfile == 3 && session.UriScheme == "ws")
         {
-            session.ISOCPP20 = session.SecWebSocketProtocol.ToLower().Contains("ocpp2.0");
+            StringBuilder responseBuilder = new StringBuilder();
 
-            int securityProfile = 0;
-            string authorizationKey = string.Empty;
-            if (string.IsNullOrEmpty(session.Path))
-            {
-                //logger.Log();
-                logger.LogWarning("===========================================");
-                logger.LogWarning("session.Path EMPTY");
-                logger.LogWarning("===========================================");
-            }
+            responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+            (int)HttpStatusCode.Unauthorized, @"Unauthorized");
 
-            string[] words = session.Path.Split('/');
-            session.ChargeBoxId = words.Last();
+            responseBuilder.AppendWithCrCf();
+            string sb = responseBuilder.ToString();
+            byte[] data = Encoding.UTF8.GetBytes(sb);
 
-            if (configuration["MaintainMode"] == "1")
-            {
-                session.ChargeBoxId = session.ChargeBoxId + "_2";
-            }
+            ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
+            logger.LogInformation(sb);
+            return false;
+        }
 
-            logger.LogInformation(string.Format("ValidateHandshake: {0}", session.Path));
-            bool isExistedSN = false;
-            bool authorizated = false;
-
-            var info = mainDbService.GetMachineIdAndCustomerInfo(session.ChargeBoxId).Result;
-            //var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).AsNoTracking().FirstOrDefault();
-            //session.CustomerName = machine == null ? "Unknown" : db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefault();
-            //session.CustomerId = machine == null ? Guid.Empty : machine.CustomerId;
-            //session.MachineId = machine == null ? String.Empty : machine.Id;
-            //isExistedSN = machine == null ? false : true;
-            session.CustomerName = info.CustomerName;
-            session.CustomerId = info.CustomerId;
-            session.MachineId = info.MachineId;
-            isExistedSN = !string.IsNullOrEmpty(info.MachineId);// machine == null ? false : true;
-
-            if (!isExistedSN)
+        if ((securityProfile == 1 || securityProfile == 2))
+        {
+            if (securityProfile == 2 && session.UriScheme == "ws")
             {
-                StringBuilder responseBuilder = new StringBuilder();
-
-                responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
-                (int)HttpStatusCode.NotFound, @"Not Found");
-                  
-                responseBuilder.AppendWithCrCf();
-                string sb = responseBuilder.ToString();
-                byte[] data = Encoding.UTF8.GetBytes(sb);
-                ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
-
-                logger.LogInformation(sb);
-                return false;
+                authorizated = false;
             }
 
-            //var configVaule = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.SecurityProfile)
-            //                  .Select(x => x.ConfigureSetting).FirstOrDefault();
-            var configVaule = mainDbService.GetMachineSecurityProfile(session.ChargeBoxId).Result;
-            int.TryParse(configVaule, out securityProfile);
-
-            if (session.ISOCPP20)
-            {
-                // 1.6 server only support change server  function
-                securityProfile = 0;
-            }
-            
-            if (securityProfile == 3 && session.UriScheme == "ws")
+            if (session.Items.ContainsKey("Authorization") || session.Items.ContainsKey("authorization"))
             {
-                StringBuilder responseBuilder = new StringBuilder();
-
-                responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
-                (int)HttpStatusCode.Unauthorized, @"Unauthorized");
+                //authorizationKey = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.AuthorizationKey)
+                //                    .Select(x => x.ConfigureSetting).FirstOrDefault();
+                authorizationKey = mainDbService.GetMachineAuthorizationKey(session.ChargeBoxId).Result;
 
-                responseBuilder.AppendWithCrCf();
-                string sb = responseBuilder.ToString();
-                byte[] data = Encoding.UTF8.GetBytes(sb);
-
-                ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
-                logger.LogInformation(sb);
-                return false;
-            }
-
-            if ((securityProfile == 1 || securityProfile == 2))
-            {
-                if (securityProfile == 2 && session.UriScheme == "ws")
+                if (session.ISOCPP20)
                 {
-                    authorizated = false;
+                    // 1.6 server only support change server  function
+                    securityProfile = 0;
                 }
+                
+                logger.LogInformation("***********Authorization   ");
 
-                if (session.Items.ContainsKey("Authorization") || session.Items.ContainsKey("authorization"))
+                if (!string.IsNullOrEmpty(authorizationKey))
                 {
-                    //authorizationKey = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.AuthorizationKey)
-                    //                    .Select(x => x.ConfigureSetting).FirstOrDefault();
-                    authorizationKey = mainDbService.GetMachineAuthorizationKey(session.ChargeBoxId).Result;
-
-                    if (session.ISOCPP20)
-                    {
-                        // 1.6 server only support change server  function
-                        securityProfile = 0;
-                    }
-                    
-                    logger.LogInformation("***********Authorization   ");
-
-                    if (!string.IsNullOrEmpty(authorizationKey))
+                    string base64Encoded = session.Items.ContainsKey("Authorization") ? session.Items["Authorization"].ToString().Replace("Basic ", "") : session.Items["authorization"].ToString().Replace("Basic ", "");
+                    byte[] data = Convert.FromBase64String(base64Encoded);
+                    string[] base64Decoded = System.Text.ASCIIEncoding.ASCII.GetString(data).Split(':');
+                    logger.LogInformation("***********Authorization   " + System.Text.ASCIIEncoding.ASCII.GetString(data));
+                    if (base64Decoded.Count() == 2 && base64Decoded[0] == session.ChargeBoxId && base64Decoded[1] == authorizationKey)
                     {
-                        string base64Encoded = session.Items.ContainsKey("Authorization") ? session.Items["Authorization"].ToString().Replace("Basic ", "") : session.Items["authorization"].ToString().Replace("Basic ", "");
-                        byte[] data = Convert.FromBase64String(base64Encoded);
-                        string[] base64Decoded = System.Text.ASCIIEncoding.ASCII.GetString(data).Split(':');
-                        logger.LogInformation("***********Authorization   " + System.Text.ASCIIEncoding.ASCII.GetString(data));
-                        if (base64Decoded.Count() == 2 && base64Decoded[0] == session.ChargeBoxId && base64Decoded[1] == authorizationKey)
-                        {
-                            authorizated = true;
-                        }
+                        authorizated = true;
                     }
+                }
 
 
 
 
 
-                }
-                else
-                {
-                    authorizated = true;
+            }
+            else
+            {
+                authorizated = true;
 
-                }
+            }
 
 
 
-                if (!authorizated)
-                {
-                    StringBuilder responseBuilder = new StringBuilder();
+            if (!authorizated)
+            {
+                StringBuilder responseBuilder = new StringBuilder();
 
-                    responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
-                    (int)HttpStatusCode.Unauthorized, @"Unauthorized");
+                responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+                (int)HttpStatusCode.Unauthorized, @"Unauthorized");
 
-                    responseBuilder.AppendWithCrCf();
-                    string sb = responseBuilder.ToString();
-                    byte[] data = Encoding.UTF8.GetBytes(sb);
+                responseBuilder.AppendWithCrCf();
+                string sb = responseBuilder.ToString();
+                byte[] data = Encoding.UTF8.GetBytes(sb);
 
-                    ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
-                    logger.LogInformation(sb);
-                    return false;
-                }
+                ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
+                logger.LogInformation(sb);
+                return false;
             }
+        }
 
 
-         
+     
 
 
 
-            return true;
-        }
+        return true;
     }
 }

+ 42 - 0
EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPWSServerFactory.cs

@@ -0,0 +1,42 @@
+using EVCB_OCPP.WSServer.Service;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.Protocol;
+
+public interface IOCPPWSServerFactory
+{
+    OCPPWSServer Create(IEnumerable<ISubProtocol<ClientData>> subProtocols);
+}
+
+public class OCPPWSServerFactory : IOCPPWSServerFactory
+{
+
+    public OCPPWSServerFactory(
+        IConfiguration configuration,
+        IMainDbService mainDbService,
+        ILoggerFactory loggerFactory,
+        IServiceProvider serviceProvider)
+    {
+        this.loggerFactory = loggerFactory;
+        this.serviceProvider = serviceProvider;
+    }
+    public OCPPWSServer Create(IEnumerable<ISubProtocol<ClientData>> subProtocols)
+    {
+        var logger = loggerFactory.CreateLogger<OCPPWSServer>();
+        var server = serviceProvider.GetRequiredService<OCPPWSServer>();
+        server.RegisterSubProtocol(subProtocols);
+        return server;
+    }
+
+    private readonly ILoggerFactory loggerFactory;
+    private readonly IServiceProvider serviceProvider;
+}

+ 25 - 6
SuperWebSocket/WebSocketServer.cs

@@ -117,7 +117,7 @@ namespace SuperWebSocket
 
             foreach (var protocol in subProtocols)
             {
-                if (!RegisterSubProtocol(protocol))
+                if (!RegisterSubProtocolInternal(protocol))
                     throw new Exception("Failed to register sub protocol!");
             }
 
@@ -138,7 +138,7 @@ namespace SuperWebSocket
         /// Initializes a new instance of the <see cref="WebSocketServer&lt;TWebSocketSession&gt;"/> class.
         /// </summary>
         public WebSocketServer()
-            : base(new WebSocketProtocol())
+            : base()
         {
 
         }
@@ -206,6 +206,25 @@ namespace SuperWebSocket
             }
         }
 
+        /// <summary>
+        /// Register sub protocols, Only use after empty create 
+        /// </summary>
+        /// <param name="subProtocols"></param>
+        /// <exception cref="Exception"></exception>
+        public void RegisterSubProtocol(IEnumerable<ISubProtocol<TWebSocketSession>> subProtocols)
+        {
+            if (!subProtocols.Any())
+                return;
+
+            foreach (var protocol in subProtocols)
+            {
+                if (!RegisterSubProtocolInternal(protocol))
+                    throw new Exception("Failed to register sub protocol!");
+            }
+
+            m_SubProtocolConfigured = true;
+        }
+
         bool IWebSocketServer.ValidateHandshake(IWebSocketSession session, string origin)
         {
             var s = (TWebSocketSession)session;
@@ -224,7 +243,7 @@ namespace SuperWebSocket
             return true;
         }
 
-        bool RegisterSubProtocol(ISubProtocol<TWebSocketSession> subProtocol)
+        bool RegisterSubProtocolInternal(ISubProtocol<TWebSocketSession> subProtocol)
         {
             if (m_SubProtocols.ContainsKey(subProtocol.Name))
             {
@@ -268,7 +287,7 @@ namespace SuperWebSocket
                                 return false;
                             }
 
-                            if (!RegisterSubProtocol(subProtocolInstance))
+                            if (!RegisterSubProtocolInternal(subProtocolInstance))
                                 return false;
                         }
                         else
@@ -277,7 +296,7 @@ namespace SuperWebSocket
                             {
                                 subProtocolInstance = new BasicSubProtocol<TWebSocketSession>(protocolName);
 
-                                if (!RegisterSubProtocol(subProtocolInstance))
+                                if (!RegisterSubProtocolInternal(subProtocolInstance))
                                     return false;
                             }
                         }
@@ -302,7 +321,7 @@ namespace SuperWebSocket
 
             if (m_SubProtocols.Count <= 0 || (subProtocolConfigDict.ContainsKey(BasicSubProtocol<TWebSocketSession>.DefaultName) && !m_SubProtocols.ContainsKey(BasicSubProtocol<TWebSocketSession>.DefaultName)))
             {
-                if (!RegisterSubProtocol(BasicSubProtocol<TWebSocketSession>.CreateDefaultSubProtocol()))
+                if (!RegisterSubProtocolInternal(BasicSubProtocol<TWebSocketSession>.CreateDefaultSubProtocol()))
                     return false;
             }
 

+ 7 - 0
SuperWebSocket/bin/Debug/SuperWebSocket.XML

@@ -1375,6 +1375,13 @@
             Gets the request filter factory.
             </summary>
         </member>
+        <member name="M:SuperWebSocket.WebSocketServer`1.RegisterSubProtocol(System.Collections.Generic.IEnumerable{SuperWebSocket.SubProtocol.ISubProtocol{`0}})">
+            <summary>
+            Register sub protocols, Only use after empty create 
+            </summary>
+            <param name="subProtocols"></param>
+            <exception cref="T:System.Exception"></exception>
+        </member>
         <member name="M:SuperWebSocket.WebSocketServer`1.ValidateHandshake(`0,System.String)">
             <summary>
             Validates the handshake request.