Procházet zdrojové kódy

加入專案檔案。

Jessica.Tseng před 5 roky
rodič
revize
485e4919f8
100 změnil soubory, kde provedl 23618 přidání a 0 odebrání
  1. 31 0
      EVCB_OCPP.Server.sln
  2. 47 0
      EVCB_OCPP.WSServer/App.config
  3. binární
      EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Domain.dll
  4. binární
      EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Packet.dll
  5. binární
      EVCB_OCPP.WSServer/DLL/SuperSocket.Common.dll
  6. binární
      EVCB_OCPP.WSServer/DLL/SuperSocket.SocketBase.dll
  7. binární
      EVCB_OCPP.WSServer/DLL/SuperSocket.SocketEngine.dll
  8. binární
      EVCB_OCPP.WSServer/DLL/SuperWebSocket.dll
  9. 26 0
      EVCB_OCPP.WSServer/Dto/ConnectorErrorStauts.cs
  10. 128 0
      EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj
  11. 16 0
      EVCB_OCPP.WSServer/GlobalConfig.cs
  12. 19 0
      EVCB_OCPP.WSServer/Helper/Convertor.cs
  13. 343 0
      EVCB_OCPP.WSServer/Message/BasicMessageHandler.cs
  14. 606 0
      EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs
  15. 42 0
      EVCB_OCPP.WSServer/Message/MessageResult.cs
  16. 52 0
      EVCB_OCPP.WSServer/Message/NeedConfirmMessage.cs
  17. 15 0
      EVCB_OCPP.WSServer/Message/ProfileErrorHandler.cs
  18. 44 0
      EVCB_OCPP.WSServer/NLog.config
  19. 3531 0
      EVCB_OCPP.WSServer/NLog.xsd
  20. 18 0
      EVCB_OCPP.WSServer/Program.cs
  21. 36 0
      EVCB_OCPP.WSServer/Properties/AssemblyInfo.cs
  22. 762 0
      EVCB_OCPP.WSServer/ProtalServer.cs
  23. 23 0
      EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallCmd.cs
  24. 23 0
      EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallErrorCmd.cs
  25. 23 0
      EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallResultCmd.cs
  26. 56 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/ClientData.cs
  27. 445 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPLog.cs
  28. 38 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPLogFactory.cs
  29. 38 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPSubCommandParser.cs
  30. 321 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPSubProtocol.cs
  31. 127 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPWSServer.cs
  32. 9 0
      EVCB_OCPP.WSServer/packages.config
  33. 86 0
      OCPPServer/App.config
  34. 114 0
      OCPPServer/ClientData.cs
  35. 23 0
      OCPPServer/Command/ProcessCallCmd.cs
  36. 23 0
      OCPPServer/Command/ProcessCallErrorCmd.cs
  37. 23 0
      OCPPServer/Command/ProcessCallResultCmd.cs
  38. 78 0
      OCPPServer/Common/ListenerConfig.cs
  39. 22 0
      OCPPServer/Common/LocalAuthorization.cs
  40. 61 0
      OCPPServer/Common/OCPPResult.cs
  41. 69 0
      OCPPServer/Config/log4net.config
  42. 69 0
      OCPPServer/Config/log4net.unix.config
  43. 109 0
      OCPPServer/Handler/FeatureHandler.cs
  44. 256 0
      OCPPServer/Handler/MakeConfirmationHandler.cs
  45. 612 0
      OCPPServer/Handler/MakeRequestHandler.cs
  46. 2556 0
      OCPPServer/Handler/OCPPMessageHandler.cs
  47. 60 0
      OCPPServer/NLog.config
  48. 3027 0
      OCPPServer/NLog.xsd
  49. 40 0
      OCPPServer/NeedConfirmPacket.cs
  50. 222 0
      OCPPServer/OCPPServer.csproj
  51. 127 0
      OCPPServer/OCPPWebSocketHeaderReceiveFilter.cs
  52. 35 0
      OCPPServer/OCPPWebSocketProtocol.cs
  53. 36 0
      OCPPServer/Properties/AssemblyInfo.cs
  54. 240 0
      OCPPServer/Protocol/ClientData.cs
  55. 149 0
      OCPPServer/Protocol/OCPPWSServer.cs
  56. 28 0
      OCPPServer/Server.cs
  57. 770 0
      OCPPServer/Service/WebSocketCommandService.cs
  58. 445 0
      OCPPServer/SubProtocol/OCPPLog.cs
  59. 108 0
      OCPPServer/SubProtocol/OCPPLogFactory.cs
  60. 35 0
      OCPPServer/SubProtocol/OCPPSubCommandParser.cs
  61. 319 0
      OCPPServer/SubProtocol/OCPPSubProtocol.cs
  62. 1862 0
      OCPPServer/WSServer.cs
  63. 20 0
      OCPPServer/packages.config
  64. 373 0
      SocketBase/AppServer.cs
  65. 159 0
      SocketBase/AppServerBase.ConfigHotUpdate.cs
  66. 51 0
      SocketBase/AppServerBase.Net45.cs
  67. 1723 0
      SocketBase/AppServerBase.cs
  68. 689 0
      SocketBase/AppSession.cs
  69. 143 0
      SocketBase/Async.cs
  70. 53 0
      SocketBase/Command/CommandBase.cs
  71. 39 0
      SocketBase/Command/CommandInfo.cs
  72. 74 0
      SocketBase/Command/CommandLoaderBase.cs
  73. 28 0
      SocketBase/Command/CommandUpdateEventArgs.cs
  74. 51 0
      SocketBase/Command/CommandUpdateInfo.cs
  75. 70 0
      SocketBase/Command/ICommand.cs
  76. 20 0
      SocketBase/Command/ICommandFilterProvider.cs
  77. 51 0
      SocketBase/Command/ICommandLoader.cs
  78. 104 0
      SocketBase/Command/ReflectCommandLoader.cs
  79. 26 0
      SocketBase/Command/StringCommandBase.cs
  80. 67 0
      SocketBase/CommandExecutingContext.cs
  81. 63 0
      SocketBase/Config/CertificateConfig.cs
  82. 22 0
      SocketBase/Config/CommandAssemblyConfig.cs
  83. 92 0
      SocketBase/Config/ConfigurationSource.cs
  84. 17 0
      SocketBase/Config/HotUpdateAttribute.cs
  85. 59 0
      SocketBase/Config/ICertificateConfig.cs
  86. 21 0
      SocketBase/Config/ICommandAssemblyConfig.cs
  87. 42 0
      SocketBase/Config/IConfigurationSource.cs
  88. 33 0
      SocketBase/Config/IListenerConfig.cs
  89. 22 0
      SocketBase/Config/IRootConfig.Net45.cs
  90. 79 0
      SocketBase/Config/IRootConfig.cs
  91. 22 0
      SocketBase/Config/IServerConfig.Net45.cs
  92. 272 0
      SocketBase/Config/IServerConfig.cs
  93. 22 0
      SocketBase/Config/ITypeProvider.cs
  94. 43 0
      SocketBase/Config/ListenerConfig.cs
  95. 19 0
      SocketBase/Config/RootConfig.Net45.cs
  96. 115 0
      SocketBase/Config/RootConfig.cs
  97. 19 0
      SocketBase/Config/ServerConfig.Net45.cs
  98. 401 0
      SocketBase/Config/ServerConfig.cs
  99. 32 0
      SocketBase/Config/TypeProvider.cs
  100. 59 0
      SocketBase/Config/TypeProviderCollection.cs

+ 31 - 0
EVCB_OCPP.Server.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.705
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EVCB_OCPP.WSServer", "EVCB_OCPP.WSServer\EVCB_OCPP.WSServer.csproj", "{DE0C1E9A-1EEE-42CC-8A91-73BF9056A7E7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestTool.RemoteTriggerAPP", "TestTool.RemoteTriggerAPP\TestTool.RemoteTriggerAPP.csproj", "{F39A3B1E-2B93-40E1-9C7B-8CEE2529BF52}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{DE0C1E9A-1EEE-42CC-8A91-73BF9056A7E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DE0C1E9A-1EEE-42CC-8A91-73BF9056A7E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DE0C1E9A-1EEE-42CC-8A91-73BF9056A7E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DE0C1E9A-1EEE-42CC-8A91-73BF9056A7E7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F39A3B1E-2B93-40E1-9C7B-8CEE2529BF52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F39A3B1E-2B93-40E1-9C7B-8CEE2529BF52}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F39A3B1E-2B93-40E1-9C7B-8CEE2529BF52}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F39A3B1E-2B93-40E1-9C7B-8CEE2529BF52}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {79198208-2DBA-46E9-B361-18617E64EAAE}
+	EndGlobalSection
+EndGlobal

+ 47 - 0
EVCB_OCPP.WSServer/App.config

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <configSections>
+    <section name="superSocket" type="SuperSocket.SocketEngine.Configuration.SocketServiceConfig, SuperSocket.SocketEngine" />
+    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
+    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
+    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
+  </configSections>
+   <connectionStrings>
+    <add name="ConnectionLogDBContext" connectionString="data source=172.1.0.142\SQLEXPRESS;initial catalog=OCPP_ConnectionLogDBContext;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.0.142\SQLEXPRESS;initial catalog=OCPP_MainDBContext;;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
+    <add name="MeterValueDBContext" connectionString="data source=172.1.0.142\SQLEXPRESS;initial catalog=OCPP_MeterValueDBContext;;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
+  </connectionStrings>
+  <startup>
+    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
+  </startup>
+  <superSocket>
+    <servers>
+      <server name="SuperWebSocket" serverTypeName="SuperWebSocket">
+        <!--  <certificate filePath="localhost.pfx" password="supersocket" storeName="My" thumbprint="‎f42585bceed2cb049ef4a3c6d0ad572a6699f6f3" storeLocation="LocalMachine" clientCertificateRequired="false" keyStorageFlags="Exportable"></certificate> -->
+        <!--<certificate filePath="certificate.pfx" password="y42j/4cj84" storeName="My" thumbprint="‎f115742946884724c07cd507564daa12dac89615" storeLocation="LocalMachine" clientCertificateRequired="false" keyStorageFlags="Exportable"></certificate>-->
+        <listeners></listeners>
+      </server>
+    </servers>
+    <serverTypes>
+      <add name="SuperWebSocket" type="SuperWebSocket.WebSocketServer, SuperWebSocket" />
+    </serverTypes>
+  </superSocket>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
+  <entityFramework>
+    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
+      <parameters>
+        <parameter value="mssqllocaldb" />
+      </parameters>
+    </defaultConnectionFactory>
+    <providers>
+      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
+    </providers>
+  </entityFramework>
+</configuration>

binární
EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Domain.dll


binární
EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Packet.dll


binární
EVCB_OCPP.WSServer/DLL/SuperSocket.Common.dll


binární
EVCB_OCPP.WSServer/DLL/SuperSocket.SocketBase.dll


binární
EVCB_OCPP.WSServer/DLL/SuperSocket.SocketEngine.dll


binární
EVCB_OCPP.WSServer/DLL/SuperWebSocket.dll


+ 26 - 0
EVCB_OCPP.WSServer/Dto/ConnectorErrorStauts.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Dto
+{
+    public class ConnectorErrorStauts
+    {
+        
+        public byte ConnectorId { get; set; }
+
+        public int Status { get; set; }
+
+        public int ChargePointErrorCodeId { get; set; }
+    
+        public string ErrorInfo { get; set; }
+      
+        public string VendorId { get; set; }
+      
+        public string VendorErrorCode { get; set; }
+
+       
+    }
+}

+ 128 - 0
EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj

@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{DE0C1E9A-1EEE-42CC-8A91-73BF9056A7E7}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>EVCB_OCPP.WSServer</RootNamespace>
+    <AssemblyName>EVCB_OCPP.WSServer</AssemblyName>
+    <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
+      <HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
+    </Reference>
+    <Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
+      <HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
+    </Reference>
+    <Reference Include="EVCB_OCPP.Domain, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>DLL\EVCB_OCPP.Domain.dll</HintPath>
+    </Reference>
+    <Reference Include="EVCB_OCPP.Packet, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>DLL\EVCB_OCPP.Packet.dll</HintPath>
+    </Reference>
+    <Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
+      <HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.12.0.1\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>
+    <Reference Include="SuperSocket.Common">
+      <HintPath>DLL\SuperSocket.Common.dll</HintPath>
+    </Reference>
+    <Reference Include="SuperSocket.SocketBase">
+      <HintPath>DLL\SuperSocket.SocketBase.dll</HintPath>
+    </Reference>
+    <Reference Include="SuperSocket.SocketEngine">
+      <HintPath>DLL\SuperSocket.SocketEngine.dll</HintPath>
+    </Reference>
+    <Reference Include="SuperWebSocket">
+      <HintPath>DLL\SuperWebSocket.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.ComponentModel.DataAnnotations" />
+    <Reference Include="System.Configuration" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.IO.Compression" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.ServiceModel" />
+    <Reference Include="System.Transactions" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Dto\ConnectorErrorStauts.cs" />
+    <Compile Include="GlobalConfig.cs" />
+    <Compile Include="Helper\Convertor.cs" />
+    <Compile Include="Message\BasicMessageHandler.cs" />
+    <Compile Include="Message\CoreProfileHandler.cs" />
+    <Compile Include="Message\MessageResult.cs" />
+    <Compile Include="Message\NeedConfirmMessage.cs" />
+    <Compile Include="Message\ProfileErrorHandler.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ProtalServer.cs" />
+    <Compile Include="SuperSocket.Command\ProcessCallCmd.cs" />
+    <Compile Include="SuperSocket.Command\ProcessCallErrorCmd.cs" />
+    <Compile Include="SuperSocket.Command\ProcessCallResultCmd.cs" />
+    <Compile Include="SuperSocket.Protocol\ClientData.cs" />
+    <Compile Include="SuperSocket.Protocol\OCPPLog.cs" />
+    <Compile Include="SuperSocket.Protocol\OCPPLogFactory.cs" />
+    <Compile Include="SuperSocket.Protocol\OCPPSubCommandParser.cs" />
+    <Compile Include="SuperSocket.Protocol\OCPPSubProtocol.cs" />
+    <Compile Include="SuperSocket.Protocol\OCPPWSServer.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+    <Content Include="DLL\SuperSocket.Common.dll" />
+    <Content Include="DLL\SuperSocket.SocketBase.dll" />
+    <Content Include="DLL\SuperSocket.SocketEngine.dll" />
+    <Content Include="DLL\SuperWebSocket.dll" />
+    <Content Include="NLog.config">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <None Include="NLog.xsd">
+      <SubType>Designer</SubType>
+    </None>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="DLL\EVCB_OCPP.Domain.dll" />
+    <Content Include="DLL\EVCB_OCPP.Packet.dll" />
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 16 - 0
EVCB_OCPP.WSServer/GlobalConfig.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer
+{
+    public static class GlobalConfig
+    {
+        /// <summary>
+        /// 預設心跳間隔時間 單位:秒
+        /// </summary>
+        public static readonly int DEFAULT_HEARTBEAT_INTERVAL=15;
+    }
+}

+ 19 - 0
EVCB_OCPP.WSServer/Helper/Convertor.cs

@@ -0,0 +1,19 @@
+using EVCB_OCPP.Packet.Features;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Helper
+{
+    public  static class Convertor
+    {
+        public static Actions GetAction(string action)
+        {
+            Actions result = Actions.None;
+            Enum.TryParse<Actions>(action, out result);
+            return result;
+        }
+    }
+}

+ 343 - 0
EVCB_OCPP.WSServer/Message/BasicMessageHandler.cs

@@ -0,0 +1,343 @@
+using EVCB_OCPP.Packet.Features;
+using EVCB_OCPP.Packet.Messages;
+using EVCB_OCPP.Packet.Messages.Basic;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NLog;
+using OCPPServer.Protocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    /// <summary>
+    /// 實現 OCPP 基本傳送規範,
+    /// 1.訊息 基本格式,將訊息包裝成 Call 、CallResult、CallError 三種格式
+    /// 2.OCPP 定義的傳送規則:交易相關的訊息必須依照時序性傳送,一個傳完才能接著送下一個(忽略規則 由Center System定義)
+    /// </summary>
+    internal class BasicMessageHandler
+    {
+        static protected ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+
+        #region 傳送 or 解析訊息需要欄位
+        private const int INDEX_MESSAGEID = 0;
+        private const int INDEX_UNIQUEID = 1;
+        internal const int TYPENUMBER_CALL = 2;
+        private const int INDEX_CALL_ACTION = 2;
+        private const int INDEX_CALL_PAYLOAD = 3;
+
+        internal const int TYPENUMBER_CALLRESULT = 3;
+        private const int INDEX_CALLRESULT_PAYLOAD = 2;
+
+        internal const int TYPENUMBER_CALLERROR = 4;
+        private const int INDEX_CALLERROR_ERRORCODE = 2;
+        private const int INDEX_CALLERROR_DESCRIPTION = 3;
+        private const int INDEX_CALLERROR_PAYLOAD = 4;
+
+        private const string CALL_FORMAT = "[2,\"{0}\",\"{1}\",{2}]";
+        private const string CALLRESULT_FORMAT = "[3,\"{0}\",{1}]";
+        private const string CALLERROR_FORMAT = "[4,\"{0}\",\"{1}\",\"{2}\",{3}]";
+        private const string DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+        private const string DATE_FORMAT_WITH_MS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+        #endregion
+
+        private List<Profile> profiles = new List<Profile>()
+        {
+             new CoreProfile(),
+             new FirmwareManagementProfile(),
+             new ReservationProfile(),
+             new RemoteTriggerProfile(),
+             new SmartChargingProfile(),
+             new LocalAuthListManagementProfile()
+
+        };
+
+        /// <summary>
+        /// 將收到的封包做基本的拆解分成 Call 、CallResult、CallError
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        internal MessageResult AnalysisReceiveData(ClientData client, string data)
+        {
+            MessageResult result = new MessageResult();
+            try
+            {
+                var msg = Parse(data);
+                if (msg != null)
+                {
+
+                    result.UUID = msg.Id;
+                    switch (msg.TypeId)
+                    {
+                        case TYPENUMBER_CALL:
+                            {
+                                //只有CallMessage 才有在RawData有Action
+                                IRequest request = UnPackPayloadbyCall(msg.Action, msg.Payload.ToString());
+                                Actions action = Actions.None;
+                                Enum.TryParse<Actions>(msg.Action, out action);
+                                result.Action = msg.Action;
+                                if (request != null)
+                                {
+                                    if (request.Validate())
+                                    {
+                                        result.Id = TYPENUMBER_CALL;
+                                        result.Message = request;
+
+                                    }
+                                    else
+                                    {
+                                        string replyMsg = GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(),
+                                             OCPPErrorDescription.OccurenceConstraintViolation);
+                                        result.Id = TYPENUMBER_CALL;
+                                        result.Message = request;
+                                        result.Success = false;
+                                        result.CallErrorMsg = replyMsg;
+
+                                    }
+                                }
+                                else
+                                {
+                                    string replyMsg = GenerateCallError(msg.Id, OCPPErrorCodes.NotImplemented, OCPPErrorDescription.NotImplemented);
+                                    result.Id = TYPENUMBER_CALL;
+                                    result.Message = request;
+                                    result.Success = false;
+                                    result.CallErrorMsg = replyMsg;
+                                }
+
+                            }
+                            break;
+                        case TYPENUMBER_CALLRESULT:
+                            {
+                                IConfirmation confirmation = UnPackPayloadbyCallResult(client.queue, msg.Id, msg.Payload.ToString());
+
+                                if (confirmation != null)
+                                {
+
+                                    if (confirmation.Validate())
+                                    {
+                                        result.Id = TYPENUMBER_CALLRESULT;
+                                        result.Message = confirmation;
+                                        result.Action = confirmation.GetRequest().Action;
+                                        //return data
+                                    }
+                                    else
+                                    {
+                                        string replyMsg = GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(),
+                                      OCPPErrorDescription.OccurenceConstraintViolation);
+                                        result.Id = TYPENUMBER_CALLRESULT;
+                                        result.Message = confirmation;
+                                        result.Success = false;
+                                        result.CallErrorMsg = replyMsg;
+                                    }
+                                }
+                                else
+                                {
+                                    string replyMsg = GenerateCallError(msg.Id, OCPPErrorCodes.NotImplemented.ToString(),
+                                   OCPPErrorDescription.NotImplemented);
+                                    result.Id = TYPENUMBER_CALLRESULT;
+                                    result.Message = confirmation;
+                                    result.Success = false;
+                                    result.CallErrorMsg = replyMsg;
+                                }
+
+                            }
+                            break;
+                        case TYPENUMBER_CALLERROR:
+                            {
+                                var sentRequest = UnPackPayloadbyCallError(client.queue, msg.Id);
+
+                                if (sentRequest != null)
+                                {
+                                    IRequest request = sentRequest as IRequest;
+                                    result.Action = request.Action;
+                                    result.Id = TYPENUMBER_CALLERROR;
+                                    result.Message = sentRequest;
+                                    result.ReceivedErrorCode = string.Format("ErrorMsg {0}:{1}", ((CallErrorMessage)msg).ErrorCode, ((CallErrorMessage)msg).ErrorDescription);
+                                }
+
+
+                            }
+                            break;
+                        default:
+                            break;
+
+                    }
+
+                    // if (msg != null) Console.WriteLine(string.Format("Receieved Message : {0}", msg.ToString()));
+
+                }
+            }
+            catch (Exception ex)
+            {
+                result.Success = false;
+                result.Exception = ex;
+            }
+            return result;
+        }
+
+        #region 解析收到的訊息
+        /// <summary>
+        /// Parse data to OCPP Basic Message
+        /// </summary>
+        /// <param name="message"></param>
+        /// <returns></returns>
+        private BaseMessage Parse(string message)
+        {
+            try
+            {
+                var array = JsonConvert.DeserializeObject<JArray>(message);
+                BaseMessage msg = null;
+                switch ((int)array[INDEX_MESSAGEID])
+                {
+                    case TYPENUMBER_CALL:
+                        {
+                            CallMessage call = new CallMessage();
+                            call.Action = array[INDEX_CALL_ACTION].ToString();
+                            call.Payload = array[INDEX_CALL_PAYLOAD].ToString().Replace("\r\n", "");
+                            msg = call;
+                        }
+                        break;
+                    case TYPENUMBER_CALLRESULT:
+                        {
+                            CallResultMessage callResult = new CallResultMessage();
+                            callResult.Payload = array[INDEX_CALLRESULT_PAYLOAD].ToString().Replace("\r\n", "");
+                            msg = callResult;
+                        }
+                        break;
+                    case TYPENUMBER_CALLERROR:
+                        {
+                            CallErrorMessage callError = new CallErrorMessage();
+                            callError.ErrorCode = array[INDEX_CALLERROR_ERRORCODE].ToString();
+                            callError.ErrorDescription = array[INDEX_CALLERROR_DESCRIPTION].ToString();
+                            callError.ErrorDetails = array[INDEX_CALLERROR_PAYLOAD].ToString().Replace("\r\n", "");
+                            msg = callError;
+                        }
+                        break;
+                    default:
+                        throw new Exception("Message Type notSupported");
+
+
+                }
+                msg.Id = array[INDEX_UNIQUEID].ToString();
+                return msg;
+            }
+            catch (Exception ex)
+            {
+                throw new Exception(string.Format("Error=>Communicator.Parse: {0}", ex.ToString()));
+
+            }
+
+
+
+        }
+
+
+        private IRequest UnPackPayloadbyCall(string action, string payload)
+        {
+            try
+            {
+                Feature feature = null;
+                foreach (var profile in profiles)
+                {
+                    feature = profile.GetFeaturebyAction(action);
+                    if (feature == null)
+                    {
+                        continue;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+
+                return JsonConvert.DeserializeObject(payload, feature.GetRequestType()) as IRequest;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(string.Format("UnPackPayloadbyCall Ex: {0}", ex.ToString()), "UnPack");
+                return null;
+            }
+        }
+
+        private IConfirmation UnPackPayloadbyCallResult(Queue requestQueue, string uniqueId, string payload)
+        {
+            try
+            {
+                IRequest request = requestQueue.RestoreRequest(uniqueId);
+                Feature feature = null;
+                foreach (var profile in profiles)
+                {
+                    feature = profile.GetFeaturebyType(request.GetType());
+                    if (feature == null)
+                    {
+                        continue;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+                IConfirmation confrim = JsonConvert.DeserializeObject(payload, feature.GetConfirmationType()) as IConfirmation;
+                confrim.SetRequest(request);
+                return confrim;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(string.Format("UnPackPayloadbyCallResult Data:[{0},{1}] Ex: {2}", uniqueId, payload, ex.ToString()), "UnPack");
+                return null;
+            }
+
+        }
+
+        private object UnPackPayloadbyCallError(Queue requestQueue, string uniqueId)
+        {
+            object sentMsg = requestQueue.RestoreRequest(uniqueId);
+
+            return sentMsg;
+
+        }
+        #endregion
+
+        internal string GenerateCallError(string uniqueId, string errorCode, string errorDescription)
+        {
+            
+            string msg = string.Format(CALLERROR_FORMAT, uniqueId, errorCode, errorDescription, "{}");
+            return msg;
+
+        }
+
+        internal string GenerateConfirmation(string uniqueId, IConfirmation confirmation)
+        {
+            string msg = string.Empty;
+            if (confirmation != null && confirmation.Validate())
+            {
+
+                msg = string.Format(CALLRESULT_FORMAT, uniqueId, JsonConvert.SerializeObject(confirmation, new JsonSerializerSettings() {  NullValueHandling= NullValueHandling.Ignore}));
+            }
+            else
+            {
+                logger.Error(string.Format("confirmation is null  or InVaild in GenerateConfirmation Method"), "Warning");
+            }
+            return msg;
+        }
+
+        internal string GenerateRequest(string uniqueId, string action, IRequest request)
+        {
+            string msg = string.Empty;
+            if (request != null && request.Validate())
+            {
+
+                msg = string.Format(CALL_FORMAT, uniqueId, action, JsonConvert.SerializeObject(request, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }));
+            }
+            else
+            {
+                logger.Error(string.Format("confirmation is null  or InVaild in GenerateRequest Method"), "Warning");
+            }
+            return msg;
+        }
+    }
+}

+ 606 - 0
EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs

@@ -0,0 +1,606 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Entity;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.Domain.Models.Database;
+using EVCB_OCPP.Packet.Features;
+using EVCB_OCPP.Packet.Messages;
+using EVCB_OCPP.Packet.Messages.Core;
+using EVCB_OCPP.Packet.Messages.SubTypes;
+using EVCB_OCPP.WSServer.Dto;
+using Newtonsoft.Json;
+using OCPPPackage.Profiles;
+using OCPPServer.Protocol;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+
+    internal partial class ProfileHandler
+    {
+        
+
+        internal MessageResult ExecuteCoreRequest(Actions action, ClientData session, IRequest request)
+        {
+            MessageResult result = new MessageResult() { Success = false };
+
+            switch (action)
+            {
+
+                case Actions.BootNotification:
+                    {
+                        BootNotificationRequest _request = request as BootNotificationRequest;
+                        int heartbeat_interval = GlobalConfig.DEFAULT_HEARTBEAT_INTERVAL;
+                        using (var db = new MainDBContext())
+                        {
+                            var _machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+                            _machine.ChargeBoxSerialNumber = string.IsNullOrEmpty(_request.chargeBoxSerialNumber) ? string.Empty : _request.chargeBoxSerialNumber;
+                            _machine.ChargePointModel = string.IsNullOrEmpty(_request.chargePointModel) ? string.Empty : _request.chargePointModel;
+                            _machine.ChargePointVendor = string.IsNullOrEmpty(_request.chargePointVendor) ? string.Empty : _request.chargePointVendor;
+                            _machine.FW_CurrentVersion = string.IsNullOrEmpty(_request.firmwareVersion) ? string.Empty : _request.firmwareVersion;
+                            _machine.Iccid = string.IsNullOrEmpty(_request.iccid) ? string.Empty : _request.iccid;
+                            _machine.Imsi = string.IsNullOrEmpty(_request.imsi) ? string.Empty : _request.imsi;
+                            _machine.MeterSerialNumber = string.IsNullOrEmpty(_request.meterSerialNumber) ? string.Empty : _request.meterSerialNumber;
+                            _machine.MeterType = string.IsNullOrEmpty(_request.meterType) ? string.Empty : _request.meterType;
+
+                            db.SaveChanges();
+
+                            var configVaule = db.MachineConfigure.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.HeartbeatInterval)
+                                .Select(x => x.ConfigureSetting).FirstOrDefault();
+
+                            if (configVaule != null)
+                            {
+                                int.TryParse(configVaule, out heartbeat_interval);
+                            }
+                        }
+                        var confirm = new BootNotificationConfirmation() { currentTime = DateTime.Now, interval = heartbeat_interval, status = Packet.Messages.SubTypes.RegistrationStatus.Accepted };
+
+                        result.Message = confirm;
+                        result.Success = true;
+                    }
+                    break;
+                case Actions.StatusNotification:
+                    {
+                        //只保留最新上報狀況
+                        StatusNotificationRequest _request = request as StatusNotificationRequest;
+                        List<string> removeIds = new List<string>();
+                        ConnectorErrorStauts _oldStatus = null;
+                        using (var oldDB = new MainDBContext())
+                        {
+                            if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
+                            {
+                                _oldStatus = oldDB.ConnectorStatus.Where(x => x.ChargeBoxId == session.ChargeBoxId
+                             && x.ConnectorId == _request.connectorId).Select(x => new ConnectorErrorStauts
+                             {
+                                 Status = x.Status,
+                                 ChargePointErrorCodeId = x.ChargePointErrorCodeId,
+                                 ErrorInfo = x.ErrorInfo,
+                                 VendorId = x.VendorId,
+                                 VendorErrorCode = x.VendorErrorCode
+                             }).FirstOrDefault();
+                            }
+
+                            removeIds = oldDB.ConnectorStatus.Where(x => x.ChargeBoxId == session.ChargeBoxId
+                             && x.ConnectorId == _request.connectorId).Select(x => x.Id).ToList();
+                        }
+
+                        using (var db = new MainDBContext())
+                        {
+                            foreach (var id in removeIds)
+                            {
+
+                                string query = "DELETE FROM [dbo].[ConnectorStatus] WHERE [Id]={0} ";
+                                db.Database.ExecuteSqlCommand(query, id);
+                            }
+
+                            db.SaveChanges();
+                            var _currentStatus = new Domain.Models.Database.ConnectorStatus()
+                            {
+                                ChargeBoxId = session.ChargeBoxId,
+                                ConnectorId = (byte)_request.connectorId,
+                                CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.Now,
+                                Status = (int)_request.status,
+                                ChargePointErrorCodeId = (int)_request.errorCode,
+                                ErrorInfo = _request.info,
+                                VendorId = _request.vendorId,
+                                VendorErrorCode = _request.vendorErrorCode,
+                                Id = Guid.NewGuid().ToString()
+                            };
+                            db.ConnectorStatus.Add(_currentStatus);
+
+                            if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
+                            {
+                                db.MachineError.Add(new MachineError()
+                                {
+                                    ConnectorId = (byte)_request.connectorId,
+                                    CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.Now,
+                                    Status = (int)_request.status,
+                                    ChargeBoxId = session.ChargeBoxId,
+                                    ErrorCodeId = (int)_request.errorCode,
+                                    ErrorInfo = _request.info,
+                                    PreStatus = _oldStatus.Status,
+                                    VendorErrorCode = _request.vendorErrorCode,
+                                    VendorId = _request.vendorId
+                                });
+                            }
+
+
+                            db.SaveChanges();
+                        }
+                        var confirm = new StatusNotificationConfirmation() { };
+                        result.Message = confirm;
+                        result.Success = true;
+                    }
+                    break;
+                case Actions.Heartbeat:
+                    {
+                        using (var db = new MainDBContext())
+                        {
+                            var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+                            if (machine != null)
+                            {
+                                db.Configuration.AutoDetectChangesEnabled = false;
+                                db.Configuration.ValidateOnSaveEnabled = false;
+                                db.Machine.Attach(machine);
+                                machine.HeartbeatUpdatedOn = DateTime.Now;
+
+                                db.Entry(machine).Property(x => x.HeartbeatUpdatedOn).IsModified = true;
+                                db.SaveChanges();
+                            }
+                            var confirm = new HeartbeatConfirmation() { currentTime = DateTime.Now };
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                    }
+                    break;
+                case Actions.MeterValues:
+                    {
+                        MeterValuesRequest _request = request as MeterValuesRequest;
+
+                        if (_request.meterValue.Count > 0)
+                        {
+                            using (var db = new MeterValueDBContext())
+                            {
+                                foreach (var item in _request.meterValue)
+                                {
+                                    foreach (var sampleVaule in item.sampledValue)
+                                    {
+
+                                        string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
+                     "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";
+
+                                        List<SqlParameter> parameter = new List<SqlParameter>
+                                        {
+                                              new SqlParameter("ChargeBoxId",session.ChargeBoxId),
+                                              new SqlParameter("ConnectorId",  (byte)_request.connectorId),
+                                              new SqlParameter("Value",sampleVaule.value),
+                                              new SqlParameter("CreatedOn",item.timestamp),
+                                              new SqlParameter("ContextId",sampleVaule.context.HasValue ? (int)sampleVaule.context : 0),
+                                              new SqlParameter("FormatId",sampleVaule.format.HasValue ? (int)sampleVaule.format : 0),
+                                              new SqlParameter("MeasurandId",sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0),
+                                              new SqlParameter("PhaseId",sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0),
+                                              new SqlParameter("LocationId",sampleVaule.location.HasValue ? (int)sampleVaule.location : 0),
+                                              new SqlParameter("UnitId",sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0),
+                                              new SqlParameter("TransactionId",_request.transactionId.HasValue?_request.transactionId:-1),
+                                          };
+
+
+                                        db.Database.ExecuteSqlCommand(sp, parameter.ToArray());
+                                    }
+                                }
+                            }
+
+
+                        }
+
+                        var confirm = new MeterValuesConfirmation() { };
+                        result.Message = confirm;
+                        result.Success = true;
+                    }
+                    break;
+                case Actions.StartTransaction:
+                    {
+                        StartTransactionRequest _request = request as StartTransactionRequest;
+                        long _pastTime = DateTimeOffset.Now.AddYears(-20).ToUnixTimeSeconds();
+                        int _tranId = (int)(_pastTime % 100000000) * 10 + 1;
+
+                        //需處理StopTransactionOnInvalidId 情境,時間若和當前時間差距10秒,發動Authorize詢問後在回覆
+                        using (var db = new MainDBContext())
+                        {
+                            var _CustomerId = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).Include(x => x.Customer).
+                                 Select(x => x.CustomerId).FirstOrDefault();
+
+                            db.TransactionRecord.Add(new TransactionRecord()
+                            {
+                                ChargeBoxId = session.ChargeBoxId,
+                                ConnectorId = (byte)_request.connectorId,
+                                CreatedOn = DateTime.Now,
+                                StartIdTag = _request.idTag,
+                                MeterStart = _request.meterStart,
+                                CustomerId = _CustomerId,
+                                StartTime = _request.timestamp,
+                                ReservationId = _request.reservationId.HasValue ? _request.reservationId.Value : 0,
+                                TransactionId = _tranId
+
+
+
+                            });
+
+                            db.SaveChanges();
+                        }
+
+
+                        var confirm = new StartTransactionConfirmation()
+                        {
+                            idTagInfo = new Packet.Messages.SubTypes.IdTagInfo()
+                            {
+                                status = Packet.Messages.SubTypes.AuthorizationStatus.Accepted
+                            },
+                            transactionId = _tranId
+
+
+
+                        };
+                        result.Message = confirm;
+                        result.Success = true;
+                    }
+                    break;
+                case Actions.StopTransaction:
+                    {
+                        StopTransactionRequest _request = request as StopTransactionRequest;
+                        List<TransactionDataRecord> _TransactionDatas = new List<TransactionDataRecord>();
+                        if (_request.transactionData != null)
+                        {
+                            if (_request.transactionData.Count > 0)
+                            {
+                                foreach (var item in _request.transactionData)
+                                {
+                                    foreach (var sampleVaule in item.sampledValue)
+                                    {
+                                        _TransactionDatas.Add(new TransactionDataRecord()
+                                        {
+                                            ChargeBoxId = session.ChargeBoxId,
+                                            MeasuredOn = item.timestamp,
+                                            TransactionId = _request.transactionId,
+                                            ContextId = sampleVaule.context.HasValue ? (int)sampleVaule.context : 0,
+                                            FormatId = sampleVaule.format.HasValue ? (int)sampleVaule.format : 0,
+                                            LocationId = sampleVaule.location.HasValue ? (int)sampleVaule.location : 0,
+                                            MeasurandId = sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0,
+                                            PhaseId = sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0,
+                                            UnitId = sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0,
+                                            Value = sampleVaule.value
+                                        });
+                                    }
+                                }
+
+                            }
+                        }
+
+                        using (var db = new MainDBContext())
+                        {
+                            var transaction = db.TransactionRecord.Where(x => x.TransactionId == _request.transactionId
+                             && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+
+                            if (transaction != null)
+                            {
+                                transaction.MeterStop = _request.meterStop;
+                                transaction.StopTime = _request.timestamp;
+                                transaction.StopReasonId = _request.reason.HasValue ? (int)_request.reason.Value : 0;
+                                transaction.StopIdTag = _request.idTag;
+
+                                if (_TransactionDatas.Count > 0)
+                                {
+                                    _TransactionDatas.ForEach(x => x.ConnectorId = (byte)transaction.ConnectorId);
+                                }
+
+                                db.SaveChanges();
+                                var confirm = new StopTransactionConfirmation()
+                                {
+                                    idTagInfo = new Packet.Messages.SubTypes.IdTagInfo()
+                                    {
+
+                                        status = Packet.Messages.SubTypes.AuthorizationStatus.Accepted
+                                    }
+
+                                };
+                                result.Message = confirm;
+                                result.Success = true;
+                            }
+                            else
+                            {
+                                result.Exception = new Exception("Can't find transactionId " + _request.transactionId);
+
+                            }
+                        }
+
+
+
+
+                    }
+                    break;
+                case Actions.Authorize:
+                    {
+                       
+
+                        var confirm = new AuthorizeConfirmation()
+                        {
+
+                            idTagInfo = new Packet.Messages.SubTypes.IdTagInfo()
+                            {
+                                status =  AuthorizationStatus.Accepted
+                            }
+                        };
+                        result.Message = confirm;
+                        result.Success = true;
+                    }
+                    break;
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", request.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
+                    }
+                    break;
+            }
+            return result;
+        }
+
+        internal MessageResult ExecuteCoreConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.ChangeAvailability:
+                    {
+                        ChangeAvailabilityConfirmation _confirm = confirm as ChangeAvailabilityConfirmation;
+                        ChangeAvailabilityRequest _request = _confirm.GetRequest() as ChangeAvailabilityRequest;
+
+                        using (var db = new MainDBContext())
+                        {
+                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                            x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+                            if (operation != null)
+                            {
+                                operation.FinishedOn = DateTime.Now;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+
+                case Actions.ClearCache:
+                    {
+                        ClearCacheConfirmation _confirm = confirm as ClearCacheConfirmation;
+                        ClearCacheRequest _request = _confirm.GetRequest() as ClearCacheRequest;
+
+                        using (var db = new MainDBContext())
+                        {
+                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                            x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+                            if (operation != null)
+                            {
+                                operation.FinishedOn = DateTime.Now;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.RemoteStartTransaction:
+                    {
+                        RemoteStartTransactionConfirmation _confirm = confirm as RemoteStartTransactionConfirmation;
+                        RemoteStartTransactionRequest _request = _confirm.GetRequest() as RemoteStartTransactionRequest;
+
+                        using (var db = new MainDBContext())
+                        {
+                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                            x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+                            if (operation != null)
+                            {
+                                operation.FinishedOn = DateTime.Now;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.RemoteStopTransaction:
+                    {
+                        RemoteStopTransactionConfirmation _confirm = confirm as RemoteStopTransactionConfirmation;
+                        RemoteStopTransactionRequest _request = _confirm.GetRequest() as RemoteStopTransactionRequest;
+
+                        using (var db = new MainDBContext())
+                        {
+                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                            x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+                            if (operation != null)
+                            {
+                                operation.FinishedOn = DateTime.Now;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.Reset:
+                    {
+                        ResetConfirmation _confirm = confirm as ResetConfirmation;
+                        ResetRequest _request = _confirm.GetRequest() as ResetRequest;
+
+                        using (var db = new MainDBContext())
+                        {
+                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                            x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+                            if (operation != null)
+                            {
+                                operation.FinishedOn = DateTime.Now;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.ChangeConfiguration:
+                    {
+                        ChangeConfigurationConfirmation _confirm = confirm as ChangeConfigurationConfirmation;
+                        ChangeConfigurationRequest _request = _confirm.GetRequest() as ChangeConfigurationRequest;
+
+
+
+                        using (var db = new MainDBContext())
+                        {
+                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                     x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+
+                            if (operation != null)
+                            {
+                                operation.FinishedOn = DateTime.Now;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;
+                                operation.EVSE_Value = _confirm.status.ToString();
+
+                            }
+
+                            if (_confirm.status == Packet.Messages.SubTypes.ConfigurationStatus.Accepted || _confirm.status == Packet.Messages.SubTypes.ConfigurationStatus.RebootRequired)
+                            {
+                                var configure = db.MachineConfigure.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToList();
+
+                                var foundConfig = configure.Find(x => x.ConfigureName == _request.key);
+                                if (foundConfig != null)
+                                {
+                                    foundConfig.ReadOnly = false;
+                                    foundConfig.ConfigureSetting = _request.value;
+                                }
+                                else
+                                {
+                                    db.MachineConfigure.Add(new MachineConfigure()
+                                    {
+                                        ChargeBoxId = session.ChargeBoxId,
+                                        ConfigureName = _request.key,
+                                        ReadOnly = false,
+                                        ConfigureSetting = _request.value
+                                    });
+                                }
+                            }
+
+
+
+
+                            db.SaveChanges();
+
+                        }
+
+                    }
+                    break;
+                case Actions.GetConfiguration:
+                    {
+                        GetConfigurationConfirmation _confirm = confirm as GetConfigurationConfirmation;
+                        GetConfigurationRequest _request = _confirm.GetRequest() as GetConfigurationRequest;
+
+                        using (var db = new MainDBContext())
+                        {
+                            var configure = db.MachineConfigure.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToList();
+
+                            foreach (var item in _confirm.configurationKey)
+                            {
+                                var foundConfig = configure.Find(x => x.ConfigureName == item.key);
+                                if (foundConfig != null)
+                                {
+                                    foundConfig.ReadOnly = item.IsReadOnly;
+                                    foundConfig.ConfigureSetting = item.value;
+                                }
+                                else
+                                {
+                                    db.MachineConfigure.Add(new MachineConfigure()
+                                    {
+                                        ChargeBoxId = session.ChargeBoxId,
+                                        ConfigureName = item.key,
+                                        ReadOnly = item.IsReadOnly,
+                                        ConfigureSetting = item.value
+                                    });
+                                }
+                            }
+
+                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                           x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+
+                            if (operation != null)
+                            {
+                                operation.FinishedOn = DateTime.Now;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = 1;
+                                operation.EVSE_Value = JsonConvert.SerializeObject(_confirm.configurationKey, Formatting.Indented);
+
+                            }
+
+                            db.SaveChanges();
+
+                        }
+                    }
+                    break;
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", confirm.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
+                    }
+                    break;
+            }
+            return result;
+        }
+
+
+        internal MessageResult ReceivedCoreError(Actions action, string errorMsg, ClientData session, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.ChangeAvailability:
+                case Actions.ChangeConfiguration:
+                case Actions.ClearCache:
+                case Actions.RemoteStartTransaction:
+                case Actions.RemoteStopTransaction:
+                case Actions.Reset:
+                case Actions.GetConfiguration:
+                    {
+                        using (var db = new MainDBContext())
+                        {
+                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                            x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+                            if (operation != null)
+                            {
+                                operation.FinishedOn = DateTime.Now;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)255;//錯誤
+                                operation.EVSE_Value = errorMsg;
+                                db.SaveChanges();
+                            }
+
+                        }
+
+                    }
+                    break;
+
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", action));
+                    }
+                    break;
+            }
+            return result;
+
+        }
+    }
+}

+ 42 - 0
EVCB_OCPP.WSServer/Message/MessageResult.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    internal class MessageResult
+    {
+
+        internal MessageResult()
+        {          
+            Success = true;
+        }
+
+        internal string RequestId { set; get; }
+
+        internal string UUID { get; set; }
+
+        internal string Action { get; set; }
+
+        internal int Id { get; set; }
+
+        internal bool Success { get; set; }
+
+        internal object Message { get; set; }
+
+        internal Exception Exception { get; set; }
+
+        internal string CallErrorMsg { get; set; }
+
+        internal string ReceivedErrorCode { get; set; }
+
+
+
+
+
+
+
+    }
+}

+ 52 - 0
EVCB_OCPP.WSServer/Message/NeedConfirmMessage.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    public class NeedConfirmMessage
+    {
+        /// <summary>
+        /// 資料庫的ID
+        /// </summary>
+        public int Id { get; set; }
+
+        public string ChargePointSerialNumber { get; set; }
+
+        /// <summary>
+        /// 傳送次數
+        /// </summary>
+        public int SentTimes { get; set; }
+
+        /// <summary>
+        /// 傳送間隔秒數
+        /// </summary>
+        public int SentInterval { get; set; }
+
+
+        /// <summary>
+        /// 送出訊息
+        /// </summary>
+        public string SentMessage { get; set; }
+
+        /// <summary>
+        /// 送出的UUID
+        /// </summary>
+        public string SentUniqueId { get; set; }
+
+        ///// <summary>
+        ///// 送出的action
+        ///// </summary>
+        public string SentAction { get; set; }
+
+        public DateTime SentOn { set; get; }
+
+        /// <summary>
+        /// 請求 Id
+        /// </summary>
+        public string RequestId { set; get; }
+
+    }
+}

+ 15 - 0
EVCB_OCPP.WSServer/Message/ProfileErrorHandler.cs

@@ -0,0 +1,15 @@
+using EVCB_OCPP.Packet.Messages;
+using OCPPServer.Protocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    internal partial class ProfileHandler
+    {
+       
+    }
+}

+ 44 - 0
EVCB_OCPP.WSServer/NLog.config

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
+      autoReload="true"
+      throwExceptions="false"
+      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
+
+  <!-- optional, add some variables
+  https://github.com/nlog/NLog/wiki/Configuration-file#variables
+  -->
+  <variable name="myvar" value="myvalue"/>
+
+  <!--
+  See https://github.com/nlog/nlog/wiki/Configuration-file
+  for information on customizing logging rules and outputs.
+   -->
+  <targets>
+
+    <!--
+    add your targets here
+    See https://github.com/nlog/NLog/wiki/Targets for possible targets.
+    See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
+    -->
+
+    <!--
+    Write events to a file with the date in the filename.
+    <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
+            layout="${longdate} ${uppercase:${level}} ${message}" />
+    -->
+   <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
+            layout="${longdate} ${uppercase:${level}} ${message}" />
+  </targets>
+
+  <rules>
+    <!-- add your logging rules here -->
+
+    <!--
+    Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace)  to "f"
+    <logger name="*" minlevel="Debug" writeTo="f" />
+    -->
+    <logger name="*" minlevel="Debug" writeTo="f" />
+  </rules>
+</nlog>

+ 3531 - 0
EVCB_OCPP.WSServer/NLog.xsd

@@ -0,0 +1,3531 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xs:schema id="NLog" targetNamespace="http://www.nlog-project.org/schemas/NLog.xsd" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.nlog-project.org/schemas/NLog.xsd">
+  <xs:element name="nlog" type="NLogConfiguration" />
+  <xs:complexType name="NLogConfiguration">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="extensions" type="NLogExtensions" />
+      <xs:element name="include" type="NLogInclude" />
+      <xs:element name="variable" type="NLogVariable" />
+      <xs:element name="targets" type="NLogTargets" />
+      <xs:element name="rules" type="NLogRules" />
+      <xs:element name="time" type="TimeSource" />
+    </xs:choice>
+    <xs:attribute name="autoReload" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Watch config file for changes and reload automatically.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogToConsole" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Print internal NLog messages to the console. Default value is: false</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogToConsoleError" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Print internal NLog messages to the console error output. Default value is: false</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogFile" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Write internal NLog messages to the specified file.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogLevel" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Log level threshold for internal log messages. Default value is: Info.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="globalThreshold" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Global log level threshold for application log messages. Messages below this level won't be logged.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="throwExceptions" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Throw an exception when there is an internal error. Default value is: false. Not recommend to set to true in production!</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="throwConfigExceptions" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Throw an exception when there is a configuration error. If not set, determined by throwExceptions.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="keepVariablesOnReload" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogToTrace" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogIncludeTimestamp" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Write timestamps for internal NLog messages. Default value is: true.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="useInvariantCulture" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Use InvariantCulture as default culture instead of CurrentCulture.  Default value is: false.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="parseMessageTemplates" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Perform message template parsing and formatting of LogEvent messages (true = Always, false = Never, empty = Auto Detect). Default value is: empty.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogTargets">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="default-wrapper" type="WrapperTargetBase" />
+      <xs:element name="default-target-parameters" type="Target" />
+      <xs:element name="target" type="Target" />
+      <xs:element name="wrapper-target" type="WrapperTargetBase" />
+      <xs:element name="compound-target" type="CompoundTargetBase" />
+    </xs:choice>
+    <xs:attribute name="async" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes).</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogRules">
+    <xs:sequence minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="logger" type="NLogLoggerRule" />
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="NLogExtensions">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="add" type="NLogExtensionsAdd" />
+    </xs:choice>
+  </xs:complexType>
+  <xs:complexType name="NLogExtensionsAdd">
+    <xs:attribute name="prefix" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Prefix for targets/layout renderers/filters/conditions loaded from this assembly.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="assemblyFile" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Load NLog extensions from the specified file (*.dll)</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="assembly" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Load NLog extensions from the specified assembly. Assembly name should be fully qualified.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogLoggerRule">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="filters" type="NLogFilters" />
+    </xs:choice>
+    <xs:attribute name="name" use="optional">
+      <xs:annotation>
+        <xs:documentation>Name of the logger. May include  wildcard characters ('*' or '?').</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="levels" type="NLogLevelList">
+      <xs:annotation>
+        <xs:documentation>Comma separated list of levels that this rule matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="minlevel" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Minimum level that this rule matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="maxlevel" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Maximum level that this rule matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="level" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Level that this rule matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="writeTo" type="NLogTargetIDList">
+      <xs:annotation>
+        <xs:documentation>Comma separated list of target names.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="final" type="xs:boolean" default="false">
+      <xs:annotation>
+        <xs:documentation>Ignore further rules if this one matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="ruleName" type="xs:string" use="optional">
+      <xs:annotation>
+        <xs:documentation>Rule identifier to allow rule lookup with Configuration.FindRuleByName and Configuration.RemoveRuleByName.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogFilters">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="when" type="when" />
+      <xs:element name="whenContains" type="whenContains" />
+      <xs:element name="whenEqual" type="whenEqual" />
+      <xs:element name="whenNotContains" type="whenNotContains" />
+      <xs:element name="whenNotEqual" type="whenNotEqual" />
+      <xs:element name="whenRepeated" type="whenRepeated" />
+    </xs:choice>
+    <xs:attribute name="defaultAction" type="FilterResult">
+      <xs:annotation>
+        <xs:documentation>Default action if none of the filters match.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:simpleType name="NLogLevel">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Off" />
+      <xs:enumeration value="Trace" />
+      <xs:enumeration value="Debug" />
+      <xs:enumeration value="Info" />
+      <xs:enumeration value="Warn" />
+      <xs:enumeration value="Error" />
+      <xs:enumeration value="Fatal" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="LineEndingMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Default" />
+      <xs:enumeration value="CRLF" />
+      <xs:enumeration value="CR" />
+      <xs:enumeration value="LF" />
+      <xs:enumeration value="None" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLogLevelList">
+    <xs:restriction base="xs:string">
+      <xs:pattern value="(|Trace|Debug|Info|Warn|Error|Fatal)(,(Trace|Debug|Info|Warn|Error|Fatal))*" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLogInclude">
+    <xs:attribute name="file" type="SimpleLayoutAttribute" use="required">
+      <xs:annotation>
+        <xs:documentation>Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="ignoreErrors" type="xs:boolean" use="optional" default="false">
+      <xs:annotation>
+        <xs:documentation>Ignore any errors in the include file.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogVariable">
+    <xs:choice minOccurs="0" maxOccurs="1">
+      <xs:element name="value" minOccurs="0" maxOccurs="1" type="xs:string">
+        <xs:annotation>
+          <xs:documentation>Variable value. Note, the 'value' attribute has precedence over this one.</xs:documentation>
+        </xs:annotation>
+      </xs:element>
+    </xs:choice>
+    <xs:attribute name="name" type="xs:string" use="required">
+      <xs:annotation>
+        <xs:documentation>Variable name.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="value" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Variable value.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:simpleType name="NLogTargetIDList">
+    <xs:restriction base="xs:string">
+      <xs:pattern value="(|([a-zA-Z][a-zA-Z0-9_\-]*))(,([a-zA-Z][a-zA-Z0-9_\-]*))*" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="Target" abstract="true"></xs:complexType>
+  <xs:complexType name="TargetRef">
+    <xs:attribute name="name" type="xs:string" use="required" />
+  </xs:complexType>
+  <xs:complexType name="WrapperTargetBase" abstract="true">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="target" type="Target" minOccurs="1" maxOccurs="1" />
+          <xs:element name="wrapper-target" type="WrapperTargetBase" minOccurs="1" maxOccurs="1" />
+          <xs:element name="compound-target" type="CompoundTargetBase" minOccurs="1" maxOccurs="1" />
+          <xs:element name="target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+          <xs:element name="wrapper-target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+          <xs:element name="compound-target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+        </xs:choice>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="CompoundTargetBase" abstract="true">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="target" type="Target" minOccurs="1" maxOccurs="unbounded" />
+          <xs:element name="wrapper-target" type="WrapperTargetBase" minOccurs="1" maxOccurs="1" />
+          <xs:element name="compound-target" type="CompoundTargetBase" minOccurs="1" maxOccurs="1" />
+          <xs:element name="target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+          <xs:element name="wrapper-target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+          <xs:element name="compound-target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+        </xs:choice>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Filter" abstract="true"></xs:complexType>
+  <xs:complexType name="TimeSource" abstract="true"></xs:complexType>
+  <xs:simpleType name="SimpleLayoutAttribute">
+    <xs:restriction base="xs:string">
+      <xs:pattern value=".*" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="Condition">
+    <xs:restriction base="xs:string">
+      <xs:minLength value="1" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="AsyncWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="batchSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="forceLockingQueue" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="fullBatchSizeWriteLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="overflowAction" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.AsyncTargetWrapperOverflowAction" />
+          <xs:element name="queueLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="timeToSleepBetweenBatches" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="batchSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of log events that should be processed in a batch by the lazy writer thread.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="forceLockingQueue" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Whether to use the locking queue, instead of a lock-free concurrent queue The locking queue is less concurrent when many logger threads, but reduces memory allocation</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="fullBatchSizeWriteLimit" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Limit of full s to write before yielding into  Performance is better when writing many small batches, than writing a single large batch</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="overflowAction" type="NLog.Targets.Wrappers.AsyncTargetWrapperOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when the lazy writer thread request queue count exceeds the set limit.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="queueLimit" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Limit on the number of requests in the lazy writer thread request queue.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="timeToSleepBetweenBatches" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Time in milliseconds to sleep between batches. (1 or less means trigger on new activity)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.Wrappers.AsyncTargetWrapperOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Grow" />
+      <xs:enumeration value="Discard" />
+      <xs:enumeration value="Block" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="AutoFlushWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="asyncFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="flushOnConditionOnly" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="asyncFlush" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Delay the flush until the LogEvent has been confirmed as written</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="condition" type="Condition">
+          <xs:annotation>
+            <xs:documentation>Condition expression. Log events who meet this condition will cause a flush on the wrapped target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="flushOnConditionOnly" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Only flush when LogEvent matches condition. Ignore explicit-flush, config-reload-flush and shutdown-flush</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="BufferingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="bufferSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="flushTimeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="overflowAction" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.BufferingTargetWrapperOverflowAction" />
+          <xs:element name="slidingTimeout" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="bufferSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of log events to be buffered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="flushTimeout" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="overflowAction" type="NLog.Targets.Wrappers.BufferingTargetWrapperOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action to take if the buffer overflows.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="slidingTimeout" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use sliding timeout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.Wrappers.BufferingTargetWrapperOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Flush" />
+      <xs:enumeration value="Discard" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="Chainsaw">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="lineEnding" minOccurs="0" maxOccurs="1" type="LineEndingMode" />
+          <xs:element name="maxMessageSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="newLine" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="connectionCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepConnection" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxConnections" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="onConnectionOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetConnectionsOverflowAction" />
+          <xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetOverflowAction" />
+          <xs:element name="sslProtocols" minOccurs="0" maxOccurs="1" type="System.Security.Authentication.SslProtocols" />
+          <xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepAliveTimeSeconds" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
+          <xs:element name="ndlcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="loggerName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="includeNLogData" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeNdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="appInfo" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="ndcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Instance of  that is used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="lineEnding" type="LineEndingMode">
+          <xs:annotation>
+            <xs:documentation>End of line value if a newline is appended at the end of log message .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxMessageSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum message size in bytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="newLine" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to append newline at the end of log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="address" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Network address.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Size of the connection cache (number of connections which are kept alive).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepConnection" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep connection open whenever possible.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxConnections" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum current connections. 0 = no maximum.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onConnectionOverflow" type="NLog.Targets.NetworkTargetConnectionsOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the will be more connections than .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onOverflow" type="NLog.Targets.NetworkTargetOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the message is larger than maxMessageSize.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="sslProtocols" type="System.Security.Authentication.SslProtocols">
+          <xs:annotation>
+            <xs:documentation>Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxQueueSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum queue size.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepAliveTimeSeconds" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>The number of seconds a connection will remain idle before the first keep-alive probe is sent</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ndlcItemSeparator" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>NDLC item separator.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeSourceInfo" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include source info (file name and line number) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="loggerName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Renderer for log4j:event logger-xml-attribute (Default ${logger})</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNLogData" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include NLog-specific extensions to log4j schema.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  stack.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  stack contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  dictionary contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  dictionary contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeCallSite" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include call site (class and method name) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeAllProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to include all properties from the log events</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="appInfo" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>AppInfo field. By default it's the friendly name of the current AppDomain.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ndcItemSeparator" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>NDC item separator.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.NetworkTargetConnectionsOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="AllowNewConnnection" />
+      <xs:enumeration value="DiscardMessage" />
+      <xs:enumeration value="Block" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.NetworkTargetOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Error" />
+      <xs:enumeration value="Split" />
+      <xs:enumeration value="Discard" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="System.Security.Authentication.SslProtocols">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="None" />
+      <xs:enumeration value="Ssl2" />
+      <xs:enumeration value="Ssl3" />
+      <xs:enumeration value="Tls" />
+      <xs:enumeration value="Tls11" />
+      <xs:enumeration value="Tls12" />
+      <xs:enumeration value="Default" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLog.Targets.NLogViewerParameterInfo">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="includeEmptyValue" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that should be use to calculate the value for the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Viewer parameter name.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="includeEmptyValue" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Whether an attribute with empty value should be included in the output</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="ColoredConsole">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="detectConsoleAvailable" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="enableAnsiOutput" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="errorStream" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="useDefaultRowHighlightingRules" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="highlight-row" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.ConsoleRowHighlightingRule" />
+          <xs:element name="highlight-word" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.ConsoleWordHighlightingRule" />
+          <xs:element name="autoFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="detectConsoleAvailable" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="enableAnsiOutput" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Enables output using ANSI Color Codes</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>The encoding for writing messages to the .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="errorStream" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether the error stream (stderr) should be used instead of the output stream (stdout).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useDefaultRowHighlightingRules" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use default row highlighting rules.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="autoFlush" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to auto-flush after </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.ConsoleOutputColor">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Black" />
+      <xs:enumeration value="DarkBlue" />
+      <xs:enumeration value="DarkGreen" />
+      <xs:enumeration value="DarkCyan" />
+      <xs:enumeration value="DarkRed" />
+      <xs:enumeration value="DarkMagenta" />
+      <xs:enumeration value="DarkYellow" />
+      <xs:enumeration value="Gray" />
+      <xs:enumeration value="DarkGray" />
+      <xs:enumeration value="Blue" />
+      <xs:enumeration value="Green" />
+      <xs:enumeration value="Cyan" />
+      <xs:enumeration value="Red" />
+      <xs:enumeration value="Magenta" />
+      <xs:enumeration value="Yellow" />
+      <xs:enumeration value="White" />
+      <xs:enumeration value="NoChange" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLog.Targets.ConsoleRowHighlightingRule">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
+      <xs:element name="backgroundColor" minOccurs="0" maxOccurs="1" type="NLog.Targets.ConsoleOutputColor" />
+      <xs:element name="foregroundColor" minOccurs="0" maxOccurs="1" type="NLog.Targets.ConsoleOutputColor" />
+    </xs:choice>
+    <xs:attribute name="condition" type="Condition">
+      <xs:annotation>
+        <xs:documentation>Condition that must be met in order to set the specified foreground and background color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="backgroundColor" type="NLog.Targets.ConsoleOutputColor">
+      <xs:annotation>
+        <xs:documentation>Background color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="foregroundColor" type="NLog.Targets.ConsoleOutputColor">
+      <xs:annotation>
+        <xs:documentation>Foreground color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLog.Targets.ConsoleWordHighlightingRule">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="compileRegex" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="regex" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="text" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="wholeWords" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="backgroundColor" minOccurs="0" maxOccurs="1" type="NLog.Targets.ConsoleOutputColor" />
+      <xs:element name="foregroundColor" minOccurs="0" maxOccurs="1" type="NLog.Targets.ConsoleOutputColor" />
+    </xs:choice>
+    <xs:attribute name="compileRegex" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="ignoreCase" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to ignore case when comparing texts.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="regex" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Regular expression to be matched. You must specify either text or regex.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="text" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Text to be matched. You must specify either text or regex.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="wholeWords" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to match whole words only.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="backgroundColor" type="NLog.Targets.ConsoleOutputColor">
+      <xs:annotation>
+        <xs:documentation>Background color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="foregroundColor" type="NLog.Targets.ConsoleOutputColor">
+      <xs:annotation>
+        <xs:documentation>Foreground color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="Console">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="detectConsoleAvailable" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="error" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="autoFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="detectConsoleAvailable" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>The encoding for writing messages to the .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="error" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to send the log messages to the standard error instead of the standard output.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="autoFlush" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to auto-flush after </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Database">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="useTransactions" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="dbUserName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="dbProvider" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="dbPassword" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="keepConnection" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="dbDatabase" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="connectionStringName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="connectionString" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="dbHost" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="installConnectionString" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="install-command" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.DatabaseCommandInfo" />
+          <xs:element name="uninstall-command" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.DatabaseCommandInfo" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.DatabaseParameterInfo" />
+          <xs:element name="commandText" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="commandType" minOccurs="0" maxOccurs="1" type="System.Data.CommandType" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useTransactions" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbUserName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbProvider" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the database provider.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbPassword" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepConnection" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep the database connection open between the log events.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbDatabase" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionStringName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the connection string (as specified in &lt;connectionStrings&gt; configuration section.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionString" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbHost" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="installConnectionString" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="commandText" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text of the SQL command to be run on each log level.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="commandType" type="System.Data.CommandType">
+          <xs:annotation>
+            <xs:documentation>Type of the SQL command to be run on each log level.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="System.Data.CommandType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Text" />
+      <xs:enumeration value="StoredProcedure" />
+      <xs:enumeration value="TableDirect" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLog.Targets.DatabaseCommandInfo">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="commandType" minOccurs="0" maxOccurs="1" type="System.Data.CommandType" />
+      <xs:element name="connectionString" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="ignoreFailures" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.DatabaseParameterInfo" />
+      <xs:element name="text" minOccurs="0" maxOccurs="1" type="Layout" />
+    </xs:choice>
+    <xs:attribute name="commandType" type="System.Data.CommandType">
+      <xs:annotation>
+        <xs:documentation>Type of the command.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="connectionString" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Connection string to run the command against. If not provided, connection string from the target is used.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="ignoreFailures" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to ignore failures.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="text" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Command text.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLog.Targets.DatabaseParameterInfo">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="dbType" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="size" minOccurs="0" maxOccurs="1" type="xs:integer" />
+      <xs:element name="precision" minOccurs="0" maxOccurs="1" type="xs:byte" />
+      <xs:element name="scale" minOccurs="0" maxOccurs="1" type="xs:byte" />
+      <xs:element name="parameterType" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="format" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="culture" minOccurs="0" maxOccurs="1" type="xs:string" />
+    </xs:choice>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Database parameter name.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that should be use to calculate the value for the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="dbType" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Database parameter DbType.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="size" type="xs:integer">
+      <xs:annotation>
+        <xs:documentation>Database parameter size.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="precision" type="xs:byte">
+      <xs:annotation>
+        <xs:documentation>Database parameter precision.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="scale" type="xs:byte">
+      <xs:annotation>
+        <xs:documentation>Database parameter scale.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="parameterType" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Type of the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="format" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Convert format of the database parameter value .</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="culture" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Culture used for parsing parameter string-value for type-conversion</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="Debugger">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Debug">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="EventLog">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="category" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="entryType" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="eventId" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="log" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="machineName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="maxKilobytes" minOccurs="0" maxOccurs="1" type="xs:long" />
+          <xs:element name="maxMessageLength" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="source" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.EventLogTargetOverflowAction" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="category" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout that renders event Category.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="entryType" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Optional entrytype. When not set, or when not convertible to  then determined by </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="eventId" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout that renders event ID.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="log" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the Event Log to write to. This can be System, Application or any user-defined name.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="machineName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the machine on which Event Log service is running.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxKilobytes" type="xs:long">
+          <xs:annotation>
+            <xs:documentation>Maximum Event log size in kilobytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxMessageLength" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Message length limit to write to the Event Log.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="source" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Value to be used as the event Source.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onOverflow" type="NLog.Targets.EventLogTargetOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action to take if the message is larger than the  option.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.EventLogTargetOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Truncate" />
+      <xs:enumeration value="Split" />
+      <xs:enumeration value="Discard" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="FallbackGroup">
+    <xs:complexContent>
+      <xs:extension base="CompoundTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="returnToFirstOnSuccess" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="returnToFirstOnSuccess" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to return to the first target after any successful write.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="File">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="lineEnding" minOccurs="0" maxOccurs="1" type="LineEndingMode" />
+          <xs:element name="enableArchiveFileCompression" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="archiveNumbering" minOccurs="0" maxOccurs="1" type="NLog.Targets.ArchiveNumberingMode" />
+          <xs:element name="archiveFileName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="archiveFileKind" minOccurs="0" maxOccurs="1" type="NLog.Targets.FilePathKind" />
+          <xs:element name="archiveEvery" minOccurs="0" maxOccurs="1" type="NLog.Targets.FileArchivePeriod" />
+          <xs:element name="archiveAboveSize" minOccurs="0" maxOccurs="1" type="xs:long" />
+          <xs:element name="maxArchiveFiles" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="writeFooterOnArchivingOnly" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxLogFilenames" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="fileNameKind" minOccurs="0" maxOccurs="1" type="NLog.Targets.FilePathKind" />
+          <xs:element name="forceManaged" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="forceMutexConcurrentWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="replaceFileContentsOnEachWrite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="writeBom" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="enableFileDelete" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="fileName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="archiveDateFormat" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="archiveOldFileOnStartup" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="cleanupFileName" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="createDirs" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="deleteOldFileOnStartup" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="fileAttributes" minOccurs="0" maxOccurs="1" type="NLog.Targets.Win32FileAttributes" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="networkWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="openFileCacheTimeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="openFileCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepFileOpen" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="discardAll" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="concurrentWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="concurrentWriteAttempts" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="concurrentWriteAttemptDelay" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="bufferSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="openFileFlushTimeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="autoFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>File encoding.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="lineEnding" type="LineEndingMode">
+          <xs:annotation>
+            <xs:documentation>Line ending mode.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="enableArchiveFileCompression" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to compress archive files into the zip archive format.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveNumbering" type="NLog.Targets.ArchiveNumberingMode">
+          <xs:annotation>
+            <xs:documentation>Way file archives are numbered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveFileName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Name of the file to be used for an archive.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveFileKind" type="NLog.Targets.FilePathKind">
+          <xs:annotation>
+            <xs:documentation>Is the  an absolute or relative path?</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveEvery" type="NLog.Targets.FileArchivePeriod">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to automatically archive log files every time the specified time passes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveAboveSize" type="xs:long">
+          <xs:annotation>
+            <xs:documentation>Size in bytes above which log files will be automatically archived. Warning: combining this with  isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxArchiveFiles" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum number of archive files that should be kept.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="writeFooterOnArchivingOnly" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether the footer should be written only when the file is archived.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxLogFilenames" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum number of log filenames that should be stored as existing.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="fileNameKind" type="NLog.Targets.FilePathKind">
+          <xs:annotation>
+            <xs:documentation>Is the  an absolute or relative path?</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="forceManaged" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="forceMutexConcurrentWrites" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether file creation calls should be synchronized by a system global mutex.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="replaceFileContentsOnEachWrite" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to replace file contents on each write instead of appending log message at the end.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="writeBom" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to write BOM (byte order mark) in created files</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="enableFileDelete" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to enable log file(s) to be deleted.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="fileName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Name of the file to write to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveDateFormat" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Value specifying the date format to use when archiving files.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveOldFileOnStartup" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to archive old log file on startup.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="cleanupFileName" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="createDirs" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to create directories if they do not exist.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="deleteOldFileOnStartup" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to delete old log file on startup.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="fileAttributes" type="NLog.Targets.Win32FileAttributes">
+          <xs:annotation>
+            <xs:documentation>File attributes (Windows only).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="networkWrites" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether concurrent writes to the log file by multiple processes on different network hosts.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="openFileCacheTimeout" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="openFileCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepFileOpen" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep log file open instead of opening and closing it on each logging event.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="discardAll" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="concurrentWrites" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether concurrent writes to the log file by multiple processes on the same host.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="concurrentWriteAttempts" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of times the write is appended on the file before NLog discards the log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="concurrentWriteAttemptDelay" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Delay in milliseconds to wait before attempting to write to the file again.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="bufferSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Log file buffer size in bytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="openFileFlushTimeout" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum number of seconds before open files are flushed. If this number is negative or zero the files are not flushed by timer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="autoFlush" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to automatically flush the file buffers after each log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.ArchiveNumberingMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Sequence" />
+      <xs:enumeration value="Rolling" />
+      <xs:enumeration value="Date" />
+      <xs:enumeration value="DateAndSequence" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.FilePathKind">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Unknown" />
+      <xs:enumeration value="Relative" />
+      <xs:enumeration value="Absolute" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.FileArchivePeriod">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="None" />
+      <xs:enumeration value="Year" />
+      <xs:enumeration value="Month" />
+      <xs:enumeration value="Day" />
+      <xs:enumeration value="Hour" />
+      <xs:enumeration value="Minute" />
+      <xs:enumeration value="Sunday" />
+      <xs:enumeration value="Monday" />
+      <xs:enumeration value="Tuesday" />
+      <xs:enumeration value="Wednesday" />
+      <xs:enumeration value="Thursday" />
+      <xs:enumeration value="Friday" />
+      <xs:enumeration value="Saturday" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.Win32FileAttributes">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="ReadOnly" />
+      <xs:enumeration value="Hidden" />
+      <xs:enumeration value="System" />
+      <xs:enumeration value="Archive" />
+      <xs:enumeration value="Device" />
+      <xs:enumeration value="Normal" />
+      <xs:enumeration value="Temporary" />
+      <xs:enumeration value="SparseFile" />
+      <xs:enumeration value="ReparsePoint" />
+      <xs:enumeration value="Compressed" />
+      <xs:enumeration value="NotContentIndexed" />
+      <xs:enumeration value="Encrypted" />
+      <xs:enumeration value="WriteThrough" />
+      <xs:enumeration value="NoBuffering" />
+      <xs:enumeration value="DeleteOnClose" />
+      <xs:enumeration value="PosixSemantics" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="FilteringWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
+          <xs:element name="filter" minOccurs="0" maxOccurs="1" type="Filter" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="condition" type="Condition">
+          <xs:annotation>
+            <xs:documentation>Condition expression. Log events who meet this condition will be forwarded to the wrapped target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="ImpersonatingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="domain" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="impersonationLevel" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.SecurityImpersonationLevel" />
+          <xs:element name="logOnProvider" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.LogOnProviderType" />
+          <xs:element name="logOnType" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.SecurityLogOnType" />
+          <xs:element name="password" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="revertToSelf" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="userName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="domain" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Windows domain name to change context to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="impersonationLevel" type="NLog.Targets.Wrappers.SecurityImpersonationLevel">
+          <xs:annotation>
+            <xs:documentation>Required impersonation level.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="logOnProvider" type="NLog.Targets.Wrappers.LogOnProviderType">
+          <xs:annotation>
+            <xs:documentation>Type of the logon provider.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="logOnType" type="NLog.Targets.Wrappers.SecurityLogOnType">
+          <xs:annotation>
+            <xs:documentation>Logon Type.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="password" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>User account password.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="revertToSelf" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to revert to the credentials of the process instead of impersonating another user.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="userName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Username to change context to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.Wrappers.SecurityImpersonationLevel">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Anonymous" />
+      <xs:enumeration value="Identification" />
+      <xs:enumeration value="Impersonation" />
+      <xs:enumeration value="Delegation" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.Wrappers.LogOnProviderType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Default" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.Wrappers.SecurityLogOnType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Interactive" />
+      <xs:enumeration value="Network" />
+      <xs:enumeration value="Batch" />
+      <xs:enumeration value="Service" />
+      <xs:enumeration value="NetworkClearText" />
+      <xs:enumeration value="NewCredentials" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="LimitingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="interval" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="messageLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="interval" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Interval in which messages will be written up to the  number of messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="messageLimit" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum allowed number of messages written per .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="LogReceiverService">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="endpointAddress" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="endpointConfigurationName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="useOneWayContract" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="clientId" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="includeEventProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.MethodCallParameter" />
+          <xs:element name="useBinaryEncoding" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="endpointAddress" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Endpoint address.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="endpointConfigurationName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the endpoint configuration in WCF configuration file.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useOneWayContract" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="clientId" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Client ID.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeEventProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include per-event properties in the payload sent to the server.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useBinaryEncoding" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use binary message encoding.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLog.Targets.MethodCallParameter">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="parameterType" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="type" minOccurs="0" maxOccurs="1" type="xs:string" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that should be use to calculate the value for the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Name of the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="parameterType" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Type of the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="type" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Type of the parameter. Obsolete alias for </xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="Mail">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="replaceNewlineWithBrTagInHtml" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="priority" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="bcc" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="cc" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="addNewLines" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="html" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="from" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="body" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="subject" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="to" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="timeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="smtpServer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="smtpAuthentication" minOccurs="0" maxOccurs="1" type="NLog.Targets.SmtpAuthenticationMode" />
+          <xs:element name="smtpUserName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="smtpPassword" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="enableSsl" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="smtpPort" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="useSystemNetMailSettings" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="pickupDirectoryLocation" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="deliveryMethod" minOccurs="0" maxOccurs="1" type="System.Net.Mail.SmtpDeliveryMethod" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="replaceNewlineWithBrTagInHtml" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether NewLine characters in the body should be replaced with  tags.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="priority" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Priority used for sending mails.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used for sending e-mail.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="bcc" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="cc" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="addNewLines" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to add new lines between log entries.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="html" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to send message as HTML instead of plain text.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="from" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Sender's email address (e.g. joe@domain.com).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="body" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Mail message body (repeated for each log message send in one mail).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="subject" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Mail subject.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="to" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="timeout" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Indicates the SMTP client timeout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpServer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>SMTP Server to be used for sending.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpAuthentication" type="NLog.Targets.SmtpAuthenticationMode">
+          <xs:annotation>
+            <xs:documentation>SMTP Authentication mode.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpUserName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic").</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpPassword" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic").</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="enableSsl" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpPort" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Port number that SMTP Server is listening on.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useSystemNetMailSettings" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether the default Settings from System.Net.MailSettings should be used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="pickupDirectoryLocation" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Folder where applications save mail messages to be processed by the local SMTP server.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="deliveryMethod" type="System.Net.Mail.SmtpDeliveryMethod">
+          <xs:annotation>
+            <xs:documentation>Specifies how outgoing email messages will be handled.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.SmtpAuthenticationMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="None" />
+      <xs:enumeration value="Basic" />
+      <xs:enumeration value="Ntlm" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="System.Net.Mail.SmtpDeliveryMethod">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Network" />
+      <xs:enumeration value="SpecifiedPickupDirectory" />
+      <xs:enumeration value="PickupDirectoryFromIis" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="Memory">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="maxLogsCount" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxLogsCount" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Max number of items to have in memory</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="MethodCall">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="className" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="methodName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.MethodCallParameter" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="className" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Class name.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="methodName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Network">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="lineEnding" minOccurs="0" maxOccurs="1" type="LineEndingMode" />
+          <xs:element name="maxMessageSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="newLine" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="connectionCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepConnection" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxConnections" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="onConnectionOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetConnectionsOverflowAction" />
+          <xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetOverflowAction" />
+          <xs:element name="sslProtocols" minOccurs="0" maxOccurs="1" type="System.Security.Authentication.SslProtocols" />
+          <xs:element name="keepAliveTimeSeconds" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="lineEnding" type="LineEndingMode">
+          <xs:annotation>
+            <xs:documentation>End of line value if a newline is appended at the end of log message .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxMessageSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum message size in bytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="newLine" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to append newline at the end of log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="address" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Network address.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Size of the connection cache (number of connections which are kept alive).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepConnection" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep connection open whenever possible.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxConnections" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum current connections. 0 = no maximum.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxQueueSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum queue size.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onConnectionOverflow" type="NLog.Targets.NetworkTargetConnectionsOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the will be more connections than .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onOverflow" type="NLog.Targets.NetworkTargetOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the message is larger than maxMessageSize.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="sslProtocols" type="System.Security.Authentication.SslProtocols">
+          <xs:annotation>
+            <xs:documentation>Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepAliveTimeSeconds" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>The number of seconds a connection will remain idle before the first keep-alive probe is sent</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLogViewer">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="lineEnding" minOccurs="0" maxOccurs="1" type="LineEndingMode" />
+          <xs:element name="maxMessageSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="newLine" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="connectionCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepConnection" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxConnections" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="onConnectionOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetConnectionsOverflowAction" />
+          <xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetOverflowAction" />
+          <xs:element name="sslProtocols" minOccurs="0" maxOccurs="1" type="System.Security.Authentication.SslProtocols" />
+          <xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepAliveTimeSeconds" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
+          <xs:element name="ndlcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="loggerName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="includeNLogData" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeNdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="appInfo" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="ndcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Instance of  that is used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="lineEnding" type="LineEndingMode">
+          <xs:annotation>
+            <xs:documentation>End of line value if a newline is appended at the end of log message .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxMessageSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum message size in bytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="newLine" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to append newline at the end of log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="address" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Network address.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Size of the connection cache (number of connections which are kept alive).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepConnection" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep connection open whenever possible.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxConnections" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum current connections. 0 = no maximum.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onConnectionOverflow" type="NLog.Targets.NetworkTargetConnectionsOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the will be more connections than .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onOverflow" type="NLog.Targets.NetworkTargetOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the message is larger than maxMessageSize.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="sslProtocols" type="System.Security.Authentication.SslProtocols">
+          <xs:annotation>
+            <xs:documentation>Get or set the SSL/TLS protocols. Default no SSL/TLS is used. Currently only implemented for TCP.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxQueueSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum queue size.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepAliveTimeSeconds" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>The number of seconds a connection will remain idle before the first keep-alive probe is sent</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ndlcItemSeparator" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>NDLC item separator.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeSourceInfo" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include source info (file name and line number) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="loggerName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Renderer for log4j:event logger-xml-attribute (Default ${logger})</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNLogData" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include NLog-specific extensions to log4j schema.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  stack.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  stack contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  dictionary contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  dictionary contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeCallSite" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include call site (class and method name) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeAllProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to include all properties from the log events</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="appInfo" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>AppInfo field. By default it's the friendly name of the current AppDomain.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ndcItemSeparator" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>NDC item separator.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Null">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="formatMessage" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="formatMessage" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to perform layout calculation.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="OutputDebugString">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="PerfCounter">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="autoCreate" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="categoryName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="counterHelp" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="counterName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="counterType" minOccurs="0" maxOccurs="1" type="System.Diagnostics.PerformanceCounterType" />
+          <xs:element name="incrementValue" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="instanceName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="autoCreate" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether performance counter should be automatically created.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="categoryName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the performance counter category.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="counterHelp" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Counter help text.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="counterName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the performance counter.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="counterType" type="System.Diagnostics.PerformanceCounterType">
+          <xs:annotation>
+            <xs:documentation>Performance counter type.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="incrementValue" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>The value by which to increment the counter.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="instanceName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Performance counter instance name.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="System.Diagnostics.PerformanceCounterType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="NumberOfItems32" />
+      <xs:enumeration value="NumberOfItems64" />
+      <xs:enumeration value="NumberOfItemsHEX32" />
+      <xs:enumeration value="NumberOfItemsHEX64" />
+      <xs:enumeration value="RateOfCountsPerSecond32" />
+      <xs:enumeration value="RateOfCountsPerSecond64" />
+      <xs:enumeration value="CountPerTimeInterval32" />
+      <xs:enumeration value="CountPerTimeInterval64" />
+      <xs:enumeration value="RawFraction" />
+      <xs:enumeration value="RawBase" />
+      <xs:enumeration value="AverageTimer32" />
+      <xs:enumeration value="AverageBase" />
+      <xs:enumeration value="AverageCount64" />
+      <xs:enumeration value="SampleFraction" />
+      <xs:enumeration value="SampleCounter" />
+      <xs:enumeration value="SampleBase" />
+      <xs:enumeration value="CounterTimer" />
+      <xs:enumeration value="CounterTimerInverse" />
+      <xs:enumeration value="Timer100Ns" />
+      <xs:enumeration value="Timer100NsInverse" />
+      <xs:enumeration value="ElapsedTime" />
+      <xs:enumeration value="CounterMultiTimer" />
+      <xs:enumeration value="CounterMultiTimerInverse" />
+      <xs:enumeration value="CounterMultiTimer100Ns" />
+      <xs:enumeration value="CounterMultiTimer100NsInverse" />
+      <xs:enumeration value="CounterMultiBase" />
+      <xs:enumeration value="CounterDelta32" />
+      <xs:enumeration value="CounterDelta64" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="PostFilteringWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="defaultFilter" minOccurs="0" maxOccurs="1" type="Condition" />
+          <xs:element name="when" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.Wrappers.FilteringRule" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="defaultFilter" type="Condition">
+          <xs:annotation>
+            <xs:documentation>Default filter to be applied when no specific rule matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLog.Targets.Wrappers.FilteringRule">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="exists" minOccurs="0" maxOccurs="1" type="Condition" />
+      <xs:element name="filter" minOccurs="0" maxOccurs="1" type="Condition" />
+    </xs:choice>
+    <xs:attribute name="exists" type="Condition">
+      <xs:annotation>
+        <xs:documentation>Condition to be tested.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="filter" type="Condition">
+      <xs:annotation>
+        <xs:documentation>Resulting filter to be applied when the condition matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="RandomizeGroup">
+    <xs:complexContent>
+      <xs:extension base="CompoundTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="RepeatingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="repeatCount" minOccurs="0" maxOccurs="1" type="xs:integer" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="repeatCount" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of times to repeat each log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="RetryingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="retryCount" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="retryDelayMilliseconds" minOccurs="0" maxOccurs="1" type="xs:integer" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="retryCount" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of retries that should be attempted on the wrapped target in case of a failure.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="retryDelayMilliseconds" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Time to wait between retries in milliseconds.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="RoundRobinGroup">
+    <xs:complexContent>
+      <xs:extension base="CompoundTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="SplitGroup">
+    <xs:complexContent>
+      <xs:extension base="CompoundTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Trace">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="rawWrite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="rawWrite" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Always use  independent of </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="WebService">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.MethodCallParameter" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeBOM" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="methodName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="namespace" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="protocol" minOccurs="0" maxOccurs="1" type="NLog.Targets.WebServiceProtocol" />
+          <xs:element name="proxyAddress" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="url" minOccurs="0" maxOccurs="1" type="xs:anyURI" />
+          <xs:element name="escapeDataNLogLegacy" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="escapeDataRfc3986" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="preAuthenticate" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="xmlRoot" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="xmlRootNamespace" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="header" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.MethodCallParameter" />
+          <xs:element name="proxyType" minOccurs="0" maxOccurs="1" type="NLog.Targets.WebServiceProxyType" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeBOM" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Should we include the BOM (Byte-order-mark) for UTF? Influences the  property. This will only work for UTF-8.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="methodName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Web service method name. Only used with Soap.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="namespace" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Web service namespace. Only used with Soap.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="protocol" type="NLog.Targets.WebServiceProtocol">
+          <xs:annotation>
+            <xs:documentation>Protocol to be used when calling web service.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="proxyAddress" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Custom proxy address, include port separated by a colon</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="url" type="xs:anyURI">
+          <xs:annotation>
+            <xs:documentation>Web service URL.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="escapeDataNLogLegacy" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Value whether escaping be done according to the old NLog style (Very non-standard)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="escapeDataRfc3986" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="preAuthenticate" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to pre-authenticate the HttpWebRequest (Requires 'Authorization' in  parameters)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="xmlRoot" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see  and ).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="xmlRootNamespace" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>(optional) root namespace of the XML document, if POST of XML document chosen. (see  and ).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="proxyType" type="NLog.Targets.WebServiceProxyType">
+          <xs:annotation>
+            <xs:documentation>Proxy configuration when calling web service</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.WebServiceProtocol">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Soap11" />
+      <xs:enumeration value="Soap12" />
+      <xs:enumeration value="HttpPost" />
+      <xs:enumeration value="HttpGet" />
+      <xs:enumeration value="JsonPost" />
+      <xs:enumeration value="XmlPost" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.WebServiceProxyType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="DefaultWebProxy" />
+      <xs:enumeration value="AutoProxy" />
+      <xs:enumeration value="NoProxy" />
+      <xs:enumeration value="ProxyAddress" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="CompoundLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="layout" minOccurs="0" maxOccurs="unbounded" type="Layout" />
+        </xs:choice>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Layout">
+    <xs:choice minOccurs="0" maxOccurs="unbounded" />
+  </xs:complexType>
+  <xs:complexType name="CsvLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="column" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.CsvColumn" />
+          <xs:element name="customColumnDelimiter" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="delimiter" minOccurs="0" maxOccurs="1" type="NLog.Layouts.CsvColumnDelimiterMode" />
+          <xs:element name="quoteChar" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="quoting" minOccurs="0" maxOccurs="1" type="NLog.Layouts.CsvQuotingMode" />
+          <xs:element name="withHeader" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer layout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header layout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Body layout (can be repeated multiple times).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="customColumnDelimiter" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom').</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="delimiter" type="NLog.Layouts.CsvColumnDelimiterMode">
+          <xs:annotation>
+            <xs:documentation>Column delimiter.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="quoteChar" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Quote Character.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="quoting" type="NLog.Layouts.CsvQuotingMode">
+          <xs:annotation>
+            <xs:documentation>Quoting mode.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="withHeader" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether CVS should include header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Layouts.CsvQuotingMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="All" />
+      <xs:enumeration value="Nothing" />
+      <xs:enumeration value="Auto" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Layouts.CsvColumnDelimiterMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Auto" />
+      <xs:enumeration value="Comma" />
+      <xs:enumeration value="Semicolon" />
+      <xs:enumeration value="Tab" />
+      <xs:enumeration value="Pipe" />
+      <xs:enumeration value="Space" />
+      <xs:enumeration value="Custom" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLog.Layouts.CsvColumn">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="quoting" minOccurs="0" maxOccurs="1" type="NLog.Layouts.CsvQuotingMode" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout of the column.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Name of the column.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="quoting" type="NLog.Layouts.CsvQuotingMode">
+      <xs:annotation>
+        <xs:documentation>Override of Quoting mode</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="JsonLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="attribute" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.JsonAttribute" />
+          <xs:element name="excludeProperties" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeGdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxRecursionLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="renderEmptyObject" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="suppressSpaces" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="excludeProperties" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>List of property names to exclude when  is true</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeAllProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to include all properties from the log event (as JSON)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeGdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxRecursionLimit" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>How far should the JSON serializer follow object references before backing off</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="renderEmptyObject" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to render the empty object value {}</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="suppressSpaces" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to suppress the extra spaces in the output json</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLog.Layouts.JsonAttribute">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="encode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="escapeUnicode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="includeEmptyValue" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that will be rendered as the attribute's value.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Name of the attribute.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="encode" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Determines whether or not this attribute will be Json encoded.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="escapeUnicode" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to escape non-ascii characters</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="includeEmptyValue" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Whether an attribute with empty value should be included in the output</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="LayoutWithHeaderAndFooter">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+        </xs:choice>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer layout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header layout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Body layout (can be repeated multiple times).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Log4JXmlEventLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeNdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
+        </xs:choice>
+        <xs:attribute name="includeAllProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to include all properties from the log events</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeCallSite" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include call site (class and method name) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  stack.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  stack.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeSourceInfo" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include source info (file name and line number) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="SimpleLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="text" minOccurs="0" maxOccurs="1" type="xs:string" />
+        </xs:choice>
+        <xs:attribute name="text" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Layout text.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="XmlLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="excludeProperties" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxRecursionLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="propertiesCollectionItemName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="propertiesElementKeyAttribute" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="propertiesElementName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="propertiesElementValueAttribute" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="attribute" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.XmlAttribute" />
+          <xs:element name="elementName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="element" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.XmlElement" />
+          <xs:element name="elementValue" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="includeEmptyValue" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="indentXml" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="elementEncode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="excludeProperties" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>List of property names to exclude when  is true</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeAllProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to include all properties from the log event (as XML)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxRecursionLimit" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>How far should the XML serializer follow object references before backing off</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="propertiesCollectionItemName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>XML element name to use for rendering IList-collections items</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="propertiesElementKeyAttribute" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="propertiesElementName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>XML element name to use when rendering properties</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="propertiesElementValueAttribute" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="elementName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the root XML element</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="elementValue" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Value inside the root XML element</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeEmptyValue" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Whether a ElementValue with empty value should be included in the output</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="indentXml" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Auto indent and create new lines</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="elementEncode" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Determines whether or not this attribute will be Xml encoded.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLog.Layouts.XmlAttribute">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="encode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="includeEmptyValue" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that will be rendered as the attribute's value.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Name of the attribute.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="encode" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Determines whether or not this attribute will be Xml encoded.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="includeEmptyValue" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Whether an attribute with empty value should be included in the output</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLog.Layouts.XmlElement">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="encode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="value" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="attribute" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.XmlAttribute" />
+      <xs:element name="element" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.XmlElement" />
+      <xs:element name="includeEmptyValue" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="indentXml" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="excludeProperties" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="maxRecursionLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+      <xs:element name="propertiesCollectionItemName" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="propertiesElementKeyAttribute" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="propertiesElementName" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="propertiesElementValueAttribute" minOccurs="0" maxOccurs="1" type="xs:string" />
+    </xs:choice>
+    <xs:attribute name="encode" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Determines whether or not this attribute will be Xml encoded.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Name of the element</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="value" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Value inside the element</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="includeEmptyValue" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Whether a ElementValue with empty value should be included in the output</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="indentXml" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Auto indent and create new lines</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="excludeProperties" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>List of property names to exclude when  is true</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="includeAllProperties" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Option to include all properties from the log event (as XML)</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="includeMdc" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="includeMdlc" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="maxRecursionLimit" type="xs:integer">
+      <xs:annotation>
+        <xs:documentation>How far should the XML serializer follow object references before backing off</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="propertiesCollectionItemName" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>XML element name to use for rendering IList-collections items</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="propertiesElementKeyAttribute" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>XML attribute name to use when rendering property-key When null (or empty) then key-attribute is not included</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="propertiesElementName" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>XML element name to use when rendering properties</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="propertiesElementValueAttribute" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>XML attribute name to use when rendering property-value When null (or empty) then value-attribute is not included and value is formatted as XML-element-value</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="when">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="condition" type="Condition">
+          <xs:annotation>
+            <xs:documentation>Condition expression.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="FilterResult">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Neutral" />
+      <xs:enumeration value="Log" />
+      <xs:enumeration value="Ignore" />
+      <xs:enumeration value="LogFinal" />
+      <xs:enumeration value="IgnoreFinal" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="whenContains">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="substring" minOccurs="0" maxOccurs="1" type="xs:string" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ignoreCase" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to ignore case when comparing strings.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="substring" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Substring to be matched.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="whenEqual">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="compareTo" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="compareTo" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>String to compare the layout to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ignoreCase" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to ignore case when comparing strings.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="whenNotContains">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="substring" minOccurs="0" maxOccurs="1" type="xs:string" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ignoreCase" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to ignore case when comparing strings.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="substring" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Substring to be matched.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="whenNotEqual">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="compareTo" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="compareTo" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>String to compare the layout to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ignoreCase" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to ignore case when comparing strings.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="whenRepeated">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="defaultFilterCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="includeFirst" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="maxFilterCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="maxLength" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="timeoutSeconds" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferDefaultLength" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="filterCountMessageAppendFormat" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="filterCountPropertyName" minOccurs="0" maxOccurs="1" type="xs:string" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="defaultFilterCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Default number of unique filter values to expect, will automatically increase if needed</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeFirst" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Applies the configured action to the initial logevent that starts the timeout period. Used to configure that it should ignore all events until timeout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxFilterCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Max number of unique filter values to expect simultaneously</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxLength" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Max length of filter values, will truncate if above limit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="timeoutSeconds" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>How long before a filter expires, and logging is accepted again</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferDefaultLength" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Default buffer size for the internal buffers</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Reuse internal buffers, and doesn't have to constantly allocate new buffers</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="filterCountMessageAppendFormat" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Append FilterCount to the  when an event is no longer filtered</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="filterCountPropertyName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Insert FilterCount value into  when an event is no longer filtered</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="AccurateLocal">
+    <xs:complexContent>
+      <xs:extension base="TimeSource">
+        <xs:choice minOccurs="0" maxOccurs="unbounded" />
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="AccurateUTC">
+    <xs:complexContent>
+      <xs:extension base="TimeSource">
+        <xs:choice minOccurs="0" maxOccurs="unbounded" />
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="FastLocal">
+    <xs:complexContent>
+      <xs:extension base="TimeSource">
+        <xs:choice minOccurs="0" maxOccurs="unbounded" />
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="FastUTC">
+    <xs:complexContent>
+      <xs:extension base="TimeSource">
+        <xs:choice minOccurs="0" maxOccurs="unbounded" />
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+</xs:schema>

+ 18 - 0
EVCB_OCPP.WSServer/Program.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            ProtalServer s = new ProtalServer();
+            Console.WriteLine("Starting Server...");
+            s.Start();
+        }
+    }
+}

+ 36 - 0
EVCB_OCPP.WSServer/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 組件的一般資訊是由下列的屬性集控制。
+// 變更這些屬性的值即可修改組件的相關
+// 資訊。
+[assembly: AssemblyTitle("EVCB_OCPP.WSServer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("EVCB_OCPP.WSServer")]
+[assembly: AssemblyCopyright("Copyright ©  2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 將 ComVisible 設為 false 可對 COM 元件隱藏
+// 組件中的類型。若必須從 COM 存取此組件中的類型,
+// 的類型,請在該類型上將 ComVisible 屬性設定為 true。
+[assembly: ComVisible(false)]
+
+// 下列 GUID 為專案公開 (Expose) 至 COM 時所要使用的 typelib ID
+[assembly: Guid("de0c1e9a-1eee-42cc-8a91-73bf9056a7e7")]
+
+// 組件的版本資訊由下列四個值所組成: 
+//
+//      主要版本
+//      次要版本
+//      組建編號
+//      修訂編號
+//
+// 您可以指定所有的值,或將組建編號或修訂編號設為預設值
+// 指定為預設值: 
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 762 - 0
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -0,0 +1,762 @@
+using NLog;
+using OCPPServer.Protocol;
+using OCPPServer.SubProtocol;
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Config;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+using EVCB_OCPP.Packet.Messages.Basic;
+using EVCB_OCPP.Packet.Messages.Core;
+using EVCB_OCPP.WSServer.Message;
+using EVCB_OCPP.Packet.Messages;
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.Domain.Models.Database;
+using System.Threading;
+using System.Data.Entity;
+using EVCB_OCPP.Packet.Features;
+using EVCB_OCPP.Packet.Features.Core;
+using Newtonsoft.Json;
+using OCPPPackage.Profiles;
+using System.Threading.Tasks;
+using EVCB_OCPP.WSServer.Helper;
+using System.Data.SqlClient;
+
+namespace EVCB_OCPP.WSServer
+{
+    internal class ProtalServer
+    {
+
+        internal Dictionary<string, ClientData> ClientDic = new Dictionary<string, ClientData>();
+        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        private readonly Object _lockClientDic = new object();
+        private readonly Object _lockConfirmPacketList = new object();
+        ProfileHandler _profileHandler = new ProfileHandler();
+        private List<NeedConfirmMessage> NeedConfirmPacketList = new List<NeedConfirmMessage>();
+        private List<string> _needConfirmActions = new List<string>()
+        {
+            "GetConfiguration",
+            "ChangeConfiguration",
+            "RemoteStartTransaction",
+            "RemoteStopTransaction",
+            "ChangeAvailability",
+            "ClearCache",
+            "DataTransfer",
+            "Reset",
+             "UnlockConnector",
+
+
+        };
+        private List<Profile> profiles = new List<Profile>()
+        {
+             new CoreProfile(),
+             new FirmwareManagementProfile(),
+             new ReservationProfile(),
+             new RemoteTriggerProfile(),
+             new SmartChargingProfile(),
+             new LocalAuthListManagementProfile()
+        };
+        private CancellationTokenSource _cts = new CancellationTokenSource();
+        private CancellationToken _ct;
+
+        internal ProtalServer()
+        {
+            _ct = _cts.Token;
+
+        }
+
+
+        internal void Start()
+        {
+            OpenNetwork();
+
+
+            Task taskServerCommand = new Task(ServerMessageTrigger, _ct);
+            taskServerCommand.Start();
+
+            while (true)
+            {
+                var input = Console.ReadLine();
+
+                switch (input.ToLower())
+                {
+                    case "stop":
+
+                        break;
+
+                    case "gc":
+                        logger.Info("Command GC");
+                        GC.Collect();
+                        break;
+
+                    case "lc":
+                        logger.Info("Command List Clients");
+
+                        break;
+
+                    case "lcn":
+                        logger.Info("Command List Customer Name");
+
+                        break;
+
+                    case "help":
+                        logger.Info("Command help!!");
+                        logger.Info("lcn : List Customer Name");
+                        logger.Info("gc : GC Collect");
+                        logger.Info("lc : List Clients");
+                        logger.Info("cls : clear console");
+                        logger.Info("silent : silent");
+                        logger.Info("show : show log");
+                        logger.Info("rcl : show Real Connection Limit");
+                        break;
+
+                    case "cls":
+                        logger.Info("Command clear");
+                        Console.Clear();
+                        break;
+
+                    case "silent":
+                        logger.Info("Command silent");
+                        var xe = XElement.Load("NLog.config");
+                        var xns = xe.GetDefaultNamespace();
+                        var minlevelattr = xe.Descendants(xns + "rules").Elements(xns + "logger")
+                            .Where(c => c.Attribute("writeTo").Value.Equals("console")).Attributes("minlevel").FirstOrDefault();
+                        if (minlevelattr != null)
+                        {
+                            minlevelattr.Value = "info";
+                        }
+                        xe.Save("NLog.config");
+                        break;
+
+                    case "show":
+                        logger.Info("Command show");
+                        var xe1 = XElement.Load("NLog.config");
+                        var xns1 = xe1.GetDefaultNamespace();
+                        var minlevelattr1 = xe1.Descendants(xns1 + "rules").Elements(xns1 + "logger")
+                            .Where(c => c.Attribute("writeTo").Value.Equals("console")).Attributes("minlevel").FirstOrDefault();
+                        if (minlevelattr1 != null)
+                        {
+                            minlevelattr1.Value = "trace";
+                        }
+                        xe1.Save("NLog.config");
+                        break;
+
+                    case "rcl":
+
+
+                        break;
+
+                    default:
+                        break;
+                }
+            }
+        }
+
+        private void InitEVSEConfigure(string sn)
+        {
+            List<MachineConfigure> configures = new List<MachineConfigure>();
+            configures.Add(new MachineConfigure() { ConfigureName = StandardConfiguration.AuthorizeRemoteTxRequests, ConfigureSetting = "" });
+            using (var db = new MainDBContext())
+            {
+                var _sn = db.MachineConfigure.Where(x => x.ChargeBoxId == sn).Select(x => x.ChargeBoxId).FirstOrDefault();
+                if (string.IsNullOrEmpty(_sn))
+                {
+                    db.MachineConfigure.Add(new MachineConfigure()
+                    {
+
+                    });
+                }
+            }
+        }
+
+
+        private void OpenNetwork()
+        {
+            //載入OCPP Protocol
+            var appServer = new OCPPWSServer(new OCPPSubProtocol());
+
+            List<IListenerConfig> llistener = new List<IListenerConfig>();
+
+            // llistener.Add(new ListenerConfig { Ip = "", Port = Convert.ToInt32(wssserverPort), Backlog = 100, Security = serverSecurity });
+            llistener.Add(new ListenerConfig { Ip = "172.17.40.13", Port = Convert.ToInt32(2012), Backlog = 100, Security = "None" });
+
+            IEnumerable<IListenerConfig> listeners = llistener;
+
+            //設定server config
+            var serverConfig = new ServerConfig
+            {
+                //Port = Convert.ToInt32(2012),
+                //Ip = "172.17.40.13",
+                MaxRequestLength = 4096,
+                //Security = serverSecurity,
+                // Certificate = Certificate,
+                Listeners = listeners
+
+            };
+
+            //Setup with listening port
+            if (!appServer.Setup(serverConfig, logFactory: new OCPPLogFactory()))
+            {
+                Console.WriteLine("Failed to setup!");
+                return;
+            }
+
+            appServer.NewSessionConnected += AppServer_NewSessionConnected;
+            appServer.SessionClosed += AppServer_SessionClosed;
+
+            //Try to start the appServer
+            if (!appServer.Start())
+            {
+                Console.WriteLine("Failed to start!");
+                Console.ReadKey();
+                return;
+            }
+        }
+
+        private void AppServer_SessionClosed(ClientData session, CloseReason value)
+        {
+            WriteMachineLog(session.ChargeBoxId, string.Format("CloseReason: {0}", value), "Connection", "");
+            RemoveClient(session);
+            // close Connection
+        }
+
+        private void AppServer_NewSessionConnected(ClientData session)
+        {
+            session.m_ReceiveData += new ClientData.OCPPClientDataEventHandler<ClientData, String>(ReceivedMessage);
+            try
+            {
+                lock (_lockClientDic)
+                {
+                    ClientData _removeClient = null;
+
+                    ClientDic.TryGetValue(session.ChargeBoxId, out _removeClient);
+                    if (_removeClient != null)
+                    {
+                        RemoveClient(_removeClient);
+                    }
+                    ClientDic.Add(session.ChargeBoxId, session);
+                    WriteMachineLog(session.ChargeBoxId, "", "Connection", "");
+                }
+            }
+            catch (Exception ex)
+            {
+
+            }
+
+
+        }
+
+        private void ReceivedMessage(ClientData session, string rawdata)
+        {
+
+            BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+            MessageResult analysisResult = msgAnalyser.AnalysisReceiveData(session, rawdata);
+
+
+            WriteMachineLog(session.ChargeBoxId, rawdata,
+                string.Format("{0} {1}", analysisResult.Action, analysisResult.Id == 2 ? "Request" : (analysisResult.Id == 3 ? "Confirmation" : "Error")));
+
+            if (analysisResult.Success == false)
+            {
+                //解析RawData就發生錯誤
+                if (!string.IsNullOrEmpty(analysisResult.CallErrorMsg))
+                {
+                    Send(session, rawdata, string.Format("{0} {1}", analysisResult.Action, "Error"));
+                }
+                else
+                {
+                    BaseMessage _baseMsg = analysisResult.Message as BaseMessage;
+                    string replyMsg = msgAnalyser.GenerateCallError(_baseMsg.Id, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError);
+                    string errorMsg = string.Empty;
+                    if (analysisResult.Exception != null)
+                    {
+                        errorMsg = analysisResult.Exception.ToString();
+                    }
+
+                    Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg);
+                }
+            }
+            else
+            {
+
+                Actions action = Convertor.GetAction(analysisResult.Action);
+                switch (analysisResult.Id)
+                {
+                    case BasicMessageHandler.TYPENUMBER_CALL:
+                        {
+                            ProcessRequestMessage(analysisResult, session, action);
+
+                        }
+                        break;
+                    case BasicMessageHandler.TYPENUMBER_CALLRESULT:
+                        {
+
+                            ProcessConfirmationMessage(analysisResult, session, action);
+                        }
+                        break;
+                    case BasicMessageHandler.TYPENUMBER_CALLERROR:
+                        {
+                            //只處理 丟出Request 收到Error的訊息
+                            if (analysisResult.Success)
+                            {
+                                ProcessErrorMessage(analysisResult, session, action);
+                            }
+
+                        }
+                        break;
+                    default:
+                        {
+                            logger.Error(string.Format("Can't analyze messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
+                        }
+                        break;
+
+                }
+            }
+
+
+        }
+
+        private void ProcessRequestMessage(MessageResult analysisResult, ClientData session, Actions action)
+        {
+            BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+            if (!session.IsCheckIn && action != Actions.BootNotification)
+            {
+                string response = msgAnalyser.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.GenericError, OCPPErrorDescription.NotChecked);
+                Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Error"));
+            }
+            else
+            {
+                var profileName = profiles.Where(x => x.IsExisted(analysisResult.Action)).Select(x => x.Name).FirstOrDefault();
+                switch (profileName)
+                {
+                    case "Core":
+                        {
+                            if (!session.IsCheckIn && action == Actions.BootNotification)
+                            {
+
+                                var requestId = Guid.NewGuid();
+                                //測試命令
+                                try
+                                {
+
+
+                                    //using (var db = new MainDBContext())
+                                    //{
+                                    //    //db.MachineOperateRecord.Add(new MachineOperateRecord()
+                                    //    //{
+                                    //    //    CreatedOn = DateTime.Now,
+                                    //    //    ChargePointSerialNumber = session.ChargePointSerialNumber,
+                                    //    //    SerialNo = requestId.ToString(),
+                                    //    //    RequestContent = JsonConvert.SerializeObject(new GetConfigurationRequest() { key = new List<string>() { StandardConfiguration.HeartbeatInterval } }),
+                                    //    //    EVSE_Status = 0,
+                                    //    //    Status = 0,
+                                    //    //    RequestType = 1,
+
+
+                                    //    //});
+                                    //    //core 26 項 設定
+                                    //    GetConfigurationRequest core_getConfigReq = new GetConfigurationRequest()
+                                    //    {
+
+                                    //        key = new List<string>()
+                                    //     {
+                                    //           StandardConfiguration.AuthorizeRemoteTxRequests,
+                                    //           StandardConfiguration.ClockAlignedDataInterval,
+                                    //           StandardConfiguration.ConnectionTimeOut,
+                                    //           //StandardConfiguration.HeartbeatInterval,
+                                    //           //StandardConfiguration.HeartbeatInterval,
+                                    //           //StandardConfiguration.LocalAuthorizeOffline,
+                                    //           //StandardConfiguration.LocalPreAuthorize,
+                                    //           //StandardConfiguration.MeterValuesAlignedData,
+                                    //           //StandardConfiguration.MeterValuesAlignedDataMaxLength,
+                                    //           //StandardConfiguration.MeterValuesSampledData,
+                                    //           //StandardConfiguration.MeterValuesSampledDataMaxLength,
+                                    //           //StandardConfiguration.MeterValueSampleInterval,
+                                    //           //StandardConfiguration.NumberOfConnectors,
+                                    //           //StandardConfiguration.ResetRetries,
+                                    //           //StandardConfiguration.ConnectorPhaseRotation,
+                                    //           //StandardConfiguration.ConnectorPhaseRotationMaxLength,
+                                    //           //StandardConfiguration.StopTransactionOnEVSideDisconnect,
+                                    //           //StandardConfiguration.StopTransactionOnInvalidId,
+                                    //           //StandardConfiguration.StopTxnAlignedData,
+                                    //           //StandardConfiguration.StopTxnAlignedDataMaxLength,
+                                    //           //StandardConfiguration.StopTxnSampledData,
+                                    //           //StandardConfiguration.StopTxnSampledDataMaxLength,
+                                    //           //StandardConfiguration.SupportedFeatureProfiles,
+                                    //           //StandardConfiguration.SupportedFeatureProfilesMaxLength,
+                                    //           //StandardConfiguration.TransactionMessageAttempts,
+                                    //           //StandardConfiguration.TransactionMessageRetryInterval,
+                                    //           //StandardConfiguration.UnlockConnectorOnEVSideDisconnect,
+                                    //           //StandardConfiguration.WebSocketPingInterval
+
+                                    //     }
+                                    //    };
+                                    //    db.ServerMessage.Add(new ServerMessage()
+                                    //    {
+                                    //        ChargeBoxId = session.ChargeBoxId,
+                                    //        CreatedBy = "Server",
+                                    //        CreatedOn = DateTime.Now,
+                                    //        OutAction = Actions.GetConfiguration.ToString(),
+                                    //        OutRequest = JsonConvert.SerializeObject(core_getConfigReq),
+                                    //        SerialNo = Guid.Empty.ToString(),
+                                    //        InMessage = string.Empty
+
+                                    //    });
+
+                                    //    db.SaveChanges();
+                                    //}
+                                }
+                                catch (Exception ex)
+                                {
+
+                                }
+
+                            }
+                            var replyResult = _profileHandler.ExecuteCoreRequest(action, session, (IRequest)analysisResult.Message);
+                            if (replyResult.Success)
+                            {
+                                string response = msgAnalyser.GenerateConfirmation(analysisResult.UUID, (IConfirmation)replyResult.Message);
+                                Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Confirmation"));
+                                if (action == Actions.BootNotification && replyResult.Message is BootNotificationConfirmation)
+                                {
+                                    if (((BootNotificationConfirmation)replyResult.Message).status == Packet.Messages.SubTypes.RegistrationStatus.Accepted)
+                                    {
+                                        session.IsCheckIn = true;
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                string response = msgAnalyser.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError);
+                                string errorMsg = replyResult.Exception != null ? replyResult.Exception.ToString() : string.Empty;
+
+                                Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg);
+                            }
+
+                        }
+                        break;
+                    default:
+                        {
+                            string replyMsg = msgAnalyser.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError);
+                            string errorMsg = string.Format("Couldn't find action name: {0} of profile", action);
+                            Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg);
+                        }
+                        break;
+
+                }
+            }
+        }
+
+        private void ProcessConfirmationMessage(MessageResult analysisResult, ClientData session, Actions action)
+        {
+            BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+            if (ConfirmMessage(analysisResult))
+            {
+                var profileName = profiles.Where(x => x.IsExisted(analysisResult.Action)).Select(x => x.Name).FirstOrDefault();
+                MessageResult confirmResult = null;
+                switch (profileName)
+                {
+                    case "Core":
+                        {
+                            confirmResult = _profileHandler.ExecuteCoreConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId);
+                        }
+                        break;
+                    default:
+                        {
+                            string replyMsg = msgAnalyser.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError);
+                            string errorMsg = string.Format("Couldn't find action name: {0} of profile", action);
+                            Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg);
+                        }
+                        break;
+
+                }
+
+                if (confirmResult == null || !confirmResult.Success)
+                {
+                    logger.Error(string.Format("Action:{0} MessageId:{1}  ExecuteConfirm Error:{2} ",
+                        analysisResult.Action, analysisResult.UUID, confirmResult.Exception.ToString()));
+                }
+            }
+            else
+            {
+                string replyMsg = msgAnalyser.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError);
+                string errorMsg = string.Format("Action:{0} MessageId:{1}  didn't exist in confirm message", analysisResult.Action, analysisResult.UUID);
+                Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg);
+            }
+        }
+
+        private void ProcessErrorMessage(MessageResult analysisResult, ClientData session, Actions action)
+        {
+            BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+            if (ConfirmMessage(analysisResult))
+            {
+                var profileName = profiles.Where(x => x.IsExisted(analysisResult.Action)).Select(x => x.Name).FirstOrDefault();
+                switch (profileName)
+                {
+                    case "Core":
+                        {
+                            _profileHandler.ReceivedCoreError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId);
+                        }
+                        break;
+                    default:
+                        {
+                            string replyMsg = msgAnalyser.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError);
+                            string errorMsg = string.Format("Couldn't find action name: {0} of profile", action);
+                            Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg);
+                        }
+                        break;
+
+                }
+            }
+            else
+            {
+                string replyMsg = msgAnalyser.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError);
+                string errorMsg = string.Format("Action:{0} MessageId:{1}  didn't exist in confirm message", analysisResult.Action, analysisResult.UUID);
+                Send(session, replyMsg, string.Format("{0} {1}", analysisResult.Action, "Error"), errorMsg);
+
+
+            }
+        }
+
+
+        private void Send(ClientData session, string msg, string messageType, string errorMsg = "")
+        {
+            try
+            {
+
+                if (session != null)
+                {
+                    WriteMachineLog(session.ChargeBoxId, msg, messageType, errorMsg);
+                    session.Send(msg);
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error(string.Format("Send Ex:{0}", ex.ToString()));
+            }
+
+
+        }
+
+        private void ServerMessageTrigger()
+        {
+            for (; ; )
+            {
+                if (_ct.IsCancellationRequested)
+                {
+                    break;
+                }
+
+                try
+                {
+                    RemoveConfirmMessage();
+
+                    BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+                    using (var db = new MainDBContext())
+                    {
+                        DateTime startDt = DateTime.Now.AddSeconds(-30);
+                        DateTime dt = new DateTime(1991, 1, 1);
+                        Console.WriteLine(string.Format("{0} IN", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
+                        var commandList = db.ServerMessage.Where(c => c.ReceivedOn == dt && c.CreatedOn >= startDt && c.CreatedOn <= DateTime.Now).AsNoTracking().ToList();
+                        //處理主機傳送的有指令
+                        var cmdMachineList = commandList.Select(c => c.ChargeBoxId).Distinct().ToList();
+                        foreach (var charger_SN in cmdMachineList)
+                        {
+                           
+                            ClientData session;
+                            string uuid = string.Empty;
+                            if (ClientDic.TryGetValue(charger_SN, out session))
+                            {
+                                Console.WriteLine(string.Format("charger_SN:{0} startDt:{1} CreatedOn:{2}", charger_SN, startDt.ToString("yyyy/MM/dd HH:mm:ss"), DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
+                                if (session.IsCheckIn)
+                                {
+                                    var cmdList = commandList.Where(c => c.ChargeBoxId == charger_SN).ToList();
+
+                                    foreach (var item in cmdList)
+                                    {
+                                        IRequest request = null;
+                                        Actions action = Actions.None;
+                                        Enum.TryParse(item.OutAction, out action);
+                                        Type _RequestType = null;
+
+                                        for (int i = 0; i < profiles.Count; i++)
+                                        {
+                                            var feature = profiles[i].GetFeaturebyAction(item.OutAction);
+
+                                            if (feature != null)
+                                            {
+                                                _RequestType = feature.GetRequestType();
+                                                break;
+                                            }
+                                        }
+
+                                        if (_RequestType != null)
+                                        {
+                                            request = JsonConvert.DeserializeObject(item.OutRequest, _RequestType) as IRequest;
+                                            uuid = session.queue.store(request);
+                                            string rawRequest = msgAnalyser.GenerateRequest(uuid, item.OutAction, request);
+                                            Send(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
+                                        }
+
+
+                                        AddConfirmMessage(charger_SN, item.Id, item.SerialNo, item.OutAction, uuid);
+
+
+
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    Task.Delay(2000);
+                }
+                catch (Exception ex)
+                {
+                    logger.Error("ServerMessageTrigger stop");
+                    logger.Error(ex);
+                }
+            }
+        }
+
+
+        private void AddConfirmMessage(string chargePointSerialNumber, int table_id, string requestId, string action, string msg_id)
+        {
+            NeedConfirmMessage _needConfirmMsg = new NeedConfirmMessage();
+            _needConfirmMsg.Id = table_id;
+            _needConfirmMsg.SentAction = action;
+            _needConfirmMsg.SentOn = DateTime.Now;
+            _needConfirmMsg.SentTimes = 1;
+            _needConfirmMsg.ChargePointSerialNumber = chargePointSerialNumber;
+            _needConfirmMsg.RequestId = requestId;
+            _needConfirmMsg.SentUniqueId = msg_id;
+
+            if (_needConfirmActions.Contains(action))
+            {
+
+                lock (_lockConfirmPacketList)
+                {
+                    NeedConfirmPacketList.Add(_needConfirmMsg);
+                }
+            }
+        }
+
+        private void RemoveConfirmMessage()
+        {
+
+            var before_3mins = DateTime.Now.AddMinutes(-3);
+            lock (_lockConfirmPacketList)
+            {
+                var removeList = NeedConfirmPacketList.Where(x => x.SentTimes == 0 || x.SentOn < before_3mins).ToList();
+                foreach (var item in removeList)
+                {
+                    NeedConfirmPacketList.Remove(item);
+                }
+            }
+        }
+
+        private bool ConfirmMessage(MessageResult analysisResult)
+        {
+            bool confirmed = false;
+            if (_needConfirmActions.Contains(analysisResult.Action))
+            {
+                NeedConfirmMessage foundRequest = null;
+                lock (_lockConfirmPacketList)
+                {
+                    foundRequest = NeedConfirmPacketList.Where(x => x.SentUniqueId == analysisResult.UUID).FirstOrDefault();
+                }
+
+                if (foundRequest.Id > 0)
+                {
+                    foundRequest.SentTimes = 0;
+                    foundRequest.SentInterval = 0;
+                    analysisResult.RequestId = foundRequest.RequestId;
+
+                    using (var db = new MainDBContext())
+                    {
+                        var sc = db.ServerMessage.Where(x => x.Id == foundRequest.Id).FirstOrDefault();
+                        sc.InMessage = JsonConvert.SerializeObject(analysisResult.Message, Formatting.Indented);
+                        sc.ReceivedOn = DateTime.Now;
+                        db.SaveChanges();
+
+                    }
+                    confirmed = true;
+
+
+
+                }
+                else
+                {
+                    logger.Error(string.Format("Received no record Action:{0} MessageId:{1} ", analysisResult.Action, analysisResult.UUID));
+                }
+            }
+
+            return confirmed;
+        }
+
+
+
+
+        private void RemoveClient(ClientData session)
+        {
+            logger.Trace("RemoveClient" + session.ChargeBoxId);
+            RemoveClientDic(session);
+            if (session != null)
+            {
+                try
+                {
+                    session.m_ReceiveData -= new ClientData.OCPPClientDataEventHandler<ClientData, String>(ReceivedMessage);
+                    session.Close(CloseReason.ServerClosing);
+                }
+                catch (Exception ex)
+                {
+                    //logger.Warn("Close client socket error!!");
+                    logger.Warn(string.Format("Close client socket error!! {0} Msg:{1}", session.ChargeBoxId, ex.Message));
+                }
+
+                if (session != null)
+                {
+                    session = null;
+                }
+
+            }
+        }
+
+        private void RemoveClientDic(ClientData session)
+        {
+            lock (_lockClientDic)
+            {
+                if (ClientDic.ContainsKey(session.ChargeBoxId))
+                {
+                    ClientDic.Remove(session.ChargeBoxId);
+                    logger.Trace("RemoveClient ContainsKey " + session.ChargeBoxId);
+                }
+            }
+        }
+
+        private void WriteMachineLog(string sn, string data, string messageType, string errorMsg = "")
+        {
+
+            using (var db = new ConnectionLogDBContext())
+            {
+                string sp = "[dbo].[uspInsertMachineConnectionLog] @CreatedOn," +
+                      "@ChargeBoxId,@MessageType,@Data,@Msg";
+                var dd = DateTime.Now.AddDays(-1);
+                SqlParameter[] parameter =
+                {
+                      new SqlParameter("CreatedOn",dd),
+                      new SqlParameter("ChargeBoxId",sn.Replace("'","''")),
+                      new SqlParameter("MessageType",messageType.Replace("'","''")),
+                      new SqlParameter("Data",data.Replace("'","''")),
+                      new SqlParameter("Msg",errorMsg.Replace("'","''"))
+               };
+
+                db.Database.ExecuteSqlCommand(sp, parameter);
+            }
+        }
+    }
+}

+ 23 - 0
EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallCmd.cs

@@ -0,0 +1,23 @@
+using OCPPServer;
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace EVCB_OCPP.WSServer.SuperSocket.Command
+{
+    public class ProcessCallCmd : SubCommandBase<ClientData>
+    {
+        public override void ExecuteCommand(ClientData session, SubRequestInfo requestInfo)
+        {
+            session.ReceiveData(session, requestInfo.Body);
+        }
+
+        public override string Name
+        {
+            get { return "2"; }
+        }
+    }
+}

+ 23 - 0
EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallErrorCmd.cs

@@ -0,0 +1,23 @@
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.SuperSocket.Command
+{
+    public class ProcessCallErrorCmd : SubCommandBase<ClientData>
+    {
+        public override void ExecuteCommand(ClientData session, SubRequestInfo requestInfo)
+        {
+            session.ReceiveData(session, requestInfo.Body);
+        }
+
+        public override string Name
+        {
+            get { return "4"; }
+        }
+    }
+}

+ 23 - 0
EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallResultCmd.cs

@@ -0,0 +1,23 @@
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.SuperSocket.Command
+{
+    public class ProcessCallResultCmd : SubCommandBase<ClientData>
+    {
+        public override void ExecuteCommand(ClientData session, SubRequestInfo requestInfo)
+        {
+            session.ReceiveData(session, requestInfo.Body);
+        }
+
+        public override string Name
+        {
+            get { return "3"; }
+        }
+    }
+}

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

@@ -0,0 +1,56 @@
+
+using EVCB_OCPP.Packet.Messages.Basic;
+using SuperSocket.SocketBase;
+using SuperWebSocket;
+using System;
+using System.Collections.Generic;
+
+namespace OCPPServer.Protocol
+{
+    public class ClientData : WebSocketSession<ClientData>
+    { /// <summary>
+      /// 根據unique id來儲存.取出OCPP Request
+      /// </summary>
+        public Queue queue = new Queue();
+
+        public bool IsCheckIn { set; get; }
+
+        public string ChargeBoxId { set; get; }
+        
+        public delegate void OCPPClientDataEventHandler<ClientData, String>(ClientData clientdata, String msg);
+
+        public event OCPPClientDataEventHandler<ClientData, String> m_ReceiveData;
+
+       public ClientData()
+        {
+            IsCheckIn = false;
+
+        }
+
+        /// <summary>
+        /// Sends the raw binary data to client.
+        /// </summary>
+        /// <param name="data">The data.</param>
+        /// <param name="offset">The offset.</param>
+        /// <param name="length">The length.</param>
+        public void SendRawData(byte[] data, int offset, int length)
+        {
+            base.Send(data, offset, length);
+        }
+
+        //receive data event trigger
+        public void ReceiveData(ClientData clientdata, string msg)
+        {
+            if (m_ReceiveData != null)
+                m_ReceiveData(clientdata, msg);
+        }
+
+        /// <summary>
+        /// Called when [session closed].
+        /// </summary>
+        /// <param name="reason">The reason.</param>
+        protected override void OnSessionClosed(CloseReason reason)
+        {
+        }
+    }
+}

+ 445 - 0
EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPLog.cs

@@ -0,0 +1,445 @@
+
+
+using SuperSocket.SocketBase.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.SubProtocol
+{
+    public class OCPPLog : ILog
+    {
+        private NLog.ILogger m_Log;
+
+        public OCPPLog(string name)
+        {
+            m_Log = NLog.LogManager.GetCurrentClassLogger();
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is debug enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is debug enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsDebugEnabled
+        {
+            get { return m_Log.IsDebugEnabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is error enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is error enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsErrorEnabled
+        {
+            get { return m_Log.IsErrorEnabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is fatal enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is fatal enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsFatalEnabled
+        {
+            get { return m_Log.IsFatalEnabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is info enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is info enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsInfoEnabled
+        {
+            get { return m_Log.IsInfoEnabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is warn enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is warn enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsWarnEnabled
+        {
+            get { return m_Log.IsWarnEnabled; }
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Debug(object message)
+        {
+            m_Log.Debug(message);
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Debug(object message, Exception exception)
+        {
+            //m_Log.Debug((System.IFormatProvider)message, exception);
+            m_Log.Debug(exception, message.ToString());
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void DebugFormat(string format, object arg0)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void DebugFormat(string format, params object[] args)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void DebugFormat(IFormatProvider provider, string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void DebugFormat(string format, object arg0, object arg1)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void DebugFormat(string format, object arg0, object arg1, object arg2)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Error(object message)
+        {
+            m_Log.Error(message);
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Error(object message, Exception exception)
+        {
+            //m_Log.Error((System.IFormatProvider)message, exception);
+            m_Log.Error(exception.ToString());
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void ErrorFormat(string format, object arg0)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void ErrorFormat(string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void ErrorFormat(IFormatProvider provider, string format, params object[] args)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void ErrorFormat(string format, object arg0, object arg1)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void ErrorFormat(string format, object arg0, object arg1, object arg2)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Fatal(object message)
+        {
+            m_Log.Fatal(message);
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Fatal(object message, Exception exception)
+        {
+            m_Log.Fatal((System.IFormatProvider)message, exception);
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void FatalFormat(string format, object arg0)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void FatalFormat(string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void FatalFormat(IFormatProvider provider, string format, params object[] args)
+        {
+          
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void FatalFormat(string format, object arg0, object arg1)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void FatalFormat(string format, object arg0, object arg1, object arg2)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Info(object message)
+        {
+            m_Log.Info(message);
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Info(object message, Exception exception)
+        {
+            //m_Log.Info((System.IFormatProvider)message, exception);
+            m_Log.Info(exception, message.ToString());
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void InfoFormat(string format, object arg0)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void InfoFormat(string format, params object[] args)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void InfoFormat(IFormatProvider provider, string format, params object[] args)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void InfoFormat(string format, object arg0, object arg1)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void InfoFormat(string format, object arg0, object arg1, object arg2)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Warn(object message)
+        {
+            m_Log.Warn(message);
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Warn(object message, Exception exception)
+        {
+            //m_Log.Warn((System.IFormatProvider)message, exception);
+            m_Log.Warn(exception, message.ToString());
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void WarnFormat(string format, object arg0)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void WarnFormat(string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void WarnFormat(IFormatProvider provider, string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void WarnFormat(string format, object arg0, object arg1)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void WarnFormat(string format, object arg0, object arg1, object arg2)
+        {
+            
+        }
+    }
+}

+ 38 - 0
EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPLogFactory.cs

@@ -0,0 +1,38 @@
+
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.SubProtocol
+{
+    /// <summary>
+    /// OCPP log factory
+    /// </summary>
+    public class OCPPLogFactory : LogFactoryBase
+    {
+        public OCPPLogFactory() : this("NLog.config")
+        {
+
+        }
+
+        public OCPPLogFactory(string log4netConfig)
+            : base(log4netConfig)
+        {
+            List<string> configlist = new List<string>();
+            configlist.Add(ConfigFile);
+            NLog.Config.XmlLoggingConfiguration.SetCandidateConfigFilePaths(configlist);
+        }
+
+        public override SuperSocket.SocketBase.Logging.ILog GetLog(string name)
+        {
+
+            return new OCPPLog(name);
+        }
+    }
+
+}

+ 38 - 0
EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPSubCommandParser.cs

@@ -0,0 +1,38 @@
+
+
+
+using SuperSocket.SocketBase.Protocol;
+using SuperWebSocket.SubProtocol;
+
+namespace OCPPServer.SubProtocol
+{
+    public class OCPPSubCommandParser : IRequestInfoParser<SubRequestInfo>
+    {
+        #region ISubProtocolCommandParser Members
+
+        /// <summary>
+        /// Parses the request info.
+        /// </summary>
+        /// <param name="source">The source.</param>
+        /// <returns></returns>
+        public SubRequestInfo ParseRequestInfo(string source)
+        {
+            var cmd = source.Trim();
+            int pos = cmd.IndexOf(',');
+            string name;
+
+            if (pos > 0)
+            {
+                name = cmd.Substring(pos - 1,1);
+            }
+            else
+            {
+                name = "4";
+            }
+
+            return new SubRequestInfo(name, "", source);
+        }
+
+        #endregion ISubProtocolCommandParser Members
+    }
+}

+ 321 - 0
EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPSubProtocol.cs

@@ -0,0 +1,321 @@
+using OCPPServer.Protocol;
+using SuperSocket.Common;
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Logging;
+using SuperSocket.SocketBase.Protocol;
+using SuperWebSocket;
+using SuperWebSocket.Config;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.SubProtocol
+{
+    public class OCPPSubProtocol : OCPPSubProtocol<ClientData>
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol"/> class.
+        /// </summary>
+        public OCPPSubProtocol()
+            : base(Assembly.GetCallingAssembly())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol"/> class.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        public OCPPSubProtocol(string name)
+            : base(name, Assembly.GetCallingAssembly())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol"/> class.
+        /// </summary>
+        /// <param name="commandAssembly">The command assembly.</param>
+        public OCPPSubProtocol(Assembly commandAssembly)
+            : base(commandAssembly)
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OCPPSubProtocol"/> class.
+        /// </summary>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        public OCPPSubProtocol(IEnumerable<Assembly> commandAssemblies)
+            : base(commandAssemblies)
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OCPPSubProtocol"/> class.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        /// <param name="commandAssembly">The command assembly.</param>
+        public OCPPSubProtocol(string name, Assembly commandAssembly)
+            : base(name, commandAssembly)
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OCPPSubProtocol"/> class.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        public OCPPSubProtocol(string name, IEnumerable<Assembly> commandAssemblies)
+            : base(name, commandAssemblies)
+        {
+
+        }
+
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OCPPSubProtocol"/> class.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        /// <param name="requestInfoParser">The request info parser.</param>
+        public OCPPSubProtocol(string name, IEnumerable<Assembly> commandAssemblies, IRequestInfoParser<SubRequestInfo> requestInfoParser)
+            : base(name, commandAssemblies, requestInfoParser)
+        {
+
+        }
+
+        public ILog Getmlog()
+        {
+            return getlog();
+        }
+
+    }
+
+    public class OCPPSubProtocol<TWebSocketSession> : SubProtocolBase<TWebSocketSession>
+       where TWebSocketSession : WebSocketSession<TWebSocketSession>, new()
+    {
+        /// <summary>
+        /// Default basic sub protocol name
+        /// </summary>
+        public const string DefaultName = "ocpp1.6";//"OCPP1.6";
+
+        private List<Assembly> m_CommandAssemblies = new List<Assembly>();
+
+        private Dictionary<string, ISubCommand<TWebSocketSession>> m_CommandDict;
+
+        private ILog m_Logger;
+
+        private SubCommandFilterAttribute[] m_GlobalFilters;
+
+        internal static BasicSubProtocol<TWebSocketSession> CreateDefaultSubProtocol()
+        {
+            var commandAssembly = typeof(TWebSocketSession).Assembly;
+
+            if (commandAssembly == Assembly.GetExecutingAssembly())
+                commandAssembly = Assembly.GetEntryAssembly();
+
+            return new BasicSubProtocol<TWebSocketSession>(DefaultName, commandAssembly);
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with the calling aseembly as command assembly
+        /// </summary>
+        public OCPPSubProtocol()
+            : this(DefaultName, Assembly.GetCallingAssembly())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with the calling aseembly as command assembly
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        public OCPPSubProtocol(string name)
+            : this(name, Assembly.GetCallingAssembly())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with command assemblies
+        /// </summary>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        public OCPPSubProtocol(IEnumerable<Assembly> commandAssemblies)
+            : this(DefaultName, commandAssemblies, new OCPPSubCommandParser())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with single command assembly.
+        /// </summary>
+        /// <param name="commandAssembly">The command assembly.</param>
+        public OCPPSubProtocol(Assembly commandAssembly)
+            : this(DefaultName, new List<Assembly> { commandAssembly }, new OCPPSubCommandParser())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with name and single command assembly.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        /// <param name="commandAssembly">The command assembly.</param>
+        public OCPPSubProtocol(string name, Assembly commandAssembly)
+            : this(name, new List<Assembly> { commandAssembly }, new OCPPSubCommandParser())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with name and command assemblies.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        public OCPPSubProtocol(string name, IEnumerable<Assembly> commandAssemblies)
+            : this(name, commandAssemblies, new OCPPSubCommandParser())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        /// <param name="requestInfoParser">The request info parser.</param>
+        public OCPPSubProtocol(string name, IEnumerable<Assembly> commandAssemblies, IRequestInfoParser<SubRequestInfo> requestInfoParser)
+            : base(name)
+        {
+            //The items in commandAssemblies may be null, so filter here
+            m_CommandAssemblies.AddRange(commandAssemblies.Where(a => a != null));
+            SubRequestParser = requestInfoParser;
+        }
+
+        #region ISubProtocol Members
+
+        private void DiscoverCommands()
+        {
+            var subCommands = new List<ISubCommand<TWebSocketSession>>();
+
+            foreach (var assembly in m_CommandAssemblies)
+            {
+                subCommands.AddRange(assembly.GetImplementedObjectsByInterface<ISubCommand<TWebSocketSession>>());
+            }
+
+#if DEBUG
+            var cmdbuilder = new StringBuilder();
+            cmdbuilder.AppendLine(string.Format("SubProtocol {0} found the commands below:", this.Name));
+
+            foreach (var c in subCommands)
+            {
+                cmdbuilder.AppendLine(c.Name);
+            }
+
+
+            m_Logger.Debug(cmdbuilder.ToString());
+#endif
+
+            m_CommandDict = new Dictionary<string, ISubCommand<TWebSocketSession>>(subCommands.Count, StringComparer.OrdinalIgnoreCase);
+
+            subCommands.ForEach(c =>
+            {
+                var fc = c as ISubCommandFilterLoader;
+
+                if (fc != null)
+                    fc.LoadSubCommandFilters(m_GlobalFilters);
+
+                m_CommandDict.Add(c.Name, c);
+            }
+                );
+        }
+
+
+        private bool ResolveCommmandAssembly(string definition)
+        {
+            try
+            {
+                var assemblies = AssemblyUtil.GetAssembliesFromString(definition);
+
+                if (assemblies.Any())
+                    m_CommandAssemblies.AddRange(assemblies);
+
+                return true;
+            }
+            catch (Exception e)
+            {
+                m_Logger.Error(e);
+                return false;
+            }
+        }
+
+        public ILog getlog()
+        {
+            return m_Logger;
+        }
+
+        /// <summary>
+        /// Tries get command from the sub protocol's command inventory.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <param name="command">The command.</param>
+        /// <returns></returns>
+        public override bool TryGetCommand(string name, out ISubCommand<TWebSocketSession> command)
+        {
+            return m_CommandDict.TryGetValue(name, out command);
+        }
+
+       
+
+        public override bool Initialize(IAppServer appServer, SubProtocolConfig protocolConfig, ILog logger)
+        {
+            m_Logger = logger;
+
+            var config = appServer.Config;
+
+            m_GlobalFilters = appServer.GetType()
+                    .GetCustomAttributes(true)
+                    .OfType<SubCommandFilterAttribute>()
+                    .Where(a => string.IsNullOrEmpty(a.SubProtocol) || Name.Equals(a.SubProtocol, StringComparison.OrdinalIgnoreCase)).ToArray();
+
+            if (Name.Equals(DefaultName, StringComparison.OrdinalIgnoreCase))
+            {
+                var commandAssembly = config.Options.GetValue("commandAssembly");
+
+                if (!string.IsNullOrEmpty(commandAssembly))
+                {
+                    if (!ResolveCommmandAssembly(commandAssembly))
+                        return false;
+                }
+            }
+
+            if (protocolConfig != null && protocolConfig.Commands != null)
+            {
+                foreach (var commandConfig in protocolConfig.Commands)
+                {
+                    var assembly = commandConfig.Options.GetValue("assembly");
+
+                    if (!string.IsNullOrEmpty(assembly))
+                    {
+                        if (!ResolveCommmandAssembly(assembly))
+                            return false;
+                    }
+                }
+            }
+
+            //Always discover commands
+            DiscoverCommands();
+
+            return true;
+        }
+
+        #endregion
+    }
+}

+ 127 - 0
EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPWSServer.cs

@@ -0,0 +1,127 @@
+
+using EVCB_OCPP.Domain;
+using SuperSocket.Common;
+using SuperWebSocket;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OCPPServer.Protocol
+{
+    public class OCPPWSServer : WebSocketServer<ClientData>
+    {
+        /// <summary>
+        /// 可允許連線Clinet數
+        /// </summary>
+        public int connectNum { get; set; }
+
+        /// <summary>
+        /// 是否限制連線Clinet數
+        /// </summary>
+        public bool beConnectLimit { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+        /// </summary>
+        /// <param name="subProtocols">The sub protocols.</param>
+        public OCPPWSServer(IEnumerable<ISubProtocol<ClientData>> subProtocols)
+            : base(subProtocols)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+        /// </summary>
+        /// <param name="subProtocol">The sub protocol.</param>
+        public OCPPWSServer(ISubProtocol<ClientData> subProtocol)
+            : base(subProtocol)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+        /// </summary>
+        public OCPPWSServer()
+            : base(new List<ISubProtocol<ClientData>>())
+        {
+
+        }
+
+        protected override bool ValidateHandshake(ClientData session, string origin)
+        {
+            string[] words = session.Path.Split('/');
+            session.ChargeBoxId = words.Last();
+
+            
+            bool isExistedSN = false;
+            using (var db = new MainDBContext())
+            {
+                var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+                isExistedSN = machine == null ? false : true;
+            }
+            if(!isExistedSN)
+            {
+                byte[] m_SwitchResponse;
+                var responseBuilder = new StringBuilder();
+                responseBuilder.AppendWithCrCf("HTTP/1.1 403"); //403 Forbidden : 用戶端並無訪問權限,所以伺服器給予應有的回應。
+                responseBuilder.AppendWithCrCf("Upgrade: WebSocket");
+                responseBuilder.AppendWithCrCf("Connection: Upgrade");
+                responseBuilder.AppendWithCrCf("Sec-WebSocket-Version: " + session.SecWebSocketVersion);
+                responseBuilder.AppendWithCrCf();
+
+                m_SwitchResponse = Encoding.UTF8.GetBytes(responseBuilder.ToString());
+
+                session.SendRawData(m_SwitchResponse, 0, m_SwitchResponse.Length);
+                return false;
+            }
+            else
+            {
+                //確認電樁連線所送的SubProtocol是否被Server支援
+                if (session.SecWebSocketProtocol.ToLower() != "ocpp1.6")
+                {
+                    const string m_Magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+                    const string SecWebSocketKey = "Sec-WebSocket-Key";
+                    const string ResponseHeadLine10 = "HTTP/1.1 101 Switching Protocols";
+                    const string Upgrade = "Upgrade";
+                    const string ResponseUpgradeLine = Upgrade + ": WebSocket";
+                    const string Connection = "Connection";
+                    const string ResponseConnectionLine = Connection + ": Upgrade";
+                    const string ResponseAcceptLine = "Sec-WebSocket-Accept: {0}";
+
+                    var responseBuilder = new StringBuilder();
+                    var secWebSocketKey = session.Items.GetValue<string>(SecWebSocketKey, string.Empty);
+                    if (string.IsNullOrEmpty(secWebSocketKey))
+                    {
+                        return false;
+                    }
+                    string secKeyAccept = string.Empty;
+
+                    try
+                    {
+                        secKeyAccept = Convert.ToBase64String(System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(secWebSocketKey + m_Magic)));
+                    }
+                    catch (Exception)
+                    {
+                        return false;
+                    }
+
+                    responseBuilder.AppendWithCrCf(ResponseHeadLine10);
+                    responseBuilder.AppendWithCrCf(ResponseUpgradeLine);
+                    responseBuilder.AppendWithCrCf(ResponseConnectionLine);
+                    responseBuilder.AppendFormatWithCrCf(ResponseAcceptLine, secKeyAccept);
+                    responseBuilder.AppendWithCrCf();
+                    byte[] data = Encoding.UTF8.GetBytes(responseBuilder.ToString());
+                    session.SendRawData(data, 0, data.Length);
+                    session.CloseWithHandshake(session.ProtocolProcessor.CloseStatusClode.NormalClosure, "This SubProtocol can't be  supported.");
+                    return false;
+                }
+            }
+
+            
+
+            return true;
+        }
+    }
+}

+ 9 - 0
EVCB_OCPP.WSServer/packages.config

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="EntityFramework" version="6.2.0" targetFramework="net471" />
+  <package id="log4net" version="2.0.3" targetFramework="net471" />
+  <package id="Newtonsoft.Json" version="12.0.1" 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" />
+</packages>

+ 86 - 0
OCPPServer/App.config

@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <configSections>
+
+    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
+    <section name="superSocket" type="SuperSocket.SocketEngine.Configuration.SocketServiceConfig, SuperSocket.SocketEngine" />
+    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
+  </configSections>
+  <connectionStrings>
+    <add name="DefaultConnection" connectionString="data source=172.1.0.142\SQLEXPRESS;initial catalog=Evcb_formal;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
+    <!--<add name="DefaultConnection" connectionString="data source=localhost\SQL2016;initial catalog=Evcb;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />-->
+    <!-- <add name="DefaultConnection" connectionString="data source=localhost\SQL2016;initial catalog=Evcb;persist security info=True;user id=sa;password=Ph0930118811;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" /> -->
+    <!-- <add name="DefaultConnection" connectionString="Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Evcb" providerName="System.Data.SqlClient" /> -->
+  </connectionStrings>
+  <startup>
+    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
+  </startup>
+  <entityFramework>
+    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
+      <parameters>
+        <parameter value="mssqllocaldb" />
+      </parameters>
+    </defaultConnectionFactory>
+    <providers>
+      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
+    </providers>
+  </entityFramework>
+  <superSocket>
+    <servers>
+      <server name="SuperWebSocket" serverTypeName="SuperWebSocket">
+        <!--  <certificate filePath="localhost.pfx" password="supersocket" storeName="My" thumbprint="‎f42585bceed2cb049ef4a3c6d0ad572a6699f6f3" storeLocation="LocalMachine" clientCertificateRequired="false" keyStorageFlags="Exportable"></certificate> -->
+        <certificate filePath="certificate.pfx" password="y42j/4cj84" storeName="My" thumbprint="‎f115742946884724c07cd507564daa12dac89615" storeLocation="LocalMachine" clientCertificateRequired="false" keyStorageFlags="Exportable"></certificate>
+        <listeners>
+        </listeners>
+      </server>
+    </servers>
+    <serverTypes>
+      <add name="SuperWebSocket" type="SuperWebSocket.WebSocketServer, SuperWebSocket" />
+    </serverTypes>
+  </superSocket>
+  <appSettings>
+    <add key="ServerIp" value="172.17.40.13" />
+    <add key="ServerPort" value="8080" />
+    <add key="WSSServerPort" value="2013" />
+    <add key="ServerSecurity" value="tls" />
+    <add key="MaxRequestLength" value="3500" />
+    <!--<add key="ServerIp" value="127.0.0.1" />-->
+    <!--<add key="ServerPort" value="9999" />-->
+    <!--<add key="Customer" value="Phihong" />-->
+    <!-- 是否轉換socket 0:不要、1:要 -->
+    <add key="Redirect" value="0" />
+    <add key="ApiUrl" value="http://172.18.61.97:58612/api/ChargerService/" />
+    <!--<add key="ApiUrl" value="http://172.1.2.186/FoxconnWeb/api/ChargerService/" />-->
+    <add key="ClientSettingsProvider.ServiceUri" value="" />
+    <!-- 設定電樁連線數量資訊 (mac,limit,Signature 三組資料會作組合比對,切勿隨意修改)-->
+    <!-- Server Mac-->
+    <add key="mac" value="94-E9-79-91-A5-9A" />
+    <!-- 可連線數量  ∞ :無限制數量 -->
+    <add key="limit" value="∞" />
+    <!-- 為從 mac.limit 組合,計算出來的加密簽章 -->
+    <add key="Signature" value="2CVsI2YqXEG5zFxq1LbWH/8NuNCe3ItVyaOa4wUirdY=" />
+    <!-- 以上設定電樁連線數量資訊-->
+    <!-- 是否要記錄MachineConnectionLog 0:不要、1:要 -->
+    <add key="EnableMachineConnectionLog" value="1" />
+    <!-- 是否要將Machinelog、chargingRecordStatus轉移到mongodb ,若使用mssql 不能使用chargingRecordStatus trigger-->
+    <!--<add key="mongoDb" value="null" />-->
+    <!--mongodb connection example: mongodb://172.1.2.194:27017-->
+    <add key="mongoCon" value="" />
+  </appSettings>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.1.1.2" newVersion="4.1.1.2" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
+</configuration>

+ 114 - 0
OCPPServer/ClientData.cs

@@ -0,0 +1,114 @@
+using OCPPServer.Protocol;
+using Packet.Cmd;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer
+{
+    public class ClientData
+    {
+        public byte[] _buffer = new byte[1024];
+        public List<byte> bufferList = new List<byte>();
+        public OCPPSession ClientSocket;
+
+        public CmdHelper CmdHelper;
+
+        public bool IsCheckIn { get; set; }
+
+        public DateTime LastAccessDatetime { get; set; }
+
+        /// <summary>
+        /// 每個client有他自己的指令序號 2 byte
+        /// </summary>
+        private ushort _cmdSerNum = 0;
+
+        /// <summary>
+        /// 傳送的封包序號 1 byte
+        /// </summary>
+        private byte _serNum = 0;
+
+        public string MachineId { get; set; }
+
+        public Guid CustomerId { get; set; }
+        public string CustomerName { get; set; }
+
+        /// <summary>
+        /// 客戶自訂的樁id
+        /// </summary>
+        public string MachineCustomId
+        {
+            get; set;
+        }
+
+        /// <summary>
+        ///  Accepted:0, Pending:1, Rejected:2
+        /// </summary>
+        public bool RegistrationStatus  { get; set; }
+
+        /// <summary>
+        /// 0: 会员、1: 卡号、2:不识别
+        /// </summary>
+        public byte StartWith { get; set; }
+
+        /// <summary>
+        /// 充電/預約卡號、 36 bytes assic碼、不夠長度補'\0'
+        /// </summary>
+        public string ReservationCardNum { get; set; }
+
+        /// <summary>
+        /// Remote充電 
+        /// </summary>
+        public bool RemoteCharging { get; set; }
+
+        public ClientData()
+        {
+            MachineCustomId = Guid.NewGuid().ToString();
+            IsCheckIn = false;
+            RegistrationStatus = false;
+            StartWith = 2; /// 0: 会员、1: 卡号、2:不识别
+            ReservationCardNum = "";
+            RemoteCharging = false;  /// Remote充電  
+            LastAccessDatetime = DateTime.Now;
+        }
+
+        public ClientData(OCPPSession clientSocket)
+        {
+            this.ClientSocket = clientSocket;
+            MachineCustomId = Guid.NewGuid().ToString();
+            RemoteCharging = false;  /// Remote充電 
+            LastAccessDatetime = DateTime.Now;
+        }
+
+        /// <summary>
+        /// 取得指令序號 0-65535
+        /// </summary>
+        /// <returns></returns>
+        public ushort GetCmdSerNum()
+        {
+            if (_cmdSerNum == ushort.MaxValue)
+                _cmdSerNum = 0;
+            return ++_cmdSerNum;
+        }
+
+        /// <summary>
+        /// 取得封包序號 1-255
+        /// </summary>
+        /// <returns></returns>
+        public byte GetSerNum()
+        {
+            if (_serNum == byte.MaxValue)
+            {
+                _serNum = 0;
+            }
+            return ++_serNum;
+        }
+
+        public bool CheckClient()
+        {
+            return false;
+        }
+    }
+}

+ 23 - 0
OCPPServer/Command/ProcessCallCmd.cs

@@ -0,0 +1,23 @@
+using OCPPServer;
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OCPPServer.Command
+{
+    public class ProcessCallCmd : SubCommandBase<ClientData>
+    {
+        public override void ExecuteCommand(ClientData session, SubRequestInfo requestInfo)
+        {
+            session.ReceiveData(session, requestInfo.Body);
+        }
+
+        public override string Name
+        {
+            get { return "2"; }
+        }
+    }
+}

+ 23 - 0
OCPPServer/Command/ProcessCallErrorCmd.cs

@@ -0,0 +1,23 @@
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.Command
+{
+    public class ProcessCallErrorCmd : SubCommandBase<ClientData>
+    {
+        public override void ExecuteCommand(ClientData session, SubRequestInfo requestInfo)
+        {
+            session.ReceiveData(session, requestInfo.Body);
+        }
+
+        public override string Name
+        {
+            get { return "4"; }
+        }
+    }
+}

+ 23 - 0
OCPPServer/Command/ProcessCallResultCmd.cs

@@ -0,0 +1,23 @@
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.Command
+{
+    public class ProcessCallResultCmd : SubCommandBase<ClientData>
+    {
+        public override void ExecuteCommand(ClientData session, SubRequestInfo requestInfo)
+        {
+            session.ReceiveData(session, requestInfo.Body);
+        }
+
+        public override string Name
+        {
+            get { return "3"; }
+        }
+    }
+}

+ 78 - 0
OCPPServer/Common/ListenerConfig.cs

@@ -0,0 +1,78 @@
+using SuperSocket.SocketBase.Config;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.Common
+{
+    public class ListenerConfig : IListenerConfig
+    {
+        /// <summary>
+        /// Gets the ip of listener
+        /// </summary>
+        private string ip;
+
+        /// <summary>
+        /// Gets the port of listener
+        /// </summary>
+        private int port;
+
+        /// <summary>
+        /// Gets the backlog.
+        /// </summary>
+        private int backlog;
+
+        /// <summary>
+        /// Gets the security option, None/Default/Tls/Ssl/...
+        /// </summary>
+        private string security;
+
+        public ListenerConfig(string a, int b, int c, string d)
+        {
+            ip = a;
+            port = b;
+            backlog = c;
+            security = d;
+        }
+
+        // Property implementation:
+        public string Ip
+        {
+            get
+            {
+                return ip;
+            }
+
+           
+        }
+
+        public int Port
+        {
+            get
+            {
+                return port;
+            }
+           
+        }
+
+        public int Backlog
+        {
+            get
+            {
+                return port;
+            }
+
+        }
+
+        public string Security
+        {
+            get
+            {
+                return security;
+            }
+
+        }
+    }
+}

+ 22 - 0
OCPPServer/Common/LocalAuthorization.cs

@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+
+namespace OCPPServer.Common
+{
+    public class LocalAuthorization
+    {
+        /// <summary>
+        /// 卡片號碼
+        /// </summary>
+        public string CardNumber { set; get; }
+
+        /// <summary>
+        /// 卡片狀態 0:允許充電 1:鎖定卡片(不允許充電) 2:逾期卡片(不允許充電) 3:無效卡片(不允許充電)
+        /// </summary>
+        public int Status { set; get; }
+
+        /// <summary>
+        /// 逾期時間 格式:yyyy-MM-dd HH:mm:ss, 秒一律帶0, 例如:2018-06-08 23:00:00, 可不填寫
+        /// </summary>
+        public string ExpiryDate { set; get; }
+    }
+}

+ 61 - 0
OCPPServer/Common/OCPPResult.cs

@@ -0,0 +1,61 @@
+using Evcb.Service.Service.Common;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.Common
+{
+    public class OCPPResult : IResult
+    {
+        public Guid ID
+        {
+            get;
+            private set;
+        }
+
+        public bool Success
+        {
+            get;
+            set;
+        }
+
+        public string Message
+        {
+            get;
+            set;
+        }
+
+        public Exception Exception
+        {
+            get;
+            set;
+        }
+
+        public List<IResult> InnerResults
+        {
+            get;
+            protected set;
+        }
+
+        public object Payload
+        {
+            get;
+            set;
+        }
+
+        public OCPPResult()
+            : this(false)
+        {
+        }
+
+        public OCPPResult(bool success)
+        {
+            ID = Guid.NewGuid();
+            Success = success;
+            InnerResults = new List<IResult>();
+        }
+
+    }
+}

+ 69 - 0
OCPPServer/Config/log4net.config

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<log4net>
+    <appender name="errorAppender" type="log4net.Appender.RollingFileAppender">
+        <filter type="log4net.Filter.LevelMatchFilter">
+            <levelToMatch value="ERROR" />
+        </filter>
+        <filter type="log4net.Filter.DenyAllFilter" />
+        <File value="Logs\err.log" />
+        <PreserveLogFileNameExtension value="true" />
+        <appendToFile value="true" />
+        <rollingStyle value="Date" />
+        <datePattern value="yyyyMMdd" />
+        <layout type="log4net.Layout.PatternLayout">
+            <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
+        </layout>
+    </appender>
+    <appender name="infoAppender" type="log4net.Appender.RollingFileAppender">
+        <filter type="log4net.Filter.LevelMatchFilter">
+            <levelToMatch value="INFO" />
+        </filter>
+        <filter type="log4net.Filter.DenyAllFilter" />
+        <File value="Logs\info.log" />
+        <PreserveLogFileNameExtension value="true" />
+        <appendToFile value="true" />
+        <rollingStyle value="Date" />
+        <datePattern value="yyyyMMdd" />
+        <layout type="log4net.Layout.PatternLayout">
+            <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
+        </layout>
+    </appender>
+    <appender name="debugAppender" type="log4net.Appender.RollingFileAppender">
+        <filter type="log4net.Filter.LevelMatchFilter">
+            <levelToMatch value="DEBUG" />
+        </filter>
+        <filter type="log4net.Filter.DenyAllFilter" />
+        <File value="Logs\debug.log" />
+        <PreserveLogFileNameExtension value="true" />
+        <appendToFile value="true" />
+        <rollingStyle value="Date" />
+        <datePattern value="yyyyMMdd" />
+        <layout type="log4net.Layout.PatternLayout">
+            <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
+        </layout>
+    </appender>
+    <appender name="perfAppender" type="log4net.Appender.RollingFileAppender">
+        <filter type="log4net.Filter.LevelMatchFilter">
+            <levelToMatch value="INFO" />
+        </filter>
+        <filter type="log4net.Filter.DenyAllFilter" />
+        <File value="Logs\perf.log" />
+        <PreserveLogFileNameExtension value="true" />
+        <appendToFile value="true" />
+        <rollingStyle value="Date" />
+        <datePattern value="yyyyMMdd" />
+        <layout type="log4net.Layout.PatternLayout">
+            <conversionPattern value="%date %logger - %message%newline" />
+        </layout>
+    </appender>
+    <root>
+        <level value="ALL" />
+        <appender-ref ref="errorAppender" />
+        <appender-ref ref="infoAppender" />
+        <appender-ref ref="debugAppender" />
+    </root>
+    <logger name="Performance" additivity="false">
+      <level value="ALL" />
+      <appender-ref ref="perfAppender" />
+    </logger>
+</log4net>

+ 69 - 0
OCPPServer/Config/log4net.unix.config

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<log4net>
+    <appender name="errorAppender" type="log4net.Appender.RollingFileAppender">
+        <filter type="log4net.Filter.LevelMatchFilter">
+            <levelToMatch value="ERROR" />
+        </filter>
+        <filter type="log4net.Filter.DenyAllFilter" />
+        <File value="Logs/err.log" />
+        <PreserveLogFileNameExtension value="true" />
+        <appendToFile value="true" />
+        <rollingStyle value="Date" />
+        <datePattern value="yyyyMMdd" />
+        <layout type="log4net.Layout.PatternLayout">
+            <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
+        </layout>
+    </appender>
+    <appender name="infoAppender" type="log4net.Appender.RollingFileAppender">
+        <filter type="log4net.Filter.LevelMatchFilter">
+            <levelToMatch value="INFO" />
+        </filter>
+        <filter type="log4net.Filter.DenyAllFilter" />
+        <File value="Logs/info.log" />
+        <PreserveLogFileNameExtension value="true" />
+        <appendToFile value="true" />
+        <rollingStyle value="Date" />
+        <datePattern value="yyyyMMdd" />
+        <layout type="log4net.Layout.PatternLayout">
+            <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
+        </layout>
+    </appender>
+    <appender name="debugAppender" type="log4net.Appender.RollingFileAppender">
+        <filter type="log4net.Filter.LevelMatchFilter">
+            <levelToMatch value="DEBUG" />
+        </filter>
+        <filter type="log4net.Filter.DenyAllFilter" />
+        <File value="Logs/debug.log" />
+        <PreserveLogFileNameExtension value="true" />
+        <appendToFile value="true" />
+        <rollingStyle value="Date" />
+        <datePattern value="yyyyMMdd" />
+        <layout type="log4net.Layout.PatternLayout">
+            <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
+        </layout>
+    </appender>
+    <appender name="perfAppender" type="log4net.Appender.RollingFileAppender">
+        <filter type="log4net.Filter.LevelMatchFilter">
+            <levelToMatch value="TRACE" />
+        </filter>
+        <filter type="log4net.Filter.DenyAllFilter" />
+        <File value="Logs/perf.log" />
+        <PreserveLogFileNameExtension value="true" />
+        <appendToFile value="true" />
+        <rollingStyle value="Date" />
+        <datePattern value="yyyyMMdd" />
+        <layout type="log4net.Layout.PatternLayout">
+            <conversionPattern value="%date %logger - %message%newline" />
+        </layout>
+    </appender>
+    <root>
+        <level value="ALL" />
+        <appender-ref ref="errorAppender" />
+        <appender-ref ref="infoAppender" />
+        <appender-ref ref="debugAppender" />
+    </root>
+    <logger name="Performance" additivity="false">
+        <level value="ALL" />
+        <appender-ref ref="perfAppender" />
+    </logger>
+</log4net>

+ 109 - 0
OCPPServer/Handler/FeatureHandler.cs

@@ -0,0 +1,109 @@
+using NLog;
+using OCPPPacket.Packet.Feature;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.Handler
+{
+    public class FeatureHandler
+    {
+        private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        List<Feature> featureList;
+        public FeatureHandler()
+        {
+            this.featureList = new List<Feature>();
+        }
+
+        public void AddFeatureProfile(Feature feature)
+        {
+            featureList.Add(feature);
+        }
+
+        public  Feature FindFeatureByConfirm(Type confirm)
+        {
+            return FindFeature(confirm, featureList);
+        }
+
+        public  Feature FindFeatureByRequest(Type request)
+        {
+            return FindFeature(request, featureList);
+        }
+
+        public bool FeatureContains(Feature feature, Type seektype)
+        {
+            bool contains = false;
+            if(feature.GetRequestType() == seektype)
+            {
+                contains |= true;
+            }
+
+            if(feature.GetConfirmationType() == seektype)
+            {
+                contains |= true;
+            }
+            //old coding type
+            ////contains |= feature.getAction().Equals(object1);
+            //contains |= feature.GetRequestType() == object1;
+            //contains |= feature.GetConfirmationType() == object1;
+            return contains;
+        }
+
+        public  Feature FindFeature(Type needle, List<Feature> featureList)
+        {
+            Feature output = null;
+
+            foreach (Feature feature in featureList)
+            {
+                if (FeatureContains(feature, needle))
+                {
+                    output = feature;
+                    break;
+                }
+            }
+
+            return output;
+        }
+
+        public  Feature FindFeatureByAction(string v)
+        {
+            try
+            {
+                Feature output = null;
+
+                foreach (Feature feature in featureList)
+                {
+                    if (TestfeatureContains(feature, v))
+                    {
+                        output = feature;
+                        break;
+                    }
+                }
+
+                return output;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                throw new ApplicationException(ex.ToString());
+            }
+        }
+
+        public  bool TestfeatureContains(Feature feature, string v)
+        {
+            try
+            {
+                bool contains = false;
+                contains |= feature.GetAction().Equals(v);
+                return contains;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                throw new ApplicationException(ex.ToString());
+            }
+        }
+    }
+}

+ 256 - 0
OCPPServer/Handler/MakeConfirmationHandler.cs

@@ -0,0 +1,256 @@
+using Newtonsoft.Json;
+using OCPPPacket.Packet.DataTransfer;
+using OCPPPacket.Packet.Messages;
+using OCPPPacket.Packet.Messages.Core;
+using OCPPPacket.Packet.Messages.FirmwareManagement;
+using OCPPPacket.Packet.Status;
+using System;
+
+namespace OCPPServer.Handler
+{
+    public class MakeConfirmationHandler
+    {
+        #region Core
+
+        /// <summary>
+        /// 建立 Authorize comfirm message
+        /// </summary>
+        /// <param name="request">AuthorizeRequest</param>
+        /// <param name="authstatus">AuthorizationStatus</param>
+        /// <returns></returns>
+        public IConfirmation MakeAuthorizeConfirmation(AuthorizeRequest request, AuthorizationStatus authstatus)
+        {
+            AuthorizeConfirmation conf = new AuthorizeConfirmation();
+            conf.idTagInfo = new IdTagInfo();
+            conf.idTagInfo.expiryDate = new DateTime(2020, 06, 19, 09, 10, 00, 000);//date;
+            conf.idTagInfo.parentIdTag = request.idTag;
+            conf.idTagInfo.status = authstatus;
+
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 BootNotification comfirm message
+        /// </summary>
+        /// <param name="request">BootNotificationRequest</param>
+        /// <returns></returns>
+        public IConfirmation MakeBootNotificationConfirmation(BootNotificationRequest request)
+        {
+            BootNotificationConfirmation conf = new BootNotificationConfirmation();
+            conf.currentTime = DateTime.Now;
+            conf.interval = 10;
+            conf.status = RegistrationStatus.Accepted;
+
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 StatusNotification comfirm message
+        /// </summary>
+        /// <param name="request">StatusNotificationRequest</param>
+        /// <returns></returns>
+        public IConfirmation MakeStatusNotificationConfirmation(StatusNotificationRequest request)
+        {
+            StatusNotificationConfirmation conf = new StatusNotificationConfirmation();
+
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 Heartbeat comfirm message
+        /// </summary>
+        /// <param name="request">HeartbeatRequest</param>
+        /// <returns></returns>
+        public IConfirmation MakeHeartbeatConfirmation(HeartbeatRequest request)
+        {
+            HeartbeatConfirmation conf = new HeartbeatConfirmation();
+            conf.currentTime = DateTime.Now;
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 ClearCache comfirm message
+        /// </summary>
+        /// <param name="request">ClearCacheRequest</param>
+        /// <returns></returns>
+        public IConfirmation MakeClearCacheConfirmation(ClearCacheRequest request)
+        {
+            ClearCacheConfirmation conf = new ClearCacheConfirmation();
+            conf.status = ClearCacheStatus.Accepted;
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 MeterValues comfirm message
+        /// </summary>
+        /// <param name="request">MeterValuesRequest</param>
+        /// <returns></returns>
+        public IConfirmation MakeMeterValuesConfirmation(MeterValuesRequest request)
+        {
+            MeterValuesConfirmation conf = new MeterValuesConfirmation();
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 StartTransaction comfirm message
+        /// </summary>
+        /// <param name="request">StartTransactionRequest</param>
+        /// <param name="transactionId"></param>
+        /// <returns></returns>
+        public IConfirmation MakeStartTransactionConfirmation(StartTransactionRequest request, out int transactionId)
+        {
+            StartTransactionConfirmation conf = new StartTransactionConfirmation();
+            conf.idTagInfo = new IdTagInfo();
+            conf.idTagInfo.expiryDate = new DateTime(2020, 06, 19, 09, 10, 00, 000);
+            conf.idTagInfo.parentIdTag = request.idTag;
+            conf.transactionId = new Random().Next(1, int.MaxValue);
+            conf.idTagInfo.status = AuthorizationStatus.Accepted; // 一律回 "Accepted"
+            transactionId = conf.transactionId;
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 Repeat StartTransaction comfirm message
+        /// </summary>
+        /// <param name="request">StartTransactionRequest</param>
+        /// <param name="transactionId"></param>
+        /// <returns></returns>
+        public IConfirmation MakeRePeatStartTransactionConfirmation(StartTransactionRequest request, int transactionId)
+        {
+            StartTransactionConfirmation conf = new StartTransactionConfirmation();
+            conf.idTagInfo = new IdTagInfo();
+            conf.idTagInfo.expiryDate = new DateTime(2020, 06, 19, 09, 10, 00, 000);
+            conf.idTagInfo.parentIdTag = request.idTag;
+            conf.transactionId = transactionId;
+            conf.idTagInfo.status = AuthorizationStatus.Accepted; // 一律回 "Accepted"
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 StopTransaction comfirm message
+        /// </summary>
+        /// <param name="request">StopTransactionRequest</param>
+        /// <param name="authstatus">AuthorizationStatus</param>
+        /// <returns></returns>
+        public IConfirmation MakeStopTransactionConfirmation(StopTransactionRequest request, AuthorizationStatus authstatus)
+        {
+            StopTransactionConfirmation conf = new StopTransactionConfirmation();
+            conf.idTagInfo = new IdTagInfo();
+            conf.idTagInfo.expiryDate = new DateTime(2020, 06, 19, 09, 10, 00, 000);
+            conf.idTagInfo.parentIdTag = request.idTag;
+            conf.idTagInfo.status = authstatus;
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 GunStatus comfirm message
+        /// </summary>
+        /// <returns></returns>
+        public IConfirmation MakeGunStatusConfirmation()
+        {
+            DataTransferConfirmation conf = new DataTransferConfirmation();
+            conf.status = DataTransferStatus.Accepted;
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 ChargeComplete comfirm message
+        /// </summary>
+        /// <returns></returns>
+        public IConfirmation MakeChargeCompleteConfirmation()
+        {
+            DataTransferConfirmation conf = new DataTransferConfirmation();
+            conf.status = DataTransferStatus.Accepted;
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 Location comfirm message
+        /// </summary>
+        /// <returns></returns>
+        public IConfirmation MakeLocationConfirmation()
+        {
+            DataTransferConfirmation conf = new DataTransferConfirmation();
+            conf.status = DataTransferStatus.Accepted;
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 ChargingGunBMS comfirm message
+        /// </summary>
+        /// <returns></returns>
+        public IConfirmation MakeChargingGunBMSConfirmation()
+        {
+            DataTransferConfirmation conf = new DataTransferConfirmation();
+            conf.status = DataTransferStatus.Accepted;
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 UpgradeVersionConf comfirm message
+        /// </summary>
+        /// <param name="result"></param>
+        /// <param name="param"></param>
+        /// <returns></returns>
+        public IConfirmation MakeUpgradeVersionConfConfirmation(bool result, ProgramParam param)
+        {
+            DataTransferConfirmation conf = new DataTransferConfirmation();
+            conf.status = DataTransferStatus.Accepted;
+            UpgradeVersionConf upgradeverConf = new UpgradeVersionConf();
+            upgradeverConf.param = param;
+            if (!result)
+            {
+                upgradeverConf.SameAsServer = true;
+            }
+            else
+            {
+                upgradeverConf.SameAsServer = false;
+            }
+
+            conf.data = JsonConvert.SerializeObject(upgradeverConf);
+            return conf;
+        }
+
+        #endregion Core
+
+        #region FirmwareManagement
+
+        /// <summary>
+        /// 建立 DiagnosticsStatusNotification comfirm message
+        /// </summary>
+        /// <returns></returns>
+        public IConfirmation MakeDiagnosticsStatusNotificationConfirmation()
+        {
+            DiagnosticsStatusNotificationConfirmation conf = new DiagnosticsStatusNotificationConfirmation();
+            return conf;
+        }
+
+        /// <summary>
+        /// 建立 FirmwareStatusNotification comfirm message
+        /// </summary>
+        /// <returns></returns>
+        public IConfirmation MakeFirmwareStatusNotificationConfirmation()
+        {
+            FirmwareStatusNotificationConfirmation conf = new FirmwareStatusNotificationConfirmation();
+            return conf;
+        }
+
+        #endregion FirmwareManagement
+
+        #region DataTransfer
+        public IConfirmation MakeAuthorizeDataConfConfirmation(AuthorizeData request, AuthorizeStatus authstatus)
+        {
+            DataTransferConfirmation conf = new DataTransferConfirmation();
+            conf.status = DataTransferStatus.Accepted;
+            AuthorizeResult authorizeResult = new AuthorizeResult();
+            authorizeResult.expiryDate = new DateTime(2020, 06, 19, 09, 10, 00, 000);//date;
+            authorizeResult.parentIdTag = request.idTag;
+            authorizeResult.status = authstatus;
+
+            conf.data = JsonConvert.SerializeObject(authorizeResult);
+            return conf;
+        }
+
+        #endregion
+    }
+}

+ 612 - 0
OCPPServer/Handler/MakeRequestHandler.cs

@@ -0,0 +1,612 @@
+using Evcb.Domain.Model;
+using Newtonsoft.Json;
+using OCPP_Packet.Packet.DataTransfer;
+using OCPPPacket.Packet.DataTransfer;
+using OCPPPacket.Packet.Messages;
+using OCPPPacket.Packet.Messages.Core;
+using OCPPPacket.Packet.Messages.FirmwareManagement;
+using OCPPPacket.Packet.Messages.LocalAuthListManagement;
+using OCPPPacket.Packet.Messages.RemoteTrigger;
+using OCPPPacket.Packet.Messages.Reservation;
+using OCPPPacket.Packet.Messages.SmartCharging;
+using OCPPPacket.Packet.Status;
+using OCPPServer.Common;
+using Packet.Cmd;
+using System;
+using System.Collections.Generic;
+
+namespace OCPPServer.Handler
+{
+    public class MakeRequestHandler
+    {
+        #region Core
+
+        /// <summary>
+        /// 取得電樁 configuration settings
+        /// </summary>
+        static public IRequest MakeGetConfigurationRequest()
+        {
+            GetConfigurationRequest req = new GetConfigurationRequest();
+            req.key = new List<string>();
+            //req.key.Add("AllowOfflineTxForUnknownIdAvailable");
+            //req.key.Add("AllowOfflineTxForUnknownIdEnabled");
+            //req.key.Add("AuthorizationCacheAvailable");
+            //req.key.Add("AuthorizationCacheEnabled");
+            //req.key.Add("BlinkRepeat");
+            //req.key.Add("ClockAlignedDataInterval");
+            //req.key.Add("ConnectionTimeOut");
+            //req.key.Add("HeartBeatInterval");
+            //req.key.Add("LightIntensity");
+            //req.key.Add("LocalAuthorizeOffline");
+            //req.key.Add("LocalPreAuthorize");
+            //req.key.Add("MeterValuesAlignedData");
+            //req.key.Add("MeterValuesSampledData");
+            //req.key.Add("MeterValueSampleInterval");
+            //req.key.Add("MinimumStatusDuration");
+            //req.key.Add("ResetRetries");
+            //req.key.Add("ConnectorPhaseRotation");
+            //req.key.Add("StopTransactionOnEVSideDisconnect");
+            //req.key.Add("LocalAuthorizationListEnabled");
+            //req.key.Add("StopTransactionOnInvalidId");
+            //req.key.Add("StopTxnAlignedData");
+            //req.key.Add("StopTxnSampledData");
+            //req.key.Add("SupportedCompliancyProfiles");
+            //req.key.Add("TransactionMessageAttempts");
+            //req.key.Add("TransactionMessageRetryInterval");
+            //req.key.Add("UnlockConnectorOnEVSideDisconnect");
+            //req.key.Add("ChargeProfileMaxStackLevel");
+            //req.key.Add("ChargingScheduleAllowedSchedulingUnit");
+            //req.key.Add("ChargingScheduleMaxPeriods");
+            //req.key.Add("MaxChargingProfilesInstalled");
+            //req.key.Add("ProximityLockRetries");
+            //req.key.Add("ProximityContactRetries");
+            //req.key.Add("ChargePointId");
+            //req.key.Add("GetConfigurationMaxKeys");
+            //req.key.Add("LocalAuthListSize");
+            //req.key.Add("LocalAuthMaxElementsOnce");
+            //req.key.Add("MaxEnergyOnInvalidId");
+            //req.key.Add("MeterValuesTriggeredData");
+            //req.key.Add("ReserveConnectorZeroSupported");
+            //req.key.Add("WebSocketPingInterval");
+            //req.key.Add("Testing");
+            return req;
+        }
+
+        /// <summary>
+        /// 要求電樁改變configuration parameters
+        /// </summary>
+        static public IRequest MakeChangeConfigurationRequest(string key, string keyvalue)
+        {
+            ChangeConfigurationRequest req = new ChangeConfigurationRequest();
+
+            if (key == "HeartbeatInterval")
+            {
+                req.key = "HeartbeatInterval";
+                req.value = keyvalue;
+            }
+            return req;
+        }
+
+        /// <summary>
+        /// APP啟動充電
+        /// </summary>
+        public IRequest MakeRemoteStartTransactionRequest(int gunSerNo, string memberId)
+        {
+            RemoteStartTransactionRequest req = new RemoteStartTransactionRequest();
+            req.connectorId = gunSerNo;//0;
+            req.idTag = memberId;//"990f57dd-b660-4734-8e96-4cd346d0qwez";
+
+            req.chargingProfile = null;
+            return req;
+        }
+
+        /// <summary>
+        /// APP結束充電
+        /// </summary>
+        public IRequest MakeRemoteStopTransactionRequest(int transactionId)
+        {
+            RemoteStopTransactionRequest req = new RemoteStopTransactionRequest();
+            req.transactionId = transactionId;
+            return req;
+        }
+
+        /// <summary>
+        /// 要求電樁清除 Authorization Cache
+        /// </summary>
+        public IRequest MakeClearCacheRequest()
+        {
+            ClearCacheRequest req = new ClearCacheRequest();
+            return req;
+        }
+
+        /// <summary>
+        /// 要求電樁 Hard Reset
+        /// </summary>
+        public IRequest MakeHardResetRequest()
+        {
+            ResetRequest req = new ResetRequest();
+            req.type = ResetType.Hard;
+            return req;
+        }
+
+        /// <summary>
+        /// 要求電樁 Soft Reset
+        /// </summary>
+        public IRequest MakeSoftResetRequest()
+        {
+            ResetRequest req = new ResetRequest();
+            req.type = ResetType.Soft;
+            return req;
+        }
+
+        /// <summary>
+        /// unlock 電樁 connector
+        /// </summary>
+        public IRequest MakeUnlockConnectorRequest(int connectorId)
+        {
+            UnlockConnectorRequest req = new UnlockConnectorRequest();
+            req.connectorId = connectorId;
+            return req;
+        }
+
+        /// <summary>
+        /// 取得 電樁 configuration
+        /// </summary>
+        public IRequest MakeGetConfigurationRequest(string key)
+        {
+            GetConfigurationRequest req = new GetConfigurationRequest();
+            //req.key = new List<string> {"LocalAuthListEnabled","SendLocalListMaxLength"};
+            //req.key = new List<string> { "AllowOfflineTxForUnknownId", "AuthorizationCacheEnabled", "AuthorizeRemoteTxRequests", "BlinkRepeat", "ClockAlignedDataInterval", "ConnectionTimeOut", "GetConfigurationMaxKeys", "HeartbeatInterval", "LightIntensity", "LocalAuthorizeOffline", "LocalPreAuthorize", "MaxEnergyOnInvalidId" , "MeterValuesAlignedData", "MeterValuesAlignedDataMaxLength", "MeterValuesSampledData", "MeterValuesSampledDataMaxLength", "MeterValueSampleInterval", "MinimumStatusDuration", "NumberOfConnectors", "ResetRetries", "ConnectorPhaseRotation", "ConnectorPhaseRotationMaxLength", "StopTransactionOnEVSideDisconnect", "StopTransactionOnInvalidId", "StopTxnAlignedData", "StopTxnAlignedDataMaxLength", "StopTxnSampledData", "StopTxnSampledDataMaxLength", "SupportedFeatureProfiles", "SupportedFeatureProfilesMaxLength", "TransactionMessageAttempts", "TransactionMessageRetryInterval", "UnlockConnectorOnEVSideDisconnect", "WebSocketPingInterval", "LocalAuthListEnabled", "LocalAuthListMaxLength", "SendLocalListMaxLength", "ReserveConnectorZeroSupported", "ChargeProfileMaxStackLevel", "ChargingScheduleAllowedChargingRateUnit", "ChargingScheduleMaxPeriods", "ConnectorSwitch3to1PhaseSupported", "MaxChargingProfilesInstalled" };
+            //req.key = new List<string> ();
+            req.key = new List<string> { "AllowOfflineTxForUnknownId", "AuthorizationCacheEnabled", "LocalAuthListEnabled", "SendLocalListMaxLength", "ConnectionTimeOut", "HeartbeatInterval", "test1", "test2" };
+
+            return req;
+        }
+
+        /// <summary>
+        /// 設定 電樁槍號 availability 改變
+        /// </summary>
+        public IRequest MakeChangeAvailabilityRequest(int connectorId)
+        {
+            ChangeAvailabilityRequest req = new ChangeAvailabilityRequest();
+            req.connectorId = connectorId;
+            req.type = AvailabilityType.Operative;
+            return req;
+        }
+
+        #endregion Core
+
+        #region RemoteTrigger
+
+        /// <summary>
+        /// Trigger 電樁 送相關的message
+        /// </summary>
+        public IRequest MakeTriggerMessageRequest(MessageTrigger messageType, int connectorId)
+        {
+            TriggerMessageRequest req = new TriggerMessageRequest();
+            req.requestedMessage = messageType;
+            req.connectorId = connectorId;
+            return req;
+        }
+
+        #endregion RemoteTrigger
+
+        #region FirmwareManagement
+
+        /// <summary>
+        /// 請電樁送diagnostic information
+        /// </summary>
+        static public IRequest MakeGetDiagnosticsRequest(bool ac)
+        {
+            GetDiagnosticsRequest req = new GetDiagnosticsRequest();
+
+            if (ac == true)
+                req.location = new Uri("ftp://ipc_ui:pht2016@ftp.phihong.com.tw/AC/log/");
+            else
+                req.location = new Uri("ftp://ipc_ui:pht2016@ftp.phihong.com.tw/DC/log/");
+
+            return req;
+        }
+
+        /// <summary>
+        /// 請電樁 update firmware
+        /// </summary>
+        static public IRequest MakeOCPPUpdateFirmwareRequest(string fileurl)
+        {
+            UpdateFirmwareRequest req = new UpdateFirmwareRequest();
+            req.location = new Uri(fileurl);
+            req.retrieveDate = DateTime.Now;
+            return req;
+        }
+
+        #endregion FirmwareManagement
+
+        #region LocalAuthListManagement
+
+        /// <summary>
+        /// 請電樁送 the Local Authorization Lis 版本號
+        /// </summary>
+        static public IRequest MakeGetLocalListVersionRequest()
+        {
+            GetLocalListVersionRequest req = new GetLocalListVersionRequest();
+
+            return req;
+        }
+
+        /// <summary>
+        /// 送給電樁 Local Authorization Lis 版本號 跟 idTags
+        /// </summary>
+        static public IRequest MakeSendLocalListRequest(List<OCPPServer.Common.LocalAuthorization> localauth, uint version, byte type)
+        {
+            SendLocalListRequest req = new SendLocalListRequest();
+            req.localAuthorizationList = new List<AuthorizationData>();
+            foreach (var localAuthorization in localauth)
+            {
+                AuthorizationData athData = new AuthorizationData();
+                athData.IdToken = localAuthorization.CardNumber;
+                IdTagInfo idTagInfo = new IdTagInfo();
+                idTagInfo.expiryDate = DateTime.Parse(localAuthorization.ExpiryDate);
+                idTagInfo.parentIdTag = localAuthorization.CardNumber;
+                idTagInfo.status = (AuthorizationStatus)localAuthorization.Status;
+                athData.idTagInfo = idTagInfo;
+                req.localAuthorizationList.Add(athData);
+            }
+            req.listVersion = Convert.ToInt32(version);
+
+            //0:全部更新 ,1:差异更新
+            if (type == 0)
+            {
+                req.updateType = UpdateType.Full;
+            }
+            else
+            {
+                req.updateType = UpdateType.Differential;
+            }
+
+            return req;
+        }
+
+        #endregion LocalAuthListManagement
+
+        #region Reservation
+
+        /// <summary>
+        /// 預約電樁槍號
+        /// </summary>
+        public IRequest MakeReserveNowRequest(int connectorId, string memberId, bool reserveConnectorZeroSupported, int reservationId)
+        {
+            int RetainMinute = 5;
+            ReserveNowRequest req = new ReserveNowRequest();
+
+            if (reserveConnectorZeroSupported == true)
+                req.connectorId = 0;
+            else
+                req.connectorId = connectorId;
+
+            req.expiryDate = DateTime.Now.AddMinutes(RetainMinute);
+            req.parentIdTag = memberId;
+            req.idTag = memberId;
+            req.reservationId = reservationId;
+
+            return req;
+        }
+
+        /// <summary>
+        /// 取消預約電樁槍號
+        /// </summary>
+        public IRequest MakeCancelReservationRequest(int reservationId)
+        {
+            CancelReservationRequest req = new CancelReservationRequest();
+            req.reservationId = reservationId;
+            return req;
+        }
+
+        #endregion Reservation
+
+        #region SmartCharging
+
+        /// <summary>
+        /// 設定 Charging Profile
+        /// </summary>
+        static public IRequest MakeSetChargingProfileRequest(int connectorId, int transactionId, int chargingProfileId, string purpose)
+        {
+            SetChargingProfileRequest req = new SetChargingProfileRequest();
+            req.connectorId = connectorId;
+            req.csChargingProfiles = new csChargingProfiles();
+            req.csChargingProfiles.chargingProfileId = chargingProfileId;
+            req.csChargingProfiles.transactionId = transactionId;
+
+            if (purpose.Contains("ChargePointMaxProfile"))
+            {
+                req.csChargingProfiles.stackLevel = 2;
+                req.csChargingProfiles.chargingProfilePurpose = ChargingProfilePurposeType.ChargePointMaxProfile;
+                req.csChargingProfiles.chargingProfileKind = ChargingProfileKindType.Recurring;
+                req.csChargingProfiles.recurrencyKind = RecurrencyKindType.Daily;
+            }
+            else if (purpose.Contains("TxDefaultProfile"))
+            {
+                req.csChargingProfiles.stackLevel = 1;
+                req.csChargingProfiles.chargingProfilePurpose = ChargingProfilePurposeType.TxDefaultProfile;
+                req.csChargingProfiles.chargingProfileKind = ChargingProfileKindType.Absolute;
+                req.csChargingProfiles.recurrencyKind = RecurrencyKindType.Weekly;
+            }
+            else if (purpose.Contains("TxProfile"))
+            {
+                req.csChargingProfiles.stackLevel = 0;
+                req.csChargingProfiles.chargingProfilePurpose = ChargingProfilePurposeType.TxProfile;
+                req.csChargingProfiles.chargingProfileKind = ChargingProfileKindType.Relative;
+                req.csChargingProfiles.recurrencyKind = RecurrencyKindType.Daily;
+            }
+
+            req.csChargingProfiles.validFrom = DateTime.Now;
+            req.csChargingProfiles.validTo = DateTime.Now.AddHours(2);
+            req.csChargingProfiles.chargingSchedule = new ChargingSchedule();
+            req.csChargingProfiles.chargingSchedule.chargingRateUnit = ChargingRateUnitType.A;
+            req.csChargingProfiles.chargingSchedule.chargingSchedulePeriod = new List<ChargingSchedulePeriod>();
+            req.csChargingProfiles.chargingSchedule.duration = 120;
+            req.csChargingProfiles.chargingSchedule.minChargingRate = 0.0m;
+            req.csChargingProfiles.chargingSchedule.startSchedule = DateTime.Now;
+            req.csChargingProfiles.chargingSchedule.chargingSchedulePeriod = new List<ChargingSchedulePeriod>();
+            req.csChargingProfiles.chargingSchedule.chargingSchedulePeriod.Add(new ChargingSchedulePeriod() { startPeriod = 0, limit = 11000.0m, numberPhases = 3 });
+            req.csChargingProfiles.chargingSchedule.chargingSchedulePeriod.Add(new ChargingSchedulePeriod() { startPeriod = 28800, limit = 6000.0m, numberPhases = 3 });
+            req.csChargingProfiles.chargingSchedule.chargingSchedulePeriod.Add(new ChargingSchedulePeriod() { startPeriod = 72000, limit = 6000.0m, numberPhases = 3 });
+
+            // Add parts to the list.
+            //req.csChargingProfiles.chargingSchedule.chargingSchedulePeriod.Add(new ChargingSchedulePeriod() { startPeriod = 5, limit = 8.1m, numberPhases = 3 });
+
+            return req;
+        }
+
+        /// <summary>
+        /// 設定 Charging Profile
+        /// </summary>
+        static public IRequest MakeSetChargingProfileRequest(BaseCmd cmd, int transactionId)
+        {
+            SetChargingProfileRequest req = new SetChargingProfileRequest();
+            var c = cmd as Cmd1013;
+
+            if(c.GunSerNo == 255)
+                req.connectorId = 0;
+            else
+                req.connectorId = c.GunSerNo + 1;
+
+            req.csChargingProfiles = new csChargingProfiles();
+            req.csChargingProfiles.chargingProfileId = c.ChargingProfileId;
+            req.csChargingProfiles.transactionId = transactionId;
+
+
+            req.csChargingProfiles.stackLevel = c.stackLevel;
+            req.csChargingProfiles.chargingProfilePurpose = (c.ChargingProfilePurpose == 0) ? ChargingProfilePurposeType.ChargePointMaxProfile : ((c.ChargingProfilePurpose == 1) ? ChargingProfilePurposeType.TxDefaultProfile : ChargingProfilePurposeType.TxProfile);
+            req.csChargingProfiles.chargingProfileKind = (c.ChargingProfileKind == 0) ? ChargingProfileKindType.Absolute : ((c.ChargingProfileKind == 1) ? ChargingProfileKindType.Recurring : ChargingProfileKindType.Relative);
+            req.csChargingProfiles.recurrencyKind = (c.RecurrencyKind == 0) ? RecurrencyKindType.Daily : RecurrencyKindType.Weekly;
+
+            req.csChargingProfiles.validFrom = c.ValidFrom.Value;
+            req.csChargingProfiles.validTo = c.ValidTo.Value;
+            req.csChargingProfiles.chargingSchedule = new ChargingSchedule();
+            req.csChargingProfiles.chargingSchedule.chargingRateUnit = (c.ChargingRateUnit == 0) ?  ChargingRateUnitType.W: ChargingRateUnitType.A;
+            req.csChargingProfiles.chargingSchedule.duration = Convert.ToInt32(c.Duration);
+            req.csChargingProfiles.chargingSchedule.minChargingRate = c.MinChargingRate.RealValue;
+            req.csChargingProfiles.chargingSchedule.startSchedule = c.StartSchedule.Value;
+            
+           
+
+            req.csChargingProfiles.chargingSchedule.chargingSchedulePeriod = new List<ChargingSchedulePeriod>();
+
+            var chargingPeriod = c.ChargingSchedulePeriod as List<ChargingSchedulePeriodParameter>;
+
+            foreach (var csp in chargingPeriod)
+            {
+                req.csChargingProfiles.chargingSchedule.chargingSchedulePeriod.Add(new ChargingSchedulePeriod() { startPeriod = Convert.ToInt32(csp.StartPeriod), limit =  csp.Limit.RealValue, numberPhases = 3 });
+            }
+       
+            return req;
+        }
+
+        /// <summary>
+        /// 設定Clear Charging Profile
+        /// </summary>
+        public IRequest MakeClearChargingProfileRequest(int connectorId)
+        {
+            ClearChargingProfileRequest req = new ClearChargingProfileRequest();
+            req.connectorId = 0;
+            req.id = 1;
+            req.stackLevel = 1;
+            req.chargingProfilePurpose = ChargingProfilePurposeType.TxDefaultProfile;
+            return req;
+        }
+
+        /// <summary>
+        /// 設定Clear Charging Profile
+        /// </summary>
+        public IRequest MakeClearChargingProfileRequest(BaseCmd cmd)
+        {
+            ClearChargingProfileRequest req = new ClearChargingProfileRequest();
+            var c = cmd as Cmd1015;
+            if (c.GunSerNo == 255)
+                req.connectorId = 0;
+            else
+                req.connectorId = c.GunSerNo + 1;
+
+            req.id = c.ChargingProfileId;
+            req.stackLevel = c.stackLevel;
+            req.chargingProfilePurpose = (c.ChargingProfilePurpose == 0) ? ChargingProfilePurposeType.ChargePointMaxProfile : ((c.ChargingProfilePurpose == 1) ? ChargingProfilePurposeType.TxDefaultProfile : ChargingProfilePurposeType.TxProfile);
+            return req;
+        }
+
+        /// <summary>
+        /// 取得電樁 Composite Charging Schedule
+        /// </summary>
+        public IRequest MakeGetCompositeScheduleRequest(int connectorId)
+        {
+            GetCompositeScheduleRequest req = new GetCompositeScheduleRequest();
+            req.connectorId = connectorId;
+            req.duration = 120;
+            req.chargingRateUnit = ChargingRateUnitType.A;
+            return req;
+        }
+
+
+
+        #endregion SmartCharging
+
+        #region DataTransfer
+
+        /// <summary>
+        /// 向充電樁下發升級指令
+        /// </summary>
+        public IRequest MakeUpdateFirmware(int type, ProgramParam param, UploadFile uploadFile)
+        {
+            UpdateFirmware updatefirm = new UpdateFirmware();
+            updatefirm.type = (ProgramType)type;
+            updatefirm.param = param;
+            updatefirm.url = @"http://" + uploadFile.FileUrl;
+            updatefirm.md5 = uploadFile.FileMD5.ToUpper();
+
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "UpdateFirmware";
+            req.data = JsonConvert.SerializeObject(updatefirm);
+
+            return req;
+        }
+
+        /// <summary>
+        /// 設定簡易版充電電量
+        /// </summary>
+        static public IRequest MakeSendChargingConfigRequest(BaseCmd cmd)
+        {
+            var c = cmd as Cmd1017;
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = c.vendorId;
+            req.messageId = "SetChargingConfig";
+            req.data = JsonConvert.SerializeObject( new ChargingRate() {  chargingRateUnit="A", limit=c.limit});
+            return req;
+        }
+
+        /// <summary>
+        /// 取得簡易版充電電量
+        /// </summary>
+        static public IRequest MakeSendGetChargingConfigRequest(BaseCmd cmd)
+        {
+            var c = cmd as Cmd1019;
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = c.vendorId;
+            req.messageId = "GetChargingConfig";
+         
+            return req;
+        }
+
+        /// <summary>
+        /// 設定樁的夥伴代碼
+        /// </summary>
+        public IRequest MakeSendPartnerPoleIdRequest(string partnerPoleId)
+        {
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "SendPartnerPoleId";
+            req.data = partnerPoleId;
+            return req;
+        }
+
+        /// <summary>
+        ///  將電樁轉伺服器
+        /// </summary>
+        public IRequest MakeServerDomainNameRequest(string ServerDomainName)
+        {
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "ServerDomainName";
+            req.data = ServerDomainName;
+            return req;
+        }
+
+        /// <summary>
+        ///  下發全天電費
+        /// </summary>
+        public IRequest MakeAllDayUniformElectricBillRequest(decimal pricePerKWH)
+        {
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "AllDayUniformElectricBill";
+            req.data = System.Convert.ToString((double)pricePerKWH);
+            return req;
+        }
+
+        /// <summary>
+        ///  下發全天 48 時段電費
+        /// </summary>
+        public IRequest MakeAllDaySectionElectricBillRequest(List<double> sectionElectricBill)
+        {
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "AllDaySectionElectricBill";
+            req.data = JsonConvert.SerializeObject(sectionElectricBill);
+
+            return req;
+        }
+
+        /// <summary>
+        ///   下發充電服務費
+        /// </summary>
+        public IRequest MakeFeePerKWHRequest(decimal pricePerKWH)
+        {
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "FeePerKWH";
+            req.data = System.Convert.ToString((double)pricePerKWH);
+            return req;
+        }
+
+        /// <summary>
+        ///   下發帳戶餘額
+        /// </summary>
+        public IRequest MakeAccountBalanceRequest(string uniqueId, decimal balance, int VehicleType)
+        {
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "AccountBalance";
+
+            AccountBalance account = new AccountBalance();
+            account.uniqueId = uniqueId;
+            account.balance = (double)balance;
+            account.VehicleType = VehicleType;
+            req.data = JsonConvert.SerializeObject(account);
+            return req;
+        }
+
+        /// <summary>
+        ///   下發定時充電資訊
+        /// </summary>
+        public IRequest MakeAutoChargeReservationRequest(AutoChargeReservation autoChargeReservationParam)
+        {
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "AutoChargeReservation";
+
+            req.data = JsonConvert.SerializeObject(autoChargeReservationParam);
+            return req;
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="gunNo"></param>
+        /// <param name="Type"></param>
+        /// <returns></returns>
+        public IRequest MakeCustomCommandRequest(int gunNo, int Type)
+        {
+            DataTransferRequest req = new DataTransferRequest();
+            req.vendorId = "Phihong";
+            req.messageId = "SetCustomCMD";
+
+            CustomCommand customCommandParam = new CustomCommand();
+            customCommandParam.connectorId = gunNo;
+            customCommandParam.CommandType = (Type == 18) ? OCPP_Packet.Packet.Status.CustomCommandType.QRCodeFlash : ((Type == 19) ? OCPP_Packet.Packet.Status.CustomCommandType.DisableCharger : OCPP_Packet.Packet.Status.CustomCommandType.EnableCharger);
+
+            req.data = JsonConvert.SerializeObject(customCommandParam);
+            return req;
+        }
+
+        #endregion DataTransfer
+    }
+}

+ 2556 - 0
OCPPServer/Handler/OCPPMessageHandler.cs

@@ -0,0 +1,2556 @@
+using Evcb.Domain.Model;
+using Evcb.Domain.mongo;
+using Evcb.Repository;
+using Evcb.Service;
+using Evcb.Utility;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NLog;
+using OCPP_Packet.Packet.DataTransfer;
+using OCPPPacket.Packet.DataTransfer;
+using OCPPPacket.Packet.Feature;
+using OCPPPacket.Packet.Feature.Core;
+using OCPPPacket.Packet.Feature.FirmwareManagement;
+using OCPPPacket.Packet.Feature.LocalAuthListManagement;
+using OCPPPacket.Packet.Feature.RemoteTrigger;
+using OCPPPacket.Packet.Feature.Reservation;
+using OCPPPacket.Packet.Feature.SmartCharging;
+using OCPPPacket.Packet.Messages;
+using OCPPPacket.Packet.Messages.Basic;
+using OCPPPacket.Packet.Messages.Core;
+using OCPPPacket.Packet.Messages.FirmwareManagement;
+using OCPPPacket.Packet.Messages.LocalAuthListManagement;
+using OCPPPacket.Packet.Messages.RemoteTrigger;
+using OCPPPacket.Packet.Messages.Reservation;
+using OCPPPacket.Packet.Messages.SmartCharging;
+using OCPPPacket.Packet.Status;
+using OCPPServer.Common;
+using OCPPServer.Protocol;
+using Packet.Cmd;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data.Entity;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace OCPPServer.Handler
+{
+    public class OCPPMessageHandler
+    {
+        private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        private FeatureHandler featureHandler;
+
+        //private CmdHelper CmdHelper;
+        private List<string> _cmd202lst = new List<string>();
+
+        private static readonly Object _clientQueue = new object();
+
+        static private MakeConfirmationHandler makeConfirmation = new MakeConfirmationHandler();
+        static private MakeRequestHandler makeRequest = new MakeRequestHandler();
+
+        private string ApiUrl { get; set; }
+
+        //設定OCPP Message Constant
+        private const int INDEX_MESSAGEID = 0;
+
+        private const int TYPENUMBER_CALL = 2;
+        private const int TYPENUMBER_CALLRESULT = 3;
+        private const int TYPENUMBER_CALLERROR = 4;
+
+        private const int INDEX_CALL_ACTION = 2;
+        private const int INDEX_CALL_PAYLOAD = 3;
+        private const int INDEX_CALLRESULT_PAYLOAD = 2;
+        private const int INDEX_CALLERROR_ERRORCODE = 2;
+        private const int INDEX_CALLERROR_DESCRIPTION = 3;
+        private const int INDEX_CALLERROR_PAYLOAD = 4;
+        private const int INDEX_UNIQUEID = 1;
+
+        /// <summary>
+        /// 初始化 OCPPMessageHandler Class
+        /// </summary>
+        public OCPPMessageHandler()
+        {
+            this.featureHandler = new FeatureHandler();
+            ApiUrl = ConfigurationManager.AppSettings.Get("ApiUrl");
+
+            #region Core
+
+            //加入core feature
+            featureHandler.AddFeatureProfile(new AuthorizeFeature());
+            featureHandler.AddFeatureProfile(new BootNotificationFeature());
+            featureHandler.AddFeatureProfile(new ChangeAvailabilityFeature());
+            featureHandler.AddFeatureProfile(new ChangeConfigurationFeature());
+            featureHandler.AddFeatureProfile(new ClearCacheFeature());
+            featureHandler.AddFeatureProfile(new DataTransferFeature());
+            featureHandler.AddFeatureProfile(new GetConfigurationFeature());
+            featureHandler.AddFeatureProfile(new HeartbeatFeature());
+            featureHandler.AddFeatureProfile(new StartTransactionFeature());
+            featureHandler.AddFeatureProfile(new StatusNotificationFeature());
+            featureHandler.AddFeatureProfile(new MeterValuesFeature());
+            featureHandler.AddFeatureProfile(new RemoteStartTransactionFeature());
+            featureHandler.AddFeatureProfile(new RemoteStopTransactionFeature());
+            featureHandler.AddFeatureProfile(new ResetFeature());
+            featureHandler.AddFeatureProfile(new StopTransactionFeature());
+            featureHandler.AddFeatureProfile(new UnlockConnectorFeature());
+
+            #endregion Core
+
+            #region FirmwareManagement
+
+            //加入FirmwareManagement feature
+            featureHandler.AddFeatureProfile(new GetDiagnosticsFeature());
+            featureHandler.AddFeatureProfile(new DiagnosticsStatusNotificationFeature());
+            featureHandler.AddFeatureProfile(new FirmwareStatusNotificationFeature());
+            featureHandler.AddFeatureProfile(new UpdateFirmwareFeature());
+
+            #endregion FirmwareManagement
+
+            #region LocalAuthListManagement
+
+            //加入LocalAuthListManagement feature
+            featureHandler.AddFeatureProfile(new GetLocalListVersionFeature());
+            featureHandler.AddFeatureProfile(new SendLocalListFeature());
+
+            #endregion LocalAuthListManagement
+
+            #region RemoteTrigger
+
+            //加入RemoteTrigger feature
+            featureHandler.AddFeatureProfile(new TriggerMessageFeature());
+
+            #endregion RemoteTrigger
+
+            #region Reservation
+
+            //加入Reservation feature
+            featureHandler.AddFeatureProfile(new CancelReservationFeature());
+            featureHandler.AddFeatureProfile(new ReserveNowFeature());
+
+            #endregion Reservation
+
+            #region SmartCharging
+
+            //加入SmartCharging feature
+            featureHandler.AddFeatureProfile(new ClearChargingProfileFeature());
+            featureHandler.AddFeatureProfile(new GetCompositeScheduleFeature());
+            featureHandler.AddFeatureProfile(new SetChargingProfileFeature());
+
+            #endregion SmartCharging
+        }
+
+        /// <summary>
+        /// 解析message type,歸類為Call Message 或 CALLRESULT Message或 CALLERROR Message
+        /// </summary>
+        /// <param name="message">OCPP Message</param>
+        /// <returns></returns>
+        public BaseMessage Parse(string message)
+        {
+            const int INDEX_MESSAGEID = 0;
+            const int TYPENUMBER_CALL = 2;
+            const int TYPENUMBER_CALLRESULT = 3;
+            const int TYPENUMBER_CALLERROR = 4;
+
+            const int INDEX_CALL_ACTION = 2;
+            const int INDEX_CALL_PAYLOAD = 3;
+            const int INDEX_CALLRESULT_PAYLOAD = 2;
+            const int INDEX_CALLERROR_ERRORCODE = 2;
+            const int INDEX_CALLERROR_DESCRIPTION = 3;
+            const int INDEX_CALLERROR_PAYLOAD = 4;
+            const int INDEX_UNIQUEID = 1;
+
+            try
+            {
+                var array = JsonConvert.DeserializeObject<JArray>(message);
+                BaseMessage msg = null;
+                switch ((int)array[INDEX_MESSAGEID])
+                {
+                    case TYPENUMBER_CALL:
+                        {
+                            CallMessage call = new CallMessage();
+                            call.action = array[INDEX_CALL_ACTION].ToString();
+                            call.payload = array[INDEX_CALL_PAYLOAD].ToString().Replace("\r\n", "");
+                            msg = call;
+                        }
+                        break;
+
+                    case TYPENUMBER_CALLRESULT:
+                        {
+                            CallResultMessage callResult = new CallResultMessage();
+                            callResult.payload = array[INDEX_CALLRESULT_PAYLOAD].ToString().Replace("\r\n", "");
+                            msg = callResult;
+                        }
+                        break;
+
+                    case TYPENUMBER_CALLERROR:
+                        {
+                            CallErrorMessage callError = new CallErrorMessage();
+                            callError.ErrorCode = array[INDEX_CALLERROR_ERRORCODE].ToString();
+                            callError.ErrorDescription = array[INDEX_CALLERROR_DESCRIPTION].ToString();
+                            //假如沒有error details ,判斷CallError Message的payload 是 an empty object \{}.
+                            if (array.Count > INDEX_CALLERROR_PAYLOAD)
+                            {
+                                callError.payload = array[INDEX_CALLERROR_PAYLOAD].ToString().Replace("\r\n", "");
+                            }
+                            msg = callError;
+                        }
+                        break;
+
+                    default:
+                        throw new Exception("Message Type notSupported");
+                }
+                msg.id = array[INDEX_UNIQUEID].ToString();
+                return msg;
+            }
+            catch (Exception ex)
+            {
+                throw new Exception(string.Format("Error=>Communicator.Parse: {0}", ex.ToString()));
+            }
+        }
+
+        /// <summary>
+        /// 解壓縮Payload為OCPP Message
+        /// </summary>
+        /// <param name="payload">OCPP Message Payload</param>
+        /// <param name="type">OCPP Message Type</param>
+        /// <returns></returns>
+        private object UnpackPayload(object payload, Type type)
+        {
+            var ocppMessage = JsonConvert.DeserializeObject(payload.ToString(), type);
+            return ocppMessage;
+        }
+
+        /// <summary>
+        /// 壓縮OCPP Message為Payload
+        /// </summary>
+        /// <param name="ocppMessage"></param>
+        /// <returns></returns>
+        private object PackPayload(object ocppMessage)
+        {
+            return JsonConvert.SerializeObject(ocppMessage);
+        }
+
+        /// <summary>
+        /// 透過OCPP Message 的UniqueId,從client queue 取出之前後台送出的request, 透過取出的request找出相對應的confirmation type
+        /// </summary>
+        /// <param name="queue">Messgae queue</param>
+        /// <param name="uniqueId">OCPP Message UniqueId</param>
+        /// <param name="requestObj">OCPP Request Message</param>
+        /// <returns></returns>
+        private Type GetConfirmationType(ref Queue queue, string uniqueId, out IRequest requestObj)
+        {
+            IRequest request = queue.RestoreRequest(uniqueId);
+            if (request == null)
+            {
+                requestObj = null;
+                return null;
+            }
+            Feature feature = this.featureHandler.FindFeatureByRequest(request.GetType());
+            requestObj = request;
+            return feature.GetConfirmationType();
+        }
+
+        //處理OCPP errorCode, 相對應到資料庫ChargingRecordStatus的Warning欄位
+        //DC:
+        //ConnectorLockFailure: 90000,EVCommunicationError: 90001,GroundFailure: 90002,HighTemperature: 90003,
+        //InternalError: 90004,LocalListConflict: 90005,NoError: 90006,OtherError: 90007,OverCurrentFailure: 90008,
+        //OverVoltage: 90009, PowerMeterFailure: 90010,PowerSwitchFailure: 90011,ReaderFailure: 90012,ResetFailure: 90013,
+        //UnderVoltage: 90014, WeakSignal: 90015
+        //AC:
+        //ConnectorLockFailure: 110000,EVCommunicationError: 110001,GroundFailure: 110002,HighTemperature: 110003,
+        //InternalError: 110004,LocalListConflict: 110005,NoError: 110006,OtherError: 110007,OverCurrentFailure: 110008,
+        //OverVoltage: 110009, PowerMeterFailure: 110010,PowerSwitchFailure: 110011,ReaderFailure: 110012,ResetFailure: 110013,
+        //UnderVoltage: 110014, WeakSignal: 110015
+        /// <summary>
+        /// 處理 Error Code
+        /// </summary>
+        /// <param name="typeAC"></param>
+        /// <param name="errorCode"></param>
+        /// <returns></returns>
+        private int ProcessErrorCode(bool typeAC, ChargePointErrorCode errorCode)
+        {
+            int warning = 0;
+            switch (errorCode)
+            {
+                case ChargePointErrorCode.ConnectorLockFailure:
+                    if (typeAC == true)
+                        warning = 110000; // AC
+                    else
+                        warning = 90000;  // DC
+                    break;
+
+                case ChargePointErrorCode.EVCommunicationError:
+                    if (typeAC == true)
+                        warning = 110001; // AC
+                    else
+                        warning = 90001;  // DC
+                    break;
+
+                case ChargePointErrorCode.GroundFailure:
+                    if (typeAC == true)
+                        warning = 110002; // AC
+                    else
+                        warning = 90002;  // DC
+                    break;
+
+                case ChargePointErrorCode.HighTemperature:
+                    if (typeAC == true)
+                        warning = 110003; // AC
+                    else
+                        warning = 90003;  // DC
+                    break;
+
+                case ChargePointErrorCode.InternalError:
+                    if (typeAC == true)
+                        warning = 110004; // AC
+                    else
+                        warning = 90004;  // DC
+                    break;
+
+                case ChargePointErrorCode.LocalListConflict:
+                    if (typeAC == true)
+                        warning = 110005; // AC
+                    else
+                        warning = 90005;  // DC
+                    break;
+
+                case ChargePointErrorCode.NoError:
+                    if (typeAC == true)
+                        warning = 110006; // AC
+                    else
+                        warning = 90006;  // DC
+                    break;
+
+                case ChargePointErrorCode.OtherError:
+                    if (typeAC == true)
+                        warning = 110007; // AC
+                    else
+                        warning = 90007;  // DC
+                    break;
+
+                case ChargePointErrorCode.OverCurrentFailure:
+                    if (typeAC == true)
+                        warning = 110008; // AC
+                    else
+                        warning = 90008;  // DC
+                    break;
+
+                case ChargePointErrorCode.OverVoltage:
+                    if (typeAC == true)
+                        warning = 110009; // AC
+                    else
+                        warning = 90009;  // DC
+                    break;
+
+                case ChargePointErrorCode.PowerMeterFailure:
+                    if (typeAC == true)
+                        warning = 110010; // AC
+                    else
+                        warning = 90010;  // DC
+                    break;
+
+                case ChargePointErrorCode.PowerSwitchFailure:
+                    if (typeAC == true)
+                        warning = 110011; // AC
+                    else
+                        warning = 90011;  // DC
+                    break;
+
+                case ChargePointErrorCode.ReaderFailure:
+                    if (typeAC == true)
+                        warning = 110012; // AC
+                    else
+                        warning = 90012;  // DC
+                    break;
+
+                case ChargePointErrorCode.ResetFailure:
+                    if (typeAC == true)
+                        warning = 110013; // AC
+                    else
+                        warning = 90013;  // DC
+                    break;
+
+                case ChargePointErrorCode.UnderVoltage:
+                    if (typeAC == true)
+                        warning = 110014; // AC
+                    else
+                        warning = 90014;  // DC
+                    break;
+
+                case ChargePointErrorCode.WeakSignal:
+                    if (typeAC == true)
+                        warning = 110015; // AC
+                    else
+                        warning = 90015;  // DC
+                    break;
+
+                default:
+                    break;
+            }
+            return warning;
+        }
+
+        /// <summary>
+        /// 產生 random 20 character alphanumeric strings
+        /// </summary>
+        /// <returns></returns>
+        private string GenerateRandomString()
+        {
+            var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+            var random = new Random();
+            var list = Enumerable.Repeat(0, 20).Select(x => chars[random.Next(chars.Length)]);
+            string finalString = string.Join("", list);
+            return finalString;
+        }
+
+        /// <summary>
+        /// 處理 Server Command
+        /// </summary>
+        /// <param name="cmd">server command</param>
+        /// <param name="machineCustomId">電樁樁號</param>
+        /// <param name="queue">Messgae queue</param>
+        /// <param name="configurationKey">Configuration Key</param>
+        /// <param name="reservationId">預約卡號</param>
+        /// <param name="chargingProfileId">充電檔案</param>
+        /// <param name="messageType">Message Type</param>
+        /// <param name="db">Data Base Entity</param>
+        /// <returns></returns>
+        public string HandleServerCommand(BaseCmd cmd, string machineCustomId, ref Queue queue, ref List<KeyValueType> configurationKey, int reservationId, int chargingProfileId, out string messageType, PhihongDbContext db)
+        {
+            string result = string.Empty;
+            messageType = string.Empty;
+            //設定心跳時間和心跳的timeout次數
+            if (cmd is Cmd1001)
+            {
+                var c = cmd as Cmd1001;
+                if (c.ParamIndex == 21)
+                {
+                    var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+                    if (machine != null)
+                    {
+                        IRequest req = MakeRequestHandler.MakeChangeConfigurationRequest("HeartbeatInterval", machine.HeartbeatInterval.ToString());
+                        string uuid = String.Empty;
+                        lock (_clientQueue)
+                        {
+                            uuid = queue.store(req);
+                        }
+                        messageType = "ChangeConfiguration Request(HeartbeatInterval)";
+                        result = GenerateCall(uuid, Actions.ChangeConfiguration.ToString(), req);
+                    }
+                }
+            }
+
+            //校時
+            if (cmd is Cmd1003)
+            {
+                var c = cmd as Cmd1003;
+                var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+
+                if (machine != null)
+                {
+                    //確認發送是校時參數,就修改校時時間為封包送出時間
+                    if (c.ParamIndex == 2)
+                    {
+                        IRequest req = MakeRequestHandler.MakeChangeConfigurationRequest("HeartbeatInterval", "1");
+                        string uuid = String.Empty;
+                        lock (_clientQueue)
+                        {
+                            uuid = queue.store(req);
+                        }
+
+                        messageType = "ChangeConfiguration Request(HeartbeatInterval)";
+                        result = GenerateCall(uuid, Actions.ChangeConfiguration.ToString(), req);
+                    }
+
+                    //確認發送設定Server Domain Name
+                    if (c.ParamIndex == 11)
+                    {
+                        //OCPP 不支援Server Domain Name
+                        messageType = "Server Domain Name Request";
+                        result = String.Empty;
+                    }
+
+                    //確認發送設定樁的夥伴代碼
+                    if (c.ParamIndex == 12)
+                    {
+                        IRequest req = makeRequest.MakeSendPartnerPoleIdRequest(machine.PartnerPoleId);
+                        string uuid = String.Empty;
+                        lock (_clientQueue)
+                        {
+                            uuid = queue.store(req);
+                        }
+
+                        messageType = "DataTransfer Request(SendPartnerPoleId)";
+                        result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+                    }
+                }
+            }
+
+            //Remote結束充電
+            if (cmd is Cmd1005)
+            {
+                var c = cmd as Cmd1005;
+                //int transactionId = 0;
+                var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+
+                //確認發送停止充電
+                if (c.ParamIndex == 2)
+                {
+                    if (machine != null)
+                    {
+                        var gunStatusNow = db.MachineGun.Where(x => x.GunSerNo == c.GunSerNo
+                         && x.MachineId == machine.Id).AsNoTracking().FirstOrDefault();
+
+                        //如果是 DC且狀態等於 2(充電中)  或是 AC或 HMI 且在充電中
+                        if ((machine.MachineModelId == 2 && gunStatusNow.Status == 2) || (machine.MachineModelId != 2 && (gunStatusNow.Status == 2 || gunStatusNow.Status == 3)))
+                        {
+                            var record = db.ChargingRecord.Where(x => x.GunSerNo == c.GunSerNo
+                            && x.MachineId == machine.Id
+                            && x.ClientStartChargingDateTime == null
+                            && x.ClientEndChargingDateTime == null
+                            && x.EndChargingDateTime == null
+                            && x.StartChargingDateTime != null
+                            && x.ChargingSerNo == gunStatusNow.ChargingSerNo).AsNoTracking().FirstOrDefault();
+                            var chargingTransaction = db.ChargingTransaction.Where(x => x.ChargingRecordId == record.Id).AsNoTracking().FirstOrDefault();
+
+                            IRequest req = makeRequest.MakeRemoteStopTransactionRequest(chargingTransaction.TransactionId);
+                            //client.RemoteCharging = true; /// Remote充電
+                            string uuid = String.Empty;
+                            lock (_clientQueue)
+                            {
+                                uuid = queue.store(req);
+                            }
+
+                            messageType = "RemoteStopTransaction Request";
+                            result = GenerateCall(uuid, Actions.RemoteStopTransaction.ToString(), req);
+                        }
+                    }
+                }
+
+                //確認發送停止預約
+                if (c.ParamIndex == 10)
+                {
+                    if (machine != null)
+                    {
+                        IRequest req = makeRequest.MakeCancelReservationRequest(reservationId);
+                        //client.RemoteCharging = true; /// Remote充電
+                        string uuid = String.Empty;
+                        lock (_clientQueue)
+                        {
+                            uuid = queue.store(req);
+                        }
+
+                        messageType = "CancelReservation Request";
+                        result = GenerateCall(uuid, Actions.CancelReservation.ToString(), req);
+                    }
+                }
+
+                //確認發送Psu Log To Ftp (發送 GetDiagnostics 訊息)
+                if (c.ParamIndex == 17)
+                {
+                    if (machine != null)
+                    {
+                        IRequest req = MakeRequestHandler.MakeGetDiagnosticsRequest(machine.AC);
+                        string uuid = String.Empty;
+                        lock (_clientQueue)
+                        {
+                            uuid = queue.store(req);
+                        }
+
+                        messageType = "GetDiagnostics Request";
+                        result = GenerateCall(uuid, Actions.GetDiagnostics.ToString(), req);
+                    }
+                }
+
+                /// 18: QRCode Flash 效果 , 19: 禁用充电桩, 20: 启用充电桩
+                if (c.ParamIndex == 18 || c.ParamIndex == 19 || c.ParamIndex == 20)
+                {
+                    if (machine != null)
+                    {
+                        var type = Convert.ToInt32(c.ParamIndex);
+                        int gunNo = 0;
+                        switch (type)
+                        {
+                            //QRCode Flash 效果
+                            case 18:
+
+                                gunNo = c.GunSerNo + 1;
+                                break;
+
+                            // 19: 禁用充电桩
+                            case 19:
+                                gunNo = 0;
+                                break;
+
+                            //20: 启用充电桩
+                            case 20:
+                                gunNo = 0;
+                                break;
+                        }
+
+                        IRequest req = makeRequest.MakeCustomCommandRequest(gunNo, type);
+                        string uuid = String.Empty;
+                        lock (_clientQueue)
+                        {
+                            uuid = queue.store(req);
+                        }
+
+                        messageType = "DataTransfer Request(CustomCommand)";
+                        result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+                    }
+                }
+            }
+
+            //Remote啟動充電 / 預約充電
+            if (cmd is Cmd1007)
+            {
+                var c = cmd as Cmd1007;
+                if (c.StartChargingType == 0) // 0:实时充电
+                {
+                    var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+                    var memberQuery = db.MemberCharging.Where(x => x.MemberId == c.ReservationCardNum).AsNoTracking().FirstOrDefault();
+                    string randomString = string.Empty;
+                    if (memberQuery == null) // member id不存在,才產生新的SelfDefinedId
+                    {
+                        randomString = GenerateRandomString();
+                        var selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault();
+                        while (selfDefinedIdQuery != null)
+                        {
+                            randomString = GenerateRandomString();
+                            selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault();
+                        }
+
+                        //將 random 20 character alphanumeric strings 與 MemberId 儲存到 Member 表格,之後處理Remote充電會透過Member表格取得 MemberId
+                        MemberMapping remotemember = new MemberMapping();
+                        remotemember.MemberId = c.ReservationCardNum;
+                        remotemember.SelfDefinedId = randomString;
+                        remotemember.CustomerId = machine.CustomerId;
+                        remotemember.CreatedOn = DateTime.Now;
+                        db.MemberCharging.Add(remotemember);
+                        db.SaveChanges();
+                    }
+                    else
+                    {
+                        randomString = memberQuery.SelfDefinedId;
+                    }
+
+                    //處理Remote充電命令,並且發送給電樁
+                    IRequest req = makeRequest.MakeRemoteStartTransactionRequest((int)c.GunSerNo + 1, randomString);
+                    string uuid = String.Empty;
+                    lock (_clientQueue)
+                    {
+                        uuid = queue.store(req);
+                    }
+
+                    messageType = "RemoteStartTransaction Request";
+                    result = GenerateCall(uuid, Actions.RemoteStartTransaction.ToString(), req);
+                }
+
+                if (c.StartChargingType == 1) // 1: 定時啟動充電
+                {
+                    var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+                    var memberQuery = db.MemberCharging.Where(x => x.MemberId == c.ReservationCardNum).AsNoTracking().FirstOrDefault();
+                    string randomString = string.Empty;
+                    if (memberQuery == null) // member id不存在,才產生新的SelfDefinedId
+                    {
+                        randomString = GenerateRandomString();
+                        var selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault();
+                        while (selfDefinedIdQuery != null)
+                        {
+                            randomString = GenerateRandomString();
+                            selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault();
+                        }
+
+                        //將 random 20 character alphanumeric strings 與 MemberId 儲存到 Member 表格,之後處理Remote充電會透過Member表格取得 MemberId
+                        MemberMapping remotemember = new MemberMapping();
+                        remotemember.MemberId = c.ReservationCardNum;
+                        remotemember.SelfDefinedId = randomString;
+                        remotemember.CustomerId = machine.CustomerId;
+                        remotemember.CreatedOn = DateTime.Now;
+                        db.MemberCharging.Add(remotemember);
+                        db.SaveChanges();
+                    }
+                    else
+                    {
+                        randomString = memberQuery.SelfDefinedId;
+                    }
+
+                    AutoChargeReservation autoChargeReservation = new AutoChargeReservation();
+                    autoChargeReservation.connectorId = (int)c.GunSerNo + 1;
+                    autoChargeReservation.chargingStrategy = (ChargingStrategy)c.ChargingStrategy;
+                    autoChargeReservation.chargingStrategyParam = (double)c.ChargingStrategyParam;
+                    autoChargeReservation.startChargingDateTime = (DateTime)c.StartChargingDateTime;
+                    autoChargeReservation.idTag = randomString;
+                    autoChargeReservation.accountBalance = (double)c.AccountBalance.RealValue;
+                    autoChargeReservation.memberName = c.MemberName;
+                    autoChargeReservation.vehicleType = (int)c.VehicleType;
+                    autoChargeReservation.reservationId = reservationId;
+
+                    //處理定時啟動充電,並且發送給電樁
+                    IRequest req = makeRequest.MakeAutoChargeReservationRequest(autoChargeReservation);
+                    string uuid = String.Empty;
+                    lock (_clientQueue)
+                    {
+                        uuid = queue.store(req);
+                    }
+
+                    messageType = "DataTransfer Request(AutoChargeReservation)";
+                    result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+                }
+
+                if (c.StartChargingType == 2) // 2:预约充电
+                {
+                    var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+                    var memberQuery = db.MemberCharging.Where(x => x.MemberId == c.ReservationCardNum).AsNoTracking().FirstOrDefault();
+                    string randomString = string.Empty;
+                    if (memberQuery == null) // member id不存在,才產生新的SelfDefinedId
+                    {
+                        randomString = GenerateRandomString();
+                        var selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault();
+                        while (selfDefinedIdQuery != null)
+                        {
+                            randomString = GenerateRandomString();
+                            selfDefinedIdQuery = db.MemberCharging.Where(x => x.SelfDefinedId == randomString).AsNoTracking().FirstOrDefault();
+                        }
+
+                        //將 random 20 character alphanumeric strings 與 MemberId 儲存到 Member 表格,之後處理Remote充電會透過Member表格取得 MemberId
+                        MemberMapping remotemember = new MemberMapping();
+                        remotemember.MemberId = c.ReservationCardNum;
+                        remotemember.SelfDefinedId = randomString;
+                        remotemember.CustomerId = machine.CustomerId;
+                        remotemember.CreatedOn = DateTime.Now;
+                        db.MemberCharging.Add(remotemember);
+                        db.SaveChanges();
+                    }
+                    else
+                    {
+                        randomString = memberQuery.SelfDefinedId;
+                    }
+
+                    var item = configurationKey.Where(a => a.key == "ReserveConnectorZeroSupported").FirstOrDefault();
+                    IRequest req = makeRequest.MakeReserveNowRequest((int)c.GunSerNo + 1, randomString, Convert.ToBoolean(item.value), reservationId);
+                    string uuid = String.Empty;
+                    lock (_clientQueue)
+                    {
+                        uuid = queue.store(req);
+                    }
+
+                    messageType = "ReserveNow Request";
+                    result = GenerateCall(uuid, Actions.ReserveNow.ToString(), req);
+                }
+            }
+
+            //后台服务器下发充电桩本地验证卡号名单命令
+            if (cmd is Cmd1009)
+            {
+                var c = cmd as Cmd1009;
+                string sLine = "";
+                if (!Directory.Exists(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UploadFiles")))
+                {
+                    Directory.CreateDirectory(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UploadFiles"));
+                }
+
+                string fileName = System.IO.Path.GetFileName(c.Url);
+                string destFilePath = System.IO.Path.Combine(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UploadFiles/"), fileName);
+                if (!Directory.Exists(destFilePath))
+                {
+                    List<Task> tList = new List<Task>();
+                    //downloading
+                    using (WebClient webClient = new WebClient())
+                    {
+                        webClient.Credentials = CredentialCache.DefaultNetworkCredentials;
+                        if (c.Url.Contains("http") == false)
+                        {
+                            c.Url = "http://" + c.Url;
+                        }
+                        tList.Add(Task.Run(() => webClient.DownloadFileTaskAsync(new Uri(c.Url), destFilePath)));
+                    }
+                    Task.WaitAll(tList.ToArray());
+                }
+
+                if (File.Exists(destFilePath))
+                {
+                    using (FileStream file = new FileStream(destFilePath, FileMode.Open, FileAccess.Read))
+                    {
+                        using (StreamReader srReader = new StreamReader(file))
+                        {
+                            sLine = srReader.ReadToEnd();
+                        }
+                    }
+                }
+
+                List<OCPPServer.Common.LocalAuthorization> localAuthorizationList = JsonConvert.DeserializeObject<List<OCPPServer.Common.LocalAuthorization>>(sLine);
+
+                IRequest req = MakeRequestHandler.MakeSendLocalListRequest(localAuthorizationList, c.VersionNo, c.Type);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "SendLocalListRequest Request";
+                result = GenerateCall(uuid, Actions.SendLocalList.ToString(), req);
+            }
+
+            if (cmd is Cmd1011)
+            {
+                IRequest req = MakeRequestHandler.MakeGetLocalListVersionRequest();
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "GetLocalListVersion Request";
+                result = GenerateCall(uuid, Actions.GetLocalListVersion.ToString(), req);
+            }
+
+            if (cmd is Cmd1013)
+            {
+                var c = cmd as Cmd1013;
+                int TransactionId = 0;
+
+                var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+
+                var gunStatusNow = db.MachineGun.Where(y => y.GunSerNo == c.GunSerNo
+                                     && y.MachineId == machine.Id).AsNoTracking().FirstOrDefault();
+
+                //充電目的檔為 TxProfile
+                if (gunStatusNow != null && c.ChargingProfilePurpose == 2)
+                {
+                    //確認電樁在充電中
+                    if (gunStatusNow.Status == 2 || gunStatusNow.Status == 3)
+                    {
+                        var chargingRecord = db.ChargingRecord.Where(x => x.ChargingSerNo == gunStatusNow.ChargingSerNo).AsNoTracking().FirstOrDefault();
+
+                        if (chargingRecord != null)
+                        {
+                            var chargingTransaction = db.ChargingTransaction.Where(x => x.MachineId == machine.Id
+                                                        && x.GunSerNo == c.GunSerNo
+                                                        && x.ChargingRecordId == chargingRecord.Id).AsNoTracking().FirstOrDefault();
+
+                            if (chargingTransaction != null)
+                            {
+                                TransactionId = chargingTransaction.TransactionId;
+                            }
+                        }
+
+                    }
+                }
+
+                IRequest req = MakeRequestHandler.MakeSetChargingProfileRequest(cmd, TransactionId);
+
+                lock (_clientQueue)
+                {
+                    queue.store(req, c.UUID);
+                }
+
+                messageType = "SetChargingProfile Request";
+                result = GenerateCall(c.UUID, Actions.SetChargingProfile.ToString(), req);
+            }
+
+            if (cmd is Cmd1015)
+            {
+                var c = cmd as Cmd1015;
+                IRequest req = makeRequest.MakeClearChargingProfileRequest(cmd);
+
+                lock (_clientQueue)
+                {
+                    queue.store(req, c.UUID);
+                }
+
+                messageType = "ClearChargingProfile Request";
+                result = GenerateCall(c.UUID, Actions.ClearChargingProfile.ToString(), req);
+            }
+
+            if (cmd is Cmd1017)
+            {
+                var c = cmd as Cmd1017;
+
+                IRequest req = MakeRequestHandler.MakeSendChargingConfigRequest(cmd);
+
+                lock (_clientQueue)
+                {
+                    queue.store(req, c.UUID);
+                }
+
+                messageType = "SetChargingConfig Request";
+                result = GenerateCall(c.UUID, Actions.DataTransfer.ToString(), req);
+            }
+
+            if (cmd is Cmd1019)
+            {
+                var c = cmd as Cmd1019;
+
+                IRequest req = MakeRequestHandler.MakeSendGetChargingConfigRequest(cmd);
+
+                lock (_clientQueue)
+                {
+                    queue.store(req, c.UUID);
+                }
+
+                messageType = "GetChargingConfig Request";
+                result = GenerateCall(c.UUID, Actions.DataTransfer.ToString(), req);
+            }
+
+            //設定每度費用
+            if (cmd is Cmd2301)
+            {
+                var c = cmd as Cmd2301;
+                IRequest req = makeRequest.MakeAllDayUniformElectricBillRequest(c.PricePerKWH.RealValue);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "DataTransfer Request(AllDayUniformElectricBill)";
+                result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+            }
+
+            //設定區段費用
+            if (cmd is Cmd2303)
+            {
+                var c = cmd as Cmd2303;
+                List<double> sectionElectricBill = new List<double>();
+                sectionElectricBill.Add((double)c.Section0000_0030.RealValue);
+                sectionElectricBill.Add((double)c.Section0030_0100.RealValue);
+                sectionElectricBill.Add((double)c.Section0100_0130.RealValue);
+                sectionElectricBill.Add((double)c.Section0130_0200.RealValue);
+                sectionElectricBill.Add((double)c.Section0200_0230.RealValue);
+                sectionElectricBill.Add((double)c.Section0230_0300.RealValue);
+                sectionElectricBill.Add((double)c.Section0300_0330.RealValue);
+                sectionElectricBill.Add((double)c.Section0330_0400.RealValue);
+                sectionElectricBill.Add((double)c.Section0400_0430.RealValue);
+                sectionElectricBill.Add((double)c.Section0430_0500.RealValue);
+                sectionElectricBill.Add((double)c.Section0500_0530.RealValue);
+                sectionElectricBill.Add((double)c.Section0530_0600.RealValue);
+                sectionElectricBill.Add((double)c.Section0600_0630.RealValue);
+                sectionElectricBill.Add((double)c.Section0630_0700.RealValue);
+                sectionElectricBill.Add((double)c.Section0700_0730.RealValue);
+                sectionElectricBill.Add((double)c.Section0730_0800.RealValue);
+                sectionElectricBill.Add((double)c.Section0800_0830.RealValue);
+                sectionElectricBill.Add((double)c.Section0830_0900.RealValue);
+                sectionElectricBill.Add((double)c.Section0900_0930.RealValue);
+                sectionElectricBill.Add((double)c.Section0930_1000.RealValue);
+                sectionElectricBill.Add((double)c.Section1000_1030.RealValue);
+                sectionElectricBill.Add((double)c.Section1030_1100.RealValue);
+                sectionElectricBill.Add((double)c.Section1100_1130.RealValue);
+                sectionElectricBill.Add((double)c.Section1130_1200.RealValue);
+                sectionElectricBill.Add((double)c.Section1200_1230.RealValue);
+                sectionElectricBill.Add((double)c.Section1230_1300.RealValue);
+                sectionElectricBill.Add((double)c.Section1300_1330.RealValue);
+                sectionElectricBill.Add((double)c.Section1330_1400.RealValue);
+                sectionElectricBill.Add((double)c.Section1400_1430.RealValue);
+                sectionElectricBill.Add((double)c.Section1430_1500.RealValue);
+                sectionElectricBill.Add((double)c.Section1500_1530.RealValue);
+                sectionElectricBill.Add((double)c.Section1530_1600.RealValue);
+                sectionElectricBill.Add((double)c.Section1600_1630.RealValue);
+                sectionElectricBill.Add((double)c.Section1630_1700.RealValue);
+                sectionElectricBill.Add((double)c.Section1700_1730.RealValue);
+                sectionElectricBill.Add((double)c.Section1730_1800.RealValue);
+                sectionElectricBill.Add((double)c.Section1800_1830.RealValue);
+                sectionElectricBill.Add((double)c.Section1830_1900.RealValue);
+                sectionElectricBill.Add((double)c.Section1900_1930.RealValue);
+                sectionElectricBill.Add((double)c.Section1930_2000.RealValue);
+                sectionElectricBill.Add((double)c.Section2000_2030.RealValue);
+                sectionElectricBill.Add((double)c.Section2030_2100.RealValue);
+                sectionElectricBill.Add((double)c.Section2100_2130.RealValue);
+                sectionElectricBill.Add((double)c.Section2130_2200.RealValue);
+                sectionElectricBill.Add((double)c.Section2200_2230.RealValue);
+                sectionElectricBill.Add((double)c.Section2230_2300.RealValue);
+                sectionElectricBill.Add((double)c.Section2300_2330.RealValue);
+                sectionElectricBill.Add((double)c.Section2330_2400.RealValue);
+                IRequest req = makeRequest.MakeAllDaySectionElectricBillRequest(sectionElectricBill);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "DataTransfer Request(AllDaySectionElectricBill)";
+                result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+            }
+
+            //設定每度服務費
+            if (cmd is Cmd2305)
+            {
+                var c = cmd as Cmd2305;
+                IRequest req = makeRequest.MakeFeePerKWHRequest(c.FeePerKWH.RealValue);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "DataTransfer Request(FeePerKWH)";
+                result = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+            }
+
+            //ocpp cmd: MessageTrigger BootNotification
+            if (cmd is Cmd9001)
+            {
+                IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.BootNotification, 1);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+                messageType = "TriggerMessage Request(BootNotification)";
+                result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req);
+            }
+
+            //ocpp cmd: MessageTrigger DiagnosticsStatusNotification
+            if (cmd is Cmd9003)
+            {
+                IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.DiagnosticsStatusNotification, 1);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+                messageType = "TriggerMessage Request(DiagnosticsStatusNotification)";
+                result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req);
+            }
+
+            //ocpp cmd: MessageTrigger FirmwareStatusNotification
+            if (cmd is Cmd9005)
+            {
+                IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.FirmwareStatusNotification, 1);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+                messageType = "TriggerMessage Request(FirmwareStatusNotification)";
+                result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req);
+            }
+
+            //ocpp cmd: MessageTrigger Heartbeat
+            if (cmd is Cmd9007)
+            {
+                IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.Heartbeat, 1);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+                messageType = "TriggerMessage Request(Heartbeat)";
+                result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req);
+            }
+
+            //ocpp cmd: MessageTrigger MeterValues
+            if (cmd is Cmd9009)
+            {
+                IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.MeterValues, 1);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+                messageType = "TriggerMessage Request(MeterValues)";
+                result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req);
+            }
+
+            //ocpp cmd: MessageTrigger StatusNotification
+            if (cmd is Cmd9011)
+            {
+                IRequest req = makeRequest.MakeTriggerMessageRequest(MessageTrigger.StatusNotification, 1);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+                messageType = "TriggerMessage Request(StatusNotification)";
+                result = GenerateCall(uuid, Actions.TriggerMessage.ToString(), req);
+            }
+
+            //ocpp cmd : SendLocalListRequest , GetLocalListVersion , OCPPUpdateFirmware
+            if (cmd is Cmd9013)
+            {
+                var c = cmd as Cmd9013;
+
+                //OCPP UpdateFirmware
+                if (c.ParamIndex == 4)
+                {
+                    var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+
+                    IRequest req = MakeRequestHandler.MakeOCPPUpdateFirmwareRequest(@"http://");
+                    string uuid = String.Empty;
+                    lock (_clientQueue)
+                    {
+                        uuid = queue.store(req);
+                    }
+
+                    messageType = "OCPPUpdateFirmware Request";
+                    result = GenerateCall(uuid, Actions.UpdateFirmware.ToString(), req);
+                }
+            }
+
+            //ocpp cmd : SetChargingProfile
+            if (cmd is Cmd9015)
+            {
+                var c = cmd as Cmd9015;
+                string purpose = System.Text.Encoding.ASCII.GetString(c.ParamByteList.ToArray()).Trim('\0');
+                var machine = db.Machine.Where(x => x.CustomId == machineCustomId).AsNoTracking().FirstOrDefault();
+
+                IRequest req = MakeRequestHandler.MakeSetChargingProfileRequest(0, 0, chargingProfileId, purpose);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "SetChargingProfile Request";
+                result = GenerateCall(uuid, Actions.SetChargingProfile.ToString(), req);
+                if (result.Contains("TxProfile") == false)
+                {
+                    int location = result.IndexOf("transactionId");
+                    int len = "\"transactionId\":0,".Length;
+                    result = result.Remove(location - 1, len);
+                }
+            }
+
+            //ocpp cmd : ClearChargingProfile
+            if (cmd is Cmd9017)
+            {
+                IRequest req = makeRequest.MakeClearChargingProfileRequest(0);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "ClearChargingProfile Request";
+                result = GenerateCall(uuid, Actions.ClearChargingProfile.ToString(), req);
+            }
+
+            //ocpp cmd : GetCompositeSchedule
+            if (cmd is Cmd9019)
+            {
+                IRequest req = makeRequest.MakeGetCompositeScheduleRequest(0);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "GetCompositeSchedule Request";
+                result = GenerateCall(uuid, Actions.GetCompositeSchedule.ToString(), req);
+            }
+
+            //ocpp cmd : HardReset
+            if (cmd is Cmd9021)
+            {
+                IRequest req = makeRequest.MakeHardResetRequest();
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "Reset Request(Hard)";
+                result = GenerateCall(uuid, Actions.Reset.ToString(), req);
+            }
+
+            //ocpp cmd : SoftReset
+            if (cmd is Cmd9023)
+            {
+                IRequest req = makeRequest.MakeSoftResetRequest();
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "Reset Request(Soft)";
+                result = GenerateCall(uuid, Actions.Reset.ToString(), req);
+            }
+
+            //ocpp cmd : ClearCache
+            if (cmd is Cmd9025)
+            {
+                IRequest req = makeRequest.MakeClearCacheRequest();
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "ClearCache Request";
+                result = GenerateCall(uuid, Actions.ClearCache.ToString(), req);
+            }
+
+            //ocpp cmd : ChangeAvailability
+            if (cmd is Cmd9027)
+            {
+                IRequest req = makeRequest.MakeChangeAvailabilityRequest(1);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "ChangeAvailability Request";
+                result = GenerateCall(uuid, Actions.ChangeAvailability.ToString(), req);
+            }
+
+            //ocpp cmd : GetConfiguration
+            if (cmd is Cmd9029)
+            {
+                IRequest req = makeRequest.MakeGetConfigurationRequest("");
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "GetConfiguration Request";
+                result = GenerateCall(uuid, Actions.GetConfiguration.ToString(), req);
+            }
+
+            //ocpp cmd : UnlockConnector
+            if (cmd is Cmd9031)
+            {
+                IRequest req = makeRequest.MakeUnlockConnectorRequest(1);
+                string uuid = String.Empty;
+                lock (_clientQueue)
+                {
+                    uuid = queue.store(req);
+                }
+
+                messageType = "UnlockConnector Request";
+                result = GenerateCall(uuid, Actions.UnlockConnector.ToString(), req);
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// 處理OCPP Request Message
+        /// </summary>
+        /// <param name="request">OCPP Request Message</param>
+        /// <param name="callid">OCPP Request Message Call Id</param>
+        /// <param name="db">Data Base Entity</param>
+        /// <param name="client">Connected Client Data</param>
+        /// <returns></returns>
+        public OCPPResult HandleRequest(IRequest request, string callid, PhihongDbContext db, ref ClientData client)
+        {
+            OCPPResult result = new OCPPResult();
+            IUnitOfWork uowtemp = new UnitOfWork(db);
+            IMachineService machineSrvTemp = new MachineService(uowtemp);
+            ISocketCommandService socketCommandSrvTemp = new SocketCommandService(uowtemp);
+            ICustomerService _customerServiceTemp = new CustomerService();
+
+            try
+            {
+                //簽到
+                if (request is BootNotificationRequest)
+                {
+                    var cmd = request as BootNotificationRequest;
+                    result.Success = true;
+                    result.Payload = makeConfirmation.MakeBootNotificationConfirmation(cmd);
+                }
+
+                //刷卡認證
+                if (request is AuthorizeRequest)
+                {
+                    #region AuthorizeRequest
+
+                    var cmd = request as AuthorizeRequest;
+                    var c = new HttpClient();
+                    string receivedCard = string.Empty;
+                    string cardpwd = string.Empty;
+                    //判斷此Authorize Reauest是Remote啟動充電 或 刷卡/後台啟動
+                    MemberMapping memberObj = db.MemberCharging.Where(x => x.SelfDefinedId == cmd.idTag).AsNoTracking().FirstOrDefault();
+                    if (memberObj != null)//為Remote啟動充電
+                    {
+                        receivedCard = memberObj.MemberId;
+                    }
+                    else //為刷卡或後台啟動
+                    {
+                        receivedCard = cmd.idTag;
+                        cardpwd = "nocheck";
+                    }
+
+                    var param = new { CustomerName = client.CustomerName, CardNum = receivedCard, PoleId = client.MachineCustomId, CardPwd = cardpwd };
+
+                    //將卡片資訊送到營運平台驗證
+                    var response = c.PostAsJsonAsync(ApiUrl + @"AuthenticateCard", param).Result;
+                    string responseBody = response.Content.ReadAsStringAsync().Result;
+                    if (response.IsSuccessStatusCode)
+                    {
+                        //伺服器回應正常
+                        var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId);
+                        var authresult = JsonConvert.DeserializeObject<Cmd1203>(responseBody);
+                        if (authresult.UserMessageCode == 1) //卡片餘額充足
+                        {
+                            //卡片認證成功
+                            result.Payload = makeConfirmation.MakeAuthorizeConfirmation(cmd, AuthorizationStatus.Accepted);
+
+                            #region 下發卡片餘額
+
+                            IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType);
+                            string uuid = String.Empty;
+                            lock (_clientQueue)
+                            {
+                                uuid = client.queue.store(req);
+                            }
+                            result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+
+                            #endregion 下發卡片餘額
+                        }
+                        else
+                        {
+                            if (authresult.UserMessageCode == 6) //卡片被凍結
+                            {
+                                //卡片認證失敗,檢查卡片是否在充電中
+                                //取得充電樁目前的Status
+                                string rqueryCard = string.Empty;
+                                if (memberObj != null) //為Remote啟動充電
+                                {
+                                    rqueryCard = memberObj.MemberId;
+                                }
+                                else
+                                {
+                                    rqueryCard = cmd.idTag;
+                                }
+
+                                //若電樁狀態不是"充電中"/"充電結束" ,MachineGun的ReservationCardNum欄位為空白,此時取出的 gunStatusNow 為null
+                                var gunStatusNow = db.MachineGun.Where(x => x.ReservationCardNum == rqueryCard
+                                 && x.MachineId == machine.Id).AsNoTracking().FirstOrDefault();
+
+                                if (gunStatusNow != null && (gunStatusNow.Status == 2 || gunStatusNow.Status == 3))
+                                {
+                                    //取得充電樁在充電中或充電結束的狀態,回應有效
+                                    result.Payload = makeConfirmation.MakeAuthorizeConfirmation(cmd, AuthorizationStatus.Accepted);
+                                }
+                                else
+                                {
+                                    //取得充電樁不在充電中或充電結束的狀態,回應無效
+                                    result.Payload = makeConfirmation.MakeAuthorizeConfirmation(cmd, AuthorizationStatus.Blocked);
+
+                                    #region 下發卡片餘額
+
+                                    {
+                                        IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType);
+                                        string uuid = String.Empty;
+                                        lock (_clientQueue)
+                                        {
+                                            uuid = client.queue.store(req);
+                                        }
+                                        result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+                                    }
+
+                                    #endregion 下發卡片餘額
+                                }
+                            }
+                            else
+                            {
+                                //卡片為非餘額充足.非凍結的其他狀態
+                                result.Payload = makeConfirmation.MakeAuthorizeConfirmation(cmd, AuthorizationStatus.Invalid);
+
+                                #region 下發卡片餘額
+
+                                {
+                                    IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType);
+                                    string uuid = String.Empty;
+                                    lock (_clientQueue)
+                                    {
+                                        uuid = client.queue.store(req);
+                                    }
+                                    result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+                                }
+
+                                #endregion 下發卡片餘額
+                            }
+                        }
+                    }
+                    else
+                    {
+                        //伺服器異常
+                        result.Payload = makeConfirmation.MakeAuthorizeConfirmation((AuthorizeRequest)request, AuthorizationStatus.Invalid);
+                    }
+                    result.Success = true;
+
+                    #endregion AuthorizeRequest
+                }
+
+                //狀態包
+                if (request is StatusNotificationRequest)
+                {
+                    #region StatusNotificationRequest
+
+                    //目前以DataTransfer的GunStatus封包取代
+                    var cmd = request as StatusNotificationRequest;
+                    logger.Trace("Receive Command Status{0}", client.MachineCustomId);
+                    result.Success = true;
+                    result.Payload = makeConfirmation.MakeStatusNotificationConfirmation(cmd);
+
+                    #endregion StatusNotificationRequest
+                }
+
+                //心跳包
+                if (request is HeartbeatRequest)
+                {
+                    result.Success = true;
+                    result.Payload = makeConfirmation.MakeHeartbeatConfirmation((HeartbeatRequest)request);
+                    client.heartbeatDate = DateTime.Now;
+                }
+
+                //充電狀態包
+                if (request is MeterValuesRequest)
+                {
+                    #region MeterValuesRequest
+
+                    var cmd = request as MeterValuesRequest;
+                    logger.Trace("Receive Command Status{0}", client.MachineCustomId);
+                    WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp);
+
+                    //處理104狀態包
+                    //不存在機器,要紀錄起來
+                    var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId);
+
+                    if (machine != null)
+                    {
+                        //取得充電樁目前Status的充電序號
+                        var gunStatusNow = db.MachineGun.Where(c => c.GunSerNo == (cmd.connectorId - 1)
+                            && c.MachineId == machine.Id).AsNoTracking().FirstOrDefault();
+
+                        if (gunStatusNow.Status != (byte)ChargePointStatus.Charging)
+                        {
+                            result.Success = true;
+                            result.Payload = makeConfirmation.MakeMeterValuesConfirmation(cmd);
+                            return result;
+                        }
+
+                        result.Success = true;
+                        result.Payload = makeConfirmation.MakeMeterValuesConfirmation(cmd);
+                    }
+
+                    #endregion MeterValuesRequest
+                }
+
+                //啟動充電狀態包
+                if (request is StartTransactionRequest)
+                {
+                    #region StartTransactionRequest
+
+                    var cmd = request as StartTransactionRequest;
+                    logger.Trace("Receive Command Status{0}", client.MachineCustomId);
+                    WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp);
+
+                    //處理104狀態包
+                    //不存在機器,要紀錄起來
+                    var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId);
+
+                    if (machine != null)
+                    {
+                        //取得充電樁目前Status的充電序號
+                        var gunStatusNow = db.MachineGun.Where(c => c.GunSerNo == (cmd.connectorId - 1)
+                            && c.MachineId == machine.Id).AsNoTracking().FirstOrDefault();
+
+                        int transactionId = 0;
+                        result.Success = true;
+
+                        #region 確認是否有重覆的StartTransaction
+
+                        var repeatStartTransaction = db.ChargingTransaction.Where(c => c.GunSerNo == (cmd.connectorId - 1)
+                           && c.MachineId == machine.Id
+                           && c.StartChargingDateTime == cmd.timestamp
+                           ).AsNoTracking().FirstOrDefault();
+
+                        if (repeatStartTransaction != null) //重複的StartTransaction
+                            result.Payload = makeConfirmation.MakeRePeatStartTransactionConfirmation(cmd, repeatStartTransaction.TransactionId);
+                        else
+                            result.Payload = makeConfirmation.MakeStartTransactionConfirmation(cmd, out transactionId);
+
+                        #endregion 確認是否有重覆的StartTransaction
+
+                        //取得OCPP transactionId之後,寫入mapping table(OCPP transactionId, 充電紀錄ID),取得充電樁目前Status的充電序號
+                        if (gunStatusNow.Status != (byte)ChargePointStatus.Charging)
+                        {
+                            gunStatusNow = db.MachineGun.Where(c => c.GunSerNo == (cmd.connectorId - 1)
+                            && c.MachineId == machine.Id).AsNoTracking().FirstOrDefault();
+                        }
+
+                        var record = db.ChargingRecord.Where(c => c.GunSerNo == (cmd.connectorId - 1)
+                        && c.MachineId == machine.Id
+                        && c.ClientStartChargingDateTime == null
+                        && c.ClientEndChargingDateTime == null
+                        && c.EndChargingDateTime == null
+                        && c.StartChargingDateTime != null
+                        && c.ChargingSerNo == gunStatusNow.ChargingSerNo).AsNoTracking().FirstOrDefault();
+
+                        //處理沒有重複送的StartTransaction封包
+                        if (record != null)
+                        {
+                            //避免在物件 'dbo.ChargingTransaction' 中插入重複的索引鍵資料列
+                            var chargingTransactiontemp = db.ChargingTransaction.Where(c => c.GunSerNo == (cmd.connectorId - 1)
+                            && c.MachineId == machine.Id
+                            && c.StartChargingDateTime == record.StartChargingDateTime
+                            ).AsNoTracking().FirstOrDefault();
+
+                            if (chargingTransactiontemp == null)
+                            {
+                                ChargingTransaction chargingTransaction = new ChargingTransaction();
+                                chargingTransaction.MachineId = machine.Id;
+                                chargingTransaction.TransactionId = transactionId;
+                                chargingTransaction.GunSerNo = (byte)(cmd.connectorId - 1);
+                                chargingTransaction.StartChargingDateTime = record.StartChargingDateTime;
+                                chargingTransaction.ChargingRecordId = record.Id;
+                                chargingTransaction.CreatedOn = DateTime.Now;
+                                db.ChargingTransaction.Add(chargingTransaction);
+                                db.SaveChanges();
+                            }
+                        }
+                    }
+
+                    #endregion StartTransactionRequest
+                }
+
+                //停止充電,接收充电桩上報最新一次充电信息
+                if (request is StopTransactionRequest)
+                {
+                    #region StopTransactionRequest
+
+                    //目前採用DataTransfer的ChargeComplete封包來取代 StopTransactionRequest
+                    var cmd = request as StopTransactionRequest;
+                    result.Success = true;
+                    result.Payload = makeConfirmation.MakeStopTransactionConfirmation(cmd, AuthorizationStatus.Accepted);
+                    logger.Trace("reply StopTransactionRequest");
+
+                    #endregion StopTransactionRequest
+                }
+
+                //接收電樁DiagnosticsStatus狀態
+                if (request is DiagnosticsStatusNotificationRequest)
+                {
+                    var cmd = request as DiagnosticsStatusNotificationRequest;
+                    logger.Trace("Receive 電樁 {0} Staus {1}", client.MachineCustomId, cmd.status.ToString());
+                    result.Success = true;
+                    result.Payload = makeConfirmation.MakeDiagnosticsStatusNotificationConfirmation();
+                }
+
+                //接收電樁update firmware 進度狀態
+                if (request is FirmwareStatusNotificationRequest)
+                {
+                    var cmd = request as FirmwareStatusNotificationRequest;
+                    if ((cmd.status == FirmwareStatus.DownloadFailed) || (cmd.status == FirmwareStatus.InstallationFailed))
+                    {
+                        //更新主程序 或 FW 部分
+                    }
+                    logger.Trace("Receive 電樁 {0} Staus {1}", client.MachineCustomId, cmd.status.ToString());
+                    result.Success = true;
+                    result.Payload = makeConfirmation.MakeFirmwareStatusNotificationConfirmation();
+                }
+
+                //處理自訂的訊息
+                if (request is DataTransferRequest)
+                {
+                    var cmd = request as DataTransferRequest;
+
+                    //刷卡認證
+                    if (cmd.messageId == "AuthorizeData")
+                    {
+                        #region AuthorizeData
+                        logger.Trace("Receive Command Status{0}", client.MachineCustomId);
+                        var cmd1204 = JsonConvert.DeserializeObject<AuthorizeData>(cmd.data);
+
+                        var c = new HttpClient();
+                        string receivedCard = string.Empty;
+
+                        //判斷此Authorize Reauest是Remote啟動充電 或 刷卡/後台啟動
+                        MemberMapping memberObj = db.MemberCharging.Where(x => x.SelfDefinedId == cmd1204.idTag).AsNoTracking().FirstOrDefault();
+                        if (memberObj != null)//為Remote啟動充電
+                        {
+                            receivedCard = memberObj.MemberId;
+                        }
+                        else //為刷卡或後台啟動
+                        {
+                            receivedCard = cmd1204.idTag;
+                        }
+
+                        var param = new { CustomerName = client.CustomerName, CardNum = receivedCard, PoleId = client.MachineCustomId, CardPwd = cmd1204.password };
+
+                        //將卡片資訊送到營運平台驗證
+                        var response = c.PostAsJsonAsync(ApiUrl + @"AuthenticateCard", param).Result;
+                        string responseBody = response.Content.ReadAsStringAsync().Result;
+                        if (response.IsSuccessStatusCode)
+                        {
+                            //伺服器回應正常
+                            var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId);
+                            var authresult = JsonConvert.DeserializeObject<Cmd1203>(responseBody);
+                            if (authresult.UserMessageCode == 1) //卡片餘額充足
+                            {
+                                //卡片認證成功
+                                result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.Accepted);
+
+                                #region 下發卡片餘額
+
+                                IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType);
+                                string uuid = String.Empty;
+                                lock (_clientQueue)
+                                {
+                                    uuid = client.queue.store(req);
+                                }
+                                result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+
+                                #endregion 下發卡片餘額
+                            }
+                            else
+                            {
+                                if (authresult.UserMessageCode == 6) //卡片被凍結
+                                {
+                                    //卡片認證失敗,檢查卡片是否在充電中
+                                    //取得充電樁目前的Status
+                                    string rqueryCard = string.Empty;
+                                    if (memberObj != null) //為Remote啟動充電
+                                    {
+                                        rqueryCard = memberObj.MemberId;
+                                    }
+                                    else
+                                    {
+                                        rqueryCard = cmd1204.idTag;
+                                    }
+
+                                    //若電樁狀態不是"充電中"/"充電結束" ,MachineGun的ReservationCardNum欄位為空白,此時取出的 gunStatusNow 為null
+                                    var gunStatusNow = db.MachineGun.Where(x => x.ReservationCardNum == rqueryCard
+                                     && x.MachineId == machine.Id).AsNoTracking().FirstOrDefault();
+
+                                    if (gunStatusNow != null && (gunStatusNow.Status == 2 || gunStatusNow.Status == 3))
+                                    {
+                                        //取得充電樁在充電中或充電結束的狀態,回應有效
+                                        result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.Accepted);
+                                    }
+                                    else
+                                    {
+                                        //取得充電樁不在充電中或充電結束的狀態,回應無效
+                                        result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.Blocked);
+
+                                        #region 下發卡片餘額
+
+                                        {
+                                            IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType);
+                                            string uuid = String.Empty;
+                                            lock (_clientQueue)
+                                            {
+                                                uuid = client.queue.store(req);
+                                            }
+                                            result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+                                        }
+
+                                        #endregion 下發卡片餘額
+                                    }
+                                }
+                                else if (authresult.UserMessageCode == 13) //密碼錯誤
+                                {
+                                    result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.PasswordError);
+                                }
+                                else
+                                {
+                                    //卡片為非餘額充足.非凍結的其他狀態
+                                    result.Payload = makeConfirmation.MakeAuthorizeDataConfConfirmation(cmd1204, AuthorizeStatus.Invalid);
+
+                                    #region 下發卡片餘額
+
+                                    {
+                                        IRequest req = makeRequest.MakeAccountBalanceRequest(callid, authresult.AccountBalance.RealValue, client.CarType);
+                                        string uuid = String.Empty;
+                                        lock (_clientQueue)
+                                        {
+                                            uuid = client.queue.store(req);
+                                        }
+                                        result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+                                    }
+
+                                    #endregion 下發卡片餘額
+                                }
+                            }
+                        }
+                        else
+                        {
+                            //伺服器異常
+                            result.Payload = makeConfirmation.MakeAuthorizeConfirmation((AuthorizeRequest)request, AuthorizationStatus.Invalid);
+                        }
+                        result.Success = true;
+
+
+                        #endregion
+                    }
+
+                    if (cmd.messageId == "GunStatus")
+                    {
+                        #region GunStatus
+
+                        logger.Trace("Receive Command Status{0}", client.MachineCustomId);
+                        var cmd104 = JsonConvert.DeserializeObject<GunStatus>(cmd.data);
+                        WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp);
+
+                        //處理104狀態包
+                        //不存在機器,要紀錄起來
+                        var machine = machineSrvTemp.GetByCustomId(client.MachineCustomId);
+
+                        #region mapper
+
+                        ChargingRecordStatus dObj = new ChargingRecordStatus();
+                        dObj.MachineId = machine.Id;
+                        dObj.MachineCustomId = machine.CustomId;
+                        dObj.Warning = ProcessErrorCode(machine.AC, cmd104.errorCode);
+                        dObj.DcV = (decimal)cmd104.dcVoltage;
+                        dObj.DcA = (decimal)cmd104.dcCurrent;
+                        dObj.BmsV = (decimal)cmd104.bmsVoltage;
+                        dObj.BmsA = (decimal)cmd104.bmsCurrent;
+                        dObj.AcV_A = (decimal)cmd104.acVoltage_A;
+                        dObj.AcV_B = (decimal)cmd104.acVoltage_B;
+                        dObj.AcV_C = (decimal)cmd104.acVoltage_C;
+                        dObj.AcA_A = (decimal)cmd104.acCurrent_A;
+                        dObj.AcA_B = (decimal)cmd104.acCurrent_B;
+                        dObj.AcA_C = (decimal)cmd104.acCurrent_C;
+                        dObj.BmsWorkingMode = (byte)cmd104.bmsWorkingMode;
+                        dObj.CumulativeKwh = (decimal)(cmd104.currentKwh - cmd104.beforeChargingKwh);//(decimal)cmd104.cumulativeKwh;
+                        dObj.CumulativeTime = cmd104.cumulativeTime;
+                        dObj.CurrentKwh = (decimal)cmd104.currentKwh;
+                        dObj.CardBalanceBeforeCharging = (decimal)cmd104.cardBalanceBeforeCharging;
+                        dObj.ChargingStrategy = (byte)cmd104.chargingStrategy;
+                        dObj.ChargingStrategyParam = (decimal)cmd104.chargingStrategyParameter;
+                        dObj.CurrentSoc = (byte)cmd104.currentSOC;
+                        dObj.CarConnection = (byte)cmd104.carConnectionStatus;
+                        dObj.KwhBeforeCharging = (decimal)cmd104.beforeChargingKwh;
+                        dObj.CumulativeChargeFee = (decimal)cmd104.cumulativeChargeFee;
+                        dObj.GunAmt = (byte)machine.GunAmt;
+                        dObj.GunSerNo = (byte)(cmd104.connectorId - 1);
+                        dObj.GunType = (byte)(machine.AC ? 2 : 1);
+                        dObj.RemainingTime = cmd104.remainingTime;
+                        dObj.Reservation = (byte)(cmd104.isReserved ? 1 : 0);
+                        dObj.ReservationOverTime = 0;
+                        dObj.StartWith = (byte)cmd104.startWith;
+                        dObj.CumulativeOutputKwh = Convert.ToDecimal(cmd104.CumulativeOutputKwh);
+                        dObj.GunTemperature = cmd104.GunTemperature;
+                        if (cmd104.status == ChargePointStatus.Charging || cmd104.status == ChargePointStatus.Finishing)
+                        {
+                            dObj.StartChargingDateTime = cmd104.startChargingDateTime;
+                        }
+                        else
+                        {
+                            dObj.StartChargingDateTime = null;
+                        }
+
+                        dObj.StartChargingType = (byte)cmd104.startChargingType;
+                        dObj.Status = (byte)cmd104.status;
+
+                        if (cmd104.startWith == StartWith.Member)
+                        {
+                            var membertb = db.MemberCharging.Where(c => c.SelfDefinedId == cmd104.idTag).AsNoTracking().FirstOrDefault();
+                            if (membertb != null)
+                            {
+                                //會員
+                                dObj.MemberId = membertb.MemberId.ToString();//client.ReservationCardNum;
+                                dObj.ReservationCardNum = membertb.MemberId.ToString();/*client.ReservationCardNum;*/
+                            }
+                        }
+                        else if ((cmd104.startWith == StartWith.CardNumber) || (cmd104.startWith == StartWith.Unknown))
+                        {
+                            dObj.ReservationCardNum = cmd104.idTag;
+                        }
+
+
+                        MChargingRecordStatus mdObj = new MChargingRecordStatus();
+                        mdObj.MachineId = machine.Id;
+                        mdObj.MachineCustomId = machine.CustomId;
+                        mdObj.Warning = ProcessErrorCode(machine.AC, cmd104.errorCode);
+                        mdObj.DcV = (decimal)cmd104.dcVoltage;
+                        mdObj.DcA = (decimal)cmd104.dcCurrent;
+                        mdObj.BmsV = (decimal)cmd104.bmsVoltage;
+                        mdObj.BmsA = (decimal)cmd104.bmsCurrent;
+                        mdObj.AcV_A = (decimal)cmd104.acVoltage_A;
+                        mdObj.AcV_B = (decimal)cmd104.acVoltage_B;
+                        mdObj.AcV_C = (decimal)cmd104.acVoltage_C;
+                        mdObj.AcA_A = (decimal)cmd104.acCurrent_A;
+                        mdObj.AcA_B = (decimal)cmd104.acCurrent_B;
+                        mdObj.AcA_C = (decimal)cmd104.acCurrent_C;
+                        mdObj.BmsWorkingMode = (byte)cmd104.bmsWorkingMode;
+                        mdObj.CumulativeKwh = (decimal)(cmd104.currentKwh - cmd104.beforeChargingKwh);//(decimal)cmd104.cumulativeKwh;
+                        mdObj.CumulativeTime = cmd104.cumulativeTime;
+                        mdObj.CurrentKwh = (decimal)cmd104.currentKwh;
+                        mdObj.CardBalanceBeforeCharging = (decimal)cmd104.cardBalanceBeforeCharging;
+                        mdObj.ChargingStrategy = (byte)cmd104.chargingStrategy;
+                        mdObj.ChargingStrategyParam = (decimal)cmd104.chargingStrategyParameter;
+                        mdObj.CurrentSoc = (byte)cmd104.currentSOC;
+                        mdObj.CarConnection = (byte)cmd104.carConnectionStatus;
+                        mdObj.KwhBeforeCharging = (decimal)cmd104.beforeChargingKwh;
+                        mdObj.CumulativeChargeFee = (decimal)cmd104.cumulativeChargeFee;
+                        mdObj.GunAmt = (byte)machine.GunAmt;
+                        mdObj.GunSerNo = (byte)(cmd104.connectorId - 1);
+                        mdObj.GunType = (byte)(machine.AC ? 2 : 1);
+                        mdObj.RemainingTime = cmd104.remainingTime;
+                        mdObj.Reservation = (byte)(cmd104.isReserved ? 1 : 0);
+                        mdObj.ReservationOverTime = 0;
+                        mdObj.StartWith = (byte)cmd104.startWith;
+                        mdObj.CumulativeOutputKwh = Convert.ToDecimal(cmd104.CumulativeOutputKwh);
+                        mdObj.GunTemperature = cmd104.GunTemperature;
+                        if (cmd104.status == ChargePointStatus.Charging || cmd104.status == ChargePointStatus.Finishing)
+                        {
+                            mdObj.StartChargingDateTime = cmd104.startChargingDateTime;
+                        }
+                        else
+                        {
+                            mdObj.StartChargingDateTime = null;
+                        }
+
+                        mdObj.StartChargingType = (byte)cmd104.startChargingType;
+                        mdObj.Status = (byte)cmd104.status;
+
+                        if (cmd104.startWith == StartWith.Member)
+                        {
+                            var membertb = db.MemberCharging.Where(c => c.SelfDefinedId == cmd104.idTag).AsNoTracking().FirstOrDefault();
+                            if (membertb != null)
+                            {
+                                //會員
+                                mdObj.MemberId = membertb.MemberId.ToString();//client.ReservationCardNum;
+                                mdObj.ReservationCardNum = membertb.MemberId.ToString();/*client.ReservationCardNum;*/
+                            }
+                        }
+                        else if ((cmd104.startWith == StartWith.CardNumber) || (cmd104.startWith == StartWith.Unknown))
+                        {
+                            mdObj.ReservationCardNum = cmd104.idTag;
+                        }
+                        #endregion mapper
+                        GunMessage socketCommand1104 = new GunMessage();
+                        socketCommand1104.chargingRecordStatus = dObj;
+                        socketCommand1104.mchargingRecordStatus = mdObj;
+                        var process1104Result = wsCommandSrv.Process1104(socketCommand1104);
+
+                        result.Success = true;
+                        result.Payload = makeConfirmation.MakeGunStatusConfirmation();
+
+                        #endregion GunStatus
+                    }
+
+                    if (cmd.messageId == "ChargeComplete")
+                    {
+                        #region ChargeComplete
+
+                        var chargeComplete = JsonConvert.DeserializeObject<ChargeComplete>(cmd.data);
+                        WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp);
+                        //避免同時處理同一個樁號的ChargeComplete
+                        lock (_cmd202lst)
+                        {
+                            if (_cmd202lst.Contains(client.MachineCustomId))
+                            {
+                                result.Success = false;
+                                return result;
+                            }
+                            if (!_cmd202lst.Contains(client.MachineCustomId))
+                            {
+                                _cmd202lst.Add(client.MachineCustomId);
+                            }
+                        }
+                        logger.Trace("receive ChargeComplete{0}", client.MachineCustomId);
+
+                        var processChargeCompleteResult = wsCommandSrv.ProcessChargeComplete(chargeComplete, client.MachineCustomId);
+                        if (processChargeCompleteResult.Success)
+                        {
+                            result.Success = true;
+                            result.Payload = makeConfirmation.MakeChargeCompleteConfirmation();
+                            logger.Trace("reply ChargeComplete");
+                        }
+                        else
+                        {
+                            result.Success = false;
+                            result.Exception = processChargeCompleteResult.Exception;
+                            result.Message = processChargeCompleteResult.Message;
+                            logger.Error(processChargeCompleteResult.Exception, "ChargeComplete error");
+                        }
+                        lock (_cmd202lst)
+                        {
+                            _cmd202lst.Remove(client.MachineCustomId);
+                        }
+
+                        #endregion ChargeComplete
+                    }
+
+                    if (cmd.messageId == "Location")
+                    {
+                        #region Location
+
+                        var data = JsonConvert.DeserializeObject<OCPPPacket.Packet.DataTransfer.Location>(cmd.data);
+                        WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp);
+                        logger.Trace("receive 1120 {0}", client.MachineCustomId);
+                        logger.Trace("receive 1120 {0} {1} {2}", client.MachineCustomId, data.longitude, data.latitude);
+                        var processLocationResult = wsCommandSrv.ProcessLocation(client.MachineCustomId, data);
+                        if (processLocationResult.Success)
+                        {
+                            result.Success = true;
+                            result.Payload = makeConfirmation.MakeLocationConfirmation();
+                            logger.Trace("reply Location");
+                        }
+                        else
+                        {
+                            result.Success = false;
+                            result.Exception = processLocationResult.Exception;
+                            logger.Error(processLocationResult.Exception, "Location.conf error");
+                        }
+
+                        #endregion Location
+                    }
+
+                    if (cmd.messageId == "ChargingGunBMS")
+                    {
+                        #region ChargingGunBMS
+
+                        var data = JsonConvert.DeserializeObject<ChargingGunBMS>(cmd.data);
+                        WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp);
+                        var processChargingGunBMSResult = wsCommandSrv.ProcessChargingGunBMS(client.MachineId, data);
+                        if (processChargingGunBMSResult.Success)
+                        {
+                            result.Success = true;
+                            result.Payload = makeConfirmation.MakeChargingGunBMSConfirmation();
+                            logger.Trace("reply ChargingGunBMS");
+                        }
+
+                        #endregion ChargingGunBMS
+                    }
+
+                    //電樁上報升級文件的版
+                    if (cmd.messageId == "UpgradeVersion")
+                    {
+                        #region UpgradeVersion
+
+                        UploadFile uploadFile = null;
+                        WebSocketCommandService wsCommandSrv = new WebSocketCommandService(uowtemp);
+                        var data = JsonConvert.DeserializeObject<UpgradeVersion>(cmd.data);
+                        var processUpgradeVersionResult = wsCommandSrv.CheckCmd2104(client.MachineId, data, out uploadFile);
+
+                        result.Success = true;
+                        result.Payload = makeConfirmation.MakeUpgradeVersionConfConfirmation(processUpgradeVersionResult, data.param);
+                        logger.Trace("reply DataTransfer.conf");
+
+                        //DC 桩
+                        if (uploadFile != null)
+                        {
+                            //製作 UpdateFirmware Request
+                            if (((int)data.param == 1 || CmdHelper.GetCmd2101Type((byte)data.param) == 2) && processUpgradeVersionResult == false)
+                            {
+                                int type = CmdHelper.GetCmd2101Type((byte)data.param);
+                                IRequest req = makeRequest.MakeUpdateFirmware(type, data.param, uploadFile);
+                                string uuid = String.Empty;
+                                lock (_clientQueue)
+                                {
+                                    uuid = client.queue.store(req);
+                                }
+                                result.Message = GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+                            }
+                        }
+
+                        #endregion UpgradeVersion
+                    }
+
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result.Exception = ex;
+                result.Success = false;
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 判斷是否由會員帳號充電
+        /// </summary>
+        /// <param name="request">OCPP Request</param>
+        /// <param name="db">Data Base Entity</param>
+        /// <returns></returns>
+        public bool IsMemberCharging(IRequest request, PhihongDbContext db)
+        {
+            bool result = new Boolean();
+            IUnitOfWork uowtemp = new UnitOfWork(db);
+            IMachineService machineSrvTemp = new MachineService(uowtemp);
+            ISocketCommandService socketCommandSrvTemp = new SocketCommandService(uowtemp);
+            ICustomerService _customerServiceTemp = new CustomerService();
+
+            try
+            {
+                //刷卡認證
+                if (request is AuthorizeRequest)
+                {
+                    var cmd = request as AuthorizeRequest;
+                    //判斷此Authorize Reauest是Remote啟動充電 或 刷卡/後台啟動
+                    MemberMapping memberObj = db.MemberCharging.Where(x => x.SelfDefinedId == cmd.idTag).AsNoTracking().FirstOrDefault();
+                    if (memberObj != null)//為Remote啟動充電
+                    {
+                        result = true;
+                    }
+                    else //為刷卡或後台啟動
+                    {
+                        result = false;
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result = false;
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 處理電樁所送的comfirm message
+        /// </summary>
+        /// <param name="uniqueId">OCPP Message uniqueId</param>
+        /// <param name="confirmation">OCPP Confirm Message</param>
+        /// <param name="db">Data Base Entity</param>
+        /// <param name="client">Connected Client Data</param>
+        /// <returns></returns>
+        public OCPPResult HandleConfirmation(string uniqueId, IConfirmation confirmation, string callResultAction, PhihongDbContext db, ref ClientData client)
+        {
+            OCPPResult result = new OCPPResult();
+            IUnitOfWork uowconf = new UnitOfWork(db);
+            IRepository<MachineVersionFile> _mvfRepo = uowconf.Repository<MachineVersionFile>();
+            try
+            {
+                #region Core
+
+                //接收電樁對Remote啟動充電回應
+                if (confirmation is RemoteStartTransactionConfirmation)
+                {
+                    IConfirmation cmd = confirmation as RemoteStartTransactionConfirmation;
+                }
+
+                //接收電樁對Remote停止充電回應
+                if (confirmation is RemoteStopTransactionConfirmation)
+                {
+                    IConfirmation cmd = confirmation as RemoteStopTransactionConfirmation;
+                }
+
+                //接收電樁對Unlock Connector回應
+                if (confirmation is UnlockConnectorConfirmation)
+                {
+                    IConfirmation cmd = confirmation as UnlockConnectorConfirmation;
+                }
+
+                //接收電樁對configuration 回應
+                if (confirmation is GetConfigurationConfirmation)
+                {
+                    GetConfigurationConfirmation cmd = confirmation as GetConfigurationConfirmation;
+
+                    foreach (var config in cmd.configurationKey)
+                    {
+                        var item = client.configurationKey.Where(c => c.key == config.key).FirstOrDefault();
+                        item.value = config.value;
+                    }
+                }
+
+                //接收電樁對電樁槍號 availability 改變 回應
+                if (confirmation is ChangeAvailabilityConfirmation)
+                {
+                    ChangeAvailabilityConfirmation cmd = confirmation as ChangeAvailabilityConfirmation;
+                }
+
+                #endregion Core
+
+                #region FirmwareManagement
+
+                //接收電樁對取得 GetDiagnostics資訊回應
+                if (confirmation is GetDiagnosticsConfirmation)
+                {
+                    GetDiagnosticsConfirmation cmd = confirmation as GetDiagnosticsConfirmation;
+                    logger.Trace("Receive 電樁 {0} 上傳檔案 {1}", client.MachineCustomId, cmd.fileName);
+                }
+
+                //接收電樁對UpdateFirmware回應
+                if (confirmation is UpdateFirmwareConfirmation)
+                {
+                    UpdateFirmwareConfirmation cmd = confirmation as UpdateFirmwareConfirmation;
+                }
+
+                #endregion FirmwareManagement
+
+                #region LocalAuthListManagement
+
+                //接收電樁對  Get Local List Version 回應
+                if (confirmation is GetLocalListVersionConfirmation)
+                {
+                    GetLocalListVersionConfirmation cmd = confirmation as GetLocalListVersionConfirmation;
+                    string customId = client.MachineCustomId;
+                    var localauthTable = db.LocalAuthorization.Where(c => c.CustomId == customId).FirstOrDefault();
+                    if (localauthTable != null)
+                    {
+                        localauthTable.CurrentVersion = cmd.listVersion;
+                        localauthTable.UpdatedOn = DateTime.Now;
+                        db.SaveChanges();
+                    }
+                }
+
+                //接收電樁對  Send Local List 回應
+                if (confirmation is SendLocalListConfirmation)
+                {
+                    SendLocalListConfirmation cmd = confirmation as SendLocalListConfirmation;
+
+                    if (cmd.updateStatus == UpdateStatus.Accepted)
+                    {
+                        //下發GetLocalListVersion.req
+                        Cmd1011 cmd1011 = client.CmdHelper.Create(1011) as Cmd1011;
+                        cmd1011.CmdSerNum = client.GetCmdSerNum();
+                        cmd1011.SerNum = client.GetSerNum();
+                        cmd1011.Pack();
+                        ServerCommand sc = new ServerCommand();
+                        sc.OutCmdNum = cmd1011.Cmd;
+                        sc.CreatedOn = DateTime.Now;
+                        sc.MachineId = client.MachineId;
+                        sc.MachineCustomId = client.MachineCustomId;
+                        sc.OutTransData = cmd1011.byteList.ToArray();
+                        db.ServerCommand.Add(sc);
+                        db.SaveChanges();
+                    }
+                }
+
+                #endregion LocalAuthListManagement
+
+                #region Reservation
+
+                //接收電樁對 預約 回應
+                if (confirmation is ReserveNowConfirmation)
+                {
+                    ReserveNowConfirmation cmd = confirmation as ReserveNowConfirmation;
+                }
+
+                //接收電樁對 取消預約 回應
+                if (confirmation is CancelReservationConfirmation)
+                {
+                    CancelReservationConfirmation cmd = confirmation as CancelReservationConfirmation;
+                }
+
+                #endregion Reservation
+
+                #region SmartCharging
+
+                //接收電樁對 Charging Profile回應
+                if (confirmation is SetChargingProfileConfirmation)
+                {
+                    SetChargingProfileConfirmation cmd = confirmation as SetChargingProfileConfirmation;
+                    var uuid = Guid.Parse(uniqueId);
+                    var chargingProfile = db.ChargingProfile.Where(x => x.Id == uuid).FirstOrDefault();
+                    if (chargingProfile != null)
+                    {
+                        chargingProfile.UpdatedOn = DateTime.Now;
+                        chargingProfile.ResponseStatus = (int)cmd.status;
+                        db.SaveChanges();
+                    }
+                    logger.Trace("Receive 電樁 {0} Charging Profile回應 Status:{1} \n", client.MachineCustomId, cmd.status);
+                }
+
+                //接收電樁對清除Charging Profile回應
+                if (confirmation is ClearChargingProfileConfirmation)
+                {
+                    ClearChargingProfileConfirmation cmd = confirmation as ClearChargingProfileConfirmation;
+                    var uuid = Guid.Parse(uniqueId);
+                    var chargingProfile = db.ChargingProfile.Where(x => x.Id == uuid).FirstOrDefault();
+                    if (chargingProfile != null)
+                    {
+                        chargingProfile.UpdatedOn = DateTime.Now;
+                        chargingProfile.ResponseStatus = (int)cmd.status;
+                        db.SaveChanges();
+                    }
+                    logger.Trace("Receive 電樁 {0} 清除Charging Profile Status:{1} \n", client.MachineCustomId, cmd.status);
+                }
+
+                //接收電樁對Composite Charging Schedule 回應
+                if (confirmation is GetCompositeScheduleConfirmation)
+                {
+                    GetCompositeScheduleConfirmation cmd = confirmation as GetCompositeScheduleConfirmation;
+                    if (cmd.chargingSchedule != null)
+                    {
+                        logger.Trace("Receive 電樁 {0} Composite Charging Schedule  connectorId:{1} Status:{2} ScheduleStart:{3} duration:{4}  chargingRateUnit:{5} minChargingRate:{6}\n", client.MachineCustomId, cmd.connectorId, cmd.status, cmd.scheduleStart.ToLocalTime(), cmd.chargingSchedule.duration, cmd.chargingSchedule.chargingRateUnit, cmd.chargingSchedule.minChargingRate);
+                        foreach (var period in cmd.chargingSchedule.chargingSchedulePeriod)
+                        {
+                            logger.Trace("startPeriod:{0} limit:{1} numberPhases:{2}\n", period.startPeriod, period.limit, period.numberPhases);
+                        }
+                    }
+                    else
+                        logger.Trace("Receive 電樁 {0} Composite Charging Schedule  connectorId:{1} Status:{2} ScheduleStart:{3} \n", client.MachineCustomId, cmd.connectorId, cmd.status, cmd.scheduleStart.ToLocalTime());
+                }
+
+                #endregion SmartCharging
+
+                #region DataTransfer
+
+                //處理自訂的訊息
+                if (confirmation is DataTransferConfirmation)
+                {
+
+
+                    DataTransferConfirmation cmd = confirmation as DataTransferConfirmation;
+                  
+
+                    if (cmd.data != null)
+                    {
+                        
+
+                        if (callResultAction.Contains("SetChargingConfig"))
+                        {
+                           
+                            var uuid = Guid.Parse(uniqueId);
+                            var chargingProfile = db.ChargingProfile.Where(x => x.Id == uuid).FirstOrDefault();
+                            if (chargingProfile != null)
+                            {
+                                chargingProfile.UpdatedOn = DateTime.Now;
+                                chargingProfile.ResponseStatus = (int)cmd.status;
+                                db.SaveChanges();
+                            }
+                           
+                        }
+                        else if (callResultAction.Contains("GetChargingConfig"))
+                        {
+                          
+                            var uuid = Guid.Parse(uniqueId);
+                            var chargingProfile = db.ChargingProfile.Where(x => x.Id == uuid).FirstOrDefault();
+                            if (chargingProfile != null)
+                            {
+                                var charingRate = JsonConvert.DeserializeObject<ChargingRate>(cmd.data);
+                                chargingProfile.UpdatedOn = DateTime.Now;
+                                chargingProfile.ResponseStatus = (int)cmd.status;
+                                chargingProfile.EasyConfig = String.Format("{0}:{1}", charingRate.chargingRateUnit, charingRate.limit);
+                                db.SaveChanges();
+                            }
+                           
+                        }
+                        else if (cmd.data[0] == '{')
+                        {
+                          
+                            JObject restoredObject = JObject.Parse(cmd.data);
+                            FirmwareMD5 fwmd5 = new FirmwareMD5();
+
+                          
+                            if (cmd.data.Contains("md5") == true)
+                            {
+                                // Copy to a static fwmd5 instance
+                                fwmd5 = restoredObject.ToObject<FirmwareMD5>();
+
+                                //充電樁對服務器下發升級指令的應答
+                                //處理 UpdateFirmware conf
+                                fwmd5.md5 = fwmd5.md5.ToUpper();
+                                string machineId = client.MachineId;
+                                var mvFile = _mvfRepo.Query(c => c.MachineVersion.MachineId.Equals(machineId) && c.UploadFile.FileMD5.ToUpper().Equals(fwmd5.md5)).Select(c => c).OrderByDescending(c => c.CreatedOn).FirstOrDefault();
+
+                                if (mvFile != null)
+                                {
+                                    mvFile.DownloadedOn = DateTime.Now;
+                                    uowconf.SaveChanges();
+                                    result.Success = true;
+                                }
+                                else
+                                {
+                                    throw new ApplicationException("找不到相關檔案");
+                                }
+                            }
+                        }
+                        else
+                        {
+                            if (cmd.data == "true") //成功
+                            {
+                                logger.Trace("Receive 電樁 {0} 處理電費/服務費 結果 {1}", client.MachineCustomId, cmd.data);
+                            }
+                            else if (cmd.data == "false") //失敗
+                            {
+                                logger.Trace("Receive 電樁 {0} 處理電費/服務費 結果 {1}", client.MachineCustomId, cmd.data);
+                            }
+                        }
+                    }
+                }
+
+                #endregion DataTransfer
+
+                result.Success = true;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result.Exception = ex;
+                result.Success = false;
+            }
+            return result;
+        }
+
+        //處理電樁所送的Error Message
+        public OCPPResult HandleError(string uniqueId, string errorCode, string errorDescription, object payload)
+        {
+            OCPPResult result = new OCPPResult();
+            return result;
+        }
+
+        /// <summary>
+        /// 處理WSServer 產生DataTransfer的ServerDomainName命令
+        /// </summary>
+        /// <param name="queue">Message Queue</param>
+        /// <param name="ServerDomainName">ServerDomainName</param>
+        /// <returns></returns>
+        public string HandleServerDomainName(ref Queue queue, string ServerDomainName)
+        {
+            IRequest req = makeRequest.MakeServerDomainNameRequest(ServerDomainName);
+            string uuid = String.Empty;
+            lock (_clientQueue)
+            {
+                uuid = queue.store(req);
+            }
+            return GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+        }
+
+        /// <summary>
+        /// 處理WSServer 產生DataTransfer的UpdateFirmware命令
+        /// </summary>
+        /// <param name="queue">Message Queue</param>
+        /// <param name="type">Messgae Type</param>
+        /// <param name="param">ProgramParam Type</param>
+        /// <param name="uploadFile">UploadFile</param>
+        /// <returns></returns>
+        public string HandleUpdateFirmware(ref Queue queue, int type, ProgramParam param, UploadFile uploadFile)
+        {
+            IRequest req = makeRequest.MakeUpdateFirmware(type, param, uploadFile);
+            string uuid = String.Empty;
+            lock (_clientQueue)
+            {
+                uuid = queue.store(req);
+            }
+            return GenerateCall(uuid, Actions.DataTransfer.ToString(), req);
+        }
+
+        /// <summary>
+        /// 處理WSServer 產生OCPP協議的UpdateFirmware命令
+        /// </summary>
+        /// <param name="queue">Message Queue</param>
+        /// <param name="fileurl">FileURL</param>
+        /// <returns></returns>
+        public string HandleOCPPUpdateFirmware(ref Queue queue, string fileurl)
+        {
+            IRequest req = MakeRequestHandler.MakeOCPPUpdateFirmwareRequest(fileurl);
+            string uuid = String.Empty;
+            lock (_clientQueue)
+            {
+                uuid = queue.store(req);
+            }
+            return GenerateCall(uuid, Actions.UpdateFirmware.ToString(), req);
+        }
+
+        /// <summary>
+        /// 將後台要傳給電樁的request轉換成Call Message
+        /// </summary>
+        /// <param name="uniqueId">OCPP Call Message uniqueId</param>
+        /// <param name="action">OCPP Call Message action</param>
+        /// <param name="request">Request Message</param>
+        /// <returns></returns>
+        public string GenerateCall(string uniqueId, string action, object request)
+        {
+            try
+            {
+                return MakeCall(uniqueId, action, PackPayload(request));
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                throw new ApplicationException(ex.ToString());
+            }
+        }
+
+        /// <summary>
+        /// 將後台要傳給電樁的comfirm轉換成CallResult Message
+        /// </summary>
+        /// <param name="uniqueId">OCPP Call Message uniqueId</param>
+        /// <param name="confirmation">Confirmation Message</param>
+        /// <returns></returns>
+        public string GenerateCallResult(string uniqueId, object confirmation)
+        {
+            try
+            {
+                return MakeCallResult(uniqueId, PackPayload(confirmation));
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                throw new ApplicationException(ex.ToString());
+            }
+        }
+
+        /// <summary>
+        /// 將後台要傳給電樁的錯誤訊息轉換成CallError Message
+        /// </summary>
+        /// <param name="uniqueId">OCPP CallError Message uniqueId</param>
+        /// <param name="errorCode">OCPP CallError Message errorCode</param>
+        /// <param name="errorDescription">OCPP CallError Message errorDescription</param>
+        /// <returns></returns>
+        public string GenerateCallError(string uniqueId, string errorCode, string errorDescription)
+        {
+            try
+            {
+                return MakeCallError(uniqueId, errorCode, errorDescription);
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                throw new ApplicationException(ex.ToString());
+            }
+        }
+
+        /// <summary>
+        /// 產生CallResult Message
+        /// </summary>
+        /// <param name="uniqueId">OCPP CallResult Message uniqueId</param>
+        /// <param name="payload">OCPP CallResult Message payload</param>
+        /// <returns></returns>
+        private string MakeCallResult(string uniqueId, object payload)
+        {
+            const string CALLRESULT_FORMAT = "[3,\"{0}\",{1}]";
+            return string.Format(CALLRESULT_FORMAT, uniqueId, payload.ToString());
+        }
+
+        /// <summary>
+        /// 產生Call Message
+        /// </summary>
+        /// <param name="uniqueId">OCPP CallResult Message uniqueId</param>
+        /// <param name="action">OCPP CallResult Message action</param>
+        /// <param name="payload">OCPP CallResult Message payload</param>
+        /// <returns></returns>
+        private string MakeCall(string uniqueId, string action, object payload)
+        {
+            const string CALL_FORMAT = "[2,\"{0}\",\"{1}\",{2}]";
+            return string.Format(CALL_FORMAT, uniqueId, action, payload.ToString());
+        }
+
+        /// <summary>
+        /// 產生CallError Message
+        /// </summary>
+        /// <param name="uniqueId">OCPP CallError Message uniqueId</param>
+        /// <param name="errorCode">OCPP CallError Message errorCode</param>
+        /// <param name="errorDescription">OCPP CallError Message errorDescription</param>
+        /// <returns></returns>
+        private string MakeCallError(string uniqueId, string errorCode, string errorDescription)
+        {
+            const string CALLERROR_FORMAT = "[4,\"{0}\",\"{1}\",\"{2}\",{3}]";
+            return string.Format(CALLERROR_FORMAT, uniqueId, errorCode, errorDescription, "{}");
+        }
+
+        /// <summary>
+        /// 處理OCPP CallResult Message
+        /// </summary>
+        /// <param name="id">OCPP CallResult Message uniqueId</param>
+        /// <param name="payload">OCPP CallResult Message payload</param>
+        /// <param name="queue">OCPP CallResult Message queue</param>
+        /// <param name="messageType">OCPP CallResult Message messageType</param>
+        /// <returns></returns>
+        public OCPPResult OnCallResult(string id, object payload, ref Queue queue, out string messageType)
+        {
+            OCPPResult result = new OCPPResult();
+
+            try
+            {
+                IRequest request;
+                Type actionType = GetConfirmationType(ref queue, id, out request);
+
+                if (request == null)
+                {
+                    messageType = "There are two conditions. Maybe it was sent repeatedly or there is no related Request. ";
+                    result.Message = "none";
+                    result.Success = false;
+                }
+                else
+                {
+                    IConfirmation confirmation = (IConfirmation)UnpackPayload(payload, actionType);
+                    if (actionType.Name.Contains("DataTransferConfirmation"))
+                    {
+                        messageType = "DataTransfer Confirmation(" + ((DataTransferRequest)request).messageId + ")";
+                    }
+                    else if (actionType.Name.Contains("TriggerMessageConfirmation"))
+                    {
+                        messageType = "TriggerMessage Confirmation(" + ((TriggerMessageRequest)request).requestedMessage.ToString() + ")";
+                    }
+                    else
+                    {
+                        messageType = actionType.Name.Remove(actionType.Name.IndexOf("Confirmation")) + " Confirmation";
+                    }
+
+                    if (confirmation.Validate())
+                    {
+                        result.Payload = confirmation;
+                        result.Success = true;
+                    }
+                    else
+                    {
+                        result.Message = GenerateCallError(id, "OccurenceConstraintViolation", "Payload for Action is syntactically correct but at least one of the fields violates occurence constraints");
+                        result.Success = false;
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result.Exception = ex;
+                messageType = string.Empty;
+                result.Success = false;
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 處理OCPP CALL Message
+        /// </summary>
+        /// <param name="id">OCPP CALL Message uniqueId</param>
+        /// <param name="action">OCPP CALL Message action</param>
+        /// <param name="payload">OCPP CALL Message payload</param>
+        /// <returns></returns>
+        public OCPPResult OnCall(string id, string action, object payload)
+        {
+            OCPPResult result = new OCPPResult();
+            Feature feature = featureHandler.FindFeatureByAction(action);
+            if (feature == null)
+            {
+                result.Message = GenerateCallError(id, "NotImplemented", "Requested Action is not known by receiver");
+                result.Success = false;
+            }
+            else
+            {
+                try
+                {
+                    IRequest request = (IRequest)UnpackPayload(payload, feature.GetRequestType());
+                    if (request.Validate())
+                    {
+                        result.Payload = request;
+                        result.Success = true;
+                    }
+                    else
+                    {
+                        result.Message = GenerateCallError(id, "OccurenceConstraintViolation", "Payload for Action is syntactically correct but at least one of the fields violates occurence constraints");
+                        result.Success = false;
+                    }
+                }
+                catch (Exception ex)
+                {
+                    logger.Error(ex);
+                    result.Exception = ex;
+                    result.Success = false;
+                }
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 處理OCPP Error Message
+        /// </summary>
+        /// <param name="id">OCPP Error Message uniqueId</param>
+        /// <param name="errorCode">OCPP Error Message errorCode</param>
+        /// <param name="errorDescription">OCPP Error Message errorDescription</param>
+        /// <param name="payload">OCPP Error Message payload</param>
+        /// <returns></returns>
+        public OCPPResult OnError(string id, string errorCode, string errorDescription, object payload)
+        {
+            OCPPResult result = new OCPPResult();
+            result.Success = true;
+            return result;
+        }
+    }
+}

+ 60 - 0
OCPPServer/NLog.config

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
+      autoReload="true"
+      throwExceptions="false"
+      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
+
+  <!-- optional, add some variabeles
+  https://github.com/nlog/NLog/wiki/Configuration-file#variables
+  -->
+  <variable name="myvar" value="myvalue" />
+
+  <!--
+  See https://github.com/nlog/nlog/wiki/Configuration-file
+  for information on customizing logging rules and outputs.
+   -->
+  <targets>
+    <target name="console" xsi:type="Console" layout="${date:format=HH\:MM\:ss} ${logger} ${message}" />
+    <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
+            layout="${longdate} ${uppercase:${level}} ${message}" />
+
+    <target xsi:type="Database" name="databaseLog"
+    commandText="insert into NlogTable (CreatedOn, LogLevel, Logger, Msg,Host,Type,Source,Stacktrace,Detail) Values (@CreatedOn, @LogLevel, @Logger, @Msg,@Host,@Type,@Source,@Stacktrace,@Detail)">
+      <connectionStringName>DefaultConnection</connectionStringName>
+      <parameter name="@CreatedOn" layout="${date}" />
+      <parameter name="@LogLevel" layout="${level}" />
+      <parameter name="@Logger" layout="${logger}" />
+      <parameter name="@Msg" layout="${message}" />
+      <parameter name="@Host" layout="${machinename}" />
+      <parameter name="@Type" layout="${exception:format=type}" />
+      <parameter name="@Source" layout="${callsite:className=true}" />
+      <parameter name="@Stacktrace" layout="${exception:stacktrace}" />
+      <parameter name="@Detail" layout="${exception:format=tostring}" />
+    </target>
+    <!--
+    add your targets here
+    See https://github.com/nlog/NLog/wiki/Targets for possible targets.
+    See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
+    -->
+
+    <!--
+    Writing events to the a file with the date in the filename.
+    <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
+            layout="${longdate} ${uppercase:${level}} ${message}" />
+    -->
+  </targets>
+
+  <rules>
+    <logger name="*" minlevel="Trace" writeTo="f" />
+    <logger name="*" minlevel="Info" writeTo="console" />
+    <logger name="*" minlevel="Error" writeTo="databaseLog" />
+    <!-- add your logging rules here -->
+
+    <!--
+    Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace)  to "f"
+    <logger name="*" minlevel="Debug" writeTo="f" />
+    -->
+  </rules>
+</nlog>

+ 3027 - 0
OCPPServer/NLog.xsd

@@ -0,0 +1,3027 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xs:schema id="NLog" targetNamespace="http://www.nlog-project.org/schemas/NLog.xsd" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.nlog-project.org/schemas/NLog.xsd">
+  <xs:element name="nlog" type="NLogConfiguration" />
+  <xs:complexType name="NLogConfiguration">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="extensions" type="NLogExtensions" />
+      <xs:element name="include" type="NLogInclude" />
+      <xs:element name="variable" type="NLogVariable" />
+      <xs:element name="targets" type="NLogTargets" />
+      <xs:element name="rules" type="NLogRules" />
+      <xs:element name="time" type="TimeSource" />
+    </xs:choice>
+    <xs:attribute name="autoReload" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Watch config file for changes and reload automatically.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogToConsole" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Print internal NLog messages to the console. Default value is: false</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogToConsoleError" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Print internal NLog messages to the console error output. Default value is: false</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogFile" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Write internal NLog messages to the specified file.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogLevel" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Log level threshold for internal log messages. Default value is: Info.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="globalThreshold" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Global log level threshold for application log messages. Messages below this level won't be logged..</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="throwExceptions" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Throw an exception when there is an internal error. Default value is: false.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="throwConfigExceptions" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Throw an exception when there is a configuration error. If not set, determined by throwExceptions.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="keepVariablesOnReload" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Gets or sets a value indicating whether Variables should be kept on configuration reload. Default value is: false.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogToTrace" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Write internal NLog messages to the System.Diagnostics.Trace. Default value is: false.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="internalLogIncludeTimestamp" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Write timestamps for internal NLog messages. Default value is: true.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="useInvariantCulture" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Use InvariantCulture as default culture instead of CurrentCulture.  Default value is: false.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogTargets">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="default-wrapper" type="WrapperTargetBase" />
+      <xs:element name="default-target-parameters" type="Target" />
+      <xs:element name="target" type="Target" />
+      <xs:element name="wrapper-target" type="WrapperTargetBase" />
+      <xs:element name="compound-target" type="CompoundTargetBase" />
+    </xs:choice>
+    <xs:attribute name="async" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes).</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogRules">
+    <xs:sequence minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="logger" type="NLogLoggerRule" />
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="NLogExtensions">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="add" type="NLogExtensionsAdd" />
+    </xs:choice>
+  </xs:complexType>
+  <xs:complexType name="NLogExtensionsAdd">
+    <xs:attribute name="prefix" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Prefix for targets/layout renderers/filters/conditions loaded from this assembly.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="assemblyFile" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Load NLog extensions from the specified file (*.dll)</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="assembly" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Load NLog extensions from the specified assembly. Assembly name should be fully qualified.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogLoggerRule">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="filters" type="NLogFilters" />
+    </xs:choice>
+    <xs:attribute name="name" use="optional">
+      <xs:annotation>
+        <xs:documentation>Name of the logger. May include '*' character which acts like a wildcard. Allowed forms are: *, Name, *Name, Name* and *Name*</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="levels" type="NLogLevelList">
+      <xs:annotation>
+        <xs:documentation>Comma separated list of levels that this rule matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="minlevel" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Minimum level that this rule matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="maxlevel" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Maximum level that this rule matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="level" type="NLogLevel">
+      <xs:annotation>
+        <xs:documentation>Level that this rule matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="writeTo" type="NLogTargetIDList">
+      <xs:annotation>
+        <xs:documentation>Comma separated list of target names.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="final" type="xs:boolean" default="false">
+      <xs:annotation>
+        <xs:documentation>Ignore further rules if this one matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="enabled" type="xs:boolean" default="true">
+      <xs:annotation>
+        <xs:documentation>Enable or disable logging rule. Disabled rules are ignored.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogFilters">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="when" type="when" />
+      <xs:element name="whenContains" type="whenContains" />
+      <xs:element name="whenEqual" type="whenEqual" />
+      <xs:element name="whenNotContains" type="whenNotContains" />
+      <xs:element name="whenNotEqual" type="whenNotEqual" />
+    </xs:choice>
+  </xs:complexType>
+  <xs:simpleType name="NLogLevel">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Off" />
+      <xs:enumeration value="Trace" />
+      <xs:enumeration value="Debug" />
+      <xs:enumeration value="Info" />
+      <xs:enumeration value="Warn" />
+      <xs:enumeration value="Error" />
+      <xs:enumeration value="Fatal" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="LineEndingMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Default" />
+      <xs:enumeration value="CRLF" />
+      <xs:enumeration value="CR" />
+      <xs:enumeration value="LF" />
+      <xs:enumeration value="None" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLogLevelList">
+    <xs:restriction base="xs:string">
+      <xs:pattern value="(|Trace|Debug|Info|Warn|Error|Fatal)(,(Trace|Debug|Info|Warn|Error|Fatal))*" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLogInclude">
+    <xs:attribute name="file" type="SimpleLayoutAttribute" use="required">
+      <xs:annotation>
+        <xs:documentation>Name of the file to be included. You could use * wildcard. The name is relative to the name of the current config file.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="ignoreErrors" type="xs:boolean" use="optional" default="false">
+      <xs:annotation>
+        <xs:documentation>Ignore any errors in the include file.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLogVariable">
+    <xs:attribute name="name" type="xs:string" use="required">
+      <xs:annotation>
+        <xs:documentation>Variable name.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="value" type="SimpleLayoutAttribute" use="required">
+      <xs:annotation>
+        <xs:documentation>Variable value.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:simpleType name="NLogTargetIDList">
+    <xs:restriction base="xs:string">
+      <xs:pattern value="(|([a-zA-Z][a-zA-Z0-9_\-]*))(,([a-zA-Z][a-zA-Z0-9_\-]*))*" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="Target" abstract="true"></xs:complexType>
+  <xs:complexType name="TargetRef">
+    <xs:attribute name="name" type="xs:string" use="required" />
+  </xs:complexType>
+  <xs:complexType name="WrapperTargetBase" abstract="true">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="target" type="Target" minOccurs="1" maxOccurs="1" />
+          <xs:element name="wrapper-target" type="WrapperTargetBase" minOccurs="1" maxOccurs="1" />
+          <xs:element name="compound-target" type="CompoundTargetBase" minOccurs="1" maxOccurs="1" />
+          <xs:element name="target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+          <xs:element name="wrapper-target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+          <xs:element name="compound-target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+        </xs:choice>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="CompoundTargetBase" abstract="true">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="target" type="Target" minOccurs="1" maxOccurs="unbounded" />
+          <xs:element name="wrapper-target" type="WrapperTargetBase" minOccurs="1" maxOccurs="1" />
+          <xs:element name="compound-target" type="CompoundTargetBase" minOccurs="1" maxOccurs="1" />
+          <xs:element name="target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+          <xs:element name="wrapper-target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+          <xs:element name="compound-target-ref" type="TargetRef" minOccurs="1" maxOccurs="1" />
+        </xs:choice>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Filter" abstract="true"></xs:complexType>
+  <xs:complexType name="TimeSource" abstract="true"></xs:complexType>
+  <xs:simpleType name="SimpleLayoutAttribute">
+    <xs:restriction base="xs:string">
+      <xs:pattern value=".*" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="Condition">
+    <xs:restriction base="xs:string">
+      <xs:minLength value="1" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="AsyncWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="batchSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="fullBatchSizeWriteLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="overflowAction" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.AsyncTargetWrapperOverflowAction" />
+          <xs:element name="queueLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="timeToSleepBetweenBatches" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="batchSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of log events that should be processed in a batch by the lazy writer thread.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="fullBatchSizeWriteLimit" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Limit of full s to write before yielding into  Performance is better when writing many small batches, than writing a single large batch</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="overflowAction" type="NLog.Targets.Wrappers.AsyncTargetWrapperOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when the lazy writer thread request queue count exceeds the set limit.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="queueLimit" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Limit on the number of requests in the lazy writer thread request queue.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="timeToSleepBetweenBatches" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Time in milliseconds to sleep between batches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.Wrappers.AsyncTargetWrapperOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Grow" />
+      <xs:enumeration value="Discard" />
+      <xs:enumeration value="Block" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="AutoFlushWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="asyncFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="asyncFlush" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Delay the flush until the LogEvent has been confirmed as written</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="condition" type="Condition">
+          <xs:annotation>
+            <xs:documentation>Condition expression. Log events who meet this condition will cause a flush on the wrapped target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="BufferingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="bufferSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="flushTimeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="slidingTimeout" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="bufferSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of log events to be buffered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="flushTimeout" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="slidingTimeout" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use sliding timeout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Chainsaw">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="lineEnding" minOccurs="0" maxOccurs="1" type="LineEndingMode" />
+          <xs:element name="maxMessageSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="newLine" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="onConnectionOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetConnectionsOverflowAction" />
+          <xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetOverflowAction" />
+          <xs:element name="maxConnections" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepConnection" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="connectionCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
+          <xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeNLogData" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="appInfo" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="ndcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Instance of  that is used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="lineEnding" type="LineEndingMode">
+          <xs:annotation>
+            <xs:documentation>End of line value if a newline is appended at the end of log message .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxMessageSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum message size in bytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="newLine" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to append newline at the end of log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onConnectionOverflow" type="NLog.Targets.NetworkTargetConnectionsOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the will be more connections than .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onOverflow" type="NLog.Targets.NetworkTargetOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the message is larger than maxMessageSize.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxConnections" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum current connections. 0 = no maximum.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepConnection" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep connection open whenever possible.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Size of the connection cache (number of connections which are kept alive).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="address" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Network address.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxQueueSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum queue size.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  stack contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeSourceInfo" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include source info (file name and line number) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNLogData" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include NLog-specific extensions to log4j schema.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  dictionary contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeCallSite" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include call site (class and method name) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="appInfo" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>AppInfo field. By default it's the friendly name of the current AppDomain.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ndcItemSeparator" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>NDC item separator.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  dictionary contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.NetworkTargetConnectionsOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="AllowNewConnnection" />
+      <xs:enumeration value="DiscardMessage" />
+      <xs:enumeration value="Block" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.NetworkTargetOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Error" />
+      <xs:enumeration value="Split" />
+      <xs:enumeration value="Discard" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLog.Targets.NLogViewerParameterInfo">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that should be use to calcuate the value for the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Viewer parameter name.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="ColoredConsole">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="useDefaultRowHighlightingRules" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="highlight-row" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.ConsoleRowHighlightingRule" />
+          <xs:element name="highlight-word" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.ConsoleWordHighlightingRule" />
+          <xs:element name="detectConsoleAvailable" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="errorStream" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useDefaultRowHighlightingRules" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use default row highlighting rules.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="detectConsoleAvailable" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to auto-check if the console is available. - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>The encoding for writing messages to the .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="errorStream" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether the error stream (stderr) should be used instead of the output stream (stdout).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.ConsoleOutputColor">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Black" />
+      <xs:enumeration value="DarkBlue" />
+      <xs:enumeration value="DarkGreen" />
+      <xs:enumeration value="DarkCyan" />
+      <xs:enumeration value="DarkRed" />
+      <xs:enumeration value="DarkMagenta" />
+      <xs:enumeration value="DarkYellow" />
+      <xs:enumeration value="Gray" />
+      <xs:enumeration value="DarkGray" />
+      <xs:enumeration value="Blue" />
+      <xs:enumeration value="Green" />
+      <xs:enumeration value="Cyan" />
+      <xs:enumeration value="Red" />
+      <xs:enumeration value="Magenta" />
+      <xs:enumeration value="Yellow" />
+      <xs:enumeration value="White" />
+      <xs:enumeration value="NoChange" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLog.Targets.ConsoleRowHighlightingRule">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
+      <xs:element name="backgroundColor" minOccurs="0" maxOccurs="1" type="NLog.Targets.ConsoleOutputColor" />
+      <xs:element name="foregroundColor" minOccurs="0" maxOccurs="1" type="NLog.Targets.ConsoleOutputColor" />
+    </xs:choice>
+    <xs:attribute name="condition" type="Condition">
+      <xs:annotation>
+        <xs:documentation>Condition that must be met in order to set the specified foreground and background color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="backgroundColor" type="NLog.Targets.ConsoleOutputColor">
+      <xs:annotation>
+        <xs:documentation>Background color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="foregroundColor" type="NLog.Targets.ConsoleOutputColor">
+      <xs:annotation>
+        <xs:documentation>Foreground color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLog.Targets.ConsoleWordHighlightingRule">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="regex" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="text" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="wholeWords" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="compileRegex" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="backgroundColor" minOccurs="0" maxOccurs="1" type="NLog.Targets.ConsoleOutputColor" />
+      <xs:element name="foregroundColor" minOccurs="0" maxOccurs="1" type="NLog.Targets.ConsoleOutputColor" />
+    </xs:choice>
+    <xs:attribute name="ignoreCase" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to ignore case when comparing texts.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="regex" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Regular expression to be matched. You must specify either text or regex.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="text" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Text to be matched. You must specify either text or regex.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="wholeWords" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to match whole words only.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="compileRegex" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="backgroundColor" type="NLog.Targets.ConsoleOutputColor">
+      <xs:annotation>
+        <xs:documentation>Background color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="foregroundColor" type="NLog.Targets.ConsoleOutputColor">
+      <xs:annotation>
+        <xs:documentation>Foreground color.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="Console">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="error" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="detectConsoleAvailable" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="error" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to send the log messages to the standard error instead of the standard output.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="detectConsoleAvailable" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to auto-check if the console is available - Disables console writing if Environment.UserInteractive = False (Windows Service) - Disables console writing if Console Standard Input is not available (Non-Console-App)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>The encoding for writing messages to the .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Database">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="useTransactions" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="dbUserName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="dbProvider" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="dbPassword" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="keepConnection" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="dbDatabase" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="connectionStringName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="connectionString" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="dbHost" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="installConnectionString" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="install-command" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.DatabaseCommandInfo" />
+          <xs:element name="uninstall-command" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.DatabaseCommandInfo" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.DatabaseParameterInfo" />
+          <xs:element name="commandText" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="commandType" minOccurs="0" maxOccurs="1" type="System.Data.CommandType" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useTransactions" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbUserName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbProvider" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the database provider.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbPassword" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepConnection" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep the database connection open between the log events.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbDatabase" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionStringName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the connection string (as specified in &lt;connectionStrings&gt; configuration section.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionString" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="dbHost" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="installConnectionString" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="commandText" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text of the SQL command to be run on each log level.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="commandType" type="System.Data.CommandType">
+          <xs:annotation>
+            <xs:documentation>Type of the SQL command to be run on each log level.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="System.Data.CommandType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Text" />
+      <xs:enumeration value="StoredProcedure" />
+      <xs:enumeration value="TableDirect" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLog.Targets.DatabaseCommandInfo">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="commandType" minOccurs="0" maxOccurs="1" type="System.Data.CommandType" />
+      <xs:element name="connectionString" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="ignoreFailures" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.DatabaseParameterInfo" />
+      <xs:element name="text" minOccurs="0" maxOccurs="1" type="Layout" />
+    </xs:choice>
+    <xs:attribute name="commandType" type="System.Data.CommandType">
+      <xs:annotation>
+        <xs:documentation>Type of the command.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="connectionString" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Connection string to run the command against. If not provided, connection string from the target is used.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="ignoreFailures" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to ignore failures.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="text" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Command text.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="NLog.Targets.DatabaseParameterInfo">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="precision" minOccurs="0" maxOccurs="1" type="xs:byte" />
+      <xs:element name="scale" minOccurs="0" maxOccurs="1" type="xs:byte" />
+      <xs:element name="size" minOccurs="0" maxOccurs="1" type="xs:integer" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that should be use to calcuate the value for the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Database parameter name.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="precision" type="xs:byte">
+      <xs:annotation>
+        <xs:documentation>Database parameter precision.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="scale" type="xs:byte">
+      <xs:annotation>
+        <xs:documentation>Database parameter scale.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="size" type="xs:integer">
+      <xs:annotation>
+        <xs:documentation>Database parameter size.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="Debugger">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Debug">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="EventLog">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="category" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="eventId" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="log" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="machineName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="source" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.EventLogTargetOverflowAction" />
+          <xs:element name="entryType" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="maxKilobytes" minOccurs="0" maxOccurs="1" type="xs:long" />
+          <xs:element name="maxMessageLength" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="category" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout that renders event Category.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="eventId" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout that renders event ID.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="log" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the Event Log to write to. This can be System, Application or any user-defined name.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="machineName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the machine on which Event Log service is running.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="source" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Value to be used as the event Source.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onOverflow" type="NLog.Targets.EventLogTargetOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action to take if the message is larger than the  option.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="entryType" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Optional entrytype. When not set, or when not convertable to  then determined by </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxKilobytes" type="xs:long">
+          <xs:annotation>
+            <xs:documentation>Maximum Event log size in kilobytes. If null, the value won't be set. Default is 512 Kilobytes as specified by Eventlog API</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxMessageLength" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Message length limit to write to the Event Log.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.EventLogTargetOverflowAction">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Truncate" />
+      <xs:enumeration value="Split" />
+      <xs:enumeration value="Discard" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="FallbackGroup">
+    <xs:complexContent>
+      <xs:extension base="CompoundTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="returnToFirstOnSuccess" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="returnToFirstOnSuccess" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to return to the first target after any successful write.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="File">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="lineEnding" minOccurs="0" maxOccurs="1" type="LineEndingMode" />
+          <xs:element name="archiveNumbering" minOccurs="0" maxOccurs="1" type="NLog.Targets.ArchiveNumberingMode" />
+          <xs:element name="archiveFileName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="archiveEvery" minOccurs="0" maxOccurs="1" type="NLog.Targets.FileArchivePeriod" />
+          <xs:element name="archiveAboveSize" minOccurs="0" maxOccurs="1" type="xs:long" />
+          <xs:element name="enableArchiveFileCompression" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxArchiveFiles" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="forceManaged" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="archiveFileKind" minOccurs="0" maxOccurs="1" type="NLog.Targets.FilePathKind" />
+          <xs:element name="cleanupFileName" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="discardAll" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="fileNameKind" minOccurs="0" maxOccurs="1" type="NLog.Targets.FilePathKind" />
+          <xs:element name="forceMutexConcurrentWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="writeFooterOnArchivingOnly" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="fileName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="archiveDateFormat" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="archiveOldFileOnStartup" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="createDirs" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="enableFileDelete" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="fileAttributes" minOccurs="0" maxOccurs="1" type="NLog.Targets.Win32FileAttributes" />
+          <xs:element name="deleteOldFileOnStartup" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="replaceFileContentsOnEachWrite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="concurrentWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="keepFileOpen" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxLogFilenames" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="networkWrites" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="openFileCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="openFileCacheTimeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="bufferSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="autoFlush" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="concurrentWriteAttemptDelay" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="concurrentWriteAttempts" minOccurs="0" maxOccurs="1" type="xs:integer" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>File encoding.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="lineEnding" type="LineEndingMode">
+          <xs:annotation>
+            <xs:documentation>Line ending mode.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveNumbering" type="NLog.Targets.ArchiveNumberingMode">
+          <xs:annotation>
+            <xs:documentation>Way file archives are numbered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveFileName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Name of the file to be used for an archive.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveEvery" type="NLog.Targets.FileArchivePeriod">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to automatically archive log files every time the specified time passes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveAboveSize" type="xs:long">
+          <xs:annotation>
+            <xs:documentation>Size in bytes above which log files will be automatically archived. Warning: combining this with  isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="enableArchiveFileCompression" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to compress archive files into the zip archive format.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxArchiveFiles" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum number of archive files that should be kept.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="forceManaged" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveFileKind" type="NLog.Targets.FilePathKind">
+          <xs:annotation>
+            <xs:documentation>Is the  an absolute or relative path?</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="cleanupFileName" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="discardAll" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Whether or not this target should just discard all data that its asked to write. Mostly used for when testing NLog Stack except final write</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="fileNameKind" type="NLog.Targets.FilePathKind">
+          <xs:annotation>
+            <xs:documentation>Is the  an absolute or relative path?</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="forceMutexConcurrentWrites" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Value indicationg whether file creation calls should be synchronized by a system global mutex.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="writeFooterOnArchivingOnly" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether the footer should be written only when the file is archived.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="fileName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Name of the file to write to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveDateFormat" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Value specifying the date format to use when archiving files.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="archiveOldFileOnStartup" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to archive old log file on startup.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="createDirs" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to create directories if they do not exist.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="enableFileDelete" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to enable log file(s) to be deleted.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="fileAttributes" type="NLog.Targets.Win32FileAttributes">
+          <xs:annotation>
+            <xs:documentation>File attributes (Windows only).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="deleteOldFileOnStartup" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to delete old log file on startup.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="replaceFileContentsOnEachWrite" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to replace file contents on each write instead of appending log message at the end.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="concurrentWrites" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether concurrent writes to the log file by multiple processes on the same host.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepFileOpen" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep log file open instead of opening and closing it on each logging event.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxLogFilenames" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum number of log filenames that should be stored as existing.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="networkWrites" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether concurrent writes to the log file by multiple processes on different network hosts.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="openFileCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="openFileCacheTimeout" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="bufferSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Log file buffer size in bytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="autoFlush" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to automatically flush the file buffers after each log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="concurrentWriteAttemptDelay" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Delay in milliseconds to wait before attempting to write to the file again.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="concurrentWriteAttempts" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of times the write is appended on the file before NLog discards the log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.ArchiveNumberingMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Sequence" />
+      <xs:enumeration value="Rolling" />
+      <xs:enumeration value="Date" />
+      <xs:enumeration value="DateAndSequence" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.FileArchivePeriod">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="None" />
+      <xs:enumeration value="Year" />
+      <xs:enumeration value="Month" />
+      <xs:enumeration value="Day" />
+      <xs:enumeration value="Hour" />
+      <xs:enumeration value="Minute" />
+      <xs:enumeration value="Sunday" />
+      <xs:enumeration value="Monday" />
+      <xs:enumeration value="Tuesday" />
+      <xs:enumeration value="Wednesday" />
+      <xs:enumeration value="Thursday" />
+      <xs:enumeration value="Friday" />
+      <xs:enumeration value="Saturday" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.FilePathKind">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Unknown" />
+      <xs:enumeration value="Relative" />
+      <xs:enumeration value="Absolute" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.Win32FileAttributes">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="ReadOnly" />
+      <xs:enumeration value="Hidden" />
+      <xs:enumeration value="System" />
+      <xs:enumeration value="Archive" />
+      <xs:enumeration value="Device" />
+      <xs:enumeration value="Normal" />
+      <xs:enumeration value="Temporary" />
+      <xs:enumeration value="SparseFile" />
+      <xs:enumeration value="ReparsePoint" />
+      <xs:enumeration value="Compressed" />
+      <xs:enumeration value="NotContentIndexed" />
+      <xs:enumeration value="Encrypted" />
+      <xs:enumeration value="WriteThrough" />
+      <xs:enumeration value="NoBuffering" />
+      <xs:enumeration value="DeleteOnClose" />
+      <xs:enumeration value="PosixSemantics" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="FilteringWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="condition" type="Condition">
+          <xs:annotation>
+            <xs:documentation>Condition expression. Log events who meet this condition will be forwarded to the wrapped target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="ImpersonatingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="domain" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="impersonationLevel" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.SecurityImpersonationLevel" />
+          <xs:element name="logOnProvider" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.LogOnProviderType" />
+          <xs:element name="logOnType" minOccurs="0" maxOccurs="1" type="NLog.Targets.Wrappers.SecurityLogOnType" />
+          <xs:element name="password" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="revertToSelf" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="userName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="domain" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Windows domain name to change context to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="impersonationLevel" type="NLog.Targets.Wrappers.SecurityImpersonationLevel">
+          <xs:annotation>
+            <xs:documentation>Required impersonation level.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="logOnProvider" type="NLog.Targets.Wrappers.LogOnProviderType">
+          <xs:annotation>
+            <xs:documentation>Type of the logon provider.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="logOnType" type="NLog.Targets.Wrappers.SecurityLogOnType">
+          <xs:annotation>
+            <xs:documentation>Logon Type.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="password" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>User account password.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="revertToSelf" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to revert to the credentials of the process instead of impersonating another user.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="userName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Username to change context to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.Wrappers.SecurityImpersonationLevel">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Anonymous" />
+      <xs:enumeration value="Identification" />
+      <xs:enumeration value="Impersonation" />
+      <xs:enumeration value="Delegation" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.Wrappers.LogOnProviderType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Default" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Targets.Wrappers.SecurityLogOnType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Interactive" />
+      <xs:enumeration value="Network" />
+      <xs:enumeration value="Batch" />
+      <xs:enumeration value="Service" />
+      <xs:enumeration value="NetworkClearText" />
+      <xs:enumeration value="NewCredentials" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="LimitingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="interval" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="messageLimit" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="interval" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Interval in which messages will be written up to the  number of messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="messageLimit" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum allowed number of messages written per .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="LogReceiverService">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="endpointAddress" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="endpointConfigurationName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="useOneWayContract" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="clientId" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="includeEventProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.MethodCallParameter" />
+          <xs:element name="useBinaryEncoding" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="endpointAddress" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Endpoint address.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="endpointConfigurationName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the endpoint configuration in WCF configuration file.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useOneWayContract" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="clientId" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Client ID.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeEventProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include per-event properties in the payload sent to the server.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useBinaryEncoding" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use binary message encoding.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLog.Targets.MethodCallParameter">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="parameterType" minOccurs="0" maxOccurs="1" type="xs:string" />
+      <xs:element name="type" minOccurs="0" maxOccurs="1" type="xs:string" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that should be use to calculate the value for the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Name of the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="parameterType" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Type of the parameter.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="type" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Type of the parameter. Obsolete alias for </xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="Mail">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="html" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="addNewLines" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="cc" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="to" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="bcc" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="body" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="subject" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="from" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="timeout" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="priority" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="replaceNewlineWithBrTagInHtml" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="smtpServer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="smtpAuthentication" minOccurs="0" maxOccurs="1" type="NLog.Targets.SmtpAuthenticationMode" />
+          <xs:element name="smtpUserName" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="smtpPassword" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="enableSsl" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="smtpPort" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="useSystemNetMailSettings" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="pickupDirectoryLocation" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="deliveryMethod" minOccurs="0" maxOccurs="1" type="System.Net.Mail.SmtpDeliveryMethod" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Text to be rendered.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="html" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to send message as HTML instead of plain text.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used for sending e-mail.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="addNewLines" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to add new lines between log entries.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="cc" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="to" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="bcc" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="body" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Mail message body (repeated for each log message send in one mail).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="subject" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Mail subject.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="from" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Sender's email address (e.g. joe@domain.com).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="timeout" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Indicates the SMTP client timeout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="priority" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Priority used for sending mails.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="replaceNewlineWithBrTagInHtml" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether NewLine characters in the body should be replaced with  tags.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpServer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>SMTP Server to be used for sending.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpAuthentication" type="NLog.Targets.SmtpAuthenticationMode">
+          <xs:annotation>
+            <xs:documentation>SMTP Authentication mode.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpUserName" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic").</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpPassword" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic").</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="enableSsl" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="smtpPort" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Port number that SMTP Server is listening on.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useSystemNetMailSettings" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether the default Settings from System.Net.MailSettings should be used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="pickupDirectoryLocation" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Folder where applications save mail messages to be processed by the local SMTP server.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="deliveryMethod" type="System.Net.Mail.SmtpDeliveryMethod">
+          <xs:annotation>
+            <xs:documentation>Specifies how outgoing email messages will be handled.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.SmtpAuthenticationMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="None" />
+      <xs:enumeration value="Basic" />
+      <xs:enumeration value="Ntlm" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="System.Net.Mail.SmtpDeliveryMethod">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Network" />
+      <xs:enumeration value="SpecifiedPickupDirectory" />
+      <xs:enumeration value="PickupDirectoryFromIis" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="Memory">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="MSMQ">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="useXmlEncoding" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="checkIfQueueExists" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="createQueueIfNotExists" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="label" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="queue" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="recoverable" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used when writing text to the queue.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="useXmlEncoding" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use the XML format when serializing message. This will also disable creating queues.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="checkIfQueueExists" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to check if a queue exists before writing to it.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="createQueueIfNotExists" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to create the queue if it doesn't exists.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="label" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Label to associate with each message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="queue" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Name of the queue to write to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="recoverable" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to use recoverable messages (with guaranteed delivery).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="MethodCall">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="className" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="methodName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.MethodCallParameter" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="className" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Class name.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="methodName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Network">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="lineEnding" minOccurs="0" maxOccurs="1" type="LineEndingMode" />
+          <xs:element name="maxMessageSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="newLine" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="onConnectionOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetConnectionsOverflowAction" />
+          <xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetOverflowAction" />
+          <xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="connectionCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepConnection" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="maxConnections" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="lineEnding" type="LineEndingMode">
+          <xs:annotation>
+            <xs:documentation>End of line value if a newline is appended at the end of log message .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxMessageSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum message size in bytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="newLine" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to append newline at the end of log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onConnectionOverflow" type="NLog.Targets.NetworkTargetConnectionsOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the will be more connections than .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onOverflow" type="NLog.Targets.NetworkTargetOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the message is larger than maxMessageSize.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="address" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Network address.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Size of the connection cache (number of connections which are kept alive).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepConnection" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep connection open whenever possible.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxConnections" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum current connections. 0 = no maximum.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxQueueSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum queue size.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLogViewer">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="lineEnding" minOccurs="0" maxOccurs="1" type="LineEndingMode" />
+          <xs:element name="maxMessageSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="newLine" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="onConnectionOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetConnectionsOverflowAction" />
+          <xs:element name="onOverflow" minOccurs="0" maxOccurs="1" type="NLog.Targets.NetworkTargetOverflowAction" />
+          <xs:element name="maxConnections" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="keepConnection" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="connectionCacheSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="address" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="maxQueueSize" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.NLogViewerParameterInfo" />
+          <xs:element name="includeNdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeSourceInfo" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeNLogData" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeCallSite" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="appInfo" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="ndcItemSeparator" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding to be used.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Instance of  that is used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="lineEnding" type="LineEndingMode">
+          <xs:annotation>
+            <xs:documentation>End of line value if a newline is appended at the end of log message .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxMessageSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum message size in bytes.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="newLine" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to append newline at the end of log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onConnectionOverflow" type="NLog.Targets.NetworkTargetConnectionsOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the will be more connections than .</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="onOverflow" type="NLog.Targets.NetworkTargetOverflowAction">
+          <xs:annotation>
+            <xs:documentation>Action that should be taken if the message is larger than maxMessageSize.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxConnections" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum current connections. 0 = no maximum.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="keepConnection" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to keep connection open whenever possible.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="connectionCacheSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Size of the connection cache (number of connections which are kept alive).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="address" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Network address.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="maxQueueSize" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Maximum queue size.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  stack contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeSourceInfo" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include source info (file name and line number) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeNLogData" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include NLog-specific extensions to log4j schema.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  dictionary contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeCallSite" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include call site (class and method name) in the information sent over the network.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="appInfo" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>AppInfo field. By default it's the friendly name of the current AppDomain.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ndcItemSeparator" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>NDC item separator.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include  dictionary contents.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Null">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="formatMessage" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="formatMessage" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to perform layout calculation.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="OutputDebugString">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="PerfCounter">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="autoCreate" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="categoryName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="counterHelp" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="counterName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="counterType" minOccurs="0" maxOccurs="1" type="System.Diagnostics.PerformanceCounterType" />
+          <xs:element name="incrementValue" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="instanceName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="autoCreate" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether performance counter should be automatically created.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="categoryName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the performance counter category.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="counterHelp" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Counter help text.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="counterName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the performance counter.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="counterType" type="System.Diagnostics.PerformanceCounterType">
+          <xs:annotation>
+            <xs:documentation>Performance counter type.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="incrementValue" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>The value by which to increment the counter.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="instanceName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Performance counter instance name.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="System.Diagnostics.PerformanceCounterType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="NumberOfItems32" />
+      <xs:enumeration value="NumberOfItems64" />
+      <xs:enumeration value="NumberOfItemsHEX32" />
+      <xs:enumeration value="NumberOfItemsHEX64" />
+      <xs:enumeration value="RateOfCountsPerSecond32" />
+      <xs:enumeration value="RateOfCountsPerSecond64" />
+      <xs:enumeration value="CountPerTimeInterval32" />
+      <xs:enumeration value="CountPerTimeInterval64" />
+      <xs:enumeration value="RawFraction" />
+      <xs:enumeration value="RawBase" />
+      <xs:enumeration value="AverageTimer32" />
+      <xs:enumeration value="AverageBase" />
+      <xs:enumeration value="AverageCount64" />
+      <xs:enumeration value="SampleFraction" />
+      <xs:enumeration value="SampleCounter" />
+      <xs:enumeration value="SampleBase" />
+      <xs:enumeration value="CounterTimer" />
+      <xs:enumeration value="CounterTimerInverse" />
+      <xs:enumeration value="Timer100Ns" />
+      <xs:enumeration value="Timer100NsInverse" />
+      <xs:enumeration value="ElapsedTime" />
+      <xs:enumeration value="CounterMultiTimer" />
+      <xs:enumeration value="CounterMultiTimerInverse" />
+      <xs:enumeration value="CounterMultiTimer100Ns" />
+      <xs:enumeration value="CounterMultiTimer100NsInverse" />
+      <xs:enumeration value="CounterMultiBase" />
+      <xs:enumeration value="CounterDelta32" />
+      <xs:enumeration value="CounterDelta64" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="PostFilteringWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="defaultFilter" minOccurs="0" maxOccurs="1" type="Condition" />
+          <xs:element name="when" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.Wrappers.FilteringRule" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="defaultFilter" type="Condition">
+          <xs:annotation>
+            <xs:documentation>Default filter to be applied when no specific rule matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLog.Targets.Wrappers.FilteringRule">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="exists" minOccurs="0" maxOccurs="1" type="Condition" />
+      <xs:element name="filter" minOccurs="0" maxOccurs="1" type="Condition" />
+    </xs:choice>
+    <xs:attribute name="exists" type="Condition">
+      <xs:annotation>
+        <xs:documentation>Condition to be tested.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="filter" type="Condition">
+      <xs:annotation>
+        <xs:documentation>Resulting filter to be applied when the condition matches.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="RandomizeGroup">
+    <xs:complexContent>
+      <xs:extension base="CompoundTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="RepeatingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="repeatCount" minOccurs="0" maxOccurs="1" type="xs:integer" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="repeatCount" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of times to repeat each log message.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="RetryingWrapper">
+    <xs:complexContent>
+      <xs:extension base="WrapperTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="retryCount" minOccurs="0" maxOccurs="1" type="xs:integer" />
+          <xs:element name="retryDelayMilliseconds" minOccurs="0" maxOccurs="1" type="xs:integer" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="retryCount" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Number of retries that should be attempted on the wrapped target in case of a failure.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="retryDelayMilliseconds" type="xs:integer">
+          <xs:annotation>
+            <xs:documentation>Time to wait between retries in milliseconds.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="RoundRobinGroup">
+    <xs:complexContent>
+      <xs:extension base="CompoundTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="SplitGroup">
+    <xs:complexContent>
+      <xs:extension base="CompoundTargetBase">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Trace">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout used to format log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="WebService">
+    <xs:complexContent>
+      <xs:extension base="Target">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="includeBOM" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="parameter" minOccurs="0" maxOccurs="unbounded" type="NLog.Targets.MethodCallParameter" />
+          <xs:element name="optimizeBufferReuse" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="encoding" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="escapeDataNLogLegacy" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="escapeDataRfc3986" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="methodName" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="namespace" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="protocol" minOccurs="0" maxOccurs="1" type="NLog.Targets.WebServiceProtocol" />
+          <xs:element name="url" minOccurs="0" maxOccurs="1" type="xs:anyURI" />
+          <xs:element name="xmlRoot" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="xmlRootNamespace" minOccurs="0" maxOccurs="1" type="xs:string" />
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the target.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeBOM" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Should we include the BOM (Byte-order-mark) for UTF? Influences the  property. This will only work for UTF-8.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="optimizeBufferReuse" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers Required for legacy NLog-targets, that expects buffers to remain stable after Write-method exit</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="encoding" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Encoding.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="escapeDataNLogLegacy" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Value whether escaping be done according to the old NLog style (Very non-standard)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="escapeDataRfc3986" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs)</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="methodName" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Web service method name. Only used with Soap.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="namespace" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Web service namespace. Only used with Soap.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="protocol" type="NLog.Targets.WebServiceProtocol">
+          <xs:annotation>
+            <xs:documentation>Protocol to be used when calling web service.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="url" type="xs:anyURI">
+          <xs:annotation>
+            <xs:documentation>Web service URL.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="xmlRoot" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Name of the root XML element, if POST of XML document chosen. If so, this property must not be null. (see  and ).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="xmlRootNamespace" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>(optional) root namespace of the XML document, if POST of XML document chosen. (see  and ).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Targets.WebServiceProtocol">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Soap11" />
+      <xs:enumeration value="Soap12" />
+      <xs:enumeration value="HttpPost" />
+      <xs:enumeration value="HttpGet" />
+      <xs:enumeration value="JsonPost" />
+      <xs:enumeration value="XmlPost" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="CompoundLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="layout" minOccurs="0" maxOccurs="unbounded" type="Layout" />
+        </xs:choice>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Layout">
+    <xs:choice minOccurs="0" maxOccurs="unbounded" />
+  </xs:complexType>
+  <xs:complexType name="CsvLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="column" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.CsvColumn" />
+          <xs:element name="customColumnDelimiter" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="delimiter" minOccurs="0" maxOccurs="1" type="NLog.Layouts.CsvColumnDelimiterMode" />
+          <xs:element name="quoteChar" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="quoting" minOccurs="0" maxOccurs="1" type="NLog.Layouts.CsvQuotingMode" />
+          <xs:element name="withHeader" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer layout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header layout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Body layout (can be repeated multiple times).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="customColumnDelimiter" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom').</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="delimiter" type="NLog.Layouts.CsvColumnDelimiterMode">
+          <xs:annotation>
+            <xs:documentation>Column delimiter.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="quoteChar" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Quote Character.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="quoting" type="NLog.Layouts.CsvQuotingMode">
+          <xs:annotation>
+            <xs:documentation>Quoting mode.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="withHeader" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether CVS should include header.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="NLog.Layouts.CsvColumnDelimiterMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Auto" />
+      <xs:enumeration value="Comma" />
+      <xs:enumeration value="Semicolon" />
+      <xs:enumeration value="Tab" />
+      <xs:enumeration value="Pipe" />
+      <xs:enumeration value="Space" />
+      <xs:enumeration value="Custom" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="NLog.Layouts.CsvQuotingMode">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="All" />
+      <xs:enumeration value="Nothing" />
+      <xs:enumeration value="Auto" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="NLog.Layouts.CsvColumn">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+    </xs:choice>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout of the column.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Name of the column.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="JsonLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="attribute" minOccurs="0" maxOccurs="unbounded" type="NLog.Layouts.JsonAttribute" />
+          <xs:element name="excludeProperties" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="renderEmptyObject" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="suppressSpaces" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="excludeProperties" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>List of property names to exclude when  is true</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeAllProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to include all properties from the log events</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="renderEmptyObject" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to render the empty object value {}</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="suppressSpaces" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to suppress the extra spaces in the output json</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="NLog.Layouts.JsonAttribute">
+    <xs:choice minOccurs="0" maxOccurs="unbounded">
+      <xs:element name="encode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="escapeUnicode" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+      <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+      <xs:element name="name" minOccurs="0" maxOccurs="1" type="xs:string" />
+    </xs:choice>
+    <xs:attribute name="encode" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Determines wether or not this attribute will be Json encoded.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="escapeUnicode" type="xs:boolean">
+      <xs:annotation>
+        <xs:documentation>Indicates whether to escape non-ascii characters</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="layout" type="SimpleLayoutAttribute">
+      <xs:annotation>
+        <xs:documentation>Layout that will be rendered as the attribute's value.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+    <xs:attribute name="name" type="xs:string">
+      <xs:annotation>
+        <xs:documentation>Name of the attribute.</xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
+  </xs:complexType>
+  <xs:complexType name="LayoutWithHeaderAndFooter">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="footer" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="header" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+        </xs:choice>
+        <xs:attribute name="footer" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Footer layout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="header" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Header layout.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Body layout (can be repeated multiple times).</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="Log4JXmlEventLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="includeAllProperties" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="includeMdlc" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+        </xs:choice>
+        <xs:attribute name="includeAllProperties" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Option to include all properties from the log events</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="includeMdlc" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to include contents of the  dictionary.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="SimpleLayout">
+    <xs:complexContent>
+      <xs:extension base="Layout">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="text" minOccurs="0" maxOccurs="1" type="xs:string" />
+        </xs:choice>
+        <xs:attribute name="text" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Layout text.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="when">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="condition" minOccurs="0" maxOccurs="1" type="Condition" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="condition" type="Condition">
+          <xs:annotation>
+            <xs:documentation>Condition expression.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:simpleType name="FilterResult">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="Neutral" />
+      <xs:enumeration value="Log" />
+      <xs:enumeration value="Ignore" />
+      <xs:enumeration value="LogFinal" />
+      <xs:enumeration value="IgnoreFinal" />
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:complexType name="whenContains">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="substring" minOccurs="0" maxOccurs="1" type="xs:string" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ignoreCase" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to ignore case when comparing strings.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="substring" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Substring to be matched.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="whenEqual">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="compareTo" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="compareTo" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>String to compare the layout to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ignoreCase" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to ignore case when comparing strings.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="whenNotContains">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+          <xs:element name="substring" minOccurs="0" maxOccurs="1" type="xs:string" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ignoreCase" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to ignore case when comparing strings.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="substring" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>Substring to be matched.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="whenNotEqual">
+    <xs:complexContent>
+      <xs:extension base="Filter">
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+          <xs:element name="action" minOccurs="0" maxOccurs="1" type="FilterResult" />
+          <xs:element name="compareTo" minOccurs="0" maxOccurs="1" type="xs:string" />
+          <xs:element name="ignoreCase" minOccurs="0" maxOccurs="1" type="xs:boolean" />
+          <xs:element name="layout" minOccurs="0" maxOccurs="1" type="Layout" />
+        </xs:choice>
+        <xs:attribute name="action" type="FilterResult">
+          <xs:annotation>
+            <xs:documentation>Action to be taken when filter matches.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="compareTo" type="xs:string">
+          <xs:annotation>
+            <xs:documentation>String to compare the layout to.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="ignoreCase" type="xs:boolean">
+          <xs:annotation>
+            <xs:documentation>Indicates whether to ignore case when comparing strings.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="layout" type="SimpleLayoutAttribute">
+          <xs:annotation>
+            <xs:documentation>Layout to be used to filter log messages.</xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="AccurateLocal">
+    <xs:complexContent>
+      <xs:extension base="TimeSource">
+        <xs:choice minOccurs="0" maxOccurs="unbounded" />
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="AccurateUTC">
+    <xs:complexContent>
+      <xs:extension base="TimeSource">
+        <xs:choice minOccurs="0" maxOccurs="unbounded" />
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="FastLocal">
+    <xs:complexContent>
+      <xs:extension base="TimeSource">
+        <xs:choice minOccurs="0" maxOccurs="unbounded" />
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="FastUTC">
+    <xs:complexContent>
+      <xs:extension base="TimeSource">
+        <xs:choice minOccurs="0" maxOccurs="unbounded" />
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+</xs:schema>

+ 40 - 0
OCPPServer/NeedConfirmPacket.cs

@@ -0,0 +1,40 @@
+namespace OCPPServer
+{
+    /// <summary>
+    /// 需要被確認的數據包
+    /// </summary>
+    public class NeedConfirmPacket
+    {
+        /// <summary>
+        /// 資料庫的ID
+        /// </summary>
+        public int Id { get; set; }
+
+        public string MachineCustomId { get; set; }
+
+        /// <summary>
+        /// 傳送次數
+        /// </summary>
+        public int SentTimes { get; set; }
+
+        /// <summary>
+        /// 傳送間隔秒數
+        /// </summary>
+        public int SentInterval { get; set; }
+
+        /// <summary>
+        /// 送出訊息
+        /// </summary>
+        public string SentMessage { get; set; }
+
+        /// <summary>
+        /// 送出的UUID
+        /// </summary>
+        public string SentUniqueId { get; set; }
+
+        ///// <summary>
+        ///// 送出的action
+        ///// </summary>
+        public string SentAction { get; set; }
+    }
+}

+ 222 - 0
OCPPServer/OCPPServer.csproj

@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{9EEC9681-46E9-4656-A4D4-102EB36CDEE2}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>OCPPServer</RootNamespace>
+    <AssemblyName>OCPPServer</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <PublishUrl>發行\</PublishUrl>
+    <Install>true</Install>
+    <InstallFrom>Disk</InstallFrom>
+    <UpdateEnabled>false</UpdateEnabled>
+    <UpdateMode>Foreground</UpdateMode>
+    <UpdateInterval>7</UpdateInterval>
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+    <UpdatePeriodically>false</UpdatePeriodically>
+    <UpdateRequired>false</UpdateRequired>
+    <MapFileExtensions>true</MapFileExtensions>
+    <ApplicationRevision>0</ApplicationRevision>
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
+    <UseApplicationTrust>false</UseApplicationTrust>
+    <BootstrapperEnabled>true</BootstrapperEnabled>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="DnsClient, Version=1.0.7.0, Culture=neutral, PublicKeyToken=4574bb5573c51424, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\DnsClient.1.0.7\lib\net45\DnsClient.dll</HintPath>
+    </Reference>
+    <Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
+      <HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
+      <HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="log4net">
+      <HintPath>..\SuperWebSocket\DLL\log4net.dll</HintPath>
+    </Reference>
+    <Reference Include="MongoDB.Bson, Version=2.7.2.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\MongoDB.Bson.2.7.2\lib\net45\MongoDB.Bson.dll</HintPath>
+    </Reference>
+    <Reference Include="MongoDB.Driver, Version=2.7.2.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\MongoDB.Driver.2.7.0\lib\net45\MongoDB.Driver.dll</HintPath>
+    </Reference>
+    <Reference Include="MongoDB.Driver.Core, Version=2.7.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\MongoDB.Driver.Core.2.7.0\lib\net45\MongoDB.Driver.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
+      <HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.ComponentModel.DataAnnotations" />
+    <Reference Include="System.Configuration" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Net.Http, Version=4.1.1.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Net.Http.4.3.3\lib\net46\System.Net.Http.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Security.Cryptography.Algorithms, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Command\ProcessCallErrorCmd.cs" />
+    <Compile Include="Command\ProcessCallResultCmd.cs" />
+    <Compile Include="Common\ListenerConfig.cs" />
+    <Compile Include="Common\OCPPResult.cs" />
+    <Compile Include="Common\LocalAuthorization.cs" />
+    <Compile Include="Handler\FeatureHandler.cs" />
+    <Compile Include="Handler\MakeRequestHandler.cs" />
+    <Compile Include="Handler\OCPPMessageHandler.cs" />
+    <Compile Include="Command\ProcessCallCmd.cs" />
+    <Compile Include="NeedConfirmPacket.cs" />
+    <Compile Include="Protocol\OCPPWSServer.cs" />
+    <Compile Include="Protocol\ClientData.cs" />
+    <Compile Include="SubProtocol\OCPPLog.cs" />
+    <Compile Include="SubProtocol\OCPPLogFactory.cs" />
+    <Compile Include="SubProtocol\OCPPSubCommandParser.cs" />
+    <Compile Include="SubProtocol\OCPPSubProtocol.cs" />
+    <Compile Include="Server.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Handler\MakeConfirmationHandler.cs" />
+    <Compile Include="WSServer.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config">
+      <SubType>Designer</SubType>
+    </None>
+    <Content Include="NLog.config">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <None Include="certificate.pfx">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Include="Config\log4net.config" />
+    <None Include="Config\log4net.unix.config" />
+    <None Include="localhost.pfx">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Include="NLog.xsd">
+      <SubType>Designer</SubType>
+    </None>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\SocketCommon\SuperSocket.Common.Net40.csproj">
+      <Project>{a24f4d38-ba9c-4fd6-95b7-4980de36131a}</Project>
+      <Name>SuperSocket.Common.Net40</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Evcb.Domain\Evcb.Domain.csproj">
+      <Project>{e67aaa23-f1aa-420e-9f6b-4ba9adfa9942}</Project>
+      <Name>Evcb.Domain</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Evcb.Service\Evcb.Service.csproj">
+      <Project>{dafb773d-bbc9-4e14-ab70-5cab24f39cdd}</Project>
+      <Name>Evcb.Service</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Evcb.Utility\Evcb.Utility.csproj">
+      <Project>{a40fff0e-ae48-48ee-bcf2-da7e9dd3f798}</Project>
+      <Name>Evcb.Utility</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\OCPPPacket\OCPPPacket.csproj">
+      <Project>{EB2072E6-C383-48CE-8A33-B5760F32EDC9}</Project>
+      <Name>OCPPPacket</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\Packet\Packet.csproj">
+      <Project>{d507c3f2-c539-4857-8bbf-c391a5f63e33}</Project>
+      <Name>Packet</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\SocketBase\SuperSocket.SocketBase.Net40.csproj">
+      <Project>{40b77789-ea11-4c05-8f52-86711d7bcaaf}</Project>
+      <Name>SuperSocket.SocketBase.Net40</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\SocketEngine\SuperSocket.SocketEngine.Net40.csproj">
+      <Project>{153fef72-191c-43d9-be71-2b351c7ac760}</Project>
+      <Name>SuperSocket.SocketEngine.Net40</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\SuperWebSocket\SuperWebSocket.csproj">
+      <Project>{2dc79e40-bb70-4f6a-b378-905f2fbc6e97}</Project>
+      <Name>SuperWebSocket</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <BootstrapperPackage Include=".NETFramework,Version=v4.5.2">
+      <Visible>False</Visible>
+      <ProductName>Microsoft .NET Framework 4.5.2 %28x86 和 x64%29</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 127 - 0
OCPPServer/OCPPWebSocketHeaderReceiveFilter.cs

@@ -0,0 +1,127 @@
+using SuperSocket.Common;
+using SuperSocket.SocketBase;
+using SuperWebSocket;
+using SuperWebSocket.Protocol;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ConsoleApplication1
+{
+    class OCPPWebSocketHeaderReceiveFilter : WebSocketReceiveFilterBase
+    {
+        private static readonly byte[] m_HeaderTerminator = Encoding.UTF8.GetBytes("\r\n\r\n");
+
+        private readonly SearchMarkState<byte> m_SearchState;
+
+        public OCPPWebSocketHeaderReceiveFilter(IWebSocketSession session)
+            : base(session)
+        {
+            m_SearchState = new SearchMarkState<byte>(m_HeaderTerminator);
+        }
+
+        public override IWebSocketFragment Filter(byte[] readBuffer, int offset, int length, bool isReusableBuffer, out int rest)
+        {
+            rest = 0;
+
+            int prevMatched = m_SearchState.Matched;
+
+            var result = readBuffer.SearchMark(offset, length, m_SearchState);
+
+            if (result < 0)
+            {
+                this.AddArraySegment(readBuffer, offset, length, isReusableBuffer);
+                return null;
+            }
+
+            int findLen = result - offset;
+            string header = string.Empty;
+
+            if (this.BufferSegments.Count > 0)
+            {
+                if (findLen > 0)
+                {
+                    this.AddArraySegment(readBuffer, offset, findLen, false);
+                    header = this.BufferSegments.Decode(Encoding.UTF8);
+                }
+                else
+                {
+                    header = this.BufferSegments.Decode(Encoding.UTF8, 0, this.BufferSegments.Count - prevMatched);
+                }
+            }
+            else
+            {
+                header = Encoding.UTF8.GetString(readBuffer, offset, findLen);
+            }
+
+            var webSocketSession = Session;
+
+            try
+            {
+                WebSocketServer.ParseHandshake(webSocketSession, new StringReader(header));
+            }
+            catch (Exception e)
+            {
+                webSocketSession.Logger.Error("Failed to parse handshake!" + Environment.NewLine + header, e);
+                webSocketSession.Close(CloseReason.ProtocolError);
+                return null;
+            }
+
+            var secWebSocketKey1 = webSocketSession.Items.GetValue<string>(WebSocketConstant.SecWebSocketKey1, string.Empty);
+            var secWebSocketKey2 = webSocketSession.Items.GetValue<string>(WebSocketConstant.SecWebSocketKey2, string.Empty);
+            var secWebSocketVersion = webSocketSession.SecWebSocketVersion;
+
+            rest = length - findLen - (m_HeaderTerminator.Length - prevMatched);
+
+            this.ClearBufferSegments();
+
+            if (string.IsNullOrEmpty(secWebSocketKey1) && string.IsNullOrEmpty(secWebSocketKey2))
+            {
+                //draft-hixie-thewebsocketprotocol-75
+                if (Handshake(webSocketSession.AppServer.WebSocketProtocolProcessor, webSocketSession))
+                    return HandshakeRequestInfo;
+            }
+            else if ("6".Equals(secWebSocketVersion)) //draft-ietf-hybi-thewebsocketprotocol-06
+            {
+                if (Handshake(webSocketSession.AppServer.WebSocketProtocolProcessor, webSocketSession))
+                    return HandshakeRequestInfo;
+            }
+            else
+            {
+                //draft-hixie-thewebsocketprotocol-76/draft-ietf-hybi-thewebsocketprotocol-00
+                //Read SecWebSocketKey3(8 bytes)
+                if (rest == SecKey3Len)
+                {
+                    webSocketSession.Items[WebSocketConstant.SecWebSocketKey3] = readBuffer.CloneRange(offset + length - rest, rest);
+                    rest = 0;
+                    if (Handshake(webSocketSession.AppServer.WebSocketProtocolProcessor, webSocketSession))
+                        return HandshakeRequestInfo;
+                }
+                else if (rest > SecKey3Len)
+                {
+                    webSocketSession.Items[WebSocketConstant.SecWebSocketKey3] = readBuffer.CloneRange(offset + length - rest, 8);
+                    rest -= 8;
+                    if (Handshake(webSocketSession.AppServer.WebSocketProtocolProcessor, webSocketSession))
+                        return HandshakeRequestInfo;
+                }
+                else
+                {
+                    //rest < 8
+                    if (rest > 0)
+                    {
+                        AddArraySegment(readBuffer, offset + length - rest, rest, isReusableBuffer);
+                        rest = 0;
+                    }
+
+                    NextReceiveFilter = new WebSocketSecKey3ReceiveFilter(this);
+                    return null;
+                }
+            }
+
+            return null;
+        }
+    }
+}

+ 35 - 0
OCPPServer/OCPPWebSocketProtocol.cs

@@ -0,0 +1,35 @@
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Protocol;
+using SuperWebSocket;
+using SuperWebSocket.Protocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ConsoleApplication1
+{
+    class OCPPWebSocketProtocol : WebSocketProtocol
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebSocketProtocol"/> class.
+        /// </summary>
+        public OCPPWebSocketProtocol()
+        {
+
+        }
+
+        /// <summary>
+        /// Creates the filter.
+        /// </summary>
+        /// <param name="appServer">The app server.</param>
+        /// <param name="appSession">The app session.</param>
+        /// <param name="remoteEndPoint">The remote end point.</param>
+        /// <returns></returns>
+        public IReceiveFilter<IWebSocketFragment> CreateFilter(IAppServer appServer, IAppSession appSession, System.Net.IPEndPoint remoteEndPoint)
+        {
+            return new OCPPWebSocketHeaderReceiveFilter((IWebSocketSession)appSession);
+        }
+    }
+}

+ 36 - 0
OCPPServer/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 組件的一般資訊是由下列的屬性集控制。
+// 變更這些屬性的值即可修改組件的相關
+// 資訊。
+[assembly: AssemblyTitle("OCPPServer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("OCPPServer")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 將 ComVisible 設定為 false 會使得這個組件中的類型
+// 對 COM 元件而言為不可見。如果您需要從 COM 存取這個組件中
+// 的類型,請在該類型上將 ComVisible 屬性設定為 true。
+[assembly: ComVisible(false)]
+
+// 下列 GUID 為專案公開 (Expose) 至 COM 時所要使用的 typelib ID
+[assembly: Guid("9eec9681-46e9-4656-a4d4-102eb36cdee2")]
+
+// 組件的版本資訊由下列四個值所組成: 
+//
+//      主要版本
+//      次要版本 
+//      組建編號
+//      修訂編號
+//
+// 您可以指定所有的值,也可以依照以下的方式,使用 '*' 將組建和修訂編號
+// 指定為預設值: 
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 240 - 0
OCPPServer/Protocol/ClientData.cs

@@ -0,0 +1,240 @@
+using OCPPPacket.Packet.Messages.Basic;
+using Packet.Cmd;
+using SuperSocket.SocketBase;
+using SuperWebSocket;
+using System;
+using System.Collections.Generic;
+
+namespace OCPPServer.Protocol
+{
+    public class ClientData : WebSocketSession<ClientData>
+    {
+        public string ocppmessage = String.Empty;
+
+        public delegate void OCPPClientDataEventHandler<ClientData, String>(ClientData clientdata, String msg);
+
+        public event OCPPClientDataEventHandler<ClientData, String> m_ReceiveData;
+
+        public CmdHelper CmdHelper;
+
+        public bool IsCheckIn { get; set; }
+        public string MachineId { get; set; }
+
+        public Guid CustomerId { get; set; }
+        public string CustomerName { get; set; }
+
+        /// <summary>
+        /// 每個client有他自己的指令序號 2 byte
+        /// </summary>
+        private ushort _cmdSerNum = 0;
+
+        /// <summary>
+        /// 傳送的封包序號 1 byte
+        /// </summary>
+        private byte _serNum = 0;
+
+        /// <summary>
+        /// 傳送的chargingProfileId 1 byte
+        /// </summary>
+        private byte _chargingProfileId = 0;
+
+        /// <summary>
+        /// 取得指令序號 0-65535
+        /// </summary>
+        /// <returns></returns>
+        public ushort GetCmdSerNum()
+        {
+            if (_cmdSerNum == ushort.MaxValue)
+                _cmdSerNum = 0;
+            return ++_cmdSerNum;
+        }
+
+        /// <summary>
+        /// 取得封包序號 1-255
+        /// </summary>
+        /// <returns></returns>
+        public byte GetSerNum()
+        {
+            if (_serNum == byte.MaxValue)
+            {
+                _serNum = 0;
+            }
+            return ++_serNum;
+        }
+
+        public byte GetCurrentSerNum()
+        {
+            return _serNum;
+        }
+
+        /// <summary>
+        /// 取得封包序號 1-255
+        /// </summary>
+        /// <returns></returns>
+        public byte GetChargingProfileId()
+        {
+            if (_chargingProfileId == byte.MaxValue)
+            {
+                _chargingProfileId = 0;
+            }
+            return ++_chargingProfileId;
+        }
+
+        public byte GetCurrentChargingProfileId()
+        {
+            return _chargingProfileId;
+        }
+
+        /// <summary>
+        /// 客戶自訂的樁id
+        /// </summary>
+        public string MachineCustomId
+        {
+            get; set;
+        }
+
+        /// <summary>
+        /// 客戶自訂的樁id
+        /// </summary>
+        public string MachineCustomIdTemp
+        {
+            get; set;
+        }
+
+        /// <summary>
+        /// 根據unique id來儲存.取出OCPP Request
+        /// </summary>
+        public Queue queue;
+
+        /// <summary>
+        /// 根據 GetConfiguration Key來儲存.
+        /// </summary>
+        public List<KeyValueType> configurationKey;
+
+        /// <summary>
+        /// 車輛類別
+        /// 0:默認樁體設定  , 1:小車(12V) , 2:大車(24V)
+        /// </summary>
+        public int CarType;
+
+        /// <summary>
+        /// 上一個Heartbeat日期封包
+        /// </summary>
+        public DateTime? heartbeatDate { get; set; }
+
+        /// <summary>
+        /// 當 Heartbeat 與 上一個 Heartbeat相差值小於2秒,判斷是否送出ChangeConfiguration.req(HeartbeatInterval: 15) 給電樁
+        /// </summary>
+        public bool IsSendHeartbeatChangeConfiguration { get; set; }
+
+        /// <summary>
+        /// Gets the app server.
+        /// </summary>
+        public new OCPPWSServer AppServer
+        {
+            get { return (OCPPWSServer)base.AppServer; }
+        }
+
+        /// <summary>
+        /// Sends the raw binary data to client.
+        /// </summary>
+        /// <param name="data">The data.</param>
+        /// <param name="offset">The offset.</param>
+        /// <param name="length">The length.</param>
+        public void SendRawData(byte[] data, int offset, int length)
+        {
+            base.Send(data, offset, length);
+        }
+
+        //receive data event trigger
+        public void ReceiveData(ClientData clientdata, string msg)
+        {
+            if (m_ReceiveData != null)
+                m_ReceiveData(clientdata, msg);
+        }
+
+        /// <summary>
+        /// Called when [session closed].
+        /// </summary>
+        /// <param name="reason">The reason.</param>
+        protected override void OnSessionClosed(CloseReason reason)
+        {
+        }
+
+        public void InitialPileData()
+        {
+            this.queue = new Queue();
+            this.MachineCustomId = Guid.NewGuid().ToString();
+            this.MachineCustomIdTemp = Guid.NewGuid().ToString();
+            this.IsCheckIn = false;
+            this.configurationKey = new List<KeyValueType>();
+            this.CarType = 0; // 默認樁體設定
+            heartbeatDate = null;
+            IsSendHeartbeatChangeConfiguration = false;
+
+            #region Core
+
+            configurationKey.Add(new KeyValueType { key = "AllowOfflineTxForUnknownId", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "AuthorizationCacheEnabled", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "AuthorizeRemoteTxRequests", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "BlinkRepeat", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "ClockAlignedDataInterval", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "ConnectionTimeOut", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "GetConfigurationMaxKeys", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "HeartbeatInterval", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "LightIntensity", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "LocalAuthorizeOffline", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "LocalPreAuthorize", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "MaxEnergyOnInvalidId", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "MeterValuesAlignedData", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "MeterValuesAlignedDataMaxLength", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "MeterValuesSampledData", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "MeterValuesSampledDataMaxLength", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "MeterValueSampleInterval", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "MinimumStatusDuration", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "NumberOfConnectors", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "ResetRetries", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "ConnectorPhaseRotation", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "ConnectorPhaseRotationMaxLength", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "StopTransactionOnEVSideDisconnect", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "StopTransactionOnInvalidId", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "StopTxnAlignedData", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "StopTxnAlignedDataMaxLength", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "StopTxnSampledData", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "StopTxnSampledDataMaxLength", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "SupportedFeatureProfiles", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "SupportedFeatureProfilesMaxLength", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "TransactionMessageAttempts", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "TransactionMessageRetryInterval", keyreadonly = false, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "UnlockConnectorOnEVSideDisconnect", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "WebSocketPingInterval", keyreadonly = false, value = "0" });
+
+            #endregion Core
+
+            #region Local Auth List Management
+
+            configurationKey.Add(new KeyValueType { key = "LocalAuthListEnabled", keyreadonly = false, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "LocalAuthListMaxLength", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "SendLocalListMaxLength", keyreadonly = true, value = "0" });
+            #endregion Local Auth List Management
+
+
+
+            #region Reservation Profile
+
+            configurationKey.Add(new KeyValueType { key = "ReserveConnectorZeroSupported", keyreadonly = true, value = "false" });
+
+            #endregion Reservation Profile
+
+            #region Smart Charging Profile
+
+            configurationKey.Add(new KeyValueType { key = "ChargeProfileMaxStackLevel", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "ChargingScheduleAllowedChargingRateUnit", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "ChargingScheduleMaxPeriods", keyreadonly = true, value = "0" });
+            configurationKey.Add(new KeyValueType { key = "ConnectorSwitch3to1PhaseSupported", keyreadonly = true, value = "false" });
+            configurationKey.Add(new KeyValueType { key = "MaxChargingProfilesInstalled", keyreadonly = true, value = "0" });
+
+            #endregion Smart Charging Profile
+        }
+    }
+}

+ 149 - 0
OCPPServer/Protocol/OCPPWSServer.cs

@@ -0,0 +1,149 @@
+using Evcb.Domain.Model;
+using Evcb.Repository;
+using Evcb.Service;
+using SuperSocket.Common;
+using SuperWebSocket;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OCPPServer.Protocol
+{
+    public class OCPPWSServer : WebSocketServer<ClientData>
+    {
+        /// <summary>
+        /// 可允許連線Clinet數
+        /// </summary>
+        public int connectNum { get; set; }
+
+        /// <summary>
+        /// 是否限制連線Clinet數
+        /// </summary>
+        public bool beConnectLimit { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+        /// </summary>
+        /// <param name="subProtocols">The sub protocols.</param>
+        public OCPPWSServer(IEnumerable<ISubProtocol<ClientData>> subProtocols)
+            : base(subProtocols)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+        /// </summary>
+        /// <param name="subProtocol">The sub protocol.</param>
+        public OCPPWSServer(ISubProtocol<ClientData> subProtocol)
+            : base(subProtocol)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
+        /// </summary>
+        public OCPPWSServer()
+            : base(new List<ISubProtocol<ClientData>>())
+        {
+        }
+
+        protected override bool ValidateHandshake(ClientData session, string origin)
+        {
+            //初始化ClientData
+            session.InitialPileData();
+            //確認電樁所送的電樁識別碼是否包含在request path中
+            string[] words = session.Path.Split('/');
+            using (var db = new PhihongDbContext())
+            {
+                if (this.beConnectLimit == true)
+                {
+                    var connectPile = db.Machine.Where(c => c.Online == true).ToList();
+                    if (connectPile.Count >= this.connectNum)
+                    {
+                        byte[] m_SwitchResponse;
+                        var responseBuilder = new StringBuilder();
+                        responseBuilder.AppendWithCrCf("HTTP/1.1 403"); //403 Forbidden : 用戶端並無訪問權限,所以伺服器給予應有的回應。
+                        responseBuilder.AppendWithCrCf("Upgrade: WebSocket");
+                        responseBuilder.AppendWithCrCf("Connection: Upgrade");
+                        responseBuilder.AppendWithCrCf("Sec-WebSocket-Version: " + session.SecWebSocketVersion);
+                        responseBuilder.AppendWithCrCf();
+
+                        m_SwitchResponse = Encoding.UTF8.GetBytes(responseBuilder.ToString());
+
+                        session.SendRawData(m_SwitchResponse, 0, m_SwitchResponse.Length);
+                        return false;
+                    }
+                }
+
+                IUnitOfWork uow = new UnitOfWork(db);
+                IMachineService machineSrv = new MachineService(uow);
+                var machine = machineSrv.GetByCustomId(words.Last());
+                if (machine == null)
+                {
+                    byte[] m_SwitchResponse;
+                    var responseBuilder = new StringBuilder();
+                    responseBuilder.AppendWithCrCf("HTTP/1.1 404");
+                    responseBuilder.AppendWithCrCf("Upgrade: WebSocket");
+                    responseBuilder.AppendWithCrCf("Connection: Upgrade");
+                    responseBuilder.AppendWithCrCf("Sec-WebSocket-Version: " + session.SecWebSocketVersion);
+                    responseBuilder.AppendWithCrCf();
+
+                    m_SwitchResponse = Encoding.UTF8.GetBytes(responseBuilder.ToString());
+
+                    session.SendRawData(m_SwitchResponse, 0, m_SwitchResponse.Length);
+                    return false;
+
+                    //session.CloseWithHandshake(session.ProtocolProcessor.CloseStatusClode.NormalClosure, "This machine can't be  recognized.");
+                }
+                session.MachineCustomIdTemp = words.Last();
+                uow.Dispose();
+            }
+
+            //session.MachineCustomIdTemp = "RDTEST101";
+
+            //確認電樁連線所送的SubProtocol是否被Server支援
+            if (session.SecWebSocketProtocol.ToLower() != "ocpp1.6")
+            {
+                const string m_Magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+                const string SecWebSocketKey = "Sec-WebSocket-Key";
+                const string ResponseHeadLine10 = "HTTP/1.1 101 Switching Protocols";
+                const string Upgrade = "Upgrade";
+                const string ResponseUpgradeLine = Upgrade + ": WebSocket";
+                const string Connection = "Connection";
+                const string ResponseConnectionLine = Connection + ": Upgrade";
+                const string ResponseAcceptLine = "Sec-WebSocket-Accept: {0}";
+
+                var responseBuilder = new StringBuilder();
+                var secWebSocketKey = session.Items.GetValue<string>(SecWebSocketKey, string.Empty);
+                if (string.IsNullOrEmpty(secWebSocketKey))
+                {
+                    return false;
+                }
+                string secKeyAccept = string.Empty;
+
+                try
+                {
+                    secKeyAccept = Convert.ToBase64String(System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(secWebSocketKey + m_Magic)));
+                }
+                catch (Exception)
+                {
+                    return false;
+                }
+
+                responseBuilder.AppendWithCrCf(ResponseHeadLine10);
+                responseBuilder.AppendWithCrCf(ResponseUpgradeLine);
+                responseBuilder.AppendWithCrCf(ResponseConnectionLine);
+                responseBuilder.AppendFormatWithCrCf(ResponseAcceptLine, secKeyAccept);
+                responseBuilder.AppendWithCrCf();
+                byte[] data = Encoding.UTF8.GetBytes(responseBuilder.ToString());
+                session.SendRawData(data, 0, data.Length);
+                session.CloseWithHandshake(session.ProtocolProcessor.CloseStatusClode.NormalClosure, "This SubProtocol can't be  supported.");
+                return false;
+            }
+            //session.m_ReceiveData += new ClientData.OCPPClientDataEventHandler<ClientData, String>(WSServer.ReceivedMessage);
+            return true;
+        }
+    }
+}

+ 28 - 0
OCPPServer/Server.cs

@@ -0,0 +1,28 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NLog;
+using OCPPServer.Handler;
+using OCPPServer.Protocol;
+using OCPPServer.SubProtocol;
+using SuperSocket.SocketBase;
+using SuperWebSocket;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+namespace OCPPServer
+{
+    internal class Server
+    {
+        private static void Main(string[] args)
+        {
+            WSServer s = new WSServer();
+            Console.WriteLine("Starting Server...");
+            s.Start();
+        }
+    }
+}

+ 770 - 0
OCPPServer/Service/WebSocketCommandService.cs

@@ -0,0 +1,770 @@
+using Evcb.Domain.Model;
+using Evcb.Repository;
+using Evcb.Service;
+using Evcb.Service.Helpers;
+using Evcb.Service.Service.Common;
+using Newtonsoft.Json;
+using NLog;
+using OCPP_Packet.Packet.DataTransfer;
+using OCPP_Packet.Packet.Status;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.Service
+{
+    public class WebSocketCommandService : Service<Machine>
+    {
+        private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+
+        private readonly IRepository<ChargingRecordStatus> _chargingRecordStatusRepo;
+        private readonly IRepository<MachineGun> _machineGunRepo;
+        private readonly IRepository<ChargingRecord> _chargingRecordRepo;
+        private readonly IRepository<ChargingRecordReport> _chargingRecordReportRepo;
+        private readonly IRepository<MachineError> _meRepo;
+        private readonly IRepository<MemberCharging> _memberChargingRepo;
+        public WebSocketCommandService(IUnitOfWork uow) : base(uow)
+        {
+            _uow = uow;
+            _chargingRecordRepo = _uow.Repository<ChargingRecord>();
+            _machineGunRepo = _uow.Repository<MachineGun>();
+            _chargingRecordStatusRepo = _uow.Repository<ChargingRecordStatus>();
+            _chargingRecordReportRepo = _uow.Repository<ChargingRecordReport>();
+            _meRepo = _uow.Repository<MachineError>();
+            _memberChargingRepo = _uow.Repository<MemberCharging>();
+        }
+
+        public ProcessStatusPacketResult Process1104(ChargingRecordStatus value)
+        {
+            ProcessStatusPacketResult result = new ProcessStatusPacketResult() { StartChargingType = -1 };
+            var gun = _machineGunRepo.Query(c => c.GunSerNo == value.GunSerNo && c.Machine.Id == value.MachineId).Include(c => c.Machine).Include(c => c.Machine.Customer).Select().FirstOrDefault();
+            value.CreatedOn = DateTime.Now;
+            logger.Trace(value.MachineCustomId + "進入104處理");
+
+            if (gun != null)
+            {
+                //舊的充電序號
+                Guid oldGuid = Guid.Empty;
+
+                //狀態改變,更換序號
+                if ((gun.Status == 2 || gun.Status == 3) && (value.Status == 2 || value.Status == 3))
+                {
+                    //2跟3算同一個狀態 For AC,不更換序號
+                }
+                else if (gun.Status != value.Status)
+                {
+                    oldGuid = gun.ChargingSerNo;
+                    gun.ChargingSerNo = Guid.NewGuid();
+                }
+
+                //從正準備開始充電轉換到充電進行中,新增一筆充電紀錄
+                //if (((gun.Status == 1 || gun.Status == 0) && value.Status == 2) || ((gun.Status == 1 || gun.Status == 0) && value.Status == 3))
+                if (value.Status == 2)
+                {
+
+                    //DateTime sDate = value.StartChargingDateTime.Value.AddMilliseconds(-1);
+                    //DateTime eDate = value.StartChargingDateTime.Value.AddMilliseconds(3);
+                    ChargingRecord nObj = _chargingRecordRepo.Query(
+                    //    c => c.StartChargingDateTime >= sDate
+                    //&& c.StartChargingDateTime <= eDate
+                    c => c.StartChargingDateTime == value.StartChargingDateTime.Value
+                    && c.GunSerNo == value.GunSerNo
+                    && c.MachineId == gun.MachineId).Select().FirstOrDefault();
+
+                    //沒有該筆充電序才去產生帳單紀錄
+                    if (nObj == null && value.StartChargingDateTime.HasValue)
+                    {
+                        gun.ChargingSerNo = Guid.NewGuid();
+
+                        nObj = new ChargingRecord()
+                        {
+                            StartChargingDateTime = value.StartChargingDateTime.Value,
+                            StartWith = value.StartWith,
+                            GunSerNo = gun.GunSerNo,
+                            MachineId = gun.Machine.Id,
+                            MachineCustomId = gun.Machine.CustomId,
+                            ChargingSerNo = gun.ChargingSerNo,
+                            ChargingStrategy = value.ChargingStrategy,
+                            ChargingStrategyParam = (int)value.ChargingStrategyParam,
+                            CreatedOn = DateTime.Now
+                        };
+
+                        _chargingRecordRepo.Add(nObj);
+
+                        logger.Trace(gun.Machine.CustomId + "開始充電轉換到充電進行中");
+                        logger.Trace("nObj:" + JsonConvert.SerializeObject(nObj));
+                        logger.Trace("value:" + JsonConvert.SerializeObject(value));
+
+                        //如果每筆記錄都要回報
+                        if (gun.Machine.Customer.AutoCreateReportRecord)
+                        {
+                            //如果是卡片充電的話
+                            if (value.StartWith == 1)
+                            {
+                                logger.Trace("卡片充電:");
+                                ChargingRecordReport rr = new ChargingRecordReport()
+                                {
+                                    StartChargingDateTime = value.StartChargingDateTime.Value,
+                                    MachineId = nObj.MachineId,
+                                    MachineCustomId = nObj.MachineCustomId,
+                                    CardNum = value.ReservationCardNum,
+                                    StartWith = value.StartWith,
+                                    ChargingSerNo = nObj.ChargingSerNo,
+                                    CustomerId = gun.Machine.CustomerId,
+                                    GunSerNo = value.GunSerNo,
+                                    //ChargeStrategy = 2,
+                                    ChargeStrategy = value.ChargingStrategy,
+                                    ChargeStrategyParam = value.CardBalanceBeforeCharging,
+                                    CreatedOn = DateTime.Now,
+                                };
+                                logger.Trace("rr:" + JsonConvert.SerializeObject(rr));
+                                _chargingRecordReportRepo.Add(rr);
+                                result.StartChargingType = 0;
+                                result.ChargingSerNo = rr.ChargingSerNo;
+                                result.Result = 12;
+                            }
+                        }
+                    }
+                }
+
+                //發出一個新的告警,就紀錄到table中
+                if (value.Status == 6)
+                {
+                    var hasSameError = _uow.Repository<MachineError>().Query(c => c.MachineId == value.MachineId && c.GunSerNo == gun.GunSerNo).Select().OrderByDescending(c => c.CreatedOn).FirstOrDefault();
+
+                    MachineError me = new MachineError { CreatedOn = DateTime.Now, MachineId = gun.MachineId, PreStatus = gun.Status, Status = value.Status, ErrorCode = value.Warning, GunSerNo = gun.GunSerNo };
+
+                    if (gun.Status != 6)
+                    {
+                        // Gun previous status is other status
+                        if (hasSameError != null)
+                        {
+                            if (((value.Warning == hasSameError.ErrorCode) && ((value.CreatedOn - hasSameError.CreatedOn).Value.TotalMinutes > 3)) || (value.Warning != hasSameError.ErrorCode))
+                            {
+                                _meRepo.Add(me);
+                                NoticeError(me);
+                            }
+                        }
+                        else
+                        {
+                            _meRepo.Add(me);
+                            NoticeError(me);
+                        }
+                    }
+                    else
+                    {
+                        // Gun previous status is alarm status
+                        if (value.Warning != hasSameError.ErrorCode)
+                        {
+                            _meRepo.Add(me);
+                            NoticeError(me);
+                        }
+                        //logger.Trace("value.CreatedOn - hasSameError.CreatedOn=" + (value.CreatedOn - hasSameError.CreatedOn).Value.TotalMinutes.ToString());
+                    }
+                }
+                ////從充電中,切換狀態到充電結束
+                //if (gun.Status == 2 && value.Status == 3)
+                //{
+                //    var chargingRecordObj = _chargingRecordRepo.Query(c => c.ChargingSerNo == oldGuid).Select().FirstOrDefault();
+                //    chargingRecordObj.EndChargingDateTime = DateTime.Now;
+                //    _chargingRecordRepo.Update(chargingRecordObj);
+                //}
+
+                gun.Status = value.Status;
+                value.ChargingSerNo = gun.ChargingSerNo;
+                //value.CreatedOn = DateTime.Now;
+                _chargingRecordStatusRepo.Add(value);
+                _uow.SaveChanges();
+
+                return result;
+            }
+            else
+            {
+                throw new ApplicationException("wrong guns!");
+            }
+        }
+
+        private IResult NoticeError(MachineError error)
+        {
+            IResult result = new Result();
+            try
+            {
+                string description = "None";
+                var machineRepo = _uow.Repository<Machine>();
+                var machine = machineRepo.Query(c => c.Id == error.MachineId).Include(c => c.Customer).Select().FirstOrDefault();
+                var accountRepo = _uow.Repository<Account>();
+                var account = accountRepo.Query(c => c.CustomerId == machine.CustomerId).Select().FirstOrDefault();
+                var errorRepo = _uow.Repository<ErrorCode>();
+                var errorMsg = errorRepo.Query(c => c.Code == error.ErrorCode).Select().FirstOrDefault();
+                if (errorMsg != null)
+                {
+                    if (errorMsg.AC == machine.AC)
+                    {
+                        description = errorMsg.Description;
+                    }
+                }
+                if (!string.IsNullOrEmpty(account.Email))
+                {
+                    string[] emailgroup = account.Email.Split(',');
+                    string subject = string.Format("客户: {0} 飞宏电桩号: {1} 客户电桩号: {2} 错误代码:{3}", machine.Customer.CustomerDesc, machine.CustomId, machine.PartnerPoleId, error.ErrorCode);
+                    string detail = string.Format("[时间点: {0}] 飞宏电桩号: {1} 客户电桩号: {2},枪号 : {3},错误代码: {4} 说明: {5}", error.CreatedOn, machine.CustomId, machine.PartnerPoleId, Convert.ToChar(65 + error.GunSerNo), error.ErrorCode, description);
+                    string body = string.Format("详细讯息: {2} ", machine.PartnerPoleId, error.ErrorCode, detail);
+                    foreach (var email in emailgroup)
+                    {
+                        DispatchMessageHelper.SendMail(email, subject, body);
+                    }
+                }
+
+                result.Success = true;
+                return result;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result.Exception = ex;
+                result.Success = false;
+                return result;
+            }
+        }
+
+        /// <summary>
+        /// 處理經緯度
+        /// </summary>
+        /// <param name="machineId">電樁Id</param>
+        /// <param name="Location">Location command</param>
+        /// <returns></returns>
+        public IResult ProcessLocation(string customId, Location value)
+        {
+            IResult result = new Result();
+            double radius = 0.005d;
+            try
+            {
+                var machineRepo = _uow.Repository<Machine>();
+                var _chargerStationRepo = _uow.Repository<ChargerStation>();
+                var machine = machineRepo.Query(c => c.CustomId == customId).Select().FirstOrDefault();
+                var stations = _chargerStationRepo.Query(c => c.CustomerId == machine.CustomerId).Select().ToList();
+                //Mapping 充電樁的位置 以資料庫列表中充電樁站點為圓心,判斷上報的充電樁位置是否在附近
+                //以0.005d為半徑 超出者預設 0
+                //double KeyX = 0d, KeyY = 0d;
+                bool isNearAnyStation = false;
+                foreach (var station in stations)
+                {
+                    double YDist = Convert.ToDouble(station.Latitude - Convert.ToDecimal(value.latitude));
+                    double XDist = Convert.ToDouble(station.Longitude - Convert.ToDecimal(value.longitude));
+                    if (Math.Pow(XDist, 2d) + Math.Pow(YDist, 2d) <= Math.Pow(radius, 2d))
+                    {
+                        isNearAnyStation = true;
+                        if (machine.Location != station.Id)
+                        {
+                            machine.Location = station.Id;
+                        }
+                    }
+                }
+
+                if (!isNearAnyStation & machine.Location != 0)
+                {
+                    machine.Location = 0;
+                }
+                _uow.SaveChanges();
+                result.Success = true;
+                return result;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result.Exception = ex;
+                result.Success = false;
+                return result;
+            }
+        }
+
+        /// <summary>
+        /// 處理BMS訊息
+        /// </summary>
+        /// <param name="machineId">電樁Id</param>
+        /// <param name="ChargingGunBMS">ChargingGunBMS command</param>
+        /// <returns></returns>
+        public IResult ProcessChargingGunBMS(string machineId, ChargingGunBMS cmd)
+        {
+            IResult result = new Result();
+            var _machineGunRepo = _uow.Repository<MachineGun>();
+            var _chargingRecordBMSRepo = _uow.Repository<ChargingRecordBMS>();
+            try
+            {
+                var chargingSerNo = _machineGunRepo.Query(x => x.MachineId == machineId && x.GunSerNo == cmd.connectorId && x.Status != 0).Select(x => x.ChargingSerNo).FirstOrDefault();
+                if (chargingSerNo == null) chargingSerNo = Guid.Empty;
+
+                _chargingRecordBMSRepo.Add(new ChargingRecordBMS()
+                {
+                    MachineId = machineId,
+                    MachineCustomId = _repository.Query(x => x.Id == machineId).Select(x => x.PartnerPoleId).FirstOrDefault(),
+                    GunSerNo = Convert.ToByte(cmd.connectorId-1),
+                    ChargingSerNo = chargingSerNo == null ? Guid.Empty : chargingSerNo,
+                    Status = Convert.ToByte(cmd.status),
+                    CarConnection = Convert.ToByte(cmd.carConnectionStatus),
+                    BRMBMS_ProtocolVersion = cmd.brm_bmsVersion,
+                    BR_BatteryType = Convert.ToByte(cmd.br_BatteryType),
+                    BRM_CarBattery_SystemRatedCurrent = Convert.ToDecimal(cmd.brm_BatterySystemRatedcapacity),
+                    BRM_CarBattery_SystemRatedVoltage = Convert.ToDecimal(cmd.brm_BatterySystemRatedVoltage),
+                    BRM_BatteryManufacturer = cmd.brm_BatteryManufacturer,
+                    BRM_BatteryGroupSN = cmd.brm_BatteryNumber,
+                    BRM_BatteryGroupProductionDate = cmd.brm_ProductionDate,
+                    BRM_BatteryGroupChargingTimes = cmd.brm_BatteryChargeTimes,
+                    BRM_BatteryGroupPropertyRight = Convert.ToByte(cmd.brm_BatteryPropertySign),
+                    BRM_Reserve = Convert.ToByte(cmd.brm_Reserved),
+                    BRM_VIN = cmd.brm_Vin,
+                    BRMBMS_SoftwareVersion = cmd.brm_bmsSoftwareVersion,
+                    BCP_SingleBatteryAllowChargeMaxVoltage = Convert.ToDecimal(cmd.bcp_SingleBatteryHightestChargeVoltage),
+                    BCP_AllowChargeMaxCurrent = Convert.ToDecimal(cmd.bcp_HightestChargeCurrent),
+                    BCP_BatteryFlagTotalEnergy = Convert.ToDecimal(cmd.bcp_BatteryLabelingTotalEnergy),
+                    BCP_AllowChargeMaxTotalVoltage = Convert.ToDecimal(cmd.bcp_HightestChargeVoltage),
+                    BCP_AllowHighestTemperature = cmd.bcp_HightestTemperature,
+                    BCP_CarBatterySOC = Convert.ToDecimal(cmd.bcp_VehiclePowerBatteryChargeState),
+                    BCP_CarBatteryCurrentBatteryVoltage = Convert.ToDecimal(cmd.bcp_VehicleBatteryCurrentVoltage),
+                    BROBMS_IsReadyCharge = Convert.ToByte((cmd.bro_bms_ChargeReadyStatus == ChargeReadyStatus.NotReady) ? 0x00 : ((cmd.bro_bms_ChargeReadyStatus == ChargeReadyStatus.Ready) ? 0xAA : 0xFF)),
+                    BCL_Voltage = Convert.ToDecimal(cmd.bcl_DemandVoltage),
+                    BCL_Current = Convert.ToDecimal(cmd.bcl_DemandCurrent),
+                    BCL_ChargeMode = Convert.ToByte(cmd.bcl_ChargeMode),
+                    BCS_ChargeVotageMeasurement = Convert.ToDecimal(cmd.bcs_ChargeVoltageMeasurement),
+                    BCS_ChargeCurrentMeasurement = Convert.ToDecimal(cmd.bcs_ChargeCurrentMeasurement),
+                    BCS_SingleBatteryMaxVoltage = Convert.ToDecimal(cmd.bcs_HightestSingleBatteryVoltage),
+                    BCS_SingleBatteryGroupNumber = cmd.bcs_HightestSingleBatteryNumber,
+                    BCS_CurrentSOC = Convert.ToByte(cmd.bcs_CurrentSOC),
+                    RemainingChargeMins = cmd.remainingChargeTime,
+                    BSM_SingleBatteryMaxVoltageNumber = cmd.bsm_HightestSingleBatteryVoltageNumber,
+                    BSM_BatteryHighestTemperature = cmd.bsm_HightestBatteryTemperature,
+                    BSM_HighestTemperatureCheckPointNumber = cmd.bsm_HightestTemperatureDetectionPointNumber,
+                    BSM_BatteryLowestTemperature = cmd.bsm_LowestBatteryTemperature,
+                    BSM_LowestTemperatureCheckPointNumber = cmd.bsm_LowestTemperatureDetectionPointNumber,
+                    BSM_SingleBatteryVoltageStatus = Convert.ToByte((cmd.bsm_SingleBatteryVoltageStatus == BMSStatusAType.Normal) ? 0x00 : ((cmd.bsm_SingleBatteryVoltageStatus == BMSStatusAType.TooHigh) ? 0x01 : 0x10)),
+                    BSM_CarBatterySOCStatus = Convert.ToByte((cmd.bsm_VehicleBatterySOCStatus == BMSStatusAType.Normal) ? 0x00: ((cmd.bsm_VehicleBatterySOCStatus == BMSStatusAType.TooHigh) ? 0x01 : 0x10)),
+                    BSM_BatteryChargineCurrentStatus = Convert.ToByte((cmd.bsm_BatteryChargeCurrentStatus == BMSStatusBType.Normal) ? 0x00 : ((cmd.bsm_BatteryChargeCurrentStatus == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BSM_BatteryTemperatureTooHigh = Convert.ToByte((cmd.bsm_BatteryTemperatureStatus == BMSStatusBType.Normal) ? 0x00 : ((cmd.bsm_BatteryTemperatureStatus == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BSM_BatteryInsulationStatus = Convert.ToByte((cmd.bsm_BatteryInsulationStatus == BMSStatusBType.Normal) ? 0x00 : ((cmd.bsm_BatteryInsulationStatus == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BSM_BatteryOutputConnectionStatus = Convert.ToByte((cmd.bsm_BatteryOutputConnectorConnectionStatus == BMSStatusBType.Normal) ? 0x00 : ((cmd.bsm_BatteryOutputConnectorConnectionStatus == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BSM_AllowCharge = Convert.ToByte((cmd.bsm_IsAllowedCharge == false) ? 0x00 : 0x01),
+                    BSTBMS_SOCGoal = Convert.ToByte((cmd.bst_bms_IsAchievedSOCTargetStatus == BMSStatusCType.Below) ? 0x00 : ((cmd.bst_bms_IsAchievedSOCTargetStatus == BMSStatusCType.Standard) ? 0x01 : 0x10)),
+                    BSTBMS_TotalVotageSetting = Convert.ToByte((cmd.bst_bms_IsAchievedSettingTotalVoltageStatus == BMSStatusCType.Below) ? 0x00 : ((cmd.bst_bms_IsAchievedSettingTotalVoltageStatus == BMSStatusCType.Standard) ? 0x01 : 0x10)),
+                    BST_SingleVotageSetting = Convert.ToByte((cmd.bst_IsAchievedSettingSingleVoltageStatus == BMSStatusCType.Below) ? 0x00 : ((cmd.bst_IsAchievedSettingSingleVoltageStatus == BMSStatusCType.Standard) ? 0x01 : 0x10)),
+                    BST_ChargerActiveStop = Convert.ToByte((cmd.bst_chargerAutoTerminationStatus == ChargerAutoTerminationStatus.Normal) ? 0x00 : ((cmd.bst_chargerAutoTerminationStatus == ChargerAutoTerminationStatus.ChargerIsTerminated) ? 0x01 : 0x10)),
+                    BST_InsulationError = Convert.ToByte((cmd.bst_InsulationFaultStatus == BMSStatusDType.Normal) ? 0x00 : ((cmd.bst_InsulationFaultStatus == BMSStatusDType.Fault) ? 0x01 : 0x10)),
+                    BST_OutputConnectionOverTemperatureError = Convert.ToByte((cmd.bst_OutputConnectorTemperatureStatus == BMSStatusDType.Normal) ? 0x00 : ((cmd.bst_OutputConnectorTemperatureStatus == BMSStatusDType.Fault) ? 0x01 : 0x10)),
+                    BSTBMSComponent_OutputConnectionOverTemperatureError = Convert.ToByte((cmd.bst_bms_OutputConnectorTemperatureStatus == BMSStatusDType.Normal) ? 0x00 : ((cmd.bst_bms_OutputConnectorTemperatureStatus == BMSStatusDType.Fault) ? 0x01 : 0x10)),
+                    BST_ChargeConnectionError = Convert.ToByte((cmd.bst_chargerConnectorStatus == BMSStatusBType.Normal) ? 0x00 : ((cmd.bst_chargerConnectorStatus == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BST_BatteryGroupOverTemperatureError = Convert.ToByte((cmd.bst_BatteryTemperatureStatus == BMSStatusBType.Normal) ? 0x00 : ((cmd.bst_BatteryTemperatureStatus == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BST_HighVoltageRelayError = Convert.ToByte((cmd.bst_HighVoltageRelayStatus == BMSStatusBType.Normal) ? 0x00 : ((cmd.bst_HighVoltageRelayStatus == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BST_Checkponit2VoltageCheckError = Convert.ToByte((cmd.bst_DetectionPoint2Voltage == BMSStatusBType.Normal) ? 0x00 : ((cmd.bst_DetectionPoint2Voltage == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BST_OtherError = Convert.ToByte((cmd.bst_Other == BMSStatusBType.Normal) ? 0x00 : ((cmd.bst_Other == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BST_OverCurrent = Convert.ToByte((cmd.bst_Current == BMSStatusBType.Normal) ? 0x00 : ((cmd.bst_Current == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BST_VoltageError = Convert.ToByte((cmd.bst_Voltage == BMSStatusBType.Normal) ? 0x00 : ((cmd.bst_Voltage == BMSStatusBType.TooHigh) ? 0x01 : 0x10)),
+                    BSD_StopSOC = Convert.ToByte(cmd.bsd_EndSOC),
+                    BSD_SingleBatteryLowestVoltage = Convert.ToDecimal(cmd.bsd_SingleBatteryLowestVoltage),
+                    BSD_SingleBatteryHighestVoltage = Convert.ToDecimal(cmd.bsd_SingleBatteryHightestVoltage),
+                    BSD_BatteryLowestTemperature = cmd.bsd_BatteryLowestTemperature,
+                    BSD_BatteryHighestTemperature = cmd.bsd_BatteryHightestTemperature,
+                    BEM_ReceivedSPN2560_0x00 = Convert.ToByte((cmd.bem_ReceivedSPN2560_00_Status == BMSStatusEType.Normal) ? 0x00 : ((cmd.bem_ReceivedSPN2560_00_Status == BMSStatusEType.TimeOut) ? 0x01 : 0x10)),
+                    BEM_ReceivedSPN2560_0xAA = Convert.ToByte((cmd.bem_ReceivedSPN2560_aa_Status == BMSStatusEType.Normal) ? 0x00 : ((cmd.bem_ReceivedSPN2560_aa_Status == BMSStatusEType.TimeOut) ? 0x01 : 0x10)),
+                    BEM_ReceivedSyncAndMaxOutputPower_Timeout = Convert.ToByte((cmd.bem_ReceivedTimeSyncAndMaxOutputStatus == BMSStatusEType.Normal) ? 0x00 : ((cmd.bem_ReceivedTimeSyncAndMaxOutputStatus == BMSStatusEType.TimeOut) ? 0x01 : 0x10)),
+                    BEM_ReceivedReadyCharge_Timeout = Convert.ToByte((cmd.bem_ReceivedReadyMessageStatus == BMSStatusEType.Normal) ? 0x00 : ((cmd.bem_ReceivedReadyMessageStatus == BMSStatusEType.TimeOut) ? 0x01 : 0x10)),
+                    BEM_ReceivedChargerStatus_Timeout = Convert.ToByte((cmd.bem_ReceivedStatusMessageStatus == BMSStatusEType.Normal) ? 0x00 : ((cmd.bem_ReceivedStatusMessageStatus == BMSStatusEType.TimeOut) ? 0x01 : 0x10)),
+                    BEM_ReceivedChargerStopCharge_Timeout = Convert.ToByte((cmd.bem_ReceivedStopMessageStatus == BMSStatusEType.Normal) ? 0x00 : ((cmd.bem_ReceivedStopMessageStatus == BMSStatusEType.TimeOut) ? 0x01 : 0x10)),
+                    BEM_ReceivedChargerChargeStatistics_Timeout = Convert.ToByte((cmd.bem_ReceivedStatisticsMessageStatus == BMSStatusEType.Normal) ? 0x00 : ((cmd.bem_ReceivedStatisticsMessageStatus == BMSStatusEType.TimeOut) ? 0x01 : 0x10)),
+                    BEM_Other = Convert.ToByte(cmd.bem_Other),
+                    CreateOn = DateTime.Now
+                });
+                _uow.SaveChanges();
+                result.Success = true;
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+                result.Message = ex.Message;
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// 處理ChargeComplete
+        /// </summary>
+        /// <param name="value">ChargeComplete command</param>
+        /// <param name="MachineCustomId">電樁樁號</param>
+        /// <returns></returns>
+        public IResult ProcessChargeComplete(ChargeComplete value, string MachineCustomId)
+        {
+            IResult result = new Result();
+            var _chargingRecordRepo = _uow.Repository<ChargingRecord>();
+            var crrRepo = _uow.Repository<ChargingRecordReport>();
+            var machineRepo = _uow.Repository<Machine>();
+            try
+            {
+                var machine = machineRepo.Query(c => c.CustomId == MachineCustomId).Select().FirstOrDefault();
+                var chargingTransaction = _uow.Repository<ChargingTransaction>().Query(c => c.TransactionId == value.transactionId).Select().FirstOrDefault();
+                if(chargingTransaction == null)
+                {
+                    result.Success = false;
+                    result.Message = "No Transaction Id";
+                    return result;
+                }
+                var record = _chargingRecordRepo.Query(c => c.Id == chargingTransaction.ChargingRecordId).Select().FirstOrDefault();
+
+                //沒有資料就補上去
+                bool isNew = false;
+                if (record == null)
+                {
+                    record = new ChargingRecord();
+                    record.MachineId = machine.Id;
+                    record.MachineCustomId = machine.CustomId;
+                    record.GunSerNo = (byte)(value.connectorId-1);
+                    record.ChargingSerNo = Guid.NewGuid();
+                    record.StartChargingDateTime = value.startChargingDateTime;//value.startChargingDateTime.ToLocalTime();
+                    record.EndChargingDateTime = value.chargeCompleteDateTime;//value.chargeCompleteDateTime.ToLocalTime();
+                    record.CreatedOn = DateTime.Now;
+                    isNew = true;
+                }
+
+                #region Mapper
+                record.CumulativeKwh = (decimal)value.cumulativeKwh;
+                record.KwhBeforeCharging = (decimal)value.beforeChargingKwh;
+                record.KwhAfterCharging = (decimal)value.chargeCompleteKwh;
+                record.ElectricBill = Decimal.Multiply((decimal)value.cumulativeKwh, machine.PricePerKWH);//(decimal)value.electricBill;
+                record.FeeAmount = Decimal.Multiply((decimal)value.cumulativeKwh, machine.FeePerKWH);//(decimal)value.feeAmount;
+                record.SumOfMoney = record.ElectricBill + record.FeeAmount;
+                record.StopChargingReason = (int)value.reason;
+                record.StartWith = (byte)value.startWith;
+                record.StartSoc = (byte)value.startChargingSOC;
+                record.StartChargingType = (byte)value.startChargingType;
+                record.PayOffline = (byte)(value.isPayOffline ? 1 : 0);
+                record.EndSoc = (byte)value.chargeCompleteSOC;
+                record.ChargingStrategyParam = (int)value.chargingStrategyParameter;
+                record.ChargingStrategy = (byte)value.chargingStrategy;
+                record.CarNum = value.licensePlateNumber;
+                record.CardVIN = value.vinCode;
+                record.CardBalanceBeforeCharging = (decimal)value.cardBalanceBeforeCharging;
+                record.CardBalanceAfterCharging = (decimal)value.cardBalanceAfterCharging;
+                record.Time1 = (decimal)value.sectionKwh[0];
+                record.Time2 = (decimal)value.sectionKwh[1];
+                record.Time3 = (decimal)value.sectionKwh[2]; 
+                record.Time4 = (decimal)value.sectionKwh[3];
+                record.Time5 = (decimal)value.sectionKwh[4];
+                record.Time6 = (decimal)value.sectionKwh[5];
+                record.Time7 = (decimal)value.sectionKwh[6];
+                record.Time8 = (decimal)value.sectionKwh[7];
+                record.Time9 = (decimal)value.sectionKwh[8];
+                record.Time10 = (decimal)value.sectionKwh[9];
+                record.Time11 = (decimal)value.sectionKwh[10];
+                record.Time12 = (decimal)value.sectionKwh[11];
+                record.Time13 = (decimal)value.sectionKwh[12];
+                record.Time14 = (decimal)value.sectionKwh[13];
+                record.Time15 = (decimal)value.sectionKwh[14];
+                record.Time16 = (decimal)value.sectionKwh[15];
+                record.Time17 = (decimal)value.sectionKwh[16];
+                record.Time18 = (decimal)value.sectionKwh[17];
+                record.Time19 = (decimal)value.sectionKwh[18];
+                record.Time20 = (decimal)value.sectionKwh[19];
+                record.Time21 = (decimal)value.sectionKwh[20];
+                record.Time22 = (decimal)value.sectionKwh[21];
+                record.Time23 = (decimal)value.sectionKwh[22];
+                record.Time24 = (decimal)value.sectionKwh[23];
+                record.Time25 = (decimal)value.sectionKwh[24];
+                record.Time26 = (decimal)value.sectionKwh[25];
+                record.Time27 = (decimal)value.sectionKwh[26];
+                record.Time28 = (decimal)value.sectionKwh[27];
+                record.Time29 = (decimal)value.sectionKwh[28];
+                record.Time30 = (decimal)value.sectionKwh[29];
+                record.Time31 = (decimal)value.sectionKwh[30];
+                record.Time32 = (decimal)value.sectionKwh[31];
+                record.Time33 = (decimal)value.sectionKwh[32];
+                record.Time34 = (decimal)value.sectionKwh[33];
+                record.Time35 = (decimal)value.sectionKwh[34];
+                record.Time36 = (decimal)value.sectionKwh[35];
+                record.Time37 = (decimal)value.sectionKwh[36];
+                record.Time38 = (decimal)value.sectionKwh[37];
+                record.Time39 = (decimal)value.sectionKwh[38];
+                record.Time40 = (decimal)value.sectionKwh[39];
+                record.Time41 = (decimal)value.sectionKwh[40];
+                record.Time42 = (decimal)value.sectionKwh[41];
+                record.Time43 = (decimal)value.sectionKwh[42];
+                record.Time44 = (decimal)value.sectionKwh[43];
+                record.Time45 = (decimal)value.sectionKwh[44];
+                record.Time46 = (decimal)value.sectionKwh[45];
+                record.Time47 = (decimal)value.sectionKwh[46];
+                record.Time48 = (decimal)value.sectionKwh[47];
+
+                record.ClientStartChargingDateTime = value.startChargingDateTime;//value.startChargingDateTime.ToLocalTime();
+                record.ClientEndChargingDateTime = value.chargeCompleteDateTime;//value.chargeCompleteDateTime.ToLocalTime();
+
+                if (value.startWith == StartWith.Member)
+                {
+                    
+                    var membertb = _memberChargingRepo.Query(c => c.SelfDefinedId == value.idTag).Select().FirstOrDefault();
+                    if (membertb != null)
+                    {
+                        //會員
+                        record.MemberId = membertb.MemberChargingId.ToString();               
+                    }
+                }
+                else if ((value.startWith == StartWith.CardNumber) || (value.startWith == StartWith.Unknown))
+                {
+                    record.CardNum = value.idTag;
+                }
+                #endregion Mapper
+                if (isNew)
+                    _chargingRecordRepo.Add(record);
+                else
+                    _chargingRecordRepo.Update(record);
+
+                //處理傳送給客戶api的紀錄
+                var report = crrRepo.Query(c => c.ChargingSerNo == record.ChargingSerNo).Select().FirstOrDefault();
+
+                if (report == null)
+                {
+                    report = new ChargingRecordReport();
+                    report.CreatedOn = DateTime.Now;
+                    report.StartChargingDateTime = record.StartChargingDateTime;
+                    report.ChargingSerNo = record.ChargingSerNo;
+                    report.CustomerId = machine.CustomerId;
+                    report.GunSerNo = record.GunSerNo;
+                    report.MachineCustomId = machine.CustomId;
+                    report.MachineId = machine.Id;
+                    report.MemberId = record.MemberId;
+                    report.CardNum = record.CardNum;
+                    report.StartWith = record.StartWith;
+                    report.Time1 = record.Time1;
+                    report.Time2 = record.Time2;
+                    report.Time3 = record.Time3;
+                    report.Time4 = record.Time4;
+                    report.Time5 = record.Time5;
+                    report.Time6 = record.Time6;
+                    report.Time7 = record.Time7;
+                    report.Time8 = record.Time8;
+                    report.Time9 = record.Time9;
+                    report.Time10 = record.Time10;
+                    report.Time11 = record.Time11;
+                    report.Time12 = record.Time12;
+                    report.Time13 = record.Time13;
+                    report.Time14 = record.Time14;
+                    report.Time15 = record.Time15;
+                    report.Time16 = record.Time16;
+                    report.Time17 = record.Time17;
+                    report.Time18 = record.Time18;
+                    report.Time19 = record.Time19;
+                    report.Time20 = record.Time20;
+                    report.Time21 = record.Time21;
+                    report.Time22 = record.Time22;
+                    report.Time23 = record.Time23;
+                    report.Time24 = record.Time24;
+                    report.Time25 = record.Time25;
+                    report.Time26 = record.Time26;
+                    report.Time27 = record.Time27;
+                    report.Time28 = record.Time28;
+                    report.Time29 = record.Time29;
+                    report.Time30 = record.Time30;
+                    report.Time31 = record.Time31;
+                    report.Time32 = record.Time32;
+                    report.Time33 = record.Time33;
+                    report.Time34 = record.Time34;
+                    report.Time35 = record.Time35;
+                    report.Time36 = record.Time36;
+                    report.Time37 = record.Time37;
+                    report.Time38 = record.Time38;
+                    report.Time39 = record.Time39;
+                    report.Time40 = record.Time40;
+                    report.Time41 = record.Time41;
+                    report.Time42 = record.Time42;
+                    report.Time43 = record.Time43;
+                    report.Time44 = record.Time44;
+                    report.Time45 = record.Time45;
+                    report.Time46 = record.Time46;
+                    report.Time47 = record.Time47;
+                    report.Time48 = record.Time48;
+                    report.ChargeStrategy = record.ChargingStrategy;
+                    report.ChargeStrategyParam = record.ChargingStrategyParam;
+                    report.SumOfMoney = record.SumOfMoney;
+                    report.FeeAmount = record.FeeAmount;
+                    report.ElectricBill = record.ElectricBill;
+                    report.StartChargingDateTime = record.ClientStartChargingDateTime;
+                    report.EndChargingDateTime = record.ClientEndChargingDateTime;
+                    report.CumulativeKwh = record.CumulativeKwh;
+                    var ts = report.EndChargingDateTime - report.StartChargingDateTime;
+                    report.TotalUsedTime = Convert.ToInt32(ts.Value.TotalSeconds);
+                    report.CarNum = record.CarNum;
+                    report.StartSoc = record.StartSoc;
+                    report.EndSoc = record.EndSoc;
+                    report.KwhBeforeCharging = record.KwhBeforeCharging;
+                    report.KwhAfterCharging = record.KwhAfterCharging;
+                    report.CardBalanceBeforeCharging = record.CardBalanceBeforeCharging;
+                    report.CardBalanceAfterCharging = record.CardBalanceAfterCharging;
+                    report.PayOffline = record.PayOffline;
+                    report.CardVIN = record.CardVIN;
+                    report.StartChargingType = record.StartChargingType;
+                    report.StopChargingReason = record.StopChargingReason;
+                    report.UpdatedOn = DateTime.Now;
+                    crrRepo.Add(report);
+                }
+                else
+                {
+                    report.MemberId = record.MemberId;
+                    report.CardNum = record.CardNum;
+                    report.StartWith = record.StartWith;
+                    report.Time1 = record.Time1;
+                    report.Time2 = record.Time2;
+                    report.Time3 = record.Time3;
+                    report.Time4 = record.Time4;
+                    report.Time5 = record.Time5;
+                    report.Time6 = record.Time6;
+                    report.Time7 = record.Time7;
+                    report.Time8 = record.Time8;
+                    report.Time9 = record.Time9;
+                    report.Time10 = record.Time10;
+                    report.Time11 = record.Time11;
+                    report.Time12 = record.Time12;
+                    report.Time13 = record.Time13;
+                    report.Time14 = record.Time14;
+                    report.Time15 = record.Time15;
+                    report.Time16 = record.Time16;
+                    report.Time17 = record.Time17;
+                    report.Time18 = record.Time18;
+                    report.Time19 = record.Time19;
+                    report.Time20 = record.Time20;
+                    report.Time21 = record.Time21;
+                    report.Time22 = record.Time22;
+                    report.Time23 = record.Time23;
+                    report.Time24 = record.Time24;
+                    report.Time25 = record.Time25;
+                    report.Time26 = record.Time26;
+                    report.Time27 = record.Time27;
+                    report.Time28 = record.Time28;
+                    report.Time29 = record.Time29;
+                    report.Time30 = record.Time30;
+                    report.Time31 = record.Time31;
+                    report.Time32 = record.Time32;
+                    report.Time33 = record.Time33;
+                    report.Time34 = record.Time34;
+                    report.Time35 = record.Time35;
+                    report.Time36 = record.Time36;
+                    report.Time37 = record.Time37;
+                    report.Time38 = record.Time38;
+                    report.Time39 = record.Time39;
+                    report.Time40 = record.Time40;
+                    report.Time41 = record.Time41;
+                    report.Time42 = record.Time42;
+                    report.Time43 = record.Time43;
+                    report.Time44 = record.Time44;
+                    report.Time45 = record.Time45;
+                    report.Time46 = record.Time46;
+                    report.Time47 = record.Time47;
+                    report.Time48 = record.Time48;
+                    report.CumulativeKwh = record.CumulativeKwh;
+                    report.EndChargingDateTime = record.ClientEndChargingDateTime;
+                    report.StartChargingDateTime = record.ClientStartChargingDateTime;
+                    report.FeeAmount = record.FeeAmount;
+                    report.ElectricBill = record.ElectricBill;
+                    report.SumOfMoney = record.SumOfMoney;
+                    var ts = report.EndChargingDateTime - report.StartChargingDateTime;
+                    report.TotalUsedTime = Convert.ToInt32(ts.Value.TotalSeconds);
+                    report.CarNum = record.CarNum;
+                    report.StartSoc = record.StartSoc;
+                    report.EndSoc = record.EndSoc;
+                    report.KwhBeforeCharging = record.KwhBeforeCharging;
+                    report.KwhAfterCharging = record.KwhAfterCharging;
+                    report.CardBalanceBeforeCharging = record.CardBalanceBeforeCharging;
+                    report.CardBalanceAfterCharging = record.CardBalanceAfterCharging;
+                    report.PayOffline = record.PayOffline;
+                    report.CardVIN = record.CardVIN;
+                    report.StartChargingType = record.StartChargingType;
+                    report.StopChargingReason = record.StopChargingReason;
+                    report.UpdatedOn = DateTime.Now;
+                }
+
+                WarnChargeIsAbnormal(record);
+                _uow.SaveChanges();
+                result.Success = true;
+                return result;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result.Exception = ex;
+                result.Success = false;
+                return result;
+            }
+
+        }
+
+        /// <summary>
+        /// 通知Email
+        /// </summary>
+        /// <param name="machineId">電樁Id</param>
+        /// <param name="subject">主旨</param>
+        /// <param name="body">內文</param>
+        /// <returns></returns>
+        private IResult NoticeEmail(string machineId, string subject, string body)
+        {
+            IResult result = new Result();
+            try
+            {
+                var machineRepo = _uow.Repository<Machine>();
+                var machine = machineRepo.Query(c => c.Id == machineId).Include(c => c.Customer).Select().FirstOrDefault();
+                var accountRepo = _uow.Repository<Account>();
+                var account = accountRepo.Query(c => c.CustomerId == machine.CustomerId).Select().FirstOrDefault();
+
+                if (!string.IsNullOrEmpty(account.Email))
+                {
+                    string[] emailgroup = account.Email.Split(',');
+                    string _subject = string.Format("[{0}] 客户: {1} 飞宏电桩号: {2} 客户电桩号: {3} ", subject, machine.Customer.CustomerDesc, machine.CustomId, machine.PartnerPoleId);
+                    foreach (var email in emailgroup)
+                    {
+                        DispatchMessageHelper.SendMail(email, _subject, body);
+                    }
+                }
+                result.Success = true;
+                return result;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result.Exception = ex;
+                result.Success = false;
+                return result;
+            }
+        }
+
+        /// <summary>
+        /// 警告充電量異常
+        /// </summary>
+        /// <returns></returns>
+        private IResult WarnChargeIsAbnormal(ChargingRecord record)
+        {
+            IResult result = new Result();
+            try
+            {
+                //設定條件-充電時間超過 1800秒 -總充電量小於 0.5kw  - StartWith != 2
+                if (record.CumulativeKwh < Convert.ToDecimal(0.5) && record.ClientEndChargingDateTime.HasValue
+                    && record.ClientStartChargingDateTime.HasValue && record.StartWith != 2)
+                {
+                    var totalSeconds = record.ClientEndChargingDateTime.Value.Subtract(record.ClientStartChargingDateTime.Value).TotalSeconds;
+                    if (totalSeconds > 1800d)
+                    {
+                        string body = string.Format("详细讯息: 充电序:{0} 充电开始时间:{1} 充电结束时间: {2} 充电秒数:{3} 充电度数:{4}",
+                            record.ChargingSerNo, record.ClientStartChargingDateTime.HasValue ? record.ClientStartChargingDateTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : "None",
+                            record.ClientEndChargingDateTime.HasValue ? record.ClientEndChargingDateTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : "None", totalSeconds, record.CumulativeKwh);
+                        NoticeEmail(record.MachineId, "充电量异常通知", body);
+                    }
+                }
+                result.Success = true;
+                return result;
+            }
+            catch (Exception ex)
+            {
+                logger.Error(ex);
+                result.Success = false;
+                result.Exception = ex;
+                result.Message = ex.Message;
+                return result;
+            }
+        }
+    }
+}

+ 445 - 0
OCPPServer/SubProtocol/OCPPLog.cs

@@ -0,0 +1,445 @@
+using NLog;
+using NLog.Fluent;
+using SuperSocket.SocketBase.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.SubProtocol
+{
+    public class OCPPLog : ILog
+    {
+        private NLog.ILogger m_Log;
+
+        public OCPPLog(string name)
+        {
+            m_Log = NLog.LogManager.GetCurrentClassLogger();
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is debug enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is debug enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsDebugEnabled
+        {
+            get { return m_Log.IsDebugEnabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is error enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is error enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsErrorEnabled
+        {
+            get { return m_Log.IsErrorEnabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is fatal enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is fatal enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsFatalEnabled
+        {
+            get { return m_Log.IsFatalEnabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is info enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is info enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsInfoEnabled
+        {
+            get { return m_Log.IsInfoEnabled; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance is warn enabled.
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if this instance is warn enabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool IsWarnEnabled
+        {
+            get { return m_Log.IsWarnEnabled; }
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Debug(object message)
+        {
+            m_Log.Debug(message);
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Debug(object message, Exception exception)
+        {
+            //m_Log.Debug((System.IFormatProvider)message, exception);
+            m_Log.Debug(exception, message.ToString());
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void DebugFormat(string format, object arg0)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void DebugFormat(string format, params object[] args)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void DebugFormat(IFormatProvider provider, string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void DebugFormat(string format, object arg0, object arg1)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the debug message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void DebugFormat(string format, object arg0, object arg1, object arg2)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Error(object message)
+        {
+            m_Log.Error(message);
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Error(object message, Exception exception)
+        {
+            //m_Log.Error((System.IFormatProvider)message, exception);
+            m_Log.Error(exception, message.ToString());
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void ErrorFormat(string format, object arg0)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void ErrorFormat(string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void ErrorFormat(IFormatProvider provider, string format, params object[] args)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void ErrorFormat(string format, object arg0, object arg1)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void ErrorFormat(string format, object arg0, object arg1, object arg2)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Fatal(object message)
+        {
+            m_Log.Fatal(message);
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Fatal(object message, Exception exception)
+        {
+            m_Log.Fatal((System.IFormatProvider)message, exception);
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void FatalFormat(string format, object arg0)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void FatalFormat(string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void FatalFormat(IFormatProvider provider, string format, params object[] args)
+        {
+          
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void FatalFormat(string format, object arg0, object arg1)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the fatal error message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void FatalFormat(string format, object arg0, object arg1, object arg2)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Info(object message)
+        {
+            m_Log.Info(message);
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Info(object message, Exception exception)
+        {
+            //m_Log.Info((System.IFormatProvider)message, exception);
+            m_Log.Info(exception, message.ToString());
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void InfoFormat(string format, object arg0)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void InfoFormat(string format, params object[] args)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void InfoFormat(IFormatProvider provider, string format, params object[] args)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void InfoFormat(string format, object arg0, object arg1)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the info message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void InfoFormat(string format, object arg0, object arg1, object arg2)
+        {
+            
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        public void Warn(object message)
+        {
+            m_Log.Warn(message);
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="exception">The exception.</param>
+        public void Warn(object message, Exception exception)
+        {
+            //m_Log.Warn((System.IFormatProvider)message, exception);
+            m_Log.Warn(exception, message.ToString());
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        public void WarnFormat(string format, object arg0)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void WarnFormat(string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="format">The format.</param>
+        /// <param name="args">The args.</param>
+        public void WarnFormat(IFormatProvider provider, string format, params object[] args)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        public void WarnFormat(string format, object arg0, object arg1)
+        {
+           
+        }
+
+        /// <summary>
+        /// Logs the warning message.
+        /// </summary>
+        /// <param name="format">The format.</param>
+        /// <param name="arg0">The arg0.</param>
+        /// <param name="arg1">The arg1.</param>
+        /// <param name="arg2">The arg2.</param>
+        public void WarnFormat(string format, object arg0, object arg1, object arg2)
+        {
+            
+        }
+    }
+}

+ 108 - 0
OCPPServer/SubProtocol/OCPPLogFactory.cs

@@ -0,0 +1,108 @@
+
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.SubProtocol
+{
+    /// <summary>
+    /// OCPP log factory
+    /// </summary>
+    public class OCPPLogFactory : LogFactoryBase 
+    {
+        public OCPPLogFactory()
+            : this("NLog.config")
+        {
+
+        }
+
+        public OCPPLogFactory(string log4netConfig)
+            : base(log4netConfig)
+        {
+            List<string> configlist = new List<string>();
+            configlist.Add(ConfigFile);
+            NLog.Config.XmlLoggingConfiguration.SetCandidateConfigFilePaths(configlist);
+        }
+
+
+        /// <summary>
+        /// Gets the config file file path.
+        /// </summary>
+        //protected string ConfigFile { get; private set; }
+
+        //public OCPPLogFactory()
+        //{
+        //    string configFile = "NLog.config";
+        //    var currentAppDomain = AppDomain.CurrentDomain;
+        //    var isolation = IsolationMode.None;
+
+        //    var isolationValue = currentAppDomain.GetData(typeof(IsolationMode).Name);
+
+        //    if (isolationValue != null)
+        //        isolation = (IsolationMode)isolationValue;
+
+        //    if (isolation == IsolationMode.None)
+        //    {
+        //        var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, configFile);
+
+        //        if (File.Exists(filePath))
+        //        {
+        //            ConfigFile = filePath;
+        //            return;
+        //        }
+
+        //        filePath = Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config"), configFile);
+
+        //        if (File.Exists(filePath))
+        //        {
+        //            ConfigFile = filePath;
+        //            return;
+        //        }
+
+        //        ConfigFile = configFile;
+
+        //        List<string> configlist = new List<string>();
+        //        configlist.Add(ConfigFile);
+        //        NLog.Config.XmlLoggingConfiguration.SetCandidateConfigFilePaths(configlist);
+        //        return;
+        //    }
+        //}
+
+        /// <summary>
+        /// Gets the log by name.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <returns></returns>
+        /// <summary>
+        /// Gets the log by name.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <returns></returns>
+        /// 
+
+
+        //public override ILog GetLog(string name)
+        //{
+        //    NLog.ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        //    //return new ConsoleLog(name);
+
+        //    ILog log = logger as Log4NetLog;
+
+
+
+
+
+        //    return new Log4NetLog(LogManager.GetLogger(name)); ;
+        //}
+        public override SuperSocket.SocketBase.Logging.ILog GetLog(string name)
+        {
+           
+            return new OCPPLog(name);
+        }
+    }
+}

+ 35 - 0
OCPPServer/SubProtocol/OCPPSubCommandParser.cs

@@ -0,0 +1,35 @@
+using SuperSocket.SocketBase.Protocol;
+using SuperWebSocket.SubProtocol;
+
+namespace OCPPServer.SubProtocol
+{
+    public class OCPPSubCommandParser : IRequestInfoParser<SubRequestInfo>
+    {
+        #region ISubProtocolCommandParser Members
+
+        /// <summary>
+        /// Parses the request info.
+        /// </summary>
+        /// <param name="source">The source.</param>
+        /// <returns></returns>
+        public SubRequestInfo ParseRequestInfo(string source)
+        {
+            var cmd = source.Trim();
+            int pos = cmd.IndexOf(',');
+            string name;
+
+            if (pos > 0)
+            {
+                name = cmd.Substring(pos - 1,1);
+            }
+            else
+            {
+                name = "4";
+            }
+
+            return new SubRequestInfo(name, "", source);
+        }
+
+        #endregion ISubProtocolCommandParser Members
+    }
+}

+ 319 - 0
OCPPServer/SubProtocol/OCPPSubProtocol.cs

@@ -0,0 +1,319 @@
+using OCPPServer.Protocol;
+using SuperSocket.Common;
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Logging;
+using SuperSocket.SocketBase.Protocol;
+using SuperWebSocket;
+using SuperWebSocket.Config;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OCPPServer.SubProtocol
+{
+    public class OCPPSubProtocol : OCPPSubProtocol<ClientData>
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol"/> class.
+        /// </summary>
+        public OCPPSubProtocol()
+            : base(Assembly.GetCallingAssembly())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol"/> class.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        public OCPPSubProtocol(string name)
+            : base(name, Assembly.GetCallingAssembly())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol"/> class.
+        /// </summary>
+        /// <param name="commandAssembly">The command assembly.</param>
+        public OCPPSubProtocol(Assembly commandAssembly)
+            : base(commandAssembly)
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OCPPSubProtocol"/> class.
+        /// </summary>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        public OCPPSubProtocol(IEnumerable<Assembly> commandAssemblies)
+            : base(commandAssemblies)
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OCPPSubProtocol"/> class.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        /// <param name="commandAssembly">The command assembly.</param>
+        public OCPPSubProtocol(string name, Assembly commandAssembly)
+            : base(name, commandAssembly)
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OCPPSubProtocol"/> class.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        public OCPPSubProtocol(string name, IEnumerable<Assembly> commandAssemblies)
+            : base(name, commandAssemblies)
+        {
+
+        }
+
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="OCPPSubProtocol"/> class.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        /// <param name="requestInfoParser">The request info parser.</param>
+        public OCPPSubProtocol(string name, IEnumerable<Assembly> commandAssemblies, IRequestInfoParser<SubRequestInfo> requestInfoParser)
+            : base(name, commandAssemblies, requestInfoParser)
+        {
+
+        }
+
+        public ILog Getmlog()
+        {
+            return getlog();
+        }
+
+    }
+
+    public class OCPPSubProtocol<TWebSocketSession> : SubProtocolBase<TWebSocketSession>
+       where TWebSocketSession : WebSocketSession<TWebSocketSession>, new()
+    {
+        /// <summary>
+        /// Default basic sub protocol name
+        /// </summary>
+        public const string DefaultName = "ocpp1.6";//"OCPP1.6";
+
+        private List<Assembly> m_CommandAssemblies = new List<Assembly>();
+
+        private Dictionary<string, ISubCommand<TWebSocketSession>> m_CommandDict;
+
+        private ILog m_Logger;
+
+        private SubCommandFilterAttribute[] m_GlobalFilters;
+
+        internal static BasicSubProtocol<TWebSocketSession> CreateDefaultSubProtocol()
+        {
+            var commandAssembly = typeof(TWebSocketSession).Assembly;
+
+            if (commandAssembly == Assembly.GetExecutingAssembly())
+                commandAssembly = Assembly.GetEntryAssembly();
+
+            return new BasicSubProtocol<TWebSocketSession>(DefaultName, commandAssembly);
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with the calling aseembly as command assembly
+        /// </summary>
+        public OCPPSubProtocol()
+            : this(DefaultName, Assembly.GetCallingAssembly())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with the calling aseembly as command assembly
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        public OCPPSubProtocol(string name)
+            : this(name, Assembly.GetCallingAssembly())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with command assemblies
+        /// </summary>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        public OCPPSubProtocol(IEnumerable<Assembly> commandAssemblies)
+            : this(DefaultName, commandAssemblies, new OCPPSubCommandParser())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with single command assembly.
+        /// </summary>
+        /// <param name="commandAssembly">The command assembly.</param>
+        public OCPPSubProtocol(Assembly commandAssembly)
+            : this(DefaultName, new List<Assembly> { commandAssembly }, new OCPPSubCommandParser())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with name and single command assembly.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        /// <param name="commandAssembly">The command assembly.</param>
+        public OCPPSubProtocol(string name, Assembly commandAssembly)
+            : this(name, new List<Assembly> { commandAssembly }, new OCPPSubCommandParser())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class with name and command assemblies.
+        /// </summary>
+        /// <param name="name">The sub protocol name.</param>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        public OCPPSubProtocol(string name, IEnumerable<Assembly> commandAssemblies)
+            : this(name, commandAssemblies, new OCPPSubCommandParser())
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BasicSubProtocol&lt;TWebSocketSession&gt;"/> class.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <param name="commandAssemblies">The command assemblies.</param>
+        /// <param name="requestInfoParser">The request info parser.</param>
+        public OCPPSubProtocol(string name, IEnumerable<Assembly> commandAssemblies, IRequestInfoParser<SubRequestInfo> requestInfoParser)
+            : base(name)
+        {
+            //The items in commandAssemblies may be null, so filter here
+            m_CommandAssemblies.AddRange(commandAssemblies.Where(a => a != null));
+            SubRequestParser = requestInfoParser;
+        }
+
+        #region ISubProtocol Members
+
+        private void DiscoverCommands()
+        {
+            var subCommands = new List<ISubCommand<TWebSocketSession>>();
+
+            foreach (var assembly in m_CommandAssemblies)
+            {
+                subCommands.AddRange(assembly.GetImplementedObjectsByInterface<ISubCommand<TWebSocketSession>>());
+            }
+
+#if DEBUG
+            var cmdbuilder = new StringBuilder();
+            cmdbuilder.AppendLine(string.Format("SubProtocol {0} found the commands below:", this.Name));
+
+            foreach (var c in subCommands)
+            {
+                cmdbuilder.AppendLine(c.Name);
+            }
+
+
+            m_Logger.Debug(cmdbuilder.ToString());
+#endif
+
+            m_CommandDict = new Dictionary<string, ISubCommand<TWebSocketSession>>(subCommands.Count, StringComparer.OrdinalIgnoreCase);
+
+            subCommands.ForEach(c =>
+            {
+                var fc = c as ISubCommandFilterLoader;
+
+                if (fc != null)
+                    fc.LoadSubCommandFilters(m_GlobalFilters);
+
+                m_CommandDict.Add(c.Name, c);
+            }
+                );
+        }
+
+
+        private bool ResolveCommmandAssembly(string definition)
+        {
+            try
+            {
+                var assemblies = AssemblyUtil.GetAssembliesFromString(definition);
+
+                if (assemblies.Any())
+                    m_CommandAssemblies.AddRange(assemblies);
+
+                return true;
+            }
+            catch (Exception e)
+            {
+                m_Logger.Error(e);
+                return false;
+            }
+        }
+
+        public ILog getlog()
+        {
+            return m_Logger;
+        }
+
+        /// <summary>
+        /// Tries get command from the sub protocol's command inventory.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <param name="command">The command.</param>
+        /// <returns></returns>
+        public override bool TryGetCommand(string name, out ISubCommand<TWebSocketSession> command)
+        {
+            return m_CommandDict.TryGetValue(name, out command);
+        }
+
+        public override bool Initialize(IAppServer appServer, SubProtocolConfig protocolConfig, ILog logger)
+        {
+            m_Logger = logger;
+
+            var config = appServer.Config;
+
+            m_GlobalFilters = appServer.GetType()
+                    .GetCustomAttributes(true)
+                    .OfType<SubCommandFilterAttribute>()
+                    .Where(a => string.IsNullOrEmpty(a.SubProtocol) || Name.Equals(a.SubProtocol, StringComparison.OrdinalIgnoreCase)).ToArray();
+
+            if (Name.Equals(DefaultName, StringComparison.OrdinalIgnoreCase))
+            {
+                var commandAssembly = config.Options.GetValue("commandAssembly");
+
+                if (!string.IsNullOrEmpty(commandAssembly))
+                {
+                    if (!ResolveCommmandAssembly(commandAssembly))
+                        return false;
+                }
+            }
+
+            if (protocolConfig != null && protocolConfig.Commands != null)
+            {
+                foreach (var commandConfig in protocolConfig.Commands)
+                {
+                    var assembly = commandConfig.Options.GetValue("assembly");
+
+                    if (!string.IsNullOrEmpty(assembly))
+                    {
+                        if (!ResolveCommmandAssembly(assembly))
+                            return false;
+                    }
+                }
+            }
+
+            //Always discover commands
+            DiscoverCommands();
+
+            return true;
+        }
+
+        #endregion
+    }
+}

+ 1862 - 0
OCPPServer/WSServer.cs

@@ -0,0 +1,1862 @@
+using Evcb.Domain.Model;
+using Evcb.Domain.mongo;
+using Evcb.Repository;
+using Evcb.Service;
+using Evcb.Service.Helpers;
+using Evcb.Service.Service;
+using MongoDB.Driver;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NLog;
+using OCPPPacket.Packet.Feature;
+using OCPPPacket.Packet.Messages;
+using OCPPPacket.Packet.Messages.Basic;
+using OCPPPacket.Packet.Messages.Core;
+using OCPPPacket.Packet.Status;
+using OCPPServer.Handler;
+using OCPPServer.Protocol;
+using OCPPServer.SubProtocol;
+using Packet;
+using Packet.Cmd;
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Config;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data.Entity;
+using System.Data.Entity.Validation;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+namespace OCPPServer
+{
+    public class WSServer
+    {
+        private readonly Object _lockClientDic = new object();
+        private readonly Object _lockConfirmPacketList = new object();
+        private CancellationTokenSource _cts = new CancellationTokenSource();
+        private CancellationToken _ct;
+        private IMongoService mongoService = new MongoService();
+        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        private List<string> _cmd202lst = new List<string>();
+        private List<int> _needConfirmCmdList;
+        public Dictionary<string, ClientData> ClientDic { get; set; }
+        public string Listening { get; set; }
+
+        public string ApiUrl { get; set; }
+        public List<NeedConfirmPacket> NeedConfirmPacketList { get; set; }
+
+        /// <summary>
+        /// 上次校時時間
+        /// </summary>
+        private DateTime sendTiming { get; set; }
+
+        /// <summary>
+        /// 檢查軟韌體時間
+        /// </summary>
+        private DateTime checkUpdateDt { get; set; }
+
+        /// <summary>
+        /// 可允許連線Clinet數
+        /// </summary>
+        public int connectNum { get; set; }
+
+        /// <summary>
+        /// 是否限制連線Clinet數
+        /// </summary>
+        public bool beConnectLimit { get; set; }
+
+        /// <summary>
+        /// 是否紀錄與充電樁互傳命令 在 MachineConnectionLog
+        /// </summary>
+        private bool ENABLELOG_MACHINECMD = false;
+
+        public WSServer()
+        {
+            using (var db = new PhihongDbContext())
+            {
+                _ct = _cts.Token;
+                ClientDic = new Dictionary<string, ClientData>();
+                NeedConfirmPacketList = new List<NeedConfirmPacket>();
+
+                //需要確認回傳的指令
+                _needConfirmCmdList = db.NeedConfirmCmd.Select(c => c.Id).ToList();
+
+                //載入相關config
+                ENABLELOG_MACHINECMD = ConfigurationManager.AppSettings.Get("EnableMachineConnectionLog").Equals("0") ? false : true;
+                ApiUrl = ConfigurationManager.AppSettings.Get("ApiUrl");
+                string serverIp = ConfigurationManager.AppSettings.Get("ServerIp");
+                string serverPort = ConfigurationManager.AppSettings.Get("ServerPort");
+                
+                string wssserverPort = ConfigurationManager.AppSettings.Get("WSSServerPort");
+                string serverSecurity = ConfigurationManager.AppSettings.Get("ServerSecurity");
+                int maxRequestLength = int.Parse(ConfigurationManager.AppSettings.Get("MaxRequestLength"));
+                var config = ConfigurationManager.GetSection("superSocket") as IConfigurationSource;
+                ICertificateConfig Certificate = config.Servers.ElementAt(0).Certificate;
+
+                sendTiming = DateTime.Now;
+                checkUpdateDt = DateTime.Now;
+
+                //載入OCPP Protocol
+                var appServer = new OCPPWSServer(new OCPPSubProtocol());
+
+                List<IListenerConfig> llistener = new List<IListenerConfig>();
+
+                llistener.Add(new ListenerConfig { Ip = serverIp, Port = Convert.ToInt32(wssserverPort), Backlog = 100, Security = serverSecurity });
+                llistener.Add(new ListenerConfig { Ip = serverIp, Port = Convert.ToInt32(serverPort), Backlog = 100, Security = "None" });
+
+                IEnumerable<IListenerConfig> listeners = llistener;
+
+               //設定server config
+               var serverConfig = new ServerConfig
+                {
+                    //Port = Convert.ToInt32(serverPort),
+                    //Ip = serverIp,
+                    MaxRequestLength = maxRequestLength,
+                    //Security = serverSecurity,
+                    Certificate = Certificate,
+                   Listeners = listeners
+
+               };
+
+                //Setup with listening port
+                if (!appServer.Setup(serverConfig, logFactory: new OCPPLogFactory()))
+                {
+                    Console.WriteLine("Failed to setup!");
+                    Console.ReadKey();
+                    return;
+                }
+
+                appServer.NewSessionConnected += AppServer_NewSessionConnected;
+                appServer.SessionClosed += AppServer_SessionClosed;
+                Console.WriteLine();
+
+                string mac = ConfigurationManager.AppSettings.Get("mac");
+                string limit = ConfigurationManager.AppSettings.Get("limit");
+                string signature = ConfigurationManager.AppSettings.Get("Signature");
+                appServer.beConnectLimit = true;
+                Console.Write("<=============================================================== \n");
+                Console.Write("Server Mac: {0}\n", mac);
+                Console.Write("Connection Limit: {0}\n", limit);
+                Console.Write("Signature: {0}\n", signature);
+                var result = ConnectionLimitHepler.ConnectionLimitValidation(ConnectionLimitHepler.Key, signature, mac, limit);
+                if (result.IsValid == true)
+                {
+                    Console.Write("Verification Signature Result : correct\n");
+                    if (limit.Contains("∞") == false)
+                    {
+                        appServer.connectNum = Int32.Parse(limit);
+                        Console.Write("Real Connection Limit: {0}\n", appServer.connectNum);
+                    }
+                    else
+                    {
+                        appServer.connectNum = 0;
+                        appServer.beConnectLimit = false;
+                        Console.Write("Real Connection Limit: {0}\n", "∞");
+                    }
+                }
+                else
+                {
+                    Console.Write("Verification Signature Result : incorrect\n");
+                    appServer.connectNum = 10;
+                    Console.Write("Real Connection Limit: {0}\n", appServer.connectNum);
+                }
+                Console.Write("===============================================================> \n");
+
+                //Try to start the appServer
+                if (!appServer.Start())
+                {
+                    Console.WriteLine("Failed to start!");
+                    Console.ReadKey();
+                    return;
+                }
+            }
+        }
+
+        public void Start()
+        {
+            Task taskServerCommand = new Task(ServerCommandThread, _ct);
+            taskServerCommand.Start();
+
+            while (true)
+            {
+                var input = Console.ReadLine();
+
+                switch (input.ToLower())
+                {
+                    case "stop":
+                        //ListenerSocket.Shutdown(SocketShutdown.Both);
+                        //ListenerSocket.Close();
+                        Stop();
+                        logger.Info("server stop");
+                        Task.WaitAny(taskServerCommand);
+                        break;
+
+                    case "gc":
+                        logger.Info("Command GC");
+                        GC.Collect();
+                        break;
+
+                    case "lc":
+                        logger.Info("Command List Clients");
+                        var list = ClientDic.Select(c => c.Value).ToList();
+                        int i = 1;
+                        foreach (var c in list)
+                        {
+                            logger.Info(i + ":" + c.MachineCustomId);
+                            i++;
+                        }
+                        break;
+
+                    case "lcn":
+                        logger.Info("Command List Customer Name");
+                        var lcn = ClientDic.Select(c => c.Value.CustomerName).Distinct().ToList();
+                        int iLcn = 1;
+                        foreach (var c in lcn)
+                        {
+                            logger.Info(iLcn + ":" + c + ":" + ClientDic.Where(z => z.Value.CustomerName == c).Count().ToString());
+                            iLcn++;
+                        }
+                        break;
+
+                    case "help":
+                        logger.Info("Command help!!");
+                        logger.Info("lcn : List Customer Name");
+                        logger.Info("gc : GC Collect");
+                        logger.Info("lc : List Clients");
+                        logger.Info("cls : clear console");
+                        logger.Info("silent : silent");
+                        logger.Info("show : show log");
+                        logger.Info("rcl : show Real Connection Limit");
+                        break;
+
+                    case "cls":
+                        logger.Info("Command clear");
+                        Console.Clear();
+                        break;
+
+                    case "silent":
+                        logger.Info("Command silent");
+                        var xe = XElement.Load("NLog.config");
+                        var xns = xe.GetDefaultNamespace();
+                        var minlevelattr = xe.Descendants(xns + "rules").Elements(xns + "logger")
+                            .Where(c => c.Attribute("writeTo").Value.Equals("console")).Attributes("minlevel").FirstOrDefault();
+                        if (minlevelattr != null)
+                        {
+                            minlevelattr.Value = "info";
+                        }
+                        xe.Save("NLog.config");
+                        break;
+
+                    case "show":
+                        logger.Info("Command show");
+                        var xe1 = XElement.Load("NLog.config");
+                        var xns1 = xe1.GetDefaultNamespace();
+                        var minlevelattr1 = xe1.Descendants(xns1 + "rules").Elements(xns1 + "logger")
+                            .Where(c => c.Attribute("writeTo").Value.Equals("console")).Attributes("minlevel").FirstOrDefault();
+                        if (minlevelattr1 != null)
+                        {
+                            minlevelattr1.Value = "trace";
+                        }
+                        xe1.Save("NLog.config");
+                        break;
+
+                    case "rcl":
+                        logger.Info("Command rcln");
+                        if (beConnectLimit == true)
+                        {
+                            logger.Info("Real Connection Limit:" + connectNum);
+                        }
+                        else
+                        {
+                            logger.Info("Real Connection Limit:" + "∞");
+                        }
+
+                        break;
+
+                    default:
+                        break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 處理電樁送的OCPP 訊息
+        /// </summary>
+        public void ReceivedMessage(ClientData ctdata, String msg)
+        {
+            using (var db = new PhihongDbContext())
+            {
+                IUnitOfWork uow = new UnitOfWork(db);
+                IMachineService machineSrv = new MachineService(uow);
+                ISocketCommandService socketCommandSrv = new SocketCommandService(uow);
+                ICustomerService _customerService = new CustomerService();
+                OCPPMessageHandler ocppMsgHandler = new OCPPMessageHandler();
+                try
+                {
+                    //檢查是否有Connection 沒有加入ClientDic
+                    if (ctdata.IsCheckIn)
+                    {
+                        lock (_lockClientDic)
+                        {
+                            if (!ClientDic.ContainsKey(ctdata.MachineCustomId))
+                            {
+                                ClientDic.Add(ctdata.MachineCustomId, ctdata);
+                                logger.Trace("Rejoin MachineCustomId: {0}", ctdata.MachineCustomId);
+                            }
+                        }
+                    }
+
+                    BaseMessage message = ocppMsgHandler.Parse(msg);
+                    CheckConfirmPacket(ctdata, message);
+                    if (message is CallResultMessage)
+                    {
+                        #region 處理CallResultMessage
+
+                        //更新充電樁的心跳包接收時間
+                        socketCommandSrv.UpdateHeartbeat(ctdata.MachineCustomId);
+                        CallResultMessage call = (CallResultMessage)message;
+                        string callResultAction = string.Empty;
+                        var result = ocppMsgHandler.OnCallResult(call.id, call.payload, ref ctdata.queue, out callResultAction);
+
+                        if (result.Success == true)
+                        {
+                            if (ENABLELOG_MACHINECMD)
+                            {
+                                MachineConnectionLog log = new MachineConnectionLog();
+                                log.MachineCustomId = ctdata.MachineCustomId;
+                                log.MachineId = ctdata.MachineId;
+                                log.IpPort = ctdata.LocalEndPoint.ToString();
+                                log.IsIllegal = false;
+
+                                if (msg.Length >= 2000)
+                                    log.Data = msg.Substring(0, 1999);
+                                else
+                                    log.Data = msg;
+
+                                log.IsDataOut = false;
+                                log.CmdNum = 0;
+                                log.CreatedOn = DateTime.Now;
+                                log.Msg = callResultAction;
+                                var ep = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                log.ClientIpPort = ep.Address.ToString() + ":" + ep.Port.ToString();
+
+                                SaveMachineConnectionLog(log);
+
+                                if (msg.Length >= 2000) //  GetConfiguration.conf封包
+                                {
+                                    MachineConnectionLog log1 = new MachineConnectionLog();
+                                    log1.MachineCustomId = ctdata.MachineCustomId;
+                                    log1.MachineId = ctdata.MachineId;
+                                    log1.IpPort = ctdata.LocalEndPoint.ToString();
+                                    log1.IsIllegal = false;
+                                    log1.IsDataOut = false;
+                                    log1.CmdNum = 0;
+                                    log1.Data = "config :" + msg.Substring(2000, msg.Length - 2000);
+                                    log1.CreatedOn = DateTime.Now;
+                                    log1.Msg = callResultAction;
+                                    var ep1 = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                    log1.ClientIpPort = ep1.Address.ToString() + ":" + ep1.Port.ToString();
+
+                                    SaveMachineConnectionLog(log1);
+                                }
+
+                              
+                            }
+
+                            if (!ctdata.IsCheckIn && (result.Payload is GetConfigurationConfirmation))
+                            {
+                                IRequest req = MakeRequestHandler.MakeChangeConfigurationRequest("HeartBeatInterval", "60");
+                                string uuid = ctdata.queue.store(req);
+                                Send(ctdata, ocppMsgHandler.GenerateCall(uuid, Actions.ChangeConfiguration.ToString(), req), "ChangeConfiguration Request");
+                            }
+
+                            //將OCPP CallResult Message的Payload所夾帶參數資訊進行處理
+                            var handleConfirmationResult = ocppMsgHandler.HandleConfirmation(call.id, (IConfirmation)result.Payload, callResultAction, db, ref ctdata);
+                            if (handleConfirmationResult.Success == true)
+                            {
+                                //do nothing
+                            }
+                            else
+                            {
+                                if (ENABLELOG_MACHINECMD)
+                                {
+                                    //紀錄error type message
+                                    MachineConnectionLog log1 = new MachineConnectionLog();
+                                    log1.MachineCustomId = ctdata.MachineCustomId;
+                                    log1.MachineId = ctdata.MachineId;
+                                    log1.IpPort = ctdata.LocalEndPoint.ToString();
+                                    log1.IsIllegal = true;
+                                    log1.IsDataOut = false;
+                                    log1.CmdNum = 0;
+                                    log1.Data = msg;
+                                    log1.CreatedOn = DateTime.Now;
+                                    log1.Msg = handleConfirmationResult.Message;
+                                    var ep1 = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                    log1.ClientIpPort = ep1.Address.ToString() + ":" + ep1.Port.ToString();
+
+                                    SaveMachineConnectionLog(log1);
+                                }
+                                //送出錯誤訊息給電樁
+                                //Send(ctdata, handleConfirmationResult.Message);
+                            }
+                        }
+                        else
+                        {
+                            #region error type message 處理
+
+                            if(result.Message.Equals("none") == true)
+                            {
+                                //重複送相同Message, 不處理
+                            }
+                            else
+                            {
+                                if (ENABLELOG_MACHINECMD)
+                                {
+                                    //紀錄error type message
+                                    MachineConnectionLog log = new MachineConnectionLog();
+                                    log.MachineCustomId = ctdata.MachineCustomId;
+                                    log.MachineId = ctdata.MachineId;
+                                    log.IpPort = ctdata.LocalEndPoint.ToString();
+                                    log.IsIllegal = true;
+                                    log.IsDataOut = false;
+                                    log.CmdNum = 0;
+                                    log.Data = msg;
+                                    log.CreatedOn = DateTime.Now;
+
+                                    //紀錄exception, 送出錯誤訊息給電樁
+                                    if (result.Exception != null)
+                                    {
+                                        logger.Error(result.Exception, "OnCallResult error");
+                                        log.Msg = result.Exception.ToString();
+                                    }
+                                    else
+                                    {
+                                        ////送出錯誤訊息給電樁
+                                        //Send(ctdata, result.Message, "Error Message");
+                                        log.Msg = result.Message;
+                                    }
+
+                                    var ep = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                    log.ClientIpPort = ep.Address.ToString() + ":" + ep.Port.ToString();
+
+                                    SaveMachineConnectionLog(log);
+                                }
+                                Send(ctdata, result.Message, "Error Message");
+
+                            }
+
+                          
+                            #endregion error type message 處理
+                        }
+
+                        #endregion 處理CallResultMessage
+                    }
+                    else if (message is CallErrorMessage)
+                    {
+                        #region 處理CallErrorMessage
+
+                        //failedFlag = true;
+                        //更新充電樁的心跳包接收時間
+                        socketCommandSrv.UpdateHeartbeat(ctdata.MachineCustomId);
+                        CallErrorMessage call = (CallErrorMessage)message;
+                        var result = ocppMsgHandler.OnError(call.id, call.ErrorCode, call.ErrorDescription, call.payload);
+                        if (result.Success == true)
+                        {
+                            if (ENABLELOG_MACHINECMD)
+                            {
+                                MachineConnectionLog log = new MachineConnectionLog();
+                                log.MachineCustomId = ctdata.MachineCustomId;
+                                log.MachineId = ctdata.MachineId;
+                                log.IpPort = ctdata.LocalEndPoint.ToString();
+                                log.IsIllegal = false;
+                                log.Data = msg;//call.id.ToString() + "," + JsonConvert.SerializeObject(result.Payload); ;
+                                log.IsDataOut = false;
+                                log.CmdNum = 0;
+                                log.CreatedOn = DateTime.Now;
+                                log.Msg = call.ErrorCode;
+                                var ep = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                log.ClientIpPort = ep.Address.ToString() + ":" + ep.Port.ToString();
+
+                                SaveMachineConnectionLog(log);
+                            }
+                            //重送上次未成功的訊息 或 移除Client
+                        }
+
+                        var handleErrorResult = ocppMsgHandler.HandleError(call.id, call.ErrorCode, call.ErrorDescription, call.payload);
+
+                        #endregion 處理CallErrorMessage
+                    }
+                    else if (message is CallMessage)
+                    {
+                        #region 處理CallMessage
+
+                        //處理OCPP Call Message
+                        CallMessage call = (CallMessage)message;
+                        var result = ocppMsgHandler.OnCall(call.id, call.action, call.payload);
+                        bool BootNotificationPending = false;
+
+                        if (result.Success == true)
+                        {
+                            //如果沒有 check in,設定為illegal的封包資料,紀錄之後,此筆放棄,處理下一個
+                            if (!ctdata.IsCheckIn)
+                            {
+                                if (ENABLELOG_MACHINECMD)
+                                {
+                                    MachineConnectionLog log = new MachineConnectionLog();
+                                    log.MachineCustomId = ctdata.MachineCustomId;
+                                    log.MachineId = ctdata.MachineId;
+                                    log.IpPort = ctdata.LocalEndPoint.ToString();
+                                    log.IsIllegal = true;
+                                    log.Data = msg;
+                                    log.IsDataOut = false;
+                                    log.CmdNum = 0;
+                                    log.CreatedOn = DateTime.Now;
+                                    log.Msg = call.action + " Request";
+                                    var ep = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                    log.ClientIpPort = ep.Address.ToString() + ":" + ep.Port.ToString();
+
+                                    SaveMachineConnectionLog(log);
+                                }
+
+                                //如果不是 BootNotificationRequest 就不處理,處理下一個指令
+                                if (!(result.Payload is BootNotificationRequest))
+                                {
+                                    return;
+                                }
+                                else
+                                {
+                                    if (ENABLELOG_MACHINECMD)
+                                    {
+                                        //紀錄收到BootNotificationRequest(目前還沒有CheckIn)
+                                        MachineConnectionLog log2 = new MachineConnectionLog();
+                                        log2.MachineCustomId = ctdata.MachineCustomId;
+                                        log2.MachineId = ctdata.MachineId;
+                                        log2.IpPort = ctdata.LocalEndPoint.ToString();
+                                        log2.IsIllegal = false;
+                                        log2.Data = msg;//call.id.ToString() + "," + call.action.ToString() + "," + JsonConvert.SerializeObject(result.Payload);
+                                        log2.IsDataOut = false;
+                                        log2.CmdNum = 0;
+                                        log2.CreatedOn = DateTime.Now;
+                                        log2.Msg = "first signin";
+                                        var ep2 = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                        log2.ClientIpPort = ep2.Address.ToString() + ":" + ep2.Port.ToString();
+
+                                        SaveMachineConnectionLog(log2);
+                                    }
+                                    //初始化client的CmdHelper instance
+                                    var machine = machineSrv.GetByCustomId(ctdata.MachineCustomIdTemp);
+                                    var customer = _customerService.GetByCustomerId(machine.CustomerId);
+                                    ctdata.CmdHelper = new CmdHelper(customer.MachineCode1, customer.MachineCode2);
+                                }
+                            }
+                            else
+                            {
+                                //更新充電樁的心跳包接收時間
+                                socketCommandSrv.UpdateHeartbeat(ctdata.MachineCustomId);
+
+                                //當 Heartbeat 與 上一個 Heartbeat相差值小於2秒,送出ChangeConfiguration.req(HeartbeatInterval: 15) 給電樁
+                                if ((IRequest)result.Payload is HeartbeatRequest)
+                                {
+
+                                    //var HeartbeatNow = db.MachineConnectionLog.Where(c => c.MachineCustomId == ctdata.MachineCustomId).OrderByDescending(c => c.Id).AsNoTracking().FirstOrDefault();
+                                    //if (HeartbeatNow.Data.Contains("currentTime") == true)
+                                    if (ctdata.heartbeatDate != null)
+                                    {
+                                        DateTime currenttime = DateTime.Now;
+                                        //DateTime basetime = HeartbeatNow.CreatedOn;
+                                        DateTime basetime = (DateTime)ctdata.heartbeatDate;
+                                        TimeSpan ts = currenttime.Subtract(basetime).Duration();
+                                        double seconddiff = ts.TotalSeconds;
+                                        if ((seconddiff <= 2) && (ctdata.IsSendHeartbeatChangeConfiguration == false)) // Heartbeat 與上一個 Heartbeat時間小於 2秒
+                                        {
+                                            //當 Heartbeat 與 上一個 Heartbeat相差值小於2秒,判斷是否送出ChangeConfiguration.req(HeartbeatInterval: 15) 給電樁
+                                            ctdata.IsSendHeartbeatChangeConfiguration = true;
+
+                                            var machine = machineSrv.GetByCustomId(ctdata.MachineCustomId);
+                                            //重發校時
+                                            //設定心跳時間和心跳的timeout次數
+                                            logger.Trace("set 心跳時間和心跳的timeout次數");
+                                            Cmd1001 cmd1001temp = ctdata.CmdHelper.Create(1001) as Cmd1001;
+                                            cmd1001temp.Type = 1;
+                                            cmd1001temp.ParamIndex = 21;
+                                            cmd1001temp.ParamAmt = 2;
+                                            cmd1001temp.ParamByteLength = (ushort)((ushort)cmd1001temp.ParamAmt * 4);
+
+                                            uint p21 = (uint)machine.HeartbeatInterval;
+                                            cmd1001temp.ParamByteList.AddRange(CmdUtil.ToByteArray(p21).ToList());
+                                            uint p22 = (uint)machine.HeartbeatTimeoutTimes;
+                                            cmd1001temp.ParamByteList.AddRange(CmdUtil.ToByteArray(p22).ToList());
+
+                                            cmd1001temp.Pack();
+                                            ServerCommand sc = new ServerCommand();
+                                            sc.OutCmdNum = cmd1001temp.Cmd;
+                                            sc.CreatedOn = DateTime.Now;
+                                            sc.MachineId = ctdata.MachineId;
+                                            sc.MachineCustomId = ctdata.MachineCustomId;
+                                            sc.OutTransData = cmd1001temp.byteList.ToArray();
+                                            db.ServerCommand.Add(sc);
+                                            db.SaveChanges();
+                                        }
+                                    }
+                                }
+                                if (ENABLELOG_MACHINECMD)
+                                {
+                                    //紀錄電樁的連線資訊
+                                    MachineConnectionLog log = new MachineConnectionLog();
+                                    log.MachineCustomId = ctdata.MachineCustomId;
+                                    log.MachineId = ctdata.MachineId;
+                                    log.IpPort = ctdata.LocalEndPoint.ToString();
+                                    log.IsIllegal = false;
+                                    log.IsDataOut = false;
+                                    log.CmdNum = 0;
+                                    if (msg.Length > 2000)
+                                        log.Data = msg.Substring(0, 1999);
+                                    else
+                                        log.Data = msg;//call.id.ToString() + "," +call.action.ToString() + "," + JsonConvert.SerializeObject(result.Payload);
+                                    log.CreatedOn = DateTime.Now;
+                                    if (call.action.Contains("DataTransfer"))
+                                    {
+                                        log.Msg = call.action + " Request(" + ((DataTransferRequest)(result.Payload)).messageId + ")";
+                                    }
+                                    else
+                                        log.Msg = call.action + " Request";
+
+                                    var ep = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                    log.ClientIpPort = ep.Address.ToString() + ":" + ep.Port.ToString();
+
+                                    SaveMachineConnectionLog(log);
+
+                                    if (msg.Length >= 2000) // BMS封包
+                                    {
+                                        MachineConnectionLog log1 = new MachineConnectionLog();
+                                        log1.MachineCustomId = ctdata.MachineCustomId;
+                                        log1.MachineId = ctdata.MachineId;
+                                        log1.IpPort = ctdata.LocalEndPoint.ToString();
+                                        log1.IsIllegal = false;
+                                        log1.IsDataOut = false;
+                                        log1.CmdNum = 0;
+                                        log1.Data = "ChargingGunBMS :" + msg.Substring(2000, msg.Length - 2000);
+                                        log1.CreatedOn = DateTime.Now;
+                                        if (call.action.Contains("DataTransfer"))
+                                        {
+                                            log.Msg = call.action + " Request(" + ((DataTransferRequest)(result.Payload)).messageId + ")";
+                                        }
+                                        else
+                                            log.Msg = call.action + " Request";
+                                        var ep1 = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                        log1.ClientIpPort = ep1.Address.ToString() + ":" + ep1.Port.ToString();
+
+                                        SaveMachineConnectionLog(log1);
+                                    }
+                              
+                                }
+
+                               
+                            }
+
+                            //將OCPP Call Message的Payload所夾帶參數資訊進行處理
+                            var handleRequestResult = ocppMsgHandler.HandleRequest((IRequest)result.Payload, call.id, db, ref ctdata);
+
+                            if (handleRequestResult.Success == true)
+                            {
+                                //針對即將回傳給電樁的Confirm Message進行處理
+                                IConfirmation conf = (IConfirmation)handleRequestResult.Payload;
+
+                                if (!ctdata.IsCheckIn && (conf is BootNotificationConfirmation))
+                                {
+                                    //後台接受電樁發送的BootNotification Request
+                                    if (((BootNotificationConfirmation)conf).status == RegistrationStatus.Accepted)
+                                    {
+                                        //不存在機器,要紀錄起來
+                                        var machine = machineSrv.GetByCustomId(ctdata.MachineCustomIdTemp);
+
+                                        ctdata.CustomerId = machine.CustomerId;
+                                        ctdata.CustomerName = machine.Customer.Name;
+                                        ctdata.MachineId = machine.Id;
+                                        string temp = String.Empty;
+                                        temp = ctdata.MachineCustomId;
+                                        ctdata.MachineCustomId = ctdata.MachineCustomIdTemp;
+                                        ctdata.MachineCustomIdTemp = temp;
+                                        OCPP_ClientDicAdded(ctdata);
+
+                                        //確認是否已經有新增槍數
+                                        machineSrv.CheckGunsByMachineCustomId(ctdata.MachineCustomId);
+                                        uow.SaveChanges();
+
+                                        //設定心跳時間和心跳的timeout次數
+                                        logger.Trace("set 心跳時間和心跳的timeout次數");
+                                        Cmd1001 cmd1001 = ctdata.CmdHelper.Create(1001) as Cmd1001;
+                                        cmd1001.Type = 1;
+                                        cmd1001.ParamIndex = 21;
+                                        cmd1001.ParamAmt = 2;
+                                        cmd1001.ParamByteLength = (ushort)((ushort)cmd1001.ParamAmt * 4);
+
+                                        uint p21 = (uint)machine.HeartbeatInterval;
+                                        cmd1001.ParamByteList.AddRange(CmdUtil.ToByteArray(p21).ToList());
+                                        uint p22 = (uint)machine.HeartbeatTimeoutTimes;
+                                        cmd1001.ParamByteList.AddRange(CmdUtil.ToByteArray(p22).ToList());
+
+                                        cmd1001.Pack();
+                                        ServerCommand sc = new ServerCommand();
+                                        sc.OutCmdNum = cmd1001.Cmd;
+                                        sc.CreatedOn = DateTime.Now;
+                                        sc.MachineId = ctdata.MachineId;
+                                        sc.MachineCustomId = ctdata.MachineCustomId;
+                                        sc.OutTransData = cmd1001.byteList.ToArray();
+                                        db.ServerCommand.Add(sc);
+                                        db.SaveChanges();
+
+                                        //設定樁的夥伴代碼
+                                        Cmd1003 cmd1003 = ctdata.CmdHelper.Create(1003) as Cmd1003;
+                                        cmd1003.ParamByteLength = 36;
+                                        cmd1003.Type = 1;
+                                        cmd1003.ParamIndex = 12;
+                                        cmd1003.ParamByteList.AddRange(CmdUtil.ToByteArray(machine.PartnerPoleId, 36));
+                                        cmd1003.Pack();
+
+                                        ServerCommand sc1003 = new ServerCommand();
+                                        sc1003.OutCmdNum = cmd1003.Cmd;
+                                        sc1003.CreatedOn = DateTime.Now;
+                                        sc1003.MachineId = ctdata.MachineId;
+                                        sc1003.MachineCustomId = ctdata.MachineCustomId;
+                                        sc1003.OutTransData = cmd1003.byteList.ToArray();
+                                        db.ServerCommand.Add(sc1003);
+                                        db.SaveChanges();
+
+                                        SendElectricityRates(machine, ctdata);
+
+                                        //設定GetConfiguration
+                                        Cmd9029 cmd9029 = ctdata.CmdHelper.Create(9029) as Cmd9029;
+                                        cmd9029.SerNum = 1;
+                                        cmd9029.CmdSerNum = 1;
+                                        cmd9029.ParamIndex = 1;
+                                        //cmd1005.ParamAmt = 2;
+                                        cmd9029.ParamByteLength = (ushort)4;
+                                        uint p2 = 0x55;
+                                        cmd9029.ParamByteList.AddRange(CmdUtil.ToByteArray(p2));
+                                        //string type = "GetConfiguration";
+                                        //cmd1005.ParamByteList.AddRange(CmdUtil.ToByteArray(type));
+                                        cmd9029.Pack();
+
+                                        ServerCommand sc9029 = new ServerCommand();
+                                        sc9029.OutCmdNum = cmd9029.Cmd;
+                                        sc9029.CreatedOn = DateTime.Now;
+                                        sc9029.MachineId = machine.Id;
+                                        sc9029.MachineCustomId = machine.CustomId;
+                                        sc9029.OutTransData = cmd9029.byteList.ToArray();
+                                        db.ServerCommand.Add(sc9029);
+                                        db.SaveChanges();
+
+
+                                        //后台服务器下发查询目前验证卡号名单版号
+                                        var localauthTable = db.LocalAuthorization.Where(l => l.CustomId == ctdata.MachineCustomId).FirstOrDefault();
+
+                                        if (localauthTable != null)    //查詢電樁是否已建立 Local Authorization Table
+                                        {
+                                            //下發GetLocalListVersion.req
+                                            logger.Trace("set CMD1011 to ServerCommand");
+                                            Cmd1011 cmd1011 = ctdata.CmdHelper.Create(1011) as Cmd1011;
+                                            cmd1011.CmdSerNum = ctdata.GetCmdSerNum();
+                                            cmd1011.SerNum = ctdata.GetSerNum();
+                                            cmd1011.Pack();
+                                            ServerCommand sc1011 = new ServerCommand();
+                                            sc1011.OutCmdNum = cmd1011.Cmd;
+                                            sc1011.CreatedOn = DateTime.Now;
+                                            sc1011.MachineId = ctdata.MachineId;
+                                            sc1011.MachineCustomId = ctdata.MachineCustomId;
+                                            sc1011.OutTransData = cmd1011.byteList.ToArray();
+                                            db.ServerCommand.Add(sc1011);
+                                            db.SaveChanges();
+                                        }
+                                    }
+                                    else if (((BootNotificationConfirmation)conf).status == RegistrationStatus.Pending)
+                                    {
+                                        BootNotificationPending = true;
+                                    }
+                                }
+
+                                //回傳Confirm Message給電樁
+                                string confirmType = string.Empty;
+                                if (call.action.Contains("DataTransfer"))
+                                {
+                                    confirmType = call.action + " Confirm(" + ((DataTransferRequest)(result.Payload)).messageId + ")";
+                                }
+                                else
+                                    confirmType = call.action + " Confirm";
+                                Send(ctdata, ocppMsgHandler.GenerateCallResult(call.id, conf), confirmType);
+
+                                //若BootNotification的status為Pending,後台送出 GetConfiguration Request 給電樁
+                                if (BootNotificationPending == true)
+                                {
+                                    IRequest req = MakeRequestHandler.MakeGetConfigurationRequest();
+                                    string uuid = ctdata.queue.store(req);
+                                    Send(ctdata, ocppMsgHandler.GenerateCall(uuid, Actions.GetConfiguration.ToString(), req), "GetConfiguration Request");
+                                    BootNotificationPending = false;
+                                }
+
+                                if (handleRequestResult.Message != null)
+                                {
+                                    //送 UpdateFirmware封包給電樁
+                                    if (handleRequestResult.Message.Contains("UpdateFirmware"))
+                                    {
+                                        //DataTransfer的下發升級指令
+                                        Send(ctdata, handleRequestResult.Message, "UpdateFirmware Request");
+                                    }
+
+                                    //在回覆StartTransaction.conf後,下發SetChargingProfile.req
+                                    if (handleRequestResult.Message.Contains("SetChargingProfile"))
+                                    {
+                                        //下發SetChargingProfile.req
+                                        Send(ctdata, handleRequestResult.Message, "SetChargingProfile Request");
+                                    }
+
+                                    //下發 APP 餘額/卡片餘額
+                                    if (handleRequestResult.Message.Contains("AccountBalance"))
+                                    {
+                                        //判斷是否為會員
+                                        if (ocppMsgHandler.IsMemberCharging((IRequest)result.Payload, db) == false)
+                                        {
+                                            var machine = machineSrv.GetByCustomId(ctdata.MachineCustomId);
+                                            SendElectricityRates(machine, ctdata);
+                                        }
+
+                                        //下發AccountBalance.req
+                                        Send(ctdata, handleRequestResult.Message, "AccountBalance Request");
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                if (ENABLELOG_MACHINECMD)
+                                {
+                                    //紀錄error type message
+                                    MachineConnectionLog log = new MachineConnectionLog();
+                                    log.MachineCustomId = ctdata.MachineCustomId;
+                                    log.MachineId = ctdata.MachineId;
+                                    log.IpPort = ctdata.LocalEndPoint.ToString();
+                                    log.IsIllegal = true;
+                                    log.IsDataOut = false;
+                                    log.CmdNum = 0;
+                                    if (msg.Length >= 2000)
+                                        log.Data = msg.Substring(0, 1999);
+                                    else
+                                        log.Data = msg;
+                                    log.CreatedOn = DateTime.Now;
+                                    log.Msg = handleRequestResult.Message;
+                                    var ep = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                    log.ClientIpPort = ep.Address.ToString() + ":" + ep.Port.ToString();
+
+                                    SaveMachineConnectionLog(log);
+
+                                    if (msg.Length >= 2000) // BMS錯誤封包
+                                    {
+                                        MachineConnectionLog log1 = new MachineConnectionLog();
+                                        log1.MachineCustomId = ctdata.MachineCustomId;
+                                        log1.MachineId = ctdata.MachineId;
+                                        log1.IpPort = ctdata.LocalEndPoint.ToString();
+                                        log1.IsIllegal = true;
+                                        log1.IsDataOut = false;
+                                        log1.CmdNum = 0;
+                                        log1.Data = "ChargingGunBMS :" + msg.Substring(2000, msg.Length - 2000);
+                                        log1.CreatedOn = DateTime.Now;
+                                        log1.Msg = handleRequestResult.Message;
+                                        var ep1 = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                        log1.ClientIpPort = ep1.Address.ToString() + ":" + ep1.Port.ToString();
+
+                                        SaveMachineConnectionLog(log1);
+                                    }
+                                }
+                                //送出錯誤訊息給電樁
+
+                                Send(ctdata, ocppMsgHandler.GenerateCallError(call.id, "NotImplemented", handleRequestResult.Message), "NotImplemented");
+                            }
+                        }
+                        else
+                        {
+                            #region error type 處理
+
+                            if (ENABLELOG_MACHINECMD)
+                            {
+                                //紀錄error type message
+                                MachineConnectionLog log = new MachineConnectionLog();
+                                log.MachineCustomId = ctdata.MachineCustomId;
+                                log.MachineId = ctdata.MachineId;
+                                log.IpPort = ctdata.LocalEndPoint.ToString();
+                                log.IsIllegal = true;
+                                log.IsDataOut = false;
+                                log.CmdNum = 0;
+                                log.Data = msg;
+                                log.CreatedOn = DateTime.Now;
+
+                                //紀錄exception, 送出錯誤訊息給電樁
+                                if (result.Exception != null)
+                                {
+                                    logger.Error(result.Exception, "OnCall error");
+                                    log.Msg = result.Exception.ToString();
+                                }
+                                else
+                                {
+                                    Send(ctdata, result.Message, "Error Message");
+                                    log.Msg = result.Message;
+                                }
+
+                                var ep = ((IPEndPoint)ctdata.RemoteEndPoint);
+                                log.ClientIpPort = ep.Address.ToString() + ":" + ep.Port.ToString();
+
+                                SaveMachineConnectionLog(log);
+                            }
+
+                            #endregion error type 處理
+                        }
+
+                        #endregion 處理CallMessage
+                    }
+                }
+                catch (SocketException e)
+                {
+                    logger.Trace("disconnected:{0}", ctdata.MachineCustomId);
+                    RemoveClient(ctdata);
+                    return;
+                }
+                catch (ObjectDisposedException e)
+                {
+                    logger.Trace("ObjectDisposedException:{0}", ctdata.MachineCustomId);
+                    //RemoveClientDic(client);
+                    return;
+                }
+                catch (DbEntityValidationException e)
+                {
+                    StringBuilder sb = new StringBuilder();
+                    foreach (var eve in e.EntityValidationErrors)
+                    {
+                        sb.AppendLine(string.Format("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
+                                                        eve.Entry.Entity.GetType().Name,
+                                                        eve.Entry.State));
+                        foreach (var ve in eve.ValidationErrors)
+                        {
+                            sb.AppendLine(string.Format("- Property: \"{0}\", Error: \"{1}\"",
+                                                        ve.PropertyName,
+                                                        ve.ErrorMessage));
+                        }
+                    }
+                    logger.Warn(e);
+                }
+                catch (Exception e)
+                {
+                    logger.Warn(e);
+                }
+                finally
+                {
+                    uow.Dispose();
+                }
+            }
+        }
+
+        /// <summary>
+        /// 增加電樁到ClientDic
+        /// </summary>
+        private void OCPP_ClientDicAdded(ClientData ctdata)
+        {
+            //刪除重複的機器
+            if (ClientDic.ContainsKey(ctdata.MachineCustomId))
+            {
+                //刪除重複機器
+                var dupClient = ClientDic[ctdata.MachineCustomId];
+                RemoveClient(dupClient);
+            }
+
+            //刪掉剛連線的紀錄
+            lock (_lockClientDic)
+            {
+                if (ClientDic.ContainsKey(ctdata.MachineCustomIdTemp))
+                {
+                    ClientDic.Remove(ctdata.MachineCustomIdTemp);
+                    logger.Trace("delete connect record {0}", ctdata.MachineCustomId);
+                }
+
+                var oldMachineCustomId = ctdata.MachineCustomId;
+                ctdata.MachineCustomIdTemp = ctdata.MachineCustomId;
+                ctdata.IsCheckIn = true;
+
+                logger.Trace("connect id {0} check in id {1}", oldMachineCustomId, ctdata.MachineCustomId);
+                ClientDic.Add(ctdata.MachineCustomId, ctdata);
+            }
+        }
+
+        /// <summary>
+        /// 從ClientDic取出電樁
+        /// </summary>
+        private void OCPP_ClientDataRemoved(ClientData ctdata)
+        {
+            RemoveClient(ctdata);
+        }
+
+        private void AppServer_SessionClosed(ClientData session, CloseReason reason)
+        {
+            logger.Trace("Enter AppServer_SessionClosed()");
+            lock (_lockClientDic)
+            {
+                ClientData client;
+
+                if (!String.IsNullOrEmpty(session.Path))
+                {
+                    string[] words = session.Path.Split('/');
+
+                    if (ClientDic.ContainsKey(words.Last()))
+                    {
+                        if (ClientDic.TryGetValue(words.Last(), out client))
+                        {
+                            ClientDic.Remove(words.Last());
+                            logger.Trace("RemoveClient ContainsKey" + words.Last());
+                            client = null;
+                        }
+                    }
+                    else
+                    {
+                        if((session.MachineCustomId != null) && (session.MachineCustomIdTemp != null))
+                        {
+                            if (ClientDic.TryGetValue(session.MachineCustomId, out client))
+                            {
+                                ClientDic.Remove(session.MachineCustomId);
+                                logger.Trace("RemoveClient ContainsKey" + words.Last());
+                                client = null;
+                            }
+                            else if (ClientDic.TryGetValue(session.MachineCustomIdTemp, out client))
+                            {
+                                ClientDic.Remove(session.MachineCustomIdTemp);
+                                logger.Trace("RemoveClient ContainsKey" + words.Last());
+                                client = null;
+                            }
+                        }
+
+                    }
+                }
+            }
+        }
+
+        private void AppServer_NewSessionConnected(ClientData client)
+        {
+            try
+            {
+                client.m_ReceiveData += new ClientData.OCPPClientDataEventHandler<ClientData, String>(ReceivedMessage);
+                lock (_lockClientDic)
+                {
+                    ClientDic.Add(client.MachineCustomId, client);
+                }
+
+                logger.Trace("client: " + client.MachineCustomId + " in");
+                using (var db = new PhihongDbContext())
+                {
+                    if (ENABLELOG_MACHINECMD)
+                    {
+                        // 紀錄電樁的新連線資訊
+                        MachineConnectionLog log = new MachineConnectionLog();
+                        log.MachineCustomId = client.MachineCustomId;
+                        log.MachineId = client.MachineId;
+                        log.IpPort = client.LocalEndPoint.ToString();
+                        log.IsIllegal = true;
+                        log.IsDataOut = false;
+                        log.CmdNum = 0;
+                        log.CreatedOn = DateTime.Now;
+                        var ep = ((IPEndPoint)client.RemoteEndPoint);
+                        log.ClientIpPort = ep.ToString();
+                        log.Msg = "connect";
+
+                        SaveMachineConnectionLog(log);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Warn(ex);
+                RemoveClient(client);
+            }
+        }
+
+        public void Stop()
+        {
+            _cts.Cancel();
+
+            var list = ClientDic.Select(c => c.Value).ToList();
+            foreach (var c in list)
+            {
+                RemoveClient(c);
+            }
+        }
+
+        public void RemoveClient(ClientData client)
+        {
+            logger.Trace("RemoveClient" + client.MachineCustomId);
+            if (client != null)
+            {
+                try
+                {
+                    client.Close(CloseReason.ServerClosing);
+                }
+                catch (Exception ex)
+                {
+                    //logger.Warn("Close client socket error!!");
+                    logger.Warn(string.Format("Close client socket error!! {0} Msg:{1}", client.MachineCustomId, ex.Message));
+                }
+                client.m_ReceiveData -= new ClientData.OCPPClientDataEventHandler<ClientData, String>(ReceivedMessage);
+                client.Close();
+                // logger.Trace("Close client socket!!");
+                logger.Trace("Close client socket!!" + client.MachineCustomId);
+                RemoveClientDic(client);
+
+                logger.Trace("RemoveClient null" + client.MachineCustomId);
+                if (client != null)
+                {
+                    client = null;
+                }
+            }
+        }
+
+        public void RemoveClientDic(ClientData client)
+        {
+            lock (_lockClientDic)
+            {
+                if (ClientDic.ContainsKey(client.MachineCustomId))
+                {
+                    ClientDic.Remove(client.MachineCustomId);
+                    logger.Trace("RemoveClient ContainsKey" + client.MachineCustomId);
+                }
+            }
+        }
+
+        public void ServerCommandThread()
+        {
+            for (; ; )
+            {
+                if (_ct.IsCancellationRequested)
+                {
+                    break;
+                }
+
+                logger.Trace("command loop");
+                logger.Trace("ClientDic:" + ClientDic.Count().ToString());
+                logger.Trace("NeedConfirmPacketList:" + NeedConfirmPacketList.Count().ToString());
+
+                try
+                {
+                    var redirect = ConfigurationManager.AppSettings.Get("Redirect");
+                    if (redirect.Equals("1"))
+                    {
+                        System.Threading.Thread.Sleep(1000);
+                        continue;
+                    }
+
+                    using (var db = new PhihongDbContext())
+                    {
+                        ProcessServerCommand(db);
+
+                        ProcessConfirmPacket();
+
+                        System.Threading.Thread.Sleep(1000);
+
+                        //每小時發送校時
+                        var ts = DateTime.Now - sendTiming;
+                        if (ts.TotalHours > 1)
+                        {
+                            Timing(db);
+                            sendTiming = DateTime.Now;
+                        }
+
+                        //每5分鐘檢查
+                        ts = DateTime.Now - checkUpdateDt;
+                        if (ts.TotalMinutes > 5)
+                        {
+                            logger.Trace("check sw update");
+                            CheckSW(db);
+                            logger.Trace("check fw update");
+                            CheckFW(db);
+                            checkUpdateDt = DateTime.Now;
+                        }
+                    }
+                }
+                catch (Exception ex)
+                {
+                    logger.Error("server stop");
+                    logger.Error(ex);
+                }
+            }
+        }
+
+        private void ProcessServerCommand(PhihongDbContext db)
+        {
+            OCPPMessageHandler ocppMsgHandler = new OCPPMessageHandler();
+            string respone = String.Empty;
+            string messageType = string.Empty;
+            var machineList = ClientDic.Select(c => c.Key).ToList();
+
+            DateTime startDt = DateTime.Now.AddSeconds(-30);
+
+            var commandList = db.ServerCommand.Where(c => !c.UpdatedOn.HasValue && c.CreatedOn >= startDt && c.CreatedOn <= DateTime.Now).ToList().Where(c => machineList.Contains(c.MachineCustomId)).ToList();
+            //處理主機傳送的有指令
+            var cmdMachineList = commandList.Select(c => c.MachineCustomId).Distinct().ToList();
+            foreach (var cmdMachine in cmdMachineList)
+            {
+                ClientData client;
+                if (ClientDic.TryGetValue(cmdMachine, out client))
+                {
+                    //有簽到或測試指令模式
+                    if (client.IsCheckIn)
+                    {
+                        var cmdList = commandList.Where(c => c.MachineCustomId == cmdMachine && c.OutTransData != null).ToList();
+
+                        foreach (var item in cmdList)
+                        {
+                            BaseCmd c = client.CmdHelper.UpPack(item.OutTransData.ToList());
+                            int reservationId = 0;
+                            int chargingProfileId = 0;
+                            if (c is CmdError)
+                            {
+                                continue;
+                            }
+
+                            if (c is Cmd1005) //Remote結束充電
+                            {
+                                var cmd = c as Cmd1005;
+                                //Cancel預約reservationId
+                                if (cmd.ParamIndex == 10)
+                                {
+                                    //槍號=> reservationID, OCPP 槍號從1 開始
+                                    reservationId = cmd.GunSerNo + 1;
+                                }
+                            }
+                            else if (c is Cmd1007) //Remote啟動充電, 如果是發送充電指令的,送一次費率過去
+                            {
+                                var cmd = c as Cmd1007;
+
+                                IMachineService machineSrv = new MachineService();
+                                var machine = machineSrv.GetByCustomId(item.MachineCustomId);
+                                SendElectricityRates(machine, client);
+                                //產生定時充電/預約 reservationId
+                                if (cmd.StartChargingType == 1 || cmd.StartChargingType == 2)
+                                {
+                                    //槍號=> reservationID, OCPP 槍號從1 開始
+                                    reservationId = cmd.GunSerNo + 1;
+                                }
+
+                                client.CarType = Convert.ToInt32(cmd.VehicleType);
+                            }
+                            else if (c is Cmd9015) //ocpp command : SetChargingProfile
+                            {
+                                var cmd9015 = c as Cmd9015;
+                                chargingProfileId = client.GetChargingProfileId();
+                            }
+                            respone = ocppMsgHandler.HandleServerCommand(c, client.MachineCustomId, ref client.queue, ref client.configurationKey, reservationId, chargingProfileId, out messageType, db);
+
+                            item.OutTransData = CmdUtil.ToByteArray(respone, respone.Length);//System.Text.UTF8Encoding.Default.GetBytes(respone);//c.byteList.ToArray();
+                            item.UpdatedOn = DateTime.Now;
+                            db.SaveChanges();
+                            Send(client, respone, messageType);
+                            AddConfirmPacket(client, c, respone, item.Id, messageType);
+                            respone = String.Empty;
+                            Thread.Sleep(1);
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 送出指令
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="cmd"></param>
+        /// <returns></returns>
+        private bool Send(ClientData client, string ocppmessage, string messageType)
+        {
+            try
+            {
+                if (String.IsNullOrEmpty(ocppmessage) == true)
+                    return false;
+                using (var db = new PhihongDbContext())
+                {
+                    if (ENABLELOG_MACHINECMD)
+                    {
+                        MachineConnectionLog log = new MachineConnectionLog();
+                        log.MachineCustomId = client.MachineCustomId;
+                        log.MachineId = client.MachineId;
+                        log.IpPort = client.LocalEndPoint.ToString();
+
+                        if (ocppmessage.Length > 2000)
+                            log.Data = ocppmessage.Substring(0, 1999);
+                        else
+                            log.Data = ocppmessage;
+
+                        //log.Data = ocppmessage;
+                        log.IsDataOut = true;
+                        log.CmdNum = 0;
+                        log.CreatedOn = DateTime.Now;
+                        log.Msg = messageType;
+                        var ep = ((IPEndPoint)client.RemoteEndPoint);
+                        log.ClientIpPort = ep.Address.ToString() + ":" + ep.Port.ToString();
+
+                        SaveMachineConnectionLog(log);
+
+                        if (ocppmessage.Length >= 2000) // BMS封包
+                        {
+                            MachineConnectionLog log1 = new MachineConnectionLog();
+                            log1.MachineCustomId = client.MachineCustomId;
+                            log1.MachineId = client.MachineId;
+                            log1.IpPort = client.LocalEndPoint.ToString();
+                            log1.IsIllegal = false;
+                            log1.IsDataOut = false;
+                            log1.CmdNum = 0;
+                            log1.Data = "Key :" + ocppmessage.Substring(2000, ocppmessage.Length - 2000);
+                            log1.CreatedOn = DateTime.Now;
+                            log1.Msg = messageType;
+                            var ep1 = ((IPEndPoint)client.RemoteEndPoint);
+                            log1.ClientIpPort = ep1.Address.ToString() + ":" + ep1.Port.ToString();
+
+                            SaveMachineConnectionLog(log1);
+                        }
+
+                      
+                    }
+                }
+
+                client.Send(ocppmessage);
+            }
+            catch (Exception ex)
+            {
+                RemoveClient(client);
+                logger.Error(ex);
+                return false;
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// 判斷數據包的指令,是否需要確認
+        /// </summary>
+        /// <param name="cmd"></param>
+        public void AddConfirmPacket(ClientData client, BaseCmd cmd, string sentmessage, int id, string messageType)
+        {
+            if (String.IsNullOrEmpty(sentmessage) == true)
+                return;
+            NeedConfirmPacket p = new NeedConfirmPacket();
+            var array = JsonConvert.DeserializeObject<JArray>(sentmessage);
+            p.Id = id;
+            //p.CmdSerNum = cmd.CmdSerNum;
+            p.SentInterval = 10;
+            p.SentTimes = 3;
+            p.MachineCustomId = client.MachineCustomId;
+            p.SentMessage = sentmessage;
+            p.SentAction = messageType;
+            if (_needConfirmCmdList.Contains(cmd.Cmd))
+            {
+                p.SentUniqueId = array[1].ToString();
+
+                lock (_lockConfirmPacketList)
+                {
+                    NeedConfirmPacketList.Add(p);
+                    logger.Trace("Add NeedConfirmPacketList ");
+                }
+            }
+        }
+
+        private void Timing(PhihongDbContext db)
+        {
+            var list = ClientDic.Values.Where(c => c.IsCheckIn).ToList();
+
+            foreach (var item in list)
+            {
+                logger.Trace(item.MachineCustomId + ":sent commant timing");
+
+                Cmd1003 cmd3 = item.CmdHelper.Create(1003) as Cmd1003;
+
+                cmd3.ParamByteLength = 8;
+                cmd3.Type = 1;
+                cmd3.ParamIndex = 2;
+                cmd3.ParamByteList.AddRange(CmdUtil.ToByteArray(DateTime.Now));
+                cmd3.Pack();
+
+                ServerCommand sc2 = new ServerCommand();
+                sc2.OutCmdNum = cmd3.Cmd;
+                sc2.CreatedOn = DateTime.Now;
+                sc2.MachineId = item.MachineId;
+                sc2.MachineCustomId = item.MachineCustomId;
+                sc2.OutTransData = cmd3.byteList.ToArray();
+                db.ServerCommand.Add(sc2);
+                db.SaveChanges();
+            }
+        }
+
+        /// <summary>
+        /// 確認機器版本
+        /// </summary>
+        /// <param name="db"></param>
+        private void CheckSW(PhihongDbContext db)
+        {
+            OCPPMessageHandler ocppMsgHandler = new OCPPMessageHandler();
+            string respone = String.Empty;
+            if (ClientDic.Count() == 0)
+                return;
+
+            foreach (var item in ClientDic)
+            {
+                logger.Trace("check sw loop");
+                logger.Trace("check sw" + item.Value.MachineCustomId);
+                var machine = db.Machine.Where(c => c.Id == item.Value.MachineId).AsNoTracking().FirstOrDefault();
+
+                if (machine == null)
+                {
+                    logger.Error("check sw,can't find machine " + item.Value.MachineCustomId);
+                    continue;
+                }
+
+                if (machine.SW_AssignedMachineVersionId.HasValue)
+                {
+                    logger.Trace(machine.CustomId + "asinged sw value");
+
+                    var mv = db.MachineVersion.Include(c => c.PublishVersion)
+                        .Include(c => c.PublishVersion.PublishVersionFiles)
+                        .Include(c => c.PublishVersion.PublishVersionFiles.Select(z => z.UploadFile))
+                        .Where(c => c.Id == machine.SW_AssignedMachineVersionId.Value).First();
+
+                    logger.Trace(mv.Id + "get sw value");
+
+                    if (!machine.SW_VersionReport.HasValue || machine.SW_VersionReport.Value != mv.PublishVersion.Version)
+                    {
+                        logger.Trace(machine.CustomId + "ready to sent commmand to update sw ");
+                        UploadFile uploadFile;
+
+                        //ipc 就更新清單,非ipc就更新檔案
+                        if (mv.PublishVersion.UpdateJsonUploadFileId == null)
+                            uploadFile = mv.PublishVersion.PublishVersionFiles.First().UploadFile;
+                        else
+                            uploadFile = db.UploadFile.Find(mv.PublishVersion.UpdateJsonUploadFileId.Value);
+
+                        byte param = 0;
+
+                        //DataTransfer的下發升級指令
+                        respone = ocppMsgHandler.HandleUpdateFirmware(ref item.Value.queue, 1, (ProgramParam)param, uploadFile);
+                        Send(item.Value, respone, "UpdateFirmware Request");
+
+                        ////OCPP 的下發升級指令
+                        //respone = String.Empty;
+                        //string fileurl = @"http://" + uploadFile.FileUrl;
+                        //respone = ocppMsgHandler.HandleOCPPUpdateFirmware(ref item.Value.queue, fileurl);
+                        //Send(item.Value, respone);
+
+                        logger.Trace(item.Key + ":sent command to update sw");
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 確認機器版本
+        /// </summary>
+        /// <param name="db"></param>
+        private void CheckFW(PhihongDbContext db)
+        {
+            OCPPMessageHandler ocppMsgHandler = new OCPPMessageHandler();
+            string respone = String.Empty;
+            foreach (var item in ClientDic)
+            {
+                logger.Trace("check fw loop");
+                logger.Trace("check fw" + item.Value.MachineCustomId);
+                var machine = db.Machine.Where(c => c.Id == item.Value.MachineId).AsNoTracking().FirstOrDefault();
+
+                if (machine == null)
+                {
+                    logger.Error("check fw,can't find machine" + item.Value.MachineCustomId);
+                    continue;
+                }
+
+                if (machine.FW_AssignedMachineVersionId.HasValue)
+                {
+                    logger.Trace(machine.CustomId + "assigned fw update value");
+
+                    var mv = db.MachineVersion.Include(c => c.PublishVersion)
+                        .Include(c => c.PublishVersion.PublishVersionFiles)
+                        .Include(c => c.PublishVersion.PublishVersionFiles.Select(z => z.UploadFile))
+                        .Where(c => c.Id == machine.FW_AssignedMachineVersionId.Value).First();
+
+                    logger.Trace(mv.Id + "get fw update value");
+
+                    if (!machine.FW_VersionReport.HasValue || machine.FW_VersionReport.Value != mv.PublishVersion.Version)
+                    {
+                        logger.Trace(machine.CustomId + "ready to sent command to update fw");
+                        UploadFile uploadFile;
+
+                        List<UploadFile> ufList = new List<UploadFile>();
+
+                        //ipc 就更新清單,非ipc就更新檔案
+                        if (mv.PublishVersion.UpdateJsonUploadFileId == null)
+                        {
+                            ufList.AddRange(mv.PublishVersion.PublishVersionFiles.Select(c => c.UploadFile).ToList());
+                            //uploadFile = mv.PublishVersion.PublishVersionFiles.First().UploadFile;
+                        }
+                        else
+                        {
+                            uploadFile = db.UploadFile.Find(mv.PublishVersion.UpdateJsonUploadFileId.Value);
+                            ufList.Add(uploadFile);
+                        }
+
+                        foreach (var f in ufList)
+                        {
+                            byte param = 0;
+                            if (mv.PublishVersion.UpdateJsonUploadFileId == null)
+                            {
+                                param = (byte)f.FwTagId.Value;
+                            }
+
+                            if (param == 28) // HMI-B2
+                                continue;
+
+                            //DataTransfer的下發升級指令
+                            respone = ocppMsgHandler.HandleUpdateFirmware(ref item.Value.queue, 2, (ProgramParam)param, f);
+
+                            Send(item.Value, respone, "DataTransfer Request(UpdateFirmware)");
+
+                            ////OCPP 的下發升級指令
+                            //respone = String.Empty;
+                            //string fileurl = @"http://" + f.FileUrl;
+                            //respone = ocppMsgHandler.HandleOCPPUpdateFirmware(ref item.Value.queue, fileurl);
+                            //Send(item.Value, respone);
+
+                            logger.Trace(item.Key + ":send command to update fw");
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 處理需確認的數據包
+        /// </summary>
+        private void ProcessConfirmPacket()
+        {
+            List<NeedConfirmPacket> removeList = new List<NeedConfirmPacket>();
+            //處理需要重送的數據包
+            var count = NeedConfirmPacketList.Count;
+            for (int i = 0; i < count; i++)
+            {
+                var item = NeedConfirmPacketList[i];
+
+                //這個要移除,不處理
+                if (item.SentTimes <= 0 && item.SentInterval <= 0)
+                {
+                    removeList.Add(item);
+                    continue;
+                }
+
+                //要處理送出
+                if (--item.SentInterval <= 0)
+                {
+                    ClientData client;
+                    if (ClientDic.TryGetValue(item.MachineCustomId, out client))
+                    {
+                        //有簽到或測試指令模式
+                        if (client.IsCheckIn)
+                        {
+                            Send(client, item.SentMessage, item.SentAction);
+                        }
+                    }
+
+                    //如果已經到了次數限制,3,就不再送了
+                    if (--item.SentTimes <= 0)
+                    {
+                        item.SentTimes = 0;
+                        item.SentInterval = 0;
+                        removeList.Add(item);
+                    }
+                    else
+                    {
+                        //把間隔秒數set 3
+                        item.SentInterval = 10;
+                    }
+                }
+            }
+
+            //移除已經結束的數據包
+            lock (_lockConfirmPacketList)
+            {
+                foreach (var item in removeList)
+                {
+                    NeedConfirmPacketList.Remove(item);
+                    logger.Trace("delete needConfirmPacketList MachineCustomId:{0},UUID:{1}", item.MachineCustomId, item.SentUniqueId);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 送出電費及服務費率
+        /// </summary>
+        /// <param name="machine"></param>
+        /// <param name="client"></param>
+        private void SendElectricityRates(Machine machine, ClientData client)
+        {
+            using (var db = new PhihongDbContext())
+            {
+                if (machine.Customer.SinglePricePerKWH)
+                {
+                    //設定每度費用
+                    Cmd2301 cmd2301 = client.CmdHelper.Create(2301) as Cmd2301;
+                    cmd2301.CmdSerNum = client.GetCmdSerNum();
+                    cmd2301.SerNum = client.GetSerNum();
+                    cmd2301.PricePerKWH.RealValue = machine.PricePerKWH;
+                    cmd2301.Pack();
+
+                    ServerCommand sc1 = new ServerCommand();
+                    sc1.OutCmdNum = cmd2301.Cmd;
+                    sc1.CreatedOn = DateTime.Now;
+                    sc1.MachineId = client.MachineId;
+                    sc1.MachineCustomId = client.MachineCustomId;
+                    sc1.OutTransData = cmd2301.byteList.ToArray();
+                    db.ServerCommand.Add(sc1);
+                    db.SaveChanges();
+                }
+                else
+                {
+                    Cmd2303 cmd2303 = client.CmdHelper.Create(2303) as Cmd2303;
+                    cmd2303.CmdSerNum = client.GetCmdSerNum();
+                    cmd2303.SerNum = client.GetSerNum();
+                    cmd2303.Section0000_0030.RealValue = machine.Section0000_0030;
+                    cmd2303.Section0030_0100.RealValue = machine.Section0030_0100;
+                    cmd2303.Section0100_0130.RealValue = machine.Section0100_0130;
+                    cmd2303.Section0130_0200.RealValue = machine.Section0130_0200;
+                    cmd2303.Section0200_0230.RealValue = machine.Section0200_0230;
+                    cmd2303.Section0230_0300.RealValue = machine.Section0230_0300;
+                    cmd2303.Section0300_0330.RealValue = machine.Section0300_0330;
+                    cmd2303.Section0330_0400.RealValue = machine.Section0330_0400;
+                    cmd2303.Section0400_0430.RealValue = machine.Section0400_0430;
+                    cmd2303.Section0430_0500.RealValue = machine.Section0430_0500;
+                    cmd2303.Section0500_0530.RealValue = machine.Section0500_0530;
+                    cmd2303.Section0530_0600.RealValue = machine.Section0530_0600;
+                    cmd2303.Section0600_0630.RealValue = machine.Section0600_0630;
+                    cmd2303.Section0630_0700.RealValue = machine.Section0630_0700;
+                    cmd2303.Section0700_0730.RealValue = machine.Section0700_0730;
+                    cmd2303.Section0730_0800.RealValue = machine.Section0730_0800;
+                    cmd2303.Section0800_0830.RealValue = machine.Section0800_0830;
+                    cmd2303.Section0830_0900.RealValue = machine.Section0830_0900;
+                    cmd2303.Section0900_0930.RealValue = machine.Section0900_0930;
+                    cmd2303.Section0930_1000.RealValue = machine.Section0930_1000;
+                    cmd2303.Section1000_1030.RealValue = machine.Section1000_1030;
+                    cmd2303.Section1030_1100.RealValue = machine.Section1030_1100;
+                    cmd2303.Section1100_1130.RealValue = machine.Section1100_1130;
+                    cmd2303.Section1130_1200.RealValue = machine.Section1130_1200;
+                    cmd2303.Section1200_1230.RealValue = machine.Section1200_1230;
+                    cmd2303.Section1230_1300.RealValue = machine.Section1230_1300;
+                    cmd2303.Section1300_1330.RealValue = machine.Section1300_1330;
+                    cmd2303.Section1330_1400.RealValue = machine.Section1330_1400;
+                    cmd2303.Section1400_1430.RealValue = machine.Section1400_1430;
+                    cmd2303.Section1430_1500.RealValue = machine.Section1430_1500;
+                    cmd2303.Section1500_1530.RealValue = machine.Section1500_1530;
+                    cmd2303.Section1530_1600.RealValue = machine.Section1530_1600;
+                    cmd2303.Section1600_1630.RealValue = machine.Section1600_1630;
+                    cmd2303.Section1630_1700.RealValue = machine.Section1630_1700;
+                    cmd2303.Section1700_1730.RealValue = machine.Section1700_1730;
+                    cmd2303.Section1730_1800.RealValue = machine.Section1730_1800;
+                    cmd2303.Section1800_1830.RealValue = machine.Section1800_1830;
+                    cmd2303.Section1830_1900.RealValue = machine.Section1830_1900;
+                    cmd2303.Section1900_1930.RealValue = machine.Section1900_1930;
+                    cmd2303.Section1930_2000.RealValue = machine.Section1930_2000;
+                    cmd2303.Section2000_2030.RealValue = machine.Section2000_2030;
+                    cmd2303.Section2030_2100.RealValue = machine.Section2030_2100;
+                    cmd2303.Section2100_2130.RealValue = machine.Section2100_2130;
+                    cmd2303.Section2130_2200.RealValue = machine.Section2130_2200;
+                    cmd2303.Section2200_2230.RealValue = machine.Section2200_2230;
+                    cmd2303.Section2230_2300.RealValue = machine.Section2230_2300;
+                    cmd2303.Section2300_2330.RealValue = machine.Section2300_2330;
+                    cmd2303.Section2330_2400.RealValue = machine.Section2330_2400;
+                    cmd2303.Pack();
+
+                    ServerCommand sc2 = new ServerCommand();
+                    sc2.OutCmdNum = cmd2303.Cmd;
+                    sc2.CreatedOn = DateTime.Now;
+                    sc2.MachineId = client.MachineId;
+                    sc2.MachineCustomId = client.MachineCustomId;
+                    sc2.OutTransData = cmd2303.byteList.ToArray();
+                    db.ServerCommand.Add(sc2);
+                    db.SaveChanges();
+                }
+                //設定每度服務費
+                Cmd2305 cmd2305 = client.CmdHelper.Create(2305) as Cmd2305;
+                cmd2305.CmdSerNum = client.GetCmdSerNum();
+                cmd2305.SerNum = client.GetSerNum();
+                cmd2305.FeePerKWH.RealValue = machine.FeePerKWH;
+                cmd2305.Pack();
+
+                ServerCommand sc3 = new ServerCommand();
+                sc3.OutCmdNum = cmd2305.Cmd;
+                sc3.CreatedOn = DateTime.Now;
+                sc3.MachineId = client.MachineId;
+                sc3.MachineCustomId = client.MachineCustomId;
+                sc3.OutTransData = cmd2305.byteList.ToArray();
+                db.ServerCommand.Add(sc3);
+                db.SaveChanges();
+            }
+        }
+
+        private void CheckConfirmPacket(ClientData client, BaseMessage message)
+        {
+            lock (_lockConfirmPacketList)
+            {
+                logger.Trace("CheckConfirmPacket NeedConfirmPacketList:" + NeedConfirmPacketList.Count().ToString());
+                //檢查 Call Message/ CallResult Message / CallError Message的 UniqueId屬性, 是否與  NeedConfirmPacket的 SentUniqueId屬性 一致
+                var item = NeedConfirmPacketList.Where(c => c.MachineCustomId == client.MachineCustomId && c.SentUniqueId == message.id).FirstOrDefault();
+
+                if (item != null)
+                {
+                    logger.Trace("check CheckConfirmPacket MachineCustomId:{0},SentUniqueId:{1}", item.MachineCustomId, item.SentUniqueId);
+                    using (var db = new PhihongDbContext())
+                    {
+                        var sc = db.ServerCommand.Where(c => c.Id == item.Id).FirstOrDefault();
+                        object payload = null;
+
+                        if (sc.OutCmdNum == 1001)
+                        {
+                            // 當 Heartbeat 與 上一個 Heartbeat相差值小於2秒,判斷是否送出ChangeConfiguration.req(HeartbeatInterval: 15) 給電樁
+                            client.IsSendHeartbeatChangeConfiguration = false;
+                        }
+                        else if (sc.OutCmdNum == 9001)
+                        {
+                            sc.InCmdNum = 9002; /*9002為Cmd9002, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9003)
+                        {
+                            sc.InCmdNum = 9004; /*9004為Cmd9004, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9005)
+                        {
+                            sc.InCmdNum = 9006; /*9006為Cmd9006, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9007)
+                        {
+                            sc.InCmdNum = 9008; /*9008為Cmd9008, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9009)
+                        {
+                            sc.InCmdNum = 9010; /*9010為Cmd9010, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9011)
+                        {
+                            sc.InCmdNum = 9012; /*9012為Cmd9012, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9013)
+                        {
+                            sc.InCmdNum = 9014; /*9014為Cmd9014, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9015)
+                        {
+                            sc.InCmdNum = 9016; /*9016為Cmd9016, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9017)
+                        {
+                            sc.InCmdNum = 9018; /*9018為Cmd9018, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9019)
+                        {
+                            sc.InCmdNum = 9020; /*9020為Cmd9020, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9021)
+                        {
+                            sc.InCmdNum = 9022; /*9022為Cmd9022, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9023)
+                        {
+                            sc.InCmdNum = 9024; /*9024為Cmd9024, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9025)
+                        {
+                            sc.InCmdNum = 9026; /*9026為Cmd9026, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9027)
+                        {
+                            sc.InCmdNum = 9028; /*9028為Cmd9028, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9029)
+                        {
+                            sc.InCmdNum = 9030; /*9030為Cmd9030, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else if (sc.OutCmdNum == 9031)
+                        {
+                            sc.InCmdNum = 9032; /*9032為Cmd9032, 目前Packet Cmd沒有建立此Command*/
+                        }
+                        else
+                        {
+                            sc.InCmdNum = 0;
+                        }
+
+                        if (message is CallResultMessage)
+                        {
+                            CallResultMessage call = (CallResultMessage)message;
+                            payload = call.payload;
+                        }
+                        else if (message is CallErrorMessage)
+                        {
+                            CallErrorMessage call = (CallErrorMessage)message;
+                            payload = call.payload;
+                        }
+                        else if (message is CallMessage)
+                        {
+                            CallMessage call = (CallMessage)message;
+                            payload = call.payload;
+                        }
+
+                        //sc.InTransData = CmdUtil.ToByteArray(message.ToString(), message.ToString().Length);//System.Text.UTF8Encoding.Default.GetBytes(message.ToString());
+                        sc.InTransData = CmdUtil.ToByteArray(payload.ToString(), payload.ToString().Length);//System.Text.UTF8Encoding.Default.GetBytes(message.ToString());
+                        sc.ReceivedOn = DateTime.Now;
+                        db.SaveChanges();
+
+                        item.SentInterval = 0;
+                        item.SentTimes = 0;
+
+                        logger.Trace("reply NeedConfirmPacket MachineCustomId:{0},SentUniqueId:{1}", item.MachineCustomId, item.SentUniqueId);
+                    }
+                }
+            }
+        }
+
+        private void SaveMachineConnectionLog(MachineConnectionLog log)
+        {
+            if (System.Configuration.ConfigurationManager.AppSettings["mongoDb"] != null)
+            {
+
+                try
+                {
+                    MMachineConnectionLog mlog = new MMachineConnectionLog();
+                    mlog.MachineCustomId = log.MachineCustomId;
+                    mlog.MachineId = log.MachineId;
+                    mlog.IpPort = log.IpPort;
+                    mlog.IsIllegal = log.IsIllegal;
+                    mlog.IsDataOut = log.IsDataOut;
+                    mlog.CmdNum = log.CmdNum;
+                    mlog.CreatedOn = log.CreatedOn;                 
+                    mlog.ClientIpPort = log.ClientIpPort;
+                    mlog.Msg = log.Msg;
+                    mlog.Data = log.Data;
+                    IMongoDatabase database = mongoService.getMongoDatabase();
+                    var mdb = database.GetCollection<MMachineConnectionLog>(Globals._col_MachineConnectionLog);
+                    mdb.InsertOne(mlog);
+                }
+                catch (Exception e)
+                {
+                    logger.Trace("mongo MachineConnectionLog error:" + e.Message);
+
+                }
+            }
+            else
+            {
+                using (var db = new PhihongDbContext())
+                {
+                    db.MachineConnectionLog.Add(log);
+                    db.SaveChanges();
+                }
+                  
+            }
+        }
+    }
+}

+ 20 - 0
OCPPServer/packages.config

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="DnsClient" version="1.0.7" targetFramework="net461" />
+  <package id="EntityFramework" version="6.2.0" targetFramework="net461" />
+  <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net461" />
+  <package id="MongoDB.Bson" version="2.7.0" targetFramework="net461" />
+  <package id="MongoDB.Driver" version="2.7.0" targetFramework="net461" />
+  <package id="MongoDB.Driver.Core" version="2.7.0" targetFramework="net461" />
+  <package id="Newtonsoft.Json" version="10.0.2" targetFramework="net452" />
+  <package id="NLog" version="4.4.12" targetFramework="net461" />
+  <package id="NLog.Config" version="4.4.10" targetFramework="net461" />
+  <package id="NLog.Schema" version="4.4.10" targetFramework="net461" />
+  <package id="System.Buffers" version="4.3.0" targetFramework="net461" />
+  <package id="System.Net.Http" version="4.3.3" targetFramework="net461" />
+  <package id="System.Runtime.InteropServices.RuntimeInformation" version="4.0.0" targetFramework="net461" />
+  <package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" />
+  <package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
+  <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
+  <package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
+</packages>

+ 373 - 0
SocketBase/AppServer.cs

@@ -0,0 +1,373 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Command;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Protocol;
+using SuperSocket.SocketBase.Security;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// AppServer class
+    /// </summary>
+    public class AppServer : AppServer<AppSession>
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppServer"/> class.
+        /// </summary>
+        public AppServer()
+            : base()
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppServer"/> class.
+        /// </summary>
+        /// <param name="receiveFilterFactory">The Receive filter factory.</param>
+        public AppServer(IReceiveFilterFactory<StringRequestInfo> receiveFilterFactory)
+            : base(receiveFilterFactory)
+        {
+
+        }
+    }
+
+    /// <summary>
+    /// AppServer class
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    public class AppServer<TAppSession> : AppServer<TAppSession, StringRequestInfo>
+        where TAppSession : AppSession<TAppSession, StringRequestInfo>, IAppSession, new()
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppServer&lt;TAppSession&gt;"/> class.
+        /// </summary>
+        public AppServer()
+            : base()
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppServer&lt;TAppSession&gt;"/> class.
+        /// </summary>
+        /// <param name="receiveFilterFactory">The Receive filter factory.</param>
+        public AppServer(IReceiveFilterFactory<StringRequestInfo> receiveFilterFactory)
+            : base(receiveFilterFactory)
+        {
+
+        }
+
+        internal override IReceiveFilterFactory<StringRequestInfo> CreateDefaultReceiveFilterFactory()
+        {
+            return new CommandLineReceiveFilterFactory(TextEncoding);
+        }
+    }
+
+
+    /// <summary>
+    /// AppServer basic class
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    public abstract class AppServer<TAppSession, TRequestInfo> : AppServerBase<TAppSession, TRequestInfo>
+        where TRequestInfo : class, IRequestInfo
+        where TAppSession : AppSession<TAppSession, TRequestInfo>, IAppSession, new()
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppServer&lt;TAppSession, TRequestInfo&gt;"/> class.
+        /// </summary>
+        public AppServer()
+            : base()
+        {
+            
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppServer&lt;TAppSession, TRequestInfo&gt;"/> class.
+        /// </summary>
+        /// <param name="protocol">The protocol.</param>
+        protected AppServer(IReceiveFilterFactory<TRequestInfo> protocol)
+            : base(protocol)
+        {
+   
+        }
+
+        internal override IReceiveFilterFactory<TRequestInfo> CreateDefaultReceiveFilterFactory()
+        {
+            return null;
+        }
+
+        /// <summary>
+        /// Starts this AppServer instance.
+        /// </summary>
+        /// <returns></returns>
+        public override bool Start()
+        {
+            if (!base.Start())
+                return false;
+
+            if (!Config.DisableSessionSnapshot)
+                StartSessionSnapshotTimer();
+
+            if (Config.ClearIdleSession)
+                StartClearSessionTimer();
+
+            return true;
+        }
+
+        private ConcurrentDictionary<string, TAppSession> m_SessionDict = new ConcurrentDictionary<string, TAppSession>(StringComparer.OrdinalIgnoreCase);
+
+        /// <summary>
+        /// Registers the session into the session container.
+        /// </summary>
+        /// <param name="sessionID">The session ID.</param>
+        /// <param name="appSession">The app session.</param>
+        /// <returns></returns>
+        protected override bool RegisterSession(string sessionID, TAppSession appSession)
+        {
+            if (m_SessionDict.TryAdd(sessionID, appSession))
+                return true;
+
+            if (Logger.IsErrorEnabled)
+                Logger.Error(appSession, "The session is refused because the it's ID already exists!");
+
+            return false;
+        }
+
+        /// <summary>
+        /// Gets the app session by ID.
+        /// </summary>
+        /// <param name="sessionID">The session ID.</param>
+        /// <returns></returns>
+        [Obsolete("Use the method GetSessionByID instead")]
+        public TAppSession GetAppSessionByID(string sessionID)
+        {
+            return GetSessionByID(sessionID);
+        }
+
+        /// <summary>
+        /// Gets the app session by ID.
+        /// </summary>
+        /// <param name="sessionID">The session ID.</param>
+        /// <returns></returns>
+        public override TAppSession GetSessionByID(string sessionID)
+        {
+            if (string.IsNullOrEmpty(sessionID))
+                return NullAppSession;
+
+            TAppSession targetSession;
+            m_SessionDict.TryGetValue(sessionID, out targetSession);
+            return targetSession;
+        }
+
+        /// <summary>
+        /// Called when [socket session closed].
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="reason">The reason.</param>
+        protected override void OnSessionClosed(TAppSession session, CloseReason reason)
+        {
+            string sessionID = session.SessionID;
+
+            if (!string.IsNullOrEmpty(sessionID))
+            {
+                TAppSession removedSession;
+                if (!m_SessionDict.TryRemove(sessionID, out removedSession))
+                {
+                    if (Logger.IsErrorEnabled)
+                        Logger.Error(session, "Failed to remove this session, Because it has't been in session container!");
+                }
+            }
+
+            base.OnSessionClosed(session, reason);
+        }
+
+        /// <summary>
+        /// Gets the total session count.
+        /// </summary>
+        public override int SessionCount
+        {
+            get
+            {
+                return m_SessionDict.Count;
+            }
+        }
+
+        #region Clear idle sessions
+
+        private System.Threading.Timer m_ClearIdleSessionTimer = null;
+
+        private void StartClearSessionTimer()
+        {
+            int interval = Config.ClearIdleSessionInterval * 1000;//in milliseconds
+            m_ClearIdleSessionTimer = new System.Threading.Timer(ClearIdleSession, new object(), interval, interval);
+        }
+
+        /// <summary>
+        /// Clears the idle session.
+        /// </summary>
+        /// <param name="state">The state.</param>
+        private void ClearIdleSession(object state)
+        {
+            if (Monitor.TryEnter(state))
+            {
+                try
+                {
+                    var sessionSource = SessionSource;
+
+                    if (sessionSource == null)
+                        return;
+
+                    DateTime now = DateTime.Now;
+                    DateTime timeOut = now.AddSeconds(0 - Config.IdleSessionTimeOut);
+
+                    var timeOutSessions = sessionSource.Where(s => s.Value.LastActiveTime <= timeOut).Select(s => s.Value);
+
+                    System.Threading.Tasks.Parallel.ForEach(timeOutSessions, s =>
+                        {
+                            if (Logger.IsInfoEnabled)
+                                Logger.Info(s, string.Format("The session will be closed for {0} timeout, the session start time: {1}, last active time: {2}!", now.Subtract(s.LastActiveTime).TotalSeconds, s.StartTime, s.LastActiveTime));
+                            s.Close(CloseReason.TimeOut);
+                        });
+                }
+                catch (Exception e)
+                {
+                    if(Logger.IsErrorEnabled)
+                        Logger.Error("Clear idle session error!", e);
+                }
+                finally
+                {
+                    Monitor.Exit(state);
+                }
+            }
+        }
+
+        private KeyValuePair<string, TAppSession>[] SessionSource
+        {
+            get
+            {
+                if (Config.DisableSessionSnapshot)
+                    return m_SessionDict.ToArray();
+                else
+                    return m_SessionsSnapshot;
+            }
+        }
+
+        #endregion
+
+        #region Take session snapshot
+
+        private System.Threading.Timer m_SessionSnapshotTimer = null;
+
+        private KeyValuePair<string, TAppSession>[] m_SessionsSnapshot = new KeyValuePair<string, TAppSession>[0];
+
+        private void StartSessionSnapshotTimer()
+        {
+            int interval = Math.Max(Config.SessionSnapshotInterval, 1) * 1000;//in milliseconds
+            m_SessionSnapshotTimer = new System.Threading.Timer(TakeSessionSnapshot, new object(), interval, interval);
+        }
+
+        private void TakeSessionSnapshot(object state)
+        {
+            if (Monitor.TryEnter(state))
+            {
+                Interlocked.Exchange(ref m_SessionsSnapshot, m_SessionDict.ToArray());
+                Monitor.Exit(state);
+            }
+        }
+
+        #endregion
+
+        #region Search session utils
+
+        /// <summary>
+        /// Gets the matched sessions from sessions snapshot.
+        /// </summary>
+        /// <param name="critera">The prediction critera.</param>
+        /// <returns></returns>
+        public override IEnumerable<TAppSession> GetSessions(Func<TAppSession, bool> critera)
+        {
+            var sessionSource = SessionSource;
+
+            if (sessionSource == null)
+                return null;
+
+            return sessionSource.Select(p => p.Value).Where(critera);
+        }
+
+        /// <summary>
+        /// Gets all sessions in sessions snapshot.
+        /// </summary>
+        /// <returns></returns>
+        public override IEnumerable<TAppSession> GetAllSessions()
+        {
+            var sessionSource = SessionSource;
+
+            if (sessionSource == null)
+                return null;
+
+            return sessionSource.Select(p => p.Value);
+        }
+
+        /// <summary>
+        /// Stops this instance.
+        /// </summary>
+        public override void Stop()
+        {
+            base.Stop();
+
+            if (m_SessionSnapshotTimer != null)
+            {
+                m_SessionSnapshotTimer.Change(Timeout.Infinite, Timeout.Infinite);
+                m_SessionSnapshotTimer.Dispose();
+                m_SessionSnapshotTimer = null;
+            }
+
+            if (m_ClearIdleSessionTimer != null)
+            {
+                m_ClearIdleSessionTimer.Change(Timeout.Infinite, Timeout.Infinite);
+                m_ClearIdleSessionTimer.Dispose();
+                m_ClearIdleSessionTimer = null;
+            }
+
+            m_SessionsSnapshot = null;
+
+            var sessions = m_SessionDict.ToArray();
+
+            if (sessions.Length > 0)
+            {
+                var tasks = new Task[sessions.Length];
+
+                for (var i = 0; i < tasks.Length; i++)
+                {
+                    tasks[i] = Task.Factory.StartNew((s) =>
+                    {
+                        var session = s as TAppSession;
+
+                        if (session != null)
+                        {
+                            session.Close(CloseReason.ServerShutdown);
+                        }
+
+                    }, sessions[i].Value);
+                }
+
+                Task.WaitAll(tasks);
+            }
+        }
+
+        #endregion
+    }
+}

+ 159 - 0
SocketBase/AppServerBase.ConfigHotUpdate.cs

@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Protocol;
+
+namespace SuperSocket.SocketBase
+{
+    interface IConfigValueChangeNotifier
+    {
+        bool Notify(string newValue);
+    }
+
+    class ConfigValueChangeNotifier : IConfigValueChangeNotifier
+    {
+        Func<string, bool> m_Handler;
+
+        public ConfigValueChangeNotifier(Func<string, bool> handler)
+        {
+            m_Handler = handler;
+        }
+
+        public bool Notify(string newValue)
+        {
+            return m_Handler(newValue);
+        }
+    }
+    class ConfigValueChangeNotifier<TConfigOption> : IConfigValueChangeNotifier
+        where TConfigOption : ConfigurationElement, new()
+    {
+        Func<TConfigOption, bool> m_Handler;
+
+        public ConfigValueChangeNotifier(Func<TConfigOption, bool> handler)
+        {
+            m_Handler = handler;
+        }
+        public bool Notify(string newValue)
+        {
+            if (string.IsNullOrEmpty(newValue))
+                return m_Handler(default(TConfigOption));
+            else
+                return m_Handler(ConfigurationExtension.DeserializeChildConfig<TConfigOption>(newValue));
+        }
+    }
+
+    public abstract partial class AppServerBase<TAppSession, TRequestInfo>
+        where TRequestInfo : class, IRequestInfo
+        where TAppSession : AppSession<TAppSession, TRequestInfo>, IAppSession, new()
+    {
+        private Dictionary<string, IConfigValueChangeNotifier> m_ConfigUpdatedNotifiers = new Dictionary<string, IConfigValueChangeNotifier>(StringComparer.OrdinalIgnoreCase);
+
+        /// <summary>
+        /// Registers the configuration option value handler, it is used for reading configuration value and reload it after the configuration is changed;
+        /// </summary>
+        /// <typeparam name="TConfigOption">The type of the configuration option.</typeparam>
+        /// <param name="config">The server configuration.</param>
+        /// <param name="name">The changed config option's name.</param>
+        /// <param name="handler">The handler.</param>
+        protected bool RegisterConfigHandler<TConfigOption>(IServerConfig config, string name, Func<TConfigOption, bool> handler)
+            where TConfigOption : ConfigurationElement, new()
+        {
+            var notifier = new ConfigValueChangeNotifier<TConfigOption>(handler);
+            m_ConfigUpdatedNotifiers.Add(name, notifier);
+            return notifier.Notify(config.Options.GetValue(name));
+        }
+
+        /// <summary>
+        /// Registers the configuration option value handler, it is used for reading configuration value and reload it after the configuration is changed;
+        /// </summary>
+        /// <param name="config">The server configuration.</param>
+        /// <param name="name">The changed config option name.</param>
+        /// <param name="handler">The handler.</param>
+        protected bool RegisterConfigHandler(IServerConfig config, string name, Func<string, bool> handler)
+        {
+            var notifier = new ConfigValueChangeNotifier(handler);
+            m_ConfigUpdatedNotifiers.Add(name, notifier);
+            return notifier.Notify(config.OptionElements.GetValue(name));
+        }
+
+        int CheckConfigOptionsChange(NameValueCollection oldOptions, NameValueCollection newOptions)
+        {
+            var changed = 0;
+
+            if (oldOptions == null && newOptions == null)
+                return changed;
+
+            var oldOptionsDict = oldOptions == null
+                    ? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+                    : Enumerable.Range(0, oldOptions.Count)
+                        .Select(i => new KeyValuePair<string, string>(oldOptions.GetKey(i), oldOptions.Get(i)))
+                        .ToDictionary(p => p.Key, p => p.Value, StringComparer.OrdinalIgnoreCase);
+
+            foreach(var key in newOptions.AllKeys)
+            {
+                var newValue = newOptions[key];
+
+                var oldValue = string.Empty;
+
+                if (oldOptionsDict.TryGetValue(key, out oldValue))
+                    oldOptionsDict.Remove(key);
+
+                if (string.Compare(newValue, oldValue) == 0)
+                    continue;
+
+                NotifyConfigUpdated(key, newValue);
+                changed++;
+            }
+
+            if (oldOptionsDict.Count > 0)
+            {
+                foreach (var p in oldOptionsDict)
+                {
+                    NotifyConfigUpdated(p.Key, string.Empty);
+                    changed++;
+                }
+            }
+
+            return changed;
+        }
+
+        private void NotifyConfigUpdated(string key, string newValue)
+        {
+            IConfigValueChangeNotifier notifier;
+
+            if (!m_ConfigUpdatedNotifiers.TryGetValue(key, out notifier))
+                return;
+
+            try
+            {
+                if (!notifier.Notify(newValue))
+                    throw new Exception("returned false in the handling logic");
+            }
+            catch (Exception e)
+            {
+                Logger.Error("Failed to handle custom configuration reading, name: " + key, e);
+            }
+        }
+
+        void IWorkItemBase.ReportPotentialConfigChange(IServerConfig config)
+        {
+            var oldConfig = this.Config;
+
+            CheckConfigOptionsChange(oldConfig.Options, config.Options);
+            CheckConfigOptionsChange(oldConfig.OptionElements, config.OptionElements);
+
+            var updatableConfig = oldConfig as ServerConfig;
+
+            if (updatableConfig == null)
+                return;
+
+            config.CopyPropertiesTo(p => p.GetCustomAttributes(typeof(HotUpdateAttribute), true).Length > 0, updatableConfig);
+        }
+    }
+}

+ 51 - 0
SocketBase/AppServerBase.Net45.cs

@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Protocol;
+
+namespace SuperSocket.SocketBase
+{
+    public abstract partial class AppServerBase<TAppSession, TRequestInfo>
+        where TRequestInfo : class, IRequestInfo
+        where TAppSession : AppSession<TAppSession, TRequestInfo>, IAppSession, new()
+    {
+        partial void SetDefaultCulture(IRootConfig rootConfig, IServerConfig config)
+        {
+            var defaultCulture = config.DefaultCulture;
+            
+            //default culture has been set for this server instance
+            if (!string.IsNullOrEmpty(defaultCulture))
+            {
+                if (rootConfig.Isolation == IsolationMode.None)
+                {
+                    Logger.WarnFormat("The default culture '{0}' cannot be set, because you cannot set default culture for one server instance if the Isolation is None!");
+                    return;
+                }
+            }
+            else if(!string.IsNullOrEmpty(rootConfig.DefaultCulture))
+            {
+                defaultCulture = rootConfig.DefaultCulture;
+
+                //Needn't set default culture in this case, because it has been set in the bootstrap
+                if (rootConfig.Isolation == IsolationMode.None)
+                    return;
+            }
+
+            if (string.IsNullOrEmpty(defaultCulture))
+                return;
+
+            try
+            {
+                CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(defaultCulture);
+            }
+            catch (Exception e)
+            {
+                Logger.Error(string.Format("Failed to set default culture '{0}'.", defaultCulture), e);
+            }
+        }
+    }
+}

+ 1723 - 0
SocketBase/AppServerBase.cs

@@ -0,0 +1,1723 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Security;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Command;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Logging;
+using SuperSocket.SocketBase.Metadata;
+using SuperSocket.SocketBase.Protocol;
+using SuperSocket.SocketBase.Provider;
+using SuperSocket.SocketBase.Security;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// AppServer base class
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    [AppServerMetadataType(typeof(DefaultAppServerMetadata))]
+	public abstract partial class AppServerBase<TAppSession, TRequestInfo> : IAppServer<TAppSession, TRequestInfo>, IRawDataProcessor<TAppSession>, IRequestHandler<TRequestInfo>, ISocketServerAccessor, IStatusInfoSource, IRemoteCertificateValidator, IActiveConnector, ISystemEndPoint, IDisposable
+        where TRequestInfo : class, IRequestInfo
+        where TAppSession : AppSession<TAppSession, TRequestInfo>, IAppSession, new()
+    {
+        /// <summary>
+        /// Null appSession instance
+        /// </summary>
+        protected readonly TAppSession NullAppSession = default(TAppSession);
+
+        /// <summary>
+        /// Gets the server's config.
+        /// </summary>
+        public IServerConfig Config { get; private set; }
+
+        //Server instance name
+        private string m_Name;
+
+        /// <summary>
+        /// the current state's code
+        /// </summary>
+        private int m_StateCode = ServerStateConst.NotInitialized;
+
+        /// <summary>
+        /// Gets the current state of the work item.
+        /// </summary>
+        /// <value>
+        /// The state.
+        /// </value>
+        public ServerState State
+        {
+            get
+            {
+                return (ServerState)m_StateCode;
+            }
+        }
+
+        /// <summary>
+        /// Gets the certificate of current server.
+        /// </summary>
+        public X509Certificate Certificate { get; private set; }
+
+        /// <summary>
+        /// Gets or sets the receive filter factory.
+        /// </summary>
+        /// <value>
+        /// The receive filter factory.
+        /// </value>
+        public virtual IReceiveFilterFactory<TRequestInfo> ReceiveFilterFactory { get; protected set; }
+
+        /// <summary>
+        /// Gets the Receive filter factory.
+        /// </summary>
+        object IAppServer.ReceiveFilterFactory
+        {
+            get { return this.ReceiveFilterFactory; }
+        }
+
+        private List<ICommandLoader<ICommand<TAppSession, TRequestInfo>>> m_CommandLoaders = new List<ICommandLoader<ICommand<TAppSession, TRequestInfo>>>();
+
+        private Dictionary<string, CommandInfo<ICommand<TAppSession, TRequestInfo>>> m_CommandContainer;
+
+        private CommandFilterAttribute[] m_GlobalCommandFilters;
+
+        private ISocketServerFactory m_SocketServerFactory;
+
+        /// <summary>
+        /// Gets the basic transfer layer security protocol.
+        /// </summary>
+        public SslProtocols BasicSecurity { get; private set; }
+
+        /// <summary>
+        /// Gets the root config.
+        /// </summary>
+        protected IRootConfig RootConfig { get; private set; }
+
+        /// <summary>
+        /// Gets the logger assosiated with this object.
+        /// </summary>
+        public ILog Logger { get; private set; }
+
+        /// <summary>
+        /// Gets the bootstrap of this appServer instance.
+        /// </summary>
+        protected IBootstrap Bootstrap { get; private set; }
+
+        private static bool m_ThreadPoolConfigured = false;
+
+        private List<IConnectionFilter> m_ConnectionFilters;
+
+        private long m_TotalHandledRequests = 0;
+
+        /// <summary>
+        /// Gets the total handled requests number.
+        /// </summary>
+        protected long TotalHandledRequests
+        {
+            get { return m_TotalHandledRequests; }
+        }
+
+        private ListenerInfo[] m_Listeners;
+
+        /// <summary>
+        /// Gets or sets the listeners inforamtion.
+        /// </summary>
+        /// <value>
+        /// The listeners.
+        /// </value>
+        public ListenerInfo[] Listeners
+        {
+            get { return m_Listeners; }
+        }
+
+        /// <summary>
+        /// Gets the started time of this server instance.
+        /// </summary>
+        /// <value>
+        /// The started time.
+        /// </value>
+        public DateTime StartedTime { get; private set; }
+
+
+        /// <summary>
+        /// Gets or sets the log factory.
+        /// </summary>
+        /// <value>
+        /// The log factory.
+        /// </value>
+        public ILogFactory LogFactory { get; private set; }
+
+
+        /// <summary>
+        /// Gets the default text encoding.
+        /// </summary>
+        /// <value>
+        /// The text encoding.
+        /// </value>
+        public Encoding TextEncoding { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppServerBase&lt;TAppSession, TRequestInfo&gt;"/> class.
+        /// </summary>
+        public AppServerBase()
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppServerBase&lt;TAppSession, TRequestInfo&gt;"/> class.
+        /// </summary>
+        /// <param name="receiveFilterFactory">The Receive filter factory.</param>
+        public AppServerBase(IReceiveFilterFactory<TRequestInfo> receiveFilterFactory)
+        {
+            this.ReceiveFilterFactory = receiveFilterFactory;
+        }
+
+        /// <summary>
+        /// Gets the filter attributes.
+        /// </summary>
+        /// <param name="type">The type.</param>
+        /// <returns></returns>
+        internal static CommandFilterAttribute[] GetCommandFilterAttributes(Type type)
+        {
+            var attrs = type.GetCustomAttributes(true);
+            return attrs.OfType<CommandFilterAttribute>().ToArray();
+        }
+
+        /// <summary>
+        /// Setups the command into command dictionary
+        /// </summary>
+        /// <param name="discoveredCommands">The discovered commands.</param>
+        /// <returns></returns>
+        protected virtual bool SetupCommands(Dictionary<string, ICommand<TAppSession, TRequestInfo>> discoveredCommands)
+        {
+            foreach (var loader in m_CommandLoaders)
+            {
+                loader.Error += new EventHandler<ErrorEventArgs>(CommandLoaderOnError);
+                loader.Updated += new EventHandler<CommandUpdateEventArgs<ICommand<TAppSession, TRequestInfo>>>(CommandLoaderOnCommandsUpdated);
+
+                if (!loader.Initialize(RootConfig, this))
+                {
+                    if (Logger.IsErrorEnabled)
+                        Logger.ErrorFormat("Failed initialize the command loader {0}.", loader.ToString());
+                    return false;
+                }
+
+                IEnumerable<ICommand<TAppSession, TRequestInfo>> commands;
+                if (!loader.TryLoadCommands(out commands))
+                {
+                    if (Logger.IsErrorEnabled)
+                        Logger.ErrorFormat("Failed load commands from the command loader {0}.", loader.ToString());
+                    return false;
+                }
+
+                if (commands != null && commands.Any())
+                {
+                    foreach (var c in commands)
+                    {
+                        if (discoveredCommands.ContainsKey(c.Name))
+                        {
+                            if (Logger.IsErrorEnabled)
+                                Logger.Error("Duplicated name command has been found! Command name: " + c.Name);
+                            return false;
+                        }
+
+                        var castedCommand = c as ICommand<TAppSession, TRequestInfo>;
+
+                        if (castedCommand == null)
+                        {
+                            if (Logger.IsErrorEnabled)
+                                Logger.Error("Invalid command has been found! Command name: " + c.Name);
+                            return false;
+                        }
+
+                        if (Logger.IsDebugEnabled)
+                            Logger.DebugFormat("The command {0}({1}) has been discovered", castedCommand.Name, castedCommand.ToString());
+
+                        discoveredCommands.Add(c.Name, castedCommand);
+                    }
+                }
+            }
+
+            return true;
+        }
+
+        void CommandLoaderOnCommandsUpdated(object sender, CommandUpdateEventArgs<ICommand<TAppSession, TRequestInfo>> e)
+        {
+            var workingDict = m_CommandContainer.Values.ToDictionary(c => c.Command.Name, c => c.Command, StringComparer.OrdinalIgnoreCase);
+            var updatedCommands = 0;
+
+            foreach (var c in e.Commands)
+            {
+                if (c == null)
+                    continue;
+
+                if (c.UpdateAction == CommandUpdateAction.Remove)
+                {
+                    workingDict.Remove(c.Command.Name);
+                    if (Logger.IsInfoEnabled)
+                        Logger.InfoFormat("The command '{0}' has been removed from this server!", c.Command.Name);
+                }
+                else if (c.UpdateAction == CommandUpdateAction.Add)
+                {
+                    workingDict.Add(c.Command.Name, c.Command);
+                    if (Logger.IsInfoEnabled)
+                        Logger.InfoFormat("The command '{0}' has been added into this server!", c.Command.Name);
+                }
+                else
+                {
+                    workingDict[c.Command.Name] = c.Command;
+                    if (Logger.IsInfoEnabled)
+                        Logger.InfoFormat("The command '{0}' has been updated!", c.Command.Name);
+                }
+
+                updatedCommands++;
+            }
+
+            if (updatedCommands > 0)
+            {
+                OnCommandSetup(workingDict);
+            }
+        }
+
+        void CommandLoaderOnError(object sender, ErrorEventArgs e)
+        {
+            if (!Logger.IsErrorEnabled)
+                return;
+
+            Logger.Error(e.Exception);
+        }
+
+        /// <summary>
+        /// Setups the specified root config.
+        /// </summary>
+        /// <param name="rootConfig">The root config.</param>
+        /// <param name="config">The config.</param>
+        /// <returns></returns>
+        protected virtual bool Setup(IRootConfig rootConfig, IServerConfig config)
+        {
+            return true;
+        }
+
+        partial void SetDefaultCulture(IRootConfig rootConfig, IServerConfig config);
+
+        private void SetupBasic(IRootConfig rootConfig, IServerConfig config, ISocketServerFactory socketServerFactory)
+        {
+            if (rootConfig == null)
+                throw new ArgumentNullException("rootConfig");
+
+            RootConfig = rootConfig;
+
+            if (config == null)
+                throw new ArgumentNullException("config");
+
+            if (!string.IsNullOrEmpty(config.Name))
+                m_Name = config.Name;
+            else
+                m_Name = string.Format("{0}-{1}", this.GetType().Name, Math.Abs(this.GetHashCode()));
+
+            Config = config;
+
+            SetDefaultCulture(rootConfig, config);
+
+            if (!m_ThreadPoolConfigured)
+            {
+                if (!TheadPoolEx.ResetThreadPool(rootConfig.MaxWorkingThreads >= 0 ? rootConfig.MaxWorkingThreads : new Nullable<int>(),
+                        rootConfig.MaxCompletionPortThreads >= 0 ? rootConfig.MaxCompletionPortThreads : new Nullable<int>(),
+                        rootConfig.MinWorkingThreads >= 0 ? rootConfig.MinWorkingThreads : new Nullable<int>(),
+                        rootConfig.MinCompletionPortThreads >= 0 ? rootConfig.MinCompletionPortThreads : new Nullable<int>()))
+                {
+                    throw new Exception("Failed to configure thread pool!");
+                }
+
+                m_ThreadPoolConfigured = true;
+            }
+
+            if (socketServerFactory == null)
+            {
+                var socketServerFactoryType =
+                    Type.GetType("SuperSocket.SocketEngine.SocketServerFactory, SuperSocket.SocketEngine", true);
+
+                socketServerFactory = (ISocketServerFactory)Activator.CreateInstance(socketServerFactoryType);
+            }
+
+            m_SocketServerFactory = socketServerFactory;
+
+            //Read text encoding from the configuration
+            if (!string.IsNullOrEmpty(config.TextEncoding))
+                TextEncoding = Encoding.GetEncoding(config.TextEncoding);
+            else
+                TextEncoding = new ASCIIEncoding();
+        }
+
+        private bool SetupMedium(IReceiveFilterFactory<TRequestInfo> receiveFilterFactory, IEnumerable<IConnectionFilter> connectionFilters, IEnumerable<ICommandLoader<ICommand<TAppSession, TRequestInfo>>> commandLoaders)
+        {
+            if (receiveFilterFactory != null)
+                ReceiveFilterFactory = receiveFilterFactory;
+
+            if (connectionFilters != null && connectionFilters.Any())
+            {
+                if (m_ConnectionFilters == null)
+                    m_ConnectionFilters = new List<IConnectionFilter>();
+
+                m_ConnectionFilters.AddRange(connectionFilters);
+            }
+
+            if (commandLoaders != null && commandLoaders.Any())
+                m_CommandLoaders.AddRange(commandLoaders);
+
+            return SetupCommandLoaders(m_CommandLoaders);
+        }
+
+        private bool SetupAdvanced(IServerConfig config)
+        {
+            if (!SetupSecurity(config))
+                return false;
+
+            if (!SetupListeners(config))
+                return false;
+
+            m_GlobalCommandFilters = GetCommandFilterAttributes(this.GetType());
+
+            var discoveredCommands = new Dictionary<string, ICommand<TAppSession, TRequestInfo>>(StringComparer.OrdinalIgnoreCase);
+            if (!SetupCommands(discoveredCommands))
+                return false;
+
+            OnCommandSetup(discoveredCommands);
+
+            return true;
+        }
+
+        private void OnCommandSetup(IDictionary<string, ICommand<TAppSession, TRequestInfo>> discoveredCommands)
+        {
+            var commandContainer = new Dictionary<string, CommandInfo<ICommand<TAppSession, TRequestInfo>>>(StringComparer.OrdinalIgnoreCase);
+
+            foreach (var command in discoveredCommands.Values)
+            {
+                commandContainer.Add(command.Name,
+                    new CommandInfo<ICommand<TAppSession, TRequestInfo>>(command, m_GlobalCommandFilters));
+            }
+
+            Interlocked.Exchange(ref m_CommandContainer, commandContainer);
+        }
+
+        internal abstract IReceiveFilterFactory<TRequestInfo> CreateDefaultReceiveFilterFactory();
+
+        private bool SetupFinal()
+        {
+            //Check receiveFilterFactory
+            if (ReceiveFilterFactory == null)
+            {
+                ReceiveFilterFactory = CreateDefaultReceiveFilterFactory();
+
+                if (ReceiveFilterFactory == null)
+                {
+                    if (Logger.IsErrorEnabled)
+                        Logger.Error("receiveFilterFactory is required!");
+
+                    return false;
+                }
+            }
+
+            var plainConfig = Config as ServerConfig;
+
+            if (plainConfig == null)
+            {
+                //Using plain config model instead of .NET configuration element to improve performance
+                plainConfig = new ServerConfig(Config);
+
+                if (string.IsNullOrEmpty(plainConfig.Name))
+                    plainConfig.Name = Name;
+
+                Config = plainConfig;
+            }
+
+            try
+            {
+                m_ServerStatus = new StatusInfoCollection();
+                m_ServerStatus.Name = Name;
+                m_ServerStatus.Tag = Name;
+                m_ServerStatus[StatusInfoKeys.MaxConnectionNumber] = Config.MaxConnectionNumber;
+                m_ServerStatus[StatusInfoKeys.Listeners] = m_Listeners;
+            }
+            catch (Exception e)
+            {
+                if (Logger.IsErrorEnabled)
+                    Logger.Error("Failed to create ServerSummary instance!", e);
+
+                return false;
+            }
+
+            return SetupSocketServer();
+        }
+
+        /// <summary>
+        /// Setups with the specified port.
+        /// </summary>
+        /// <param name="port">The port.</param>
+        /// <returns>return setup result</returns>
+        public bool Setup(int port)
+        {
+            return Setup("Any", port);
+        }
+
+        private void TrySetInitializedState()
+        {
+            if (Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Initializing, ServerStateConst.NotInitialized)
+                    != ServerStateConst.NotInitialized)
+            {
+                throw new Exception("The server has been initialized already, you cannot initialize it again!");
+            }
+        }
+
+#if NET_35
+
+        /// <summary>
+        /// Setups with the specified ip and port.
+        /// </summary>
+        /// <param name="ip">The ip.</param>
+        /// <param name="port">The port.</param>
+        /// <param name="providers">The providers.</param>
+        /// <returns></returns>
+        public bool Setup(string ip, int port, params object[] providers)
+        {
+            return Setup(new ServerConfig
+            {
+                Name = string.Format("{0}-{1}", this.GetType().Name, Math.Abs(this.GetHashCode())),
+                Ip = ip,
+                Port = port
+            }, providers);
+        }
+        /// <summary>
+        /// Setups with the specified config, used for programming setup
+        /// </summary>
+        /// <param name="config">The server config.</param>
+        /// <param name="providers">The providers.</param>
+        /// <returns></returns>
+        public bool Setup(IServerConfig config, params object[] providers)
+        {
+            return Setup(new RootConfig(), config, providers);
+        }
+
+        /// <summary>
+        /// Setups with the specified root config, used for programming setup
+        /// </summary>
+        /// <param name="rootConfig">The root config.</param>
+        /// <param name="config">The server config.</param>
+        /// <param name="providers">The providers.</param>
+        /// <returns></returns>
+        public bool Setup(IRootConfig rootConfig, IServerConfig config, params object[] providers)
+        {
+            TrySetInitializedState();
+
+            SetupBasic(rootConfig, config, GetProviderInstance<ISocketServerFactory>(providers));
+
+            if (!SetupLogFactory(GetProviderInstance<ILogFactory>(providers)))
+                return false;
+
+            Logger = CreateLogger(this.Name);
+
+            if (!SetupMedium(GetProviderInstance<IReceiveFilterFactory<TRequestInfo>>(providers), GetProviderInstance<IEnumerable<IConnectionFilter>>(providers), GetProviderInstance<IEnumerable<ICommandLoader<ICommand<TAppSession, TRequestInfo>>>>(providers)))
+                return false;
+
+            if (!SetupAdvanced(config))
+                return false;
+
+            if (!Setup(rootConfig, config))
+                return false;
+
+            if(!SetupFinal())
+                return false;
+
+            m_StateCode = ServerStateConst.NotStarted;
+            return true;
+        }
+
+        private T GetProviderInstance<T>(object[] providers)
+        {
+            if (providers == null || !providers.Any())
+                return default(T);
+
+            var providerType = typeof(T);
+            return (T)providers.FirstOrDefault(p => p != null && providerType.IsAssignableFrom(p.GetType()));
+        }
+#else
+
+        /// <summary>
+        /// Setups with the specified config.
+        /// </summary>
+        /// <param name="config">The server config.</param>
+        /// <param name="socketServerFactory">The socket server factory.</param>
+        /// <param name="receiveFilterFactory">The receive filter factory.</param>
+        /// <param name="logFactory">The log factory.</param>
+        /// <param name="connectionFilters">The connection filters.</param>
+        /// <param name="commandLoaders">The command loaders.</param>
+        /// <returns></returns>
+        public bool Setup(IServerConfig config, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory<TRequestInfo> receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable<IConnectionFilter> connectionFilters = null, IEnumerable<ICommandLoader<ICommand<TAppSession, TRequestInfo>>> commandLoaders = null)
+        {
+            return Setup(new RootConfig(), config, socketServerFactory, receiveFilterFactory, logFactory, connectionFilters, commandLoaders);
+        }
+
+        /// <summary>
+        /// Setups the specified root config, this method used for programming setup
+        /// </summary>
+        /// <param name="rootConfig">The root config.</param>
+        /// <param name="config">The server config.</param>
+        /// <param name="socketServerFactory">The socket server factory.</param>
+        /// <param name="receiveFilterFactory">The Receive filter factory.</param>
+        /// <param name="logFactory">The log factory.</param>
+        /// <param name="connectionFilters">The connection filters.</param>
+        /// <param name="commandLoaders">The command loaders.</param>
+        /// <returns></returns>
+        public bool Setup(IRootConfig rootConfig, IServerConfig config, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory<TRequestInfo> receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable<IConnectionFilter> connectionFilters = null, IEnumerable<ICommandLoader<ICommand<TAppSession, TRequestInfo>>> commandLoaders = null)
+        {
+            TrySetInitializedState();
+
+            SetupBasic(rootConfig, config, socketServerFactory);
+
+            if (!SetupLogFactory(logFactory))
+                return false;
+
+            Logger = CreateLogger(this.Name);
+
+            if (!SetupMedium(receiveFilterFactory, connectionFilters, commandLoaders))
+                return false;
+
+            if (!SetupAdvanced(config))
+                return false;
+
+            if (!Setup(rootConfig, config))
+                return false;
+
+            if (!SetupFinal())
+                return false;
+
+            m_StateCode = ServerStateConst.NotStarted;
+            return true;
+        }
+
+        /// <summary>
+        /// Setups with the specified ip and port.
+        /// </summary>
+        /// <param name="ip">The ip.</param>
+        /// <param name="port">The port.</param>
+        /// <param name="socketServerFactory">The socket server factory.</param>
+        /// <param name="receiveFilterFactory">The Receive filter factory.</param>
+        /// <param name="logFactory">The log factory.</param>
+        /// <param name="connectionFilters">The connection filters.</param>
+        /// <param name="commandLoaders">The command loaders.</param>
+        /// <returns>return setup result</returns>
+        public bool Setup(string ip, int port, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory<TRequestInfo> receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable<IConnectionFilter> connectionFilters = null, IEnumerable<ICommandLoader<ICommand<TAppSession, TRequestInfo>>> commandLoaders = null)
+        {
+            return Setup(new ServerConfig
+                            {
+                                Ip = ip,
+                                Port = port
+                            },
+                          socketServerFactory,
+                          receiveFilterFactory,
+                          logFactory,
+                          connectionFilters,
+                          commandLoaders);
+        }
+#endif
+
+        /// <summary>
+        /// Setups the specified root config.
+        /// </summary>
+        /// <param name="bootstrap">The bootstrap.</param>
+        /// <param name="config">The socket server instance config.</param>
+        /// <param name="factories">The factories.</param>
+        /// <returns></returns>
+        bool IWorkItem.Setup(IBootstrap bootstrap, IServerConfig config, ProviderFactoryInfo[] factories)
+        {
+            if (bootstrap == null)
+                throw new ArgumentNullException("bootstrap");
+
+            Bootstrap = bootstrap;
+
+            if (factories == null)
+                throw new ArgumentNullException("factories");
+
+            TrySetInitializedState();
+
+            var rootConfig = bootstrap.Config;
+
+            SetupBasic(rootConfig, config, GetSingleProviderInstance<ISocketServerFactory>(factories, ProviderKey.SocketServerFactory));
+
+            if (!SetupLogFactory(GetSingleProviderInstance<ILogFactory>(factories, ProviderKey.LogFactory)))
+                return false;
+
+            Logger = CreateLogger(this.Name);
+
+            IEnumerable<IConnectionFilter> connectionFilters = null;
+
+            if (!TryGetProviderInstances(factories, ProviderKey.ConnectionFilter, null,
+                    (p, f) =>
+                    {
+                        var ret = p.Initialize(f.Name, this);
+
+                        if(!ret)
+                        {
+                            Logger.ErrorFormat("Failed to initialize the connection filter: {0}.", f.Name);
+                        }
+
+                        return ret;
+                    }, out connectionFilters))
+            {
+                return false;
+            }
+
+            if (!SetupMedium(
+                    GetSingleProviderInstance<IReceiveFilterFactory<TRequestInfo>>(factories, ProviderKey.ReceiveFilterFactory),
+                    connectionFilters,
+                    GetProviderInstances<ICommandLoader<ICommand<TAppSession, TRequestInfo>>>(
+                            factories,
+                            ProviderKey.CommandLoader,
+                            (t) => Activator.CreateInstance(t.MakeGenericType(typeof(ICommand<TAppSession, TRequestInfo>))))))
+            {
+                return false;
+            }
+
+            if (!SetupAdvanced(config))
+                return false;
+
+            if (!Setup(rootConfig, config))
+                return false;
+
+            if (!SetupFinal())
+                return false;
+
+            m_StateCode = ServerStateConst.NotStarted;
+            return true;
+        }
+
+
+        private TProvider GetSingleProviderInstance<TProvider>(ProviderFactoryInfo[] factories, ProviderKey key)
+        {
+            var factory = factories.FirstOrDefault(p => p.Key.Name == key.Name);
+
+            if (factory == null)
+                return default(TProvider);
+
+            return factory.ExportFactory.CreateExport<TProvider>();
+        }
+
+        private bool TryGetProviderInstances<TProvider>(ProviderFactoryInfo[] factories, ProviderKey key, Func<Type, object> creator, Func<TProvider, ProviderFactoryInfo, bool> initializer, out IEnumerable<TProvider> providers)
+            where TProvider : class
+        {
+            IEnumerable<ProviderFactoryInfo> selectedFactories = factories.Where(p => p.Key.Name == key.Name);
+
+            if (!selectedFactories.Any())
+            {
+                providers = null;
+                return true;
+            }
+
+            providers = new List<TProvider>();
+
+            var list = (List<TProvider>)providers;
+
+            foreach (var f in selectedFactories)
+            {
+                var provider = creator == null ? f.ExportFactory.CreateExport<TProvider>() : f.ExportFactory.CreateExport<TProvider>(creator);
+
+                if (!initializer(provider, f))
+                    return false;
+
+                list.Add(provider);
+            }
+
+            return true;
+        }
+
+        private IEnumerable<TProvider> GetProviderInstances<TProvider>(ProviderFactoryInfo[] factories, ProviderKey key)
+            where TProvider : class
+        {
+            return GetProviderInstances<TProvider>(factories, key, null);
+        }
+
+        private IEnumerable<TProvider> GetProviderInstances<TProvider>(ProviderFactoryInfo[] factories, ProviderKey key, Func<Type, object> creator)
+            where TProvider : class
+        {
+            IEnumerable<TProvider> providers;
+            TryGetProviderInstances<TProvider>(factories, key, creator, (p, f) => true, out providers);
+            return providers;
+        }
+
+        private bool SetupLogFactory(ILogFactory logFactory)
+        {
+            if (logFactory != null)
+            {
+                LogFactory = logFactory;
+                return true;
+            }
+
+            //Log4NetLogFactory is default log factory
+            if (LogFactory == null)
+                LogFactory = new Log4NetLogFactory();
+
+            return true;
+        }
+
+        /// <summary>
+        /// Setups the command loaders.
+        /// </summary>
+        /// <param name="commandLoaders">The command loaders.</param>
+        /// <returns></returns>
+        protected virtual bool SetupCommandLoaders(List<ICommandLoader<ICommand<TAppSession, TRequestInfo>>> commandLoaders)
+        {
+            commandLoaders.Add(new ReflectCommandLoader<ICommand<TAppSession, TRequestInfo>>());
+            return true;
+        }
+
+        /// <summary>
+        /// Creates the logger for the AppServer.
+        /// </summary>
+        /// <param name="loggerName">Name of the logger.</param>
+        /// <returns></returns>
+        protected virtual ILog CreateLogger(string loggerName)
+        {
+            return LogFactory.GetLog(loggerName);
+        }
+
+        /// <summary>
+        /// Setups the security option of socket communications.
+        /// </summary>
+        /// <param name="config">The config of the server instance.</param>
+        /// <returns></returns>
+        private bool SetupSecurity(IServerConfig config)
+        {
+            if (!string.IsNullOrEmpty(config.Security))
+            {
+                SslProtocols configProtocol;
+                if (!config.Security.TryParseEnum<SslProtocols>(true, out configProtocol))
+                {
+                    if (Logger.IsErrorEnabled)
+                        Logger.ErrorFormat("Failed to parse '{0}' to SslProtocol!", config.Security);
+
+                    return false;
+                }
+
+                BasicSecurity = configProtocol;
+            }
+            else
+            {
+                BasicSecurity = SslProtocols.None;
+            }
+
+            try
+            {
+                var certificate = GetCertificate(config.Certificate);
+
+                if (certificate != null)
+                {
+                    Certificate = certificate;
+                }
+                else if(BasicSecurity != SslProtocols.None)
+                {
+                    if (Logger.IsErrorEnabled)
+                        Logger.Error("Certificate is required in this security mode!");
+
+                    return false;
+                }
+                
+            }
+            catch (Exception e)
+            {
+                if (Logger.IsErrorEnabled)
+                    Logger.Error("Failed to initialize certificate!", e);
+
+                return false;
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Gets the certificate from server configuguration.
+        /// </summary>
+        /// <param name="certificate">The certificate config.</param>
+        /// <returns></returns>
+        protected virtual X509Certificate GetCertificate(ICertificateConfig certificate)
+        {
+            if (certificate == null)
+            {
+                if (BasicSecurity != SslProtocols.None && Logger.IsErrorEnabled)
+                    Logger.Error("There is no certificate configured!");
+                return null;
+            }
+
+            if (string.IsNullOrEmpty(certificate.FilePath) && string.IsNullOrEmpty(certificate.Thumbprint))
+            {
+                if (BasicSecurity != SslProtocols.None && Logger.IsErrorEnabled)
+                    Logger.Error("You should define certificate node and either attribute 'filePath' or 'thumbprint' is required!");
+
+                return null;
+            }
+
+            return CertificateManager.Initialize(certificate, GetFilePath);
+        }
+
+        bool IRemoteCertificateValidator.Validate(IAppSession session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        {
+            return ValidateClientCertificate((TAppSession)session, sender, certificate, chain, sslPolicyErrors);
+        }
+
+        /// <summary>
+        /// Validates the client certificate. This method is only used if the certificate configuration attribute "clientCertificateRequired" is true.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="sender">The sender.</param>
+        /// <param name="certificate">The certificate.</param>
+        /// <param name="chain">The chain.</param>
+        /// <param name="sslPolicyErrors">The SSL policy errors.</param>
+        /// <returns>return the validation result</returns>
+        protected virtual bool ValidateClientCertificate(TAppSession session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        {
+            return sslPolicyErrors == SslPolicyErrors.None;
+        }
+
+        /// <summary>
+        /// Setups the socket server.instance
+        /// </summary>
+        /// <returns></returns>
+        private bool SetupSocketServer()
+        {
+            try
+            {
+                m_SocketServer = m_SocketServerFactory.CreateSocketServer<TRequestInfo>(this, m_Listeners, Config);
+                return m_SocketServer != null;
+            }
+            catch (Exception e)
+            {
+                if (Logger.IsErrorEnabled)
+                    Logger.Error(e);
+
+                return false;
+            }
+        }
+
+        private IPAddress ParseIPAddress(string ip)
+        {
+            if (string.IsNullOrEmpty(ip) || "Any".Equals(ip, StringComparison.OrdinalIgnoreCase))
+                return IPAddress.Any;
+            else if ("IPv6Any".Equals(ip, StringComparison.OrdinalIgnoreCase))
+                return IPAddress.IPv6Any;
+            else
+               return IPAddress.Parse(ip);
+        }
+
+        /// <summary>
+        /// Setups the listeners base on server configuration
+        /// </summary>
+        /// <param name="config">The config.</param>
+        /// <returns></returns>
+        private bool SetupListeners(IServerConfig config)
+        {
+            var listeners = new List<ListenerInfo>();
+
+            try
+            {
+                if (config.Port > 0)
+                {
+                    listeners.Add(new ListenerInfo
+                    {
+                        EndPoint = new IPEndPoint(ParseIPAddress(config.Ip), config.Port),
+                        BackLog = config.ListenBacklog,
+                        Security = BasicSecurity
+                    });
+                }
+                else
+                {
+                    //Port is not configured, but ip is configured
+                    if (!string.IsNullOrEmpty(config.Ip))
+                    {
+                        if (Logger.IsErrorEnabled)
+                            Logger.Error("Port is required in config!");
+
+                        return false;
+                    }
+                }
+
+                //There are listener defined
+                if (config.Listeners != null && config.Listeners.Any())
+                {
+                    //But ip and port were configured in server node
+                    //We don't allow this case
+                    if (listeners.Any())
+                    {
+                        if (Logger.IsErrorEnabled)
+                            Logger.Error("If you configured Ip and Port in server node, you cannot defined listener in listeners node any more!");
+
+                        return false;
+                    }
+
+                    foreach (var l in config.Listeners)
+                    {
+                        SslProtocols configProtocol;
+
+                        if (string.IsNullOrEmpty(l.Security))
+                        {
+                            configProtocol = BasicSecurity;
+                        }
+                        else if (!l.Security.TryParseEnum<SslProtocols>(true, out configProtocol))
+                        {
+                            if (Logger.IsErrorEnabled)
+                                Logger.ErrorFormat("Failed to parse '{0}' to SslProtocol!", config.Security);
+
+                            return false;
+                        }
+
+                        if (configProtocol != SslProtocols.None && (Certificate == null))
+                        {
+                            if (Logger.IsErrorEnabled)
+                                Logger.Error("There is no certificate loaded, but there is a secure listener defined!");
+                            return false;
+                        }
+
+                        listeners.Add(new ListenerInfo
+                        {
+                            EndPoint = new IPEndPoint(ParseIPAddress(l.Ip), l.Port),
+                            BackLog = l.Backlog,
+                            Security = configProtocol
+                        });
+                    }
+                }
+
+                if (!listeners.Any())
+                {
+                    if (Logger.IsErrorEnabled)
+                        Logger.Error("No listener defined!");
+
+                    return false;
+                }
+
+                m_Listeners = listeners.ToArray();
+
+                return true;
+            }
+            catch (Exception e)
+            {
+                if (Logger.IsErrorEnabled)
+                    Logger.Error(e);
+
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Gets the name of the server instance.
+        /// </summary>
+        public string Name
+        {
+            get { return m_Name; }
+        }
+
+        private ISocketServer m_SocketServer;
+
+        /// <summary>
+        /// Gets the socket server.
+        /// </summary>
+        /// <value>
+        /// The socket server.
+        /// </value>
+        ISocketServer ISocketServerAccessor.SocketServer
+        {
+            get { return m_SocketServer; }
+        }
+
+        /// <summary>
+        /// Starts this server instance.
+        /// </summary>
+        /// <returns>
+        /// return true if start successfull, else false
+        /// </returns>
+        public virtual bool Start()
+        {
+            var origStateCode = Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Starting, ServerStateConst.NotStarted);
+
+            if (origStateCode != ServerStateConst.NotStarted)
+            {
+                if (origStateCode < ServerStateConst.NotStarted)
+                    throw new Exception("You cannot start a server instance which has not been setup yet.");
+
+                if (Logger.IsErrorEnabled)
+                    Logger.ErrorFormat("This server instance is in the state {0}, you cannot start it now.", (ServerState)origStateCode);
+
+                return false;
+            }
+
+            if (!m_SocketServer.Start())
+            {
+                m_StateCode = ServerStateConst.NotStarted;
+                return false;
+            }
+
+            StartedTime = DateTime.Now;
+            m_StateCode = ServerStateConst.Running;
+
+            m_ServerStatus[StatusInfoKeys.IsRunning] = true;
+            m_ServerStatus[StatusInfoKeys.StartedTime] = StartedTime;
+
+            try
+            {
+                //Will be removed in the next version
+#pragma warning disable 0612, 618
+                OnStartup();
+#pragma warning restore 0612, 618
+
+                OnStarted();
+            }
+            catch (Exception e)
+            {
+                if (Logger.IsErrorEnabled)
+                {
+                    Logger.Error("One exception wa thrown in the method 'OnStartup()'.", e);
+                }
+            }
+            finally
+            {
+                if (Logger.IsInfoEnabled)
+                    Logger.Info(string.Format("The server instance {0} has been started!", Name));
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Called when [startup].
+        /// </summary>
+        [Obsolete("Use OnStarted() instead")]
+        protected virtual void OnStartup()
+        {
+
+        }
+
+        /// <summary>
+        /// Called when [started].
+        /// </summary>
+        protected virtual void OnStarted()
+        {
+
+        }
+
+        /// <summary>
+        /// Called when [stopped].
+        /// </summary>
+        protected virtual void OnStopped()
+        {
+
+        }
+
+        /// <summary>
+        /// Stops this server instance.
+        /// </summary>
+        public virtual void Stop()
+        {
+            if (Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Stopping, ServerStateConst.Running)
+                    != ServerStateConst.Running)
+            {
+                return;
+            }
+
+            m_SocketServer.Stop();
+
+            m_StateCode = ServerStateConst.NotStarted;
+
+            OnStopped();
+
+            m_ServerStatus[StatusInfoKeys.IsRunning] = false;
+            m_ServerStatus[StatusInfoKeys.StartedTime] = null;
+
+            if (Logger.IsInfoEnabled)
+                Logger.Info(string.Format("The server instance {0} has been stopped!", Name));
+        }
+
+        /// <summary>
+        /// Gets command by command name.
+        /// </summary>
+        /// <param name="commandName">Name of the command.</param>
+        /// <returns></returns>
+        private CommandInfo<ICommand<TAppSession, TRequestInfo>> GetCommandByName(string commandName)
+        {
+            CommandInfo<ICommand<TAppSession, TRequestInfo>> commandProxy;
+
+            if (m_CommandContainer.TryGetValue(commandName, out commandProxy))
+                return commandProxy;
+            else
+                return null;
+        }
+
+
+        private Func<TAppSession, byte[], int, int, bool> m_RawDataReceivedHandler;
+
+        /// <summary>
+        /// Gets or sets the raw binary data received event handler.
+        /// TAppSession: session
+        /// byte[]: receive buffer
+        /// int: receive buffer offset
+        /// int: receive lenght
+        /// bool: whether process the received data further
+        /// </summary>
+        event Func<TAppSession, byte[], int, int, bool> IRawDataProcessor<TAppSession>.RawDataReceived
+        {
+            add { m_RawDataReceivedHandler += value; }
+            remove { m_RawDataReceivedHandler -= value; }
+        }
+
+        /// <summary>
+        /// Called when [raw data received].
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="buffer">The buffer.</param>
+        /// <param name="offset">The offset.</param>
+        /// <param name="length">The length.</param>
+        internal bool OnRawDataReceived(IAppSession session, byte[] buffer, int offset, int length)
+        {
+            var handler = m_RawDataReceivedHandler;
+            if (handler == null)
+                return true;
+
+            return handler((TAppSession)session, buffer, offset, length);
+        }
+
+        private RequestHandler<TAppSession, TRequestInfo> m_RequestHandler;
+
+        /// <summary>
+        /// Occurs when a full request item received.
+        /// </summary>
+        public virtual event RequestHandler<TAppSession, TRequestInfo> NewRequestReceived
+        {
+            add { m_RequestHandler += value; }
+            remove { m_RequestHandler -= value; }
+        }
+
+        /// <summary>
+        /// Executes the command.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="requestInfo">The request info.</param>
+        protected virtual void ExecuteCommand(TAppSession session, TRequestInfo requestInfo)
+        {
+            if (m_RequestHandler == null)
+            {
+                var commandProxy = GetCommandByName(requestInfo.Key);
+
+                if (commandProxy != null)
+                {
+                    var command = commandProxy.Command;
+                    var commandFilters = commandProxy.Filters;
+
+                    session.CurrentCommand = requestInfo.Key;
+
+                    var cancelled = false;
+
+                    if (commandFilters == null)
+                    {
+                        command.ExecuteCommand(session, requestInfo);
+                    }
+                    else
+                    {
+                        var commandContext = new CommandExecutingContext();
+                        commandContext.Initialize(session, requestInfo, command);
+
+                        for (var i = 0; i < commandFilters.Length; i++)
+                        {
+                            var filter = commandFilters[i];
+                            filter.OnCommandExecuting(commandContext);
+
+                            if (commandContext.Cancel)
+                            {
+                                cancelled = true;
+                                if(Logger.IsInfoEnabled)
+                                    Logger.Info(session, string.Format("The executing of the command {0} was cancelled by the command filter {1}.", command.Name, filter.GetType().ToString()));
+                                break;
+                            }
+                        }
+
+                        if (!cancelled)
+                        {
+                            try
+                            {
+                                command.ExecuteCommand(session, requestInfo);
+                            }
+                            catch (Exception exc)
+                            {
+                                commandContext.Exception = exc;
+                            }
+
+                            for (var i = 0; i < commandFilters.Length; i++)
+                            {
+                                var filter = commandFilters[i];
+                                filter.OnCommandExecuted(commandContext);
+                            }
+
+                            if (commandContext.Exception != null && !commandContext.ExceptionHandled)
+                            {
+                                try
+                                {
+                                    session.InternalHandleExcetion(commandContext.Exception);
+                                }
+                                catch
+                                {
+
+                                }
+                            }
+                        }
+                    }
+
+                    if(!cancelled)
+                    {
+                        session.PrevCommand = requestInfo.Key;
+
+                        if (Config.LogCommand && Logger.IsInfoEnabled)
+                            Logger.Info(session, string.Format("Command - {0}", requestInfo.Key));
+                    }
+                }
+                else
+                {
+                    session.InternalHandleUnknownRequest(requestInfo);
+                }
+
+                session.LastActiveTime = DateTime.Now;
+            }
+            else
+            {
+                session.CurrentCommand = requestInfo.Key;
+
+                try
+                {
+                    m_RequestHandler(session, requestInfo);
+                }
+                catch (Exception e)
+                {
+                    session.InternalHandleExcetion(e);
+                }
+                
+                session.PrevCommand = requestInfo.Key;
+                session.LastActiveTime = DateTime.Now;
+
+                if (Config.LogCommand && Logger.IsInfoEnabled)
+                    Logger.Info(session, string.Format("Command - {0}", requestInfo.Key));
+            }
+
+            Interlocked.Increment(ref m_TotalHandledRequests);
+        }
+
+        /// <summary>
+        /// Executes the command for the session.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="requestInfo">The request info.</param>
+        internal void ExecuteCommand(IAppSession session, TRequestInfo requestInfo)
+        {
+            this.ExecuteCommand((TAppSession)session, requestInfo);
+        }
+
+        /// <summary>
+        /// Executes the command.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="requestInfo">The request info.</param>
+        void IRequestHandler<TRequestInfo>.ExecuteCommand(IAppSession session, TRequestInfo requestInfo)
+        {
+            this.ExecuteCommand((TAppSession)session, requestInfo);
+        }
+
+        /// <summary>
+        /// Gets or sets the server's connection filter
+        /// </summary>
+        /// <value>
+        /// The server's connection filters
+        /// </value>
+        public IEnumerable<IConnectionFilter> ConnectionFilters
+        {
+            get { return m_ConnectionFilters; }
+        }
+
+        /// <summary>
+        /// Executes the connection filters.
+        /// </summary>
+        /// <param name="remoteAddress">The remote address.</param>
+        /// <returns></returns>
+        private bool ExecuteConnectionFilters(IPEndPoint remoteAddress)
+        {
+            if (m_ConnectionFilters == null)
+                return true;
+
+            for (var i = 0; i < m_ConnectionFilters.Count; i++)
+            {
+                var currentFilter = m_ConnectionFilters[i];
+                if (!currentFilter.AllowConnect(remoteAddress))
+                {
+                    if (Logger.IsInfoEnabled)
+                        Logger.InfoFormat("A connection from {0} has been refused by filter {1}!", remoteAddress, currentFilter.Name);
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Creates the app session.
+        /// </summary>
+        /// <param name="socketSession">The socket session.</param>
+        /// <returns></returns>
+        IAppSession IAppServer.CreateAppSession(ISocketSession socketSession)
+        {
+            if (!ExecuteConnectionFilters(socketSession.RemoteEndPoint))
+                return NullAppSession;
+
+            var appSession = CreateAppSession(socketSession);
+            
+            appSession.Initialize(this, socketSession);
+
+            return appSession;
+        }
+
+        /// <summary>
+        /// create a new TAppSession instance, you can override it to create the session instance in your own way
+        /// </summary>
+        /// <param name="socketSession">the socket session.</param>
+        /// <returns>the new created session instance</returns>
+        protected virtual TAppSession CreateAppSession(ISocketSession socketSession)
+        {
+            return new TAppSession();
+        }
+
+        /// <summary>
+        /// Registers the new created app session into the appserver's session container.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <returns></returns>
+        bool IAppServer.RegisterSession(IAppSession session)
+        {
+            var appSession = session as TAppSession;
+
+            if (!RegisterSession(appSession.SessionID, appSession))
+                return false;
+
+            appSession.SocketSession.Closed += OnSocketSessionClosed;
+
+            if (Config.LogBasicSessionActivity && Logger.IsInfoEnabled)
+                Logger.Info(session, "A new session connected!");
+
+            OnNewSessionConnected(appSession);
+            return true;
+        }
+
+        /// <summary>
+        /// Registers the session into session container.
+        /// </summary>
+        /// <param name="sessionID">The session ID.</param>
+        /// <param name="appSession">The app session.</param>
+        /// <returns></returns>
+        protected virtual bool RegisterSession(string sessionID, TAppSession appSession)
+        {
+            return true;
+        }
+
+
+        private SessionHandler<TAppSession> m_NewSessionConnected;
+
+        /// <summary>
+        /// The action which will be executed after a new session connect
+        /// </summary>
+        public event SessionHandler<TAppSession> NewSessionConnected
+        {
+            add { m_NewSessionConnected += value; }
+            remove { m_NewSessionConnected -= value; }
+        }
+
+        /// <summary>
+        /// Called when [new session connected].
+        /// </summary>
+        /// <param name="session">The session.</param>
+        protected virtual void OnNewSessionConnected(TAppSession session)
+        {
+            var handler = m_NewSessionConnected;
+            if (handler == null)
+                return;
+
+            handler.BeginInvoke(session, OnNewSessionConnectedCallback, handler);
+        }
+
+        private void OnNewSessionConnectedCallback(IAsyncResult result)
+        {
+            try
+            {
+                var handler = (SessionHandler<TAppSession>)result.AsyncState;
+                handler.EndInvoke(result);
+            }
+            catch (Exception e)
+            {
+                Logger.Error(e);
+            }
+        }
+
+        /// <summary>
+        /// Resets the session's security protocol.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="security">The security protocol.</param>
+        public void ResetSessionSecurity(IAppSession session, SslProtocols security)
+        {
+            m_SocketServer.ResetSessionSecurity(session, security);
+        }
+
+        /// <summary>
+        /// Called when [socket session closed].
+        /// </summary>
+        /// <param name="session">The socket session.</param>
+        /// <param name="reason">The reason.</param>
+        private void OnSocketSessionClosed(ISocketSession session, CloseReason reason)
+        {
+            //Even if LogBasicSessionActivity is false, we also log the unexpected closing because the close reason probably be useful
+            if (Logger.IsInfoEnabled && (Config.LogBasicSessionActivity || (reason != CloseReason.ServerClosing && reason != CloseReason.ClientClosing && reason != CloseReason.ServerShutdown && reason != CloseReason.SocketError)))
+                Logger.Info(session, string.Format("This session was closed for {0}!", reason));
+
+            var appSession = session.AppSession as TAppSession;
+            appSession.Connected = false;
+            OnSessionClosed(appSession, reason);
+        }
+
+        private SessionHandler<TAppSession, CloseReason> m_SessionClosed;
+        /// <summary>
+        /// Gets/sets the session closed event handler.
+        /// </summary>
+        public event SessionHandler<TAppSession, CloseReason> SessionClosed
+        {
+            add { m_SessionClosed += value; }
+            remove { m_SessionClosed -= value; }
+        }
+
+        /// <summary>
+        /// Called when [session closed].
+        /// </summary>
+        /// <param name="session">The appSession.</param>
+        /// <param name="reason">The reason.</param>
+        protected virtual void OnSessionClosed(TAppSession session, CloseReason reason)
+        {
+            var handler = m_SessionClosed;
+
+            if (handler != null)
+            {
+                handler.BeginInvoke(session, reason, OnSessionClosedCallback, handler);
+            }
+
+            session.OnSessionClosed(reason);
+        }
+
+        private void OnSessionClosedCallback(IAsyncResult result)
+        {
+            try
+            {
+                var handler = (SessionHandler<TAppSession, CloseReason>)result.AsyncState;
+                handler.EndInvoke(result);
+            }
+            catch (Exception e)
+            {
+                Logger.Error(e);
+            }
+        }
+
+        /// <summary>
+        /// Gets the app session by ID.
+        /// </summary>
+        /// <param name="sessionID">The session ID.</param>
+        /// <returns></returns>
+        public abstract TAppSession GetSessionByID(string sessionID);
+
+        /// <summary>
+        /// Gets the app session by ID.
+        /// </summary>
+        /// <param name="sessionID"></param>
+        /// <returns></returns>
+        IAppSession IAppServer.GetSessionByID(string sessionID)
+        {
+            return this.GetSessionByID(sessionID);
+        }
+
+        /// <summary>
+        /// Gets the matched sessions from sessions snapshot.
+        /// </summary>
+        /// <param name="critera">The prediction critera.</param>
+        public virtual IEnumerable<TAppSession> GetSessions(Func<TAppSession, bool> critera)
+        {
+            throw new NotSupportedException();
+        }
+
+        /// <summary>
+        /// Gets all sessions in sessions snapshot.
+        /// </summary>
+        public virtual IEnumerable<TAppSession> GetAllSessions()
+        {
+            throw new NotSupportedException();
+        }
+
+        /// <summary>
+        /// Gets the total session count.
+        /// </summary>
+        public abstract int SessionCount { get; }
+
+        /// <summary>
+        /// Gets the physical file path by the relative file path,
+        /// search both in the appserver's root and in the supersocket root dir if the isolation level has been set other than 'None'.
+        /// </summary>
+        /// <param name="relativeFilePath">The relative file path.</param>
+        /// <returns></returns>
+        public string GetFilePath(string relativeFilePath)
+        {
+            var filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, relativeFilePath);
+
+            if (!System.IO.File.Exists(filePath) && RootConfig != null && RootConfig.Isolation != IsolationMode.None)
+            {
+                var rootDir = System.IO.Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent.FullName;
+                var rootFilePath = System.IO.Path.Combine(rootDir, relativeFilePath);
+
+                if (System.IO.File.Exists(rootFilePath))
+                    return rootFilePath;
+            }
+
+            return filePath;
+        }
+
+        #region IActiveConnector
+
+        /// <summary>
+        /// Connect the remote endpoint actively.
+        /// </summary>
+        /// <param name="targetEndPoint">The target end point.</param>
+        /// <param name="localEndPoint">The local end point.</param>
+        /// <returns></returns>
+        /// <exception cref="System.Exception">This server cannot support active connect.</exception>
+        Task<ActiveConnectResult> IActiveConnector.ActiveConnect(EndPoint targetEndPoint, EndPoint localEndPoint)
+        {
+            var activeConnector = m_SocketServer as IActiveConnector;
+
+            if (activeConnector == null)
+                throw new Exception("This server cannot support active connect.");
+
+            return activeConnector.ActiveConnect(targetEndPoint, localEndPoint);
+        }
+
+        /// <summary>
+        /// Connect the remote endpoint actively.
+        /// </summary>
+        /// <param name="targetEndPoint">The target end point.</param>
+        /// <returns></returns>
+        /// <exception cref="System.Exception">This server cannot support active connect.</exception>
+        Task<ActiveConnectResult> IActiveConnector.ActiveConnect(EndPoint targetEndPoint)
+        {
+            return ((IActiveConnector)this).ActiveConnect(targetEndPoint, null);
+        }
+
+        #endregion IActiveConnector
+
+        #region ISystemEndPoint
+        /// <summary>
+        /// Transfers the system message
+        /// </summary>
+        /// <param name="messageType">Type of the message.</param>
+        /// <param name="messageData">The message data.</param>
+        void ISystemEndPoint.TransferSystemMessage(string messageType, object messageData)
+        {
+            OnSystemMessageReceived(messageType, messageData);
+        }
+
+        /// <summary>
+        /// Called when [system message received].
+        /// </summary>
+        /// <param name="messageType">Type of the message.</param>
+        /// <param name="messageData">The message data.</param>
+        protected virtual void OnSystemMessageReceived(string messageType, object messageData)
+        {
+
+        }
+
+        #endregion ISystemEndPoint
+
+        #region IStatusInfoSource
+
+        private StatusInfoCollection m_ServerStatus;
+
+        StatusInfoAttribute[] IStatusInfoSource.GetServerStatusMetadata()
+        {
+            return this.GetType().GetStatusInfoMetadata();
+        }
+
+        StatusInfoCollection IStatusInfoSource.CollectServerStatus(StatusInfoCollection bootstrapStatus)
+        {
+            UpdateServerStatus(m_ServerStatus);
+            this.AsyncRun(() => OnServerStatusCollected(bootstrapStatus, m_ServerStatus), e => Logger.Error(e));
+            return m_ServerStatus;
+        }
+
+        /// <summary>
+        /// Updates the summary of the server.
+        /// </summary>
+        /// <param name="serverStatus">The server status.</param>
+        protected virtual void UpdateServerStatus(StatusInfoCollection serverStatus)
+        {
+            DateTime now = DateTime.Now;
+
+            serverStatus[StatusInfoKeys.IsRunning] = m_StateCode == ServerStateConst.Running;
+            serverStatus[StatusInfoKeys.TotalConnections] = this.SessionCount;
+
+            var totalHandledRequests0 = serverStatus.GetValue<long>(StatusInfoKeys.TotalHandledRequests, 0);
+
+            var totalHandledRequests = this.TotalHandledRequests;
+
+            serverStatus[StatusInfoKeys.RequestHandlingSpeed] = ((totalHandledRequests - totalHandledRequests0) / now.Subtract(serverStatus.CollectedTime).TotalSeconds);
+            serverStatus[StatusInfoKeys.TotalHandledRequests] = totalHandledRequests;
+
+            if (State == ServerState.Running)
+            {
+                var sendingQueuePool = m_SocketServer.SendingQueuePool;
+                serverStatus[StatusInfoKeys.AvialableSendingQueueItems] = sendingQueuePool.AvialableItemsCount;
+                serverStatus[StatusInfoKeys.TotalSendingQueueItems] = sendingQueuePool.TotalItemsCount;
+            }
+            else
+            {
+                serverStatus[StatusInfoKeys.AvialableSendingQueueItems] = 0;
+                serverStatus[StatusInfoKeys.TotalSendingQueueItems] = 0;
+            }
+
+            serverStatus.CollectedTime = now;
+        }
+
+        /// <summary>
+        /// Called when [server status collected].
+        /// </summary>
+        /// <param name="bootstrapStatus">The bootstrapStatus status.</param>
+        /// <param name="serverStatus">The server status.</param>
+        protected virtual void OnServerStatusCollected(StatusInfoCollection bootstrapStatus, StatusInfoCollection serverStatus)
+        {
+
+        }
+
+        #endregion IStatusInfoSource
+
+        #region IDisposable Members
+
+        /// <summary>
+        /// Releases unmanaged and - optionally - managed resources
+        /// </summary>
+        public void Dispose()
+        {
+            if (m_StateCode == ServerStateConst.Running)
+                Stop();
+        }
+
+        #endregion
+    }
+}

+ 689 - 0
SocketBase/AppSession.cs

@@ -0,0 +1,689 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Security.Authentication;
+using System.Text;
+using System.Threading;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Command;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Logging;
+using SuperSocket.SocketBase.Protocol;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// AppSession base class
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    public abstract class AppSession<TAppSession, TRequestInfo> : IAppSession, IAppSession<TAppSession, TRequestInfo>
+        where TAppSession : AppSession<TAppSession, TRequestInfo>, IAppSession, new()
+        where TRequestInfo : class, IRequestInfo
+    {
+        #region Properties
+
+        /// <summary>
+        /// Gets the app server instance assosiated with the session.
+        /// </summary>
+        public virtual AppServerBase<TAppSession, TRequestInfo> AppServer { get; private set; }
+
+        /// <summary>
+        /// Gets the app server instance assosiated with the session.
+        /// </summary>
+        IAppServer IAppSession.AppServer
+        {
+            get { return this.AppServer; }
+        }
+
+        /// <summary>
+        /// Gets or sets the charset which is used for transfering text message.
+        /// </summary>
+        /// <value>
+        /// The charset.
+        /// </value>
+        public Encoding Charset { get; set; }
+
+        private IDictionary<object, object> m_Items;
+
+        /// <summary>
+        /// Gets the items dictionary, only support 10 items maximum
+        /// </summary>
+        public IDictionary<object, object> Items
+        {
+            get
+            {
+                if (m_Items == null)
+                    m_Items = new Dictionary<object, object>(10);
+
+                return m_Items;
+            }
+        }
+
+
+        private bool m_Connected = false;
+
+        /// <summary>
+        /// Gets a value indicating whether this <see cref="IAppSession"/> is connected.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if connected; otherwise, <c>false</c>.
+        /// </value>
+        public bool Connected
+        {
+            get { return m_Connected; }
+            internal set { m_Connected = value; }
+        }
+
+        /// <summary>
+        /// Gets or sets the previous command.
+        /// </summary>
+        /// <value>
+        /// The prev command.
+        /// </value>
+        public string PrevCommand { get; set; }
+
+        /// <summary>
+        /// Gets or sets the current executing command.
+        /// </summary>
+        /// <value>
+        /// The current command.
+        /// </value>
+        public string CurrentCommand { get; set; }
+
+
+        /// <summary>
+        /// Gets or sets the secure protocol of transportation layer.
+        /// </summary>
+        /// <value>
+        /// The secure protocol.
+        /// </value>
+        public SslProtocols SecureProtocol
+        {
+            get { return SocketSession.SecureProtocol; }
+            set { SocketSession.SecureProtocol = value; }
+        }
+
+        /// <summary>
+        /// Gets the local listening endpoint.
+        /// </summary>
+        public IPEndPoint LocalEndPoint
+        {
+            get { return SocketSession.LocalEndPoint; }
+        }
+
+        /// <summary>
+        /// Gets the remote endpoint of client.
+        /// </summary>
+        public IPEndPoint RemoteEndPoint
+        {
+            get { return SocketSession.RemoteEndPoint; }
+        }
+
+        /// <summary>
+        /// Gets the logger.
+        /// </summary>
+        public ILog Logger
+        {
+            get { return AppServer.Logger; }
+        }
+
+        /// <summary>
+        /// Gets or sets the last active time of the session.
+        /// </summary>
+        /// <value>
+        /// The last active time.
+        /// </value>
+        public DateTime LastActiveTime { get; set; }
+
+        /// <summary>
+        /// Gets the start time of the session.
+        /// </summary>
+        public DateTime StartTime { get; private set; }
+
+        /// <summary>
+        /// Gets the session ID.
+        /// </summary>
+        public string SessionID { get; private set; }
+
+        /// <summary>
+        /// Gets the socket session of the AppSession.
+        /// </summary>
+        public ISocketSession SocketSession { get; private set; }
+
+        /// <summary>
+        /// Gets the config of the server.
+        /// </summary>
+        public IServerConfig Config
+        {
+            get { return AppServer.Config; }
+        }
+
+        IReceiveFilter<TRequestInfo> m_ReceiveFilter;
+
+        #endregion
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppSession&lt;TAppSession, TRequestInfo&gt;"/> class.
+        /// </summary>
+        public AppSession()
+        {
+            this.StartTime = DateTime.Now;
+            this.LastActiveTime = this.StartTime;
+        }
+
+
+        /// <summary>
+        /// Initializes the specified app session by AppServer and SocketSession.
+        /// </summary>
+        /// <param name="appServer">The app server.</param>
+        /// <param name="socketSession">The socket session.</param>
+        public virtual void Initialize(IAppServer<TAppSession, TRequestInfo> appServer, ISocketSession socketSession)
+        {
+            var castedAppServer = (AppServerBase<TAppSession, TRequestInfo>)appServer;
+            AppServer = castedAppServer;
+            Charset = castedAppServer.TextEncoding;
+            SocketSession = socketSession;
+            SessionID = socketSession.SessionID;
+            m_Connected = true;
+            m_ReceiveFilter = castedAppServer.ReceiveFilterFactory.CreateFilter(appServer, this, socketSession.RemoteEndPoint);
+
+            var filterInitializer = m_ReceiveFilter as IReceiveFilterInitializer;
+            if (filterInitializer != null)
+                filterInitializer.Initialize(castedAppServer, this);
+
+            socketSession.Initialize(this);
+
+            OnInit();
+        }
+
+        /// <summary>
+        /// Starts the session.
+        /// </summary>
+        void IAppSession.StartSession()
+        {
+            OnSessionStarted();
+        }
+
+        /// <summary>
+        /// Called when [init].
+        /// </summary>
+        protected virtual void OnInit()
+        {
+            
+        }
+
+        /// <summary>
+        /// Called when [session started].
+        /// </summary>
+        protected virtual void OnSessionStarted()
+        {
+
+        }
+
+        /// <summary>
+        /// Called when [session closed].
+        /// </summary>
+        /// <param name="reason">The reason.</param>
+        internal protected virtual void OnSessionClosed(CloseReason reason)
+        {
+
+        }
+
+
+        /// <summary>
+        /// Handles the exceptional error, it only handles application error.
+        /// </summary>
+        /// <param name="e">The exception.</param>
+        protected virtual void HandleException(Exception e)
+        {
+            Logger.Error(this, e);
+            this.Close(CloseReason.ApplicationError);
+        }
+
+        /// <summary>
+        /// Handles the unknown request.
+        /// </summary>
+        /// <param name="requestInfo">The request info.</param>
+        protected virtual void HandleUnknownRequest(TRequestInfo requestInfo)
+        {
+
+        }
+
+        internal void InternalHandleUnknownRequest(TRequestInfo requestInfo)
+        {
+            HandleUnknownRequest(requestInfo);
+        }
+
+        internal void InternalHandleExcetion(Exception e)
+        {
+            HandleException(e);
+        }
+
+        /// <summary>
+        /// Closes the session by the specified reason.
+        /// </summary>
+        /// <param name="reason">The close reason.</param>
+        public virtual void Close(CloseReason reason)
+        {
+            this.SocketSession.Close(reason);
+        }
+
+        /// <summary>
+        /// Closes this session.
+        /// </summary>
+        public virtual void Close()
+        {
+            Close(CloseReason.ServerClosing);
+        }
+
+        #region Sending processing
+
+        /// <summary>
+        /// Try to send the message to client.
+        /// </summary>
+        /// <param name="message">The message which will be sent.</param>
+        /// <returns>Indicate whether the message was pushed into the sending queue</returns>
+        public virtual bool TrySend(string message)
+        {
+            var data = this.Charset.GetBytes(message);
+            return InternalTrySend(new ArraySegment<byte>(data, 0, data.Length));
+        }
+
+        /// <summary>
+        /// Sends the message to client.
+        /// </summary>
+        /// <param name="message">The message which will be sent.</param>
+        public virtual void Send(string message)
+        {
+            var data = this.Charset.GetBytes(message);
+            Send(data, 0, data.Length);
+        }
+
+        /// <summary>
+        /// Try to send the data to client.
+        /// </summary>
+        /// <param name="data">The data which will be sent.</param>
+        /// <param name="offset">The offset.</param>
+        /// <param name="length">The length.</param>
+        /// <returns>Indicate whether the message was pushed into the sending queue</returns>
+        public virtual bool TrySend(byte[] data, int offset, int length)
+        {
+            return InternalTrySend(new ArraySegment<byte>(data, offset, length));
+        }
+
+        /// <summary>
+        /// Sends the data to client.
+        /// </summary>
+        /// <param name="data">The data which will be sent.</param>
+        /// <param name="offset">The offset.</param>
+        /// <param name="length">The length.</param>
+        public virtual void Send(byte[] data, int offset, int length)
+        {
+            InternalSend(new ArraySegment<byte>(data, offset, length));
+        }
+
+        private bool InternalTrySend(ArraySegment<byte> segment)
+        {
+            if (!SocketSession.TrySend(segment))
+                return false;
+
+            LastActiveTime = DateTime.Now;
+            return true;
+        }
+
+        /// <summary>
+        /// Try to send the data segment to client.
+        /// </summary>
+        /// <param name="segment">The segment which will be sent.</param>
+        /// <returns>Indicate whether the message was pushed into the sending queue</returns>
+        public virtual bool TrySend(ArraySegment<byte> segment)
+        {
+            if (!m_Connected)
+                return false;
+
+            return InternalTrySend(segment);
+        }
+
+
+        private void InternalSend(ArraySegment<byte> segment)
+        {
+            if (!m_Connected)
+                return;
+
+            if (InternalTrySend(segment))
+                return;
+
+            var sendTimeOut = Config.SendTimeOut;
+
+            //Don't retry, timeout directly
+            if (sendTimeOut < 0)
+            {
+                throw new TimeoutException("The sending attempt timed out");
+            }
+
+            var timeOutTime = sendTimeOut > 0 ? DateTime.Now.AddMilliseconds(sendTimeOut) : DateTime.Now;
+
+            var spinWait = new SpinWait();
+
+            while (m_Connected)
+            {
+                spinWait.SpinOnce();
+
+                if (InternalTrySend(segment))
+                    return;
+
+                //If sendTimeOut = 0, don't have timeout check
+                if (sendTimeOut > 0 && DateTime.Now >= timeOutTime)
+                {
+                    throw new TimeoutException("The sending attempt timed out");
+                }
+            }
+        }
+
+        /// <summary>
+        /// Sends the data segment to client.
+        /// </summary>
+        /// <param name="segment">The segment which will be sent.</param>
+        public virtual void Send(ArraySegment<byte> segment)
+        {
+            InternalSend(segment);
+        }
+
+        private bool InternalTrySend(IList<ArraySegment<byte>> segments)
+        {
+            if (!SocketSession.TrySend(segments))
+                return false;
+
+            LastActiveTime = DateTime.Now;
+            return true;
+        }
+
+        /// <summary>
+        /// Try to send the data segments to client.
+        /// </summary>
+        /// <param name="segments">The segments.</param>
+        /// <returns>Indicate whether the message was pushed into the sending queue; if it returns false, the sending queue may be full or the socket is not connected</returns>
+        public virtual bool TrySend(IList<ArraySegment<byte>> segments)
+        {
+            if (!m_Connected)
+                return false;
+
+            return InternalTrySend(segments);
+        }
+
+        private void InternalSend(IList<ArraySegment<byte>> segments)
+        {
+            if (!m_Connected)
+                return;
+
+            if (InternalTrySend(segments))
+                return;
+
+            var sendTimeOut = Config.SendTimeOut;
+
+            //Don't retry, timeout directly
+            if (sendTimeOut < 0)
+            {
+                throw new TimeoutException("The sending attempt timed out");
+            }
+
+            var timeOutTime = sendTimeOut > 0 ? DateTime.Now.AddMilliseconds(sendTimeOut) : DateTime.Now;
+
+            var spinWait = new SpinWait();
+
+            while (m_Connected)
+            {
+                spinWait.SpinOnce();
+
+                if (InternalTrySend(segments))
+                    return;
+
+                //If sendTimeOut = 0, don't have timeout check
+                if (sendTimeOut > 0 && DateTime.Now >= timeOutTime)
+                {
+                    throw new TimeoutException("The sending attempt timed out");
+                }
+            }
+        }
+
+        /// <summary>
+        /// Sends the data segments to client.
+        /// </summary>
+        /// <param name="segments">The segments.</param>
+        public virtual void Send(IList<ArraySegment<byte>> segments)
+        {
+            InternalSend(segments);
+        }
+
+        /// <summary>
+        /// Sends the response.
+        /// </summary>
+        /// <param name="message">The message which will be sent.</param>
+        /// <param name="paramValues">The parameter values.</param>
+        public virtual void Send(string message, params object[] paramValues)
+        {
+            var data = this.Charset.GetBytes(string.Format(message, paramValues));
+            InternalSend(new ArraySegment<byte>(data, 0, data.Length));
+        }
+
+        #endregion
+
+        #region Receiving processing
+
+        /// <summary>
+        /// Sets the next Receive filter which will be used when next data block received
+        /// </summary>
+        /// <param name="nextReceiveFilter">The next receive filter.</param>
+        protected void SetNextReceiveFilter(IReceiveFilter<TRequestInfo> nextReceiveFilter)
+        {
+            m_ReceiveFilter = nextReceiveFilter;
+        }
+
+        /// <summary>
+        /// Gets the maximum allowed length of the request.
+        /// </summary>
+        /// <returns></returns>
+        protected virtual int GetMaxRequestLength()
+        {
+            return AppServer.Config.MaxRequestLength;
+        }
+
+        /// <summary>
+        /// Filters the request.
+        /// </summary>
+        /// <param name="readBuffer">The read buffer.</param>
+        /// <param name="offset">The offset.</param>
+        /// <param name="length">The length.</param>
+        /// <param name="toBeCopied">if set to <c>true</c> [to be copied].</param>
+        /// <param name="rest">The rest, the size of the data which has not been processed</param>
+        /// <param name="offsetDelta">return offset delta of next receiving buffer.</param>
+        /// <returns></returns>
+        TRequestInfo FilterRequest(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest, out int offsetDelta)
+        {
+            if (!AppServer.OnRawDataReceived(this, readBuffer, offset, length))
+            {
+                rest = 0;
+                offsetDelta = 0;
+                return null;
+            }
+
+            var currentRequestLength = m_ReceiveFilter.LeftBufferSize;
+
+            var requestInfo = m_ReceiveFilter.Filter(readBuffer, offset, length, toBeCopied, out rest);
+
+            if (m_ReceiveFilter.State == FilterState.Error)
+            {
+                rest = 0;
+                offsetDelta = 0;
+                Close(CloseReason.ProtocolError);
+                return null;
+            }
+
+            var offsetAdapter = m_ReceiveFilter as IOffsetAdapter;
+
+            offsetDelta = offsetAdapter != null ? offsetAdapter.OffsetDelta : 0;
+
+            if (requestInfo == null)
+            {
+                //current buffered length
+                currentRequestLength = m_ReceiveFilter.LeftBufferSize;
+            }
+            else
+            {
+                //current request length
+                currentRequestLength = currentRequestLength + length - rest;
+            }
+
+            var maxRequestLength = GetMaxRequestLength();
+
+            if (currentRequestLength >= maxRequestLength)
+            {
+                if (Logger.IsErrorEnabled)
+                    Logger.Error(this, string.Format("Max request length: {0}, current processed length: {1}", maxRequestLength, currentRequestLength));
+
+                Close(CloseReason.ProtocolError);
+                return null;
+            }
+
+            //If next Receive filter wasn't set, still use current Receive filter in next round received data processing
+            if (m_ReceiveFilter.NextReceiveFilter != null)
+                m_ReceiveFilter = m_ReceiveFilter.NextReceiveFilter;
+
+            return requestInfo;
+        }
+
+        /// <summary>
+        /// Processes the request data.
+        /// </summary>
+        /// <param name="readBuffer">The read buffer.</param>
+        /// <param name="offset">The offset.</param>
+        /// <param name="length">The length.</param>
+        /// <param name="toBeCopied">if set to <c>true</c> [to be copied].</param>
+        /// <returns>
+        /// return offset delta of next receiving buffer
+        /// </returns>
+        int IAppSession.ProcessRequest(byte[] readBuffer, int offset, int length, bool toBeCopied)
+        {
+            int rest, offsetDelta;
+
+            while (true)
+            {
+                var requestInfo = FilterRequest(readBuffer, offset, length, toBeCopied, out rest, out offsetDelta);
+
+                if (requestInfo != null)
+                {
+                    try
+                    {
+                        AppServer.ExecuteCommand(this, requestInfo);
+                    }
+                    catch (Exception e)
+                    {
+                        HandleException(e);
+                    }
+                }
+
+                if (rest <= 0)
+                {
+                    return offsetDelta;
+                }
+
+                //Still have data has not been processed
+                offset = offset + length - rest;
+                length = rest;
+            }
+        }
+
+        #endregion
+    }
+
+    /// <summary>
+    /// AppServer basic class for whose request infoe type is StringRequestInfo
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    public abstract class AppSession<TAppSession> : AppSession<TAppSession, StringRequestInfo>
+        where TAppSession : AppSession<TAppSession, StringRequestInfo>, IAppSession, new()
+    {
+
+        private bool m_AppendNewLineForResponse = false;
+
+        private static string m_NewLine = "\r\n";
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppSession&lt;TAppSession&gt;"/> class.
+        /// </summary>
+        public AppSession()
+            : this(true)
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AppSession&lt;TAppSession&gt;"/> class.
+        /// </summary>
+        /// <param name="appendNewLineForResponse">if set to <c>true</c> [append new line for response].</param>
+        public AppSession(bool appendNewLineForResponse)
+        {
+            m_AppendNewLineForResponse = appendNewLineForResponse;
+        }
+
+        /// <summary>
+        /// Handles the unknown request.
+        /// </summary>
+        /// <param name="requestInfo">The request info.</param>
+        protected override void HandleUnknownRequest(StringRequestInfo requestInfo)
+        {
+            Send("Unknown request: " + requestInfo.Key);
+        }
+
+        /// <summary>
+        /// Processes the sending message.
+        /// </summary>
+        /// <param name="rawMessage">The raw message.</param>
+        /// <returns></returns>
+        protected virtual string ProcessSendingMessage(string rawMessage)
+        {
+            if (!m_AppendNewLineForResponse)
+                return rawMessage;
+
+            if (AppServer.Config.Mode == SocketMode.Udp)
+                return rawMessage;
+
+            if (string.IsNullOrEmpty(rawMessage) || !rawMessage.EndsWith(m_NewLine))
+                return rawMessage + m_NewLine;
+            else
+                return rawMessage;
+        }
+
+        /// <summary>
+        /// Sends the specified message.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <returns></returns>
+        public override void Send(string message)
+        {
+            base.Send(ProcessSendingMessage(message));
+        }
+
+        /// <summary>
+        /// Sends the response.
+        /// </summary>
+        /// <param name="message">The message.</param>
+        /// <param name="paramValues">The param values.</param>
+        /// <returns>Indicate whether the message was pushed into the sending queue</returns>
+        public override void Send(string message, params object[] paramValues)
+        {
+            base.Send(ProcessSendingMessage(message), paramValues);
+        }
+    }
+
+    /// <summary>
+    /// AppServer basic class for whose request infoe type is StringRequestInfo
+    /// </summary>
+    public class AppSession : AppSession<AppSession>
+    {
+
+    }
+}

+ 143 - 0
SocketBase/Async.cs

@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SuperSocket.SocketBase.Logging;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// Async extension class
+    /// </summary>
+    public static class Async
+    {
+        /// <summary>
+        /// Runs the specified task.
+        /// </summary>
+        /// <param name="logProvider">The log provider.</param>
+        /// <param name="task">The task.</param>
+        /// <returns></returns>
+        public static Task AsyncRun(this ILoggerProvider logProvider, Action task)
+        {
+            return AsyncRun(logProvider, task, TaskCreationOptions.None);
+        }
+
+        /// <summary>
+        /// Runs the specified task.
+        /// </summary>
+        /// <param name="logProvider">The log provider.</param>
+        /// <param name="task">The task.</param>
+        /// <param name="taskOption">The task option.</param>
+        /// <returns></returns>
+        public static Task AsyncRun(this ILoggerProvider logProvider, Action task, TaskCreationOptions taskOption)
+        {
+            return AsyncRun(logProvider, task, taskOption, null);
+        }
+
+        /// <summary>
+        /// Runs the specified task.
+        /// </summary>
+        /// <param name="logProvider">The log provider.</param>
+        /// <param name="task">The task.</param>
+        /// <param name="exceptionHandler">The exception handler.</param>
+        /// <returns></returns>
+        public static Task AsyncRun(this ILoggerProvider logProvider, Action task, Action<Exception> exceptionHandler)
+        {
+            return AsyncRun(logProvider, task, TaskCreationOptions.None, exceptionHandler);
+        }
+
+        /// <summary>
+        /// Runs the specified task.
+        /// </summary>
+        /// <param name="logProvider">The log provider.</param>
+        /// <param name="task">The task.</param>
+        /// <param name="taskOption">The task option.</param>
+        /// <param name="exceptionHandler">The exception handler.</param>
+        /// <returns></returns>
+        public static Task AsyncRun(this ILoggerProvider logProvider, Action task, TaskCreationOptions taskOption, Action<Exception> exceptionHandler)
+        {
+            return Task.Factory.StartNew(task, taskOption).ContinueWith(t =>
+                {
+                    if (exceptionHandler != null)
+                        exceptionHandler(t.Exception);
+                    else
+                    {
+                        if (logProvider.Logger.IsErrorEnabled)
+                        {
+                            for (var i = 0; i < t.Exception.InnerExceptions.Count; i++)
+                            {
+                                logProvider.Logger.Error(t.Exception.InnerExceptions[i]);
+                            }
+                        }
+                    }
+                }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+
+        /// <summary>
+        /// Runs the specified task.
+        /// </summary>
+        /// <param name="logProvider">The log provider.</param>
+        /// <param name="task">The task.</param>
+        /// <param name="state">The state.</param>
+        /// <returns></returns>
+        public static Task AsyncRun(this ILoggerProvider logProvider, Action<object> task, object state)
+        {
+            return AsyncRun(logProvider, task, state, TaskCreationOptions.None);
+        }
+
+        /// <summary>
+        /// Runs the specified task.
+        /// </summary>
+        /// <param name="logProvider">The log provider.</param>
+        /// <param name="task">The task.</param>
+        /// <param name="state">The state.</param>
+        /// <param name="taskOption">The task option.</param>
+        /// <returns></returns>
+        public static Task AsyncRun(this ILoggerProvider logProvider, Action<object> task, object state, TaskCreationOptions taskOption)
+        {
+            return AsyncRun(logProvider, task, state, taskOption, null);
+        }
+
+        /// <summary>
+        /// Runs the specified task.
+        /// </summary>
+        /// <param name="logProvider">The log provider.</param>
+        /// <param name="task">The task.</param>
+        /// <param name="state">The state.</param>
+        /// <param name="exceptionHandler">The exception handler.</param>
+        /// <returns></returns>
+        public static Task AsyncRun(this ILoggerProvider logProvider, Action<object> task, object state, Action<Exception> exceptionHandler)
+        {
+            return AsyncRun(logProvider, task, state, TaskCreationOptions.None, exceptionHandler);
+        }
+
+        /// <summary>
+        /// Runs the specified task.
+        /// </summary>
+        /// <param name="logProvider">The log provider.</param>
+        /// <param name="task">The task.</param>
+        /// <param name="state">The state.</param>
+        /// <param name="taskOption">The task option.</param>
+        /// <param name="exceptionHandler">The exception handler.</param>
+        /// <returns></returns>
+        public static Task AsyncRun(this ILoggerProvider logProvider, Action<object> task, object state, TaskCreationOptions taskOption, Action<Exception> exceptionHandler)
+        {
+            return Task.Factory.StartNew(task, state, taskOption).ContinueWith(t =>
+            {
+                if (exceptionHandler != null)
+                    exceptionHandler(t.Exception);
+                else
+                {
+                    if (logProvider.Logger.IsErrorEnabled)
+                    {
+                        for (var i = 0; i < t.Exception.InnerExceptions.Count; i++)
+                        {
+                            logProvider.Logger.Error(t.Exception.InnerExceptions[i]);
+                        }
+                    }
+                }
+            }, TaskContinuationOptions.OnlyOnFaulted);
+        }
+    }
+}

+ 53 - 0
SocketBase/Command/CommandBase.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.SocketBase.Protocol;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// Command base class
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    public abstract class CommandBase<TAppSession, TRequestInfo> : ICommand<TAppSession, TRequestInfo>
+        where TAppSession : IAppSession, IAppSession<TAppSession, TRequestInfo>, new()
+        where TRequestInfo : IRequestInfo
+    {
+
+        #region ICommand<TAppSession,TRequestInfo> Members
+
+        /// <summary>
+        /// Executes the command.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="requestInfo">The request info.</param>
+        public abstract void ExecuteCommand(TAppSession session, TRequestInfo requestInfo);
+
+        #endregion
+
+        #region ICommand Members
+
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        public virtual string Name
+        {
+            get { return this.GetType().Name; }
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Returns a <see cref="System.String" /> that represents this instance.
+        /// </summary>
+        /// <returns>
+        /// A <see cref="System.String" /> that represents this instance.
+        /// </returns>
+        public override string ToString()
+        {
+            return this.GetType().AssemblyQualifiedName;
+        }
+    }
+}

+ 39 - 0
SocketBase/Command/CommandInfo.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.SocketBase.Metadata;
+
+namespace SuperSocket.SocketBase.Command
+{
+    class CommandInfo<TCommand>
+        where TCommand : ICommand
+    {
+        public TCommand Command { get; private set; }
+
+        public CommandFilterAttribute[] Filters { get; private set; }
+
+        public CommandInfo(TCommand command, IEnumerable<CommandFilterAttribute> globalFilters)
+        {
+            Command = command;
+
+            var allFilters = new List<CommandFilterAttribute>();
+
+            if (globalFilters != null && globalFilters.Any())
+            {
+                allFilters.AddRange(globalFilters);
+            }
+
+            IEnumerable<CommandFilterAttribute> filters = command is ICommandFilterProvider ?
+                (command as ICommandFilterProvider).GetFilters() : AppServer.GetCommandFilterAttributes(command.GetType());
+
+            if (filters != null && filters.Any())
+                allFilters.AddRange(filters);
+
+            if (allFilters.Any())
+            {
+                Filters = allFilters.OrderBy(f => f.Order).ToArray();
+            }
+        }
+    }
+}

+ 74 - 0
SocketBase/Command/CommandLoaderBase.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Config;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// CommandLoader base class
+    /// </summary>
+    public abstract class CommandLoaderBase<TCommand> : ICommandLoader<TCommand>
+        where TCommand : ICommand
+    {
+        /// <summary>
+        /// Initializes the command loader by the root config and appserver instance.
+        /// </summary>
+        /// <param name="rootConfig">The root config.</param>
+        /// <param name="appServer">The app server.</param>
+        /// <returns></returns>
+        public abstract bool Initialize(IRootConfig rootConfig, IAppServer appServer);
+
+        /// <summary>
+        /// Tries to load commands.
+        /// </summary>
+        /// <param name="commands">The commands.</param>
+        /// <returns></returns>
+        public abstract bool TryLoadCommands(out IEnumerable<TCommand> commands);
+
+        /// <summary>
+        /// Called when [updated].
+        /// </summary>
+        /// <param name="commands">The commands.</param>
+        protected void OnUpdated(IEnumerable<CommandUpdateInfo<TCommand>> commands)
+        {
+            var handler = Updated;
+
+            if (handler != null)
+                handler(this, new CommandUpdateEventArgs<TCommand>(commands));
+        }
+
+        /// <summary>
+        /// Occurs when [updated].
+        /// </summary>
+        public event EventHandler<CommandUpdateEventArgs<TCommand>> Updated;
+
+        /// <summary>
+        /// Called when [error].
+        /// </summary>
+        /// <param name="message">The message.</param>
+        protected void OnError(string message)
+        {
+            OnError(new Exception(message));
+        }
+
+        /// <summary>
+        /// Called when [error].
+        /// </summary>
+        /// <param name="e">The e.</param>
+        protected void OnError(Exception e)
+        {
+            var handler = Error;
+
+            if (handler != null)
+                handler(this, new ErrorEventArgs(e));
+        }
+
+        /// <summary>
+        /// Occurs when [error].
+        /// </summary>
+        public event EventHandler<ErrorEventArgs> Error;
+    }
+}

+ 28 - 0
SocketBase/Command/CommandUpdateEventArgs.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// CommandUpdateEventArgs
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class CommandUpdateEventArgs<T> : EventArgs
+    {
+        /// <summary>
+        /// Gets the commands updated.
+        /// </summary>
+        public IEnumerable<CommandUpdateInfo<T>> Commands { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CommandUpdateEventArgs&lt;T&gt;"/> class.
+        /// </summary>
+        /// <param name="commands">The commands.</param>
+        public CommandUpdateEventArgs(IEnumerable<CommandUpdateInfo<T>> commands)
+        {
+            Commands = commands;
+        }
+    }
+}

+ 51 - 0
SocketBase/Command/CommandUpdateInfo.cs

@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// Command update action enum
+    /// </summary>
+    public enum CommandUpdateAction
+    {
+        /// <summary>
+        /// Add command
+        /// </summary>
+        Add,
+
+        /// <summary>
+        /// Remove command
+        /// </summary>
+        Remove,
+
+        /// <summary>
+        /// Update command
+        /// </summary>
+        Update
+    }
+
+    /// <summary>
+    /// Command update information
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class CommandUpdateInfo<T>
+    {
+        /// <summary>
+        /// Gets or sets the update action.
+        /// </summary>
+        /// <value>
+        /// The update action.
+        /// </value>
+        public CommandUpdateAction UpdateAction { get; set; }
+
+        /// <summary>
+        /// Gets or sets the target command.
+        /// </summary>
+        /// <value>
+        /// The command.
+        /// </value>
+        public T Command { get; set; }
+    }
+}

+ 70 - 0
SocketBase/Command/ICommand.cs

@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.SocketBase.Protocol;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// Command basic interface
+    /// </summary>
+    public interface ICommand
+    {
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        string Name { get; }
+    }
+
+    /// <summary>
+    /// Command basic interface
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    public interface ICommand<TAppSession, TRequestInfo> : ICommand
+        where TRequestInfo : IRequestInfo
+        where TAppSession : IAppSession
+    {
+        /// <summary>
+        /// Executes the command.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="requestInfo">The request info.</param>
+        void ExecuteCommand(TAppSession session, TRequestInfo requestInfo);
+    }
+
+    /// <summary>
+    /// Mockup command
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    public class MockupCommand<TAppSession, TRequestInfo> : ICommand<TAppSession, TRequestInfo>
+        where TRequestInfo : IRequestInfo
+        where TAppSession : IAppSession
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="MockupCommand&lt;TAppSession, TRequestInfo&gt;"/> class.
+        /// </summary>
+        /// <param name="name">The name.</param>
+        public MockupCommand(string name)
+        {
+            Name = name;
+        }
+
+        /// <summary>
+        /// Executes the command.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="requestInfo">The request info.</param>
+        public void ExecuteCommand(TAppSession session, TRequestInfo requestInfo)
+        {
+
+        }
+
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        public string Name { get; private set; }
+    }
+}

+ 20 - 0
SocketBase/Command/ICommandFilterProvider.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.SocketBase.Metadata;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// The basic interface for CommandFilter
+    /// </summary>
+    public interface ICommandFilterProvider
+    {
+        /// <summary>
+        /// Gets the filters which assosiated with this command object.
+        /// </summary>
+        /// <returns></returns>
+        IEnumerable<CommandFilterAttribute> GetFilters();
+    }
+}

+ 51 - 0
SocketBase/Command/ICommandLoader.cs

@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Protocol;
+using SuperSocket.SocketBase.Config;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// the empty basic interface for command loader
+    /// </summary>
+    public interface ICommandLoader
+    {
+
+    }
+
+    /// <summary>
+    /// Command loader's interface
+    /// </summary>
+    public interface ICommandLoader<TCommand> : ICommandLoader
+        where TCommand : ICommand
+    {
+
+        /// <summary>
+        /// Initializes the command loader by the root config and the server instance.
+        /// </summary>
+        /// <param name="rootConfig">The root config.</param>
+        /// <param name="appServer">The app server.</param>
+        /// <returns></returns>
+        bool Initialize(IRootConfig rootConfig, IAppServer appServer);
+
+        /// <summary>
+        /// Tries to load commands.
+        /// </summary>
+        /// <param name="commands">The commands.</param>
+        /// <returns></returns>
+        bool TryLoadCommands(out IEnumerable<TCommand> commands);
+
+        /// <summary>
+        /// Occurs when [updated].
+        /// </summary>
+        event EventHandler<CommandUpdateEventArgs<TCommand>> Updated;
+
+        /// <summary>
+        /// Occurs when [error].
+        /// </summary>
+        event EventHandler<ErrorEventArgs> Error;
+    }
+}

+ 104 - 0
SocketBase/Command/ReflectCommandLoader.cs

@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Reflection;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Protocol;
+using SuperSocket.SocketBase.Config;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// A command loader which loads commands from assembly by reflection
+    /// </summary>
+    public class ReflectCommandLoader<TCommand> : CommandLoaderBase<TCommand>
+        where TCommand : class, ICommand
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ReflectCommandLoader{TCommand}"/> class.
+        /// </summary>
+        public ReflectCommandLoader()
+        {
+
+        }
+
+        private IAppServer m_AppServer;
+
+        /// <summary>
+        /// Initializes the command loader by the root config and the server instance.
+        /// </summary>
+        /// <param name="rootConfig">The root config.</param>
+        /// <param name="appServer">The app server.</param>
+        /// <returns></returns>
+        public override bool Initialize(IRootConfig rootConfig, IAppServer appServer)
+        {
+            m_AppServer = appServer;
+            return true;
+        }
+
+        /// <summary>
+        /// Tries to load commands.
+        /// </summary>
+        /// <param name="commands">The commands.</param>
+        /// <returns></returns>
+        public override bool TryLoadCommands(out IEnumerable<TCommand> commands)
+        {
+            commands = null;
+
+            var commandAssemblies = new List<Assembly>();
+
+            if (m_AppServer.GetType().Assembly != this.GetType().Assembly)
+                commandAssemblies.Add(m_AppServer.GetType().Assembly);
+
+            string commandAssembly = m_AppServer.Config.Options.GetValue("commandAssembly");
+
+            if (!string.IsNullOrEmpty(commandAssembly))
+            {
+                OnError("The configuration attribute 'commandAssembly' is not in used, please try to use the child node 'commandAssemblies' instead!");
+                return false;
+            }
+
+
+            if (m_AppServer.Config.CommandAssemblies != null && m_AppServer.Config.CommandAssemblies.Any())
+            {
+                try
+                {
+                    var definedAssemblies = AssemblyUtil.GetAssembliesFromStrings(m_AppServer.Config.CommandAssemblies.Select(a => a.Assembly).ToArray());
+
+                    if (definedAssemblies.Any())
+                        commandAssemblies.AddRange(definedAssemblies);
+                }
+                catch (Exception e)
+                {
+                    OnError(new Exception("Failed to load defined command assemblies!", e));
+                    return false;
+                }
+            }
+
+            if (!commandAssemblies.Any())
+            {
+                commandAssemblies.Add(Assembly.GetEntryAssembly());
+            }
+
+            var outputCommands = new List<TCommand>();
+
+            foreach (var assembly in commandAssemblies)
+            {
+                try
+                {
+                    outputCommands.AddRange(assembly.GetImplementedObjectsByInterface<TCommand>());
+                }
+                catch (Exception exc)
+                {
+                    OnError(new Exception(string.Format("Failed to get commands from the assembly {0}!", assembly.FullName), exc));
+                    return false;
+                }
+            }
+
+            commands = outputCommands;
+
+            return true;
+        }
+    }
+}

+ 26 - 0
SocketBase/Command/StringCommandBase.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.SocketBase.Protocol;
+
+namespace SuperSocket.SocketBase.Command
+{
+    /// <summary>
+    /// A command type for whose request info type is StringRequestInfo
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    public abstract class StringCommandBase<TAppSession> : CommandBase<TAppSession, StringRequestInfo>
+        where TAppSession : IAppSession, IAppSession<TAppSession, StringRequestInfo>, new()
+    {
+
+    }
+
+    /// <summary>
+    /// A command type for whose request info type is StringRequestInfo
+    /// </summary>
+    public abstract class StringCommandBase : StringCommandBase<AppSession>
+    {
+
+    }
+}

+ 67 - 0
SocketBase/CommandExecutingContext.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.SocketBase.Protocol;
+using SuperSocket.SocketBase.Command;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// Command Executing Context
+    /// </summary>
+    public class CommandExecutingContext
+    {
+        /// <summary>
+        /// Gets the session.
+        /// </summary>
+        public IAppSession Session { get; private set; }
+
+        /// <summary>
+        /// Gets the request info.
+        /// </summary>
+        public IRequestInfo RequestInfo { get; private set; }
+
+        /// <summary>
+        /// Gets the current command.
+        /// </summary>
+        public ICommand CurrentCommand { get; private set; }
+
+        /// <summary>
+        /// Gets the exception.
+        /// </summary>
+        /// <value>
+        /// The exception.
+        /// </value>
+        public Exception Exception { get; internal set; }
+
+        /// <summary>
+        /// Gets a value indicating whether [exception handled].
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if [exception handled]; otherwise, <c>false</c>.
+        /// </value>
+        public bool ExceptionHandled { get; internal set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this command executing is cancelled.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if cancel; otherwise, <c>false</c>.
+        /// </value>
+        public bool Cancel { get; set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CommandExecutingContext" /> class.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="requestInfo">The request info.</param>
+        /// <param name="command">The command.</param>
+        public void Initialize(IAppSession session, IRequestInfo requestInfo, ICommand command)
+        {
+            Session = session;
+            RequestInfo = requestInfo;
+            CurrentCommand = command;
+        }
+    }
+}

+ 63 - 0
SocketBase/Config/CertificateConfig.cs

@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Security.Cryptography.X509Certificates;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Certificate config model class
+    /// </summary>
+    [Serializable]
+    public class CertificateConfig : ICertificateConfig
+    {
+        #region ICertificateConfig Members
+
+        /// <summary>
+        /// Gets/sets the file path.
+        /// </summary>
+        public string FilePath { get; set; }
+
+        /// <summary>
+        /// Gets/sets the password.
+        /// </summary>
+        public string Password { get; set; }
+
+        /// <summary>
+        /// Gets/sets the the store where certificate locates.
+        /// </summary>
+        /// <value>
+        /// The name of the store.
+        /// </value>
+        public string StoreName { get; set; }
+
+        /// <summary>
+        /// Gets/sets the store location of the certificate.
+        /// </summary>
+        /// <value>
+        /// The store location.
+        /// </value>
+        public StoreLocation StoreLocation { get; set; }
+
+        /// <summary>
+        /// Gets/sets the thumbprint.
+        /// </summary>
+        public string Thumbprint { get; set; }
+
+        /// <summary>
+        /// Gets/sets a value indicating whether [client certificate required].
+        /// </summary>
+        /// <value>
+        /// <c>true</c> if [client certificate required]; otherwise, <c>false</c>.
+        /// </value>
+        public bool ClientCertificateRequired { get; set; }
+
+        /// <summary>
+        /// Gets/sets a value that will be used to instantiate the X509Certificate2 object in the CertificateManager
+        /// </summary>
+        public X509KeyStorageFlags KeyStorageFlags { get; set; }
+
+        #endregion
+    }
+}

+ 22 - 0
SocketBase/Config/CommandAssemblyConfig.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Command assembly config
+    /// </summary>
+    [Serializable]
+    public class CommandAssemblyConfig : ICommandAssemblyConfig
+    {
+        /// <summary>
+        /// Gets or sets the assembly name.
+        /// </summary>
+        /// <value>
+        /// The assembly.
+        /// </value>
+        public string Assembly { get; set; }
+    }
+}

+ 92 - 0
SocketBase/Config/ConfigurationSource.cs

@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.Common;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Poco configuration source
+    /// </summary>
+    [Serializable]
+    public class ConfigurationSource : RootConfig, IConfigurationSource
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ConfigurationSource"/> class.
+        /// </summary>
+        public ConfigurationSource()
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ConfigurationSource"/> class.
+        /// </summary>
+        /// <param name="source">The source.</param>
+        public ConfigurationSource(IConfigurationSource source)
+            : base(source)
+        {
+            if (source.Servers != null && source.Servers.Any())
+            {
+                this.Servers = source.Servers.Select(s => new ServerConfig(s)).ToArray();
+            }
+
+            if (source.ServerTypes != null && source.ServerTypes.Any())
+            {
+                this.ServerTypes = source.ServerTypes.Select(s => s.CopyPropertiesTo(new TypeProviderConfig())).ToArray();
+            }
+
+            if (source.ConnectionFilters != null && source.ConnectionFilters.Any())
+            {
+                this.ConnectionFilters = source.ConnectionFilters.Select(s => s.CopyPropertiesTo(new TypeProviderConfig())).ToArray();
+            }
+
+            if (source.LogFactories != null && source.LogFactories.Any())
+            {
+                this.LogFactories = source.LogFactories.Select(s => s.CopyPropertiesTo(new TypeProviderConfig())).ToArray();
+            }
+
+            if (source.ReceiveFilterFactories != null && source.ReceiveFilterFactories.Any())
+            {
+                this.ReceiveFilterFactories = source.ReceiveFilterFactories.Select(s => s.CopyPropertiesTo(new TypeProviderConfig())).ToArray();
+            }
+
+            if (source.CommandLoaders != null && source.CommandLoaders.Any())
+            {
+                this.CommandLoaders = source.CommandLoaders.Select(s => s.CopyPropertiesTo(new TypeProviderConfig())).ToArray();
+            }
+        }
+
+
+        /// <summary>
+        /// Gets the servers definitions.
+        /// </summary>
+        public IEnumerable<IServerConfig> Servers { get; set; }
+
+        /// <summary>
+        /// Gets/sets the server types definition.
+        /// </summary>
+        public IEnumerable<ITypeProvider> ServerTypes { get; set; }
+
+        /// <summary>
+        /// Gets/sets the connection filters definition.
+        /// </summary>
+        public IEnumerable<ITypeProvider> ConnectionFilters { get; set; }
+
+        /// <summary>
+        /// Gets/sets the log factories definition.
+        /// </summary>
+        public IEnumerable<ITypeProvider> LogFactories { get; set; }
+
+        /// <summary>
+        /// Gets/sets the Receive filter factories definition.
+        /// </summary>
+        public IEnumerable<ITypeProvider> ReceiveFilterFactories { get; set; }
+
+        /// <summary>
+        /// Gets/sets the command loaders definition.
+        /// </summary>
+        public IEnumerable<ITypeProvider> CommandLoaders { get; set; }
+    }
+}

+ 17 - 0
SocketBase/Config/HotUpdateAttribute.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// the attribute to mark which property of ServerConfig support hot update
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
+    public class HotUpdateAttribute : Attribute
+    {
+
+    }
+}

+ 59 - 0
SocketBase/Config/ICertificateConfig.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Security.Cryptography.X509Certificates;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Certificate configuration interface
+    /// </summary>
+    public interface ICertificateConfig
+    {
+        /// <summary>
+        /// Gets the file path.
+        /// </summary>
+        string FilePath { get; }
+
+        /// <summary>
+        /// Gets the password.
+        /// </summary>
+        string Password { get; }
+
+        /// <summary>
+        /// Gets the the store where certificate locates.
+        /// </summary>
+        /// <value>
+        /// The name of the store.
+        /// </value>
+        string StoreName { get; }
+
+        /// <summary>
+        /// Gets the thumbprint.
+        /// </summary>
+        string Thumbprint { get; }
+
+
+        /// <summary>
+        /// Gets the store location of the certificate.
+        /// </summary>
+        /// <value>
+        /// The store location.
+        /// </value>
+        StoreLocation StoreLocation { get; }
+
+
+        /// <summary>
+        /// Gets a value indicating whether [client certificate required].
+        /// </summary>
+        /// <value>
+        /// <c>true</c> if [client certificate required]; otherwise, <c>false</c>.
+        /// </value>
+        bool ClientCertificateRequired { get; }
+
+        /// <summary>
+        /// Gets a value that will be used to instantiate the X509Certificate2 object in the CertificateManager
+        /// </summary>
+        X509KeyStorageFlags KeyStorageFlags { get; }
+    }
+}

+ 21 - 0
SocketBase/Config/ICommandAssemblyConfig.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// The basic interface for command assembly config
+    /// </summary>
+    public interface ICommandAssemblyConfig
+    {
+        /// <summary>
+        /// Gets the assembly name.
+        /// </summary>
+        /// <value>
+        /// The assembly.
+        /// </value>
+        string Assembly { get; }
+    }
+}

+ 42 - 0
SocketBase/Config/IConfigurationSource.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Configuration source interface
+    /// </summary>
+    public interface IConfigurationSource : IRootConfig
+    {
+        /// <summary>
+        /// Gets the servers definitions.
+        /// </summary>
+        IEnumerable<IServerConfig> Servers { get; }
+
+        /// <summary>
+        /// Gets the appServer types definition.
+        /// </summary>
+        IEnumerable<ITypeProvider> ServerTypes { get; }
+
+        /// <summary>
+        /// Gets the connection filters definition.
+        /// </summary>
+        IEnumerable<ITypeProvider> ConnectionFilters { get; }
+
+        /// <summary>
+        /// Gets the log factories definition.
+        /// </summary>
+        IEnumerable<ITypeProvider> LogFactories { get; }
+
+        /// <summary>
+        /// Gets the Receive filter factories definition.
+        /// </summary>
+        IEnumerable<ITypeProvider> ReceiveFilterFactories { get; }
+
+        /// <summary>
+        /// Gets the command loaders definition.
+        /// </summary>
+        IEnumerable<ITypeProvider> CommandLoaders { get; }
+    }
+}

+ 33 - 0
SocketBase/Config/IListenerConfig.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// The listener configuration interface
+    /// </summary>
+    public interface IListenerConfig
+    {
+        /// <summary>
+        /// Gets the ip of listener
+        /// </summary>
+        string Ip { get; }
+
+        /// <summary>
+        /// Gets the port of listener
+        /// </summary>
+        int Port { get; }
+
+        /// <summary>
+        /// Gets the backlog.
+        /// </summary>
+        int Backlog { get; }
+
+        /// <summary>
+        /// Gets the security option, None/Default/Tls/Ssl/...
+        /// </summary>
+        string Security { get; }
+    }
+}

+ 22 - 0
SocketBase/Config/IRootConfig.Net45.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// IRootConfig, the part compatible with .Net 4.5 or higher
+    /// </summary>
+    public partial interface IRootConfig
+    {
+        /// <summary>
+        /// Gets the default culture for all server instances.
+        /// </summary>
+        /// <value>
+        /// The default culture.
+        /// </value>
+        string DefaultCulture { get; }
+    }
+}

+ 79 - 0
SocketBase/Config/IRootConfig.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Configuration;
+using System.Collections.Specialized;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// The root configuration interface
+    /// </summary>
+    public partial interface IRootConfig
+    {
+        /// <summary>
+        /// Gets the max working threads.
+        /// </summary>
+        int MaxWorkingThreads { get; }
+
+        /// <summary>
+        /// Gets the min working threads.
+        /// </summary>
+        int MinWorkingThreads { get; }
+
+        /// <summary>
+        /// Gets the max completion port threads.
+        /// </summary>
+        int MaxCompletionPortThreads { get; }
+
+        /// <summary>
+        /// Gets the min completion port threads.
+        /// </summary>
+        int MinCompletionPortThreads { get; }
+
+
+        /// <summary>
+        /// Gets a value indicating whether [disable performance data collector].
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if [disable performance data collector]; otherwise, <c>false</c>.
+        /// </value>
+        bool DisablePerformanceDataCollector { get; }
+
+        /// <summary>
+        /// Gets the performance data collect interval, in seconds.
+        /// </summary>
+        int PerformanceDataCollectInterval { get; }
+
+
+        /// <summary>
+        /// Gets the log factory name.
+        /// </summary>
+        /// <value>
+        /// The log factory.
+        /// </value>
+        string LogFactory { get; }
+
+
+        /// <summary>
+        /// Gets the isolation mode.
+        /// </summary>
+        IsolationMode Isolation { get; }
+
+
+        /// <summary>
+        /// Gets the option elements.
+        /// </summary>
+        NameValueCollection OptionElements { get; }
+
+        /// <summary>
+        /// Gets the child config.
+        /// </summary>
+        /// <typeparam name="TConfig">The type of the config.</typeparam>
+        /// <param name="childConfigName">Name of the child config.</param>
+        /// <returns></returns>
+        TConfig GetChildConfig<TConfig>(string childConfigName)
+            where TConfig : ConfigurationElement, new();
+    }
+}

+ 22 - 0
SocketBase/Config/IServerConfig.Net45.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// IServerConfig, the part compatible with .Net 4.5 or higher
+    /// </summary>
+    public partial interface IServerConfig
+    {
+        /// <summary>
+        /// Gets the default culture for this server.
+        /// </summary>
+        /// <value>
+        /// The default culture.
+        /// </value>
+        string DefaultCulture { get; }
+    }
+}

+ 272 - 0
SocketBase/Config/IServerConfig.cs

@@ -0,0 +1,272 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Configuration;
+using System.Security.Authentication;
+using System.Collections.Specialized;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Server instance configuation interface
+    /// </summary>
+    public partial interface IServerConfig
+    {
+        /// <summary>
+        /// Gets the name of the server type this appServer want to use.
+        /// </summary>
+        /// <value>
+        /// The name of the server type.
+        /// </value>
+        string ServerTypeName { get; }
+
+        /// <summary>
+        /// Gets the type definition of the appserver.
+        /// </summary>
+        /// <value>
+        /// The type of the server.
+        /// </value>
+        string ServerType { get; }
+
+        /// <summary>
+        /// Gets the Receive filter factory.
+        /// </summary>
+        string ReceiveFilterFactory { get; }
+
+        /// <summary>
+        /// Gets the ip.
+        /// </summary>
+        string Ip { get; }
+
+        /// <summary>
+        /// Gets the port.
+        /// </summary>
+        int Port { get; }
+
+        /// <summary>
+        /// Gets the options.
+        /// </summary>
+        NameValueCollection Options { get; }
+
+
+        /// <summary>
+        /// Gets the option elements.
+        /// </summary>
+        NameValueCollection OptionElements { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether this <see cref="IServerConfig"/> is disabled.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if disabled; otherwise, <c>false</c>.
+        /// </value>
+        bool Disabled { get; }
+
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        string Name { get; }
+
+        /// <summary>
+        /// Gets the mode.
+        /// </summary>
+        SocketMode Mode { get; }
+
+        /// <summary>
+        /// Gets the send time out.
+        /// </summary>
+        int SendTimeOut { get; }
+
+        /// <summary>
+        /// Gets the max connection number.
+        /// </summary>
+        int MaxConnectionNumber { get; }
+
+        /// <summary>
+        /// Gets the size of the receive buffer.
+        /// </summary>
+        /// <value>
+        /// The size of the receive buffer.
+        /// </value>
+        int ReceiveBufferSize { get; }
+
+        /// <summary>
+        /// Gets the size of the send buffer.
+        /// </summary>
+        /// <value>
+        /// The size of the send buffer.
+        /// </value>
+        int SendBufferSize { get; }
+
+
+        /// <summary>
+        /// Gets a value indicating whether sending is in synchronous mode.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if [sync send]; otherwise, <c>false</c>.
+        /// </value>
+        bool SyncSend { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether log command in log file.
+        /// </summary>
+        /// <value><c>true</c> if log command; otherwise, <c>false</c>.</value>
+        bool LogCommand { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether clear idle session.
+        /// </summary>
+        /// <value><c>true</c> if clear idle session; otherwise, <c>false</c>.</value>
+        bool ClearIdleSession { get; }
+
+        /// <summary>
+        /// Gets the clear idle session interval, in seconds.
+        /// </summary>
+        /// <value>The clear idle session interval.</value>
+        int ClearIdleSessionInterval { get; }
+
+
+        /// <summary>
+        /// Gets the idle session timeout time length, in seconds.
+        /// </summary>
+        /// <value>The idle session time out.</value>
+        int IdleSessionTimeOut { get; }
+
+        /// <summary>
+        /// Gets X509Certificate configuration.
+        /// </summary>
+        /// <value>X509Certificate configuration.</value>
+        ICertificateConfig Certificate { get; }
+
+
+        /// <summary>
+        /// Gets the security protocol, X509 certificate.
+        /// </summary>
+        string Security { get; }
+
+
+        /// <summary>
+        /// Gets the length of the max request.
+        /// </summary>
+        /// <value>
+        /// The length of the max request.
+        /// </value>
+        int MaxRequestLength { get; }
+
+
+        /// <summary>
+        /// Gets a value indicating whether [disable session snapshot].
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if [disable session snapshot]; otherwise, <c>false</c>.
+        /// </value>
+        bool DisableSessionSnapshot { get; }
+        /// <summary>
+        /// Gets the interval to taking snapshot for all live sessions.
+        /// </summary>
+        int SessionSnapshotInterval { get; }
+        
+        /// <summary>
+        /// Gets the connection filters used by this server instance.
+        /// </summary>
+        /// <value>
+        /// The connection filter's name list, seperated by comma
+        /// </value>
+        string ConnectionFilter { get; }
+
+        /// <summary>
+        /// Gets the command loader, multiple values should be separated by comma.
+        /// </summary>
+        string CommandLoader { get; }
+
+        /// <summary>
+        /// Gets the start keep alive time, in seconds
+        /// </summary>
+        int KeepAliveTime { get; }
+
+
+        /// <summary>
+        /// Gets the keep alive interval, in seconds.
+        /// </summary>
+        int KeepAliveInterval { get; }
+
+
+        /// <summary>
+        /// Gets the backlog size of socket listening.
+        /// </summary>
+        int ListenBacklog { get; }
+
+
+        /// <summary>
+        /// Gets the startup order of the server instance.
+        /// </summary>
+        int StartupOrder { get; }
+
+
+        /// <summary>
+        /// Gets the child config.
+        /// </summary>
+        /// <typeparam name="TConfig">The type of the config.</typeparam>
+        /// <param name="childConfigName">Name of the child config.</param>
+        /// <returns></returns>
+        TConfig GetChildConfig<TConfig>(string childConfigName)
+            where TConfig : ConfigurationElement, new();
+
+
+        /// <summary>
+        /// Gets the listeners' configuration.
+        /// </summary>
+        IEnumerable<IListenerConfig> Listeners { get; }
+
+        /// <summary>
+        /// Gets the log factory name.
+        /// </summary>
+        string LogFactory { get; }
+
+
+        /// <summary>
+        /// Gets the size of the sending queue.
+        /// </summary>
+        /// <value>
+        /// The size of the sending queue.
+        /// </value>
+        int SendingQueueSize { get; }
+
+
+
+        /// <summary>
+        /// Gets a value indicating whether [log basic session activity like connected and disconnected].
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if [log basic session activity]; otherwise, <c>false</c>.
+        /// </value>
+        bool LogBasicSessionActivity { get; }
+
+
+        /// <summary>
+        /// Gets a value indicating whether [log all socket exception].
+        /// </summary>
+        /// <value>
+        /// <c>true</c> if [log all socket exception]; otherwise, <c>false</c>.
+        /// </value>
+        bool LogAllSocketException { get; }
+
+
+        /// <summary>
+        /// Gets the default text encoding.
+        /// </summary>
+        /// <value>
+        /// The text encoding.
+        /// </value>
+        string TextEncoding { get; }
+
+
+        /// <summary>
+        /// Gets the command assemblies configuration.
+        /// </summary>
+        /// <value>
+        /// The command assemblies.
+        /// </value>
+        IEnumerable<ICommandAssemblyConfig> CommandAssemblies { get; }
+    }
+}

+ 22 - 0
SocketBase/Config/ITypeProvider.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// TypeProvider's interface
+    /// </summary>
+    public interface ITypeProvider
+    {
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        string Name { get; }
+
+        /// <summary>
+        /// Gets the type.
+        /// </summary>
+        string Type { get; }
+    }
+}

+ 43 - 0
SocketBase/Config/ListenerConfig.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Listener configuration model
+    /// </summary>
+    [Serializable]
+    public class ListenerConfig : IListenerConfig
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ListenerConfig"/> class.
+        /// </summary>
+        public ListenerConfig()
+        {
+            Backlog = 100;
+        }
+
+        /// <summary>
+        /// Gets the ip of listener
+        /// </summary>
+        public string Ip { get; set; }
+
+        /// <summary>
+        /// Gets the port of listener
+        /// </summary>
+        public int Port { get; set; }
+
+
+        /// <summary>
+        /// Gets the backlog.
+        /// </summary>
+        public int Backlog { get; set; }
+
+        /// <summary>
+        /// Gets/sets the security option, None/Default/Tls/Ssl/...
+        /// </summary>
+        public string Security { get; set; }
+    }
+}

+ 19 - 0
SocketBase/Config/RootConfig.Net45.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SuperSocket.SocketBase.Config
+{
+    public partial class RootConfig : IRootConfig
+    {
+        /// <summary>
+        /// Gets or sets the default culture.
+        /// </summary>
+        /// <value>
+        /// The default culture.
+        /// </value>
+        public string DefaultCulture { get; set; }
+    }
+}

+ 115 - 0
SocketBase/Config/RootConfig.cs

@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using SuperSocket.Common;
+using System.Collections.Specialized;
+using System.Configuration;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Root configuration model
+    /// </summary>
+    [Serializable]
+    public partial class RootConfig : IRootConfig
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RootConfig"/> class.
+        /// </summary>
+        /// <param name="rootConfig">The root config.</param>
+        public RootConfig(IRootConfig rootConfig)
+        {
+            rootConfig.CopyPropertiesTo(this);
+            this.OptionElements = rootConfig.OptionElements;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RootConfig"/> class.
+        /// </summary>
+        public RootConfig()
+        {
+            int maxWorkingThread, maxCompletionPortThreads;
+            ThreadPool.GetMaxThreads(out maxWorkingThread, out maxCompletionPortThreads);
+            MaxWorkingThreads = maxWorkingThread;
+            MaxCompletionPortThreads = maxCompletionPortThreads;
+
+            int minWorkingThread, minCompletionPortThreads;
+            ThreadPool.GetMinThreads(out minWorkingThread, out minCompletionPortThreads);
+            MinWorkingThreads = minWorkingThread;
+            MinCompletionPortThreads = minCompletionPortThreads;
+
+            PerformanceDataCollectInterval = 60;
+
+            Isolation = IsolationMode.None;
+        }
+
+        #region IRootConfig Members
+
+        /// <summary>
+        /// Gets/Sets the max working threads.
+        /// </summary>
+        public int MaxWorkingThreads { get; set; }
+
+        /// <summary>
+        /// Gets/sets the min working threads.
+        /// </summary>
+        public int MinWorkingThreads { get; set; }
+
+        /// <summary>
+        /// Gets/sets the max completion port threads.
+        /// </summary>
+        public int MaxCompletionPortThreads { get; set; }
+
+        /// <summary>
+        /// Gets/sets the min completion port threads.
+        /// </summary>
+        public int MinCompletionPortThreads { get; set; }
+
+        /// <summary>
+        /// Gets/sets the performance data collect interval, in seconds.
+        /// </summary>
+        public int PerformanceDataCollectInterval { get; set; }
+
+        /// <summary>
+        /// Gets/sets a value indicating whether [disable performance data collector].
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if [disable performance data collector]; otherwise, <c>false</c>.
+        /// </value>
+        public bool DisablePerformanceDataCollector { get; set; }
+
+        /// <summary>
+        /// Gets/sets the isolation mode.
+        /// </summary>
+        public IsolationMode Isolation { get; set; }
+
+        /// <summary>
+        /// Gets/sets the log factory name.
+        /// </summary>
+        /// <value>
+        /// The log factory.
+        /// </value>
+        public string LogFactory { get; set; }
+
+        /// <summary>
+        /// Gets/sets the option elements.
+        /// </summary>
+        public NameValueCollection OptionElements { get; set; }
+
+        /// <summary>
+        /// Gets the child config.
+        /// </summary>
+        /// <typeparam name="TConfig">The type of the config.</typeparam>
+        /// <param name="childConfigName">Name of the child config.</param>
+        /// <returns></returns>
+        public virtual TConfig GetChildConfig<TConfig>(string childConfigName)
+            where TConfig : ConfigurationElement, new()
+        {
+            return this.OptionElements.GetChildConfig<TConfig>(childConfigName);
+        }
+
+        #endregion
+    }
+}

+ 19 - 0
SocketBase/Config/ServerConfig.Net45.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SuperSocket.SocketBase.Config
+{
+    public partial class ServerConfig : IServerConfig
+    {
+        /// <summary>
+        /// Gets or sets the default culture.
+        /// </summary>
+        /// <value>
+        /// The default culture.
+        /// </value>
+        public string DefaultCulture { get; set; }
+    }
+}

+ 401 - 0
SocketBase/Config/ServerConfig.cs

@@ -0,0 +1,401 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Configuration;
+using System.Linq;
+using System.Security.Authentication;
+using System.Text;
+using SuperSocket.Common;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Server configruation model
+    /// </summary>
+    [Serializable]
+    public partial class ServerConfig : IServerConfig
+    {
+        /// <summary>
+        /// Default ReceiveBufferSize
+        /// </summary>
+        public const int DefaultReceiveBufferSize = 4096;
+
+        /// <summary>
+        /// Default MaxConnectionNumber
+        /// </summary>
+        public const int DefaultMaxConnectionNumber = 10000;
+
+
+        /// <summary>
+        /// Default sending queue size
+        /// </summary>
+        public const int DefaultSendingQueueSize = 5;
+
+        /// <summary>
+        /// Default MaxRequestLength
+        /// </summary>
+        public const int DefaultMaxRequestLength = 1024;
+
+
+        /// <summary>
+        /// Default send timeout value, in milliseconds
+        /// </summary>
+        public const int DefaultSendTimeout = 5000;
+
+
+        /// <summary>
+        /// Default clear idle session interval
+        /// </summary>
+        public const int DefaultClearIdleSessionInterval = 120;
+
+
+        /// <summary>
+        /// Default idle session timeout
+        /// </summary>
+        public const int DefaultIdleSessionTimeOut = 300;
+
+
+        /// <summary>
+        /// The default send buffer size
+        /// </summary>
+        public const int DefaultSendBufferSize = 2048;
+
+
+        /// <summary>
+        /// The default session snapshot interval
+        /// </summary>
+        public const int DefaultSessionSnapshotInterval = 5;
+
+        /// <summary>
+        /// The default keep alive time
+        /// </summary>
+        public const int DefaultKeepAliveTime = 600; // 60 * 10 = 10 minutes
+
+
+        /// <summary>
+        /// The default keep alive interval
+        /// </summary>
+        public const int DefaultKeepAliveInterval = 60; // 60 seconds
+
+
+        /// <summary>
+        /// The default listen backlog
+        /// </summary>
+        public const int DefaultListenBacklog = 100;
+
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ServerConfig"/> class.
+        /// </summary>
+        /// <param name="serverConfig">The server config.</param>
+        public ServerConfig(IServerConfig serverConfig)
+        {
+            serverConfig.CopyPropertiesTo(this);
+            
+            this.Options = serverConfig.Options;
+            this.OptionElements = serverConfig.OptionElements;
+
+            if (serverConfig.Certificate != null)
+                this.Certificate = serverConfig.Certificate.CopyPropertiesTo(new CertificateConfig());
+
+            if (serverConfig.Listeners != null && serverConfig.Listeners.Any())
+            {
+                this.Listeners = serverConfig.Listeners.Select(l => l.CopyPropertiesTo(new ListenerConfig())).OfType<ListenerConfig>().ToArray();
+            }
+
+            if (serverConfig.CommandAssemblies != null && serverConfig.CommandAssemblies.Any())
+            {
+                this.CommandAssemblies = serverConfig.CommandAssemblies.Select(c => c.CopyPropertiesTo(new CommandAssemblyConfig())).OfType<CommandAssemblyConfig>().ToArray();
+            }
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ServerConfig"/> class.
+        /// </summary>
+        public ServerConfig()
+        {
+            Security = "None";
+            MaxConnectionNumber = DefaultMaxConnectionNumber;
+            Mode = SocketMode.Tcp;
+            MaxRequestLength = DefaultMaxRequestLength;
+            KeepAliveTime = DefaultKeepAliveTime;
+            KeepAliveInterval = DefaultKeepAliveInterval;
+            ListenBacklog = DefaultListenBacklog;
+            ReceiveBufferSize = DefaultReceiveBufferSize;
+            SendingQueueSize = DefaultSendingQueueSize;
+            SendTimeOut = DefaultSendTimeout;
+            ClearIdleSessionInterval = DefaultClearIdleSessionInterval;
+            IdleSessionTimeOut = DefaultIdleSessionTimeOut;
+            SendBufferSize = DefaultSendBufferSize;
+            LogBasicSessionActivity = true;
+            SessionSnapshotInterval = DefaultSessionSnapshotInterval;
+        }
+
+        #region IServerConfig Members
+
+        /// <summary>
+        /// Gets/sets the name of the server type of this appServer want to use.
+        /// </summary>
+        /// <value>
+        /// The name of the server type.
+        /// </value>
+        public string ServerTypeName { get; set; }
+
+
+        /// <summary>
+        /// Gets/sets the type definition of the appserver.
+        /// </summary>
+        /// <value>
+        /// The type of the server.
+        /// </value>
+        public string ServerType { get; set; }
+
+        /// <summary>
+        /// Gets/sets the Receive filter factory.
+        /// </summary>
+        public string ReceiveFilterFactory { get; set; }
+
+        /// <summary>
+        /// Gets/sets the ip.
+        /// </summary>
+        public string Ip { get; set; }
+
+        /// <summary>
+        /// Gets/sets the port.
+        /// </summary>
+        public int Port { get; set; }
+
+        /// <summary>
+        /// Gets/sets the options.
+        /// </summary>
+        [HotUpdate]
+        public NameValueCollection Options { get; set; }
+
+        /// <summary>
+        /// Gets the option elements.
+        /// </summary>
+        [HotUpdate]
+        public NameValueCollection OptionElements { get; set; }
+
+        /// <summary>
+        /// Gets/sets a value indicating whether this <see cref="IServerConfig"/> is disabled.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if disabled; otherwise, <c>false</c>.
+        /// </value>
+        public bool Disabled { get; set; }
+
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gets/sets the mode.
+        /// </summary>
+        public SocketMode Mode { get; set; }
+
+        /// <summary>
+        /// Gets/sets the send time out.
+        /// </summary>
+        public int SendTimeOut { get; set; }
+
+        /// <summary>
+        /// Gets the max connection number.
+        /// </summary>
+        public int MaxConnectionNumber { get; set; }
+
+        /// <summary>
+        /// Gets the size of the receive buffer.
+        /// </summary>
+        /// <value>
+        /// The size of the receive buffer.
+        /// </value>
+        public int ReceiveBufferSize { get; set; }
+
+        /// <summary>
+        /// Gets the size of the send buffer.
+        /// </summary>
+        /// <value>
+        /// The size of the send buffer.
+        /// </value>
+        public int SendBufferSize { get; set; }
+
+
+        /// <summary>
+        /// Gets a value indicating whether sending is in synchronous mode.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if [sync send]; otherwise, <c>false</c>.
+        /// </value>
+        public bool SyncSend { get; set; }
+
+        /// <summary>
+        /// Gets/sets a value indicating whether log command in log file.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if log command; otherwise, <c>false</c>.
+        /// </value>
+        [HotUpdate]
+        public bool LogCommand { get; set; }
+
+        /// <summary>
+        /// Gets/sets a value indicating whether clear idle session.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if clear idle session; otherwise, <c>false</c>.
+        /// </value>
+        public bool ClearIdleSession { get; set; }
+
+        /// <summary>
+        /// Gets/sets the clear idle session interval, in seconds.
+        /// </summary>
+        /// <value>
+        /// The clear idle session interval.
+        /// </value>
+        public int ClearIdleSessionInterval { get; set; }
+
+        /// <summary>
+        /// Gets/sets the idle session timeout time length, in seconds.
+        /// </summary>
+        /// <value>
+        /// The idle session time out.
+        /// </value>
+        [HotUpdate]
+        public int IdleSessionTimeOut { get; set; }
+
+        /// <summary>
+        /// Gets/sets X509Certificate configuration.
+        /// </summary>
+        /// <value>
+        /// X509Certificate configuration.
+        /// </value>
+        public ICertificateConfig Certificate { get; set; }
+
+        /// <summary>
+        /// Gets/sets the security protocol, X509 certificate.
+        /// </summary>
+        public string Security { get; set; }
+
+        /// <summary>
+        /// Gets/sets the length of the max request.
+        /// </summary>
+        /// <value>
+        /// The length of the max request.
+        /// </value>
+        [HotUpdate]
+        public int MaxRequestLength { get; set; }
+
+        /// <summary>
+        /// Gets/sets a value indicating whether [disable session snapshot].
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if [disable session snapshot]; otherwise, <c>false</c>.
+        /// </value>
+        public bool DisableSessionSnapshot { get; set; }
+
+        /// <summary>
+        /// Gets/sets the interval to taking snapshot for all live sessions.
+        /// </summary>
+        public int SessionSnapshotInterval { get; set; }
+
+        /// <summary>
+        /// Gets/sets the connection filters used by this server instance.
+        /// </summary>
+        /// <value>
+        /// The connection filter's name list, seperated by comma
+        /// </value>
+        public string ConnectionFilter { get; set; }
+
+        /// <summary>
+        /// Gets the command loader, multiple values should be separated by comma.
+        /// </summary>
+        public string CommandLoader { get; set; }
+
+        /// <summary>
+        /// Gets/sets the start keep alive time, in seconds
+        /// </summary>
+        public int KeepAliveTime { get; set; }
+
+        /// <summary>
+        /// Gets/sets the keep alive interval, in seconds.
+        /// </summary>
+        public int KeepAliveInterval { get; set; }
+
+        /// <summary>
+        /// Gets the backlog size of socket listening.
+        /// </summary>
+        public int ListenBacklog { get; set; }
+
+        /// <summary>
+        /// Gets/sets the startup order of the server instance.
+        /// </summary>
+        public int StartupOrder { get; set; }
+
+        /// <summary>
+        /// Gets the child config.
+        /// </summary>
+        /// <typeparam name="TConfig">The type of the config.</typeparam>
+        /// <param name="childConfigName">Name of the child config.</param>
+        /// <returns></returns>
+        public virtual TConfig GetChildConfig<TConfig>(string childConfigName)
+            where TConfig : ConfigurationElement, new()
+        {
+            return this.OptionElements.GetChildConfig<TConfig>(childConfigName);
+        }
+
+        /// <summary>
+        /// Gets and sets the listeners' configuration.
+        /// </summary>
+        public IEnumerable<IListenerConfig> Listeners { get; set; }
+
+        /// <summary>
+        /// Gets/sets the log factory name.
+        /// </summary>
+        public string LogFactory { get; set; }
+
+        /// <summary>
+        /// Gets/sets the size of the sending queue.
+        /// </summary>
+        /// <value>
+        /// The size of the sending queue.
+        /// </value>
+        public int SendingQueueSize { get; set; }
+
+        /// <summary>
+        /// Gets a value indicating whether [log basic session activity like connected and disconnected].
+        /// </summary>
+        /// <value>
+        /// 	<c>true</c> if [log basic session activity]; otherwise, <c>false</c>.
+        /// </value>
+        [HotUpdate]
+        public bool LogBasicSessionActivity { get; set; }
+
+        /// <summary>
+        /// Gets/sets a value indicating whether [log all socket exception].
+        /// </summary>
+        /// <value>
+        /// <c>true</c> if [log all socket exception]; otherwise, <c>false</c>.
+        /// </value>
+        [HotUpdate]
+        public bool LogAllSocketException { get; set; }
+
+        /// <summary>
+        /// Gets/sets the default text encoding.
+        /// </summary>
+        /// <value>
+        /// The text encoding.
+        /// </value>
+        public string TextEncoding { get; set; }
+
+        /// <summary>
+        /// Gets the command assemblies configuration.
+        /// </summary>
+        /// <value>
+        /// The command assemblies.
+        /// </value>
+        public IEnumerable<ICommandAssemblyConfig> CommandAssemblies { get; set; }
+
+        #endregion
+    }
+}

+ 32 - 0
SocketBase/Config/TypeProvider.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Configuration;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Type provider configuration
+    /// </summary>
+    public class TypeProvider : ConfigurationElement, ITypeProvider
+    {
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        [ConfigurationProperty("name", IsRequired = true)]
+        public string Name
+        {
+            get { return this["name"] as string; }
+        }
+
+        /// <summary>
+        /// Gets the type.
+        /// </summary>
+        [ConfigurationProperty("type", IsRequired = true)]
+        public string Type
+        {
+            get { return this["type"] as string; }
+        }
+    }
+}

+ 59 - 0
SocketBase/Config/TypeProviderCollection.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Configuration;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// Type provider colletion configuration
+    /// </summary>
+    [ConfigurationCollection(typeof(TypeProvider))]
+    public class TypeProviderCollection : ConfigurationElementCollection, IEnumerable<ITypeProvider>
+    {
+        /// <summary>
+        /// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>.
+        /// </summary>
+        /// <returns>
+        /// A new <see cref="T:System.Configuration.ConfigurationElement"/>.
+        /// </returns>
+        protected override ConfigurationElement CreateNewElement()
+        {
+            return new TypeProvider() as ConfigurationElement;
+        }
+
+        /// <summary>
+        /// Gets the element key for a specified configuration element when overridden in a derived class.
+        /// </summary>
+        /// <param name="element">The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for.</param>
+        /// <returns>
+        /// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
+        /// </returns>
+        protected override object GetElementKey(ConfigurationElement element)
+        {
+            var provider = element as TypeProvider;
+
+            if (provider == null)
+                return null;
+
+            return provider.Name;
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through the collection.
+        /// </summary>
+        /// <returns>
+        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
+        /// </returns>
+        public new IEnumerator<ITypeProvider> GetEnumerator()
+        {
+            int count = base.Count;
+
+            for (int i = 0; i < count; i++)
+            {
+                yield return (ITypeProvider)base.BaseGet(i);
+            }
+        }
+    }
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů