using Dapper; using EVCB_OCPP.Domain; using EVCB_OCPP.Packet.Features; using EVCB_OCPP.Packet.Messages; using EVCB_OCPP.Packet.Messages.Basic; 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.Extensions.Hosting; using Newtonsoft.Json; using System.Data; using System.Diagnostics; using System.Security.Authentication; using System.Xml.Linq; using NLog; using Microsoft.Extensions.Configuration; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder; using NLog.Extensions.Logging; using Microsoft.Data.SqlClient; using System.Collections.Concurrent; using Microsoft.Extensions.Logging; using EVCB_OCPP.WSServer.Service.WsService; using System.Net.WebSockets; using EVCB_OCPP.Domain.ConnectionFactory; using EVCB_OCPP.WSServer.Service.DbService; using EVCB_OCPP.Packet.Messages.SubTypes; using System.Linq; using Azure; namespace EVCB_OCPP.WSServer { public class DestroyRequest : IRequest { public string Action { get; set; } public bool TransactionRelated() { return false; } public bool Validate() { return true; } } public class ProtalServer : IHostedService { //static private ILogger logger = NLog.LogManager.GetCurrentClassLogger(); public ProtalServer( ILogger logger , IConfiguration configuration //, IDbContextFactory maindbContextFactory , IMainDbService mainDbService //, IDbContextFactory connectionLogdbContextFactory , ISqlConnectionFactory webDbConnectionFactory , ISqlConnectionFactory mainDbConnectionFactory , IHostEnvironment environment //, IOCPPWSServerFactory ocppWSServerFactory , IConnectionLogdbService connectionLogdbService , WebDbService webDbService , ServerMessageService serverMessageService , IServiceProvider serviceProvider , OcppWebsocketService websocketService , ConfirmWaitingMessageSerevice confirmWaitingMessageSerevice //, StationConfigService stationConfigService , OuterHttpClient httpClient , EnvCheckService envCheckService) { _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; this.messageService = serverMessageService; this.websocketService = websocketService; this.confirmWaitingMessageSerevice = confirmWaitingMessageSerevice; this.serviceProvider = serviceProvider; //this.stationConfigService = stationConfigService; this.httpClient = httpClient; isInDocker = !string.IsNullOrEmpty(configuration["DOTNET_RUNNING_IN_CONTAINER"]); var maxBootCntConfig = configuration["MaxBootCnt"]; if (!string.IsNullOrEmpty(maxBootCntConfig) && int.TryParse(maxBootCntConfig, out var maxBootCntConfigInt)) { maxBootCnt = maxBootCntConfigInt; } var bootReservCntConfig = configuration["BootReservCnt"]; if (!string.IsNullOrEmpty(bootReservCntConfig) && int.TryParse(bootReservCntConfig, out var bootReservCntConfigInt)) { bootReservCnt = bootReservCntConfigInt; } bootSemaphore = new SemaphoreSlim(maxBootCnt, maxBootCnt); // = configuration.GetConnectionString("WebDBContext"); this.profileHandler = serviceProvider.GetService();// new ProfileHandler(configuration, serviceProvider); _loadingBalanceService = new LoadingBalanceService(mainDbConnectionFactory, webDbConnectionFactory); envCheckService.CheckVariable(); WarmUpLog(); } #region private fields private OuterHttpClient httpClient; private DateTime lastcheckdt = DateTime.UtcNow.AddSeconds(-20); private ConcurrentDictionary clientDic = new ConcurrentDictionary(); //private readonly Object _lockClientDic = new object(); //private readonly Object _lockConfirmPacketList = new object(); private readonly ILogger logger; private readonly IConfiguration configuration; private readonly IServiceProvider serviceProvider; //private readonly IDbContextFactory maindbContextFactory; private readonly IMainDbService mainDbService; //private readonly ISqlConnectionFactory webDbConnectionFactory; //private readonly IDbContextFactory connectionLogdbContextFactory; //private readonly IOCPPWSServerFactory ocppWSServerFactory; private readonly IConnectionLogdbService connectionLogdbService; private readonly WebDbService webDbService; private readonly ServerMessageService messageService; private readonly OcppWebsocketService websocketService; private readonly ConfirmWaitingMessageSerevice confirmWaitingMessageSerevice; //private readonly StationConfigService stationConfigService; private readonly ProfileHandler profileHandler;//= new ProfileHandler(); //private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString; private readonly bool isInDocker; //private List needConfirmPacketList = new List(); private DateTime checkUpdateDt = DateTime.UtcNow; private DateTime _CheckFeeDt = DateTime.UtcNow; private DateTime _CheckLBDt = DateTime.UtcNow; private DateTime _CheckDenyListDt = DateTime.UtcNow.AddDays(-1); private readonly LoadingBalanceService _loadingBalanceService;// = new LoadingBalanceService(); private readonly int maxBootCnt = 10; private readonly int bootReservCnt = 5; private readonly SemaphoreSlim bootSemaphore = new SemaphoreSlim(20, 20); private List _StationInfo = new List(); private readonly List needConfirmActions = new List() { "GetConfiguration", "ChangeConfiguration", "RemoteStartTransaction", "RemoteStopTransaction", "ChangeAvailability", "ClearCache", "DataTransfer", "Reset", "UnlockConnector", "TriggerMessage", "GetDiagnostics", "UpdateFirmware", "GetLocalListVersion", "SendLocalList", "SetChargingProfile", "ClearChargingProfile", "GetCompositeSchedule", "ReserveNow", "CancelReservation", "ExtendedTriggerMessage" }; private readonly List profiles = new List() { new CoreProfile(), new FirmwareManagementProfile(), new ReservationProfile(), new RemoteTriggerProfile(), new SmartChargingProfile(), new LocalAuthListManagementProfile(), new SecurityProfile(), }; private CancellationTokenSource _cts = new CancellationTokenSource(); private CancellationToken _ct; #endregion internal Dictionary GetClientDic() { Dictionary toReturn = null; toReturn = new Dictionary(clientDic); return toReturn; } internal int GetBootLockCnt() { return bootSemaphore.CurrentCount; } internal IReadOnlyList Profiles => profiles.AsReadOnly(); internal LoadingBalanceService LoadingBalanceService => _loadingBalanceService; internal ProfileHandler ProfileHandler => profileHandler; internal readonly List> InitActions = new List>(); internal readonly List> LateInitActions = new List>(); public async Task StartAsync(CancellationToken cancellationToken) { GlobalConfig.DenyModelNames = await webDbService.GetDenyModelNames(cancellationToken); Start(); return; } public Task StopAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } internal void UpdateClientDisplayPrice(string key,string price) { clientDic[key].DisplayPrice = price; } internal void SendMsg(WsClientData session, string msg, string messageType, string errorMsg = "") { Send(session,msg,messageType,errorMsg); } internal void Start() { Console.WriteLine("Starting Server..."); if (!GlobalConfig.LoadAPPConfig(configuration)) { Console.WriteLine("Please check App.Config setting ."); return; } StartWsService(); //OpenNetwork(); //RunHttpConsoleService(); return; if (!isInDocker) { Task consoleReadTask = new Task(RunConsoleInteractive); consoleReadTask.Start(); //RunConsoleInteractive(); return; } } private void StartWsService() { websocketService.NewSessionConnected += AppServer_NewSessionConnected; } private void StopWsService() { websocketService.NewSessionConnected -= AppServer_NewSessionConnected; } private async void AppServer_NewSessionConnected(object sender, WsClientData session) { logger.LogDebug(string.Format("{0} NewSessionConnected", session.Path)); try { bool isNotSupported = session.SecWebSocketProtocol.Contains("ocpp1.6") ? false : session.SecWebSocketProtocol.Contains("ocpp2.0") ? false : true; if (isNotSupported) { //logger.LogDebug(string.Format("ChargeBoxId:{0} SecWebSocketProtocol:{1} NotSupported", session.ChargeBoxId, session.SecWebSocketProtocol)); WriteMachineLog(session, string.Format("SecWebSocketProtocol:{0} NotSupported", session.SecWebSocketProtocol), "Connection", ""); return; } TryRemoveDuplicatedSession(session); clientDic[session.ChargeBoxId] = session; session.SessionClosed += AppServer_SessionClosed; session.m_ReceiveData += ReceivedMessageTimeLimited; // logger.LogDebug("------------New " + (session == null ? "Oops" : session.ChargeBoxId)); WriteMachineLog(session, "NewSessionConnected", "Connection", ""); await mainDbService.UpdateMachineConnectionType(session.ChargeBoxId, session.UriScheme.Contains("wss") ? 2 : 1); await webDbService.UpdateProtocalVersion(session.ChargeBoxId, session.DisconnetCancellationToken); } catch (Exception ex) { logger.LogError(string.Format("NewSessionConnected Ex: {0}", ex.ToString())); } } private void AppServer_SessionClosed(object sender, string closeReason) { if (sender is not WsClientData session) { return; } //session.SessionClosed -= AppServer_SessionClosed; //session.m_ReceiveData -= ReceivedMessageTimeLimited; //WriteMachineLog(session, string.Format("CloseReason: {0}", closeReason), "Connection", ""); RemoveClient(session, closeReason); } private void TryRemoveDuplicatedSession(WsClientData session) { if (clientDic.ContainsKey(session.ChargeBoxId)) { var oldSession = clientDic[session.ChargeBoxId]; //WriteMachineLog(oldSession, "Duplicate Logins", "Connection", ""); RemoveClient(oldSession, "Duplicate Logins"); } } private void RunConsoleInteractive() { while (true) { if (Console.In is null) { break; } var input = Console.ReadLine(); switch (input.ToLower()) { case "stop": Console.WriteLine("Command stop"); Stop(); break; case "gc": Console.WriteLine("Command GC"); GC.Collect(); break; case "lc": { Console.WriteLine("Command List Clients"); Dictionary _copyClientDic = null; _copyClientDic = new Dictionary(clientDic); var list = _copyClientDic.Select(c => c.Value).ToList(); int i = 1; foreach (var c in list) { Console.WriteLine(i + ":" + c.ChargeBoxId + " " + c.SessionID); i++; } } break; case "lcn": { Console.WriteLine("Command List Customer Name"); Dictionary _copyClientDic = null; _copyClientDic = new Dictionary(clientDic); var lcn = clientDic.Select(c => c.Value.CustomerName).Distinct().ToList(); int iLcn = 1; foreach (var c in lcn) { Console.WriteLine(iLcn + ":" + c + ":" + clientDic.Where(z => z.Value.CustomerName == c).Count().ToString()); iLcn++; } } break; case "help": Console.WriteLine("Command help!!"); Console.WriteLine("lcn : List Customer Name"); Console.WriteLine("gc : GC Collect"); Console.WriteLine("lc : List Clients"); Console.WriteLine("cls : clear console"); Console.WriteLine("silent : silent"); Console.WriteLine("show : show log"); // logger.Info("rcl : show Real Connection Limit"); break; case "cls": Console.WriteLine("Command clear"); Console.Clear(); break; case "silent": Console.WriteLine("Command silent"); //var xe = XElement.Load("NLog.config"); //var xns = xe.GetDefaultNamespace(); //var minlevelattr = xe.Descendants(xns + "rules").Elements(xns + "logger") // .Where(c => c.Attribute("writeTo").Value.Equals("console")).Attributes("minlevel").FirstOrDefault(); //if (minlevelattr != null) //{ // minlevelattr.Value = "Warn"; //} //xe.Save("NLog.config"); foreach (var rule in LogManager.Configuration.LoggingRules) { if (rule.RuleName != "ConsoleLog") { continue; } var isTargetRule = rule.Targets.Any(x => x.Name.ToLower() == "console"); if (isTargetRule) { rule.SetLoggingLevels(NLog.LogLevel.Warn, NLog.LogLevel.Off); } } break; case "show": Console.WriteLine("Command show"); //var xe1 = XElement.Load("NLog.config"); //var xns1 = xe1.GetDefaultNamespace(); //var minlevelattr1 = xe1.Descendants(xns1 + "rules").Elements(xns1 + "logger") // .Where(c => c.Attribute("writeTo").Value.Equals("console")).Attributes("minlevel").FirstOrDefault(); //if (minlevelattr1 != null) //{ // minlevelattr1.Value = "trace"; //} //xe1.Save("NLog.config"); foreach (var rule in LogManager.Configuration.LoggingRules) { if (rule.RuleName != "ConsoleLog") { continue; } var isTargetRule = rule.Targets.Any(x => x.Name.ToLower() == "console"); if (isTargetRule) { rule.SetLoggingLevels(NLog.LogLevel.Trace, NLog.LogLevel.Off); } } break; case "rcl": break; default: break; } } } internal void Stop() { _cts?.Cancel(); } async private void ReceivedMessageTimeLimited(object sender, string rawdata) { if (sender is not WsClientData session) { return; } CancellationTokenSource tokenSource = new(); var task = ReceivedMessage(session, rawdata); var completedTask = await Task.WhenAny(task, Task.Delay(90_000, tokenSource.Token)); if (completedTask != task) { logger.LogCritical("Process timeout: {0} ", rawdata); await task; return; } tokenSource.Cancel(); return; } async private Task ReceivedMessage(WsClientData session, string rawdata) { try { BasicMessageHandler msgAnalyser = new BasicMessageHandler(); MessageResult analysisResult = msgAnalyser.AnalysisReceiveData(session, rawdata); WriteMachineLog(session, rawdata, string.Format("{0} {1}", string.IsNullOrEmpty(analysisResult.Action) ? "unknown" : analysisResult.Action, analysisResult.Id == 2 ? "Request" : (analysisResult.Id == 3 ? "Confirmation" : "Error")), analysisResult.Exception == null ? "" : analysisResult.Exception.Message); if (session.ResetSecurityProfile) { logger.LogError(string.Format("[{0}] ChargeBoxId:{1} ResetSecurityProfile", DateTime.UtcNow, session.ChargeBoxId)); RemoveClient(session, "ResetSecurityProfile"); return; } if (!analysisResult.Success) { //解析RawData就發生錯誤 if (!string.IsNullOrEmpty(analysisResult.CallErrorMsg)) { Send(session, analysisResult.CallErrorMsg, string.Format("{0} {1}", analysisResult.Action, "Error")); } else { if (analysisResult.Message == null) { string replyMsg = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = string.Empty; if (analysisResult.Exception != null) { errorMsg = analysisResult.Exception.ToString(); } Send(session, replyMsg, string.Format("{0} {1}", "unknown", "Error"), "EVSE's sent essage has parsed Failed. "); } else { BaseMessage _baseMsg = analysisResult.Message as BaseMessage; string replyMsg = BasicMessageHandler.GenerateCallError(_baseMsg.Id, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = string.Empty; if (analysisResult.Exception != null) { errorMsg = analysisResult.Exception.ToString(); } Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } } } else { switch (analysisResult.Id) { case BasicMessageHandler.TYPENUMBER_CALL: { if (!session.ISOCPP20) { Actions action = Convertor.GetAction(analysisResult.Action); try { await ProcessRequestMessage(analysisResult, session, action); } catch (Exception e) { logger.LogError($"Processing {action} exception!"); throw; } } else { EVCB_OCPP20.Packet.Features.Actions action = Convertor.GetActionby20(analysisResult.Action); MessageResult result = new MessageResult() { Success = true }; //ocpp20 處理 switch (action) { case EVCB_OCPP20.Packet.Features.Actions.BootNotification: { EVCB_OCPP20.Packet.Messages.BootNotificationRequest _request = (EVCB_OCPP20.Packet.Messages.IRequest)analysisResult.Message as EVCB_OCPP20.Packet.Messages.BootNotificationRequest; var confirm = new EVCB_OCPP20.Packet.Messages.BootNotificationResponse() { CurrentTime = DateTime.UtcNow, Interval = 180, Status = EVCB_OCPP20.Packet.DataTypes.EnumTypes.RegistrationStatusEnumType.Pending }; result.Message = confirm; result.Success = true; string response = BasicMessageHandler.GenerateConfirmationofOCPP20(analysisResult.UUID, (EVCB_OCPP20.Packet.Messages.IConfirmation)result.Message); Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Response"), result.Exception == null ? string.Empty : result.Exception.ToString()); var request = new EVCB_OCPP20.Packet.Messages.SetNetworkProfileRequest() { ConfigurationSlot = 1, ConnectionData = new EVCB_OCPP20.Packet.DataTypes.NetworkConnectionProfileType() { OcppVersion = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPVersionEnumType.OCPP20, OcppTransport = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPTransportEnumType.JSON, MessageTimeout = 30, OcppCsmsUrl = session.UriScheme == "ws" ? GlobalConfig.OCPP20_WSUrl : GlobalConfig.OCPP20_WSSUrl, OcppInterface = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPInterfaceEnumType.Wired0 } }; var uuid = session.queue20.store(request); string requestText = BasicMessageHandler.GenerateRequestofOCPP20(uuid, "SetNetworkProfile", request); Send(session, requestText, "SetNetworkProfile"); } break; default: { logger.LogError(string.Format("We don't implement messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId)); } break; } } } break; case BasicMessageHandler.TYPENUMBER_CALLRESULT: { if (!session.ISOCPP20) { Actions action = Convertor.GetAction(analysisResult.Action); ProcessConfirmationMessage(analysisResult, session, action); } else { EVCB_OCPP20.Packet.Features.Actions action = Convertor.GetActionby20(analysisResult.Action); MessageResult result = new MessageResult() { Success = true }; //ocpp20 處理 switch (action) { case EVCB_OCPP20.Packet.Features.Actions.SetNetworkProfile: { EVCB_OCPP20.Packet.Messages.SetNetworkProfileResponse response = (EVCB_OCPP20.Packet.Messages.IConfirmation)analysisResult.Message as EVCB_OCPP20.Packet.Messages.SetNetworkProfileResponse; if (response.Status == EVCB_OCPP20.Packet.DataTypes.EnumTypes.SetNetworkProfileStatusEnumType.Accepted) { var request = new EVCB_OCPP20.Packet.Messages.SetVariablesRequest() { SetVariableData = new List() { new EVCB_OCPP20.Packet.DataTypes.SetVariableDataType() { Component=new EVCB_OCPP20.Packet.DataTypes.ComponentType() { Name="OCPPCommCtrlr", }, AttributeType= EVCB_OCPP20.Packet.DataTypes.EnumTypes.AttributeEnumType.Actual, AttributeValue= JsonConvert.SerializeObject(new List(){ 1 }), Variable=new EVCB_OCPP20.Packet.DataTypes.VariableType() { Name="NetworkConfigurationPriority", } } } }; var uuid = session.queue20.store(request); string requestText = BasicMessageHandler.GenerateRequestofOCPP20(uuid, "SetVariables", request); Send(session, requestText, "SetVariables"); } } break; case EVCB_OCPP20.Packet.Features.Actions.SetVariables: { EVCB_OCPP20.Packet.Messages.SetVariablesResponse response = (EVCB_OCPP20.Packet.Messages.IConfirmation)analysisResult.Message as EVCB_OCPP20.Packet.Messages.SetVariablesResponse; if (response.SetVariableResult[0].AttributeStatus == EVCB_OCPP20.Packet.DataTypes.EnumTypes.SetVariableStatusEnumType.RebootRequired) { var request = new EVCB_OCPP20.Packet.Messages.ResetRequest() { Type = EVCB_OCPP20.Packet.DataTypes.EnumTypes.ResetEnumType.OnIdle }; var uuid = session.queue20.store(request); string requestText = BasicMessageHandler.GenerateRequestofOCPP20(uuid, "Reset", request); Send(session, requestText, "Reset"); } } break; default: { logger.LogError(string.Format("We don't implement messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId)); } break; } } } break; case BasicMessageHandler.TYPENUMBER_CALLERROR: { //只處理 丟出Request 收到Error的訊息 if (analysisResult.Success && analysisResult.Message != null) { Actions action = Convertor.GetAction(analysisResult.Action); ProcessErrorMessage(analysisResult, session, action); } } break; default: { logger.LogError(string.Format("Can't analyze messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId)); } break; } } } catch (Exception ex) { if (ex.InnerException != null) { logger.LogError(string.Format("{0} **Inner Exception :{1} ", session.ChargeBoxId + rawdata, ex.ToString())); } else { logger.LogError(string.Format("{0} **Exception :{1} ", session.ChargeBoxId, ex.ToString())); } } finally { await Task.Delay(10); } } private async Task ProcessRequestMessage(MessageResult analysisResult, WsClientData session, Actions action) { Stopwatch outter_stopwatch = Stopwatch.StartNew(); //BasicMessageHandler msgAnalyser = new BasicMessageHandler(); if (!session.IsCheckIn && action != Actions.BootNotification) { if (analysisResult.Message is IRequest request && !request.TransactionRelated()) { string response = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.GenericError, OCPPErrorDescription.NotChecked); Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Error")); } } else { var profileName = profiles.Where(x => x.IsExisted(analysisResult.Action)).Select(x => x.Name).FirstOrDefault(); switch (profileName) { case "Core": { var replyResult = await profileHandler.ExecuteCoreRequest(action, session, (IRequest)analysisResult.Message).ConfigureAwait(false); var sendTimer = Stopwatch.StartNew(); if (replyResult.Success) { string response = BasicMessageHandler.GenerateConfirmation(analysisResult.UUID, (IConfirmation)replyResult.Message); Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Confirmation"), replyResult.Exception == null ? string.Empty : replyResult.Exception.ToString()); if (action == Actions.BootNotification && replyResult.Message is BootNotificationConfirmation bootNotificationConfirmation && analysisResult.Message is BootNotificationRequest bootNotificationRequest) { session.ChargePointVendor = bootNotificationRequest.chargePointVendor; if (session.BootStatus == BootStatus.Startup ) { //session.BootStatus = BootStatus.Pending; session.BootStatus = BootStatus.Initializing; session.AddTask(StartInitializeEVSE(session)); } if (bootNotificationConfirmation.status == Packet.Messages.SubTypes.RegistrationStatus.Accepted ) { session.IsCheckIn = true; //session.AddTask(StartAllInitializeEVSE(session)); session.AddTask(StartLateInitializeEVSE(session)); //await confirmWaitingMessageSerevice.SendAndWaitUntilResultAsync(sendTask, session.DisconnetCancellationToken); } } if (action == Actions.Authorize && replyResult.Message is AuthorizeConfirmation) { var authorizeRequest = (IRequest)analysisResult.Message as AuthorizeRequest; if (session.UserDisplayPrices.ContainsKey(authorizeRequest.idTag)) { await messageService.SendDataTransferRequest( session.ChargeBoxId, messageId: "SetUserPrice", vendorId: "Phihong Technology", data: JsonConvert.SerializeObject( new { idToken = authorizeRequest.idTag, price = session.UserDisplayPrices[authorizeRequest.idTag] }) ); } } } else { if (action == Actions.StopTransaction && replyResult.CallErrorMsg == "Reject Response Message") { //do nothing logger.LogWarning(replyResult.Exception.ToString()); } else { string response = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = replyResult.Exception != null ? replyResult.Exception.ToString() : string.Empty; Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } } sendTimer.Stop(); if(sendTimer.ElapsedMilliseconds/1000 > 1) { logger.LogCritical("ProcessRequestMessage Send Cost {time} sec", sendTimer.ElapsedMilliseconds / 1000); } if (action == Actions.StartTransaction) { var stationId = await _loadingBalanceService.GetStationIdByMachineId(session.MachineId); var _powerDic = await _loadingBalanceService.GetSettingPower(stationId); if (_powerDic != null) { foreach (var kv in _powerDic) { try { if (kv.Value.HasValue) { profileHandler.SetChargingProfile(kv.Key, kv.Value.Value, Packet.Messages.SubTypes.ChargingRateUnitType.W); } } catch (Exception ex) { logger.LogError(string.Format("Set Profile Exception: {0}", ex.ToString())); } } } } if (action == Actions.StopTransaction) { var stationId = await _loadingBalanceService.GetStationIdByMachineId(session.MachineId); var _powerDic = await _loadingBalanceService.GetSettingPower(stationId); if (_powerDic != null) { foreach (var kv in _powerDic) { try { if (kv.Value.HasValue) { profileHandler.SetChargingProfile(kv.Key, kv.Value.Value, Packet.Messages.SubTypes.ChargingRateUnitType.W); } } catch (Exception ex) { logger.LogError(string.Format("Set Profile Exception: {0}", ex.ToString())); } } } } } break; case "FirmwareManagement": { var replyResult = await profileHandler.ExecuteFirmwareManagementRequest(action, session, (IRequest)analysisResult.Message); if (replyResult.Success) { string response = BasicMessageHandler.GenerateConfirmation(analysisResult.UUID, (IConfirmation)replyResult.Message); Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Confirmation", replyResult.Exception == null ? string.Empty : replyResult.Exception.ToString())); } else { string response = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = replyResult.Exception != null ? replyResult.Exception.ToString() : string.Empty; Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } } break; case "Security": { var replyResult = profileHandler.ExecuteSecurityRequest(action, session, (IRequest)analysisResult.Message); if (replyResult.Success) { string response = BasicMessageHandler.GenerateConfirmation(analysisResult.UUID, (IConfirmation)replyResult.Message); Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Confirmation", replyResult.Exception == null ? string.Empty : replyResult.Exception.ToString())); } else { string response = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = replyResult.Exception != null ? replyResult.Exception.ToString() : string.Empty; Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } } break; default: { string replyMsg = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = string.Format("Couldn't find action name: {0} of profile", action); Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } break; } } outter_stopwatch.Stop(); if (outter_stopwatch.ElapsedMilliseconds > 1000) { logger.LogCritical("ProcessRequestMessage {action} too long {time} sec", action.ToString(), outter_stopwatch.ElapsedMilliseconds / 1000); } } async private void ProcessConfirmationMessage(MessageResult analysisResult, WsClientData session, Actions action) { BasicMessageHandler msgAnalyser = new BasicMessageHandler(); if (await confirmWaitingMessageSerevice.TryConfirmMessage(analysisResult)) { var profileName = profiles.Where(x => x.IsExisted(analysisResult.Action)).Select(x => x.Name).FirstOrDefault(); MessageResult confirmResult = null; switch (profileName) { case "Core": { confirmResult = await profileHandler.ExecuteCoreConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId); } break; case "FirmwareManagement": { confirmResult = await profileHandler.ExecuteFirmwareManagementConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId); } break; case "RemoteTrigger": { confirmResult = await profileHandler.ExecuteRemoteTriggerConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId); } break; case "Reservation": { confirmResult = await profileHandler.ExecuteReservationConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId); } break; case "LocalAuthListManagement": { confirmResult = await profileHandler.ExecuteLocalAuthListManagementConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId); } break; case "SmartCharging": { confirmResult = await profileHandler.ExecuteSmartChargingConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId); } break; case "Security": { confirmResult = profileHandler.ExecuteSecurityConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId); } break; default: { string replyMsg = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = string.Format("Couldn't find action name: {0} of profile", action); Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } break; } if (confirmResult == null || !confirmResult.Success) { logger.LogError(string.Format("Action:{0} MessageId:{1} ExecuteConfirm Error:{2} ", analysisResult.Action, analysisResult.UUID, confirmResult.Exception.ToString())); } } else { string replyMsg = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = string.Format("Action:{0} MessageId:{1} didn't exist in confirm message", analysisResult.Action, analysisResult.UUID); Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } } private async void ProcessErrorMessage(MessageResult analysisResult, WsClientData session, Actions action) { BasicMessageHandler msgAnalyser = new BasicMessageHandler(); if (await confirmWaitingMessageSerevice.TryConfirmMessage(analysisResult)) { var profileName = profiles.Where(x => x.IsExisted(analysisResult.Action)).Select(x => x.Name).FirstOrDefault(); switch (profileName) { case "Core": { _ = profileHandler.ReceivedCoreError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId); } break; case "FirmwareManagement": { _ = profileHandler.ReceivedFirmwareManagementError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId); } break; case "RemoteTrigger": { _ = profileHandler.ReceivedRemoteTriggerError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId); } break; case "Reservation": { _ = profileHandler.ExecuteReservationError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId); } break; case "LocalAuthListManagement": { _ = profileHandler.ReceivedLocalAuthListManagementError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId); } break; case "SmartCharging": { _ = profileHandler.ReceivedSmartChargingError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId); } break; default: { string replyMsg = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = string.Format("Couldn't find action name: {0} of profile", action); Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } break; } } else { string replyMsg = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError); string errorMsg = string.Format("Action:{0} MessageId:{1} didn't exist in confirm message", analysisResult.Action, analysisResult.UUID); Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg); } } private async Task StartAllInitializeEVSE(WsClientData session) { await bootSemaphore.WaitAsync(); try { await InitializeEVSE(session); await LateInitializeEVSE(session); } catch (Exception e) { logger.LogCritical("StartAllInitializeEVSE:{errormsg}", e.Message); logger.LogCritical("StartAllInitializeEVSE:{errorStackTrace}", e.StackTrace); } finally { bootSemaphore.Release(); } } private async Task StartInitializeEVSE(WsClientData session) { await bootSemaphore.WaitAsync(); try { await InitializeEVSE(session); } catch (Exception e) { logger.LogCritical("StartInitializeEVSE:{errormsg}", e.Message); logger.LogCritical("StartInitializeEVSE:{errorStackTrace}", e.StackTrace); } finally { session.BootStatus = BootStatus.Pending; bootSemaphore.Release(); } } private async Task InitializeEVSE(WsClientData session) { // Pending mode 下發設定 string connectorType = await mainDbService.GetMachineConnectorType(session.ChargeBoxId, session.DisconnetCancellationToken); if (!string.IsNullOrEmpty(connectorType) && (connectorType.Contains("6") || connectorType.Contains("7") || connectorType.Contains("8") || connectorType.Contains("9"))) { session.IsAC = false; } string requestId = string.Empty; var displayPriceText = await webDbService.SetDefaultFee(session); UpdateClientDisplayPrice(session.ChargeBoxId, displayPriceText); Func> sendTask; sendTask = async (string serialNo, CancellationToken token) => await messageService.SendGetEVSEConfigureRequest(session.ChargeBoxId, serialNo: serialNo, token: token); var response = await confirmWaitingMessageSerevice.SendAndWaitUntilResultAsync(sendTask, token: session.DisconnetCancellationToken); if (response is GetConfigurationConfirmation getConfigurationConfirmation) { session.Data[GlobalConfig.BootData_EVSEConfig_Key] = getConfigurationConfirmation.configurationKey; } if (!string.IsNullOrEmpty(displayPriceText)) { sendTask = async (string serialNo, CancellationToken token) => await messageService.SendChangeConfigurationRequest( session.ChargeBoxId, key: "DefaultPrice", value: displayPriceText, serialNo: serialNo); await confirmWaitingMessageSerevice.SendAndWaitResultAsync(sendTask, token: session.DisconnetCancellationToken); } if (session.CustomerId == new Guid("298918C0-6BB5-421A-88CC-4922F918E85E") || session.CustomerId == new Guid("9E6BFDCC-09FB-4DAB-A428-43FE507600A3")) { await messageService.SendChangeConfigurationRequest( session.ChargeBoxId, key: "TimeOffset", value: "+08:00"); } if (session.CustomerId == new Guid("D57D5BCC-C5B0-4031-A7AE-7516E00CB028")) { await messageService.SendChangeConfigurationRequest( session.ChargeBoxId, key: "StopTransactionOnInvalidId", value: "True"); } //foreach (var initFunction in InitActions) for (var index = 0; index < InitActions.Count; index++) { var initFunction = InitActions[index]; await initFunction(session, session.DisconnetCancellationToken); } session.Data.Remove(GlobalConfig.BootData_EVSEConfig_Key);//= getConfigurationConfirmation.configurationKey; //await StationConfigService?.CheckAndUpdateEvseConfig(session, session.DisconnetCancellationToken); } private async Task StartLateInitializeEVSE(WsClientData session) { await WaitCanStartLateInitEVSE(session.DisconnetCancellationToken); try { //await Task.Delay(TimeSpan.FromMinutes(5)); await LateInitializeEVSE(session); } catch (Exception e) { logger.LogCritical("StartLateInitializeEVSE:{errormsg}", e.Message); logger.LogCritical("StartLateInitializeEVSE:{errorStackTrace}", e.StackTrace); } finally { //session.BootStatus = BootStatus.Pending; bootSemaphore.Release(); } } private async Task WaitCanStartLateInitEVSE(CancellationToken token) { bool passed = false; do { while (bootSemaphore.CurrentCount < bootReservCnt) { await Task.Delay(TimeSpan.FromMinutes(2), cancellationToken: token); } passed = bootSemaphore.Wait(0); } while (!passed); } private async Task LateInitializeEVSE(WsClientData session) { Func> sendTask; sendTask = async (string serialNo, CancellationToken token) => await messageService.SendDataTransferRequest( session.ChargeBoxId, messageId: "ID_FirmwareVersion", vendorId: "Phihong Technology", data: string.Empty, serialNo: serialNo, token: token); await confirmWaitingMessageSerevice.SendAndWaitResultAsync(sendTask, token: session.DisconnetCancellationToken); sendTask = async (string serialNo, CancellationToken token) => await messageService.SendTriggerMessageRequest( session.ChargeBoxId, messageTrigger: MessageTrigger.DiagnosticsStatusNotification, serialNo: serialNo, token: token); await confirmWaitingMessageSerevice.SendAndWaitResultAsync(sendTask, token: session.DisconnetCancellationToken); for (var index = 0; index < LateInitActions.Count; index++) { var lateInitFunction = LateInitActions[index]; await lateInitFunction(session, session.DisconnetCancellationToken); } } private void Send(WsClientData session, string msg, string messageType, string errorMsg = "") { try { if (session != null) { WriteMachineLog(session, msg, messageType, errorMsg, true); session.Send(msg); } } catch (Exception ex) { logger.LogError(string.Format("Send Ex:{0}", ex.ToString())); } } internal async void RemoveClient(WsClientData session, string reason) { if (session == null) { return; } if (!string.IsNullOrEmpty(session.MachineId)) logger.LogTrace("RemoveClient[{0}]:{1}", session.ChargeBoxId, reason); WriteMachineLog(session, string.Format("CloseReason: {0}", reason), "Connection", ""); //if (session.Connected) //{ // session.Close(CloseReason.ServerShutdown); //} RemoveClientDic(session); try { session.SessionClosed -= AppServer_SessionClosed; session.m_ReceiveData -= ReceivedMessageTimeLimited; if (session.State == WebSocketState.Open) { await session.Close(); } // session.Close(CloseReason.ServerShutdown); } catch (Exception ex) { //logger.LogWarning("Close client socket error!!"); logger.LogWarning(string.Format("Close client socket error!! {0} Msg:{1}", session.ChargeBoxId, ex.Message)); } if (session != null) { session = null; } } private void RemoveClientDic(WsClientData session) { if (string.IsNullOrEmpty(session.ChargeBoxId)) { return; } if (clientDic.ContainsKey(session.ChargeBoxId)) { if (clientDic[session.ChargeBoxId].SessionID == session.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.LogTrace("RemoveClient ContainsKey " + session.ChargeBoxId); } } } private void WarmUpLog() { connectionLogdbService.WarmUpLog(); } private void WriteMachineLog(WsClientData WsClientData, string data, string messageType, string errorMsg = "", bool isSent = false) { try { if (WsClientData == null || string.IsNullOrEmpty(data)) return; if (WsClientData.ChargeBoxId == null) { logger.LogCritical(WsClientData.Path.ToString() + "]********************session ChargeBoxId null sessionId=" + WsClientData.SessionID); } connectionLogdbService.WriteMachineLog(WsClientData, data, messageType, errorMsg, isSent); } catch (Exception ex) { //Console.WriteLine(ex.ToString()); logger.LogError(ex,ex.Message); } } } }