using EVCB_OCPP.Packet.Messages.Core; using EVCB_OCPP.Packet.Messages.SubTypes; 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; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EVCB_OCPP.WSServer.Service; public static class StationConfigServiceExt { public static Task InitStationConfigService(this IHost host) { var server = host.Services.GetRequiredService(); var stationConfigService = host.Services.GetRequiredService(); server.InitActions.Add(stationConfigService.CheckAndUpdateEvseConfigOnConnnected); return host.Services.GetRequiredService().Init(); } } public class StationConfigService { public StationConfigService( ProtalServer portalServer , WebDbService webDbService , ServerMessageService messageService , ConfirmWaitingMessageSerevice confirmWaitingMessageSerevice , ILogger logger) { this.portalServer = portalServer; this.webDbService = webDbService; this.messageService = messageService; this.confirmWaitingMessageSerevice = confirmWaitingMessageSerevice; this.logger = logger; } private static string Session_Station_Key = "StationConfigService_Station"; private readonly ProtalServer portalServer; private readonly WebDbService webDbService; private readonly ServerMessageService messageService; private readonly ConfirmWaitingMessageSerevice confirmWaitingMessageSerevice; private readonly ILogger logger; internal static Dictionary> stationConfigRecord = null; public async Task Init() { stationConfigRecord = await webDbService.GetStationEvseConfigs(); } public async Task CheckAndUpdateEvseConfigOnConnnected(WsClientData session, CancellationToken token = default) { var chargeBoxId = session.ChargeBoxId; var stationId = await webDbService.GetEvseStation(chargeBoxId, token); if (stationId is null) { logger.LogInformation("{chargeBoxId} doesn't belongs to any station", chargeBoxId); return; } int? sessionStationId = GetSessionStation(session); if (sessionStationId != stationId) { SetSessionStation(session, stationId); var configs = session.Data.ContainsKey(GlobalConfig.BootData_EVSEConfig_Key) ? session.Data[GlobalConfig.BootData_EVSEConfig_Key] as List : null; await UpdateEvseConfig(chargeBoxId, stationId.Value, evseCurrentKeys: configs, token: token); } return; } public async Task CheckAndUpdateStationConfig() { await UpdateStationConfigChangedEvses(); await UpdateStationChangedEvses(); return; } private async Task UpdateStationConfigChangedEvses() { List 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) { return; } stationConfigRecord = dbStationEvseConfig; Dictionary.ValueCollection connectedEvses = portalServer.GetClientDic().Values; List updateTasks = new List(); foreach (WsClientData evse in connectedEvses) { int? sessionStationId = GetSessionStation(evse); if (sessionStationId is not null && modifiedStations.Contains(sessionStationId.Value)) { var tmp = UpdateEvseConfig(evse.ChargeBoxId, sessionStationId.Value); updateTasks.Add(tmp); } } await Task.WhenAll(updateTasks); } private async Task UpdateStationChangedEvses() { List modifiedEvses = new(); var connectedEvses = portalServer.GetClientDic().Values.Where(x => x.IsCheckIn).ToList(); var evseStationPair = await webDbService.GetEvseStationPair(connectedEvses.Select(x => x.ChargeBoxId).ToList()); foreach (var evse in connectedEvses) { //var currentStation = await webDbService.GetEvseStation(evse.ChargeBoxId); int? currentStation = evseStationPair.ContainsKey(evse.ChargeBoxId) ? evseStationPair[evse.ChargeBoxId] : null; if (currentStation is null) { SetSessionStation(evse, null); continue; } int? sessionStationId = GetSessionStation(evse); if (sessionStationId != currentStation) { sessionStationId = currentStation; await UpdateEvseConfig(evse.ChargeBoxId, currentStation.Value); SetSessionStation(evse, sessionStationId); } } } private async Task UpdateEvseConfig(string chargeBoxId, int stationId, List evseCurrentKeys = null, CancellationToken token = default) { Dictionary dbConfigs = null; if (!stationConfigRecord.ContainsKey(stationId)) { logger.LogInformation("{chargeBoxId} doesnt has station config", chargeBoxId); return; } dbConfigs = stationConfigRecord[stationId]; if (evseCurrentKeys == null) { GetConfigurationConfirmation confirmation = await GetEvseCurrentConfig(chargeBoxId, ConfigKeys: dbConfigs.Keys.ToList(), token: token); if (confirmation is null) { logger.LogWarning("{chargeBoxId} get config from evse failed", chargeBoxId); return; } evseCurrentKeys = confirmation.configurationKey; } Dictionary evseCurrentConfigs = new Dictionary(); evseCurrentConfigs = evseCurrentKeys.DistinctBy(x=>x.key).ToDictionary(x => x.key, x => x.value); await CompareAndUpdateConfig(chargeBoxId, evseCurrentConfigs: evseCurrentConfigs, evseDbConfigs: dbConfigs, token); } private async Task GetEvseCurrentConfig(string chargeBoxId, List ConfigKeys = default, CancellationToken token = default) { var sendTask = async (string serialNo) => await messageService.SendGetEVSEConfigureRequest(chargeBoxId, configKeys: ConfigKeys, serialNo: serialNo); var response = await confirmWaitingMessageSerevice.SendAndWaitUntilResultAsync(sendTask, token: token); if (response is GetConfigurationConfirmation confirmation) { return confirmation; } return null; } private async Task> GetEvseDBCurrentConfig(string chargeBoxId, CancellationToken token = default) { var receivedStaionID = await webDbService.GetEvseStation(chargeBoxId, token); if (receivedStaionID is null) { logger.LogInformation("{chargeBoxId} station not found", chargeBoxId); return null; } var staionID = receivedStaionID.Value; if (!stationConfigRecord.Keys.Contains(staionID)) { stationConfigRecord[staionID] = await webDbService.GetEvseStationConfig(staionID); } return stationConfigRecord[staionID]; } internal async Task CompareAndUpdateConfig(string chargeBoxId, Dictionary evseCurrentConfigs, Dictionary evseDbConfigs, CancellationToken token = default) { foreach (var config in evseDbConfigs) { if (evseCurrentConfigs.Keys.Contains(config.Key) && evseCurrentConfigs[config.Key] == config.Value) { continue; } object response = null; var sendTask = async (string serialNo) => await messageService.SendChangeConfigurationRequest(chargeBoxId, config.Key, config.Value, serialNo: serialNo); response = await confirmWaitingMessageSerevice.SendAndWaitResultAsync(sendTask, token: token); } } private bool CheckIsEqual(Dictionary d1, Dictionary d2) { return d1.Count == d2.Count && d1.All( (d1KV) => d2.TryGetValue(d1KV.Key, out var d2Value) && ( d1KV.Value == d2Value || d1KV.Value?.Equals(d2Value) == true) ); } private int? GetSessionStation(WsClientData session) { if (session is null || !session.Data.ContainsKey(Session_Station_Key)) { return null; } return (int?)session.Data[Session_Station_Key]; } private void SetSessionStation(WsClientData session, int? stationId) { if (session is null) { return; } session.Data[Session_Station_Key] = stationId; } }