Robert 1 жил өмнө
parent
commit
803601bd32

+ 7 - 26
EVCB_OCPP.WSServer/Jobs/StationConfigPollingJob.cs

@@ -1,4 +1,5 @@
-using EVCB_OCPP.WSServer.Service.DbService;
+using EVCB_OCPP.WSServer.Service;
+using EVCB_OCPP.WSServer.Service.DbService;
 using Quartz;
 using System;
 using System.Collections.Generic;
@@ -12,35 +13,15 @@ namespace EVCB_OCPP.WSServer.Jobs
     internal class StationConfigPollingJob : IJob
     {
         public StationConfigPollingJob(
-            ProtalServer protalServer, 
-            WebDbService webDbService)
+            StationConfigService stationConfigService)
         {
-            this.protalServer = protalServer;
-            this.webDbService = webDbService;
+            this.stationConfigService = stationConfigService;
         }
+        private readonly StationConfigService stationConfigService;
 
-        private readonly ProtalServer protalServer;
-        private readonly WebDbService webDbService;
-
-        public async Task Execute(IJobExecutionContext context)
+        public Task Execute(IJobExecutionContext context)
         {
-            var connectedEvses = protalServer.GetClientDic();
-            connectedEvses = connectedEvses.Where(x => x.Value.IsCheckIn = true).ToDictionary(x=>x.Key, x=>x.Value);
-
-            Dictionary<int, Dictionary<string, string>> stationEvseConfigs = await webDbService.GetStationEvseConfigs();
-            Dictionary<string, int> evseStationPair = await webDbService.GetEvseStationPair(connectedEvses.Keys.ToList());
-
-            foreach ( var chargboxId in connectedEvses.Keys ) 
-            {
-                if (!evseStationPair.ContainsKey(chargboxId))
-                {
-                    continue;
-                }
-
-                var stationId = evseStationPair[chargboxId];
-                var configs = stationEvseConfigs[stationId];
-                await protalServer.ComparenUpdateConfig(chargboxId, configs);
-            }
+            return stationConfigService.CheckAndUpdateStationConfig();
         }
     }
 }

+ 2 - 0
EVCB_OCPP.WSServer/Program.cs

@@ -78,6 +78,8 @@ namespace EVCB_OCPP.WSServer
             app.UseOcppWsService();
             app.MapApiServce();
             app.Urls.Add("http://*:80");
+
+            app.InitStationConfigService();
             app.Run();
             //host.Run();
         }

+ 26 - 12
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -70,7 +70,7 @@ namespace EVCB_OCPP.WSServer
             , IServiceProvider serviceProvider
             , OcppWebsocketService websocketService
             , ConfirmWaitingMessageSerevice confirmWaitingMessageSerevice
-            , StationConfigService stationConfigService
+            //, StationConfigService stationConfigService
             , OuterHttpClient httpClient)
         {
             _ct = _cts.Token;
@@ -86,7 +86,8 @@ namespace EVCB_OCPP.WSServer
             this.messageService = serverMessageService;
             this.websocketService = websocketService;
             this.confirmWaitingMessageSerevice = confirmWaitingMessageSerevice;
-            this.stationConfigService = stationConfigService;
+            this.serviceProvider = serviceProvider;
+            //this.stationConfigService = stationConfigService;
             this.httpClient = httpClient;
             isInDocker = !string.IsNullOrEmpty(configuration["DOTNET_RUNNING_IN_CONTAINER"]);
 
@@ -105,7 +106,7 @@ namespace EVCB_OCPP.WSServer
         //private readonly Object _lockConfirmPacketList = new object();
         private readonly ILogger<ProtalServer> logger;
         private readonly IConfiguration configuration;
-        //private readonly IServiceProvider serviceProvider;
+        private readonly IServiceProvider serviceProvider;
         //private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
         private readonly IMainDbService mainDbService;
         //private readonly ISqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
@@ -117,7 +118,7 @@ namespace EVCB_OCPP.WSServer
         private readonly ServerMessageService messageService;
         private readonly OcppWebsocketService websocketService;
         private readonly ConfirmWaitingMessageSerevice confirmWaitingMessageSerevice;
-        private readonly StationConfigService stationConfigService;
+        //private readonly StationConfigService stationConfigService;
         private readonly ProfileHandler profileHandler;//= new ProfileHandler();
         //private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
         private readonly bool isInDocker;
@@ -165,6 +166,18 @@ namespace EVCB_OCPP.WSServer
         private CancellationTokenSource _cts = new CancellationTokenSource();
         private CancellationToken _ct;
 
+        private StationConfigService _StationConfigService = null;
+        private StationConfigService StationConfigService
+        {
+            get
+            {
+                if (_StationConfigService is null)
+                {
+                    _StationConfigService = serviceProvider.GetRequiredService<StationConfigService>();
+                }
+                return _StationConfigService;
+            }
+        }
         #endregion
 
         internal Dictionary<string, WsClientData> GetClientDic()
@@ -724,6 +737,14 @@ namespace EVCB_OCPP.WSServer
                                     if (bootNotificationConfirmation.status == Packet.Messages.SubTypes.RegistrationStatus.Accepted)
                                     {
                                         session.IsCheckIn = true;
+
+                                        var sendTask = async () => await messageService.SendDataTransferRequest(
+                                            session.ChargeBoxId,
+                                            messageId: "ID_FirmwareVersion",
+                                            vendorId: "Phihong Technology",
+                                            data: string.Empty);
+                                        await sendTask();
+                                        //await confirmWaitingMessageSerevice.SendAndWaitUntilResultAsync(sendTask, session.DisconnetCancellationToken);
                                     }
                                 }
 
@@ -1041,14 +1062,7 @@ namespace EVCB_OCPP.WSServer
                     session.ChargeBoxId, key: "StopTransactionOnInvalidId", value: "True");
             }
 
-            await stationConfigService.CheckAndUpdateEvseConfig(session.ChargeBoxId, session.DisconnetCancellationToken);
-
-            sendTask = async () => await messageService.SendDataTransferRequest(
-                session.ChargeBoxId,
-                messageId: "ID_FirmwareVersion",
-                vendorId: "Phihong Technology",
-                data: string.Empty);
-            await confirmWaitingMessageSerevice.SendAndWaitUntilResultAsync(sendTask, session.DisconnetCancellationToken);
+            await StationConfigService?.CheckAndUpdateEvseConfig(session, session.DisconnetCancellationToken);
         }
 
         private void Send(WsClientData session, string msg, string messageType, string errorMsg = "")

+ 45 - 6
EVCB_OCPP.WSServer/Service/DbService/WebDbService.cs

@@ -146,10 +146,20 @@ public class WebDbService
 
     internal async Task<Dictionary<string, string>> GetEvseStationConfig(int stationId, CancellationToken token = default)
     {
+        string strSql = """
+                SELECT [ConfigureName], [ConfigureSetting]
+                FROM [dbo].[StationMachineConfig]
+                WHERE [StationId] = @StationId
+                """;
 
+        using SqlConnection conn = await webDbConnectionFactory.CreateAsync();
+        DynamicParameters parameters = new DynamicParameters();
+        parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input);
+        var configs = await conn.QueryAsync<StationMachineConfig>(new CommandDefinition(strSql, parameters: parameters, cancellationToken: token));
+        return configs.ToDictionary(x => x.ConfigureName, x => x.ConfigureSetting);
     }
 
-    internal async Task<int?> GetEvseStation(string chargeboxIds, CancellationToken token = default)
+    internal async Task<int?> GetEvseStation(string chargeboxId, CancellationToken token = default)
     {
         string getStationStrSql = """
                 SELECT [StationId]
@@ -157,9 +167,9 @@ public class WebDbService
                 WHERE [ChargeBoxId] = @ChargeBoxIds;
                 """;
         var parameters = new DynamicParameters();
-        parameters.Add("@ChargeBoxIds", chargeboxIds, direction: ParameterDirection.Input, size: 25);
+        parameters.Add("@ChargeBoxIds", chargeboxId, direction: ParameterDirection.Input, size: 25);
         using SqlConnection conn = await webDbConnectionFactory.CreateAsync();
-        var stationId = await conn.QuerySingleAsync<int?>(new CommandDefinition(getStationStrSql, parameters, token));
+        var stationId = await conn.QuerySingleAsync<int?>(new CommandDefinition(getStationStrSql, parameters, cancellationToken: token));
         return stationId;
     }
 
@@ -177,14 +187,17 @@ public class WebDbService
         return configs.ToDictionary(x => x.ChargeBoxId, x => x.StationId);
     }
 
-    internal async Task<Dictionary<int, Dictionary<string, string>>> GetStationEvseConfigs()
+    internal async Task<Dictionary<int, Dictionary<string, string>>> GetStationEvseConfigs(List<int> stationIds, CancellationToken token = default)
     {
         string getSql = """
                 SELECT [StationId],[ConfigureName],[ConfigureSetting]
                 FROM [dbo].[StationMachine]
+                WHERE [StationId] in @StationIds
                 """;
-        using SqlConnection conn = await webDbConnectionFactory.CreateAsync();
-        var configs = await conn.QueryAsync<StationMachineConfig>(getSql);
+        using SqlConnection conn = await webDbConnectionFactory.CreateAsync(); 
+        var parameters = new DynamicParameters();
+        parameters.Add("@StationIds", stationIds, direction: ParameterDirection.Input);
+        var configs = await conn.QueryAsync<StationMachineConfig>(new CommandDefinition(getSql, parameters, cancellationToken: token));
         return configs
             .GroupBy(x => x.StationId)
             .ToDictionary(
@@ -194,4 +207,30 @@ public class WebDbService
                     x => x.ConfigureSetting
             ));
     }
+
+    internal async Task<Dictionary<int, Dictionary<string, string>>> GetStationEvseConfigs(CancellationToken token = default)
+    {
+        string getSql = """
+                SELECT [StationId],[ConfigureName],[ConfigureSetting]
+                FROM [dbo].[StationMachineConfig]
+                """;
+        using SqlConnection conn = await webDbConnectionFactory.CreateAsync();
+        try
+        {
+            var configs = await conn.QueryAsync<StationMachineConfig>(new CommandDefinition(getSql, cancellationToken: token));
+            return configs
+                .GroupBy(x => x.StationId)
+                .ToDictionary(
+                    x => x.Key,
+                    x => x.ToDictionary(
+                        x => x.ConfigureName,
+                        x => x.ConfigureSetting
+                ));
+        }
+        catch
+        {
+            logger.LogWarning("StationMachineConfig get failed");
+            return new Dictionary<int, Dictionary<string, string>>();
+        }
+    }
 }

+ 120 - 12
EVCB_OCPP.WSServer/Service/StationConfigService.cs

@@ -1,7 +1,10 @@
 using EVCB_OCPP.Packet.Messages.Core;
 using EVCB_OCPP.WSServer.Message;
 using EVCB_OCPP.WSServer.Service.DbService;
+using EVCB_OCPP.WSServer.Service.WsService;
 using log4net.Core;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.Logging;
 using System;
 using System.Collections.Generic;
@@ -11,46 +14,142 @@ using System.Threading.Tasks;
 
 namespace EVCB_OCPP.WSServer.Service;
 
-internal class StationConfigService
+public static class StationConfigServiceExt
+{
+    public static Task InitStationConfigService(this IHost host)
+    {
+        return host.Services.GetRequiredService<StationConfigService>().Init();
+    }
+}
+
+public class StationConfigService
 {
     public StationConfigService(
-        WebDbService webDbService
+        ProtalServer portalServer
+        , WebDbService webDbService
         , ServerMessageService messageService
         , ConfirmWaitingMessageSerevice confirmWaitingMessageSerevice
         , ILogger<StationConfigService> logger)
     {
+        this.portalServer = portalServer;
         this.webDbService = webDbService;
         this.messageService = messageService;
         this.confirmWaitingMessageSerevice = confirmWaitingMessageSerevice;
         this.logger = logger;
     }
+
+    private readonly ProtalServer portalServer;
     private readonly WebDbService webDbService;
     private readonly ServerMessageService messageService;
     private readonly ConfirmWaitingMessageSerevice confirmWaitingMessageSerevice;
     private readonly ILogger<StationConfigService> logger;
-    private readonly Dictionary<int, Dictionary<string, string>> stationConfigRecord = new();
 
-    public async Task CheckAndUpdateEvseConfig(string chargeBoxId, CancellationToken token = default)
+    internal static Dictionary<int, Dictionary<string, string>> stationConfigRecord = null;
+
+    public async Task Init()
     {
-        MessageResult response = await GetEvseCurrentConfig(chargeBoxId, token);
-        if (!response.Success || response.Message is not GetConfigurationConfirmation confirmation)
+        stationConfigRecord = await webDbService.GetStationEvseConfigs();
+    }
+
+    public async Task CheckAndUpdateEvseConfig(WsClientData session, CancellationToken token = default)
+    {
+        var chargeBoxId = session.ChargeBoxId;
+        var stationId = await webDbService.GetEvseStation(chargeBoxId);
+        if (stationId is null)
         {
             return;
         }
 
-        Dictionary<string, string> configs = await GetEvseDBCurrentConfig(chargeBoxId, token);
-        if (configs == null)
+        if (session.StationId != stationId)
+        {
+            session.StationId = stationId;
+            await UpdateEvseConfig(chargeBoxId, stationId.Value);
+        }
+        return;
+    }
+
+    public async Task CheckAndUpdateStationConfig()
+    {
+        await UpdateStationConfigChangedEvses();
+        await UpdateStationChangedEvses();
+        return;
+    }
+
+    private async Task UpdateStationConfigChangedEvses()
+    {
+        List<int> modifiedStations = new();
+        var dbStationEvseConfig = await webDbService.GetStationEvseConfigs();
+        foreach (var stationConfig in dbStationEvseConfig)
+        {
+            if (!stationConfigRecord.ContainsKey(stationConfig.Key) ||
+                !CheckIsEqual(stationConfig.Value, stationConfigRecord[stationConfig.Key]))
+            {
+                modifiedStations.Add(stationConfig.Key);
+            }
+        }
+        if (modifiedStations.Count == 0)
         {
-            logger.LogInformation("{0} get station config failed", chargeBoxId);
             return;
         }
 
+        stationConfigRecord = dbStationEvseConfig;
+
+        var connectedEvses = portalServer.GetClientDic().Values;
+        foreach (var evse in connectedEvses)
+        {
+            if (evse.IsCheckIn &&
+                evse.StationId is not null &&
+                modifiedStations.Contains(evse.StationId.Value))
+            {
+                await UpdateEvseConfig(evse.ChargeBoxId, evse.StationId.Value);
+            }
+        }
+    }
+
+    private async Task UpdateStationChangedEvses()
+    {
+        List<string> modifiedEvses = new();
+        var connectedEvses = portalServer.GetClientDic().Values.ToList();
+        foreach (var evse in connectedEvses)
+        {
+            var currentStation = await webDbService.GetEvseStation(evse.ChargeBoxId);
+            if (currentStation is null)
+            {
+                evse.StationId = null;
+                continue;
+            }
+
+            if (evse.StationId != currentStation)
+            {
+                evse.StationId = currentStation;
+                await UpdateEvseConfig(evse.ChargeBoxId, currentStation.Value);
+            }
+        }
+    }
+
+    private async Task UpdateEvseConfig(string chargeBoxId, int stationId, CancellationToken token = default)
+    {
+        MessageResult getEvseCurrentConfigResponse = await GetEvseCurrentConfig(chargeBoxId, token);
+        if (!getEvseCurrentConfigResponse.Success || getEvseCurrentConfigResponse.Message is not GetConfigurationConfirmation confirmation)
+        {
+            logger.LogWarning("{chargeBoxId} get config from evse failed", chargeBoxId);
+            return;
+        }
+
+        if (!stationConfigRecord.ContainsKey(stationId))
+        {
+            logger.LogInformation("{chargeBoxId} doesnt has station config", chargeBoxId);
+            return;
+        }
+
+        var dbConfigs = stationConfigRecord[stationId];
         await ComparenUpdateConfig(chargeBoxId,
             evseCurrentConfigs: confirmation.configurationKey.ToDictionary(x => x.key, x => x.value),
-            evseDbConfigs: configs,
+            evseDbConfigs: dbConfigs,
             token);
     }
 
+
     private async Task<MessageResult> GetEvseCurrentConfig(string chargeBoxId, CancellationToken token = default)
     {
         var sendTask = async () => await messageService.SendGetEVSEConfigureRequest(chargeBoxId);
@@ -70,11 +169,11 @@ internal class StationConfigService
 
         if (!stationConfigRecord.Keys.Contains(staionID))
         {
-            await webDbService.GetEvseStationConfig(staionID);
+            stationConfigRecord[staionID] = await webDbService.GetEvseStationConfig(staionID);
         }
 
         return stationConfigRecord[staionID];
-        return webDbService.GetCustomerStationEvseConfig(chargeBoxId, token);
+        //return webDbService.GetCustomerStationEvseConfig(chargeBoxId, token);
     }
 
     internal async Task ComparenUpdateConfig(string chargeBoxId,
@@ -95,4 +194,13 @@ internal class StationConfigService
             response = await confirmWaitingMessageSerevice.SendAndWaitUntilResultAsync(sendTask, token);
         }
     }
+
+    private bool CheckIsEqual(Dictionary<string, string> d1, Dictionary<string, string> d2)
+    {
+        return d1.Count == d2.Count && d1.All(
+             (d1KV) => d2.TryGetValue(d1KV.Key, out var d2Value) && (
+                  d1KV.Value == d2Value ||
+                  d1KV.Value?.Equals(d2Value) == true)
+        );
+    }
 }

+ 1 - 1
EVCB_OCPP.WSServer/Service/WsService/WsClientData.cs

@@ -82,7 +82,7 @@ public class WsClientData : WsSession
 
     public string CustomerName { get; set; }
 
-    public string StationId { set; get; }
+    public int? StationId { set; get; } = null;
 
     public event EventHandler<string> m_ReceiveData;