Răsfoiți Sursa

2020/004/21 Jessica
Actions:
1.Add Three Job to call third party APIs

Jessica.Tseng 4 ani în urmă
părinte
comite
04a1c535e8

+ 74 - 3
EVCB_OCPP.TaskScheduler/App.config

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="utf-8"?>
 <configuration>
    <configSections>
     <sectionGroup name="common">
@@ -10,8 +10,8 @@
     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
   </startup>
   <connectionStrings>
-    <add name="OnlineLogDBContext" connectionString="data source=.\SQLEXPRESS2017;initial catalog=StandardOCPP_OnlineRecord;;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
-    <add name="MainDBContext" connectionString="data source=.\SQLEXPRESS2017;initial catalog=StandardOCPP_Main;;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
+    <add name="OnlineLogDBContext" connectionString="data source=172.1.2.187\SQLEXPRESS2017;initial catalog=StandardOCPP_OnlineRecord;;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
+    <add name="MainDBContext" connectionString="data source=172.1.2.187\SQLEXPRESS2017;initial catalog=StandardOCPP_Main;;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
   </connectionStrings>
 
 <!--<common>
@@ -24,4 +24,75 @@
     </logging>
   </common>-->
   
+  <runtime>
+  
+       <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+  
+            <dependentAssembly>
+  
+                 <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+  
+                 <bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
+  
+            </dependentAssembly>
+  
+            <dependentAssembly>
+  
+                 <assemblyIdentity name="Microsoft.Extensions.Primitives" publicKeyToken="adb9793829ddae60" culture="neutral" />
+  
+                 <bindingRedirect oldVersion="0.0.0.0-3.1.2.0" newVersion="3.1.2.0" />
+  
+            </dependentAssembly>
+  
+            <dependentAssembly>
+  
+                 <assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
+  
+                 <bindingRedirect oldVersion="0.0.0.0-3.1.2.0" newVersion="3.1.2.0" />
+  
+            </dependentAssembly>
+  
+            <dependentAssembly>
+  
+                 <assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
+  
+                 <bindingRedirect oldVersion="0.0.0.0-3.1.2.0" newVersion="3.1.2.0" />
+  
+            </dependentAssembly>
+  
+            <dependentAssembly>
+  
+                 <assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral" />
+  
+                 <bindingRedirect oldVersion="0.0.0.0-3.1.2.0" newVersion="3.1.2.0" />
+  
+            </dependentAssembly>
+  
+            <dependentAssembly>
+  
+                 <assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
+  
+                 <bindingRedirect oldVersion="0.0.0.0-3.1.2.0" newVersion="3.1.2.0" />
+  
+            </dependentAssembly>
+  
+            <dependentAssembly>
+  
+                 <assemblyIdentity name="Microsoft.Extensions.DependencyInjection" publicKeyToken="adb9793829ddae60" culture="neutral" />
+  
+                 <bindingRedirect oldVersion="0.0.0.0-3.1.2.0" newVersion="3.1.2.0" />
+  
+            </dependentAssembly>
+  
+            <dependentAssembly>
+  
+                 <assemblyIdentity name="Microsoft.Extensions.Logging" publicKeyToken="adb9793829ddae60" culture="neutral" />
+  
+                 <bindingRedirect oldVersion="0.0.0.0-3.1.2.0" newVersion="3.1.2.0" />
+  
+            </dependentAssembly>
+  
+       </assemblyBinding>
+  
+  </runtime>
 </configuration>

+ 7 - 1
EVCB_OCPP.TaskScheduler/DefaultSetting.cs

@@ -1,4 +1,5 @@
-using System;
+using Newtonsoft.Json;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -8,9 +9,14 @@ namespace EVCB_OCPP.TaskScheduler
 {
     public static class DefaultSetting
     {
+
+        public static readonly int DB_DefaultConnectionTimeout = 60;
+        public static readonly string UTC_DATETIMEFORMAT = "yyyy/MM/dd'T'HH':'mm':'ss'Z'";
         /// <summary>
         /// 預設 Null的 DateTime 
         /// </summary>
         public static DateTime DefaultNullTime = new DateTime(1991, 1, 1);
+
+        public static JsonSerializerSettings JSONSERIALIZER_FORMAT = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None };
     }
 }

+ 72 - 0
EVCB_OCPP.TaskScheduler/EVCB_OCPP.TaskScheduler.csproj

@@ -36,6 +36,42 @@
     <Reference Include="Dapper, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>..\packages\Dapper.2.0.30\lib\net461\Dapper.dll</HintPath>
     </Reference>
+    <Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Configuration, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Configuration.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Configuration.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Configuration.Abstractions.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Configuration.Binder, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Configuration.Binder.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.DependencyInjection, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.DependencyInjection.3.1.2\lib\net461\Microsoft.Extensions.DependencyInjection.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.2\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Http, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Http.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Http.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Logging, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Logging.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Logging.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Options, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Options.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Options.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Primitives, Version=3.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Primitives.3.1.2\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
     <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
       <HintPath>..\packages\NLog.4.6.6\lib\net45\NLog.dll</HintPath>
     </Reference>
@@ -43,12 +79,32 @@
       <HintPath>..\packages\Quartz.3.0.7\lib\net452\Quartz.dll</HintPath>
     </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
+    </Reference>
+    <Reference Include="System.ComponentModel.Annotations, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.ComponentModel.Annotations.4.7.0\lib\net461\System.ComponentModel.Annotations.dll</HintPath>
+    </Reference>
+    <Reference Include="System.ComponentModel.DataAnnotations" />
     <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
     <Reference Include="System.IO.Compression" />
+    <Reference Include="System.Memory, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Memory.4.5.2\lib\netstandard2.0\System.Memory.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Numerics" />
+    <Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.7.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
+    </Reference>
     <Reference Include="System.Runtime.Remoting" />
     <Reference Include="System.Runtime.Serialization" />
     <Reference Include="System.ServiceModel" />
+    <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
+    </Reference>
     <Reference Include="System.Transactions" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
@@ -59,11 +115,27 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="DefaultSetting.cs" />
+    <Compile Include="Jobs\CheckExecutionCmdJob.cs" />
+    <Compile Include="Jobs\ExecutionCmdReportJob.cs" />
+    <Compile Include="Jobs\StartTransacionReportJob.cs" />
+    <Compile Include="Jobs\StopTransacionReportJob.cs" />
+    <Compile Include="Models\ComandExecution.cs" />
     <Compile Include="Models\EVSECurrentStatus.cs" />
     <Compile Include="Models\EVSEOnlineRecord.cs" />
+    <Compile Include="Models\Internal_ExecutionCode.cs" />
+    <Compile Include="Models\MachineOperateRecord.cs" />
+    <Compile Include="Models\Reason.cs" />
+    <Compile Include="Models\Transaction.cs" />
+    <Compile Include="Models\TransactionResponse.cs" />
+    <Compile Include="OuterHttpClient.cs" />
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Jobs\CheckEVSEOnlineJob.cs" />
+    <Compile Include="Services\CommonCustomerService.cs" />
+    <Compile Include="Services\CustomerBackendFactory.cs" />
+    <Compile Include="Services\DatabaseService.cs" />
+    <Compile Include="Services\HttpClientService.cs" />
+    <Compile Include="Services\ICustomerService.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="App.config">

+ 4 - 1
EVCB_OCPP.TaskScheduler/Jobs/CheckEVSEOnlineJob.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Data.SqlClient;
+using System.Diagnostics;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -37,7 +38,8 @@ namespace EVCB_OCPP.TaskScheduler.Jobs
         public async Task Execute(IJobExecutionContext context)
         {
             await Console.Out.WriteLineAsync(this.ToString() + " :Starting........");
-
+        
+        
             List<EVSECurrentStatus> _EVSEs = GetEVSEs();
 
             foreach (var evse in _EVSEs)
@@ -219,6 +221,7 @@ namespace EVCB_OCPP.TaskScheduler.Jobs
 
 
             await Console.Out.WriteLineAsync(this.ToString() + " :Finished........");
+           
         }
 
 

+ 53 - 0
EVCB_OCPP.TaskScheduler/Jobs/CheckExecutionCmdJob.cs

@@ -0,0 +1,53 @@
+using EVCB_OCPP.TaskScheduler.Services;
+using Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Jobs
+{
+    [DisallowConcurrentExecution]
+    public class CheckExecutionCmdJob : IJob
+    {
+        private NLog.ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+
+
+        public CheckExecutionCmdJob()
+        {
+
+        }
+
+
+        public async Task Execute(IJobExecutionContext context)
+        {
+            await Console.Out.WriteLineAsync(this.ToString() + " :Starting........");
+
+            List<Task> tList = new List<Task>();
+
+            ICustomerService cs = new CommonCustomerService();
+            var cList = cs.GetCallPartnerCustomers();
+
+            foreach (var customerId in cList)
+            {
+                ICustomerService s = CustomerBackendFactory.Create(customerId);
+
+
+                tList.Add(Task.Run(() => DoMainTask(customerId)));
+            }
+
+            Task.WaitAll(tList.ToArray());
+
+            await Console.Out.WriteLineAsync(this.ToString() + " :Finished........");
+        }
+
+        private void DoMainTask(Guid customerId)
+        {
+            ICustomerService _service = CustomerBackendFactory.Create(customerId);
+            _service.MonitorRemoteCommand().Wait();
+        }
+    }
+
+
+}

+ 51 - 0
EVCB_OCPP.TaskScheduler/Jobs/ExecutionCmdReportJob.cs

@@ -0,0 +1,51 @@
+using EVCB_OCPP.TaskScheduler.Services;
+using Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Jobs
+{
+    [DisallowConcurrentExecution]
+    public class ExecutionCmdReportJob : IJob
+    {
+        private NLog.ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+
+
+        public ExecutionCmdReportJob()
+        {
+
+        }
+
+
+        public async Task Execute(IJobExecutionContext context)
+        {
+            await Console.Out.WriteLineAsync(this.ToString() + " :Starting........");
+
+            List<Task> tList = new List<Task>();
+
+            ICustomerService cs = new CommonCustomerService();
+            var cList = cs.GetCallPartnerCustomers();
+
+            foreach (var customerId in cList)
+            {
+                ICustomerService s = CustomerBackendFactory.Create(customerId);
+
+
+                tList.Add(Task.Run(() => DoMainTask(customerId)));
+            }
+
+            Task.WaitAll(tList.ToArray());
+
+            await Console.Out.WriteLineAsync(this.ToString() + " :Finished........");
+        }
+
+        private void DoMainTask(Guid customerId)
+        {
+            ICustomerService _service = CustomerBackendFactory.Create(customerId);
+            _service.ReportExecutionofRemoteCommand().Wait();
+        }
+    }
+}

+ 51 - 0
EVCB_OCPP.TaskScheduler/Jobs/StartTransacionReportJob.cs

@@ -0,0 +1,51 @@
+using EVCB_OCPP.TaskScheduler.Services;
+using Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Jobs
+{
+    [DisallowConcurrentExecution]
+    public class StartTransacionReportJob : IJob
+    {
+        private NLog.ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+     
+
+        public StartTransacionReportJob()
+        {
+          
+        }
+      
+
+        public async Task Execute(IJobExecutionContext context)
+        {
+            await Console.Out.WriteLineAsync(this.ToString() + " :Starting........");
+
+            List<Task> tList = new List<Task>();
+
+            ICustomerService cs = new CommonCustomerService();
+            var cList = cs.GetCallPartnerCustomers();
+
+            foreach (var customerId in cList)
+            {
+                ICustomerService s = CustomerBackendFactory.Create(customerId);
+              
+
+               tList.Add(Task.Run(() => DoMainTask(customerId)));
+            }
+
+            Task.WaitAll(tList.ToArray());
+
+            await Console.Out.WriteLineAsync(this.ToString() + " :Finished........");
+        }
+
+        private void DoMainTask(Guid customerId)
+        {
+            ICustomerService _service = CustomerBackendFactory.Create(customerId);
+            _service.ReportStartTransaction().Wait();
+        }
+    }
+}

+ 51 - 0
EVCB_OCPP.TaskScheduler/Jobs/StopTransacionReportJob.cs

@@ -0,0 +1,51 @@
+using EVCB_OCPP.TaskScheduler.Services;
+using Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Jobs
+{
+    [DisallowConcurrentExecution]
+    public class StopTransacionReportJob : IJob
+    {
+        private NLog.ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+
+
+        public StopTransacionReportJob()
+        {
+
+        }
+
+
+        public async Task Execute(IJobExecutionContext context)
+        {
+            await Console.Out.WriteLineAsync(this.ToString() + " :Starting........");
+
+            List<Task> tList = new List<Task>();
+
+            ICustomerService cs = new CommonCustomerService();
+            var cList = cs.GetCallPartnerCustomers();
+
+            foreach (var customerId in cList)
+            {
+                ICustomerService s = CustomerBackendFactory.Create(customerId);
+
+
+                tList.Add(Task.Run(() => DoMainTask(customerId)));
+            }
+
+            Task.WaitAll(tList.ToArray());
+
+            await Console.Out.WriteLineAsync(this.ToString() + " :Finished........");
+        }
+
+        private void DoMainTask(Guid customerId)
+        {
+            ICustomerService _service = CustomerBackendFactory.Create(customerId);
+            _service.ReportStopTransaction().Wait();
+        }
+    }
+}

+ 44 - 0
EVCB_OCPP.TaskScheduler/Models/ComandExecution.cs

@@ -0,0 +1,44 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Models
+{
+    public class ComandExecution
+    {
+        [JsonIgnore]
+        public bool? IsTimeout { set; get; }
+
+        [JsonIgnore]
+        public bool? IsWaited { set; get; }
+
+        public bool IsRepliedbyEVSE { set; get; }
+
+        [JsonIgnore]
+        public int Code { set; get; }
+
+       
+        public string Detail { set; get; }
+
+       
+        public string Result { set; get; }
+    }
+
+    public enum CommandResultType
+    {
+        Waited,//server
+        Timeout,//server
+        Accepted,
+        Rejected,
+        Failed,
+        Occupied,
+        Notsupported,
+        Unknown,
+        EVSE_ERROR//EVSE Replied error call
+
+
+    }
+}

+ 48 - 0
EVCB_OCPP.TaskScheduler/Models/Internal_ExecutionCode.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Models
+{
+    public enum Internal_ExecutionCode
+    {
+
+        Accepted = 1,
+        Blocked,
+        Expired,
+        Invalid,
+        ConcurrentTx,
+        Scheduled,
+        Inoperative,
+        Operative,
+        NotSupported,
+        Unknown,
+        RebootRequired,
+        UnknownMessageId,
+        UnknownVendorId,
+        Idle,
+        Uploaded,
+        UploadFailed,
+        Uploading,
+        Downloaded,
+        DownloadFailed,
+        Downloading,
+        Installing,
+        InstallationFailed,
+        Installed,
+        Pending,
+        Faulted,
+        Occupied,
+        Unavailable,
+        Unlocked,
+        UnlockFailed,
+        VersionMismatch,
+        Rejected,
+        Failed,
+
+
+
+    }
+}

+ 262 - 0
EVCB_OCPP.TaskScheduler/Models/MachineOperateRecord.cs

@@ -0,0 +1,262 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Models
+{
+    public class MachineOperateRecord
+    {
+
+        public MachineOperateRecord()
+        {
+            EVSE_Value = string.Empty;
+            FinishedOn = new DateTime(1991, 1, 1);
+            ReportedOn = new DateTime(1991, 1, 1);
+
+        }
+
+
+        public int Id { get; set; }
+
+        /// <summary>
+        ///Action
+        /// </summary>      
+        public string Action { get; set; }
+
+
+        /// <summary>
+        /// 機器的客戶自訂ID
+        /// </summary>       
+        public string ChargeBoxId { get; set; }
+
+
+
+        /// <summary>
+        /// 隨機序號 和ServerCommand是同一個組序號
+        /// </summary>  
+        public string SerialNo { get; set; }
+
+        /// <summary>
+        /// 請求類型
+        /// </summary>     
+        public int RequestType { get; set; }
+
+
+        /// <summary>
+        /// 請求內容
+        /// </summary> 
+        public string RequestContent { get; set; }
+
+
+        /// <summary>
+        /// 狀態
+        /// </summary>     
+        public int Status { get; set; }
+
+        /// <summary>
+        /// EVSE回覆值
+        /// </summary>      
+        public string EVSE_Value { get; set; }
+
+        /// <summary>
+        /// EVSE狀態
+        /// </summary>     
+        public int EVSE_Status { get; set; }
+
+        /// <summary>
+        /// 建立時間
+        /// </summary>
+        public DateTime CreatedOn { set; get; }
+
+
+        /// <summary>
+        /// 結束時間
+        /// </summary>
+        public DateTime FinishedOn { set; get; }
+
+
+        /// <summary>
+        /// 回報時間
+        /// </summary>
+        public DateTime ReportedOn { set; get; }
+
+
+        public string ActionConverttoCommandType()
+        {
+            string cmdTypeName = "Unknown";
+
+            if (string.IsNullOrEmpty(Action))
+                return cmdTypeName;
+            switch (Action)
+            {
+                case "RemoteStartTransaction":
+                    {
+                        cmdTypeName = "Start_Session";
+                    }
+                    break;
+                case "RemoteStopTransaction":
+                    {
+                        cmdTypeName = "Stop_Session";
+                    }
+                    break;
+                case "ReserveNow":
+                    {
+                        cmdTypeName = "Reserve_Now";
+                    }
+                    break;
+                case "CancelReservation":
+                    {
+                        cmdTypeName = "CancelReservation";
+                    }
+                    break;
+                case "SetChargingProfile":
+                    {
+                        cmdTypeName = "SetChargingProfile";
+                    }
+                    break;
+                case "ClearChargingProfile":
+                    {
+                        cmdTypeName = "ClearChargingProfile";
+                    }
+                    break;
+                case "SendLocalList":
+                    {
+                        cmdTypeName = "SetLocalList";
+                    }
+                    break;
+                case "GetLocalListVersion":
+                    {
+                        cmdTypeName = "GetLocalListVersion";
+                    }
+                    break;
+                case "GetCompositeSchedule":
+                    {
+                        cmdTypeName = "GetAcitveChargingProfile";
+                    }
+                    break;
+                default:
+                    break;
+
+
+            }
+
+            return cmdTypeName;
+        }
+
+
+        public ComandExecution GetExecution()
+        {
+            ComandExecution execution = new ComandExecution();
+            if (EVSE_Status > 0)
+            {
+                execution.IsRepliedbyEVSE = true;
+
+                if (Action == "GetLocalListVersion")
+                {
+                    execution.Code = 1;
+                    execution.Detail = EVSE_Value;
+                }
+                else
+                {
+                    execution.Code = ConverttoCode(EVSE_Value);
+                    execution.Code = execution.Code == -1 ? (EVSE_Status == 1 ? 1 : (EVSE_Status == 255 ? 254 : execution.Code)) : execution.Code;
+                    execution.Detail = EVSE_Value;
+                }
+
+            }
+            else
+            {
+                execution.IsRepliedbyEVSE = false;
+                if (Status == 0)
+                {
+                    execution.Code = 0;
+                    execution.IsWaited = true;
+
+                }
+                if (Status == -1)
+                {
+                    execution.IsTimeout = true;
+                    execution.Code = 0;
+                    execution.Detail = "Timeout";
+                }
+
+
+            }
+
+            switch (execution.Code)
+            {
+                case 1:
+                    {
+                        execution.Result = CommandResultType.Accepted.ToString();
+                    }
+                    break;
+                case 9:
+                    {
+                        execution.Result = CommandResultType.Notsupported.ToString();
+                    }
+                    break;
+                case 10:
+                    {
+                        execution.Result = CommandResultType.Unknown.ToString();
+                    }
+                    break;
+                case 26:
+                    {
+                        execution.Result = CommandResultType.Occupied.ToString();
+                    }
+                    break;
+                case 31:
+                    {
+                        execution.Result = CommandResultType.Rejected.ToString();
+                    }
+                    break;
+                case 32:
+                    {
+                        execution.Result = CommandResultType.Failed.ToString();
+                    }
+                    break;
+                case 254:
+                    {
+                        execution.Result = CommandResultType.EVSE_ERROR.ToString();
+                    }
+                    break;
+                default:
+                    {
+                        if (execution.IsTimeout.HasValue && execution.IsTimeout.Value) execution.Result = CommandResultType.Timeout.ToString(); ;
+                        if (execution.IsWaited.HasValue && execution.IsWaited.Value) execution.Result = CommandResultType.Waited.ToString(); ;
+
+                    }
+                    break;
+
+
+            }
+            return execution;
+        }
+
+
+
+
+        private int ConverttoCode(string statusName)
+        {
+            int code = -1;
+
+            if (string.IsNullOrEmpty(statusName)) return code;
+
+
+            Internal_ExecutionCode result = Internal_ExecutionCode.Accepted;
+            if (Enum.TryParse<Internal_ExecutionCode>(statusName, out result))
+            {
+                code = (int)result;
+            }
+
+            return code;
+        }
+
+
+    }
+
+
+
+}

+ 24 - 0
EVCB_OCPP.TaskScheduler/Models/Reason.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Models
+{
+    public enum Reason
+    {
+
+        EmergencyStop = 1,
+        EVDisconnected,
+        HardReset,
+        Local,
+        Other,
+        PowerLoss,
+        Reboot,
+        Remote,
+        SoftReset,
+        UnlockCommand,
+        DeAuthorized,
+    }
+}

+ 111 - 0
EVCB_OCPP.TaskScheduler/Models/Transaction.cs

@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Models
+{
+    public class Transaction
+    {
+       
+        public int Id { get; set; }
+
+        /// <summary>
+        /// 客戶代碼
+        /// </summary>     
+        public Guid CustomerId { get; set; }
+
+
+
+        /// <summary>
+        /// 槍號  1 byte string、本來是 string、改為byte
+        /// </summary>      
+        public byte ConnectorId { get; set; }
+
+
+        /// <summary>
+        /// TransactionId
+        /// </summary>      
+        public int TransactionId { get; set; }
+
+
+
+        /// <summary>
+        /// chargePointSerialNumber
+        /// </summary>      
+        public string ChargeBoxId { get; set; }
+
+        /// <summary>
+        /// StartIdTag
+        /// </summary>       
+        public string StartIdTag { get; set; }
+
+
+        /// <summary>
+        /// StopIdTag
+        /// </summary>       
+        public string StopIdTag { get; set; }
+
+        /// <summary>
+        /// 開始充電開始時間
+        /// </summary>     
+        public DateTime StartTime { get; set; }
+
+        /// <summary>
+        /// 充電結束時間
+        /// </summary>
+        public DateTime StopTime { get; set; }
+
+        /// <summary>
+        /// 預約Id
+        /// </summary>
+        public int ReservationId { set; get; }
+
+        /// <summary>
+        /// 停止原因No
+        /// </summary>
+        public int StopReasonId { set; get; }
+
+
+
+        /// <summary>
+        /// 開始meter
+        /// </summary>      
+        public decimal MeterStart { get; set; }
+
+        /// <summary>
+        /// 結束meter
+        /// </summary>       
+        public decimal MeterStop { get; set; }
+
+       
+
+
+        /// <summary>
+        /// 開始充電的回報時間
+        /// </summary>
+        public DateTime StartTransactionReportedOn { get; set; }
+
+        /// <summary>
+        /// 開始充電回報的次數
+        /// </summary>
+        public int RetryStartTransactionTimes { get; set; }
+
+        /// <summary>
+        /// 結束充電的回報時間
+        /// </summary>
+        public DateTime StopTransactionReportedOn { get; set; }
+
+        /// <summary>
+        /// 結束充電回報的次數
+        /// </summary>
+        public int RetryStopTransactionTimes { get; set; }
+
+        /// <summary>
+        /// 回調客戶API,問題紀錄
+        /// </summary>
+        public string ErrorMsg { set; get; }
+
+    }
+}

+ 26 - 0
EVCB_OCPP.TaskScheduler/Models/TransactionResponse.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Models
+{
+    internal class TransactionResponse
+    {
+        internal  DateTime StartTransactionReportedOn { set; get; }
+
+        internal DateTime StopTransactionReportedOn { set; get; }
+
+        internal string ErrorMsg { set; get; }
+    }
+
+    internal class BasicResponse
+    {
+        internal DateTime ReportedOn { set; get; }
+
+       
+
+        internal string ErrorMsg { set; get; }
+    }
+}

+ 1 - 0
EVCB_OCPP.TaskScheduler/NLog.config

@@ -44,6 +44,7 @@
     <logger name="Quartz.*" minlevel="Warn" writeTo="f" />
     <logger name="EVCB_OCPP.TaskScheduler.*" minlevel="Debug" writeTo="f" />
     <logger name="EVCB_OCPP.TaskScheduler.*" minlevel="Debug" writeTo="Console" />
+      <logger name="EVCB_OCPP.TaskScheduler.*" minlevel="Debug" writeTo="Console" />
   
   </rules>
 </nlog>

+ 165 - 0
EVCB_OCPP.TaskScheduler/OuterHttpClient.cs

@@ -0,0 +1,165 @@
+using EVCB_OCPP.TaskScheduler.Services;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler
+{
+    public class OuterHttpClient
+    {
+        private HttpClientService httpClient = new HttpClientService();
+
+        async public Task<HttpResult> Post(string url, Dictionary<string, string> headers, object requestBody, string saltkey)
+        {
+            HttpResult result = new HttpResult() { Success = false };
+
+            try
+            {
+                string body = PreAction(url, ref headers, requestBody, saltkey);
+                var _response = await httpClient.PostJsonAsync(url, body, headers);
+
+                result.Response = _response.Response;
+                result.Status = _response.StatusCode;
+                result.Success = _response.IsSuccessStatusCode;
+                result.Exception = _response.Exception;
+
+
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+            }
+
+            return result;
+        }
+
+        async public Task<HttpResult> Get(string url, Dictionary<string, string> headers, string saltkey)
+        {
+
+            HttpResult result = new HttpResult() { Success = false };
+
+            try
+            {
+                string body = PreAction(url, ref headers, null, saltkey);
+                var _response = await httpClient.GetJsonAsync(url, headers);
+                result.Response = _response.Response;
+                result.Status = _response.StatusCode;
+                result.Success = _response.IsSuccessStatusCode;
+                result.Exception = _response.Exception;
+             
+
+
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+            }
+
+            return result;
+        }
+
+        async public Task<HttpResult> Delete(string url, Dictionary<string, string> headers, string saltkey)
+        {
+            HttpResult result = new HttpResult() { Success = false };
+
+            try
+            {
+                string body = PreAction(url, ref headers, null, saltkey);
+                var _response = await httpClient.DeleteJsonAsync(url, headers);
+                result.Response = _response.Response;
+                result.Status = _response.StatusCode;
+                result.Success = _response.IsSuccessStatusCode;
+                result.Exception = _response.Exception;
+
+
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+            }
+
+            return result;
+        }
+
+        async public Task<HttpResult> Put(string url, Dictionary<string, string> headers, object requestBody, string saltkey)
+        {
+            HttpResult result = new HttpResult() { Success = false };
+
+            try
+            {
+                string body = PreAction(url, ref headers, requestBody, saltkey);
+                var _response = await httpClient.PutJsonAsync(url, body, headers);
+                result.Response = _response.Response;
+                result.Status = _response.StatusCode;
+                result.Success = _response.IsSuccessStatusCode;
+                result.Exception = _response.Exception;
+
+
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+            }
+
+            return result;
+        }
+
+        private string PreAction(string url, ref Dictionary<string, string> headers, object requestBody, string saltkey)
+        {
+            var _body = requestBody == null ? "" : JsonConvert.SerializeObject(requestBody, DefaultSetting.JSONSERIALIZER_FORMAT);
+            headers.Add("Timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString());
+            string signature = GetSignature(GetUnencodeText(url, _body, headers["Timestamp"], headers["PartnerId"], saltkey));
+            headers.Add("Signature", signature);
+
+            return _body;
+        }
+
+
+        private string GetUnencodeText(string url, string body, string timestamp, string partnerId, string saltkey)
+        {
+
+            string tempText = url.Substring(url.IndexOf('?') + 1).ToLower();
+            tempText = tempText.StartsWith("http") ? string.Empty : tempText;
+            body = tempText + body;
+            string unencodeText = string.Format("{0}{1}{2}{3}", body, timestamp, partnerId, saltkey).ToLower();
+
+            return unencodeText;
+        }
+
+        private string GetSignature(string unencodeText)
+        {
+            if ((unencodeText == null) || (unencodeText.Length == 0))
+            {
+                return String.Empty;
+            }
+            unencodeText = unencodeText.ToLower();
+
+            MD5 md5 = new MD5CryptoServiceProvider();
+            byte[] textToHash = Encoding.UTF8.GetBytes(unencodeText);
+            byte[] result = md5.ComputeHash(textToHash);
+            return BitConverter.ToString(result).Replace("-", "").ToLower();
+        }
+
+    }
+
+    public class HttpResult 
+    {
+        public int StatusCode { set; get; }
+
+        public bool Success { set; get; }
+
+        public HttpStatusCode Status { set; get; }
+
+        public Exception Exception { set; get; }
+
+        public string Response { set; get; }
+
+    }
+
+}

+ 65 - 10
EVCB_OCPP.TaskScheduler/Program.cs

@@ -1,5 +1,6 @@
 
 using EVCB_OCPP.TaskScheduler.Jobs;
+using EVCB_OCPP.TaskScheduler.Services;
 using Quartz;
 using Quartz.Impl;
 using System;
@@ -18,10 +19,15 @@ namespace EVCB_OCPP.TaskScheduler
         {
             try
             {
-               //TaskScheduler.Start();
+
+
                 logger.Info("running....");
+                DatabaseService service = new DatabaseService();
+                service.GetCallParterAPICustomers();
+
+                DoHardWork().GetAwaiter().GetResult();
 
-                RunProgramRunExample().GetAwaiter().GetResult();
+              
             }
             catch (Exception ex)
             {
@@ -32,7 +38,7 @@ namespace EVCB_OCPP.TaskScheduler
 
         }
 
-        private static async Task RunProgramRunExample()
+        private static async Task DoHardWork()
         {
             try
             {
@@ -45,12 +51,28 @@ namespace EVCB_OCPP.TaskScheduler
                 await scheduler.Start();
 
                 // define the job and tie it to our HelloJob class
-                IJobDetail job = JobBuilder.Create<CheckEVSEOnlineJob>()
+                IJobDetail _CheckEVSEOnlineJob = JobBuilder.Create<CheckEVSEOnlineJob>()
                     .WithIdentity("job1", "group1")
                     .Build();
 
+                IJobDetail _StartTransacionReportJob = JobBuilder.Create<StartTransacionReportJob>()
+                 .WithIdentity("job2", "group1")
+                 .Build();
+
+                IJobDetail _StopTransacionReportJob = JobBuilder.Create<StopTransacionReportJob>()
+                 .WithIdentity("job3", "group1")
+                 .Build();
+
+                IJobDetail _CheckExecutionCmdJob = JobBuilder.Create<CheckExecutionCmdJob>()
+               .WithIdentity("job4", "group1")
+               .Build();
+
+                IJobDetail _ExecutionCmdReportJob = JobBuilder.Create<ExecutionCmdReportJob>()
+                 .WithIdentity("job5", "group1")
+                 .Build();
+
                 // Trigger the job to run now, and then repeat every 10 seconds
-                ITrigger trigger = TriggerBuilder.Create()
+                ITrigger _CheckEVSEOnlineTrigger = TriggerBuilder.Create()
                     .WithIdentity("trigger1", "group1")
                     .StartNow()
                     .WithSimpleSchedule(x => x
@@ -58,14 +80,47 @@ namespace EVCB_OCPP.TaskScheduler
                         .RepeatForever())
                     .Build();
 
+                ITrigger _StartTransacionReportTrigger = TriggerBuilder.Create()
+                  .WithIdentity("trigger2", "group1")
+                  .StartNow()
+                  .WithSimpleSchedule(x => x
+                      .WithIntervalInSeconds(10)
+                      .RepeatForever())
+                  .Build();
+
+                ITrigger _StopTransacionReportTrigger = TriggerBuilder.Create()
+               .WithIdentity("trigger3", "group1")
+               .StartNow()
+               .WithSimpleSchedule(x => x
+                   .WithIntervalInSeconds(10)
+                   .RepeatForever())
+               .Build();
+
+                ITrigger _CheckExecutionCmdTrigger = TriggerBuilder.Create()
+             .WithIdentity("trigger4", "group1")
+             .StartNow()
+             .WithSimpleSchedule(x => x
+                 .WithIntervalInSeconds(10)
+                 .RepeatForever())
+             .Build();
+
+
+                ITrigger _ExecutionCmdReportTrigger = TriggerBuilder.Create()
+             .WithIdentity("trigger5", "group1")
+             .StartNow()
+             .WithSimpleSchedule(x => x
+                 .WithIntervalInSeconds(10)
+                 .RepeatForever())
+             .Build();
+
                 // Tell quartz to schedule the job using our trigger
-                await scheduler.ScheduleJob(job, trigger);
+                await scheduler.ScheduleJob(_CheckEVSEOnlineJob, _CheckEVSEOnlineTrigger);
+                await scheduler.ScheduleJob(_StartTransacionReportJob, _StartTransacionReportTrigger);
+                await scheduler.ScheduleJob(_StopTransacionReportJob, _StopTransacionReportTrigger);
+                await scheduler.ScheduleJob(_CheckExecutionCmdJob, _CheckExecutionCmdTrigger);
+                await scheduler.ScheduleJob(_ExecutionCmdReportJob, _ExecutionCmdReportTrigger);
 
-                //// some sleep to show what's happening
-                //await Task.Delay(TimeSpan.FromSeconds(60));
 
-                //// and last shut down the scheduler when you are ready to close your program
-                //await scheduler.Shutdown();
             }
             catch (SchedulerException se)
             {

+ 404 - 0
EVCB_OCPP.TaskScheduler/Services/CommonCustomerService.cs

@@ -0,0 +1,404 @@
+using EVCB_OCPP.TaskScheduler.Models;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Transactions;
+
+namespace EVCB_OCPP.TaskScheduler.Services
+{
+    public class CommonCustomerService : ICustomerService
+    {
+        private NLog.ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        private Guid customerId = Guid.Empty;
+        private string customerName = string.Empty;
+        private string partnerAPIRoot = string.Empty;
+        private CancellationToken _ct;
+        private DatabaseService _dbService = new DatabaseService();
+        private ParallelOptions po = new ParallelOptions();
+        private OuterHttpClient httpClient = new OuterHttpClient();
+        private int ChargeRecordCallCounter = 0;
+
+        public CommonCustomerService() { }
+
+        public CommonCustomerService(Guid customerId)
+        {
+            this.customerId = customerId;
+            customerName = _dbService.GetCustomerName(this.customerId);
+            _dbService.GetCustomerName(this.customerId);
+        }
+
+
+
+        public List<Guid> GetCallPartnerCustomers()
+        {
+            return _dbService.GetCallParterAPICustomers();
+        }
+
+
+
+
+
+
+        async public Task ReportStartTransaction()
+        {
+            var items = _dbService.GetNeedReportSession(customerId, true, 1000);
+
+            Stopwatch watch = new Stopwatch();
+            watch.Start();
+
+            List<Task> groupTasks = new List<Task>();
+            int skipCount = 0;
+            int count = items.Count / 5 <= 100 ? items.Count : items.Count / 5;
+            while (skipCount < items.Count)
+            {
+                if (items.Count - skipCount < count)
+                {
+                    count = items.Count - skipCount;
+                }
+
+                var templst = items.Skip(skipCount).Take(count).ToList();
+                if (templst.Count > 0)
+                {
+                    Task t = Task.Factory.StartNew(async () =>
+                    {
+                        await Assigned_StartTransactionCallbackTask(templst);
+                    }, TaskCreationOptions.AttachedToParent);
+                    groupTasks.Add(t);
+
+                }
+                skipCount += count;
+            }
+            while (ChargeRecordCallCounter != groupTasks.Count)
+            {
+                await Task.Delay(10);
+            }
+
+            watch.Stop();
+
+            logger.Debug("ReportStartTransaction Task(" + items.Count() + ") : It takes  " + watch.ElapsedMilliseconds / 1000 + " Seconds");
+
+        }
+
+        async private Task Assigned_StartTransactionCallbackTask(List<Models.Transaction> reportlst)
+        {
+            await Task.Factory.StartNew(async () =>
+            {
+                //處理主機傳送的有指令
+                try
+                {
+                    if (reportlst.Count > 0)
+                    {
+
+                        int completecounter = 0;
+                        Dictionary<int, TransactionResponse> sendBack = new Dictionary<int, TransactionResponse>();
+
+                        object responseLock = new object();
+                        po.CancellationToken = _ct;
+                        po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
+                        Parallel.ForEach(reportlst, po, async (r) =>
+                        {
+                            var request = new
+                            {
+                                ChargeBoxId = r.ChargeBoxId,
+                                ConnectorId = r.ConnectorId,
+                                SessionId = r.Id,
+                                MeterStart = r.MeterStart,
+                                IdTag = r.StartIdTag,
+                                StartTime = r.StartTime.ToString(DefaultSetting.UTC_DATETIMEFORMAT)
+                            };
+
+                            var response = await httpClient.Post(partnerAPIRoot + "start_session", new Dictionary<string, string>()
+                            {
+                                { "PartnerId",customerId.ToString()}
+
+                            }, null, _dbService.GetAPIKey(customerId));
+
+
+                            lock (responseLock)
+                            {
+                                sendBack.Add(r.Id, new TransactionResponse()
+                                {
+                                    StartTransactionReportedOn = DateTime.Now,
+                                    ErrorMsg = response.Success ? null :
+                                    (response.Exception == null ? response.Response : response.Exception.ToString())
+                                });
+                                completecounter++;
+                            }
+
+                        });
+
+                        while (completecounter != reportlst.Count)
+                        {
+                            await Task.Delay(10);
+                        }
+
+                        _dbService.ReportStartTx(sendBack);
+
+
+
+
+
+
+
+                    }
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine("Assigned_StartTransactionCallbackTask Exception: " + ex.GetBaseException().ToString());
+                }
+
+                ChargeRecordCallCounter++;
+            }, TaskCreationOptions.AttachedToParent);
+
+
+        }
+
+        async public Task ReportStopTransaction()
+        {
+            var items = _dbService.GetNeedReportSession(customerId, false, 1000);
+
+            Stopwatch watch = new Stopwatch();
+            watch.Start();
+
+            List<Task> groupTasks = new List<Task>();
+            int skipCount = 0;
+            int count = items.Count / 5 <= 100 ? items.Count : items.Count / 5;
+            while (skipCount < items.Count)
+            {
+                if (items.Count - skipCount < count)
+                {
+                    count = items.Count - skipCount;
+                }
+                var templst = items.Skip(skipCount).Take(count).ToList();
+                if (templst.Count > 0)
+                {
+                    Task t = Task.Factory.StartNew(async () =>
+                    {
+                        await Assigned_StopTransactionCallbackTask(templst);
+                    }, TaskCreationOptions.AttachedToParent);
+                    groupTasks.Add(t);
+
+                }
+                skipCount += count;
+            }
+            while (ChargeRecordCallCounter != groupTasks.Count)
+            {
+                await Task.Delay(10);
+            }
+
+            watch.Stop();
+
+            Console.WriteLine("ReportStopTransaction Task(" + items.Count() + ") : It takes  " + watch.ElapsedMilliseconds / 1000 + " Seconds");
+
+        }
+
+        async private Task Assigned_StopTransactionCallbackTask(List<Models.Transaction> reportlst)
+        {
+            await Task.Factory.StartNew(async () =>
+            {
+                //處理主機傳送的有指令
+                try
+                {
+                    if (reportlst.Count > 0)
+                    {
+
+                        int completecounter = 0;
+                        Dictionary<int, TransactionResponse> sendBack = new Dictionary<int, TransactionResponse>();
+
+                        object responseLock = new object();
+                        po.CancellationToken = _ct;
+                        po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
+                        Parallel.ForEach(reportlst, po, async (r) =>
+                        {
+                            var request = new
+                            {
+                                ChargeBoxId = r.ChargeBoxId,
+                                ConnectorId = r.ConnectorId,
+                                SessionId = r.Id,
+                                MeterStart = r.MeterStart,
+                                MeterStop = r.MeterStop,
+                                IdTag = r.StartIdTag,
+                                StartTime = r.StartTime.ToString(DefaultSetting.UTC_DATETIMEFORMAT),
+                                StopTime = r.StopTime.ToString(DefaultSetting.UTC_DATETIMEFORMAT),
+                                StopReason = r.StopReasonId < 1 ? "Unknown" : (r.StopReasonId > 12 ? "Unknown" : ((Reason)r.StopReasonId).ToString())
+
+                            };
+
+                            var response = await httpClient.Post(partnerAPIRoot + "completed_session", new Dictionary<string, string>()
+                            {
+                                { "PartnerId",customerId.ToString()}
+
+                            }, null, _dbService.GetAPIKey(customerId));
+
+
+                            lock (responseLock)
+                            {
+                                sendBack.Add(r.Id, new TransactionResponse()
+                                {
+                                    StopTransactionReportedOn = DateTime.Now,
+                                    ErrorMsg = response.Success ? null :
+                                    (response.Exception == null ? response.Response : response.Exception.ToString())
+                                });
+                                completecounter++;
+                            }
+
+                        });
+
+                        while (completecounter != reportlst.Count)
+                        {
+                            await Task.Delay(10);
+                        }
+
+                        _dbService.ReportStopTx(sendBack);
+
+
+
+
+
+
+
+                    }
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine("Assigned_StartTransactionCallbackTask Exception: " + ex.GetBaseException().ToString());
+                }
+
+                ChargeRecordCallCounter++;
+            }, TaskCreationOptions.AttachedToParent);
+
+
+        }
+
+        async public Task MonitorRemoteCommand()
+        {
+            Stopwatch watch = new Stopwatch();
+            watch.Start();
+            _dbService.TurntoTimeoutMachineOperateCommands(60);
+            await Task.Delay(10);
+            watch.Stop();
+            logger.Debug("ReportExecutionofRemoteCommand Task : It takes  " + watch.ElapsedMilliseconds / 1000 + " Seconds");
+
+        }
+
+
+
+
+        async public Task ReportExecutionofRemoteCommand()
+        {
+            var items = _dbService.GetNeedReportExecution(customerId, 1000);
+
+            Stopwatch watch = new Stopwatch();
+            watch.Start();
+
+            List<Task> groupTasks = new List<Task>();
+            int skipCount = 0;
+            int count = items.Count / 5 <= 100 ? items.Count : items.Count / 5;
+            while (skipCount < items.Count)
+            {
+                if (items.Count - skipCount < count)
+                {
+                    count = items.Count - skipCount;
+                }
+                var templst = items.Skip(skipCount).Take(count).ToList();
+                if (templst.Count > 0)
+                {
+                    Task t = Task.Factory.StartNew(async () =>
+                    {
+                        await Assigned_ReportExecutionofRemoteCommandTask(templst);
+                    }, TaskCreationOptions.AttachedToParent);
+                    groupTasks.Add(t);
+
+                }
+                skipCount += count;
+            }
+            while (ChargeRecordCallCounter != groupTasks.Count)
+            {
+                await Task.Delay(10);
+            }
+
+            watch.Stop();
+
+            logger.Debug("ReportExecutionofRemoteCommand Task(" + items.Count() + ") : It takes  " + watch.ElapsedMilliseconds / 1000 + " Seconds");
+
+        }
+
+        async private Task Assigned_ReportExecutionofRemoteCommandTask(List<MachineOperateRecord> reportlst)
+        {
+            await Task.Factory.StartNew(async () =>
+            {
+                //處理主機傳送的有指令
+                try
+                {
+                    if (reportlst.Count > 0)
+                    {
+
+                        int completecounter = 0;
+                        Dictionary<int, BasicResponse> sendBack = new Dictionary<int, BasicResponse>();
+
+                        object responseLock = new object();
+                        po.CancellationToken = _ct;
+                        po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
+                        Parallel.ForEach(reportlst, po, async (r) =>
+                        {
+                            var request = new
+                            {
+                                ChargeBoxId = r.ChargeBoxId,
+                                SerialNo = r.SerialNo,
+                                CommandType = r.ActionConverttoCommandType(),
+                                Result = r.GetExecution().Result,
+                                Message = r.GetExecution().Detail,
+                            };
+
+                            var response = await httpClient.Post(partnerAPIRoot + "commands/results", new Dictionary<string, string>()
+                            {
+                                { "PartnerId",customerId.ToString()}
+
+                            }, null, _dbService.GetAPIKey(customerId));
+
+
+                            lock (responseLock)
+                            {
+                                sendBack.Add(r.Id, new BasicResponse()
+                                {
+                                    ReportedOn = DateTime.Now,
+                                    ErrorMsg = response.Success ? null :
+                                    (response.Exception == null ? response.Response : response.Exception.ToString())
+                                });
+                                completecounter++;
+                            }
+
+                        });
+
+                        while (completecounter != reportlst.Count)
+                        {
+                            await Task.Delay(10);
+                        }
+
+                        _dbService.ReportExecution(sendBack);
+
+
+
+
+
+
+
+                    }
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine("Assigned_ReportExecutionofRemoteCommandTask Exception: " + ex.GetBaseException().ToString());
+                }
+
+                ChargeRecordCallCounter++;
+            }, TaskCreationOptions.AttachedToParent);
+
+
+        }
+    }
+}

+ 20 - 0
EVCB_OCPP.TaskScheduler/Services/CustomerBackendFactory.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Services
+{
+    public class CustomerBackendFactory
+    {
+        public static ICustomerService Create(Guid customerId)
+        {           
+            return new CommonCustomerService(customerId);
+
+          
+        }
+
+
+    }
+}

+ 291 - 0
EVCB_OCPP.TaskScheduler/Services/DatabaseService.cs

@@ -0,0 +1,291 @@
+using Dapper;
+using EVCB_OCPP.TaskScheduler.Models;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Transactions;
+using Transaction = EVCB_OCPP.TaskScheduler.Models.Transaction;
+
+namespace EVCB_OCPP.TaskScheduler.Services
+{
+    internal class DatabaseService
+    {
+        private NLog.ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        private string mainDBConnectString = ConfigurationManager.ConnectionStrings["MainDBContext"].ToString();
+        private string onlineDBConnectString = ConfigurationManager.ConnectionStrings["OnlineLogDBContext"].ToString();
+
+        internal DatabaseService()
+        {
+
+        }
+
+        internal string GetCustomerName(Guid customerId)
+        {
+            string name = string.Empty;
+            try
+            {
+                using (var dbConn = new SqlConnection(mainDBConnectString))
+                {
+                    dbConn.Open();
+                    var parameters = new DynamicParameters();
+                    parameters.Add("@Id", customerId.ToString(), System.Data.DbType.String);
+                    name = dbConn.Query<string>("SELECT Name FROM [dbo].[Customer] where Id=@Id  ", parameters).FirstOrDefault();
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("Query Data Error " + ex.ToString());
+            }
+
+            return name;
+        }
+
+
+        internal bool IsCallParterAPI(Guid customerId)
+        {
+            bool result = false;
+            try
+            {
+                using (var dbConn = new SqlConnection(mainDBConnectString))
+                {
+                    dbConn.Open();
+                    var parameters = new DynamicParameters();
+                    parameters.Add("@Id", customerId.ToString(), System.Data.DbType.String);
+                    result = dbConn.ExecuteScalar<bool>("SELECT count(*) FROM [dbo].[Customer] where Id=@Id and CallPartnerApiOnSchedule=1 ", parameters);
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("Query Data Error " + ex.ToString());
+            }
+
+            return result;
+
+
+        }
+
+        internal string GetAPIKey(Guid partnerId)
+        {
+            string key = string.Empty;
+            var parameters = new DynamicParameters();
+            parameters.Add("@Id", partnerId, DbType.Guid, ParameterDirection.Input);
+            using (SqlConnection conn = new SqlConnection(mainDBConnectString))
+            {
+                string strSql = "Select ApiKey from [dbo].[Customer] where Id=@Id; ";
+                key = conn.ExecuteScalarAsync<string>(strSql, parameters, null, DefaultSetting.DB_DefaultConnectionTimeout).Result;
+            }
+            return key;
+
+        }
+
+
+        internal List<Guid> GetCallParterAPICustomers()
+        {
+            List<Guid> result = new List<Guid>();
+            try
+            {
+                using (var dbConn = new SqlConnection(mainDBConnectString))
+                {
+                    dbConn.Open();
+                    result = dbConn.Query<Guid>("SELECT Id FROM [dbo].[Customer] where CallPartnerApiOnSchedule=1").ToList();
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("Query Data Error " + ex.ToString());
+            }
+
+            return result;
+        }
+
+
+        internal List<Transaction> GetNeedReportSession(Guid customerId, bool isgoing, int size)
+        {
+            List<Transaction> result = new List<Transaction>();
+            try
+            {
+                using (var dbConn = new SqlConnection(mainDBConnectString))
+                {
+                    dbConn.Open();
+                    var parameters = new DynamicParameters();
+                    parameters.Add("@CustomerId", customerId.ToString(), System.Data.DbType.String);
+                    string sqlString = string.Empty;
+
+                    if (isgoing)
+                    {
+                        sqlString = "SELECT Top(" + size + ") Id, ChargeBoxId,ConnectorId,StartTime,MeterStart,StartIdTag FROM [dbo].[TransactionRecord] where CustomerId=@CustomerId and StopTime='1991/1/1' and StartTransactionReportedOn='1991/1/1' ";
+                    }
+                    else
+                    {
+                        sqlString = "SELECT Top(" + size + ") Id,ChargeBoxId,ConnectorId,StartTime,StopTime,MeterStart,MeterStop,StartIdTag ,StopReasonId FROM [dbo].[TransactionRecord] where CustomerId=@CustomerId and StopTime!='1991/1/1' and StopTransactionReportedOn='1991/1/1' ";
+                    }
+
+                    result = dbConn.Query<Transaction>(sqlString, parameters).ToList();
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("Query Data Error " + ex.ToString());
+            }
+
+            return result;
+        }
+
+        internal List<MachineOperateRecord> GetNeedReportExecution(Guid customerId, int size)
+        {
+            List<MachineOperateRecord> result = new List<MachineOperateRecord>();
+            try
+            {
+                using (var dbConn = new SqlConnection(mainDBConnectString))
+                {
+                    dbConn.Open();
+                    var parameters = new DynamicParameters();
+                    parameters.Add("@Id", customerId.ToString(), System.Data.DbType.String);
+                    string sqlString = string.Empty;
+
+                    sqlString = "SELECT Top(" + size + ") Id, ChargeBoxId,Action,SerialNo,Status,EVSE_Value,EVSE_Status FROM [dbo].[MachineOperateRecord] where Status!=0 and RequestType=1 and ReportedOn='1991/01/01' ";
+
+                    result = dbConn.Query<MachineOperateRecord>(sqlString, parameters).ToList();
+                }
+
+
+
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("GetNeedReportExecution Error " + ex.ToString());
+            }
+
+            return result;
+        }
+
+
+        internal void ReportStartTx(Dictionary<int, TransactionResponse> reportResults)
+        {
+
+            try
+            {
+                using (var tranScope = new TransactionScope())
+                {
+                    using (var dbConn = new SqlConnection(mainDBConnectString))
+                    {
+                        dbConn.Open();
+
+                        foreach (var kv in reportResults)
+                        {
+                            var parameters = new DynamicParameters();
+                            parameters.Add("@Id", kv.Key, DbType.Int32, ParameterDirection.Input);
+                            parameters.Add("@StartTransactionReportedOn", kv.Value.StartTransactionReportedOn, DbType.DateTime, ParameterDirection.Input);
+                            parameters.Add("@ErrorMsg", kv.Value.ErrorMsg, DbType.String, ParameterDirection.Input);
+                            dbConn.Execute("UPDATE [dbo].[TransactionRecord] set StartTransactionReportedOn=@StartTransactionReportedOn, ErrorMsg=@ErrorMsg  where Id=@Id",parameters);
+                        }
+
+                        tranScope.Complete();
+                    }
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("ReportStartTx Error " + ex.ToString());
+            }
+
+
+        }
+
+        internal void ReportStopTx(Dictionary<int, TransactionResponse> reportResults)
+        {
+
+            try
+            {
+                using (var tranScope = new TransactionScope())
+                {
+                    using (var dbConn = new SqlConnection(mainDBConnectString))
+                    {
+                        dbConn.Open();
+
+                        foreach (var kv in reportResults)
+                        {
+                            var parameters = new DynamicParameters();
+                            parameters.Add("@Id", kv.Key, DbType.Int32, ParameterDirection.Input);
+                            parameters.Add("@StopTransactionReportedOn", kv.Value.StopTransactionReportedOn, DbType.DateTime, ParameterDirection.Input);
+                            parameters.Add("@ErrorMsg", kv.Value.ErrorMsg, DbType.String, ParameterDirection.Input);
+                            dbConn.Execute("UPDATE [dbo].[TransactionRecord] set StopTransactionReportedOn=@StopTransactionReportedOn, ErrorMsg=@ErrorMsg  where Id=@Id",parameters);
+                        }
+
+                        tranScope.Complete();
+                    }
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("ReportStopTx Error " + ex.ToString());
+            }
+
+
+        }
+
+
+        internal void TurntoTimeoutMachineOperateCommands(int intervalSeconds)
+        {
+           
+            try
+            {
+                using (var dbConn = new SqlConnection(mainDBConnectString))
+                {
+                    dbConn.Open();
+                    dbConn.Execute("CheckUnCommitMachineOperateCommand", new { @TimeoutIntervalSeconds = intervalSeconds }, null, null, commandType: CommandType.StoredProcedure);
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("TurntoTimeoutMachineOperateCommands Error " + ex.ToString());
+            }
+
+           
+        }
+
+        internal void ReportExecution(Dictionary<int, BasicResponse> reportResults)
+        {
+            try
+            {
+               using (var tranScope = new TransactionScope())
+                {
+                    using (var dbConn = new SqlConnection(mainDBConnectString))
+                    {
+                        dbConn.Open();
+
+                        foreach (var kv in reportResults)
+                        {
+                            var parameters = new DynamicParameters();
+                            parameters.Add("@Id", kv.Key, DbType.Int32, ParameterDirection.Input);
+                            parameters.Add("@ReportedOn", kv.Value.ReportedOn, DbType.DateTime, ParameterDirection.Input);
+                          
+                            dbConn.Execute("UPDATE [dbo].[MachineOperateRecord] set ReportedOn=@ReportedOn where Id=@Id",parameters);
+                        }
+
+                       tranScope.Complete();
+                    }
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error("ReportExecution Error " + ex.ToString());
+            }
+
+        }
+    }
+}

+ 308 - 0
EVCB_OCPP.TaskScheduler/Services/HttpClientService.cs

@@ -0,0 +1,308 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Services
+{
+    public class HttpClientService
+    {
+        /// <summary>
+        /// 要求逾時前等候的時間長度
+        /// #網域名稱系統(DNS)查詢最多可能需要15秒的時間才會傳回或超時
+        /// 預設60秒
+        /// </summary>
+        public int Timeout { get => _timeout; set => _timeout = value; }
+
+        /// <summary>
+        /// 取得或設定使用 HttpClient 物件提出要求時,所允許的同時連線 數目上限 (每個伺服器端點)。
+        /// 請注意,此限制是按照每個伺服器端點計算,例如值 256 允許 http://www.adatum.com/ 使用 256 個同時連線,
+        /// 而 http://www.adventure-works.com/ 另有 256 個同時連線。
+        /// 預設100個
+        /// </summary>
+        public int MaxConnectionsPerServer { get => _maxConnectionsPerServer; set => _maxConnectionsPerServer = value; }
+
+
+        /// <summary>
+        /// 設定HttpMessageHandler的生命週期,如果其存留期間尚未過期,HttpMessageHandler 執行個體可從集區重複使用(建立新的 HttpClient 執行個體時)
+        /// 因為處理常式通常會管理自己專屬的底層 HTTP 連線。 建立比所需數目更多的處理常式,可能會導致連線延遲。 有些處理常式也會保持連線無限期地開啟,這可能導致處理常式無法回應 DNS (網域名稱系統)變更。
+        /// 預設處理常式存留時間為120秒。
+        /// </summary>
+        public int HandlerLifetime { get => _handlerLifetime; set => _handlerLifetime = value; }
+
+
+        private IHttpClientFactory _clientFactory = null;
+        private IServiceCollection _services = new ServiceCollection();
+        private int _handlerLifetime = 2;
+        private int _maxConnectionsPerServer = 20;
+        private int _timeout = 60;
+
+
+        public HttpClientService(string baseAddress = "")
+        {
+
+            _services.AddHttpClient("Default", c =>
+            {
+                if (!string.IsNullOrEmpty(baseAddress))
+                {
+                    c.BaseAddress = new Uri(baseAddress);
+                }
+                c.Timeout = TimeSpan.FromSeconds(_timeout);
+                c.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
+            })
+          .AddTypedClient<HttpClient>().SetHandlerLifetime(TimeSpan.FromSeconds(_handlerLifetime)).ConfigurePrimaryHttpMessageHandler((h =>
+          {
+              return new HttpClientHandler
+              {
+                  MaxConnectionsPerServer = _maxConnectionsPerServer
+
+              };
+          }));
+
+            Init();
+        }
+
+
+        private void Init()
+        {
+            _clientFactory = _services.BuildServiceProvider()
+                     .GetRequiredService<IHttpClientFactory>();
+        }
+
+
+        public virtual async Task<HttpResponse> PostJsonAsync(string Url, string bodyData, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
+        {
+            HttpResponse result = new HttpResponse() { IsError = false };
+
+            try
+            {
+                var client = _clientFactory.CreateClient(clientName);
+
+                if (!string.IsNullOrEmpty(authorizationToken))
+                {
+                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
+                }
+                if (headers != null)
+                {
+                    for (int idx = 0; idx < headers.Count; idx++)
+                    {
+                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
+                    }
+                }
+
+                HttpContent content = new StringContent(bodyData);
+                content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");
+                content.Headers.ContentType.CharSet = "UTF-8";
+
+                var response = await client.PostAsync(Url, content).ConfigureAwait(false);
+
+                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
+                result.Headers = response.Headers;
+                result.RequestMessage = response.RequestMessage;
+                result.StatusCode = response.StatusCode;
+                result.Response = await response.Content.ReadAsStringAsync();
+
+
+
+            }
+            catch (Exception ex)
+            {
+                result.IsError = true;
+                result.Exception = ex;
+
+            }
+
+
+            return result;
+        }
+
+        public virtual async Task<HttpResponse> GetJsonAsync(string Url, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
+        {
+            HttpResponse result = new HttpResponse() { IsError = false };
+
+            try
+            {
+                var client = _clientFactory.CreateClient(clientName);
+
+                if (!string.IsNullOrEmpty(authorizationToken))
+                {
+                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
+                }
+
+                if (headers != null)
+                {
+                    for (int idx = 0; idx < headers.Count; idx++)
+                    {
+                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
+                    }
+                }
+
+                // client.DefaultRequestHeaders.Add("Content-Type", "application/json");
+
+
+                var response = await client.GetAsync(Url).ConfigureAwait(false);
+
+                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
+                result.Headers = response.Headers;
+                result.RequestMessage = response.RequestMessage;
+                result.StatusCode = response.StatusCode;
+                result.Response = await response.Content.ReadAsStringAsync();
+
+
+
+            }
+            catch (Exception ex)
+            {
+                result.IsError = true;
+                result.Exception = ex;
+
+            }
+
+
+            return result;
+        }
+
+        public virtual async Task<HttpResponse> PutJsonAsync(string Url, string bodyData, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
+        {
+            HttpResponse result = new HttpResponse() { IsError = false };
+
+            try
+            {
+                var client = _clientFactory.CreateClient(clientName);
+
+                if (!string.IsNullOrEmpty(authorizationToken))
+                {
+                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
+                }
+                if (headers != null)
+                {
+                    for (int idx = 0; idx < headers.Count; idx++)
+                    {
+                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
+                    }
+                }
+
+                HttpContent content = new StringContent(bodyData);
+                content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");
+                content.Headers.ContentType.CharSet = "UTF-8";
+
+                var response = await client.PutAsync(Url, content).ConfigureAwait(false);
+
+                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
+                result.Headers = response.Headers;
+                result.RequestMessage = response.RequestMessage;
+                result.StatusCode = response.StatusCode;
+                result.Response = await response.Content.ReadAsStringAsync();
+
+
+
+            }
+            catch (Exception ex)
+            {
+                result.IsError = true;
+                result.Exception = ex;
+
+            }
+
+
+            return result;
+        }
+
+        public virtual async Task<HttpResponse> DeleteJsonAsync(string Url, Dictionary<string, string> headers, string clientName = "Default", bool bearerToken = false, string authorizationToken = null)
+        {
+            HttpResponse result = new HttpResponse() { IsError = false };
+
+            try
+            {
+                var client = _clientFactory.CreateClient(clientName);
+
+                if (!string.IsNullOrEmpty(authorizationToken))
+                {
+                    client.DefaultRequestHeaders.Authorization = bearerToken ? new AuthenticationHeaderValue("Bearer", authorizationToken) : new AuthenticationHeaderValue(authorizationToken);
+                }
+
+                if (headers != null)
+                {
+                    for (int idx = 0; idx < headers.Count; idx++)
+                    {
+                        client.DefaultRequestHeaders.Add(headers.ElementAt(idx).Key, headers.ElementAt(idx).Value);
+                    }
+                }
+
+                //   client.DefaultRequestHeaders.Add("Content-Type", "application/json");
+
+
+                var response = await client.DeleteAsync(Url).ConfigureAwait(false);
+
+                result.IsSuccessStatusCode = response.IsSuccessStatusCode;
+                result.Headers = response.Headers;
+                result.RequestMessage = response.RequestMessage;
+                result.StatusCode = response.StatusCode;
+                result.Response = await response.Content.ReadAsStringAsync();
+
+
+
+            }
+            catch (Exception ex)
+            {
+                result.IsError = true;
+                result.Exception = ex;
+
+            }
+
+
+            return result;
+        }
+
+    }
+
+
+    public class HttpResponse
+    {
+        public bool IsError { internal set; get; }
+
+        public Exception Exception { internal set; get; }
+
+        public string Response { internal set; get; }
+
+        /// <summary>
+        /// 摘要:
+        ///     取得或設定 HTTP 回應的狀態碼。
+        /// 傳回:
+        ///    HTTP 回應的狀態碼。
+        /// </summary>
+        public HttpStatusCode StatusCode { get; internal set; }
+
+
+        /// <summary>
+        /// 摘要:
+        ///      取得 HTTP 回應標頭的集合。
+        /// 傳回:
+        ///     HTTP 回應標頭的集合。
+        /// </summary>
+        public HttpResponseHeaders Headers { get; internal set; }
+
+
+        /// <summary>
+        /// 摘要:
+        ///     取得或設定導致此回應訊息的要求訊息。
+        /// 傳回:
+        ///       導致此回應訊息的要求訊息。
+        /// </summary>
+        public HttpRequestMessage RequestMessage { get; internal set; }
+
+        /// <summary>
+        /// 摘要:
+        ///   取得指示 HTTP 回應是否成功的值。
+        /// 傳回:
+        ///       指示 HTTP 回應是否成功的值。 如果 System.Net.Http.HttpResponseMessage.StatusCode 在 200-299
+        ///     的範圍內,則為 true;否則為 false。
+        /// </summary>
+        public bool IsSuccessStatusCode { get; internal set; }
+    }
+}

+ 21 - 0
EVCB_OCPP.TaskScheduler/Services/ICustomerService.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.TaskScheduler.Services
+{
+    public interface ICustomerService
+    {
+        List<Guid> GetCallPartnerCustomers();
+
+        Task ReportStartTransaction();
+
+        Task ReportStopTransaction();
+
+        Task MonitorRemoteCommand();
+
+        Task ReportExecutionofRemoteCommand();
+    }
+}

+ 18 - 0
EVCB_OCPP.TaskScheduler/packages.config

@@ -1,8 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="Dapper" version="2.0.30" targetFramework="net471" />
+  <package id="Microsoft.Bcl.AsyncInterfaces" version="1.1.0" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Configuration" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Configuration.Abstractions" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Configuration.Binder" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.DependencyInjection" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Http" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Logging" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Logging.Abstractions" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Options" version="3.1.2" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Primitives" version="3.1.2" targetFramework="net471" />
+  <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net471" />
   <package id="NLog" version="4.6.6" targetFramework="net471" />
   <package id="NLog.Config" version="4.6.6" targetFramework="net471" />
   <package id="NLog.Schema" version="4.6.6" targetFramework="net471" />
   <package id="Quartz" version="3.0.7" targetFramework="net471" />
+  <package id="System.Buffers" version="4.4.0" targetFramework="net471" />
+  <package id="System.ComponentModel.Annotations" version="4.7.0" targetFramework="net471" />
+  <package id="System.Memory" version="4.5.2" targetFramework="net471" />
+  <package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net471" />
+  <package id="System.Runtime.CompilerServices.Unsafe" version="4.7.0" targetFramework="net471" />
+  <package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net471" />
 </packages>