Robert пре 1 година
родитељ
комит
270ad26d6c

+ 23 - 17
Dockerfile

@@ -1,32 +1,38 @@
 #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
+FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
 EXPOSE 80
 EXPOSE 443
-EXPOSE 2222 
-
-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
+EXPOSE 54088
+WORKDIR /app
 
 RUN apt-get update \
     && apt-get install -y --no-install-recommends dialog \
     && apt-get install -y --no-install-recommends openssh-server \
+	&& apt-get install -y tcpdump\
 	&& mkdir -p /run/sshd \
     && echo "root:Docker!" | chpasswd 
 	
 COPY sshd_config /etc/ssh/sshd_config
 
-# Install dotnet debug tools
-RUN dotnet tool install --tool-path /tools dotnet-trace \
- && dotnet tool install --tool-path /tools dotnet-counters \
- && dotnet tool install --tool-path /tools dotnet-dump \
- && dotnet tool install --tool-path /tools dotnet-gcdump
- 
-RUN apt update
-RUN apt install -y linux-perf
-#RUN echo 0 > /proc/sys/kernel/kptr_restrict
+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 . .
-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
+WORKDIR "/src/EVCB_OCPP.WSServer"
+RUN dotnet build "EVCB_OCPP.WSServer.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "EVCB_OCPP.WSServer.csproj" -c Release -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+COPY entrypoint.sh .
+RUN chmod +x /app/entrypoint.sh
+CMD ["/app/entrypoint.sh"]

+ 4 - 3
Dockerfile_dev

@@ -3,8 +3,7 @@
 FROM mcr.microsoft.com/dotnet/sdk:7.0 AS final
 EXPOSE 80
 EXPOSE 443
-EXPOSE 54088 
-EXPOSE 54089 
+EXPOSE 54088
 EXPOSE 2222 
 
 #RUN sed -i 's/TLSv1.2/TLSv1/g' /etc/ssl/openssl.cnf
@@ -30,5 +29,7 @@ RUN dotnet tool install --tool-path /tools dotnet-trace \
 WORKDIR /src
 COPY . .
 #RUN export DOTNET_PerfMapEnabled=1
+RUN dotnet restore "EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj"
 RUN dotnet build ./EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj
-CMD dotnet run --project ./EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj
+RUN chmod +x /src/entrypoint.sh
+CMD ["/src/entrypoint.sh"]

+ 40 - 0
Dockerfile_dev2

@@ -0,0 +1,40 @@
+#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
+ENV COMPlus_PerfMapEnabled=1
+ENV COMPlus_EnableEventLog=1
+
+EXPOSE 80
+EXPOSE 443
+EXPOSE 54088
+EXPOSE 2222 
+
+#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
+
+RUN apt-get update \
+    && apt-get install -y --no-install-recommends dialog \
+    && apt-get install -y --no-install-recommends openssh-server \
+	&& apt-get install -y tcpdump\
+	&& mkdir -p /run/sshd \
+    && echo "root:Docker!" | chpasswd 
+	
+COPY sshd_config /etc/ssh/sshd_config
+
+# Install dotnet debug tools
+RUN dotnet tool install --tool-path /tools dotnet-trace \
+ && dotnet tool install --tool-path /tools dotnet-counters \
+ && dotnet tool install --tool-path /tools dotnet-dump \
+ && dotnet tool install --tool-path /tools dotnet-gcdump
+ 
+#RUN apt update
+#RUN apt install -y linux-perf
+#RUN echo 0 > /proc/sys/kernel/kptr_restrict
+WORKDIR /src
+COPY . .
+#RUN export DOTNET_PerfMapEnabled=1
+RUN dotnet restore "EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj"
+#RUN dotnet build ./EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj
+RUN dotnet publish ./EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj -r linux-x64 -c Release -o /app/publish --self-contained
+RUN chmod +x /src/entrypoint.sh
+CMD ["/src/entrypoint.sh"]

+ 27 - 0
Dockerfile_publish

@@ -0,0 +1,27 @@
+#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/aspnet:7.0 AS base
+EXPOSE 80
+EXPOSE 443
+EXPOSE 54088
+WORKDIR /app
+
+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"
+RUN dotnet build "EVCB_OCPP.WSServer.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "EVCB_OCPP.WSServer.csproj" -c Release -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "EVCB_OCPP.WSServer.dll"]

+ 31 - 6
EVCB_OCPP.WSServer/Helper/AddPortalDbContext.cs

@@ -10,6 +10,7 @@ using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Data.Entity.Infrastructure;
+using System.Diagnostics;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -28,7 +29,8 @@ public static class AddPortalDbContext
         var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
 
         services.AddSingleton(
-            new SqlConnectionFactory<MainDBContext>()
+            (serviceProvider) =>
+            new SqlConnectionFactory<MainDBContext>(serviceProvider.GetRequiredService<ILogger<SqlConnectionFactory>>())
             {
                 ConnectionString = conneciotnString
             });
@@ -44,7 +46,8 @@ public static class AddPortalDbContext
 
         var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
         services.AddSingleton(
-            new SqlConnectionFactory<MeterValueDBContext>()
+            (serviceProvider) =>
+            new SqlConnectionFactory<MeterValueDBContext>(serviceProvider.GetRequiredService<ILogger<SqlConnectionFactory>>())
             {
                 ConnectionString = conneciotnString
             });
@@ -60,7 +63,8 @@ public static class AddPortalDbContext
 
         var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
         services.AddSingleton(
-            new SqlConnectionFactory<ConnectionLogDBContext>()
+            (serviceProvider) =>
+            new SqlConnectionFactory<ConnectionLogDBContext>(serviceProvider.GetRequiredService<ILogger<SqlConnectionFactory>>())
             {
                 ConnectionString = conneciotnString
             });
@@ -76,7 +80,8 @@ public static class AddPortalDbContext
 
         var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
         services.AddSingleton(
-            new SqlConnectionFactory<WebDBConetext>()
+            (serviceProvider) =>
+            new SqlConnectionFactory<WebDBConetext>(serviceProvider.GetRequiredService<ILogger<SqlConnectionFactory>>())
             {
                 ConnectionString = conneciotnString
             });
@@ -91,7 +96,8 @@ public static class AddPortalDbContext
 
         var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
         services.AddSingleton(
-            new SqlConnectionFactory<OnlineLogDBContext>()
+            (serviceProvider) =>
+            new SqlConnectionFactory<OnlineLogDBContext>(serviceProvider.GetRequiredService<ILogger<SqlConnectionFactory>>())
             {
                 ConnectionString = conneciotnString
             });
@@ -124,8 +130,14 @@ public static class AddPortalDbContext
 
 public class SqlConnectionFactory<T> where T: DbContext
 {
+    private readonly ILogger<SqlConnectionFactory> logger;
+
     public string ConnectionString { get; init; }
-    public SqlConnectionFactory() { }
+    public SqlConnectionFactory(ILogger<SqlConnectionFactory> logger)
+    {
+        this.logger = logger;
+    }
+
     public SqlConnection Create()
     {
         var sqlConnection = new SqlConnection(ConnectionString);
@@ -135,8 +147,21 @@ public class SqlConnectionFactory<T> where T: DbContext
 
     public async Task<SqlConnection> CreateAsync()
     {
+        var timer = Stopwatch.StartNew();
+        long t0, t1;
+
         var sqlConnection = new SqlConnection(ConnectionString);
+        t0 = timer.ElapsedMilliseconds;
+
         await sqlConnection.OpenAsync();
+        t1 = timer.ElapsedMilliseconds;
+        timer.Stop();
+
+        if (t1 > 500)
+        {
+            logger.LogWarning($"{nameof(T)} SqlConnection Open slow {t0}/{t1}");
+        }
+
         return sqlConnection;
     }
 }

+ 22 - 0
EVCB_OCPP.WSServer/Helper/DummyHostLifeTime.cs

@@ -0,0 +1,22 @@
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Helper
+{
+    internal class DummyHostLifeTime : IHostLifetime
+    {
+        public Task StopAsync(CancellationToken cancellationToken)
+        {
+            return Task.CompletedTask;
+        }
+
+        public Task WaitForStartAsync(CancellationToken cancellationToken)
+        {
+            return Task.CompletedTask;
+        }
+    }
+}

+ 1 - 1
EVCB_OCPP.WSServer/Helper/MeterValueGroupSingleHandler.cs

@@ -201,7 +201,7 @@ public class MeterValueGroupSingleHandler
         FormattableString checkTableSql = $"SELECT Count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = {tableName}";
 
         using var db = await meterValueDbContextFactory.CreateDbContextAsync();
-        var resultList = db.Database.SqlQuery<int>(checkTableSql)?.ToList();
+        var resultList = await db.Database.SqlQuery<int>(checkTableSql)?.ToListAsync();
 
         if (resultList is not null && resultList.Count > 0 && resultList[0] > 0)
         {

+ 8 - 8
EVCB_OCPP.WSServer/HostedProtalServer.cs

@@ -120,14 +120,14 @@ namespace EVCB_OCPP.WSServer
                         .RepeatForever())
                 );
 
-                q.ScheduleJob<GoogleCheckJob>(trigger =>
-                    trigger
-                    .WithIdentity("GoogleCheckJobTrigger")
-                    .StartNow()
-                    .WithSimpleSchedule(x => x
-                        .WithIntervalInSeconds(5)
-                        .RepeatForever())
-                );
+                //q.ScheduleJob<GoogleCheckJob>(trigger =>
+                //    trigger
+                //    .WithIdentity("GoogleCheckJobTrigger")
+                //    .StartNow()
+                //    .WithSimpleSchedule(x => x
+                //        .WithIntervalInSeconds(5)
+                //        .RepeatForever())
+                //);
             });
 
             services.AddQuartzHostedService(opt =>

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

@@ -54,7 +54,8 @@ public class DenyModelCheckJob : IJob
                 var removeClients = _copyClientDic.Where(x => x.Key.StartsWith(denyName)).Select(x => x.Value).ToList();
                 foreach (var session in removeClients)
                 {
-                    Console.WriteLine(string.Format("Server forced to shut down ChargeBox ({0}: Reason: DenyModelName-{1}", session.ChargeBoxId, denyName));
+                    //Console.WriteLine(string.Format("Server forced to shut down ChargeBox ({0}: Reason: DenyModelName-{1}", session.ChargeBoxId, denyName));
+                    logger.LogInformation(string.Format("Server forced to shut down ChargeBox ({0}: Reason: DenyModelName-{1}", session.ChargeBoxId, denyName));
                     protalServer.RemoveClient(session);
                 }
             }

+ 158 - 144
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -28,7 +28,6 @@ using System.Globalization;
 using System.Linq;
 using System.Threading.Tasks;
 
-using Dapper;
 using Microsoft.AspNetCore.Http.HttpResults;
 using MongoDB.Driver.Core.Connections;
 using EVCB_OCPP20.Packet.DataTypes;
@@ -258,10 +257,12 @@ internal partial class ProfileHandler
 
                         session.IsPending = false;
 
-                        var confirm = new BootNotificationConfirmation() {
-                            currentTime = DateTime.UtcNow, 
+                        var confirm = new BootNotificationConfirmation()
+                        {
+                            currentTime = DateTime.UtcNow,
                             interval = session.IsPending ? 5 : heartbeat_interval,
-                            status = session.IsPending ? RegistrationStatus.Pending : RegistrationStatus.Accepted };
+                            status = session.IsPending ? RegistrationStatus.Pending : RegistrationStatus.Accepted
+                        };
 
                         result.Message = confirm;
                         result.Success = true;
@@ -284,7 +285,8 @@ internal partial class ProfileHandler
                         {
                             preStatus = _oldStatus.Status;
 
-                            await mainDbService.UpdateConnectorStatus(_oldStatus.Id, new ConnectorStatus() {
+                            await mainDbService.UpdateConnectorStatus(_oldStatus.Id, new ConnectorStatus()
+                            {
                                 CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
                                 Status = (int)_request.status,
                                 ChargePointErrorCodeId = (int)_request.errorCode,
@@ -347,7 +349,7 @@ internal partial class ProfileHandler
                         result.Success = true;
 
                         statusNotificationTimer.Stop();
-                        if(statusNotificationTimer.ElapsedMilliseconds/1000 > 1)
+                        if (statusNotificationTimer.ElapsedMilliseconds / 1000 > 1)
                         {
                             logger.LogCritical(string.Format("StatusNotification took {0}/{1}/{2}/{3}/{4}", s1, s2, s3, s4, s5));
                         }
@@ -445,7 +447,7 @@ internal partial class ProfileHandler
                                     await mainDbService.AddServerMessage(
                                             ChargeBoxId: session.ChargeBoxId,
                                             OutAction: Actions.DataTransfer.ToString(),
-                                            OutRequest: 
+                                            OutRequest:
                                                 new DataTransferRequest()
                                                 {
                                                     messageId = "ID_TxEnergy",
@@ -480,7 +482,7 @@ internal partial class ProfileHandler
                 case Actions.StartTransaction:
                     {
                         var timer = Stopwatch.StartNew();
-                        long t0 = 0, t1 = 0, t2 = 0, t3 = 0,t4 =0, t5 = 0;
+                        long t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0;
 
                         StartTransactionRequest _request = request as StartTransactionRequest;
 
@@ -537,60 +539,58 @@ internal partial class ProfileHandler
                             }
                         }
 
+                        var _CustomerId = await mainDbService.GetCustomerIdByChargeBoxId(session.ChargeBoxId);
+                        t2 = timer.ElapsedMilliseconds;
 
-                        using (var db = await maindbContextFactory.CreateDbContextAsync())
-                        {
-                            var _CustomerId = await db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).Include(x => x.Customer).
-                                 Select(x => x.CustomerId).FirstOrDefaultAsync();
-                            t2 = timer.ElapsedMilliseconds;
-
-                            var _existedTx = await db.TransactionRecord.Where(x => x.CustomerId == _CustomerId && x.ChargeBoxId == session.ChargeBoxId
-                               && x.ConnectorId == _request.connectorId && x.StartTime == _request.timestamp).Select(C => new { C.Id }).AsNoTracking().FirstOrDefaultAsync();
-                            t3 = timer.ElapsedMilliseconds;
-
-                            TransactionRecord _newTransaction = new TransactionRecord();
+                        var _existedTx = await mainDbService.TryGetDuplicatedTransactionId(session.ChargeBoxId, _CustomerId, _request.connectorId, _request.timestamp);
+                        t3 = timer.ElapsedMilliseconds;
 
+                        if (_existedTx != null)
+                        {
+                            _transactionId = _existedTx.Value;
+                            logger.LogError("Duplication ***************************************************** " + _existedTx);
+                        }
+                        else
+                        {
+                            TransactionRecord _newTransaction;//= new TransactionRecord();
+                            _newTransaction = new TransactionRecord()
+                            {
+                                ChargeBoxId = session.ChargeBoxId,
+                                ConnectorId = (byte)_request.connectorId,
+                                CreatedOn = DateTime.UtcNow,
+                                StartIdTag = _request.idTag,
+                                MeterStart = _request.meterStart,
+                                CustomerId = _CustomerId,
+                                StartTime = _request.timestamp.ToUniversalTime(),
+                                ReservationId = _request.reservationId.HasValue ? _request.reservationId.Value : 0,
+                            };
+
+                            if (session.UserPrices.ContainsKey(_request.idTag))
+                            {
+                                _newTransaction.Fee = !session.IsBilling ? string.Empty : session.UserPrices[_request.idTag];
 
-                            if (_existedTx == null)
+                            }
+                            else
                             {
-                                _newTransaction = new TransactionRecord()
-                                {
-                                    ChargeBoxId = session.ChargeBoxId,
-                                    ConnectorId = (byte)_request.connectorId,
-                                    CreatedOn = DateTime.UtcNow,
-                                    StartIdTag = _request.idTag,
-                                    MeterStart = _request.meterStart,
-                                    CustomerId = _CustomerId,
-                                    StartTime = _request.timestamp.ToUniversalTime(),
-                                    ReservationId = _request.reservationId.HasValue ? _request.reservationId.Value : 0,
-                                };
+                                _newTransaction.Fee = !session.IsBilling ? string.Empty : session.BillingMethod == 1 ? JsonConvert.SerializeObject(session.ChargingPrices) : session.ChargingFeebyHour.ToString();
+                                _newTransaction.Fee += !session.IsBilling ? string.Empty : "|+" + accountBalance + "+" + "&" + session.ParkingFee + "&|" + session.Currency;
+                            }
 
-                                if (session.UserPrices.ContainsKey(_request.idTag))
-                                {
-                                    _newTransaction.Fee = !session.IsBilling ? string.Empty : session.UserPrices[_request.idTag];
+                            //using (var db = await maindbContextFactory.CreateDbContextAsync())
+                            //{
+                            //    await db.TransactionRecord.AddAsync(_newTransaction);
 
-                                }
-                                else
-                                {
-                                    _newTransaction.Fee = !session.IsBilling ? string.Empty : session.BillingMethod == 1 ? JsonConvert.SerializeObject(session.ChargingPrices) : session.ChargingFeebyHour.ToString();
-                                    _newTransaction.Fee += !session.IsBilling ? string.Empty : "|+" + accountBalance + "+" + "&" + session.ParkingFee + "&|" + session.Currency;
-                                }
+                            //    await db.SaveChangesAsync();
 
-                                db.TransactionRecord.Add(_newTransaction);
+                            //    _transactionId = _newTransaction.Id;
+                            //}
 
-                                await db.SaveChangesAsync();
-                                t4 = timer.ElapsedMilliseconds;
+                            _transactionId = await mainDbService.AddNewTransactionRecord(_newTransaction);
+                            t4 = timer.ElapsedMilliseconds;
 
-                                _transactionId = _newTransaction.Id;
-                                logger.LogInformation("***************************************************** ");
-                                logger.LogInformation(string.Format("{0} :TransactionId {1} ", session.ChargeBoxId, _newTransaction.Id));
-                                logger.LogInformation("***************************************************** ");
-                            }
-                            else
-                            {
-                                _transactionId = _existedTx.Id;
-                                logger.LogError("Duplication ***************************************************** " + _existedTx.Id);
-                            }
+                            logger.LogInformation("***************************************************** ");
+                            logger.LogInformation(string.Format("{0} :TransactionId {1} ", session.ChargeBoxId, _transactionId));
+                            logger.LogInformation("***************************************************** ");
                         }
 
 
@@ -608,7 +608,7 @@ internal partial class ProfileHandler
                         t5 = timer.ElapsedMilliseconds;
                         if (t5 > 1000)
                         {
-                            logger.Log(LogLevel.Critical, "{action} {sessisonId} time {t0}/{t1}/{t2}/{t3}/{t4}/{totalTime}", action.ToString(), session.SessionID, t0, t1, t2, t3, t4, t5);
+                            logger.Log(LogLevel.Critical, "{action} {ChargeBoxId} time {t0}/{t1}/{t2}/{t3}/{t4}/{totalTime}", action.ToString(), session.ChargeBoxId, t0, t1, t2, t3, t4, t5);
                         }
                     }
                     break;
@@ -627,11 +627,12 @@ internal partial class ProfileHandler
                         getServiceTime = stopTrasactionTimer.ElapsedMilliseconds;
 
 
-                        var _idTagInfo = string.IsNullOrEmpty(_request.idTag) ? null :  (
-                            _request.idTag == "Backend" ? 
-                                new IdTagInfo() { 
-                                    expiryDate = utcNow.AddDays(1), 
-                                    status = AuthorizationStatus.Accepted 
+                        var _idTagInfo = string.IsNullOrEmpty(_request.idTag) ? null : (
+                            _request.idTag == "Backend" ?
+                                new IdTagInfo()
+                                {
+                                    expiryDate = utcNow.AddDays(1),
+                                    status = AuthorizationStatus.Accepted
                                 } :
                                 (await businessService.Authorize(session.ChargeBoxId, _request.idTag)).IdTagInfo
                             );
@@ -644,99 +645,112 @@ internal partial class ProfileHandler
                         }
                         try
                         {
-                            using (var db = await maindbContextFactory.CreateDbContextAsync())
+                            //遠傳太久以前的停止充電 直接拒絕 避免電樁持續重送~~~~~~~
+                            if (_request.timestamp < new DateTime(2021, 11, 1))
                             {
-                                var transaction = db.TransactionRecord.Where(x => x.Id == _request.transactionId
-                                 && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+                                var confirm = new StopTransactionConfirmation()
+                                {
+                                    idTagInfo = new IdTagInfo()
+                                    {
+                                        status = AuthorizationStatus.Invalid
+                                    }
 
+                                };
+
+                                result.Message = confirm;
+                                result.Success = true;
+                                return result;
+                            }
 
+                            TransactionRecord transaction;
+                            transaction = await mainDbService.GetTransactionForStopTransaction(_request.transactionId, session.ChargeBoxId);
+                            //using (var db = await maindbContextFactory.CreateDbContextAsync())
+                            //{
+                            //    transaction = db.TransactionRecord.Where(x => x.Id == _request.transactionId
+                            //     && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+                            //}
 
-                                //遠傳太久以前的停止充電 直接拒絕 避免電樁持續重送~~~~~~~
-                                if (_request.timestamp < new DateTime(2021, 11, 1))
+                            if (transaction is null)
+                            {
+                                result.Exception = new Exception("Can't find transactionId " + _request.transactionId);
+                            }
+                            else
+                            {
+                                _ConnectorId = transaction.ConnectorId;
+
+                                var confirm = new StopTransactionConfirmation()
                                 {
-                                    var confirm = new StopTransactionConfirmation()
-                                    {
-                                        idTagInfo = new IdTagInfo()
-                                        {
-                                            status = AuthorizationStatus.Invalid
-                                        }
+                                    idTagInfo = _idTagInfo
 
-                                    };
+                                };
 
+                                //Avoid rewrite transaction data
+                                if (transaction.StopTime != GlobalConfig.DefaultNullTime)
+                                {
                                     result.Message = confirm;
                                     result.Success = true;
                                     return result;
                                 }
 
-
-                                if (transaction is null)
+                                await mainDbService.UpdateTransaction(_request.transactionId,
+                                    meterStop: _request.meterStop,
+                                    stopTime: _request.timestamp.ToUniversalTime(),
+                                    stopReasonId: _request.reason.HasValue ? (int)_request.reason.Value : 0,
+                                    stopReason: _request.reason.HasValue ? _request.reason.Value.ToString() : Reason.Local.ToString(),
+                                    stopIdTag: _request.idTag,
+                                    receipt: string.Empty,
+                                    cost: session.IsBilling ? -1 : 0);
+
+                                //using (var db = await maindbContextFactory.CreateDbContextAsync())
+                                //{
+                                //    var _transaction = db.TransactionRecord.Where(x => x.Id == _request.transactionId
+                                //     && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+
+                                //    _transaction.MeterStop = _request.meterStop;
+                                //    _transaction.StopTime = _request.timestamp.ToUniversalTime();
+                                //    _transaction.StopReasonId = _request.reason.HasValue ? (int)_request.reason.Value : 0;
+                                //    _transaction.StopReason = _request.reason.HasValue ? _request.reason.Value.ToString() : Reason.Local.ToString();
+                                //    _transaction.StopIdTag = _request.idTag;
+                                //    _transaction.Receipt = string.Empty;
+                                //    _transaction.Cost = session.IsBilling ? -1 : 0;
+
+                                //    //await db.SaveChangesAsync();
+                                //    await db.SaveChangesAsync();
+                                //}
+
+
+                                if (_request.transactionData != null && _request.transactionData.Count > 0)
                                 {
-
-                                    result.Exception = new Exception("Can't find transactionId " + _request.transactionId);
-
-                                }
-                                else
-                                {
-                                    var confirm = new StopTransactionConfirmation()
-                                    {
-                                        idTagInfo = _idTagInfo
-
-                                    };
-
-                                    //Avoid rewrite transaction data
-                                    if (transaction.StopTime != GlobalConfig.DefaultNullTime)
-                                    {
-                                        result.Message = confirm;
-                                        result.Success = true;
-                                        return result;
-                                    }
-
-                                    _ConnectorId = transaction.ConnectorId;
-                                    transaction.MeterStop = _request.meterStop;
-                                    transaction.StopTime = _request.timestamp.ToUniversalTime();
-                                    transaction.StopReasonId = _request.reason.HasValue ? (int)_request.reason.Value : 0;
-                                    transaction.StopReason = _request.reason.HasValue ? _request.reason.Value.ToString() : Reason.Local.ToString();
-                                    transaction.StopIdTag = _request.idTag;
-                                    transaction.Receipt = string.Empty;
-                                    transaction.Cost = session.IsBilling ? -1 : 0;
-
-                                    if (_request.transactionData != null && _request.transactionData.Count > 0)
+                                    _request.transactionData[0].sampledValue.Add(new SampledValue()
                                     {
-                                        _request.transactionData[0].sampledValue.Add(new SampledValue()
-                                        {
-                                            context = ReadingContext.Transaction_End,
-                                            format = ValueFormat.Raw,
-                                            location = Location.Outlet,
-                                            phase = _request.transactionData[0].sampledValue.Where(x => x.context.HasValue).Select(x => x.phase).FirstOrDefault(),
-                                            unit = UnitOfMeasure.Wh,
-                                            measurand = Measurand.TotalEnergy,
-                                            value = decimal.Subtract(transaction.MeterStop, transaction.MeterStart).ToString()
-                                        });
-                                    }
-
-
-                                    //await db.SaveChangesAsync();
-                                    await db.SaveChangesAsync();
+                                        context = ReadingContext.Transaction_End,
+                                        format = ValueFormat.Raw,
+                                        location = Location.Outlet,
+                                        phase = _request.transactionData[0].sampledValue.Where(x => x.context.HasValue).Select(x => x.phase).FirstOrDefault(),
+                                        unit = UnitOfMeasure.Wh,
+                                        measurand = Measurand.TotalEnergy,
+                                        value = decimal.Subtract(transaction.MeterStop, transaction.MeterStart).ToString()
+                                    });
+                                }
 
+                                if (session.IsBilling)
+                                {
+                                    await mainDbService.AddServerMessage(
+                                        ChargeBoxId: session.ChargeBoxId,
+                                        OutAction: Actions.DataTransfer.ToString(),
+                                        OutRequest:
+                                            new DataTransferRequest()
+                                            {
+                                                messageId = "ID_TxEnergy",
+                                                vendorId = "Phihong Technology",
+                                                data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = transaction.ConnectorId })
+                                            }
+                                        );
+                                }
 
-                                    if (session.IsBilling)
-                                    {
-                                        await mainDbService.AddServerMessage(
-                                            ChargeBoxId: session.ChargeBoxId,
-                                            OutAction: Actions.DataTransfer.ToString(),
-                                            OutRequest:
-                                                new DataTransferRequest()
-                                                {
-                                                    messageId = "ID_TxEnergy",
-                                                    vendorId = "Phihong Technology",
-                                                    data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = transaction.ConnectorId })
-                                                }
-                                            );
-                                    }
+                                result.Message = confirm;
+                                result.Success = true;
 
-                                    result.Message = confirm;
-                                    result.Success = true;
-                                }
                             }
                             dbOpTime = watch.ElapsedMilliseconds;
 
@@ -787,8 +801,8 @@ internal partial class ProfileHandler
 
                         if (stopTrasactionTimer.ElapsedMilliseconds > 1000)
                         {
-                            logger.Log(LogLevel.Critical, "ExecuteCoreRequest {action} {sessisonId} took {time} sec", action.ToString(), session.SessionID, stopTrasactionTimer.ElapsedMilliseconds / 1000);
-                            logger.Log(LogLevel.Critical, "{action} {sessisonId} time {getDateTime}/{serviceTime}/{tagInfoTime}/{dbOpTime}/{meterValueTime}", action.ToString(), session.SessionID, getDateTimeTime, getServiceTime, getTagInfoTime, dbOpTime, meterValueTime);
+                            logger.Log(LogLevel.Critical, "ExecuteCoreRequest {action} {ChargeBoxId} took {time} sec", action.ToString(), session.ChargeBoxId, stopTrasactionTimer.ElapsedMilliseconds / 1000);
+                            logger.Log(LogLevel.Critical, "{action} {ChargeBoxId} time {getDateTime}/{serviceTime}/{tagInfoTime}/{dbOpTime}/{meterValueTime}", action.ToString(), session.ChargeBoxId, getDateTimeTime, getServiceTime, getTagInfoTime, dbOpTime, meterValueTime);
                         }
 
                     }
@@ -869,7 +883,7 @@ internal partial class ProfileHandler
         watch.Stop();
         if (watch.ElapsedMilliseconds / 1000 > 3)
         {
-        logger.LogError("Processing " + action.ToString() + " costs " + watch.ElapsedMilliseconds / 1000 + " seconds"); ;
+            logger.LogError("Processing " + action.ToString() + " costs " + watch.ElapsedMilliseconds / 1000 + " seconds"); ;
         }
         //}
 
@@ -1158,7 +1172,7 @@ internal partial class ProfileHandler
                                         await mainDbService.AddServerMessage(
                                             ChargeBoxId: session.ChargeBoxId,
                                             OutAction: Actions.DataTransfer.ToString(),
-                                            OutRequest: 
+                                            OutRequest:
                                                 new DataTransferRequest()
                                                 {
                                                     messageId = "FinalCost",
@@ -1226,7 +1240,7 @@ internal partial class ProfileHandler
                                         await mainDbService.AddServerMessage(
                                             ChargeBoxId: session.ChargeBoxId,
                                             OutAction: Actions.DataTransfer.ToString(),
-                                            OutRequest: 
+                                            OutRequest:
                                              new DataTransferRequest()
                                              {
                                                  messageId = "RunningCost",
@@ -1423,7 +1437,7 @@ internal partial class ProfileHandler
 
                             if (_confirm.status == Packet.Messages.SubTypes.ConfigurationStatus.Accepted || _confirm.status == Packet.Messages.SubTypes.ConfigurationStatus.RebootRequired)
                             {
-                                var configure = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToList();
+                                var configure = await db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToListAsync();
 
                                 var foundConfig = configure.Find(x => x.ConfigureName == _request.key);
                                 if (foundConfig != null)
@@ -1433,7 +1447,7 @@ internal partial class ProfileHandler
                                 }
                                 else
                                 {
-                                    db.MachineConfigurations.Add(new MachineConfiguration()
+                                    await db.MachineConfigurations.AddAsync(new MachineConfiguration()
                                     {
                                         ChargeBoxId = session.ChargeBoxId,
                                         ConfigureName = _request.key,
@@ -1488,7 +1502,7 @@ internal partial class ProfileHandler
                                         }
                                         else
                                         {
-                                            db.MachineConfigurations.Add(new MachineConfiguration()
+                                            await db.MachineConfigurations.AddAsync(new MachineConfiguration()
                                             {
                                                 ChargeBoxId = session.ChargeBoxId,
                                                 ConfigureName = item.key,
@@ -1515,7 +1529,7 @@ internal partial class ProfileHandler
                                         }
                                         else
                                         {
-                                            db.MachineConfigurations.Add(new MachineConfiguration()
+                                            await db.MachineConfigurations.AddAsync(new MachineConfiguration()
                                             {
                                                 ChargeBoxId = session.ChargeBoxId,
                                                 ConfigureName = item

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

@@ -51,7 +51,7 @@ namespace EVCB_OCPP.WSServer.Message
                                             retryInterval = 10
                                         };
 
-                                        db.MachineOperateRecord.Add(new MachineOperateRecord()
+                                        await db.MachineOperateRecord.AddAsync(new MachineOperateRecord()
                                         {
                                             CreatedOn = DateTime.UtcNow,
                                             ChargeBoxId = session.ChargeBoxId,

+ 2 - 0
EVCB_OCPP.WSServer/Program.cs

@@ -60,6 +60,8 @@ namespace EVCB_OCPP.WSServer
                 .ConfigureServices((hostContext, services) =>
                 {
                     //services.AddSingleton<MeterValueGroupSingleHandler>();
+                    services.AddSingleton<IHostLifetime, DummyHostLifeTime>();
+
                     services.AddProtalServer(hostContext.Configuration);
 
                     services.AddTransient<BlockingTreePrintService>();

+ 12 - 1
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -368,7 +368,9 @@ namespace EVCB_OCPP.WSServer
 
         private void RunHttpConsoleService()
         {
-            var app = WebApplication.Create();
+            var appBuilder = WebApplication.CreateBuilder();
+            appBuilder.Services.AddSingleton<IHostLifetime, DummyHostLifeTime>();
+            var app = appBuilder.Build();
 
             var helpFunc = () => {
                 return string.Join("\r\n", new[] { 
@@ -476,8 +478,17 @@ namespace EVCB_OCPP.WSServer
             _ = app.RunAsync();
 
             var builder = WebApplication.CreateBuilder();
+            //builder.Configuration.AddJsonFile("./EVCB_OCPP.WSServer/appsettings.json");
+            //var sec = builder.Configuration.GetSection("ReverseProxy");
+            //Console.WriteLine($"Printing.............");
+            //foreach (var pair in sec.AsEnumerable())
+            //{
+            //    Console.WriteLine($"{pair.Key}:{pair.Value} \n");
+            //}
+
             builder.Services.AddReverseProxy()
                 .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
+            builder.Services.AddSingleton<IHostLifetime, DummyHostLifeTime>();
             var yarpApp = builder.Build();
             yarpApp.Urls.Add("http://*:80");
             yarpApp.MapReverseProxy();

+ 5 - 3
EVCB_OCPP.WSServer/Service/ConnectionLogdbService.cs

@@ -65,7 +65,9 @@ public class ConnectionLogdbService : IConnectionLogdbService
         }
         catch (Exception ex)
         {
-            Console.WriteLine(ex.ToString());
+            logger.LogError(ex.Message);
+            logger.LogError(ex.StackTrace);
+            //Console.WriteLine(ex.ToString());
         }
     }
 
@@ -75,7 +77,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
         //queueHandler.Enqueue(log);
         //WriteMachineLog(log);
         insertConnectonLogHandler.HandleAsync(log);
-        //InsertWithDapper(log);
+        //_ = InsertWithDapper(log);
     }
 
     private async Task InsertWithDapper(MachineLog log)
@@ -334,7 +336,7 @@ public class ConnectionLogdbService : IConnectionLogdbService
         FormattableString checkTableSql = $"SELECT Count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = {tableName}";
 
         using var db = await connectionLogdbContextFactory.CreateDbContextAsync();
-        var resultList = db.Database.SqlQuery<int>(checkTableSql)?.ToList();
+        var resultList = await db.Database.SqlQuery<int>(checkTableSql)?.ToListAsync();
 
         if (resultList is not null && resultList.Count > 0 && resultList[0] > 0)
         {

+ 183 - 19
EVCB_OCPP.WSServer/Service/MainDbService.cs

@@ -2,11 +2,14 @@
 using EVCB_OCPP.Domain;
 using EVCB_OCPP.Domain.Models.Database;
 using EVCB_OCPP.WSServer.Helper;
+using log4net.Core;
+using Microsoft.AspNetCore.Http.HttpResults;
 using Microsoft.Data.SqlClient;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
+using MongoDB.Driver.Core.Connections;
 using Newtonsoft.Json;
 using OCPPPackage.Profiles;
 using System.Data;
@@ -31,6 +34,11 @@ public interface IMainDbService
     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);
+    Task<Guid> GetCustomerIdByChargeBoxId(string chargeboxId);
+    Task<int?> TryGetDuplicatedTransactionId(string chargeBoxId, Guid customerId, int connectorId, DateTime timestamp);
+    Task<int> AddNewTransactionRecord(TransactionRecord newTransaction);
+    Task<TransactionRecord> GetTransactionForStopTransaction(int transactionId, string chargeBoxId);
+    Task UpdateTransaction(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost);
 }
 
 public class MainDbService : IMainDbService
@@ -111,9 +119,9 @@ public class MainDbService : IMainDbService
     public async Task UpdateMachineBasicInfo(string ChargeBoxId, Machine machine)
     {
         //using var semaphoreWrapper = await startupSemaphore.GetToken();
-        //using var db = await await contextFactory.CreateDbContextAsyncAsyncAsync();
+        //using var db = await contextFactory.CreateDbContextAsync();
 
-        //var _machine = db.Machine.FirstOrDefault(x => x.ChargeBoxId == ChargeBoxId);
+        //var _machine = await db.Machine.FirstOrDefaultAsync(x => x.ChargeBoxId == ChargeBoxId);
         //_machine.ChargeBoxSerialNumber = machine.ChargeBoxSerialNumber;
         //_machine.ChargePointSerialNumber = machine.ChargePointSerialNumber;
         //_machine.ChargePointModel = machine.ChargePointModel;
@@ -125,14 +133,16 @@ public class MainDbService : IMainDbService
         //_machine.MeterType = machine.MeterType;
 
         //await db.SaveChangesAsync();
+
         //using var semaphoreWrapper = await startupSemaphore.GetToken();
+
         await updateMachineBasicInfoHandler.HandleAsync(new UpdateMachineBasicInfoParam(ChargeBoxId, machine));
     }
 
     public async Task AddOCMF(OCMF oCMF)
     {
         using var db = await contextFactory.CreateDbContextAsync();
-        db.OCMF.Add(oCMF);
+        await db.OCMF.AddAsync(oCMF);
         await db.SaveChangesAsync();
     }
 
@@ -153,7 +163,7 @@ public class MainDbService : IMainDbService
             VendorErrorCode = VendorErrorCode,
             Id = Guid.NewGuid().ToString()
         };
-        db.ConnectorStatus.Add(_currentStatus);
+        await db.ConnectorStatus.AddAsync(_currentStatus);
 
         await db.SaveChangesAsync();
 
@@ -186,6 +196,49 @@ public class MainDbService : IMainDbService
         return;
     }
 
+    public async Task<Guid> GetCustomerIdByChargeBoxId(string chargeboxId)
+    {
+        //using var db = await contextFactory.CreateDbContextAsync();
+        //var _CustomerId = await db.Machine.Where(x => x.ChargeBoxId == chargeboxId).Select(x => x.CustomerId).FirstOrDefaultAsync();
+        //return _CustomerId;
+        var parameters = new DynamicParameters();
+        parameters.Add("@ChargeBoxId", chargeboxId, DbType.String, ParameterDirection.Input, 50);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var _existedTx = await conn.QueryFirstOrDefaultAsync<Guid>("""
+            select CustomerId
+            from dbo.Machine
+            where
+            ChargeBoxId = @ChargeBoxId
+            """, parameters);
+
+        return _existedTx;
+    }
+
+    public async Task<int?> TryGetDuplicatedTransactionId(string chargeBoxId, Guid customerId, int connectorId, DateTime timestamp)
+    {
+        //var _existedTx = await db.TransactionRecord.Where(x => x.CustomerId == customerId && x.ChargeBoxId == chargeBoxId
+        //                       && x.ConnectorId == connectorId && x.StartTime == timestamp).Select(x=>x.Id).fi();
+        var parameters = new DynamicParameters();
+        parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@CustomerId", customerId, DbType.Guid, ParameterDirection.Input);
+        parameters.Add("@ConnectorId", connectorId, DbType.Int16, ParameterDirection.Input);
+        parameters.Add("@TimeStamp", timestamp, DbType.DateTime, ParameterDirection.Input);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var _existedTx = await conn.QueryFirstOrDefaultAsync<int?>("""
+            SELECT Id
+            FROM dbo.TransactionRecord
+            WHERE
+            ChargeBoxId = @ChargeBoxId and
+            CustomerId = @CustomerId and
+            ConnectorId = @ConnectorId and
+            StartTime = @TimeStamp
+            """, parameters);
+
+        return _existedTx;
+    }
+
     private async Task UpdateConnectorStatusEF(string Id, ConnectorStatus Status)
     {
         using var db = await contextFactory.CreateDbContextAsync();
@@ -242,22 +295,39 @@ public class MainDbService : IMainDbService
     public async ValueTask AddMachineError(byte ConnectorId, DateTime CreatedOn, int Status, string ChargeBoxId,
     int ErrorCodeId, string ErrorInfo, int PreStatus, string VendorErrorCode, string VendorId)
     {
-        using var db = await contextFactory.CreateDbContextAsync();
-        db.MachineError.Add(new MachineError()
-        {
-            ConnectorId = ConnectorId,
-            CreatedOn = CreatedOn,
-            Status = Status,
-            ChargeBoxId = ChargeBoxId,
-            ErrorCodeId = ErrorCodeId,
-            ErrorInfo = ErrorInfo,
-            PreStatus = PreStatus,
-            VendorErrorCode = VendorErrorCode,
-            VendorId = VendorId
-        });
+        //using var db = await contextFactory.CreateDbContextAsync();
+        //await db.MachineError.AddAsync(new MachineError()
+        //{
+        //    ConnectorId = ConnectorId,
+        //    CreatedOn = CreatedOn,
+        //    Status = Status,
+        //    ChargeBoxId = ChargeBoxId,
+        //    ErrorCodeId = ErrorCodeId,
+        //    ErrorInfo = ErrorInfo,
+        //    PreStatus = PreStatus,
+        //    VendorErrorCode = VendorErrorCode,
+        //    VendorId = VendorId
+        //});
 
-        await db.SaveChangesAsync();
+        //await db.SaveChangesAsync();
+
+        var parameters = new DynamicParameters();
+        parameters.Add("@ConnectorId", ConnectorId, DbType.Int16, ParameterDirection.Input);
+        parameters.Add("@PreStatus", PreStatus, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@Status", Status, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@ErrorInfo", ErrorInfo, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@VendorId", VendorId, DbType.String, ParameterDirection.Input, 255);
+        parameters.Add("@CreatedOn", CreatedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@ErrorCodeId", ErrorCodeId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@VendorErrorCode", VendorErrorCode, DbType.String, ParameterDirection.Input, 100);
+        parameters.Add("@ChargeBoxId", ChargeBoxId, DbType.String, ParameterDirection.Input, 50);
 
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        await conn.ExecuteAsync("""
+            INSERT INTO MachineError
+            (ConnectorId, PreStatus, Status, ErrorInfo, VendorId, CreatedOn, ErrorCodeId, VendorErrorCode, ChargeBoxId)
+            VALUES (@ConnectorId, @PreStatus, @Status, @ErrorInfo, @VendorId, @CreatedOn, @ErrorCodeId, @VendorErrorCode, @ChargeBoxId)
+            """, parameters);
     }
 
     public Task AddServerMessage(string ChargeBoxId, string OutAction, object OutRequest, string CreatedBy, DateTime? CreatedOn = null, string SerialNo = "", string InMessage = "")
@@ -299,6 +369,7 @@ public class MainDbService : IMainDbService
 
     public Task AddServerMessage(ServerMessage message)
     {
+        //return AddServerMessageEF(message);
         return addServerMessageHandler.HandleAsync(message);
     }
 
@@ -326,6 +397,87 @@ public class MainDbService : IMainDbService
         return toReturn;
     }
 
+    public async Task<int> AddNewTransactionRecord(TransactionRecord newTransaction)
+    {
+        //using (var db = await contextFactory.CreateDbContextAsync())
+        //{
+        //    await db.TransactionRecord.AddAsync(newTransaction);
+
+        //    await db.SaveChangesAsync();
+
+        //   return newTransaction.Id;
+        //}
+
+        var parameters = new DynamicParameters();
+        parameters.Add("@ChargeBoxId", newTransaction.ChargeBoxId, DbType.String, ParameterDirection.Input, 50);
+        parameters.Add("@ConnectorId", newTransaction.ConnectorId, DbType.Int16, ParameterDirection.Input);
+        parameters.Add("@CreatedOn", newTransaction.CreatedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@UpdatedOn", newTransaction.UpdatedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StartTransactionReportedOn", newTransaction.StartTransactionReportedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StopTransactionReportedOn", newTransaction.StopTransactionReportedOn, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StartIdTag", newTransaction.StartIdTag, DbType.String, ParameterDirection.Input, 20);
+        parameters.Add("@MeterStart", newTransaction.MeterStart, DbType.Decimal, ParameterDirection.Input, precision:18, scale:2);
+        parameters.Add("@MeterStop", newTransaction.MeterStop, DbType.Decimal, ParameterDirection.Input, precision: 18, scale: 2);
+        parameters.Add("@CustomerId", newTransaction.CustomerId, DbType.Guid, ParameterDirection.Input);
+        parameters.Add("@StartTime", newTransaction.StartTime, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StopTime", newTransaction.StopTime, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@ReservationId", newTransaction.ReservationId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@RetryStartTransactionTimes", newTransaction.RetryStartTransactionTimes, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@RetryStopTransactionTimes", newTransaction.RetryStopTransactionTimes, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@Fee", newTransaction.Fee, DbType.String, ParameterDirection.Input, 1500);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var id = await conn.QuerySingleAsync<int>("""
+            INSERT INTO TransactionRecord
+            (ChargeBoxId, ConnectorId, CreatedOn, UpdatedOn, StartTransactionReportedOn, StopTransactionReportedOn,
+            StartIdTag, MeterStart, MeterStop, CustomerId, StartTime, StopTime, ReservationId, RetryStartTransactionTimes, RetryStopTransactionTimes, Fee)
+            OUTPUT INSERTED.Id
+            VALUES (@ChargeBoxId, @ConnectorId, @CreatedOn, @UpdatedOn, @StartTransactionReportedOn, @StopTransactionReportedOn,
+            @StartIdTag, @MeterStart, @MeterStop, @CustomerId, @StartTime, @StopTime, @ReservationId, @RetryStartTransactionTimes, @RetryStopTransactionTimes, @Fee)
+            """, parameters);
+        return id;
+    }
+
+    public async Task<TransactionRecord> GetTransactionForStopTransaction(int transactionId, string chargeBoxId)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@TransactionId", transactionId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input, 50);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var record = await conn.QuerySingleAsync<TransactionRecord>("""
+            SELECT ConnectorId, MeterStop, MeterStart, StopTime FROM TransactionRecord
+            WHERE Id = @TransactionId and ChargeBoxId = @ChargeBoxId 
+            """, parameters);
+        return record;
+    }
+
+    public async Task UpdateTransaction(int transactionId, int meterStop, DateTime stopTime, int stopReasonId, string stopReason, string stopIdTag, string receipt, int cost)
+    {
+        var parameters = new DynamicParameters();
+        parameters.Add("@TransactionId", transactionId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@MeterStop", meterStop, DbType.Decimal, ParameterDirection.Input, precision: 18, scale: 2);
+        parameters.Add("@StopTime", stopTime, DbType.DateTime, ParameterDirection.Input);
+        parameters.Add("@StopReasonId", stopReasonId, DbType.Int32, ParameterDirection.Input);
+        parameters.Add("@StopReason", stopReason, DbType.String, ParameterDirection.Input, 60);
+        parameters.Add("@StopIdTag", stopIdTag, DbType.String, ParameterDirection.Input, 20);
+        parameters.Add("@Receipt", receipt, DbType.String, ParameterDirection.Input, 3000);
+        parameters.Add("@Cost", cost, DbType.Decimal, ParameterDirection.Input, precision: 18, scale: 2);
+
+        using var conn = await sqlConnectionFactory.CreateAsync();
+        var resultCnt = await conn.ExecuteAsync("""
+            UPDATE TransactionRecord
+            SET MeterStop = @MeterStop, StopTime = @StopTime, StopReasonId = @StopReasonId,
+            StopReason = @StopReason, StopIdTag = @StopIdTag, Receipt = @Receipt, Cost = @Cost
+            WHERE Id = @TransactionId
+            """, parameters);
+        if (resultCnt != 1)
+        {
+            throw new Exception("Update over one columes");
+        }
+        return;
+    }
+
     private void InitUpdateMachineBasicInfoHandler()
     {
         if (updateMachineBasicInfoHandler is not null)
@@ -467,7 +619,7 @@ public class MainDbService : IMainDbService
 
         foreach (var message in messages)
         {
-            db.ServerMessage.Add(message);
+            await db.ServerMessage.AddAsync(message);
         }
 
         await db.SaveChangesAsync();
@@ -475,6 +627,18 @@ public class MainDbService : IMainDbService
         db.ChangeTracker.Clear();
     }
 
+    private async Task AddServerMessageEF(ServerMessage message)
+    {
+        using var db = await contextFactory.CreateDbContextAsync();
+        using var trans = await db.Database.BeginTransactionAsync();
+
+        await db.ServerMessage.AddAsync(message);
+
+        await db.SaveChangesAsync();
+        await trans.CommitAsync();
+        db.ChangeTracker.Clear();
+    }
+
     private Task BulkInsertServerMessage(IEnumerable<ServerMessage> messages)
     {
         var table = new DataTable();

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

@@ -149,7 +149,7 @@ public class MeterValueDbService
         t1 = watch.ElapsedMilliseconds;
         using var sqlConnection = await sqlConnectionFactory.CreateAsync();
         t2 = watch.ElapsedMilliseconds;
-        await sqlConnection.ExecuteAsync(command, parameters);
+        await sqlConnection.ExecuteAsync(command, parameters).ConfigureAwait(false);
 
         watch.Stop();
         t3 = watch.ElapsedMilliseconds;

+ 9 - 3
EVCB_OCPP.WSServer/Service/OuterBusinessService.cs

@@ -94,7 +94,13 @@ namespace EVCB_OCPP.WSServer.Service
 
         async public Task<IdTokenInfo> Authorize(string chargeBoxId, string idTag)
         {
-            await Task.Delay(10);
+            return new IdTokenInfo() { IdTagInfo = new IdTagInfo()
+            {
+                expiryDate = DateTime.UtcNow.AddDays(1),
+                status = AuthorizationStatus.Accepted 
+            } };
+
+            //await Task.Delay(10);
             IdTokenInfo result = new IdTokenInfo() { IdTagInfo = new IdTagInfo() { status = AuthorizationStatus.Invalid } };
 
             try
@@ -113,11 +119,11 @@ namespace EVCB_OCPP.WSServer.Service
                             }, null, signMaterial.SaltKey).ConfigureAwait(false);
                // if (CustomerId.ToLower() == "9e6bfdcc-09fb-4dab-a428-43fe507600a3")
                 {
-                    logger.LogInformation(JsonConvert.SerializeObject(response));
+                    logger.LogInformation($"{chargeBoxId} response : {JsonConvert.SerializeObject(response)}" );
                 }
                 if (response.Success)
                 {
-                    Console.WriteLine(response.Response);
+                    //Console.WriteLine(response.Response);
                     var _httpResult = JsonConvert.DeserializeObject<CPOOuterResponse>(response.Response);
                     JObject jo = JObject.Parse(_httpResult.Data);
 

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

@@ -194,7 +194,7 @@ namespace EVCB_OCPP.WSServer.Service
             }
             unencodeText = unencodeText.ToLower();
 
-            MD5 md5 = new MD5CryptoServiceProvider();
+            MD5 md5 = MD5.Create();// new MD5CryptoServiceProvider();
             byte[] textToHash = Encoding.UTF8.GetBytes(unencodeText);
             byte[] result = md5.ComputeHash(textToHash);
             return BitConverter.ToString(result).Replace("-", "").ToLower();

+ 1 - 1
build.bat

@@ -1,5 +1,5 @@
 for /f %%i in ('git rev-parse --short HEAD') do set ssha=%%i
 docker build ./ -t 172.1.2.214:5000/server:test --label "git-commit=%ssha%"
-docker push 172.1.2.214:5000/server:test
+::docker push 172.1.2.214:5000/server:test
 docker tag 172.1.2.214:5000/server:test evdevcontainerregistry.azurecr.io/server:test
 docker push evdevcontainerregistry.azurecr.io/server:test

+ 6 - 0
entrypoint.sh

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