소스 검색

chage base to asp.net original

Robert 1 년 전
부모
커밋
4ab1c97082
27개의 변경된 파일1166개의 추가작업 그리고 201개의 파일을 삭제
  1. 6 2
      EVCB_OCPP.WSServer/HostedProtalServer.cs
  2. 2 1
      EVCB_OCPP.WSServer/Jobs/DenyModelCheckJob.cs
  3. 2 1
      EVCB_OCPP.WSServer/Jobs/HealthCheckTriggerJob.cs
  4. 2 1
      EVCB_OCPP.WSServer/Jobs/HeartBeatCheckJob.cs
  5. 5 4
      EVCB_OCPP.WSServer/Jobs/ServerMessageJob.cs
  6. 4 3
      EVCB_OCPP.WSServer/Jobs/ServerSetFeeJob.cs
  7. 3 2
      EVCB_OCPP.WSServer/Jobs/ServerUpdateJob.cs
  8. 2 1
      EVCB_OCPP.WSServer/Message/BasicMessageHandler.cs
  9. 10 9
      EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs
  10. 4 3
      EVCB_OCPP.WSServer/Message/FirmwareManagementProfileHandler.cs
  11. 3 2
      EVCB_OCPP.WSServer/Message/LocalAuthListManagementProfileHandler.cs
  12. 2 1
      EVCB_OCPP.WSServer/Message/OCPP16MessageHandler.cs
  13. 2 1
      EVCB_OCPP.WSServer/Message/OCPP20MessageHandler.cs
  14. 3 2
      EVCB_OCPP.WSServer/Message/RemoteTriggerHandler.cs
  15. 3 2
      EVCB_OCPP.WSServer/Message/ReservationProfileHandler.cs
  16. 3 2
      EVCB_OCPP.WSServer/Message/SecurityProfileHandler.cs
  17. 3 2
      EVCB_OCPP.WSServer/Message/SmartChargingProfileHandler.cs
  18. 24 15
      EVCB_OCPP.WSServer/Program.cs
  19. 192 119
      EVCB_OCPP.WSServer/ProtalServer.cs
  20. 9 8
      EVCB_OCPP.WSServer/Service/ConnectionLogdbService.cs
  21. 65 0
      EVCB_OCPP.WSServer/Service/HeaderRecordService.cs
  22. 33 20
      EVCB_OCPP.WSServer/Service/MainDbService.cs
  23. 115 0
      EVCB_OCPP.WSServer/Service/MapApiServce.cs
  24. 251 0
      EVCB_OCPP.WSServer/Service/WsService/OcppWebsocketService.cs
  25. 107 0
      EVCB_OCPP.WSServer/Service/WsService/WebsocketService.cs
  26. 137 0
      EVCB_OCPP.WSServer/Service/WsService/WsClientData.cs
  27. 174 0
      EVCB_OCPP.WSServer/Service/WsService/WsSession.cs

+ 6 - 2
EVCB_OCPP.WSServer/HostedProtalServer.cs

@@ -1,9 +1,11 @@
 using EVCB_OCPP.Domain;
+using EVCB_OCPP.Service;
 using EVCB_OCPP.WSServer.Fake;
 using EVCB_OCPP.WSServer.Helper;
 using EVCB_OCPP.WSServer.Jobs;
 using EVCB_OCPP.WSServer.Message;
 using EVCB_OCPP.WSServer.Service;
+using EVCB_OCPP.WSServer.Service.WsService;
 using EVCB_OCPP.WSServer.SuperSocket;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Caching.Memory;
@@ -28,8 +30,10 @@ namespace EVCB_OCPP.WSServer
             services.AddPortalServerDatabase(configuration);
             services.AddBusinessServiceFactory();
 
-            services.AddTransient<OCPPWSServer>();
-            services.AddTransient<IOCPPWSServerFactory, OCPPWSServerFactory>();
+            //services.AddTransient<OCPPWSServer>();
+            //services.AddTransient<IOCPPWSServerFactory, OCPPWSServerFactory>();
+            services.AddHeaderRecordService();
+            services.AddOcppWsServer();
 
             services.AddSingleton<ServerMessageService>();
             services.AddSingleton<MeterValueDbService>();

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

@@ -1,6 +1,7 @@
 using Dapper;
 using DnsClient.Internal;
 using EVCB_OCPP.WSServer.Service;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.Data.SqlClient;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
@@ -48,7 +49,7 @@ public class DenyModelCheckJob : IJob
                 return;
             }
 
-            Dictionary<string, ClientData> _copyClientDic = protalServer.ClientDic;
+            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();

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

@@ -1,4 +1,5 @@
 using EVCB_OCPP.Domain;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.Extensions.Logging;
 using OCPPServer.Protocol;
 using Quartz;
@@ -28,7 +29,7 @@ public class HealthCheckTriggerJob : IJob
     {
         //logger.LogDebug("{0} Started", nameof(HealthCheckTriggerJob));
 
-        Dictionary<string, ClientData> _copyClientDic = protalServer.ClientDic;
+        Dictionary<string, WsClientData> _copyClientDic = protalServer.GetClientDic();
 
         var removeClients = _copyClientDic.Where(x => x.Value.LastActiveTime < DateTime.UtcNow.AddSeconds(-300)).Select(x => x.Value).ToList();
 

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

@@ -1,6 +1,7 @@
 using EVCB_OCPP.Domain;
 using EVCB_OCPP.Domain.Models.Database;
 using EVCB_OCPP.WSServer.Service;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
 using OCPPServer.Protocol;
@@ -40,7 +41,7 @@ public class HeartBeatCheckJob : IJob
         try
         {
             Stopwatch watch = new Stopwatch();
-            Dictionary<string, ClientData> _copyClientDic = protalServer.ClientDic;
+            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();

+ 5 - 4
EVCB_OCPP.WSServer/Jobs/ServerMessageJob.cs

@@ -3,6 +3,7 @@ using EVCB_OCPP.Domain.Models.Database;
 using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages;
 using EVCB_OCPP.WSServer.Message;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
@@ -59,7 +60,7 @@ public class ServerMessageJob : IJob
         DateTime dt = new DateTime(1991, 1, 1);
         DateTime currentTime = dateTimeNow;
         List<ServerMessage> commandList;
-        var clientDic = protalServer.ClientDic;
+        var clientDic = protalServer.GetClientDic();
 
         using (var db = await maindbContextFactory.CreateDbContextAsync())
         {
@@ -71,10 +72,10 @@ public class ServerMessageJob : IJob
         {
             // Console.WriteLine(string.Format("Now:{0} commandList Count:{1} ", DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss"), commandList.Count));
         }
-        var resendList = protalServer.ResendMessage;
+        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)
@@ -89,7 +90,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))

+ 4 - 3
EVCB_OCPP.WSServer/Jobs/ServerSetFeeJob.cs

@@ -7,6 +7,7 @@ using EVCB_OCPP.WSServer.Dto;
 using EVCB_OCPP.WSServer.Helper;
 using EVCB_OCPP.WSServer.Message;
 using EVCB_OCPP.WSServer.Service;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
@@ -52,13 +53,13 @@ public class ServerSetFeeJob : IJob
     {
         //logger.LogDebug("{0} Started", nameof(ServerSetFeeJob));
         //BasicMessageHandler msgAnalyser = new BasicMessageHandler();
-        Dictionary<string, ClientData> _copyClientDic = protalServer.ClientDic;
+        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;
@@ -91,7 +92,7 @@ public class ServerSetFeeJob : IJob
         }
     }
 
-    async private Task<string> SetDefaultFee(ClientData client)
+    async private Task<string> SetDefaultFee(WsClientData client)
     {
         string displayPriceText = string.Empty;
         string charingPriceText = string.Empty;

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

@@ -1,6 +1,7 @@
 using EVCB_OCPP.Domain;
 using EVCB_OCPP.Packet.Messages.RemoteTrigger;
 using EVCB_OCPP.WSServer.Message;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
 using OCPPServer.Protocol;
@@ -35,7 +36,7 @@ public class ServerUpdateJob : IJob
     {
         //logger.LogDebug("{0} Started", nameof(ServerUpdateJob));
         BasicMessageHandler msgAnalyser = new BasicMessageHandler();
-        Dictionary<string, ClientData> _copyClientDic = protalServer.ClientDic;
+        Dictionary<string, WsClientData> _copyClientDic = protalServer.GetClientDic();
         var checkUpdateDt = DateTime.UtcNow;
         List<string> needUpdateChargers = new List<string>();
         using (var db = await maindbContextFactory.CreateDbContextAsync())
@@ -53,7 +54,7 @@ public class ServerUpdateJob : IJob
         {
             try
             {
-                ClientData session;
+                WsClientData session;
                 if (_copyClientDic.TryGetValue(chargeBoxId, out session))
                 {
 

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

@@ -1,5 +1,6 @@
 using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.Extensions.Logging;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
@@ -49,7 +50,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)

+ 10 - 9
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -19,6 +19,7 @@ using System.Data;
 using System.Diagnostics;
 using System.Globalization;
 using SuperSocket.SocketBase;
+using EVCB_OCPP.WSServer.Service.WsService;
 
 namespace EVCB_OCPP.WSServer.Message;
 
@@ -63,8 +64,8 @@ public class ID_ReaderStatus
 internal partial class ProfileHandler
 {
     private readonly ILogger logger;
-    private readonly BlockingTreePrintService blockingTreePrintService;
-    private readonly GoogleGetTimePrintService googleGetTimePrintService;
+    //private readonly BlockingTreePrintService blockingTreePrintService;
+    //private readonly GoogleGetTimePrintService googleGetTimePrintService;
     private readonly ServerMessageService messageService;
 
     //private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings[].ConnectionString;
@@ -86,16 +87,16 @@ internal partial class ProfileHandler
         IBusinessServiceFactory businessServiceFactory,
         IMainDbService mainDbService,
         ILogger<ProfileHandler> logger,
-        BlockingTreePrintService blockingTreePrintService,
-        GoogleGetTimePrintService googleGetTimePrintService,
+        //BlockingTreePrintService blockingTreePrintService,
+        //GoogleGetTimePrintService googleGetTimePrintService,
         ServerMessageService messageService,
         OuterHttpClient httpClient)
     {
         //webConnectionString = configuration.GetConnectionString("WebDBContext");
 
         this.logger = logger;
-        this.blockingTreePrintService = blockingTreePrintService;
-        this.googleGetTimePrintService = googleGetTimePrintService;
+        //this.blockingTreePrintService = blockingTreePrintService;
+        //this.googleGetTimePrintService = googleGetTimePrintService;
         this.messageService = messageService;
         this.maindbContextFactory = maindbContextFactory;
         this.webDbConnectionFactory = webDbConnectionFactory;
@@ -106,7 +107,7 @@ internal 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)
@@ -916,7 +917,7 @@ internal 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 };
 
@@ -1605,7 +1606,7 @@ internal 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 };
 

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

@@ -10,12 +10,13 @@ using System.Linq;
 using Microsoft.Extensions.Logging;
 using System.Threading.Tasks;
 using Microsoft.EntityFrameworkCore;
+using EVCB_OCPP.WSServer.Service.WsService;
 
 namespace EVCB_OCPP.WSServer.Message
 {
     internal 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
@@ -148,7 +149,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 };
 
@@ -189,7 +190,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 };
 

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

@@ -2,6 +2,7 @@
 using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages;
 using EVCB_OCPP.Packet.Messages.LocalAuthListManagement;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
 using OCPPServer.Protocol;
@@ -13,7 +14,7 @@ namespace EVCB_OCPP.WSServer.Message
 {
     internal 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 +70,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 };
 

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

@@ -1,6 +1,7 @@
 using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages;
 using EVCB_OCPP.Packet.Messages.Basic;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.Extensions.Logging;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
@@ -60,7 +61,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 - 1
EVCB_OCPP.WSServer/Message/OCPP20MessageHandler.cs

@@ -1,5 +1,6 @@
 using EVCB_OCPP.Packet.Messages;
 using EVCB_OCPP.Packet.Messages.Basic;
+using EVCB_OCPP.WSServer.Service.WsService;
 using EVCB_OCPP20.Packet.Features;
 using Microsoft.Extensions.Logging;
 using Newtonsoft.Json;
@@ -52,7 +53,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

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

@@ -2,6 +2,7 @@
 using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages;
 using EVCB_OCPP.Packet.Messages.RemoteTrigger;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
 using OCPPServer.Protocol;
@@ -15,7 +16,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 +52,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 };
 

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

@@ -2,6 +2,7 @@
 using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages;
 using EVCB_OCPP.Packet.Messages.Reservation;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
 using OCPPServer.Protocol;
@@ -15,7 +16,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 +72,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 };
 

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

@@ -3,12 +3,13 @@ using EVCB_OCPP.Packet.Messages;
 using OCPPServer.Protocol;
 using System;
 using Microsoft.Extensions.Logging;
+using EVCB_OCPP.WSServer.Service.WsService;
 
 namespace EVCB_OCPP.WSServer.Message
 {
     internal 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 +38,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 };
 

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

@@ -4,6 +4,7 @@ using EVCB_OCPP.Packet.Features;
 using EVCB_OCPP.Packet.Messages;
 using EVCB_OCPP.Packet.Messages.SmartCharging;
 using EVCB_OCPP.Packet.Messages.SubTypes;
+using EVCB_OCPP.WSServer.Service.WsService;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
 using Newtonsoft.Json;
@@ -70,7 +71,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 +149,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 };
 

+ 24 - 15
EVCB_OCPP.WSServer/Program.cs

@@ -18,6 +18,8 @@ using EVCB_OCPP.WSServer.Helper;
 using Quartz;
 using EVCB_OCPP.WSServer.Jobs;
 using Microsoft.AspNetCore.Builder;
+using EVCB_OCPP.WSServer.Service.WsService;
+using EVCB_OCPP.Service;
 
 namespace EVCB_OCPP.WSServer
 {
@@ -49,28 +51,35 @@ namespace EVCB_OCPP.WSServer
             Console.WriteLine($"Max ThreadPool workerThreads:{workerThreads} completionThreads:{completionThreads}");
             ThreadPool.SetMinThreads((int)(10), (int)(0));
 
-            IHost host = Host.CreateDefaultBuilder(args)
-                //.UseEnvironment("Development")
-                //.ConfigureLogging((context, builder) => { 
-                //    builder.ClearProviders();
-                //    builder.AddAzureWebAppDiagnostics();
-                //    NLog.LogManager.Configuration = new NLogLoggingConfiguration(context.Configuration.GetSection("NLog"));
-                //})
-                //.UseNLog()
+            var builder = WebApplication.CreateBuilder(args);
+            //IHost host = Host.CreateDefaultBuilder(args)
+            //.UseEnvironment("Development")
+            //.ConfigureLogging((context, builder) => { 
+            //    builder.ClearProviders();
+            //    builder.AddAzureWebAppDiagnostics();
+            //    NLog.LogManager.Configuration = new NLogLoggingConfiguration(context.Configuration.GetSection("NLog"));
+            //})
+            //.UseNLog()
+            builder.Host
                 .AddLogServcie()
                 .ConfigureServices((hostContext, services) =>
                 {
                     //services.AddSingleton<MeterValueGroupSingleHandler>();
-                    services.AddSingleton<IHostLifetime, DummyHostLifeTime>();
+                    //services.AddSingleton<IHostLifetime, DummyHostLifeTime>();
 
                     services.AddProtalServer(hostContext.Configuration);
 
-                    services.AddTransient<BlockingTreePrintService>();
-                    services.AddTransient<GoogleGetTimePrintService>();
-                })
-                .Build();
-
-            host.Run();
+                    //services.AddTransient<BlockingTreePrintService>();
+                    //services.AddTransient<GoogleGetTimePrintService>();
+                });
+                //.Build();
+            var app = builder.Build();
+            app.UseHeaderRecordService();
+            app.UseOcppWsService();
+            app.MapApiServce();
+            app.Urls.Add("http://*:80");
+            app.Run();
+            //host.Run();
         }
 
         public static object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

+ 192 - 119
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -29,6 +29,8 @@ using Microsoft.Data.SqlClient;
 using System.Collections.Concurrent;
 using EVCB_OCPP.WSServer.SuperSocket;
 using Microsoft.Extensions.Logging;
+using EVCB_OCPP.WSServer.Service.WsService;
+using System.Net.WebSockets;
 
 namespace EVCB_OCPP.WSServer
 {
@@ -60,12 +62,13 @@ namespace EVCB_OCPP.WSServer
             , SqlConnectionFactory<WebDBConetext> webDbConnectionFactory
             , SqlConnectionFactory<MainDBContext> mainDbConnectionFactory
             , IHostEnvironment environment
-            , IOCPPWSServerFactory ocppWSServerFactory
+            //, IOCPPWSServerFactory ocppWSServerFactory
             , IConnectionLogdbService connectionLogdbService
             , WebDbService webDbService
             , ServerMessageService serverMessageService
-            , IServiceProvider serviceProvider,
-            OuterHttpClient httpClient)
+            , IServiceProvider serviceProvider
+            , OcppWebsocketService websocketService
+            , OuterHttpClient httpClient)
         {
             _ct = _cts.Token;
             this.logger = logger;
@@ -74,10 +77,11 @@ namespace EVCB_OCPP.WSServer
             this.mainDbService = mainDbService;
             this.webDbConnectionFactory = webDbConnectionFactory;
             //this.connectionLogdbContextFactory = connectionLogdbContextFactory;
-            this.ocppWSServerFactory = ocppWSServerFactory;
+            //this.ocppWSServerFactory = ocppWSServerFactory;
             this.connectionLogdbService = connectionLogdbService;
             this.webDbService = webDbService;
             this.messageService = serverMessageService;
+            this.websocketService = websocketService;
             this.httpClient = httpClient;
             isInDocker = !string.IsNullOrEmpty(configuration["DOTNET_RUNNING_IN_CONTAINER"]);
 
@@ -91,7 +95,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;
@@ -102,10 +106,11 @@ namespace EVCB_OCPP.WSServer
         private readonly SqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
 
         //private readonly IDbContextFactory<ConnectionLogDBContext> connectionLogdbContextFactory;
-        private readonly IOCPPWSServerFactory ocppWSServerFactory;
+        //private readonly IOCPPWSServerFactory ocppWSServerFactory;
         private readonly IConnectionLogdbService connectionLogdbService;
         private readonly WebDbService webDbService;
         private readonly ServerMessageService messageService;
+        private readonly OcppWebsocketService websocketService;
         private readonly ProfileHandler profileHandler;//= new ProfileHandler();
         //private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
         private readonly bool isInDocker;
@@ -152,31 +157,26 @@ namespace EVCB_OCPP.WSServer
         };
         private CancellationTokenSource _cts = new CancellationTokenSource();
         private CancellationToken _ct;
+
         #endregion
 
-        internal Dictionary<string, ClientData> ClientDic
+        internal Dictionary<string, WsClientData> GetClientDic()
         {
-            get
-            {
-                Dictionary<string, ClientData> toReturn = null;
-                toReturn = new Dictionary<string, ClientData>(clientDic);
-                return toReturn;
-            }
+            Dictionary<string, WsClientData> toReturn = null;
+            toReturn = new Dictionary<string, WsClientData>(clientDic);
+            return toReturn;
         }
 
-        internal List<NeedConfirmMessage> ResendMessage
+        internal List<NeedConfirmMessage> GetResendMessage()
         {
-            get
+            List<NeedConfirmMessage> sendMessages = new List<NeedConfirmMessage>();
+            lock (_lockConfirmPacketList)
             {
-                List<NeedConfirmMessage> sendMessages = new List<NeedConfirmMessage>();
-                lock (_lockConfirmPacketList)
-                {
-                    sendMessages = needConfirmPacketList.Where(x => x.SentTimes > 1 && x.CreatedBy == "Server").ToList();
+                sendMessages = needConfirmPacketList.Where(x => x.SentTimes > 1 && x.CreatedBy == "Server").ToList();
 
-                }
-
-                return sendMessages;
             }
+
+            return sendMessages;
         }
 
         internal IReadOnlyList<Profile> Profiles => profiles.AsReadOnly();
@@ -200,7 +200,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);
         }
@@ -214,11 +214,12 @@ namespace EVCB_OCPP.WSServer
                 Console.WriteLine("Please check App.Config setting .");
                 return;
             }
-            
-            OpenNetwork();
 
+            StartWsService();
+            //OpenNetwork();
 
-            RunHttpConsoleService();
+
+            //RunHttpConsoleService();
             return;
             if (!isInDocker)
             {
@@ -229,6 +230,70 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
+        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);
+
+            }
+            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);
+        }
+
+        private void TryRemoveDuplicatedSession(WsClientData session)
+        {
+            if (clientDic.ContainsKey(session.ChargeBoxId))
+            {
+                var oldSession = clientDic[session.ChargeBoxId];
+                WriteMachineLog(oldSession, "Duplicate Logins", "Connection", "");
+                RemoveClient(oldSession);
+            }
+        }
+
         private void RunConsoleInteractive()
         {
             while (true)
@@ -254,8 +319,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)
@@ -268,8 +333,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)
@@ -388,8 +453,8 @@ namespace EVCB_OCPP.WSServer
 
             app.MapPost("/lc", () => {
                 List<string> toReturn = new List<string>() { "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)
@@ -402,8 +467,8 @@ namespace EVCB_OCPP.WSServer
 
             app.MapPost("/lcn", () => {
                 List<string> toReturn = new List<string> { "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)
@@ -491,74 +556,74 @@ namespace EVCB_OCPP.WSServer
             _cts?.Cancel();
         }
 
-        private void OpenNetwork()
-        {
-
-            //載入OCPP Protocol
-            OCPPWSServer appServer = ocppWSServerFactory.Create(new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") });
-            //var appServer = new OCPPWSServer(new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") });
-
-            List<IListenerConfig> llistener = new List<IListenerConfig>();
-
-            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() });
-            }
-
-            //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;
-
-            //設定server config
-            var serverConfig = new ServerConfig
-            {
-                SendingQueueSize = 10,
-                //Port = Convert.ToInt32(2012),
-                //Ip = "172.17.40.13",
-                MaxRequestLength = 204800,
-                //Security = serverSecurity,
-                //Certificate = Certificate,
-                Listeners = listeners,
-                //  LogAllSocketException = true,
-                KeepAliveTime = 10,
-                // LogBasicSessionActivity = true
-                //Security = "None"
-            };
-
-            //Setup with listening port
-            if (!appServer.Setup(serverConfig, logFactory: new NLogLoggerFactory()))
-            {
-                Console.WriteLine("Failed to setup!");
-                return;
-            }
-
-            appServer.NewSessionConnected += AppServer_NewSessionConnected;
-            appServer.SessionClosed += AppServer_SessionClosed;
-
-
-            //Try to start the appServer
-            if (!appServer.Start())
-            {
-                Console.WriteLine("Failed to start!");
-                //Console.ReadKey();
-                return;
-            }
-        }
-
-        private void AppServer_SessionClosed(ClientData session, CloseReason value)
+        //private void OpenNetwork()
+        //{
+
+        //    //載入OCPP Protocol
+        //    OCPPWSServer appServer = ocppWSServerFactory.Create(new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") });
+        //    //var appServer = new OCPPWSServer(new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") });
+
+        //    List<IListenerConfig> llistener = new List<IListenerConfig>();
+
+        //    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() });
+        //    }
+
+        //    //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;
+
+        //    //設定server config
+        //    var serverConfig = new ServerConfig
+        //    {
+        //        SendingQueueSize = 10,
+        //        //Port = Convert.ToInt32(2012),
+        //        //Ip = "172.17.40.13",
+        //        MaxRequestLength = 204800,
+        //        //Security = serverSecurity,
+        //        //Certificate = Certificate,
+        //        Listeners = listeners,
+        //        //  LogAllSocketException = true,
+        //        KeepAliveTime = 10,
+        //        // LogBasicSessionActivity = true
+        //        //Security = "None"
+        //    };
+
+        //    //Setup with listening port
+        //    if (!appServer.Setup(serverConfig, logFactory: new NLogLoggerFactory()))
+        //    {
+        //        Console.WriteLine("Failed to setup!");
+        //        return;
+        //    }
+
+        //    appServer.NewSessionConnected += AppServer_NewSessionConnected;
+        //    appServer.SessionClosed += AppServer_SessionClosed;
+
+
+        //    //Try to start the appServer
+        //    if (!appServer.Start())
+        //    {
+        //        Console.WriteLine("Failed to start!");
+        //        //Console.ReadKey();
+        //        return;
+        //    }
+        //}
+
+        private void AppServer_SessionClosed(WsClientData session, CloseReason value)
         {
             WriteMachineLog(session, string.Format("CloseReason: {0}", value), "Connection", "");
             RemoveClient(session);
         }
 
-        private async void AppServer_NewSessionConnected(ClientData session)
+        private async void AppServer_NewSessionConnected(WsClientData session)
         {
             logger.LogDebug(string.Format("{0} NewSessionConnected", session.Path));
 
@@ -571,14 +636,14 @@ namespace EVCB_OCPP.WSServer
                     WriteMachineLog(session, string.Format("SecWebSocketProtocol:{0} NotSupported", session.SecWebSocketProtocol), "Connection", "");
                     return;
                 }
-                //ClientData _removeClient = null;
+                //WsClientData _removeClient = null;
 
                 //var addedClient = clientDic.GetOrAdd(session.ChargeBoxId, session);
                 if (clientDic.ContainsKey(session.ChargeBoxId))
                 {
                     var oldSession = clientDic[session.ChargeBoxId];
                     WriteMachineLog(oldSession, "Duplicate Logins", "Connection", "");
-                    oldSession.Close(CloseReason.ServerShutdown);
+                    //oldSession.Close(CloseReason.ServerShutdown);
                     RemoveClient(oldSession);
                 }
                 clientDic[session.ChargeBoxId] = session;
@@ -601,7 +666,7 @@ namespace EVCB_OCPP.WSServer
                     var machine = await db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefaultAsync();
                     if (machine != null)
                     {
-                        machine.ConnectionType = session.Origin.Contains("https") ? 2 : 1;
+                        machine.ConnectionType = session.UriScheme.Contains("wss") ? 2 : 1;
                         await db.SaveChangesAsync();
                     }
 
@@ -615,8 +680,12 @@ namespace EVCB_OCPP.WSServer
 
         }
 
-        async private void ReceivedMessageTimeLimited(ClientData session, string rawdata)
+        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));
@@ -632,7 +701,7 @@ namespace EVCB_OCPP.WSServer
             return;
         }
 
-        async private Task ReceivedMessage(ClientData session, string rawdata)
+        async private Task ReceivedMessage(WsClientData session, string rawdata)
         {
             try
             {
@@ -875,7 +944,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();
@@ -932,7 +1001,7 @@ namespace EVCB_OCPP.WSServer
                                                 {
                                                     session.IsAC = false;
                                                 }
-                                                machine.ConnectionType = session.Origin.Contains("https") ? 2 : 1;
+                                                machine.ConnectionType = session.UriScheme.Contains("wss") ? 2 : 1;
                                                 await db.SaveChangesAsync();
                                             }
                                         }
@@ -1085,7 +1154,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();
@@ -1154,7 +1223,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))
@@ -1212,7 +1281,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
             {
@@ -1232,7 +1301,7 @@ namespace EVCB_OCPP.WSServer
 
         }
 
-        async private Task<string> SetDefaultFee(ClientData client)
+        async private Task<string> SetDefaultFee(WsClientData client)
         {
             string displayPriceText = string.Empty;
             string charingPriceText = string.Empty;
@@ -1386,7 +1455,7 @@ namespace EVCB_OCPP.WSServer
 
 
 
-        internal void RemoveClient(ClientData session)
+        internal async void RemoveClient(WsClientData session)
         {
             if (session == null)
             {
@@ -1396,13 +1465,17 @@ namespace EVCB_OCPP.WSServer
             if (!string.IsNullOrEmpty(session.MachineId))
                 logger.LogTrace("RemoveClient[" + session.ChargeBoxId + "]");
 
-            if (session.Connected)
-            {
-                session.Close(CloseReason.ServerShutdown);
-            }
+            //if (session.Connected)
+            //{
+            //    session.Close(CloseReason.ServerShutdown);
+            //}
             RemoveClientDic(session);
             try
             {
+                if (session.State == WebSocketState.Open)
+                {
+                    await session.Close();
+                }
                 session.m_ReceiveData -= ReceivedMessageTimeLimited;
                 // session.Close(CloseReason.ServerShutdown);
 
@@ -1419,7 +1492,7 @@ namespace EVCB_OCPP.WSServer
             }
         }
 
-        private void RemoveClientDic(ClientData session)
+        private void RemoveClientDic(WsClientData session)
         {
             if (string.IsNullOrEmpty(session.ChargeBoxId))
             {
@@ -1444,19 +1517,19 @@ namespace EVCB_OCPP.WSServer
             connectionLogdbService.WarmUpLog();
         }
 
-        private void WriteMachineLog(ClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false)
+        private void WriteMachineLog(WsClientData WsClientData, string data, string messageType, string errorMsg = "", bool isSent = false)
         {
             try
             {
 
-                if (clientData == null || string.IsNullOrEmpty(data)) return;
+                if (WsClientData == null || string.IsNullOrEmpty(data)) return;
 
-                if (clientData.ChargeBoxId == null)
+                if (WsClientData.ChargeBoxId == null)
                 {
-                    logger.LogCritical(clientData.Path + "]********************session ChargeBoxId null sessionId=" + clientData.SessionID);
+                    logger.LogCritical(WsClientData.Path.ToString() + "]********************session ChargeBoxId null sessionId=" + WsClientData.SessionID);
                 }
 
-                connectionLogdbService.WriteMachineLog(clientData, data, messageType, errorMsg, isSent);
+                connectionLogdbService.WriteMachineLog(WsClientData, data, messageType, errorMsg, isSent);
             }
             catch (Exception ex)
             {

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

@@ -1,6 +1,7 @@
 using Dapper;
 using EVCB_OCPP.Domain;
 using EVCB_OCPP.WSServer.Helper;
+using EVCB_OCPP.WSServer.Service.WsService;
 using log4net;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
@@ -22,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
@@ -71,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);
@@ -112,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;
@@ -152,7 +153,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";
@@ -165,7 +166,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())
@@ -234,7 +235,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
@@ -294,7 +295,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);
@@ -366,4 +367,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);

+ 65 - 0
EVCB_OCPP.WSServer/Service/HeaderRecordService.cs

@@ -0,0 +1,65 @@
+using Azure.Core;
+using HeaderRecord;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using System.Net.Http;
+
+namespace EVCB_OCPP.Service
+{
+    public static partial class AppExtention
+    {
+        public static void AddHeaderRecordService(this IServiceCollection services)
+        {
+            services.AddTransient<HeaderRecordService>();
+        }
+
+        public static void UseHeaderRecordService(this WebApplication webApplication)
+        {
+            webApplication.Use(async (context, next) =>
+            {
+                var servcie = context.RequestServices.GetService<HeaderRecordService>();
+                servcie.LogRequest(context.TraceIdentifier, context.Request);
+                await next(context);
+                servcie.LogResponse(context.TraceIdentifier, context.Response);
+                return;
+            });
+        }
+    }
+}
+
+namespace HeaderRecord
+{
+
+    public class HeaderRecordService
+    {
+        private readonly ILogger<HeaderRecordService> logger;
+
+        public HeaderRecordService(ILogger<HeaderRecordService> logger)
+        {
+            this.logger = logger;
+        }
+
+        internal void LogRequest(string traceIdentifier, HttpRequest request)
+        {
+            logger.LogInformation("LogRequest============================================================");
+            logger.LogInformation("{id} {method} {path} {protocol}", traceIdentifier, request.Method, request.Path, request.Protocol);
+            foreach (var headerKey in request.Headers.Keys)
+            {
+                logger.LogInformation("{id} {key} {value}", traceIdentifier, headerKey, request.Headers[headerKey]);
+            }
+            logger.LogInformation("LogRequest============================================================");
+        }
+
+        internal void LogResponse(string traceIdentifier, HttpResponse response)
+        {
+            logger.LogInformation("LogResponse============================================================");
+            foreach (var headerKey in response.Headers.Keys)
+            {
+                logger.LogInformation("{id} {key} {value}", traceIdentifier, headerKey, response.Headers[headerKey]);
+            }
+            logger.LogInformation("LogResponse============================================================");
+        }
+    }
+}

+ 33 - 20
EVCB_OCPP.WSServer/Service/MainDbService.cs

@@ -16,11 +16,11 @@ namespace EVCB_OCPP.WSServer.Service;
 
 public interface IMainDbService
 {
-    Task<string> GetMachineAuthorizationKey(string ChargeBoxId);
-    Task<string> GetMachineConfiguration(string ChargeBoxId, string configName);
+    Task<string> GetMachineAuthorizationKey(string ChargeBoxId, CancellationToken token = default);
+    Task<string> GetMachineConfiguration(string ChargeBoxId, string configName, CancellationToken token = default);
     Task<string> GetMachineHeartbeatInterval(string ChargeBoxId);
-    Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId);
-    Task<string> GetMachineSecurityProfile(string ChargeBoxId);
+    Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId, CancellationToken token = default);
+    Task<string> GetMachineSecurityProfile(string ChargeBoxId, CancellationToken token = default);
     Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine);
     Task AddOCMF(OCMF oCMF);
     ValueTask<ConnectorStatus> GetConnectorStatus(string ChargeBoxId, int ConnectorId);
@@ -30,8 +30,8 @@ public interface IMainDbService
     Task AddServerMessage(ServerMessage message);
     Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy = "", DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "");
     ValueTask AddMachineError(byte ConnectorId, DateTime CreatedOn, int Status, string ChargeBoxId, int ErrorCodeId, string ErrorInfo, int PreStatus, string VendorErrorCode, string VendorId);
-    ValueTask<Customer> GetCustomer(string id);
-    ValueTask<Customer> GetCustomer(Guid id);
+    ValueTask<Customer> GetCustomer(string id, CancellationToken token = default);
+    ValueTask<Customer> GetCustomer(Guid id, CancellationToken token = default);
     Task<Guid> GetCustomerIdByChargeBoxId(string chargeboxId);
     Task<int?> TryGetDuplicatedTransactionId(string chargeBoxId, Guid customerId, int connectorId, DateTime timestamp);
     Task<int> AddNewTransactionRecord(TransactionRecord newTransaction);
@@ -39,8 +39,8 @@ public interface IMainDbService
     Task UpdateTransaction(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost);
     Task<bool> UpdateHeartBeats(IEnumerable<Machine> heartBeatsData);
     Task<bool> UpdateHeartBeats(List<string> machineIds);
-
     Task UpdateTransactionSOC(int id, string startsoc, string stopsoc);
+    Task UpdateMachineConnectionType(string chargeBoxId, int v);
 }
 
 public class MainDbService : IMainDbService
@@ -86,10 +86,10 @@ public class MainDbService : IMainDbService
     private GroupHandler<UpdateMachineBasicInfoParam> updateMachineBasicInfoHandler;
     private GroupHandler<ServerMessage> addServerMessageHandler;
 
-    public async Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId)
+    public async Task<MachineAndCustomerInfo> GetMachineIdAndCustomerInfo(string ChargeBoxId, CancellationToken token = default)
     {
         using var semaphoreWrapper = await startupSemaphore.GetToken();
-        using var db = await contextFactory.CreateDbContextAsync();
+        using var db = await contextFactory.CreateDbContextAsync(token);
 
         var machine = await db.Machine.Where(x => x.ChargeBoxId == ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).AsNoTracking().FirstOrDefaultAsync();
         if (machine == null)
@@ -97,12 +97,12 @@ public class MainDbService : IMainDbService
             return new MachineAndCustomerInfo(string.Empty, Guid.Empty, "Unknown");
         }
         //var customerName = await db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefaultAsync();
-        var customer = await GetCustomer(machine.CustomerId);
+        var customer = await GetCustomer(machine.CustomerId, token);
         var customerName = customer?.Name;
         return new MachineAndCustomerInfo(machine.Id, machine.CustomerId, customerName);
     }
 
-    public async Task<string> GetMachineConfiguration(string ChargeBoxId, string configName)
+    public async Task<string> GetMachineConfiguration(string ChargeBoxId, string configName, CancellationToken token = default)
     {
         using var semaphoreWrapper = await startupSemaphore.GetToken();
         using var db = await contextFactory.CreateDbContextAsync();
@@ -111,14 +111,14 @@ public class MainDbService : IMainDbService
             .Select(x => x.ConfigureSetting).FirstOrDefaultAsync();
     }
 
-    public Task<string> GetMachineSecurityProfile(string ChargeBoxId)
+    public Task<string> GetMachineSecurityProfile(string ChargeBoxId, CancellationToken token = default)
     {
-        return GetMachineConfiguration(ChargeBoxId, StandardConfiguration.SecurityProfile);
+        return GetMachineConfiguration(ChargeBoxId, StandardConfiguration.SecurityProfile, token);
     }
 
-    public Task<string> GetMachineAuthorizationKey(string ChargeBoxId)
+    public Task<string> GetMachineAuthorizationKey(string ChargeBoxId, CancellationToken token = default)
     {
-        return GetMachineConfiguration(ChargeBoxId, StandardConfiguration.AuthorizationKey);
+        return GetMachineConfiguration(ChargeBoxId, StandardConfiguration.AuthorizationKey , token);
     }
 
     public Task<string> GetMachineHeartbeatInterval(string ChargeBoxId)
@@ -258,10 +258,10 @@ public class MainDbService : IMainDbService
         return AddServerMessageDapper(message);
     }
 
-    public ValueTask<Customer> GetCustomer(string id)
-        => GetCustomer(new Guid(id));
+    public ValueTask<Customer> GetCustomer(string id, CancellationToken token = default)
+        => GetCustomer(new Guid(id), token);
 
-    public async ValueTask<Customer> GetCustomer(Guid id)
+    public async ValueTask<Customer> GetCustomer(Guid id, CancellationToken token = default)
     {
         var key = string.Format(CustomerMemCacheKeyFromat, id);
         if (memoryCache.TryGetValue<Customer>(key, out var customer))
@@ -270,9 +270,9 @@ public class MainDbService : IMainDbService
         }
 
         Customer toReturn = null;
-        using (var db = await contextFactory.CreateDbContextAsync())
+        using (var db = await contextFactory.CreateDbContextAsync(token))
         {
-            toReturn = await db.Customer.FirstOrDefaultAsync(x => x.Id == id);
+            toReturn = await db.Customer.FirstOrDefaultAsync(x => x.Id == id, token);
         }
 
         if (toReturn is not null)
@@ -376,6 +376,19 @@ public class MainDbService : IMainDbService
         return UpdateHeartBeatsDapper(machineIds);
     }
 
+    public async Task UpdateMachineConnectionType(string chargeBoxId, int connectionType)
+    {
+        using var semaphoreWrapper = await startupSemaphore.GetToken();
+        using var db = await contextFactory.CreateDbContextAsync();
+
+        var machine = await db.Machine.Where(x => x.ChargeBoxId == chargeBoxId).FirstOrDefaultAsync();
+        if (machine != null)
+        {
+            machine.ConnectionType = connectionType;
+            await db.SaveChangesAsync();
+        }
+    }
+
     private void InitUpdateConnectorStatusHandler()
     {
         if (statusNotificationHandler is not null)

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

@@ -0,0 +1,115 @@
+using EVCB_OCPP.WSServer.Service.WsService;
+using Microsoft.AspNetCore.Builder;
+using NLog;
+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}";
+        });
+    }
+}

+ 251 - 0
EVCB_OCPP.WSServer/Service/WsService/OcppWebsocketService.cs

@@ -0,0 +1,251 @@
+using EVCB_OCPP.WSServer.Helper;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using OCPPServer.Protocol;
+using System.Net.WebSockets;
+using System.Text;
+
+namespace EVCB_OCPP.WSServer.Service.WsService;
+
+public static partial class AppExtention
+{
+    public static void AddOcppWsServer(this IServiceCollection services)
+    {
+        services.AddTransient<WsClientData>();
+        services.AddSingleton<OcppWebsocketService>();
+    }
+
+    public static void UseOcppWsService(this WebApplication webApplication)
+    {
+        webApplication.UseWebSockets(new WebSocketOptions()
+        {
+            KeepAliveInterval = TimeSpan.FromSeconds(10)
+        });
+
+        webApplication.Use(async (context, next) =>
+        {
+            if (!context.WebSockets.IsWebSocketRequest)
+            {
+                await next(context);
+                return;
+            }
+
+            var servcie = context.RequestServices.GetService<OcppWebsocketService>();
+            await servcie.AcceptWebSocket(context);
+            return;
+        });
+    }
+}
+
+public class OcppWebsocketService : WebsocketService<WsClientData>
+{
+    public static List<string> protocals = new List<string>() { "", "ocpp1.6", "ocpp2.0.1" };
+
+    private readonly IConfiguration configuration;
+    private readonly IMainDbService mainDbService;
+    private readonly ILogger<OcppWebsocketService> logger;
+
+    private readonly QueueSemaphore handshakeSemaphore;
+
+    public OcppWebsocketService(
+        IConfiguration configuration,
+        IServiceProvider serviceProvider,
+        IMainDbService mainDbService,
+        ILogger<OcppWebsocketService> logger
+        ) : base(serviceProvider)
+    {
+        this.configuration = configuration;
+        this.mainDbService = mainDbService;
+        this.logger = logger;
+
+        handshakeSemaphore = new QueueSemaphore(5);
+    }
+
+    internal override async ValueTask<string> ValidateSupportedPortocol(HttpContext context)
+    {
+        logger.LogInformation("{id} {function}:{Path}/{SubProtocol}", context.TraceIdentifier, nameof(ValidateSupportedPortocol), context.Request.Path, context.WebSockets.WebSocketRequestedProtocols);
+
+        var protocol = GetSupportedPortocol(context.WebSockets.WebSocketRequestedProtocols, protocals);
+        if (string.IsNullOrEmpty(protocol))
+        {
+            logger.LogInformation("{id} {function}:{Path} Protocol Not Supported, Disconnecting", context.TraceIdentifier, nameof(ValidateSupportedPortocol), context.Request.Path);
+
+            using WebSocket toRejectwebSocket = await context.WebSockets.AcceptWebSocketAsync();
+            await toRejectwebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, default);
+            return string.Empty;
+        }
+        return protocol;
+    }
+
+    internal override async ValueTask<bool> ValidateHandshake(HttpContext context, WsClientData session)
+    {
+        try
+        {
+            using var canValidate = await handshakeSemaphore.GetToken();
+            var result = await ValidateHandshakeUnsafe(context, session);
+            return result;
+        }
+        catch (Exception ex)
+        {
+            logger.LogError(ex.Message);
+            logger.LogError(ex.StackTrace);
+        }
+        return false;
+    }
+
+    internal async ValueTask<bool> ValidateHandshakeUnsafe(HttpContext context, WsClientData session)
+    {
+        if (context.RequestAborted.IsCancellationRequested) return false;
+
+        string authHeader = context?.Request?.Headers?.Authorization;
+
+        session.ISOCPP20 = context.WebSockets.WebSocketRequestedProtocols.Any(x => x.ToLower() == "ocpp2.0");
+
+        int securityProfile = 0;
+        string authorizationKey = string.Empty;
+        if (string.IsNullOrEmpty(session.Path))
+        {
+            //logger.Log();
+            logger.LogWarning("===========================================");
+            logger.LogWarning("{id} session.Path EMPTY", context.TraceIdentifier);
+            logger.LogWarning("===========================================");
+        }
+
+        string[] words = session.Path.ToString().Split('/');
+        session.ChargeBoxId = words.Last();
+        logger.LogDebug("{id} {0}:{1}", context.TraceIdentifier, session.ChargeBoxId, session.UriScheme);
+
+        foreach (var denyModel in GlobalConfig.DenyModelNames)
+        {
+            if (string.IsNullOrEmpty(denyModel))
+            {
+                continue;
+            }
+
+            if (session.ChargeBoxId.StartsWith(denyModel))
+            {
+                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
+
+                logger.LogTrace("{id} {func} {Statuscode}", context.TraceIdentifier, nameof(ValidateHandshake), context.Response.StatusCode);
+                return false;
+            }
+        }
+
+        if (configuration["MaintainMode"] == "1")
+        {
+            session.ChargeBoxId = session.ChargeBoxId + "_2";
+        }
+
+        logger.LogInformation("{id} ValidateHandshake: {0}", context.TraceIdentifier, session.Path);
+        bool isExistedSN = false;
+        bool authorizated = false;
+
+        var info = await mainDbService.GetMachineIdAndCustomerInfo(session.ChargeBoxId, context.RequestAborted);
+        if (context.RequestAborted.IsCancellationRequested) return false;
+        //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)
+        {
+            context.Response.StatusCode = StatusCodes.Status404NotFound;
+            logger.LogTrace("{id} {func} {Statuscode}", context.TraceIdentifier, nameof(ValidateHandshake), context.Response.StatusCode);
+            return false;
+        }
+
+        //var configVaule = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.SecurityProfile)
+        //                  .Select(x => x.ConfigureSetting).FirstOrDefault();
+        var configVaule = await mainDbService.GetMachineSecurityProfile(session.ChargeBoxId, context.RequestAborted);
+        if (context.RequestAborted.IsCancellationRequested) return false;
+        int.TryParse(configVaule, out securityProfile);
+
+        if (session.ISOCPP20)
+        {
+            // 1.6 server only support change server  function
+            securityProfile = 0;
+        }
+
+        if (securityProfile == 3 && session.UriScheme == "ws")
+        {
+            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
+            logger.LogTrace("{id} {func} {Statuscode}", context.TraceIdentifier, nameof(ValidateHandshake), context.Response.StatusCode);
+            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(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, context.RequestAborted);
+                if (context.RequestAborted.IsCancellationRequested) return false;
+
+                if (session.ISOCPP20)
+                {
+                    // 1.6 server only support change server  function
+                    securityProfile = 0;
+                }
+
+                logger.LogInformation("{id} ***********Authorization   ", context.TraceIdentifier);
+
+                if (!string.IsNullOrEmpty(authorizationKey))
+                {
+                    //string base64Encoded = session.Items.ContainsKey("Authorization") ? session.Items["Authorization"].ToString().Replace("Basic ", "") : session.Items["authorization"].ToString().Replace("Basic ", "");
+                    string base64Encoded = authHeader.Replace("Basic ", "");
+                    byte[] data = Convert.FromBase64String(base64Encoded);
+                    string[] base64Decoded = Encoding.ASCII.GetString(data).Split(':');
+                    logger.LogInformation("{id} ***********Authorization   " + Encoding.ASCII.GetString(data), context.TraceIdentifier);
+                    if (base64Decoded.Count() == 2 && base64Decoded[0] == session.ChargeBoxId && base64Decoded[1] == authorizationKey)
+                    {
+                        authorizated = true;
+                    }
+                }
+            }
+            else
+            {
+                authorizated = true;
+
+            }
+
+            if (!authorizated)
+            {
+                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
+                logger.LogTrace("{id} {func} {Statuscode}", context.TraceIdentifier, nameof(ValidateHandshake), context.Response.StatusCode);
+                return false;
+            }
+        }
+
+        logger.LogInformation("{id} ValidateHandshake PASS: {0}", context.TraceIdentifier, session.Path);
+        return true;
+    }
+
+    private static string GetSupportedPortocol(IList<string> clientProtocols, IList<string> supportedProtocols)
+    {
+        int supportedProtocolIndex = supportedProtocols.Count - 1;
+        for (; supportedProtocolIndex >= 0; supportedProtocolIndex--)
+        {
+            var testProtocol = supportedProtocols[supportedProtocolIndex];
+            if (clientProtocols.Contains(testProtocol))
+            {
+                return testProtocol;
+            }
+        }
+        return string.Empty;
+    }
+}

+ 107 - 0
EVCB_OCPP.WSServer/Service/WsService/WebsocketService.cs

@@ -0,0 +1,107 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using System.Net;
+using System.Net.Http;
+using System.Net.WebSockets;
+using System.ServiceModel.Channels;
+
+namespace EVCB_OCPP.WSServer.Service.WsService;
+
+public class WebsocketService<T> where T : WsSession
+{
+    public WebsocketService(IServiceProvider serviceProvider)
+    {
+        this.serviceProvider = serviceProvider;
+    }
+
+    private readonly IServiceProvider serviceProvider;
+
+    public event EventHandler<T> NewSessionConnected;
+
+    public async Task AcceptWebSocket(HttpContext context)
+    {
+        if (!context.WebSockets.IsWebSocketRequest)
+        {
+            return;
+        }
+        var portocol = await ValidateSupportedPortocol(context);
+        if (string.IsNullOrEmpty(portocol))
+        {
+            return;
+        }
+
+        T data = GetSession(context);
+
+        if (!await ValidateHandshake(context, data))
+        {
+            return;
+        }
+
+        using WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(portocol);
+        await AddWebSocket(webSocket, data);
+    }
+
+    internal virtual ValueTask<bool> ValidateHandshake(HttpContext context, T data)
+    {
+        return ValueTask.FromResult(true);
+    }
+
+    internal virtual ValueTask<string> ValidateSupportedPortocol(HttpContext context)
+    {
+        return ValueTask.FromResult(string.Empty);
+    }
+
+
+    private async Task AddWebSocket(WebSocket webSocket, T data)
+    {
+        data.ClientWebSocket = webSocket;
+
+        NewSessionConnected?.Invoke(this, data);
+        await data.EndConnSemaphore.WaitAsync();
+        return;
+    }
+
+    private T GetSession(HttpContext context)
+    {
+        T data = serviceProvider.GetRequiredService<T>();
+        data.Path = context?.Request?.Path;
+        data.SessionID = context.TraceIdentifier;
+        data.UriScheme = GetScheme(context);
+
+        try
+        {
+            var ipaddress = context.Connection.RemoteIpAddress;
+            var port = context.Connection.RemotePort;
+            data.Endpoint = new IPEndPoint(ipaddress, port);
+        }
+        catch
+        {
+            data.Endpoint = null;
+        }
+
+        return data;
+    }
+
+    private string GetScheme(HttpContext context)
+    {
+        string toReturn = string.Empty;
+
+        if (context.Request.Headers.ContainsKey("x-original-host"))
+        {
+            toReturn = new Uri(context.Request.Headers["x-original-host"]).Scheme;
+            return toReturn;
+        }
+
+        var origin = context.Request.Headers.Origin.FirstOrDefault();
+        try
+        {
+            toReturn = new Uri(origin).Scheme;
+            return toReturn;
+        }
+        catch
+        {
+        }
+
+        return toReturn;
+    }
+}

+ 137 - 0
EVCB_OCPP.WSServer/Service/WsService/WsClientData.cs

@@ -0,0 +1,137 @@
+
+using EVCB_OCPP.Packet.Messages.Basic;
+using EVCB_OCPP.WSServer.Dto;
+using log4net.Core;
+using Microsoft.Extensions.Logging;
+
+namespace EVCB_OCPP.WSServer.Service.WsService;
+
+public class WsClientData : WsSession
+{ /// <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 string StationId { set; get; }
+
+    public event EventHandler<string> m_ReceiveData;
+
+    private string stringBuffer = string.Empty;
+
+
+    public WsClientData(ILogger<WsClientData> logger) : base(logger)
+    {
+        ChargeBoxId = SessionID;
+        MachineId = SessionID;
+    }
+
+    internal override void HandleReceivedData(string data)
+    {
+        stringBuffer += data;
+        while (TryGetOCPPMsg(ref stringBuffer, out var msg))
+        {
+            m_ReceiveData?.Invoke(this, msg);
+        }
+    }
+
+    private bool TryGetOCPPMsg(ref string buffer, out string msg)
+    {
+        msg = string.Empty;
+        int? startIndex = null;
+        int? stopIndex = null;
+        uint cnt = 0;
+
+        for (int index = 0; index < buffer.Length; index++)
+        {
+            if (buffer[index] == '[')
+            {
+                cnt++;
+                if (startIndex == null)
+                {
+                    startIndex = index;
+                }
+            }
+
+            if (startIndex != null && buffer[index] == ']')
+            {
+                cnt--;
+            }
+
+            if (startIndex != null && cnt == 0)
+            {
+                stopIndex = index;
+                break;
+            }
+        }
+
+        if (startIndex is not null && stopIndex is not null)
+        {
+            msg = buffer.Substring(startIndex.Value, stopIndex.Value - startIndex.Value);
+            buffer = buffer.Substring(stopIndex.Value);
+            return true;
+        }
+
+        return false;
+    }
+}

+ 174 - 0
EVCB_OCPP.WSServer/Service/WsService/WsSession.cs

@@ -0,0 +1,174 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using System.Net;
+using System.Net.WebSockets;
+using System.Text;
+
+namespace EVCB_OCPP.WSServer.Service.WsService;
+
+public class WsSession
+{
+    public WsSession(ILogger<WsSession> logger)
+    {
+        this.logger = logger;
+    }
+
+    public PathString? Path { get; set; }
+    public string UriScheme { get; set; }
+    public string SessionID { get; set; }
+    public IPEndPoint Endpoint { get; internal set; }
+    public DateTime LastActiveTime { get; set; }
+
+
+    private WebSocket _WebSocket;
+    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<string> SessionClosed;
+
+    private CancellationTokenSource disconnectCancellationTokenSource = new CancellationTokenSource();
+    private Task ReceiveLoopTask;
+    private readonly ILogger<WsSession> logger;
+
+    private void Init(WebSocket webSocket)
+    {
+        _WebSocket = webSocket;
+        LastActiveTime = DateTime.UtcNow;
+        ReceiveLoopTask = StartReceivd(webSocket, disconnectCancellationTokenSource.Token);
+    }
+
+    private async Task StartReceivd(WebSocket webSocket, CancellationToken token)
+    {
+        logger.LogInformation("{id} {func} {Path} Start", SessionID, nameof(StartReceivd), Path);
+
+        byte[] receivdBuffer = new byte[0];
+        int bufferExpand = 1;
+        int receivedBytes = 0;
+        while (!token.IsCancellationRequested)
+        {
+            var tempReceiveBuffer = new byte[1024 * 4];
+            WebSocketReceiveResult result = null;
+
+            try
+            {
+                result = await webSocket.ReceiveAsync(new ArraySegment<byte>(tempReceiveBuffer), token);
+            }
+            catch (Exception e)
+            {
+                _ = BruteClose(e.Message);
+                break;
+            }
+            LastActiveTime = DateTime.UtcNow;
+
+            if (result == null || result.CloseStatus.HasValue)
+            {
+                //closed gracefully
+                await GracefulClose(result.CloseStatus.Value);
+                break;
+            }
+
+            receivdBuffer = new byte[1024 * 4 * bufferExpand];
+            Array.Copy(tempReceiveBuffer, 0, receivdBuffer, receivedBytes, result.Count);
+            receivedBytes += result.Count;
+
+            if (!result.EndOfMessage)
+            {
+                bufferExpand++;
+                continue;
+            }
+
+            var received = Encoding.UTF8.GetString(receivdBuffer, 0, receivedBytes);
+            //logger.LogInformation("{func}:{Path} {value}", nameof(StartReceivd), Path, received);
+
+            HandleReceivedData(received);
+
+            bufferExpand = 1;
+            receivedBytes = 0;
+        }
+    }
+
+    internal virtual void HandleReceivedData(string data)
+    {
+
+    }
+
+    internal Task Send(string dataString)
+    {
+        //logger.LogInformation("{func}:{Path} {value}", nameof(Send), Path, dataString);
+
+        var data = Encoding.UTF8.GetBytes(dataString);
+        return Send(data);
+    }
+
+    internal Task Close()
+    {
+        return ServerClose();
+    }
+
+    private async Task Send(byte[] data)
+    {
+        try
+        {
+            await ClientWebSocket.SendAsync(data, WebSocketMessageType.Text, endOfMessage: true, cancellationToken: disconnectCancellationTokenSource.Token);
+        }
+        catch (Exception e)
+        {
+            logger.LogInformation("{func} {Path} exception:{msg}", nameof(Send), Path, e.Message);
+        }
+    }
+
+    private Task ServerClose()
+    {
+        logger.LogInformation("{func}:{Path}", nameof(ServerClose), Path);
+
+        SessionClosed?.Invoke(this, "ServerShutdown");
+        return InternalClose(WebSocketCloseStatus.NormalClosure, "ServerShutdown");
+    }
+
+    private Task GracefulClose(WebSocketCloseStatus closeStatus)
+    {
+        logger.LogInformation("{func}:{Path} {value}", nameof(GracefulClose), Path, closeStatus);
+
+        SessionClosed?.Invoke(this, closeStatus.ToString());
+        return InternalClose(closeStatus, null);
+    }
+
+    private Task BruteClose(string description)
+    {
+        logger.LogInformation("{func}:{Path} {value}", nameof(ServerClose), Path, description);
+
+        SessionClosed?.Invoke(this, description);
+        return InternalClose(WebSocketCloseStatus.EndpointUnavailable, description);
+    }
+
+    private async Task InternalClose(WebSocketCloseStatus closeStatus, string description)
+    {
+        try
+        {
+            await _WebSocket.CloseAsync(closeStatus, description, default);
+        }
+        catch
+        {
+        }
+        finally
+        {
+            _WebSocket.Dispose();
+        }
+
+        disconnectCancellationTokenSource.Cancel();
+        EndConnSemaphore.Release();
+    }
+}