浏览代码

****Update two DLLs****
1. Add Docker debug variable in visual studio
2. Add 24 period energy of trasaction
3.Add connector status record
4.Fix the bug that occurs when there are continuous errors in the same category

Jessica Tseng 7 月之前
父节点
当前提交
929450d6a7

+ 0 - 4
Dockerfile

@@ -32,10 +32,6 @@ RUN echo 'net.ipv4.tcp_window_scaling = 1' >> /etc/sysctl.conf
 FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
 WORKDIR /src
 COPY ["EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj", "EVCB_OCPP.WSServer/"]
-COPY ["SuperWebSocket/SuperWebSocket.csproj", "SuperWebSocket/"]
-COPY ["SocketBase/SuperSocket.SocketBase.csproj", "SocketBase/"]
-COPY ["SocketCommon/SuperSocket.Common.csproj", "SocketCommon/"]
-COPY ["SocketEngine/SuperSocket.SocketEngine.csproj", "SocketEngine/"]
 RUN dotnet restore "EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj"
 COPY . .
 WORKDIR "/src/EVCB_OCPP.WSServer"

二进制
EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Domain.dll


二进制
EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Packet.dll


+ 0 - 13
EVCB_OCPP.WSServer/Dockerfile

@@ -1,13 +0,0 @@
-#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
-
-FROM mcr.microsoft.com/dotnet/sdk:7.0 AS final
-EXPOSE 80
-EXPOSE 443
-EXPOSE 54088
-RUN sed -i 's/TLSv1.2/TLSv1/g' /etc/ssl/openssl.cnf
-RUN sed -i 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf
-WORKDIR /src
-COPY . .
-RUN export DOTNET_PerfMapEnabled=1
-RUN dotnet build ./EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj
-CMD ["dotnet", " run --project ./EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj"]

+ 9 - 2
EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj

@@ -4,6 +4,8 @@
     <TargetFramework>net7.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <UserSecretsId>88f57ec2-60b9-4291-bba3-3c0d312fe6dc</UserSecretsId>
+    <DockerfileContext>$(SolutionDir.TrimEnd('\'))</DockerfileContext>
+    <DockerfileFile>$(SolutionDir)\Dockerfile</DockerfileFile>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="EVCB_OCPP.Domain">
@@ -40,8 +42,9 @@
     </BootstrapperPackage>
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Azure.Identity" Version="1.11.2" />
+    <PackageReference Include="Azure.Identity" Version="1.12.0" />
     <PackageReference Include="Dapper" Version="2.0.143" />
+    <PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" />
     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.12" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.12" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10" />
@@ -50,7 +53,7 @@
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
     <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="7.0.10" />
-    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
+    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.0" />
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
     <PackageReference Include="NLog" Version="5.2.3" />
     <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.3" />
@@ -59,11 +62,15 @@
     <PackageReference Include="RestSharp" Version="110.2.0" />
     <PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
     <PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
+    <PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
+    <PackageReference Include="System.Formats.Asn1" Version="8.0.1" />
     <PackageReference Include="System.Security.Cryptography.Pkcs" Version="6.0.3" />
     <PackageReference Include="System.Security.Permissions" Version="8.0.0" />
     <PackageReference Include="System.ServiceModel.Federation" Version="6.0.0" />
     <PackageReference Include="EntityFramework" Version="6.4.4" />
     <PackageReference Include="log4net" Version="2.0.15" />
+    <PackageReference Include="System.Text.Json" Version="8.0.4" />
+    <PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
     <PackageReference Include="System.Threading.Tasks.Dataflow" Version="7.0.0" />
     <PackageReference Include="Yarp.ReverseProxy" Version="2.0.1" />
   </ItemGroup>

+ 23 - 2
EVCB_OCPP.WSServer/Helper/DbExtention.cs

@@ -57,6 +57,27 @@ namespace EVCB_OCPP.WSServer.Helper
                 new SqlParameter("TransactionId", SqlDbType.Int) { Value = transactionId },
             };
             sqlParameters.AddRange(parameter);
-        }
-    }
+		}
+
+
+		public static void AddInsertConnectorStatusRecordSqlParameters(this List<SqlParameter> sqlParameters,
+		   string chargeBoxId, byte connectorId, int status, DateTime createdOn
+		   , string errorInfo, string vendorId, string vendorErrorCode, int chargePointErrorCodeId)
+		{
+			List<SqlParameter> parameter = new List<SqlParameter>
+			{
+				new SqlParameter("ChargeBoxId", SqlDbType.NVarChar, 50){ Value = chargeBoxId },
+				new SqlParameter("ConnectorId", SqlDbType.TinyInt) { Value = connectorId },
+				new SqlParameter("Status", SqlDbType.Int){ Value = status },
+				new SqlParameter("CreatedOn", SqlDbType.DateTime) { Value = createdOn },
+				new SqlParameter("ChargePointErrorCodeId", SqlDbType.Int) { Value = chargePointErrorCodeId },
+				new SqlParameter("ErrorInfo",SqlDbType.NVarChar, 50){ Value = errorInfo },
+				new SqlParameter("VendorId", SqlDbType.NVarChar, 255){ Value = vendorId },	
+				new SqlParameter("VendorErrorCode", SqlDbType.NVarChar, 100) {Value =vendorErrorCode },
+
+
+			};
+			sqlParameters.AddRange(parameter);
+		}
+	}
 }

+ 7 - 7
EVCB_OCPP.WSServer/HostedProtalServer.cs

@@ -25,17 +25,15 @@ namespace EVCB_OCPP.WSServer
     {
         public static void AddProtalServer(this IServiceCollection services, IConfiguration configuration)
         {
-            services.AddMemoryCache();
-            //services.AddSingleton<IMemoryCache, DummyMemoryCache>();
+            services.AddMemoryCache();      
 
             services.AddPortalServerDatabase(configuration);
             services.AddBusinessServiceFactory();
 
-            //services.AddTransient<OCPPWSServer>();
-            //services.AddTransient<IOCPPWSServerFactory, OCPPWSServerFactory>();
+          
             services.AddHeaderRecordService();
-
-            services.AddSingleton<MeterValueDbService>();
+			services.AddSingleton<ConnectorStatusDbService>();
+			services.AddSingleton<MeterValueDbService>();
             services.AddSingleton<WebDbService>();
             services.AddSingleton<IMainDbService, MainDbService>();
             services.AddSingleton<IConnectionLogdbService, ConnectionLogdbService>();
@@ -60,7 +58,9 @@ namespace EVCB_OCPP.WSServer
                 .AddMainDbContext(configuration)
                 .AddMeterValueDbContext(configuration)
                 .AddConnectionLogDbContext(configuration)
-                .AddWebDBConetext(configuration);
+                .AddWebDBConetext(configuration)
+                .AddOnlineRecordDBContext(configuration);
+           
         }
 
         public static void AddProtalServerJob(this IServiceCollection services)

+ 31 - 22
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -66,14 +66,13 @@ public class ID_ReaderStatus
 internal partial class ProfileHandler
 {
 	private readonly ILogger logger;
-	//private readonly BlockingTreePrintService blockingTreePrintService;
-	//private readonly GoogleGetTimePrintService googleGetTimePrintService;
 	private readonly ServerMessageService messageService;
 
-	//private readonly string webConnectionString;// = ConfigurationManager.ConnectionStrings[].ConnectionString;
+
 	private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
 	private readonly ISqlConnectionFactory<WebDBConetext> webDbConnectionFactory;
 	private readonly MeterValueDbService meterValueDbService;
+	private readonly ConnectorStatusDbService connectorStatusDbService;
 
 	//private readonly IDbContextFactory<MeterValueDBContext> metervaluedbContextFactory;
 	private readonly IBusinessServiceFactory businessServiceFactory;
@@ -84,27 +83,24 @@ internal partial class ProfileHandler
 		IConfiguration configuration,
 		IDbContextFactory<MainDBContext> maindbContextFactory,
 		ISqlConnectionFactory<WebDBConetext> webDbConnectionFactory,
-		//IDbContextFactory<MeterValueDBContext> metervaluedbContextFactory,
 		MeterValueDbService meterValueDbService,
+		ConnectorStatusDbService connectorStatusDbService,
 		IBusinessServiceFactory businessServiceFactory,
 		IMainDbService mainDbService,
 		ILogger<ProfileHandler> logger,
-		//BlockingTreePrintService blockingTreePrintService,
-		//GoogleGetTimePrintService googleGetTimePrintService,
 		ServerMessageService messageService,
 		OuterHttpClient httpClient)
 	{
-		//webConnectionString = configuration.GetConnectionString("WebDBContext");
+		
 
-		this.logger = logger;
-		//this.blockingTreePrintService = blockingTreePrintService;
-		//this.googleGetTimePrintService = googleGetTimePrintService;
+		this.logger = logger;	
 		this.messageService = messageService;
 		this.maindbContextFactory = maindbContextFactory;
 		this.webDbConnectionFactory = webDbConnectionFactory;
 		this.meterValueDbService = meterValueDbService;
+		this.connectorStatusDbService = connectorStatusDbService;
 		this.mainDbService = mainDbService;
-		//this.metervaluedbContextFactory = metervaluedbContextFactory;
+	
 		this.businessServiceFactory = businessServiceFactory;
 		this.httpClient = httpClient;
 	}
@@ -294,10 +290,14 @@ internal partial class ProfileHandler
                         long s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0;
                         //只保留最新上報狀況
                         StatusNotificationRequest _request = request as StatusNotificationRequest;
+						
                         int preStatus = 0;
                         ConnectorStatus _oldStatus;
 
-                        _oldStatus = await mainDbService.GetConnectorStatus(session.ChargeBoxId, _request.connectorId);
+						await connectorStatusDbService.InsertAsync(session.ChargeBoxId,(byte) _request.connectorId, (int)_request.status, _request.timestamp.HasValue? _request.timestamp.Value.ToUniversalTime(): DateTime.UtcNow,
+							_request.info, _request.vendorId, _request.vendorErrorCode, (int)_request.errorCode);
+
+						_oldStatus = await mainDbService.GetConnectorStatus(session.ChargeBoxId, _request.connectorId);
 
                         s1 = statusNotificationTimer.ElapsedMilliseconds;
 
@@ -307,7 +307,7 @@ internal partial class ProfileHandler
 
                             await mainDbService.UpdateConnectorStatus(_oldStatus.Id, new ConnectorStatus()
                             {
-                                CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
+                                CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value.ToUniversalTime() : DateTime.UtcNow,
                                 Status = (int)_request.status,
                                 ChargePointErrorCodeId = (int)_request.errorCode,
                                 ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
@@ -318,12 +318,12 @@ internal partial class ProfileHandler
                             if (preStatus == (int)ChargePointStatus.Faulted)
                             {
                                 if (_request.status != ChargePointStatus.Faulted ||
-                                    (_oldStatus.ChargePointErrorCodeId != (int)_request.errorCode && _oldStatus.VendorErrorCode != _request.vendorErrorCode))
+                                    (_oldStatus.ChargePointErrorCodeId != (int)_request.errorCode || _oldStatus.VendorErrorCode != _request.vendorErrorCode))
                                 {
                                     await mainDbService.FillupFinishedTimetoMachineError(
                                         ChargeBoxId: session.ChargeBoxId,
                                         ConnectorId: (byte)_request.connectorId,
-                                        FinishedOn: _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow
+                                        FinishedOn: _request.timestamp.HasValue ? _request.timestamp.Value.ToUniversalTime() : DateTime.UtcNow
                                         );
                                 }
 
@@ -337,7 +337,7 @@ internal partial class ProfileHandler
                             await mainDbService.AddConnectorStatus(
                                 ChargeBoxId: session.ChargeBoxId,
                                 ConnectorId: (byte)_request.connectorId,
-                                CreatedOn: _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
+                                CreatedOn: _request.timestamp.HasValue ? _request.timestamp.Value.ToUniversalTime() : DateTime.UtcNow,
                                 Status: (int)_request.status,
                                 ChargePointErrorCodeId: (int)_request.errorCode,
                                 ErrorInfo: string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
@@ -354,7 +354,7 @@ internal partial class ProfileHandler
                         if (isNeedAddMachineError)
                         {
                             await mainDbService.AddMachineError(ConnectorId: (byte)_request.connectorId,
-                                    CreatedOn: _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
+                                    CreatedOn: _request.timestamp.HasValue ? _request.timestamp.Value.ToUniversalTime() : DateTime.UtcNow,
                                     Status: (int)_request.status,
                                     ChargeBoxId: session.ChargeBoxId,
                                     ErrorCodeId: (int)_request.errorCode,
@@ -376,8 +376,8 @@ internal partial class ProfileHandler
                                 ConnectorId = _request.connectorId,
                                 ErrorCode = _request.errorCode,
                                 Info = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
-                                OCcuredOn = _request.timestamp ?? DateTime.UtcNow,
-                                VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
+                                OCcuredOn = _request.timestamp.HasValue ? _request.timestamp.Value.ToUniversalTime() : DateTime.UtcNow,
+								VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
 
                             });
                         }
@@ -1157,6 +1157,10 @@ internal partial class ProfileHandler
 
 										if (tx.BillingDone) return result;
 
+										if (feedto.StopTime != DefaultSetting.DefaultNullTime)
+										{
+											await mainDbService.InsertOrUpdateTransactionPeriodEnergy(txEnergy.TxId, txEnergy.PeriodEnergy);
+										}
 
 										var startTime = new DateTime(tx.StartTime.Year, tx.StartTime.Month, tx.StartTime.Day, tx.StartTime.Hour, 0, 0);
 										List<ChargingBill> confirmbill = new List<ChargingBill>();
@@ -1223,6 +1227,7 @@ internal partial class ProfileHandler
 
 										if (customerInfo != null && customerInfo.InstantStopTxReport)
 										{
+											var PeriodEnergy = await mainDbService.GetTransactionPeriodEnergy(tx.Id);
 
 											var request = new
 											{
@@ -1237,7 +1242,10 @@ internal partial class ProfileHandler
 												StopReason = tx.StopReasonId < 1 ? "Unknown" : (tx.StopReasonId > 12 ? "Unknown" : ((Reason)tx.StopReasonId).ToString()),
 												Receipt = tx.Receipt,
 												TotalCost = tx.Cost,
-												Fee = tx.Fee
+												Fee = tx.Fee,
+												PeriodEnergy = PeriodEnergy,
+												StartSOC = int.TryParse(tx.StartSoc, out int StartSOCint) ? StartSOCint : (int?)null,
+												StopSOC = int.TryParse(tx.StopSoc, out int StopSOCint) ? StopSOCint : (int?)null
 
 											};
 
@@ -1785,8 +1793,9 @@ internal partial class ProfileHandler
 		energy = Decimal.Parse(energy.ToString("0.0000"));
 		return energy;
 	}
+	
 
-    private static bool CheckNeedAddMachineError(StatusNotificationRequest _request, ConnectorStatus _oldStatus)
+	private static bool CheckNeedAddMachineError(StatusNotificationRequest _request, ConnectorStatus _oldStatus)
     {
 		if (_request.status != ChargePointStatus.Faulted)
 		{
@@ -1803,7 +1812,7 @@ internal partial class ProfileHandler
             {
                 isNeedAddMachineError = true;
             }
-            else if (_oldStatus.ChargePointErrorCodeId != (int)_request.errorCode && _oldStatus.VendorErrorCode != _request.vendorErrorCode)
+            else if (_oldStatus.ChargePointErrorCodeId != (int)_request.errorCode || _oldStatus.VendorErrorCode != _request.vendorErrorCode)
             {
                 isNeedAddMachineError = true;
             }

+ 2 - 1
EVCB_OCPP.WSServer/Properties/launchSettings.json

@@ -4,7 +4,8 @@
       "commandName": "Project"
     },
     "Docker": {
-      "commandName": "Docker"
+      "commandName": "Docker",
+      "DockerfileRunArguments": "-d -p 2022:80"
     }
   }
 }

+ 178 - 0
EVCB_OCPP.WSServer/Service/DbService/ConnectorStatusDbService.cs

@@ -0,0 +1,178 @@
+using Dapper;
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.Domain.ConnectionFactory;
+using EVCB_OCPP.WSServer.Dto;
+using EVCB_OCPP.WSServer.Helper;
+using Microsoft.Data.SqlClient;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using System.Data;
+using System.Data.Common;
+using System.Diagnostics;
+
+namespace EVCB_OCPP.WSServer.Service.DbService;
+
+public class ConnectorStatusDbService
+{
+	private readonly IDbContextFactory<OnlineRecordDBContext> onlineRecordDbContextFactory;
+	private readonly ISqlConnectionFactory<OnlineRecordDBContext> sqlConnectionFactory;
+	private readonly ILoggerFactory loggerFactory;
+
+	
+	private readonly ILogger logger;
+	private readonly Queue<string> _existTables = new();
+
+
+	public ConnectorStatusDbService(
+		IDbContextFactory<OnlineRecordDBContext> onlineRecordDbContextFactory,
+		ISqlConnectionFactory<OnlineRecordDBContext> sqlConnectionFactory,
+		ILogger<ConnectorStatusDbService> logger,
+		ILoggerFactory loggerFactory,
+		IConfiguration configuration		
+		)
+	{
+		this.onlineRecordDbContextFactory = onlineRecordDbContextFactory;
+		this.sqlConnectionFactory = sqlConnectionFactory;
+		this.loggerFactory = loggerFactory;	
+		this.logger = logger;
+
+	
+
+	}
+
+	public Task InsertAsync(string chargeBoxId, byte connectorId, int status, DateTime createdOn
+		   , string errorInfo, string vendorId, string vendorErrorCode, int chargePointErrorCodeId)
+	{
+		var param = new InsertConnectorStatusParam(chargeBoxId,  connectorId,  status,   errorInfo
+			,  vendorId,  createdOn,  vendorErrorCode,  chargePointErrorCodeId);
+
+		return InsertAsync(param);
+	}
+
+	public Task InsertAsync(InsertConnectorStatusParam param)
+	{	
+		return InsertWithDapper(param);
+	}
+	
+
+	private async Task InsertWithDapper(InsertConnectorStatusParam param)
+	{
+		var watch = Stopwatch.StartNew();
+		long t0, t1;
+
+		if (!await GetTableExist(param.createdOn))
+		{
+			t0 = watch.ElapsedMilliseconds;
+			await InsertWithStoredProcedure(param);
+			watch.Stop();
+			t1 = watch.ElapsedMilliseconds;
+			if (t1 > 500)
+			{
+				logger.LogWarning("ConnectorStatusRecord InsertWithStoredProcedure {0}/{1}", t0, t1);
+			}
+			return;
+		}
+
+		t0 = watch.ElapsedMilliseconds;
+		var tableName = GetTableName(param.createdOn);
+
+		await InsertWithNoCheckDapper(tableName, param);
+
+		watch.Stop();
+		t1 = watch.ElapsedMilliseconds;
+		if (t1 > 700)
+		{
+			logger.LogWarning("ConnectorStatusRecord Dapper {0}/{1}", t0, t1);
+		}
+	}
+
+
+	private async Task InsertWithNoCheckDapper(string tableName, InsertConnectorStatusParam data, SqlConnection conn = null, DbTransaction trans = null)
+	{
+		string command = $"""
+                INSERT INTO {tableName} ([ChargeBoxId],[ConnectorId]
+                ,[Status],[CreatedOn],[ChargePointErrorCodeId],[ErrorInfo]
+                ,[VendorId],[VendorErrorCode])
+                VALUES (@ChargeBoxId,@ConnectorId, @Status, @CreatedOn,@ChargePointErrorCodeId, @ErrorInfo, @VendorId, @VendorErrorCode);
+                """;
+
+		bool isLocalConnection = conn is null;
+		SqlConnection connection = isLocalConnection ? await sqlConnectionFactory.CreateAsync() : conn;
+
+		var parameters = new DynamicParameters();
+		parameters.Add("ConnectorId", data.connectorId, DbType.Int16);
+		parameters.Add("Status", data.status, DbType.Int32);
+		parameters.Add("ErrorInfo", data.errorInfo, DbType.String, size: 50);
+		parameters.Add("VendorId", data.vendorId, DbType.String, size: 255);
+		parameters.Add("CreatedOn", data.createdOn, DbType.DateTime);
+		parameters.Add("VendorErrorCode", data.vendorErrorCode, DbType.String, size: 100);
+		parameters.Add("ChargePointErrorCodeId", data.chargePointErrorCodeId, DbType.Int32);
+		parameters.Add("ChargeBoxId", data.chargeBoxId, DbType.String, size: 50);		
+
+		await connection.ExecuteAsync(command, parameters, trans);
+
+		if (isLocalConnection)
+		{
+			connection.Dispose();
+		}
+	}
+
+	
+
+	private async ValueTask<bool> GetTableExist(DateTime tableDateTime)
+	{
+		var tableName = GetTableName(tableDateTime);
+		if (_existTables.Contains(tableName))
+		{
+			return true;
+		}
+
+		FormattableString checkTableSql = $"SELECT Count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = {tableName}";
+
+		using var db = await onlineRecordDbContextFactory.CreateDbContextAsync();
+		var resultList = db.Database.SqlQuery<int>(checkTableSql)?.ToList();
+
+		if (resultList is not null && resultList.Count > 0 && resultList[0] > 0)
+		{
+			_existTables.Enqueue(tableName);
+			if (_existTables.Count > 30)
+			{
+				_existTables.TryDequeue(out _);
+			}
+			return true;
+		}
+
+		return false;
+	}
+
+	private async Task InsertWithStoredProcedure(InsertConnectorStatusParam param)
+	{
+		using var db = await onlineRecordDbContextFactory.CreateDbContextAsync();
+
+		string sp = "[dbo].[uspInsertConnectorStatusRecord] @ChargeBoxId," +
+"@ConnectorId,@Status,@CreatedOn,@ChargePointErrorCodeId,@ErrorInfo,@VendorId,@VendorErrorCode";
+
+		List<SqlParameter> parameter = new List<SqlParameter>();
+		parameter.AddInsertConnectorStatusRecordSqlParameters(
+			chargeBoxId: param.chargeBoxId
+			, connectorId: param.connectorId
+			, status: param.status
+			, errorInfo: param.errorInfo
+			, vendorId: param.vendorId
+			, createdOn: param.createdOn
+			, vendorErrorCode: param.vendorErrorCode
+			, chargePointErrorCodeId: param.chargePointErrorCodeId	);
+
+		await db.Database.ExecuteSqlRawAsync(sp, parameter.ToArray());
+	}
+
+	private static string GetTableName(DateTime dateTime)
+		=> $"ConnectorStatusRecord{dateTime:yyMMdd}";
+
+
+}
+
+public record InsertConnectorStatusParam(string chargeBoxId, byte connectorId, int status, string  errorInfo
+			, string vendorId, DateTime createdOn, string vendorErrorCode, int chargePointErrorCodeId);

+ 145 - 11
EVCB_OCPP.WSServer/Service/DbService/MainDbService.cs

@@ -58,6 +58,12 @@ public interface IMainDbService
     Task UpdateMachineConfiguration(string chargeBoxId, string item, string empty, bool isReadOnly, bool isExists = true);
     Task<List<MachineConfigurations>> GetMachineConfiguration(string chargeBoxId);
     Task<object> TryGetResponseFromDb(string msgId, CancellationToken token = default);
+
+    Task InsertOrUpdateTransactionPeriodEnergy(int txId, Dictionary<string, decimal> periodEnergy);
+
+    Task<Dictionary<string, decimal>> GetTransactionPeriodEnergy(int transactionId);
+
+
 }
 
 public class MainDbService : IMainDbService
@@ -240,13 +246,13 @@ public class MainDbService : IMainDbService
         return AddMachineErrorDapper(ConnectorId, CreatedOn, Status, ChargeBoxId, ErrorCodeId, ErrorInfo, PreStatus, VendorErrorCode, VendorId);
     }
 
-	public ValueTask FillupFinishedTimetoMachineError(string ChargeBoxId,byte ConnectorId, DateTime FinishedOn)
-	{
-		
-		return AddFinishedTimetoMachineErrorDapper(ChargeBoxId, ConnectorId, FinishedOn);
-	}
+    public ValueTask FillupFinishedTimetoMachineError(string ChargeBoxId, byte ConnectorId, DateTime FinishedOn)
+    {
 
-	public async Task<string> AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy, DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "")
+        return AddFinishedTimetoMachineErrorDapper(ChargeBoxId, ConnectorId, FinishedOn);
+    }
+
+    public async Task<string> AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy, DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "")
     {
         if (string.IsNullOrEmpty(CreatedBy))
         {
@@ -691,7 +697,7 @@ public class MainDbService : IMainDbService
         var gReult = result.GroupBy(x => x.ChargeBoxId);
         foreach (var g in gReult)
         {
-            var originKey = chargeboxIds.FirstOrDefault(x=> x.ToLower() == g.Key.ToLower());
+            var originKey = chargeboxIds.FirstOrDefault(x => x.ToLower() == g.Key.ToLower());
             if (string.IsNullOrEmpty(originKey))
             {
                 continue;
@@ -852,8 +858,8 @@ public class MainDbService : IMainDbService
             """, parameters);
     }
 
-	private async ValueTask AddFinishedTimetoMachineErrorDapper(string chargeBoxId, byte connectorId, DateTime finishedTime)
-	{
+    private async ValueTask AddFinishedTimetoMachineErrorDapper(string chargeBoxId, byte connectorId, DateTime finishedTime)
+    {
         var getCommand = """
             SELECT TOP(1) Id
             FROM [dbo].[MachineError]
@@ -862,8 +868,8 @@ public class MainDbService : IMainDbService
             """;
 
         var parameters = new DynamicParameters();
-		parameters.Add("@ConnectorId", connectorId, DbType.Int16, ParameterDirection.Input);	
-		parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@ConnectorId", connectorId, DbType.Int16, ParameterDirection.Input);
+        parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
         //parameters.Add("@CreatedOn", previousErrorOn, DbType.DateTime, ParameterDirection.Input, 50);
 
         int? recordId = null;
@@ -1308,6 +1314,134 @@ public class MainDbService : IMainDbService
         machine.ConnectionType = v;
         return;
     }
+
+
+
+    public async Task<Dictionary<string, decimal>> GetTransactionPeriodEnergy(int transactionId)
+    {
+        var parms = new DynamicParameters();
+        parms.Add("@TransactionId", transactionId);
+
+        var cmd = """
+                SELECT TOP(1) 
+                [H00],[H01],[H02],[H03],[H04],[H05],[H06],[H07],[H08],[H09],[H10]
+                ,[H11],[H12],[H13],[H14],[H15],[H16],[H17],[H18],[H19],[H20]
+                ,[H21],[H22],[H23]
+                FROM [dbo].[TransactionPeriodEnergy]
+                WHERE TransactionId = @TransactionId
+                """;
+
+        using var dbConn = await sqlConnectionFactory.CreateAsync();
+        dynamic queryResult = await dbConn.QueryFirstOrDefaultAsync(cmd, parms);
+        if (queryResult is not IDictionary<string, object> queryResultPairs)
+        {
+            return null;
+        }
+
+        var toReturn = new Dictionary<string, decimal>();
+        for (int hour = 0; hour < 24; hour++)
+        {
+            var key = GetColName(hour);
+            if (queryResultPairs[key] is decimal value)
+            {
+                toReturn.Add(hour.ToString(), value);
+            }
+        }
+
+        return toReturn;
+
+    }
+
+    public async Task InsertOrUpdateTransactionPeriodEnergy(int txId, Dictionary<string, decimal> periodEnergy)
+    {
+        try
+        {
+            var isExists = await GetTransactionPeriodEnergyExists(txId);
+            var param = CreateParameters(txId, periodEnergy);
+
+            if (isExists)
+            {
+                await UpdateTransactionPeriodEnergy(param);
+                return;
+            }
+            await InsertTransactionPeriodEnergy(param);
+        }
+        catch (Exception e)
+        {
+            logger.LogError(e.Message);
+        }
+        return;
+
+        async Task<bool> GetTransactionPeriodEnergyExists(int txId)
+        {
+            var param = new DynamicParameters();
+            param.Add("@TransactionId", txId, DbType.Int32, ParameterDirection.Input);
+
+            var cmd = """
+            SELECT TransactionId
+            FROM TransactionPeriodEnergy
+            WHERE TransactionId = @TransactionId
+            """;
+            using var conn = await sqlConnectionFactory.CreateAsync();
+            return await conn.QueryFirstOrDefaultAsync<int?>(cmd, param) != null;
+        }
+
+        async Task<int> UpdateTransactionPeriodEnergy(DynamicParameters param)
+        {
+            var cmd = """
+            UPDATE TransactionPeriodEnergy
+            SET H00 = @H00, H01 = @H01, H02 = @H02, H03 = @H03, H04 = @H04, H05 = @H05, H06 = @H06, H07 = @H07, H08 = @H08,
+            H09 = @H09, H10 = @H10, H11 = @H11, H12 = @H12, H13 = @H13, H14 = @H14, H15 = @H15, H16 = @H16, H17 = @H17,
+            H18 = @H18, H19 = @H19, H20 = @H20, H21 = @H21, H22 = @H22, H23 = @H23
+            WHERE TransactionId = @TransactionId
+            """;
+            using var conn = await sqlConnectionFactory.CreateAsync();
+            return await conn.ExecuteAsync(cmd, param);
+        }
+
+        async Task InsertTransactionPeriodEnergy(DynamicParameters param)
+        {
+            var cmd = """
+            INSERT INTO TransactionPeriodEnergy
+            (TransactionId, H00, H01, H02, H03, H04, H05, H06, H07, H08, H09, H10, H11, H12, H13, H14, H15, H16, H17, H18, H19, H20, H21, H22, H23)
+            VALUES (@TransactionId, @H00, @H01, @H02, @H03, @H04, @H05, @H06, @H07, @H08, @H09, @H10, @H11, @H12, @H13, @H14, @H15, @H16, @H17, @H18, @H19, @H20, @H21, @H22, @H23)
+            """;
+            using var conn = await sqlConnectionFactory.CreateAsync();
+            await conn.ExecuteAsync(cmd, param);
+        }
+
+        DynamicParameters CreateParameters(int txId, Dictionary<string, decimal> periodEnergy)
+        {
+            var parameters = new DynamicParameters();
+            parameters.Add("@TransactionId", txId, DbType.Int32, ParameterDirection.Input);
+
+            for (int hour = 0; hour < 24; hour++)
+            {
+                var key = hour.ToString();
+                if (!periodEnergy.ContainsKey(key))
+                {
+                    parameters.Add(GetColName(hour), 0, DbType.Decimal, ParameterDirection.Input, precision: 18, scale: 4);
+                    continue;
+                }
+
+                parameters.Add(GetColName(hour), periodEnergy[key], DbType.Decimal, ParameterDirection.Input, precision: 18, scale: 4);
+            }
+            return parameters;
+        }
+
+        string GetColName(int hour)
+        {
+            return $"@H{hour.ToString("00")}";
+        }
+
+
+
+    }
+
+	string GetColName(int hour)
+	{
+		return $"H{hour.ToString("00")}";
+	}
 }
 
 public record MachineAndCustomerInfo(string MachineId, Guid CustomerId, string CustomerName);

+ 3 - 2
EVCB_OCPP.WSServer/appsettings.json

@@ -37,7 +37,7 @@
       },
       "Console": {
         "type": "Console",
-        "layout": "${longdate} ${uppercase:${level}} ${message}"
+        "layout": "${longdate} ${uppercase:${level}} ${callsite} ${message}"
       },
       "auth": {
         "type": "File",
@@ -144,6 +144,7 @@
     "ConnectionLogDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_ConnectionLog;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=200;Connection Lifetime=0;Pooling=true;",
     "MainDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_Main;;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=1024;Connection Lifetime=0;Pooling=true;Min Pool Size=150;",
     "MeterValueDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_MeterValue;;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=200;Connection Lifetime=0;Pooling=true;",
-    "WebDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_Web;;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=200;Connection Lifetime=0;Pooling=true;"
+    "WebDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_Web;;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=200;Connection Lifetime=0;Pooling=true;",
+    "OnlineRecordDBContext": "data source=zerova-ev-dev.database.windows.net;initial catalog=StandardOCPP_OnlineRecord;persist security info=True;user id=azdevsoftware;password=1h52dev#az;MultipleActiveResultSets=True;App=EntityFramework;TrustServerCertificate=true;Max Pool Size=200;Connection Lifetime=0;Pooling=true;"
   }
 }

+ 7 - 0
entrypoint.sh

@@ -0,0 +1,7 @@
+#!/bin/bash
+set -e
+service cron start
+service ssh start
+#dotnet run --project ./EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj
+#/app/publish/EVCB_OCPP.WSServer
+dotnet EVCB_OCPP.WSServer.dll