Browse Source

build pass

Robert 1 năm trước cách đây
mục cha
commit
b0399edda5

+ 3 - 0
EVCB_OCPP.WSServer/HostedProtalServer.cs

@@ -6,6 +6,7 @@ using EVCB_OCPP.WSServer.Service;
 using EVCB_OCPP.WSServer.SuperSocket;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
+using OCPPServer.Protocol;
 using Quartz;
 
 namespace EVCB_OCPP.WSServer
@@ -20,6 +21,8 @@ namespace EVCB_OCPP.WSServer
             services.AddPortalServerDatabase(configuration);
             services.AddBusinessServiceFactory();
 
+            services.AddSingleton<WebsocketService<WsClientData>>();
+
             services.AddTransient<OCPPWSServer>();
             services.AddTransient<IOCPPWSServerFactory, OCPPWSServerFactory>();
 

+ 1 - 1
EVCB_OCPP.WSServer/Jobs/DenyModelCheckJob.cs

@@ -48,7 +48,7 @@ public class DenyModelCheckJob : IJob
                 return;
             }
 
-            Dictionary<string, ClientData> _copyClientDic = protalServer.GetClientDic();
+            Dictionary<string, WsClientData> _copyClientDic = protalServer.GetClientDic();
             foreach (var denyName in GlobalConfig.DenyModelNames)
             {
                 var removeClients = _copyClientDic.Where(x => x.Key.StartsWith(denyName)).Select(x => x.Value).ToList();

+ 1 - 1
EVCB_OCPP.WSServer/Jobs/HealthCheckTriggerJob.cs

@@ -28,7 +28,7 @@ public class HealthCheckTriggerJob : IJob
     {
         //logger.LogDebug("{0} Started", nameof(HealthCheckTriggerJob));
 
-        Dictionary<string, ClientData> _copyClientDic = protalServer.GetClientDic();
+        Dictionary<string, WsClientData> _copyClientDic = protalServer.GetClientDic();
 
         var removeClients = _copyClientDic.Where(x => x.Value.LastActiveTime < DateTime.UtcNow.AddSeconds(-300)).Select(x => x.Value).ToList();
 

+ 1 - 1
EVCB_OCPP.WSServer/Jobs/HeartBeatCheckJob.cs

@@ -40,7 +40,7 @@ public class HeartBeatCheckJob : IJob
         try
         {
             Stopwatch watch = new Stopwatch();
-            Dictionary<string, ClientData> _copyClientDic = protalServer.GetClientDic();
+            Dictionary<string, WsClientData> _copyClientDic = protalServer.GetClientDic();
 
             var cdt = DateTime.UtcNow;
             var clients = _copyClientDic.Where(x => x.Value.LastActiveTime > cdt.AddSeconds(-120)).Select(x => x.Value).ToList();

+ 2 - 2
EVCB_OCPP.WSServer/Jobs/ServerMessageJob.cs

@@ -66,7 +66,7 @@ public class ServerMessageJob : IJob
         var resendList = protalServer.GetResendMessage();
         foreach (var resendItem in resendList)
         {
-            ClientData session;
+            WsClientData session;
             if (clientDic.TryGetValue(resendItem.ChargePointSerialNumber, out session))
             {
                 if (dateTimeNow.Subtract(resendItem.SentOn).TotalSeconds > 1)
@@ -81,7 +81,7 @@ public class ServerMessageJob : IJob
         }
         foreach (var charger_SN in cmdMachineList)
         {
-            ClientData session;
+            WsClientData session;
             string uuid = string.Empty;
 
             if (!clientDic.TryGetValue(charger_SN, out session))

+ 2 - 2
EVCB_OCPP.WSServer/Jobs/ServerSetFeeJob.cs

@@ -55,13 +55,13 @@ public class ServerSetFeeJob : IJob
     {
         //logger.LogDebug("{0} Started", nameof(ServerSetFeeJob));
         //BasicMessageHandler msgAnalyser = new BasicMessageHandler();
-        Dictionary<string, ClientData> _copyClientDic = protalServer.GetClientDic();
+        Dictionary<string, WsClientData> _copyClientDic = protalServer.GetClientDic();
         //using var db = maindbContextFactory.CreateDbContextAsync();
         foreach (var item in _copyClientDic)
         {
             try
             {
-                ClientData session = item.Value;
+                WsClientData session = item.Value;
                 if (!session.IsCheckIn)
                 {
                     continue;

+ 2 - 2
EVCB_OCPP.WSServer/Jobs/ServerUpdateJob.cs

@@ -35,7 +35,7 @@ public class ServerUpdateJob : IJob
     {
         //logger.LogDebug("{0} Started", nameof(ServerUpdateJob));
         BasicMessageHandler msgAnalyser = new BasicMessageHandler();
-        Dictionary<string, ClientData> _copyClientDic = protalServer.GetClientDic();
+        Dictionary<string, WsClientData> _copyClientDic = protalServer.GetClientDic();
         var checkUpdateDt = DateTime.UtcNow;
         List<string> needUpdateChargers = new List<string>();
         using (var db = await maindbContextFactory.CreateDbContextAsync())
@@ -53,7 +53,7 @@ public class ServerUpdateJob : IJob
         {
             try
             {
-                ClientData session;
+                WsClientData session;
                 if (_copyClientDic.TryGetValue(chargeBoxId, out session))
                 {
 

+ 1 - 1
EVCB_OCPP.WSServer/Message/BasicMessageHandler.cs

@@ -49,7 +49,7 @@ namespace EVCB_OCPP.WSServer.Message
         /// <param name="client"></param>
         /// <param name="data"></param>
         /// <returns></returns>
-        internal MessageResult AnalysisReceiveData(ClientData client, string data)
+        internal MessageResult AnalysisReceiveData(WsClientData client, string data)
         {
             MessageResult result = null;
             if (!client.ISOCPP20)

+ 3 - 3
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -99,7 +99,7 @@ public partial class ProfileHandler
         this.httpClient = httpClient;
     }
 
-    async internal Task<MessageResult> ExecuteCoreRequest(Actions action, ClientData session, IRequest request)
+    async internal Task<MessageResult> ExecuteCoreRequest(Actions action, WsClientData session, IRequest request)
     {
         Stopwatch watch = new Stopwatch();
         //if (action == Actions.Heartbeat || action == Actions.StopTransaction)
@@ -882,7 +882,7 @@ public partial class ProfileHandler
         return result;
     }
 
-    async internal Task<MessageResult> ExecuteCoreConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+    async internal Task<MessageResult> ExecuteCoreConfirm(Actions action, WsClientData session, IConfirmation confirm, string requestId)
     {
         MessageResult result = new MessageResult() { Success = true };
 
@@ -1571,7 +1571,7 @@ public partial class ProfileHandler
     }
 
 
-    internal async Task<MessageResult> ReceivedCoreError(Actions action, string errorMsg, ClientData session, string requestId)
+    internal async Task<MessageResult> ReceivedCoreError(Actions action, string errorMsg, WsClientData session, string requestId)
     {
         MessageResult result = new MessageResult() { Success = true };
 

+ 3 - 3
EVCB_OCPP.WSServer/Message/FirmwareManagementProfileHandler.cs

@@ -15,7 +15,7 @@ namespace EVCB_OCPP.WSServer.Message
 {
     public partial class ProfileHandler
     {
-        internal async Task<MessageResult> ExecuteFirmwareManagementRequest(Actions action, ClientData session, IRequest request)
+        internal async Task<MessageResult> ExecuteFirmwareManagementRequest(Actions action, WsClientData session, IRequest request)
         {
             MessageResult result = new MessageResult() { Success = false };
             try
@@ -139,7 +139,7 @@ namespace EVCB_OCPP.WSServer.Message
             return result;
         }
 
-        internal async Task<MessageResult> ExecuteFirmwareManagementConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        internal async Task<MessageResult> ExecuteFirmwareManagementConfirm(Actions action, WsClientData session, IConfirmation confirm, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 
@@ -181,7 +181,7 @@ namespace EVCB_OCPP.WSServer.Message
         }
 
 
-        internal async Task<MessageResult> ReceivedFirmwareManagementError(Actions action, string errorMsg, ClientData session, string requestId)
+        internal async Task<MessageResult> ReceivedFirmwareManagementError(Actions action, string errorMsg, WsClientData session, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 

+ 2 - 2
EVCB_OCPP.WSServer/Message/LocalAuthListManagementProfileHandler.cs

@@ -13,7 +13,7 @@ namespace EVCB_OCPP.WSServer.Message
 {
     public partial class ProfileHandler
     {
-        internal async Task<MessageResult> ExecuteLocalAuthListManagementConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        internal async Task<MessageResult> ExecuteLocalAuthListManagementConfirm(Actions action, WsClientData session, IConfirmation confirm, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 
@@ -69,7 +69,7 @@ namespace EVCB_OCPP.WSServer.Message
         }
 
 
-        internal async Task<MessageResult> ReceivedLocalAuthListManagementError(Actions action, string errorMsg, ClientData session, string requestId)
+        internal async Task<MessageResult> ReceivedLocalAuthListManagementError(Actions action, string errorMsg, WsClientData session, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 

+ 1 - 1
EVCB_OCPP.WSServer/Message/OCPP16MessageHandler.cs

@@ -60,7 +60,7 @@ namespace EVCB_OCPP.WSServer.Message
         /// <param name="client"></param>
         /// <param name="data"></param>
         /// <returns></returns>
-        internal MessageResult AnalysisReceiveData(ClientData client, string data)
+        internal MessageResult AnalysisReceiveData(WsClientData client, string data)
         {
             MessageResult result = new MessageResult();
             try

+ 1 - 1
EVCB_OCPP.WSServer/Message/OCPP20MessageHandler.cs

@@ -52,7 +52,7 @@ namespace EVCB_OCPP.WSServer.Message
         /// <param name="client"></param>
         /// <param name="data"></param>
         /// <returns></returns>
-        internal MessageResult AnalysisReceiveData(ClientData client, string data)
+        internal MessageResult AnalysisReceiveData(WsClientData client, string data)
         {
             MessageResult result = new MessageResult();
             try

+ 2 - 2
EVCB_OCPP.WSServer/Message/RemoteTriggerHandler.cs

@@ -15,7 +15,7 @@ namespace EVCB_OCPP.WSServer.Message
     {
 
 
-        internal async Task<MessageResult> ExecuteRemoteTriggerConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        internal async Task<MessageResult> ExecuteRemoteTriggerConfirm(Actions action, WsClientData session, IConfirmation confirm, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 
@@ -51,7 +51,7 @@ namespace EVCB_OCPP.WSServer.Message
         }
 
 
-        internal async Task<MessageResult> ReceivedRemoteTriggerError(Actions action, string errorMsg, ClientData session, string requestId)
+        internal async Task<MessageResult> ReceivedRemoteTriggerError(Actions action, string errorMsg, WsClientData session, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 

+ 2 - 2
EVCB_OCPP.WSServer/Message/ReservationProfileHandler.cs

@@ -15,7 +15,7 @@ namespace EVCB_OCPP.WSServer.Message
     {
 
 
-        internal async Task<MessageResult> ExecuteReservationConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        internal async Task<MessageResult> ExecuteReservationConfirm(Actions action, WsClientData session, IConfirmation confirm, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 
@@ -71,7 +71,7 @@ namespace EVCB_OCPP.WSServer.Message
         }
 
 
-        internal async Task<MessageResult> ExecuteReservationError(Actions action, string errorMsg, ClientData session, string requestId)
+        internal async Task<MessageResult> ExecuteReservationError(Actions action, string errorMsg, WsClientData session, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 

+ 2 - 2
EVCB_OCPP.WSServer/Message/SecurityProfileHandler.cs

@@ -8,7 +8,7 @@ namespace EVCB_OCPP.WSServer.Message
 {
     public partial class ProfileHandler
     {
-        internal MessageResult ExecuteSecurityRequest(Actions action, ClientData session, IRequest request)
+        internal MessageResult ExecuteSecurityRequest(Actions action, WsClientData session, IRequest request)
         {
             MessageResult result = new MessageResult() { Success = false };
 
@@ -37,7 +37,7 @@ namespace EVCB_OCPP.WSServer.Message
 
             return result;
         }
-        internal MessageResult ExecuteSecurityConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        internal MessageResult ExecuteSecurityConfirm(Actions action, WsClientData session, IConfirmation confirm, string requestId)
         {
             MessageResult result = new MessageResult() { Success = false };
 

+ 2 - 2
EVCB_OCPP.WSServer/Message/SmartChargingProfileHandler.cs

@@ -70,7 +70,7 @@ namespace EVCB_OCPP.WSServer.Message
             });
         }
 
-        internal async Task<MessageResult> ExecuteSmartChargingConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        internal async Task<MessageResult> ExecuteSmartChargingConfirm(Actions action, WsClientData session, IConfirmation confirm, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 
@@ -148,7 +148,7 @@ namespace EVCB_OCPP.WSServer.Message
         }
 
 
-        internal async Task<MessageResult> ReceivedSmartChargingError(Actions action, string errorMsg, ClientData session, string requestId)
+        internal async Task<MessageResult> ReceivedSmartChargingError(Actions action, string errorMsg, WsClientData session, string requestId)
         {
             MessageResult result = new MessageResult() { Success = true };
 

+ 8 - 5
EVCB_OCPP.WSServer/Program.cs

@@ -4,6 +4,7 @@ using Microsoft.Extensions.Hosting;
 using Newtonsoft.Json;
 using EVCB_OCPP.WSServer.Helper;
 using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
 
 namespace EVCB_OCPP.WSServer
 {
@@ -35,7 +36,8 @@ namespace EVCB_OCPP.WSServer
             Console.WriteLine($"Max ThreadPool workerThreads:{workerThreads} completionThreads:{completionThreads}");
             ThreadPool.SetMinThreads((int)(10), (int)(0));
 
-            IHost host = Host.CreateDefaultBuilder(args)
+            var builder = WebApplication.CreateBuilder(args);
+            builder.Host
                 .AddLogServcie()
                 .ConfigureServices((hostContext, services) =>
                 {
@@ -46,10 +48,11 @@ namespace EVCB_OCPP.WSServer
 
                     //services.AddTransient<BlockingTreePrintService>();
                     //services.AddTransient<GoogleGetTimePrintService>();
-                })
-                .Build();
-
-            host.Run();
+                });
+            var app = builder.Build();
+            app.MapWsService();
+            app.MapApiServce();
+            app.Run();
         }
 
         public static object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

+ 235 - 182
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -25,6 +25,10 @@ using NLog.Extensions.Logging;
 using System.Collections.Concurrent;
 using EVCB_OCPP.WSServer.SuperSocket;
 using Microsoft.Extensions.Logging;
+using System.Net.WebSockets;
+using SuperWebSocket;
+using System.Net;
+using System.Text;
 
 namespace EVCB_OCPP.WSServer
 {
@@ -56,8 +60,9 @@ namespace EVCB_OCPP.WSServer
             , LoadingBalanceService loadingBalanceService
             , ServerMessageService serverMessageService
             , WebDbService webDbService
-            , ProfileHandler profileHandler,
-            OuterHttpClient httpClient)
+            , ProfileHandler profileHandler
+            , WebsocketService<WsClientData> websocketService
+            , OuterHttpClient httpClient)
         {
             this.logger = logger;
             this.configuration = configuration;
@@ -69,6 +74,7 @@ namespace EVCB_OCPP.WSServer
             isInDocker = !string.IsNullOrEmpty(configuration["DOTNET_RUNNING_IN_CONTAINER"]);
 
             this.profileHandler = profileHandler;// new ProfileHandler(configuration, serviceProvider);
+            this.websocketService = websocketService;
             this._loadingBalanceService = loadingBalanceService;// new LoadingBalanceService(mainDbConnectionFactory, webDbConnectionFactory);
             this.messageService = serverMessageService;
             WarmUpLog();
@@ -77,7 +83,7 @@ namespace EVCB_OCPP.WSServer
         #region private fields
         private OuterHttpClient httpClient;
         private DateTime lastcheckdt = DateTime.UtcNow.AddSeconds(-20);
-        private ConcurrentDictionary<string, ClientData> clientDic = new ConcurrentDictionary<string, ClientData>();
+        private ConcurrentDictionary<string, WsClientData> clientDic = new ConcurrentDictionary<string, WsClientData>();
         //private readonly Object _lockClientDic = new object();
         private readonly Object _lockConfirmPacketList = new object();
         private readonly ILogger<ProtalServer> logger;
@@ -88,6 +94,7 @@ namespace EVCB_OCPP.WSServer
         private readonly IConnectionLogdbService connectionLogdbService;
         private readonly WebDbService webDbService;
         private readonly ProfileHandler profileHandler;
+        private readonly WebsocketService<WsClientData> websocketService;
         private readonly bool isInDocker;
         private List<NeedConfirmMessage> needConfirmPacketList = new List<NeedConfirmMessage>();
         private DateTime checkUpdateDt = DateTime.UtcNow;
@@ -138,10 +145,10 @@ namespace EVCB_OCPP.WSServer
         private WebApplication appApi;
         private WebApplication yarpApp;
 
-        internal Dictionary<string, ClientData> GetClientDic()
+        internal Dictionary<string, WsClientData> GetClientDic()
         {
-            Dictionary<string, ClientData> toReturn = null;
-            toReturn = new Dictionary<string, ClientData>(clientDic);
+            Dictionary<string, WsClientData> toReturn = null;
+            toReturn = new Dictionary<string, WsClientData>(clientDic);
             return toReturn;
         }
 
@@ -179,7 +186,7 @@ namespace EVCB_OCPP.WSServer
             clientDic[key].DisplayPrice = price;
         }
 
-        internal void SendMsg(ClientData session, string msg, string messageType, string errorMsg = "")
+        internal void SendMsg(WsClientData session, string msg, string messageType, string errorMsg = "")
         {
             Send(session,msg,messageType,errorMsg);
         }
@@ -196,8 +203,6 @@ namespace EVCB_OCPP.WSServer
             
             StartWsService();
 
-            StartHttpConsoleService();
-
             if (!isInDocker)
             {
                 Task consoleReadTask = new Task(RunConsoleInteractive);
@@ -233,8 +238,8 @@ namespace EVCB_OCPP.WSServer
                     case "lc":
                         {
                             Console.WriteLine("Command List Clients");
-                            Dictionary<string, ClientData> _copyClientDic = null;
-                            _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+                            Dictionary<string, WsClientData> _copyClientDic = null;
+                            _copyClientDic = new Dictionary<string, WsClientData>(clientDic);
                             var list = _copyClientDic.Select(c => c.Value).ToList();
                             int i = 1;
                             foreach (var c in list)
@@ -247,8 +252,8 @@ namespace EVCB_OCPP.WSServer
                     case "lcn":
                         {
                             Console.WriteLine("Command List Customer Name");
-                            Dictionary<string, ClientData> _copyClientDic = null;
-                            _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+                            Dictionary<string, WsClientData> _copyClientDic = null;
+                            _copyClientDic = new Dictionary<string, WsClientData>(clientDic);
                             var lcn = clientDic.Select(c => c.Value.CustomerName).Distinct().ToList();
                             int iLcn = 1;
                             foreach (var c in lcn)
@@ -336,136 +341,6 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        private void StartHttpConsoleService()
-        {
-            var appBuilder = WebApplication.CreateBuilder();
-            //appBuilder.Services.AddSingleton<IHostLifetime, DummyHostLifeTime>();
-             
-            appApi = appBuilder.Build();
-
-            var helpFunc = () => {
-                return string.Join("\r\n", new[] { 
-                    "Command help!!",
-                    "lcn : List Customer Name",
-                    "gc : GC Collect",
-                    "lc : List Clients",
-                    "silent : silent",
-                    "show : show log"
-                });
-            };
-            appApi.MapGet("/", helpFunc);
-            appApi.MapGet("/help", helpFunc);
-
-            appApi.MapPost("/stop", () => {
-                Stop();
-                return "Command stop";
-            });
-
-            appApi.MapPost("/gc", () => {
-                GC.Collect();
-                return "Command GC";
-            });
-
-            appApi.MapPost("/lc", () => {
-                List<string> toReturn = new List<string>() { "Command List Clients" };
-                Dictionary<string, ClientData> _copyClientDic = null;
-                _copyClientDic = new Dictionary<string, ClientData>(clientDic);
-                var list = _copyClientDic.Select(c => c.Value).ToList();
-                int i = 1;
-                foreach (var c in list)
-                {
-                    toReturn.Add(i + ":" + c.ChargeBoxId + " " + c.SessionID);
-                    i++;
-                }
-                return string.Join("\r\n", toReturn);
-            });
-
-            appApi.MapPost("/lcn", () => {
-                List<string> toReturn = new List<string> { "Command List Customer Name" };
-                Dictionary<string, ClientData> _copyClientDic = null;
-                _copyClientDic = new Dictionary<string, ClientData>(clientDic);
-                var lcn = clientDic.Select(c => c.Value.CustomerName).Distinct().ToList();
-                int iLcn = 1;
-                foreach (var c in lcn)
-                {
-                    toReturn.Add(iLcn + ":" + c + ":" + clientDic.Where(z => z.Value.CustomerName == c).Count().ToString());
-                    iLcn++;
-                }
-                return string.Join("\r\n", toReturn);
-            });
-
-            appApi.MapPost("/silent", () => {
-                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);
-                    }
-                }
-                return "Command silent";
-            });
-
-            appApi.MapPost("/show", () => {
-                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);
-                    }
-                }
-                return "Command show";
-            });
-
-            appApi.MapGet("/threads", () => {
-                ThreadPool.GetMaxThreads(out var maxWorkerThread,out var maxCompletionPortThreads);
-                ThreadPool.GetAvailableThreads(out var avaliableWorkerThread, out var avaliableCompletionPortThreads);
-                return $"WorkerThread:{avaliableWorkerThread}/{maxWorkerThread} CompletionPortThreads{avaliableCompletionPortThreads}/{maxCompletionPortThreads}";
-            });
-
-            appApi.MapPost("/threads", (int min, int max) => {
-                ThreadPool.GetMaxThreads(out var maxWorkerThread, out var maxCompletionPortThreads);
-                ThreadPool.GetAvailableThreads(out var avaliableWorkerThread, out var avaliableCompletionPortThreads); 
-                ThreadPool.SetMinThreads(min, 0);
-                ThreadPool.SetMaxThreads(max, maxCompletionPortThreads);
-                return $"WorkerThread:{avaliableWorkerThread}/{maxWorkerThread} CompletionPortThreads{avaliableCompletionPortThreads}/{maxCompletionPortThreads}";
-            });
-
-            appApi.Urls.Add("http://*:54088");
-
-            _ = appApi.RunAsync();
-
-            var builder = WebApplication.CreateBuilder();
-            //builder.Configuration.AddJsonFile("./EVCB_OCPP.WSServer/appsettings.json");
-            //var sec = builder.Configuration.GetSection("ReverseProxy");
-            //Console.WriteLine($"Printing.............");
-            //foreach (var pair in sec.AsEnumerable())
-            //{
-            //    Console.WriteLine($"{pair.Key}:{pair.Value} \n");
-            //}
-
-            builder.Services.AddReverseProxy()
-                .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
-            //builder.Services.AddSingleton<IHostLifetime, DummyHostLifeTime>();
-            yarpApp = builder.Build();
-            yarpApp.Urls.Add("http://*:80");
-            yarpApp.MapReverseProxy();
-            _ = yarpApp.RunAsync();
-        }
-
         internal Task Stop()
         {
             appServer?.Stop();
@@ -479,26 +354,26 @@ namespace EVCB_OCPP.WSServer
         {
 
             //載入OCPP Protocol
-            appServer = ocppWSServerFactory.Create(new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") });
+            //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>();
+            //List<IListenerConfig> llistener = new List<IListenerConfig>();
 
-            if (GlobalConfig.GetWS_Port() != 0)
-            {
-                llistener.Add(new ListenerConfig { Ip = System.Net.IPAddress.Any.ToString(), Port = GlobalConfig.GetWS_Port(), Backlog = 100, Security = "None" });
-            }
+            //if (GlobalConfig.GetWS_Port() != 0)
+            //{
+            //    llistener.Add(new ListenerConfig { Ip = System.Net.IPAddress.Any.ToString(), Port = GlobalConfig.GetWS_Port(), Backlog = 100, Security = "None" });
+            //}
 
-            foreach (var securityport in GlobalConfig.GetWSS_Ports())
-            {
-                llistener.Add(new ListenerConfig { Ip = System.Net.IPAddress.Any.ToString(), Port = securityport, Backlog = 100, Security = SslProtocols.Tls12.ToString() });
-            }
+            //foreach (var securityport in GlobalConfig.GetWSS_Ports())
+            //{
+            //    llistener.Add(new ListenerConfig { Ip = System.Net.IPAddress.Any.ToString(), Port = securityport, Backlog = 100, Security = SslProtocols.Tls12.ToString() });
+            //}
 
             //var config = ConfigurationManager.GetSection("superSocket") as IConfigurationSource;\
             //var certificate = configuration.GetSection("superSocket").GetSection("Servers:0").GetSection("Certificate").Get<CertificateConfig>();
-            var certificate = configuration.GetSection("SuperSocketServerCertificate").Get<CertificateConfig>();
-            ICertificateConfig Certificate = certificate;
-            IEnumerable<IListenerConfig> listeners = llistener;
+            //var certificate = configuration.GetSection("SuperSocketServerCertificate").Get<CertificateConfig>();
+            //ICertificateConfig Certificate = certificate;
+            //IEnumerable<IListenerConfig> listeners = llistener;
 
             //設定server config
             var serverConfig = new ServerConfig
@@ -509,7 +384,7 @@ namespace EVCB_OCPP.WSServer
                 MaxRequestLength = 204800,
                 //Security = serverSecurity,
                 //Certificate = Certificate,
-                Listeners = listeners,
+                //Listeners = listeners,
                 //  LogAllSocketException = true,
                 KeepAliveTime = 10,
                 // LogBasicSessionActivity = true
@@ -517,15 +392,16 @@ namespace EVCB_OCPP.WSServer
             };
 
             //Setup with listening port
-            if (!appServer.Setup(serverConfig, logFactory: new NLogLoggerFactory()))
-            {
-                //Console.WriteLine("Failed to setup!");
-                logger.LogCritical("Failed to setup!");
-                return;
-            }
+            //if (!appServer.Setup(serverConfig, logFactory: new NLogLoggerFactory()))
+            //{
+            //    //Console.WriteLine("Failed to setup!");
+            //    logger.LogCritical("Failed to setup!");
+            //    return;
+            //}
 
-            appServer.NewSessionConnected += AppServer_NewSessionConnected;
-            appServer.SessionClosed += AppServer_SessionClosed;
+            websocketService.ValidateHandshake = WebsocketServiceValidateHandshake;
+            websocketService.NewSessionConnected += AppServer_NewSessionConnected;
+            websocketService.SessionClosed += AppServer_SessionClosed;
 
 
             //Try to start the appServer
@@ -537,13 +413,185 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        private void AppServer_SessionClosed(ClientData session, CloseReason value)
+        private async Task<bool> WebsocketServiceValidateHandshake(WsClientData session)
         {
-            WriteMachineLog(session, string.Format("CloseReason: {0}", value), "Connection", "");
-            RemoveClient(session);
+            session.ISOCPP20 = session.SecWebSocketProtocol.ToLower().Contains("ocpp2.0");
+
+            int securityProfile = 0;
+            string authorizationKey = string.Empty;
+            if (string.IsNullOrEmpty(session.Path))
+            {
+                //logger.Log();
+                logger.LogWarning("===========================================");
+                logger.LogWarning("session.Path EMPTY");
+                logger.LogWarning("===========================================");
+            }
+
+            string[] words = session.Path.ToString().Split('/');
+            session.ChargeBoxId = words.Last();
+
+            foreach (var denyModel in GlobalConfig.DenyModelNames)
+            {
+                if (string.IsNullOrEmpty(denyModel))
+                {
+                    continue;
+                }
+
+                if (session.ChargeBoxId.StartsWith(denyModel))
+                {
+
+                    StringBuilder responseBuilder = new StringBuilder();
+
+                    responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+                    (int)HttpStatusCode.Unauthorized, @"Unauthorized");
+
+                    responseBuilder.AppendWithCrCf();
+                    string sb = responseBuilder.ToString();
+                    byte[] data = Encoding.UTF8.GetBytes(sb);
+
+                    ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
+                    logger.LogTrace(sb);
+                    return false;
+                }
+            }
+
+            if (configuration["MaintainMode"] == "1")
+            {
+                session.ChargeBoxId = session.ChargeBoxId + "_2";
+            }
+
+            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)
+            {
+                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;
+            }
+
+            //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")
+            {
+                StringBuilder responseBuilder = new StringBuilder();
+
+                responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+                (int)HttpStatusCode.Unauthorized, @"Unauthorized");
+
+                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")
+                {
+                    authorizated = false;
+                }
+
+                //if (session.Items.ContainsKey("Authorization") || session.Items.ContainsKey("authorization"))
+                if (!string.IsNullOrEmpty(session.AuthHeader))
+                {
+                    //authorizationKey = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.AuthorizationKey)
+                    //                    .Select(x => x.ConfigureSetting).FirstOrDefault();
+                    authorizationKey = await mainDbService.GetMachineAuthorizationKey(session.ChargeBoxId);
+
+                    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 ", "");
+                        string base64Encoded = session.AuthHeader.Replace("Basic ", "");
+                        byte[] data = Convert.FromBase64String(base64Encoded);
+                        string[] base64Decoded = Encoding.ASCII.GetString(data).Split(':');
+                        logger.LogInformation("***********Authorization   " + Encoding.ASCII.GetString(data));
+                        if (base64Decoded.Count() == 2 && base64Decoded[0] == session.ChargeBoxId && base64Decoded[1] == authorizationKey)
+                        {
+                            authorizated = true;
+                        }
+                    }
+
+
+
+
+
+                }
+                else
+                {
+                    authorizated = true;
+
+                }
+
+
+
+                if (!authorizated)
+                {
+                    StringBuilder responseBuilder = new StringBuilder();
+
+                    responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+                    (int)HttpStatusCode.Unauthorized, @"Unauthorized");
+
+                    responseBuilder.AppendWithCrCf();
+                    string sb = responseBuilder.ToString();
+                    byte[] data = Encoding.UTF8.GetBytes(sb);
+
+                    ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
+                    logger.LogInformation(sb);
+                    return false;
+                }
+            }
+
+
+
+
+
+            logger.LogInformation(string.Format("ValidateHandshake PASS: {0}", session.Path));
+            return true;
         }
 
-        private async void AppServer_NewSessionConnected(ClientData session)
+        private async void AppServer_NewSessionConnected(object sender, WsClientData session)
         {
             logger.LogDebug(string.Format("{0} NewSessionConnected", session.Path));
 
@@ -571,11 +619,16 @@ namespace EVCB_OCPP.WSServer
             {
                 logger.LogError(string.Format("NewSessionConnected Ex: {0}", ex.ToString()));
             }
+        }
 
-
+        private void AppServer_SessionClosed(object sender, WsClientData session)
+        {
+            CloseReason value = CloseReason.ServerShutdown;
+            WriteMachineLog(session, string.Format("CloseReason: {0}", value), "Connection", "");
+            RemoveClient(session);
         }
 
-        private void TryRemoveDuplicatedSession(ClientData session)
+        private void TryRemoveDuplicatedSession(WsClientData session)
         {
             if (clientDic.ContainsKey(session.ChargeBoxId))
             {
@@ -586,7 +639,7 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        async private void ReceivedMessageTimeLimited(ClientData session, string rawdata)
+        async private void ReceivedMessageTimeLimited(WsClientData session, string rawdata)
         {
             CancellationTokenSource tokenSource = new();
             var task = ReceivedMessage(session, rawdata);
@@ -603,7 +656,7 @@ namespace EVCB_OCPP.WSServer
             return;
         }
 
-        async private Task ReceivedMessage(ClientData session, string rawdata)
+        async private Task ReceivedMessage(WsClientData session, string rawdata)
         {
             try
             {
@@ -846,7 +899,7 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        private async Task ProcessRequestMessage(MessageResult analysisResult, ClientData session, Actions action)
+        private async Task ProcessRequestMessage(MessageResult analysisResult, WsClientData session, Actions action)
         {
             Stopwatch outter_stopwatch = Stopwatch.StartNew();
             //BasicMessageHandler msgAnalyser = new BasicMessageHandler();
@@ -1038,7 +1091,7 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        async private void ProcessConfirmationMessage(MessageResult analysisResult, ClientData session, Actions action)
+        async private void ProcessConfirmationMessage(MessageResult analysisResult, WsClientData session, Actions action)
         {
 
             BasicMessageHandler msgAnalyser = new BasicMessageHandler();
@@ -1107,7 +1160,7 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        private async void ProcessErrorMessage(MessageResult analysisResult, ClientData session, Actions action)
+        private async void ProcessErrorMessage(MessageResult analysisResult, WsClientData session, Actions action)
         {
             BasicMessageHandler msgAnalyser = new BasicMessageHandler();
             if (await ReConfirmMessage(analysisResult))
@@ -1165,7 +1218,7 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        private void Send(ClientData session, string msg, string messageType, string errorMsg = "")
+        private void Send(WsClientData session, string msg, string messageType, string errorMsg = "")
         {
             try
             {
@@ -1257,7 +1310,7 @@ namespace EVCB_OCPP.WSServer
 
 
 
-        internal void RemoveClient(ClientData session)
+        internal void RemoveClient(WsClientData session)
         {
             if (session == null)
             {
@@ -1267,7 +1320,7 @@ namespace EVCB_OCPP.WSServer
             if (!string.IsNullOrEmpty(session.MachineId))
                 logger.LogTrace("RemoveClient[" + session.ChargeBoxId + "]");
 
-            if (session.Connected)
+            if (session.State == WebSocketState.Open)
             {
                 session.Close(CloseReason.ServerShutdown);
             }
@@ -1290,7 +1343,7 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        private void RemoveClientDic(ClientData session)
+        private void RemoveClientDic(WsClientData session)
         {
             if (string.IsNullOrEmpty(session.ChargeBoxId))
             {
@@ -1315,7 +1368,7 @@ namespace EVCB_OCPP.WSServer
             connectionLogdbService.WarmUpLog();
         }
 
-        private void WriteMachineLog(ClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false)
+        private void WriteMachineLog(WsClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false)
         {
             try
             {
@@ -1324,7 +1377,7 @@ namespace EVCB_OCPP.WSServer
 
                 if (clientData.ChargeBoxId == null)
                 {
-                    logger.LogCritical(clientData.Path + "]********************session ChargeBoxId null sessionId=" + clientData.SessionID);
+                    logger.LogCritical(clientData.Path.ToString() + "]********************session ChargeBoxId null sessionId=" + clientData.SessionID);
                 }
 
                 connectionLogdbService.WriteMachineLog(clientData, data, messageType, errorMsg, isSent);

+ 8 - 8
EVCB_OCPP.WSServer/Service/ConnectionLogdbService.cs

@@ -23,7 +23,7 @@ namespace EVCB_OCPP.WSServer.Service;
 public interface IConnectionLogdbService
 {
     void WarmUpLog();
-    void WriteMachineLog(ClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false);
+    void WriteMachineLog(WsClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false);
 }
 
 public class ConnectionLogdbService : IConnectionLogdbService
@@ -72,7 +72,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
         }
     }
 
-    public void WriteMachineLog(ClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false)
+    public void WriteMachineLog(WsClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false)
     {
         var log = new MachineLog(clientData, data, messageType, errorMsg, isSent);
         //queueHandler.Enqueue(log);
@@ -113,7 +113,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
         parameters.Add("Data", log.data.Replace("'", "''"), DbType.String);
         parameters.Add("Msg", log.errorMsg.Replace("'", "''"), DbType.String, size: 200);
         parameters.Add("IsSent", log.isSent, DbType.Boolean);
-        parameters.Add("EVSEEndPoint", log.clientData.RemoteEndPoint == null ? "123" : log.clientData.RemoteEndPoint.ToString(), DbType.String, size: 25);
+        parameters.Add("EVSEEndPoint", log.clientData.Endpoint == null ? "123" : log.clientData.Endpoint.ToString(), DbType.String, size: 25);
         parameters.Add("Session", log.clientData.SessionID == null ? "123" : log.clientData.SessionID, DbType.String, size: 36);
 
         t1 = watch.ElapsedMilliseconds;
@@ -155,7 +155,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
 
             if (log.clientData.ChargeBoxId == null)
             {
-                logger.LogCritical(log.clientData.Path + "]********************session ChargeBoxId null sessionId=" + log.clientData.SessionID);
+                logger.LogCritical(log.clientData.Path.ToString() + "]********************session ChargeBoxId null sessionId=" + log.clientData.SessionID);
             }
             string sp = "[dbo].[uspInsertMachineConnectionLog] @CreatedOn," +
                   "@ChargeBoxId,@MessageType,@Data,@Msg,@IsSent,@EVSEEndPoint,@Session";
@@ -168,7 +168,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
                     new SqlParameter("Data", SqlDbType.NVarChar, -1) { Value = log.data.Replace("'", "''") },
                     new SqlParameter("Msg", SqlDbType.NVarChar, 200) { Value = log.errorMsg.Replace("'", "''") },
                     new  SqlParameter("IsSent", SqlDbType.Bit) { Value = log.isSent },
-                    new  SqlParameter("EVSEEndPoint", SqlDbType.NVarChar, 25) { Value = log.clientData.RemoteEndPoint == null ? "123" : log.clientData.RemoteEndPoint.ToString() },
+                    new  SqlParameter("EVSEEndPoint", SqlDbType.NVarChar, 25) { Value = log.clientData.Endpoint == null ? "123" : log.clientData.Endpoint.ToString() },
                     new  SqlParameter("Session", SqlDbType.NVarChar, 36) { Value = log.clientData.SessionID == null ? "123" : log.clientData.SessionID }
             };
             using (var db = await connectionLogdbContextFactory.CreateDbContextAsync())
@@ -238,7 +238,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
             parameters.Add("Data", log.data.Replace("'", "''"), DbType.String);
             parameters.Add("Msg", log.errorMsg.Replace("'", "''"), DbType.String, size: 200);
             parameters.Add("IsSent", log.isSent, DbType.Boolean);
-            parameters.Add("EVSEEndPoint", log.clientData.RemoteEndPoint == null ? "123" : log.clientData.RemoteEndPoint.ToString(), DbType.String, size: 25);
+            parameters.Add("EVSEEndPoint", log.clientData.Endpoint == null ? "123" : log.clientData.Endpoint.ToString(), DbType.String, size: 25);
             parameters.Add("Session", log.clientData.SessionID == null ? "123" : log.clientData.SessionID, DbType.String, size: 36);
 
             await sqlConnection.ExecuteAsync(command, parameters
@@ -297,7 +297,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
             row["Data"] = param.data.Replace("'", "''");
             row["Msg"] = param.errorMsg.Replace("'", "''");
             row["IsSent"] = param.isSent;
-            row["EVSEEndPoint"] = param.clientData.RemoteEndPoint == null ? "123" : param.clientData.RemoteEndPoint.ToString();
+            row["EVSEEndPoint"] = param.clientData.Endpoint == null ? "123" : param.clientData.Endpoint.ToString();
             row["Session"] = param.clientData.SessionID == null ? "123" : param.clientData.SessionID;
 
             table.Rows.Add(row);
@@ -369,4 +369,4 @@ public class ConnectionLogdbService : IConnectionLogdbService
     }
 }
 
-internal record MachineLog(ClientData clientData, string data, string messageType, string errorMsg, bool isSent);
+internal record MachineLog(WsClientData clientData, string data, string messageType, string errorMsg, bool isSent);

+ 115 - 0
EVCB_OCPP.WSServer/Service/MapApiServce.cs

@@ -0,0 +1,115 @@
+using Microsoft.AspNetCore.Builder;
+using NLog;
+using OCPPServer.Protocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Service;
+
+public static class MapApiServceExtention
+{
+    public static void MapApiServce(this WebApplication webApplication)
+    {
+        var helpFunc = () => {
+            return string.Join("\r\n", new[] {
+                    "Command help!!",
+                    "lcn : List Customer Name",
+                    "gc : GC Collect",
+                    "lc : List Clients",
+                    "silent : silent",
+                    "show : show log"
+                });
+        };
+        webApplication.MapGet("/", helpFunc);
+        webApplication.MapGet("/help", helpFunc);
+
+        webApplication.MapPost("/stop", (ProtalServer server) => {
+            server.Stop();
+            return "Command stop";
+        });
+
+        webApplication.MapPost("/gc", () => {
+            GC.Collect();
+            return "Command GC";
+        });
+
+        webApplication.MapPost("/lc", (ProtalServer server) => {
+            List<string> toReturn = new List<string>() { "Command List Clients" };
+            Dictionary<string, WsClientData> clientDic = server.GetClientDic();
+            var list = clientDic.Select(c => c.Value).ToList();
+            int i = 1;
+            foreach (var c in list)
+            {
+                toReturn.Add(i + ":" + c.ChargeBoxId + " " + c.SessionID);
+                i++;
+            }
+            return string.Join("\r\n", toReturn);
+        });
+
+        webApplication.MapPost("/lcn", (ProtalServer server) => {
+            List<string> toReturn = new List<string> { "Command List Customer Name" };
+            Dictionary<string, WsClientData> clientDic = server.GetClientDic();
+            var lcn = clientDic.Select(c => c.Value.CustomerName).Distinct().ToList();
+            int iLcn = 1;
+            foreach (var c in lcn)
+            {
+                toReturn.Add(iLcn + ":" + c + ":" + clientDic.Where(z => z.Value.CustomerName == c).Count().ToString());
+                iLcn++;
+            }
+            return string.Join("\r\n", toReturn);
+        });
+
+        webApplication.MapPost("/silent", () => {
+            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);
+                }
+            }
+            return "Command silent";
+        });
+
+        webApplication.MapPost("/show", () => {
+            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);
+                }
+            }
+            return "Command show";
+        });
+
+        webApplication.MapGet("/threads", () => {
+            ThreadPool.GetMaxThreads(out var maxWorkerThread, out var maxCompletionPortThreads);
+            ThreadPool.GetAvailableThreads(out var avaliableWorkerThread, out var avaliableCompletionPortThreads);
+            return $"WorkerThread:{avaliableWorkerThread}/{maxWorkerThread} CompletionPortThreads{avaliableCompletionPortThreads}/{maxCompletionPortThreads}";
+        });
+
+        webApplication.MapPost("/threads", (int min, int max) => {
+            ThreadPool.GetMaxThreads(out var maxWorkerThread, out var maxCompletionPortThreads);
+            ThreadPool.GetAvailableThreads(out var avaliableWorkerThread, out var avaliableCompletionPortThreads);
+            ThreadPool.SetMinThreads(min, 0);
+            ThreadPool.SetMaxThreads(max, maxCompletionPortThreads);
+            return $"WorkerThread:{avaliableWorkerThread}/{maxWorkerThread} CompletionPortThreads{avaliableCompletionPortThreads}/{maxCompletionPortThreads}";
+        });
+    }
+}

+ 1 - 1
EVCB_OCPP.WSServer/Service/WebDbService.cs

@@ -49,7 +49,7 @@ public class WebDbService
         return result.Split(',').ToList();
     }
 
-    internal async Task<string> SetDefaultFee(ClientData client)
+    internal async Task<string> SetDefaultFee(WsClientData client)
     {
         string displayPriceText = string.Empty;
         string charingPriceText = string.Empty;

+ 163 - 0
EVCB_OCPP.WSServer/Service/WebsocketService.cs

@@ -0,0 +1,163 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Primitives;
+using OCPPServer.Protocol;
+using SuperSocket.SocketBase;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.WebSockets;
+using System.ServiceModel.Channels;
+using System.Text;
+using System.Threading.Tasks;
+using static OCPPServer.Protocol.ClientData;
+
+namespace EVCB_OCPP.WSServer.Service;
+
+public static class AppExtention
+{
+    public static void MapWsService(this WebApplication webApplication)
+    {
+        var protocals = new List<string>() { "", "ocpp1.6", "ocpp2.0" };
+
+        webApplication.UseWebSockets(new WebSocketOptions()
+        {
+            KeepAliveInterval = TimeSpan.FromSeconds(10)
+        });
+
+        webApplication.Use(async (HttpContext context, RequestDelegate next) => {
+            if (context.WebSockets.IsWebSocketRequest)
+            {
+                var matched = context.WebSockets.WebSocketRequestedProtocols.Intersect(protocals);
+                if (matched is null || !matched.Any())
+                {
+                    await context.Response.WriteAsync("Protocol not matched");
+                    return;
+                }
+                using WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(matched.First());
+                var servcie = context.RequestServices.GetService<WebsocketService<WsClientData>>();
+                await servcie.AddWebSocket(webSocket, context);
+                return;
+            }
+            else
+            {
+                await next(context);
+            }
+        });
+    }
+}
+
+public interface IWebsocketService
+{
+    public Task AddWebSocket(WebSocket webSocket, HttpContext context);
+}
+
+public class WebsocketService<T> : IWebsocketService where T: WsSession
+{
+    public WebsocketService() { }
+
+    public Func<T,  Task<bool>> ValidateHandshake;
+
+    public event EventHandler<T> NewSessionConnected;
+
+    public event EventHandler<T> SessionClosed;
+
+    public async Task AddWebSocket(WebSocket webSocket, HttpContext context)
+    {
+        T data = Activator.CreateInstance<T>();
+        data.ClientWebSocket = webSocket;
+        data.Path = context?.Request?.Path;
+        data.UriScheme = context?.Request?.Scheme;
+        data.AuthHeader = context?.Request?.Headers?.Authorization;
+        data.SessionID = context.Session.Id;
+        data.Endpoint = context.GetEndpoint();
+        data.Origin = context.Request.Headers.Origin;
+
+        var validated = await ValidateHandshake(data);
+
+        if (!validated)
+        {
+            return;
+        }
+
+        NewSessionConnected?.Invoke(this, data);
+        await data.EndConnSemaphore.WaitAsync();
+        return;
+    }
+}
+
+public class WsSession
+{
+    public WsSession()
+    {
+
+    }
+
+    public PathString? Path { get; set; }
+    public string UriScheme { get; set; }
+    public string AuthHeader { get; set; }
+    public string SessionID { get; set; }
+    public Endpoint Endpoint { get; internal set; }
+    public StringValues Origin { get; internal set; }
+
+
+    public WebSocket _WebSocket { get; internal set; }
+    public WebSocket ClientWebSocket {
+        get => _WebSocket;
+        set
+        {
+            Init(value);
+        }
+    }
+
+    public WebSocketState State => ClientWebSocket.State;
+    public string SecWebSocketProtocol => ClientWebSocket.SubProtocol;
+
+    public SemaphoreSlim EndConnSemaphore { get; } = new SemaphoreSlim(0);
+
+    public event OCPPClientDataEventHandler<WsSession, String> m_ReceiveData;
+
+    public event EventHandler SessionClosed;
+
+    private Task ReceiveLoopTask;
+
+    private void Init(WebSocket webSocket)
+    {
+        ReceiveLoopTask = StartReceivd(webSocket);
+    }
+
+    private async Task StartReceivd(WebSocket webSocket)
+    {
+        while (true)
+        {
+            var buffer = new byte[1024 * 4];
+            var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), default);
+            if (result.CloseStatus.HasValue)
+            {
+                Close(CloseReason.ClientClosing);
+                break;
+            }
+            string received = Encoding.UTF8.GetString(buffer, 0, result.Count);
+            m_ReceiveData?.Invoke(this, received);
+        }
+    }
+
+    internal void Send(string dataString)
+    {
+        var data = Encoding.UTF8.GetBytes(dataString);
+        ClientWebSocket.SendAsync(data, WebSocketMessageType.Binary, endOfMessage: true, cancellationToken: default);
+    }
+
+    internal void Send(byte[] data, int offset, int length)
+    {
+        ClientWebSocket.SendAsync(data, WebSocketMessageType.Binary, endOfMessage: true, cancellationToken: default);
+    }
+
+    internal void Close(CloseReason closeReason)
+    {
+        SessionClosed?.Invoke(this, null);
+        _WebSocket.Dispose();
+        EndConnSemaphore.Release();
+    }
+}

+ 106 - 0
EVCB_OCPP.WSServer/SuperSocket.Protocol/ClientData.cs

@@ -7,9 +7,115 @@ using System.Collections.ObjectModel;
 using System.Threading.Tasks;
 using SuperWebSocket;
 using SuperSocket.SocketBase;
+using EVCB_OCPP.WSServer.Service;
+using System.Net.WebSockets;
 
 namespace OCPPServer.Protocol
 {
+    public class WsClientData : WsSession, IDisposable
+    { /// <summary>
+      /// 根據unique id來儲存.取出OCPP Request
+      /// </summary>
+        public Queue queue = new Queue();
+
+        public EVCB_OCPP20.Packet.Messages.Basic.Queue queue20 = new EVCB_OCPP20.Packet.Messages.Basic.Queue();
+
+        public bool? IsPending { set; get; }
+        public bool IsCheckIn { set; get; } = false;
+
+        public string ChargeBoxId { set; get; }
+
+        public Guid CustomerId { get; set; }
+
+        public string MachineId { set; get; }
+
+        public bool ISOCPP20 { set; get; }
+
+        public bool ResetSecurityProfile { set; get; }
+
+
+        public bool IsAC { set; get; } = true;
+
+        #region Billing
+
+        public Dictionary<string, string> UserPrices { set; get; } = new Dictionary<string, string>();
+
+        public Dictionary<string, string> UserDisplayPrices { set; get; } = new Dictionary<string, string>();
+
+        public List<ChargingPrice> ChargingPrices { set; get; }
+
+        /// <summary>
+        /// 電樁顯示費率
+        /// </summary>
+        public string DisplayPrice { set; get; }
+
+        /// <summary>
+        /// 充電費率 以小時計費
+        /// </summary>
+        public decimal ChargingFeebyHour { set; get; }
+
+        /// <summary>
+        /// 停車費率 以小時計費
+        /// </summary>
+        public decimal ParkingFee { set; get; }
+
+        /// <summary>
+        /// 電樁是否計費
+        /// </summary>
+        public bool IsBilling { set; get; }
+
+        /// <summary>
+        /// 收費方式 1: 以度計費 2:以小時計費
+        /// </summary>
+        public int BillingMethod { set; get; }
+
+
+        /// <summary>
+        /// 電樁適用幣別
+        /// </summary>
+        public string Currency { get; internal set; }
+
+        #endregion
+
+        public string CustomerName { get; set; }
+
+        public DateTime LastActiveTime { get; set; }
+
+        public string StationId { set; get; }
+
+        public new event ClientData.OCPPClientDataEventHandler<WsClientData, string> m_ReceiveData;
+
+
+        public WsClientData()
+        {
+            ChargeBoxId = SessionID;
+            MachineId = SessionID;
+
+            base.m_ReceiveData += WsClientData_m_ReceiveData;
+        }
+
+        /// <summary>
+        /// Sends the raw binary data to client.
+        /// </summary>
+        /// <param name="data">The data.</param>
+        /// <param name="offset">The offset.</param>
+        /// <param name="length">The length.</param>
+        public void SendRawData(byte[] data, int offset, int length)
+        {
+            base.Send(data, offset, length);
+        }
+
+        private void WsClientData_m_ReceiveData(WsSession clientdata, string msg)
+        {
+            m_ReceiveData.Invoke(this, msg);
+        }
+
+        public void Dispose()
+        {
+            base.m_ReceiveData -= WsClientData_m_ReceiveData;
+        }
+    }
+
     public class ClientData : WebSocketSession<ClientData>
     { /// <summary>
       /// 根據unique id來儲存.取出OCPP Request