Browse Source

加入專案檔案。

Jessica Tseng 3 years ago
commit
ced7e87756
100 changed files with 18398 additions and 0 deletions
  1. 63 0
      .gitattributes
  2. 261 0
      .gitignore
  3. 55 0
      EVCB_OCPP.Server.sln
  4. 89 0
      EVCB_OCPP.WSServer/App.config
  5. BIN
      EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Domain.dll
  6. BIN
      EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Packet.dll
  7. BIN
      EVCB_OCPP.WSServer/DLL/EVCB_OCPP20.Packet.dll
  8. BIN
      EVCB_OCPP.WSServer/DLL/SuperSocket.Common.dll
  9. BIN
      EVCB_OCPP.WSServer/DLL/SuperSocket.SocketBase.dll
  10. BIN
      EVCB_OCPP.WSServer/DLL/SuperSocket.SocketEngine.dll
  11. BIN
      EVCB_OCPP.WSServer/DLL/SuperWebSocket.dll
  12. 65 0
      EVCB_OCPP.WSServer/Dto/ChargingPrice.cs
  13. 20 0
      EVCB_OCPP.WSServer/Dto/ConnectorErrorStauts.cs
  14. 25 0
      EVCB_OCPP.WSServer/Dto/ErrorDetails.cs
  15. 21 0
      EVCB_OCPP.WSServer/Dto/ID_GetTxUserInfo.cs
  16. 37 0
      EVCB_OCPP.WSServer/Dto/StationFee.cs
  17. 18 0
      EVCB_OCPP.WSServer/Dto/TCCStationInfoDto.cs
  18. 16 0
      EVCB_OCPP.WSServer/Dto/TCCWeatherDto.cs
  19. 17 0
      EVCB_OCPP.WSServer/Dto/TransactionEnergy.cs
  20. 220 0
      EVCB_OCPP.WSServer/EVCB_OCPP.WSServer.csproj
  21. 6 0
      EVCB_OCPP.WSServer/GitVersion.yml
  22. 117 0
      EVCB_OCPP.WSServer/GlobalConfig.cs
  23. 24 0
      EVCB_OCPP.WSServer/Helper/Convertor.cs
  24. 155 0
      EVCB_OCPP.WSServer/Message/BasicMessageHandler.cs
  25. 1489 0
      EVCB_OCPP.WSServer/Message/CoreProfileHandler.cs
  26. 228 0
      EVCB_OCPP.WSServer/Message/FirmwareManagementProfileHandler.cs
  27. 109 0
      EVCB_OCPP.WSServer/Message/LocalAuthListManagementProfileHandler.cs
  28. 54 0
      EVCB_OCPP.WSServer/Message/MessageResult.cs
  29. 48 0
      EVCB_OCPP.WSServer/Message/NeedConfirmMessage.cs
  30. 341 0
      EVCB_OCPP.WSServer/Message/OCPP16MessageHandler.cs
  31. 297 0
      EVCB_OCPP.WSServer/Message/OCPP20MessageHandler.cs
  32. 86 0
      EVCB_OCPP.WSServer/Message/RemoteTriggerHandler.cs
  33. 125 0
      EVCB_OCPP.WSServer/Message/ReservationProfileHandler.cs
  34. 73 0
      EVCB_OCPP.WSServer/Message/SecurityProfileHandler.cs
  35. 208 0
      EVCB_OCPP.WSServer/Message/SmartChargingProfileHandler.cs
  36. 48 0
      EVCB_OCPP.WSServer/NLog.config
  37. 3531 0
      EVCB_OCPP.WSServer/NLog.xsd
  38. 46 0
      EVCB_OCPP.WSServer/Program.cs
  39. 38 0
      EVCB_OCPP.WSServer/Properties/AssemblyInfo.cs
  40. 2100 0
      EVCB_OCPP.WSServer/ProtalServer.cs
  41. 36 0
      EVCB_OCPP.WSServer/Service/BusinessServiceFactory.cs
  42. 365 0
      EVCB_OCPP.WSServer/Service/HttpClientService.cs
  43. 393 0
      EVCB_OCPP.WSServer/Service/LoadingBalanceService.cs
  44. 91 0
      EVCB_OCPP.WSServer/Service/LocalBusinessService.cs
  45. 204 0
      EVCB_OCPP.WSServer/Service/OuterBusinessService.cs
  46. 214 0
      EVCB_OCPP.WSServer/Service/OuterHttpClient.cs
  47. 18 0
      EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallCmd.cs
  48. 18 0
      EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallErrorCmd.cs
  49. 18 0
      EVCB_OCPP.WSServer/SuperSocket.Command/ProcessCallResultCmd.cs
  50. 123 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/ClientData.cs
  51. 441 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPLog.cs
  52. 31 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPLogFactory.cs
  53. 38 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPSubCommandParser.cs
  54. 322 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPSubProtocol.cs
  55. 206 0
      EVCB_OCPP.WSServer/SuperSocket.Protocol/OCPPWSServer.cs
  56. 27 0
      EVCB_OCPP.WSServer/packages.config
  57. 373 0
      SocketBase/AppServer.cs
  58. 159 0
      SocketBase/AppServerBase.ConfigHotUpdate.cs
  59. 51 0
      SocketBase/AppServerBase.Net45.cs
  60. 1723 0
      SocketBase/AppServerBase.cs
  61. 689 0
      SocketBase/AppSession.cs
  62. 143 0
      SocketBase/Async.cs
  63. 53 0
      SocketBase/Command/CommandBase.cs
  64. 39 0
      SocketBase/Command/CommandInfo.cs
  65. 74 0
      SocketBase/Command/CommandLoaderBase.cs
  66. 28 0
      SocketBase/Command/CommandUpdateEventArgs.cs
  67. 51 0
      SocketBase/Command/CommandUpdateInfo.cs
  68. 70 0
      SocketBase/Command/ICommand.cs
  69. 20 0
      SocketBase/Command/ICommandFilterProvider.cs
  70. 51 0
      SocketBase/Command/ICommandLoader.cs
  71. 104 0
      SocketBase/Command/ReflectCommandLoader.cs
  72. 26 0
      SocketBase/Command/StringCommandBase.cs
  73. 67 0
      SocketBase/CommandExecutingContext.cs
  74. 63 0
      SocketBase/Config/CertificateConfig.cs
  75. 22 0
      SocketBase/Config/CommandAssemblyConfig.cs
  76. 92 0
      SocketBase/Config/ConfigurationSource.cs
  77. 17 0
      SocketBase/Config/HotUpdateAttribute.cs
  78. 59 0
      SocketBase/Config/ICertificateConfig.cs
  79. 21 0
      SocketBase/Config/ICommandAssemblyConfig.cs
  80. 42 0
      SocketBase/Config/IConfigurationSource.cs
  81. 33 0
      SocketBase/Config/IListenerConfig.cs
  82. 22 0
      SocketBase/Config/IRootConfig.Net45.cs
  83. 79 0
      SocketBase/Config/IRootConfig.cs
  84. 22 0
      SocketBase/Config/IServerConfig.Net45.cs
  85. 272 0
      SocketBase/Config/IServerConfig.cs
  86. 22 0
      SocketBase/Config/ITypeProvider.cs
  87. 43 0
      SocketBase/Config/ListenerConfig.cs
  88. 19 0
      SocketBase/Config/RootConfig.Net45.cs
  89. 115 0
      SocketBase/Config/RootConfig.cs
  90. 19 0
      SocketBase/Config/ServerConfig.Net45.cs
  91. 401 0
      SocketBase/Config/ServerConfig.cs
  92. 32 0
      SocketBase/Config/TypeProvider.cs
  93. 59 0
      SocketBase/Config/TypeProviderCollection.cs
  94. 24 0
      SocketBase/Config/TypeProviderConfig.cs
  95. 62 0
      SocketBase/Extensions.cs
  96. 52 0
      SocketBase/IActiveConnector.cs
  97. 199 0
      SocketBase/IAppServer.cs
  98. 135 0
      SocketBase/IAppSession.cs
  99. 141 0
      SocketBase/IBootstrap.cs
  100. 35 0
      SocketBase/IConnectionFilter.cs

+ 63 - 0
.gitattributes

@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs     diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following 
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln       merge=binary
+#*.csproj    merge=binary
+#*.vbproj    merge=binary
+#*.vcxproj   merge=binary
+#*.vcproj    merge=binary
+#*.dbproj    merge=binary
+#*.fsproj    merge=binary
+#*.lsproj    merge=binary
+#*.wixproj   merge=binary
+#*.modelproj merge=binary
+#*.sqlproj   merge=binary
+#*.wwaproj   merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg   binary
+#*.png   binary
+#*.gif   binary
+
+###############################################################################
+# diff behavior for common document formats
+# 
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the 
+# entries below.
+###############################################################################
+#*.doc   diff=astextplain
+#*.DOC   diff=astextplain
+#*.docx  diff=astextplain
+#*.DOCX  diff=astextplain
+#*.dot   diff=astextplain
+#*.DOT   diff=astextplain
+#*.pdf   diff=astextplain
+#*.PDF   diff=astextplain
+#*.rtf   diff=astextplain
+#*.RTF   diff=astextplain

+ 261 - 0
.gitignore

@@ -0,0 +1,261 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+#*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc

+ 55 - 0
EVCB_OCPP.Server.sln

@@ -0,0 +1,55 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30413.136
+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
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.SocketBase.Net40", "SocketBase\SuperSocket.SocketBase.Net40.csproj", "{40B77789-EA11-4C05-8F52-86711D7BCAAF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.Common.Net40", "SocketCommon\SuperSocket.Common.Net40.csproj", "{A24F4D38-BA9C-4FD6-95B7-4980DE36131A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperSocket.SocketEngine.Net40", "SocketEngine\SuperSocket.SocketEngine.Net40.csproj", "{153FEF72-191C-43D9-BE71-2B351C7AC760}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperWebSocket.NET45", "SuperWebSocket\SuperWebSocket.NET45.csproj", "{2DC79E40-BB70-4F6A-B378-905F2FBC6E97}"
+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
+		{40B77789-EA11-4C05-8F52-86711D7BCAAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{40B77789-EA11-4C05-8F52-86711D7BCAAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{40B77789-EA11-4C05-8F52-86711D7BCAAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{40B77789-EA11-4C05-8F52-86711D7BCAAF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A24F4D38-BA9C-4FD6-95B7-4980DE36131A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{153FEF72-191C-43D9-BE71-2B351C7AC760}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{153FEF72-191C-43D9-BE71-2B351C7AC760}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{153FEF72-191C-43D9-BE71-2B351C7AC760}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{153FEF72-191C-43D9-BE71-2B351C7AC760}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2DC79E40-BB70-4F6A-B378-905F2FBC6E97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2DC79E40-BB70-4F6A-B378-905F2FBC6E97}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2DC79E40-BB70-4F6A-B378-905F2FBC6E97}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2DC79E40-BB70-4F6A-B378-905F2FBC6E97}.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

+ 89 - 0
EVCB_OCPP.WSServer/App.config

@@ -0,0 +1,89 @@
+<?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.2.216\SQLEXPRESS;initial catalog=StandardOCPP_ConnectionLog;;persist security info=True;user id=tccdev;password=tccdevtest;MultipleActiveResultSets=True;App=EntityFramework; Max Pool Size=500" providerName="System.Data.SqlClient" />
+     <add name="MainDBContext" connectionString="data source=172.1.2.216\SQLEXPRESS;initial catalog=StandardOCPP_Main;;persist security info=True;user id=tccdev;password=tccdevtest;MultipleActiveResultSets=True;App=EntityFramework; Max Pool Size=500" providerName="System.Data.SqlClient" />
+     <add name="MeterValueDBContext" connectionString="data source=172.1.2.216\SQLEXPRESS;initial catalog=StandardOCPP_MeterValue;;persist security info=True;user id=tccdev;password=tccdevtest;MultipleActiveResultSets=True;App=EntityFramework; Max Pool Size=500" providerName="System.Data.SqlClient" />
+     <add name="WebDBContext" connectionString="data source=172.1.2.216\SQLEXPRESS;initial catalog=StandardOCPP_Web;;persist security info=True;user id=tccdev;password=tccdevtest;MultipleActiveResultSets=True;App=EntityFramework; Max Pool Size=500" providerName="System.Data.SqlClient" />
+   </connectionStrings>
+  <appSettings>   
+     <add key="LocalAuthAPI" value="" />
+    <add key="WSPort" value="2012" />
+    <add key="WSSPort" value="2016" />  
+  </appSettings>
+  <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="phihongtest-net.pfx" password="y42j/4cj84" storeName="My" thumbprint="‎b4be1b6f0f4a4904f48d85c04f5c2b0005c64d76" 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>
+      <dependentAssembly>
+        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Extensions.Primitives" publicKeyToken="adb9793829ddae60" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-3.1.3.0" newVersion="3.1.3.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-3.1.3.0" newVersion="3.1.3.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-3.1.3.0" newVersion="3.1.3.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-3.1.3.0" newVersion="3.1.3.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-3.1.3.0" newVersion="3.1.3.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Extensions.DependencyInjection" publicKeyToken="adb9793829ddae60" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-3.1.3.0" newVersion="3.1.3.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Extensions.Logging" publicKeyToken="adb9793829ddae60" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-3.1.3.0" newVersion="3.1.3.0" />
+      </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.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
EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Domain.dll


BIN
EVCB_OCPP.WSServer/DLL/EVCB_OCPP.Packet.dll


BIN
EVCB_OCPP.WSServer/DLL/EVCB_OCPP20.Packet.dll


BIN
EVCB_OCPP.WSServer/DLL/SuperSocket.Common.dll


BIN
EVCB_OCPP.WSServer/DLL/SuperSocket.SocketBase.dll


BIN
EVCB_OCPP.WSServer/DLL/SuperSocket.SocketEngine.dll


BIN
EVCB_OCPP.WSServer/DLL/SuperWebSocket.dll


+ 65 - 0
EVCB_OCPP.WSServer/Dto/ChargingPrice.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Dto
+{
+    public class ChargingPrice
+    {
+        private string _StartTime = "";
+        private string _EndTime = "";
+        public string StartTime
+        {
+            set
+            {
+                _StartTime = value;
+                if(!value.Contains("M"))
+                {                    
+                    DateTime dt = new DateTime(2021, 01, 01, int.Parse(_StartTime.Split(':')[0]), int.Parse(_StartTime.Split(':')[1]), 0, DateTimeKind.Utc);
+                    _StartTime = dt.ToString("hh:mm tt", new CultureInfo("en-us"));
+                }
+               
+            }
+            get
+            { return _StartTime; }
+        }
+
+        public string EndTime
+        {
+            set
+            {
+                _EndTime = value;
+                if (!value.Contains("M"))
+                {
+                    DateTime dt = new DateTime(2021, 01, 01, int.Parse(_EndTime.Split(':')[0]), int.Parse(_EndTime.Split(':')[1]), 0, DateTimeKind.Utc);
+                    _EndTime = dt.ToString("hh:mm tt", new CultureInfo("en-us"));
+                }
+                
+            }
+            get
+            { return _EndTime; }
+        }
+
+        public decimal Fee { set; get; }
+    }
+
+
+    public class ChargingBill
+    {
+        public string StartTime { set; get; }
+
+        public string EndTime { set; get; }
+
+        public decimal Total { set; get; }
+
+        public decimal PeriodEnergy { set; get; }
+
+        /// <summary>
+        /// 費率
+        /// </summary>
+        public decimal Fee { set; get; }
+    }
+}

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

@@ -0,0 +1,20 @@
+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; }
+
+
+    }
+}

+ 25 - 0
EVCB_OCPP.WSServer/Dto/ErrorDetails.cs

@@ -0,0 +1,25 @@
+using EVCB_OCPP.Packet.Messages.SubTypes;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using System;
+
+namespace EVCB_OCPP.WSServer.Dto
+{
+    public class ErrorDetails
+    {
+        public string ChargeBoxId { set; get; }
+
+        public int ConnectorId { set; get; }
+
+        [JsonConverter(typeof(StringEnumConverter))]
+        public ChargePointErrorCode ErrorCode { set; get; }
+
+        public string Info { set; get; }
+
+        public string VendorId { set; get; }
+
+        public string VendorErrorCode { set; get; }
+
+        public DateTime OCcuredOn { set; get; }
+    }
+}

+ 21 - 0
EVCB_OCPP.WSServer/Dto/ID_GetTxUserInfo.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Dto
+{
+    public class ID_GetTxUserInfo
+    {
+        public string VEMData { set; get; }
+
+        public string TxId { set; get; }
+
+        public Guid? SerialNo { set; get; }
+
+        public int ConnectorId { set; get; }
+
+        public DateTime StartTime { set; get; }
+    }
+}

+ 37 - 0
EVCB_OCPP.WSServer/Dto/StationFee.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Dto
+{
+    public class StationFee
+    {
+        /// <summary>
+        /// 收費方式 1: 以度計費 2:以小時計費
+        /// </summary>
+        public int BillingMethod { set; get; }
+
+        /// <summary>
+        /// 電樁顯示用費率 
+        /// </summary>
+        public string FeeName { set; get; }
+
+        /// <summary>
+        /// 停車費
+        /// </summary>
+        public decimal ParkingFee { set; get; }
+
+        /// <summary>
+        /// 幣別
+        /// </summary>
+        public string Currency { set; get; }
+
+        /// <summary>
+        /// 充電費率 以小時計費
+        /// </summary>
+        public decimal ChargingFeebyHour { set; get; }
+
+    }
+}

+ 18 - 0
EVCB_OCPP.WSServer/Dto/TCCStationInfoDto.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.Dto
+{
+    public class TCCStationInfoDto
+    {
+        public decimal Lat { set; get; }
+
+        public decimal Long { set; get; }
+
+        public string ZipCode { set; get; }
+
+    }
+}

+ 16 - 0
EVCB_OCPP.WSServer/Dto/TCCWeatherDto.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.Dto
+{
+    public class TCCWeatherDto
+    {
+        public int WeatherID { set; get; }
+
+
+        public int Temperature { set; get; }
+    }
+}

+ 17 - 0
EVCB_OCPP.WSServer/Dto/TransactionEnergy.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Dto
+{
+    public class TransactionEnergy
+    {       
+
+        public int TxId { set; get; }
+
+
+        public Dictionary<string,decimal> PeriodEnergy { set; get; }
+    }
+}

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

@@ -0,0 +1,220 @@
+<?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="Dapper, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\Dapper.2.0.30\lib\net461\Dapper.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>
+    </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="EVCB_OCPP20.Packet, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>DLL\EVCB_OCPP20.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="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Configuration, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Configuration.3.1.3\lib\netstandard2.0\Microsoft.Extensions.Configuration.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Configuration.Abstractions.3.1.3\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Configuration.Binder, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Configuration.Binder.3.1.3\lib\netstandard2.0\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.DependencyInjection, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.DependencyInjection.3.1.3\lib\net461\Microsoft.Extensions.DependencyInjection.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.3.1.3\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Http, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Http.3.1.3\lib\netstandard2.0\Microsoft.Extensions.Http.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Logging, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Logging.3.1.3\lib\netstandard2.0\Microsoft.Extensions.Logging.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.3.1.3\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Options, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Options.3.1.3\lib\netstandard2.0\Microsoft.Extensions.Options.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Extensions.Primitives, Version=3.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Extensions.Primitives.3.1.3\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.12.0.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="System" />
+    <Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
+    </Reference>
+    <Reference Include="System.ComponentModel.Annotations, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.ComponentModel.Annotations.4.7.0\lib\net461\System.ComponentModel.Annotations.dll</HintPath>
+    </Reference>
+    <Reference Include="System.ComponentModel.DataAnnotations" />
+    <Reference Include="System.Configuration" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.IO.Compression" />
+    <Reference Include="System.Memory, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Memory.4.5.2\lib\netstandard2.0\System.Memory.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Numerics" />
+    <Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.ServiceModel" />
+    <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Transactions" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Dto\ChargingPrice.cs" />
+    <Compile Include="Dto\ErrorDetails.cs" />
+    <Compile Include="Dto\ID_GetTxUserInfo.cs" />
+    <Compile Include="Dto\StationFee.cs" />
+    <Compile Include="Dto\TCCWeatherDto.cs" />
+    <Compile Include="Dto\TransactionEnergy.cs" />
+    <Compile Include="Dto\TCCStationInfoDto.cs" />
+    <Compile Include="Message\OCPP16MessageHandler.cs" />
+    <Compile Include="Message\OCPP20MessageHandler.cs" />
+    <Compile Include="Message\SecurityProfileHandler.cs" />
+    <Compile Include="Service\BusinessServiceFactory.cs" />
+    <Compile Include="Service\HttpClientService.cs" />
+    <Compile Include="Service\LoadingBalanceService.cs" />
+    <Compile Include="Service\LocalBusinessService.cs" />
+    <Compile Include="Service\OuterBusinessService.cs" />
+    <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\FirmwareManagementProfileHandler.cs" />
+    <Compile Include="Message\LocalAuthListManagementProfileHandler.cs" />
+    <Compile Include="Message\MessageResult.cs" />
+    <Compile Include="Message\NeedConfirmMessage.cs" />
+    <Compile Include="Message\RemoteTriggerHandler.cs" />
+    <Compile Include="Message\ReservationProfileHandler.cs" />
+    <Compile Include="Message\SmartChargingProfileHandler.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ProtalServer.cs" />
+    <Compile Include="Service\OuterHttpClient.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">
+      <SubType>Designer</SubType>
+    </None>
+    <Content Include="DLL\EVCB_OCPP20.Packet.dll" />
+    <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>
+      <SubType>Designer</SubType>
+    </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>
+    <ProjectReference Include="..\SocketBase\SuperSocket.SocketBase.Net40.csproj">
+      <Project>{40b77789-ea11-4c05-8f52-86711d7bcaaf}</Project>
+      <Name>SuperSocket.SocketBase.Net40</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\SocketCommon\SuperSocket.Common.Net40.csproj">
+      <Project>{a24f4d38-ba9c-4fd6-95b7-4980de36131a}</Project>
+      <Name>SuperSocket.Common.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.NET45.csproj">
+      <Project>{2dc79e40-bb70-4f6a-b378-905f2fbc6e97}</Project>
+      <Name>SuperWebSocket.NET45</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PreBuildEvent>GitVersion.exe $(ProjectDir) /updateassemblyinfo</PreBuildEvent>
+  </PropertyGroup>
+</Project>

+ 6 - 0
EVCB_OCPP.WSServer/GitVersion.yml

@@ -0,0 +1,6 @@
+assembly-versioning-scheme: MajorMinorPatch
+assembly-informational-format: '{ShortSha}'
+next-version: 0.1.0
+branches: {}
+ignore:
+  sha: []

+ 117 - 0
EVCB_OCPP.WSServer/GlobalConfig.cs

@@ -0,0 +1,117 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+
+namespace EVCB_OCPP.WSServer
+{
+    public static class GlobalConfig
+    {
+        public static List<string> ConfigKeys = new List<string>()
+        {
+            "WSPort",
+            "WSSPort",
+            ""
+        };
+
+        public static string TCC_API_URL = string.Empty;
+
+        public static string TCC_SALTKEY = string.Empty;
+
+
+        /// <summary>
+        /// 預設心跳間隔時間 單位:秒
+        /// </summary>
+        private static int DEFAULT_HEARTBEAT_INTERVAL = 60;
+
+
+
+        /// <summary>
+        ///WS Port
+        /// </summary>
+        private static int WS_Port = 2012;
+
+
+        /// <summary>
+        ///WSS Port
+        /// </summary>
+        private static int WSS_Port = 2013;
+
+        /// <summary>
+        /// Load setting from app.config 
+        /// </summary>
+        public static bool LoadAPPConfig()
+        {
+            bool result = false;
+            string key = string.Empty;
+            try
+            {
+
+                for (int i = 0; i < ConfigKeys.Count; i++)
+                {
+                    key = ConfigKeys[i];
+                    switch (key)
+                    {
+                        case "WSPort":// convert to int type                       
+                            {
+                                var value = ConfigurationManager.AppSettings[key];
+
+                                WS_Port = Convert.ToInt32(value);
+                            }
+                            break;
+                        case "WSSPort":
+                            {
+                                var value = ConfigurationManager.AppSettings[key];
+
+                                WSS_Port = Convert.ToInt32(value);
+                            }
+                            break;
+                        default://convert to string type                             
+                            break;
+
+                    }
+                }
+                result = true;
+
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(key + " Load from APPConfig " + ex.ToString());
+
+            }
+
+            return result;
+        }
+
+
+
+        public static int GetWS_Port()
+        {
+            return WS_Port;
+        }
+
+
+        public static int GetWSS_Port()
+        {
+            return WSS_Port;
+        }
+
+        public static int GetHEARTBEAT_INTERVAL()
+        {
+            return DEFAULT_HEARTBEAT_INTERVAL;
+        }
+
+        public static readonly int DB_DefaultConnectionTimeout = 60;
+        public static readonly string UTC_DATETIMEFORMAT = "yyyy/MM/dd'T'HH':'mm':'ss'Z'";
+        /// <summary>
+        /// 預設 Null的 DateTime 
+        /// </summary>
+        public static DateTime DefaultNullTime = new DateTime(1991, 1, 1);
+
+        public static JsonSerializerSettings JSONSERIALIZER_FORMAT = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None };
+
+
+
+
+    }
+}

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

@@ -0,0 +1,24 @@
+using EVCB_OCPP.Packet.Features;
+using System;
+
+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;
+        }
+
+        public static EVCB_OCPP20.Packet.Features.Actions GetActionby20(string action)
+        {
+            EVCB_OCPP20.Packet.Features.Actions result = EVCB_OCPP20.Packet.Features.Actions.None;
+            Enum.TryParse<EVCB_OCPP20.Packet.Features.Actions>(action, out result);
+            return result;
+        }
+
+
+    }
+}

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

@@ -0,0 +1,155 @@
+using EVCB_OCPP.Packet.Features;
+using EVCB_OCPP.Packet.Messages;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using NLog;
+using OCPPServer.Protocol;
+
+
+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 OCPP16MessageHandler _ocpp16Handler = new OCPP16MessageHandler();
+        private OCPP20MessageHandler _ocpp20Handler = new OCPP20MessageHandler();
+
+        /// <summary>
+        /// 將收到的封包做基本的拆解分成 Call 、CallResult、CallError
+        /// </summary>
+        /// <param name="client"></param>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        internal MessageResult AnalysisReceiveData(ClientData client, string data)
+        {
+            MessageResult result = null;
+            if (!client.ISOCPP20)
+            {
+                result = _ocpp16Handler.AnalysisReceiveData(client, data);
+            }
+            else
+            {
+                result = _ocpp20Handler.AnalysisReceiveData(client, data);
+            }
+
+            return result;
+        }
+
+
+        static internal string GenerateCallError(string uniqueId, string errorCode, string errorDescription)
+        {
+
+            string msg = string.Format(CALLERROR_FORMAT, uniqueId, errorCode, errorDescription, "{}");
+            return msg;
+
+        }
+
+        static 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() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }));
+            }
+            else
+            {
+                logger.Error(string.Format("confirmation is null  or InVaild in GenerateConfirmation Method"), "Warning");
+            }
+            return msg;
+        }
+        static internal string GenerateConfirmationofOCPP20(string uniqueId, EVCB_OCPP20.Packet.Messages.IConfirmation confirmation)
+        {
+            string msg = string.Empty;
+            if (confirmation != null && confirmation.Validate())
+            {
+
+                msg = string.Format(CALLRESULT_FORMAT, uniqueId, JsonConvert.SerializeObject(confirmation, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }));
+            }
+            else
+            {
+                logger.Error(string.Format("confirmation is null  or InVaild in GenerateConfirmation Method"), "Warning");
+            }
+            return msg;
+        }
+
+        static 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() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }));
+            }
+            else
+            {
+                if (action == Actions.ChangeConfiguration.ToString())
+                {
+                    if(!request.Validate())
+                    {
+                        logger.Error("!Validate", "Warning");
+                    }
+
+                    if (request == null)
+                    {
+                        logger.Error("!NULL", "Warning");
+                    }
+
+                }
+
+                logger.Error(string.Format("confirmation is null  or InVaild in GenerateRequest Method "+ action), "Warning");
+            }
+            return msg;
+        }
+
+        static internal string GenerateRequestofOCPP20(string uniqueId, string action, EVCB_OCPP20.Packet.Messages.IRequest request)
+        {
+            string msg = string.Empty;
+            if (request != null && request.Validate())
+            {
+
+                msg = string.Format(CALL_FORMAT, uniqueId, action, JsonConvert.SerializeObject(request, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }));
+            }
+            else
+            {
+                logger.Error(string.Format("confirmation is null  or InVaild in GenerateRequest Method"), "Warning");
+            }
+            return msg;
+        }
+
+        static internal string GenerateDestroyRequest(string uniqueId, string action, string request)
+        {
+
+
+            return string.Format(CALL_FORMAT, uniqueId, action, request);
+        }
+    }
+}

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

@@ -0,0 +1,1489 @@
+using Dapper;
+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 EVCB_OCPP.WSServer.Service;
+using Newtonsoft.Json;
+using NLog;
+using OCPPPackage.Profiles;
+using OCPPServer.Protocol;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Data.Entity;
+using System.Data.SqlClient;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    public class ID_CreditDeductResult
+    {
+        public int txId { set; get; }
+
+        public string creditNo { set; get; }
+
+
+        public bool deductResult { set; get; }
+
+        public bool isDonateInvoice { set; get; }
+
+
+        public decimal amount { set; get; }
+
+        public string approvalNo { set; get; }
+
+    }
+
+    public class ID_ReaderStatus
+    {
+        public int ConnectorId { set; get; }
+
+        public string creditNo { set; get; }
+
+
+        public string SerialNo { set; get; }
+
+        public int readerStatus { set; get; }
+
+        public string VEMData { set; get; }
+
+        public DateTime Timestamp { set; get; }
+
+
+
+
+    }
+
+    internal partial class ProfileHandler
+    {
+        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        string webConnectionString = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
+        private OuterHttpClient httpClient = new OuterHttpClient();
+        async internal Task<MessageResult> ExecuteCoreRequest(Actions action, ClientData session, IRequest request)
+        {
+            Stopwatch watch = new Stopwatch();
+            if (action == Actions.Heartbeat)
+            {
+                watch.Start();
+            }
+            MessageResult result = new MessageResult() { Success = false };
+
+            try
+            {
+                switch (action)
+                {
+                    case Actions.DataTransfer:
+                        {
+                            DataTransferRequest _request = request as DataTransferRequest;
+                            var confirm = new DataTransferConfirmation() { status = DataTransferStatus.UnknownMessageId };
+
+                            if (_request.messageId == "ID_CreditDeductResult")
+                            {
+
+                                var creditDeductResult = JsonConvert.DeserializeObject<ID_CreditDeductResult>(_request.data);
+                                if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
+                                {
+                                    var report = new
+                                    {
+                                        ChargeBoxId = session.ChargeBoxId,
+                                        IsDonateInvoice = creditDeductResult.isDonateInvoice,
+                                        CreditNo = creditDeductResult.creditNo,
+                                        DeductResult = creditDeductResult.deductResult,
+                                        SessionId = creditDeductResult.txId,
+                                        ApprovalNo = creditDeductResult.approvalNo,
+                                        TotalCost = creditDeductResult.amount,
+
+                                    };
+                               
+                                    var response = await httpClient.Post(GlobalConfig.TCC_API_URL + "prepare_issue_invoice", new Dictionary<string, string>()
+                                        {
+                                            { "PartnerId",session.CustomerId.ToString()}
+
+                                        }, report, GlobalConfig.TCC_SALTKEY);
+
+                                    logger.Debug(JsonConvert.SerializeObject(response));
+                                }
+
+
+                                confirm.status = DataTransferStatus.Accepted;
+                                confirm.data = JsonConvert.SerializeObject(new { txId = creditDeductResult.txId, creditNo = creditDeductResult.creditNo, msgId = _request.messageId });
+                            }
+                            if (_request.messageId == "ID_ReaderStatus")
+                            {
+                                if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
+                                {
+                                    var preauth_status = JsonConvert.DeserializeObject<ID_ReaderStatus>(_request.data);
+                                    var report = new
+                                    {
+                                        ChargeBoxId = session.ChargeBoxId,
+                                        ConnectorId = preauth_status.ConnectorId,
+                                        CreditNo = preauth_status.creditNo,
+                                        ReaderStatus = preauth_status.readerStatus,
+                                        SerialNo = preauth_status.SerialNo,
+                                        VEMData = preauth_status.VEMData,
+                                        Timestamp = preauth_status.Timestamp
+
+                                    };
+
+                                    var response = await httpClient.Post(GlobalConfig.TCC_API_URL + "preauth_status", new Dictionary<string, string>()
+                                        {
+                                            { "PartnerId",session.CustomerId.ToString()}
+
+                                        }, report, GlobalConfig.TCC_SALTKEY);
+
+
+                                    confirm.status = DataTransferStatus.Accepted;
+                                }
+                            }
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                        break;
+                    case Actions.BootNotification:
+                        {
+                            BootNotificationRequest _request = request as BootNotificationRequest;
+                            int heartbeat_interval = GlobalConfig.GetHEARTBEAT_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.ChargePointSerialNumber = string.IsNullOrEmpty(_request.chargePointSerialNumber) ? string.Empty : _request.chargePointSerialNumber;
+                                _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.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
+                                _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.MachineConfiguration.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.UtcNow, interval = session.IsPending ? heartbeat_interval : 5, status = session.IsPending ? Packet.Messages.SubTypes.RegistrationStatus.Accepted : RegistrationStatus.Pending };
+
+                            session.IsPending = !session.IsPending;
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                        break;
+                    case Actions.StatusNotification:
+                        {
+                            //只保留最新上報狀況
+                            StatusNotificationRequest _request = request as StatusNotificationRequest;
+                            int preStatus = 0;
+                            using (var db = new MainDBContext())
+                            {
+                                var _oldStatus = db.ConnectorStatus.Where(x => x.ChargeBoxId == session.ChargeBoxId
+                              && x.ConnectorId == _request.connectorId).AsNoTracking().FirstOrDefault();
+
+
+                                if (_oldStatus != null && (_request.status != (ChargePointStatus)_oldStatus.Status || _request.status == ChargePointStatus.Faulted))
+                                {
+
+                                    preStatus = _oldStatus.Status;
+
+                                    db.Configuration.AutoDetectChangesEnabled = false;
+                                    db.Configuration.ValidateOnSaveEnabled = false;
+                                    db.ConnectorStatus.Attach(_oldStatus);
+
+
+                                    _oldStatus.CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow;
+                                    _oldStatus.Status = (int)_request.status;
+                                    _oldStatus.ChargePointErrorCodeId = (int)_request.errorCode;
+                                    _oldStatus.ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info;
+                                    _oldStatus.VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId;
+                                    _oldStatus.VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode;
+
+
+                                    db.Entry(_oldStatus).Property(x => x.CreatedOn).IsModified = true;
+                                    db.Entry(_oldStatus).Property(x => x.Status).IsModified = true;
+                                    db.Entry(_oldStatus).Property(x => x.ChargePointErrorCodeId).IsModified = true;
+                                    db.Entry(_oldStatus).Property(x => x.ErrorInfo).IsModified = true;
+                                    db.Entry(_oldStatus).Property(x => x.VendorId).IsModified = true;
+                                    db.Entry(_oldStatus).Property(x => x.VendorErrorCode).IsModified = true;
+
+
+                                }
+
+                                if (_oldStatus == null)
+                                {
+                                    var _currentStatus = new Domain.Models.Database.ConnectorStatus()
+                                    {
+                                        ChargeBoxId = session.ChargeBoxId,
+                                        ConnectorId = (byte)_request.connectorId,
+                                        CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
+                                        Status = (int)_request.status,
+                                        ChargePointErrorCodeId = (int)_request.errorCode,
+                                        ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
+                                        VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId,
+                                        VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _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.UtcNow,
+                                        Status = (int)_request.status,
+                                        ChargeBoxId = session.ChargeBoxId,
+                                        ErrorCodeId = (int)_request.errorCode,
+                                        ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
+                                        PreStatus = _oldStatus == null ? -1 : preStatus,
+                                        VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
+                                        VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId
+                                    });
+                                }
+                                db.SaveChanges();
+                            }
+
+
+                            if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
+                            {
+                                var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
+                                var notification = businessService.NotifyFaultStatus(new ErrorDetails()
+                                {
+                                    ChargeBoxId = session.ChargeBoxId,
+                                    ConnectorId = _request.connectorId,
+                                    ErrorCode = _request.errorCode,
+                                    Info = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
+                                    OCcuredOn = _request.timestamp ?? DateTime.UtcNow,
+                                    VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
+
+                                });
+                            }
+
+                            var confirm = new StatusNotificationConfirmation() { };
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                        break;
+                    case Actions.Heartbeat:
+                        {
+
+                            var confirm = new HeartbeatConfirmation() { currentTime = DateTime.UtcNow };
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                        break;
+                    case Actions.MeterValues:
+                        {
+
+                            MeterValuesRequest _request = request as MeterValuesRequest;
+                            decimal energy_kwh = 0;
+
+                            if (_request.meterValue.Count > 0)
+                            {
+
+
+                                using (var db = new MeterValueDBContext())
+                                {
+                                    foreach (var item in _request.meterValue)
+                                    {
+                                        int registerCount = item.sampledValue.Where(x => x.measurand == Measurand.Energy_Active_Import_Interval).ToList().Count;
+                                        if (registerCount > 0)
+                                        {
+                                            registerCount = 0;
+                                        }
+                                        else
+                                        {
+                                            registerCount = item.sampledValue.Where(x => x.measurand == Measurand.Energy_Active_Import_Register).ToList().Count;
+                                        }
+
+                                        if (registerCount > 0)
+                                        {
+                                            string voltage_text = item.sampledValue.Where(x => x.measurand == Measurand.Voltage).Select(x => x.value).FirstOrDefault();
+                                            decimal voltage = decimal.Parse(voltage_text);
+                                            string current_text = item.sampledValue.Where(x => x.measurand == Measurand.Current_Import).Select(x => x.value).FirstOrDefault();
+                                            decimal current = decimal.Parse(current_text);
+                                            decimal meterStart = 0;
+                                            string energyRegister_text = item.sampledValue.Where(x => x.measurand == Measurand.Energy_Active_Import_Register).Select(x => x.value).FirstOrDefault();
+                                            decimal energyRegister = decimal.Parse(energyRegister_text);
+                                            using (var maindb = new MainDBContext())
+                                            {
+                                                meterStart = maindb.TransactionRecord.Where(x => x.Id == _request.transactionId.Value).Select(x => x.MeterStart).FirstOrDefault();
+                                            }
+
+                                            item.sampledValue.Add(new SampledValue()
+                                            {
+                                                context = ReadingContext.Sample_Periodic,
+                                                format = ValueFormat.Raw,
+                                                location = Location.Outlet,
+                                                phase = Phase.L1_N,
+                                                unit = UnitOfMeasure.kWh,
+                                                measurand = Measurand.Energy_Active_Import_Interval,
+                                                value = decimal.Divide(decimal.Subtract(energyRegister, meterStart), (decimal)1000).ToString()
+                                            });
+                                            item.sampledValue.Add(new SampledValue()
+                                            {
+                                                context = ReadingContext.Sample_Periodic,
+                                                format = ValueFormat.Raw,
+                                                location = Location.Outlet,
+                                                phase = Phase.L1_N,
+                                                unit = UnitOfMeasure.kW,
+                                                measurand = Measurand.Power_Active_Import,
+                                                value = decimal.Divide(decimal.Multiply(voltage, current), (decimal)1000).ToString()
+                                            });
+                                        }
+                                        foreach (var sampleVaule in item.sampledValue)
+                                        {
+                                            decimal value = Convert.ToDecimal(sampleVaule.value);
+
+                                            if (sampleVaule.context == ReadingContext.Sample_Periodic && sampleVaule.measurand == Measurand.Energy_Active_Import_Interval)
+                                            {
+                                                energy_kwh = sampleVaule.unit == UnitOfMeasure.Wh ? Decimal.Divide(value, 1000) : value;
+                                            }
+
+                                            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",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());
+
+
+
+
+                                        }
+
+                                    }
+                                }
+                            }
+
+                            //  if (energy_kwh > 0)
+                            {
+                                try
+                                {
+                                    if (session.IsBilling)
+                                        if (session.IsBilling)
+                                        {
+                                            using (var db = new MainDBContext())
+                                            {
+                                                db.ServerMessage.Add(new ServerMessage()
+                                                {
+                                                    ChargeBoxId = session.ChargeBoxId,
+                                                    CreatedBy = "Server",
+                                                    CreatedOn = DateTime.UtcNow,
+                                                    OutAction = Actions.DataTransfer.ToString(),
+                                                    OutRequest = JsonConvert.SerializeObject(
+                                                            new DataTransferRequest()
+                                                            {
+                                                                messageId = "ID_TxEnergy",
+                                                                vendorId = "Phihong Technology",
+                                                                data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = _request.connectorId })
+                                                            },
+                                                            new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                                    SerialNo = Guid.NewGuid().ToString(),
+                                                    InMessage = string.Empty
+
+                                                });
+
+                                                db.SaveChanges();
+                                            }
+                                        }
+
+
+                                }
+                                catch (Exception ex)
+                                {
+
+                                    Console.WriteLine(string.Format("{0} :{1}", session.ChargeBoxId + " RunningCost", ex.Message));
+
+                                }
+
+                            }
+
+                            var confirm = new MeterValuesConfirmation() { };
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                        break;
+                    case Actions.StartTransaction:
+                        {
+
+                            StartTransactionRequest _request = request as StartTransactionRequest;
+
+                            int _transactionId = -1;
+
+                            var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
+                            var _idTagInfo = _request.idTag == "Backend" ? new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted } : await businessService.Authorize(session.ChargeBoxId, _request.idTag);
+                            //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
+                            if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && _idTagInfo.status == AuthorizationStatus.ConcurrentTx)
+                            {
+                                _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
+                            }
+
+                            string accountBalance = "0";
+                            if (session.CustomerId.ToString().ToUpper() == "10C7F5BD-C89A-4E2A-8611-B617E0B41A73")
+                            {
+                                using (SqlConnection conn = new SqlConnection(webConnectionString))
+                                {
+                                    var parameters = new DynamicParameters();
+                                    parameters.Add("@IdTag", _request.idTag, DbType.String, ParameterDirection.Input);
+                                    string strSql = "select parentIdTag from [dbo].[LocalListDetail]  where ListId = 27 and IdTag=@IdTag; ";
+                                    accountBalance = conn.ExecuteScalar<string>(strSql, parameters);
+                                }
+                            }
+
+
+                            using (var db = new MainDBContext())
+                            {
+                                var _CustomerId = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).Include(x => x.Customer).
+                                     Select(x => x.CustomerId).FirstOrDefault();
+
+                                var _existedTx = db.TransactionRecord.Where(x => x.CustomerId == _CustomerId && x.ChargeBoxId == session.ChargeBoxId
+                                   && x.ConnectorId == _request.connectorId && x.StartTime == _request.timestamp).Select(C => new { C.Id }).AsNoTracking().FirstOrDefault();
+                                TransactionRecord _newTransaction = new TransactionRecord();
+
+
+                                if (_existedTx == null)
+                                {
+                                    _newTransaction = new TransactionRecord()
+                                    {
+                                        ChargeBoxId = session.ChargeBoxId,
+                                        ConnectorId = (byte)_request.connectorId,
+                                        CreatedOn = DateTime.UtcNow,
+                                        StartIdTag = _request.idTag,
+                                        MeterStart = _request.meterStart,
+                                        CustomerId = _CustomerId,
+                                        StartTime = _request.timestamp.ToUniversalTime(),
+                                        ReservationId = _request.reservationId.HasValue ? _request.reservationId.Value : 0,                                    
+                                    };
+
+                                    _newTransaction.Fee = !session.IsBilling ? string.Empty : session.BillingMethod == 1 ? JsonConvert.SerializeObject(session.ChargingPrices) : session.ChargingFeebyHour.ToString();
+                                    _newTransaction.Fee += !session.IsBilling ? string.Empty :"|+" + accountBalance + "+" + "&" + session.ParkingFee + "&|" + session.Currency;
+                                    db.TransactionRecord.Add(_newTransaction);
+
+                                    db.SaveChanges();
+
+                                    _transactionId = _newTransaction.Id;
+                                    logger.Info("***************************************************** ");
+                                    logger.Info(string.Format("{0} :TransactionId {1} ", session.ChargeBoxId, _newTransaction.Id));
+                                    logger.Info("***************************************************** ");
+                                }
+                                else
+                                {
+                                    _transactionId = _existedTx.Id;
+                                    logger.Error("Duplication ***************************************************** " + _existedTx.Id);
+                                }
+                            }
+
+
+                            var confirm = new StartTransactionConfirmation()
+                            {
+                                idTagInfo = _idTagInfo,
+                                transactionId = _transactionId
+                            };
+
+
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                        break;
+                    case Actions.StopTransaction:
+                        {
+                            StopTransactionRequest _request = request as StopTransactionRequest;
+
+                            int _ConnectorId = 0;
+
+                            var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
+
+                            //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
+                            var _idTagInfo = string.IsNullOrEmpty(_request.idTag) ? null : (_request.idTag == "Backend" ? new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted } : await businessService.Authorize(session.ChargeBoxId, _request.idTag));
+
+                            if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && _idTagInfo != null && _idTagInfo.status == AuthorizationStatus.ConcurrentTx)
+                            {
+                                _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
+                            }
+
+
+
+                            using (var db = new MainDBContext())
+                            {
+                                var transaction = db.TransactionRecord.Where(x => x.Id == _request.transactionId
+                                 && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+
+                                //遠傳太久以前的停止充電 直接拒絕 避免電樁持續重送~~~~~~~
+                                if (_request.timestamp < new DateTime(2021, 11, 1))
+                                {
+                                    var confirm = new StopTransactionConfirmation()
+                                    {
+                                        idTagInfo = new IdTagInfo()
+                                        {
+                                            status = AuthorizationStatus.Invalid
+                                        }
+
+                                    };
+
+                                    result.Message = confirm;
+                                    result.Success = true;
+                                    return result;
+                                }
+
+
+                                if (transaction != null)
+                                {
+                                    _ConnectorId = transaction.ConnectorId;
+                                    transaction.MeterStop = _request.meterStop;
+                                    transaction.StopTime = _request.timestamp.ToUniversalTime();
+                                    transaction.StopReasonId = _request.reason.HasValue ? (int)_request.reason.Value : 0;
+                                    transaction.StopIdTag = _request.idTag;
+                                    transaction.Receipt = string.Empty;
+                                    transaction.Cost = session.IsBilling ? -1 : 0;
+
+
+                                    await db.SaveChangesAsync();
+                                    var confirm = new StopTransactionConfirmation()
+                                    {
+                                        idTagInfo = _idTagInfo
+
+                                    };
+
+
+                                    if (session.IsBilling)
+                                    {
+                                        db.ServerMessage.Add(new ServerMessage()
+                                        {
+                                            ChargeBoxId = session.ChargeBoxId,
+                                            CreatedBy = "Server",
+                                            CreatedOn = DateTime.UtcNow,
+                                            OutAction = Actions.DataTransfer.ToString(),
+                                            OutRequest = JsonConvert.SerializeObject(
+                                                       new DataTransferRequest()
+                                                       {
+                                                           messageId = "ID_TxEnergy",
+                                                           vendorId = "Phihong Technology",
+                                                           data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = transaction.ConnectorId })
+                                                       },
+                                                       new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                            SerialNo = Guid.NewGuid().ToString(),
+                                            InMessage = string.Empty
+
+                                        });
+
+                                        db.SaveChanges();
+                                    }
+
+                                    result.Message = confirm;
+                                    result.Success = true;
+                                }
+                                else
+                                {
+
+                                    result.Exception = new Exception("Can't find transactionId " + _request.transactionId);
+
+                                }
+                            }
+
+                            if (_request.transactionData != null)
+                            {
+                                if (_request.transactionData.Count > 0)
+                                {
+                                    using (var _meterDb = new MeterValueDBContext())
+                                    {
+                                        foreach (var item in _request.transactionData)
+                                        {
+                                            foreach (var sampleVaule in item.sampledValue)
+                                            {
+                                                decimal value = Convert.ToDecimal(sampleVaule.value);
+
+                                                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)_ConnectorId),
+                                              new SqlParameter("Value",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),
+                                          };
+
+
+                                                _meterDb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
+                                            }
+                                        }
+                                    }
+
+
+                                }
+                            }
+
+
+                        }
+                        break;
+                    case Actions.Authorize:
+                        {
+                            AuthorizeRequest _request = request as AuthorizeRequest;
+
+
+                            //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
+                            var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
+                            var confirm = new AuthorizeConfirmation()
+                            {
+                                idTagInfo = _request.idTag == "Backend" ? new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted } : await businessService.Authorize(session.ChargeBoxId, _request.idTag)
+                            };
+
+                            if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && confirm.idTagInfo.status == AuthorizationStatus.ConcurrentTx)
+                            {
+                                confirm.idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
+                            }
+
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                        break;
+                    default:
+                        {
+                            Console.WriteLine(string.Format("Not Implement {0} Logic(ExecuteCoreRequest)", request.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
+                        }
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Fatal(string.Format("chargeBoxId:{0} {1}", session.ChargeBoxId, action));
+                logger.Fatal(string.Format("Data {0}", request.ToString()));
+                logger.Fatal(string.Format("Error {0}", ex.ToString()));
+                result.Exception = ex;
+            }
+
+
+            if (action == Actions.Heartbeat)
+            {
+                watch.Stop();
+                if (watch.ElapsedMilliseconds / 1000 > 3)
+                {
+                    logger.Error("Processing Hearbeat costs " + watch.ElapsedMilliseconds / 1000 + " seconds");
+                }
+            }
+            return result;
+        }
+
+        internal MessageResult ExecuteCoreConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            try
+            {
+                switch (action)
+                {
+
+                    case Actions.DataTransfer:
+                        {
+                            DataTransferConfirmation _confirm = confirm as DataTransferConfirmation;
+                            DataTransferRequest _request = _confirm.GetRequest() as DataTransferRequest;
+                            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.UtcNow;
+                                    operation.Status = 1;//電樁有回覆
+                                    operation.EVSE_Status = (int)_confirm.status;
+                                    operation.EVSE_Value = string.IsNullOrEmpty(_confirm.data) ? "" : _confirm.data;
+                                    db.SaveChanges();
+                                }
+
+                                if (_request.messageId == "ID_FirmwareVersion")
+                                {
+                                    var machine = new Machine() { Id = session.MachineId };
+                                    if (machine != null)
+                                    {
+                                        db.Configuration.AutoDetectChangesEnabled = false;
+                                        db.Configuration.ValidateOnSaveEnabled = false;
+                                        db.Machine.Attach(machine);
+                                        machine.BoardVersions = _confirm.data;
+                                        db.Entry(machine).Property(x => x.BoardVersions).IsModified = true;
+                                        db.SaveChanges();
+                                    }
+                                }
+
+                                if (_request.messageId == "ID_TxEnergy")
+                                {
+                                    if (_confirm.status == DataTransferStatus.Accepted)
+                                    {
+                                        string receipt = string.Empty;
+                                        List<ChargingBill> bill = new List<ChargingBill>();
+                                        List<ChargingPrice> chargingPrices = new List<ChargingPrice>();
+                                        var txEnergy = JsonConvert.DeserializeObject<TransactionEnergy>(_confirm.data);
+                                        var feedto = db.TransactionRecord.Where(x => x.Id == txEnergy.TxId).Select(x => new { Id = x.Id, ConnectorId = x.ConnectorId, Fee = x.Fee, StopTime = x.StopTime, StartTime = x.StartTime }).FirstOrDefault();
+                                        decimal chargedEnergy = 0m;
+                                        if (feedto == null || string.IsNullOrEmpty(feedto.Fee)) return result;
+                                        string currency = feedto.Fee.Substring(feedto.Fee.Length - 3);
+                                        decimal chargingCost = 0;
+                                        if (feedto.Fee.Length > 58)
+                                        {
+                                            chargingPrices = JsonConvert.DeserializeObject<List<ChargingPrice>>(feedto.Fee.Split('|')[0]);
+                                            foreach (var item in txEnergy.PeriodEnergy)
+                                            {
+                                                DateTime dt = new DateTime(2021, 01, 01, int.Parse(item.Key), 0, 0, DateTimeKind.Utc);
+                                                string startTime = dt.ToString("hh:mm tt", new CultureInfo("en-us"));
+                                                decimal perfee = 0;
+
+                                                //小數點無條件捨去到第4位
+                                                var periodEnergy = (decimal)((int)Decimal.Multiply(item.Value, 10000) / (double)10000);
+                                                chargedEnergy += periodEnergy;
+                                                if (chargingPrices.Count == 1)
+                                                {
+                                                    perfee = Decimal.Multiply(periodEnergy, chargingPrices[0].Fee);
+                                                    if (bill.Count == 0)
+                                                    {
+                                                        bill.Add(new ChargingBill()
+                                                        {
+                                                            StartTime = "12:00 AM",
+                                                            EndTime = "11:59 PM",
+                                                            Fee = chargingPrices[0].Fee
+                                                        });
+                                                    }
+
+                                                    bill[0].PeriodEnergy += periodEnergy;
+
+                                                }
+                                                else
+                                                {
+                                                    var price = chargingPrices.Where(x => x.StartTime == startTime).FirstOrDefault();
+                                                    perfee = Decimal.Multiply(periodEnergy, price.Fee);
+
+                                                    bill.Add(new ChargingBill()
+                                                    {
+                                                        StartTime = price.StartTime,
+                                                        EndTime = price.EndTime,
+                                                        PeriodEnergy = periodEnergy,
+                                                        Fee = price.Fee,
+
+                                                    });
+                                                }
+                                                if (bill.Count > 0)
+                                                {
+                                                    bill[bill.Count - 1].Total += DollarRounding(perfee, session.Currency);
+                                                    chargingCost += bill[bill.Count - 1].Total;
+
+                                                    if (bill.Count == 1)
+                                                    {
+
+                                                        bill[bill.Count - 1].Total = DollarRounding(Decimal.Multiply(bill[0].PeriodEnergy, bill[0].Fee), session.Currency);
+                                                        chargingCost = bill[bill.Count - 1].Total;
+                                                    }
+                                                }
+                                            }
+                                        }
+                                        else
+                                        {
+                                            //以小時計費
+                                            foreach (var item in txEnergy.PeriodEnergy)
+                                            {  //小數點無條件捨去到第4位
+                                                var periodEnergy = (decimal)((int)Decimal.Multiply(item.Value, 10000) / (double)10000);
+                                                chargedEnergy += periodEnergy;
+                                            }
+
+                                            var fee = decimal.Parse(feedto.Fee.Split('|')[0]);
+                                            var charging_stoptime = feedto.StopTime == GlobalConfig.DefaultNullTime ? DateTime.Parse(DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm")) : DateTime.Parse(feedto.StopTime.ToString("yyyy/MM/dd HH:mm"));
+                                            var charging_starttime = DateTime.Parse(feedto.StartTime.ToString("yyyy/MM/dd HH:mm"));
+                                            chargingCost = Decimal.Multiply((decimal)charging_stoptime.Subtract(charging_starttime).TotalHours, fee);
+                                            chargingCost = DollarRounding(chargingCost, session.Currency);
+                                        }
+
+                                        // 計算停車費
+                                        var parkingFee = decimal.Parse(feedto.Fee.Split('&')[1]);
+                                        var stoptime = feedto.StopTime==GlobalConfig.DefaultNullTime ? DateTime.Parse(DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm")) : DateTime.Parse(feedto.StopTime.ToString("yyyy/MM/dd HH:mm"));
+                                        var starttime = DateTime.Parse(feedto.StartTime.ToString("yyyy/MM/dd HH:mm"));
+                                        var totalHours = stoptime.Subtract(starttime).TotalHours;
+                                        var parkingCost = Decimal.Multiply((decimal)totalHours, parkingFee);
+                                        parkingCost = DollarRounding(parkingCost, session.Currency);
+
+                                        if (feedto.StopTime != GlobalConfig.DefaultNullTime)
+                                        {
+                                            decimal accountBalance = 0;
+                                            decimal.TryParse(feedto.Fee.Split('+')[1], out accountBalance);
+
+                                            var tx = db.TransactionRecord.Where(x => x.Id == txEnergy.TxId).FirstOrDefault();
+                                            if (tx == null)
+                                            {
+                                                Console.WriteLine("Tx is empty");
+                                                return result;
+                                            }
+                                            if (tx.UploadedtoTTIA) return result;
+                                            var startTime = new DateTime(tx.StartTime.Year, tx.StartTime.Month, tx.StartTime.Day, tx.StartTime.Hour, 0, 0);
+                                            List<ChargingBill> confirmbill = new List<ChargingBill>();
+                                            receipt = string.Format("({0} )Energy:", chargedEnergy);
+
+                                            while (startTime < tx.StopTime)
+                                            {
+                                                if (bill.Count == 1)
+                                                {
+                                                    confirmbill = bill;
+                                                    receipt += string.Format("| {0} - {1}:| {2} kWh @ ${3}/kWh=${4}", tx.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")), tx.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")),
+                                                        confirmbill[0].PeriodEnergy.ToString("0.0000"), bill[0].Fee, bill[0].Total);
+
+                                                    break;
+                                                }
+                                                if (bill.Count == 0)
+                                                {
+                                                    receipt += string.Format("| {0} - {1} @ ${2}/hr=${3}", feedto.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")),
+                                                       feedto.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")), feedto.Fee.Split('|')[0], chargingCost);
+
+                                                    break;
+                                                }
+                                                if (bill.Count > 1)
+                                                {
+                                                    var time = startTime.ToString("hh:mm tt", new CultureInfo("en-us"));
+                                                    var tt = bill.Where(x => x.StartTime == time).FirstOrDefault();
+                                                    confirmbill.Add(tt);
+                                                    if (confirmbill.Count == 1)
+                                                    {
+                                                        confirmbill[0].StartTime = tx.StartTime.ToString("hh:mm tt", new CultureInfo("en-us"));
+                                                    }
+
+
+                                                    var stopTimeText = tx.StopTime.ToString("hh:mm tt", new CultureInfo("en-us"));
+                                                    if (confirmbill[confirmbill.Count - 1].StartTime.Contains(stopTimeText.Split(' ')[1]))
+                                                    {
+                                                        var subHourText = (int.Parse(stopTimeText.Split(':')[0])).ToString();
+                                                        subHourText = subHourText.Length == 1 ? "0" + subHourText : subHourText;
+                                                        if (confirmbill[confirmbill.Count - 1].StartTime.Contains(subHourText))
+                                                        {
+                                                            confirmbill[confirmbill.Count - 1].EndTime = stopTimeText;
+                                                        }
+
+                                                    }
+                                                    receipt += string.Format("| {0} - {1}:| {2} kWh @ ${3}/kWh=${4}", confirmbill[confirmbill.Count - 1].StartTime, confirmbill[confirmbill.Count - 1].EndTime,
+                                                        confirmbill[confirmbill.Count - 1].PeriodEnergy.ToString("0.0000"), confirmbill[confirmbill.Count - 1].Fee, confirmbill[confirmbill.Count - 1].Total);
+
+                                                    if (confirmbill.Count == 24) break;
+
+                                                }
+                                                startTime = startTime.AddHours(1);
+
+                                            }
+
+                                            if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
+                                            {
+                                                chargingCost = confirmbill.Count > 0 ? confirmbill.Sum(x => x.Total) : chargingCost;
+                                                chargingCost = chargedEnergy == 0 ? 0 : (chargingCost + parkingCost < 1 ? 1 : chargingCost);  //台泥最低一元
+                                                receipt += string.Format("|Total Energy Fee : ${0}", chargingCost);
+
+                                                receipt += string.Format("|Parking Fee: | {0} - {1} @ ${2}/hr=${3}", feedto.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")),
+                                                feedto.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")), parkingFee, parkingCost);
+                                                tx.Cost = chargingCost + parkingCost;
+                                            }
+                                            else
+                                            {
+                                                chargingCost = confirmbill.Count > 0 ? confirmbill.Sum(x => x.Total) : chargingCost;
+                                                receipt += string.Format("|Total Energy Fee : ${0}", chargingCost);
+
+                                                receipt += string.Format("|Parking Fee: | {0} - {1} @ ${2}/hr=${3}", feedto.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")),
+                                                feedto.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")), parkingFee, parkingCost);
+                                                tx.Cost = chargingCost + parkingCost;
+                                            }
+
+                                            tx.Receipt = receipt;
+                                            tx.UploadedtoTTIA = true;
+                                            db.Configuration.AutoDetectChangesEnabled = false;
+                                            db.Configuration.ValidateOnSaveEnabled = false;
+                                            db.TransactionRecord.Attach(tx);
+                                            db.Entry(tx).Property(x => x.Cost).IsModified = true;
+                                            db.Entry(tx).Property(x => x.Receipt).IsModified = true;
+                                            db.Entry(tx).Property(x => x.UploadedtoTTIA).IsModified = true;
+
+                                            db.ServerMessage.Add(new ServerMessage()
+                                            {
+                                                ChargeBoxId = session.ChargeBoxId,
+                                                CreatedBy = "Server",
+                                                CreatedOn = DateTime.UtcNow,
+                                                OutAction = Actions.DataTransfer.ToString(),
+                                                OutRequest = JsonConvert.SerializeObject(
+                                                            new DataTransferRequest()
+                                                            {
+                                                                messageId = "FinalCost",
+                                                                vendorId = "Phihong Technology",
+                                                                data = JsonConvert.SerializeObject(new
+                                                                {
+                                                                    txId = txEnergy.TxId,
+                                                                    description = JsonConvert.SerializeObject(new
+                                                                    {
+                                                                        chargedEnergy = chargedEnergy,
+                                                                        chargingFee = chargingCost,
+                                                                        parkTime = (int)stoptime.Subtract(starttime).TotalSeconds,
+                                                                        parkingFee = parkingCost,
+                                                                        currency = currency,
+                                                                        couponPoint = 0,
+                                                                        accountBalance = accountBalance - tx.Cost
+                                                                    })
+                                                                })
+
+                                                            },
+                                                            new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                                SerialNo = Guid.NewGuid().ToString(),
+                                                InMessage = string.Empty
+
+                                            }); ;
+
+                                            db.SaveChanges();
+
+                                            using (var meterdb = new MeterValueDBContext())
+                                            {
+                                                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)feedto.ConnectorId),
+                                                      new SqlParameter("Value",chargingCost),
+                                                      new SqlParameter("CreatedOn",DateTime.UtcNow),
+                                                      new SqlParameter("ContextId",(int)ReadingContext.Sample_Periodic),
+                                                      new SqlParameter("FormatId",(int)ValueFormat.Raw),
+                                                      new SqlParameter("MeasurandId",(int)Measurand.TotalCost),
+                                                      new SqlParameter("PhaseId", -1),
+                                                      new SqlParameter("LocationId", -1),
+                                                      new SqlParameter("UnitId", -1),
+                                                      new SqlParameter("TransactionId",feedto.Id),
+                                                };
+
+
+                                                meterdb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
+                                            }
+
+                                            using (SqlConnection conn = new SqlConnection(webConnectionString))
+                                            {
+                                                var parameters = new DynamicParameters();
+                                                parameters.Add("@IdTag", tx.StartIdTag, DbType.String, ParameterDirection.Input);
+                                                parameters.Add("@parentIdTag", accountBalance - tx.Cost, DbType.String, ParameterDirection.Input);
+                                                string strSql = "update [dbo].[LocalListDetail] set parentIdTag =@parentIdTag  where ListId = 27 and IdTag=@IdTag; ";
+                                                conn.Execute(strSql, parameters);
+
+                                            }
+
+                                            if (tx.CustomerId == Guid.Parse("10C7F5BD-C89A-4E2A-8611-B617E0B41A73"))
+                                            {
+                                                var mail_response = new OuterHttpClient().PostFormDataAsync("http://ocpp.phihong.com.tw/CDFA/" + tx.Id, new Dictionary<string, string>()
+                                                {
+                                                    { "email","2"},
+                                                    { "to","wonderj@phihongusa.com;jessica_tseng@phihong.com.tw"}
+                                                    //{ "to","jessica_tseng@phihong.com.tw"}
+                                               }, null);
+
+                                                Console.WriteLine(JsonConvert.SerializeObject(mail_response));
+
+                                            }
+
+                                        }
+                                        else
+                                        {
+                                            db.ServerMessage.Add(new ServerMessage()
+                                            {
+                                                ChargeBoxId = session.ChargeBoxId,
+                                                CreatedBy = "Server",
+                                                CreatedOn = DateTime.UtcNow,
+                                                OutAction = Actions.DataTransfer.ToString(),
+                                                OutRequest = JsonConvert.SerializeObject(
+                                                 new DataTransferRequest()
+                                                 {
+                                                     messageId = "RunningCost",
+                                                     vendorId = "Phihong Technology",
+                                                     data = JsonConvert.SerializeObject(new
+                                                     {
+                                                         txId = txEnergy.TxId,
+                                                         description = JsonConvert.SerializeObject(new
+                                                         {
+                                                             chargedEnergy = chargedEnergy,
+                                                             chargingFee = chargingCost,
+                                                             parkTime = (int)stoptime.Subtract(starttime).TotalSeconds,
+                                                             parkingFee = parkingCost,
+                                                             currency = currency
+                                                         })
+
+                                                     })
+
+                                                 },
+                                                 new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                                SerialNo = Guid.NewGuid().ToString(),
+                                                InMessage = string.Empty
+
+                                            }); ;
+
+                                            db.SaveChanges();
+
+                                            using (var meterdb = new MeterValueDBContext())
+                                            {
+                                                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)feedto.ConnectorId),
+                                                      new SqlParameter("Value",chargingCost),
+                                                      new SqlParameter("CreatedOn",DateTime.UtcNow),
+                                                      new SqlParameter("ContextId",(int)ReadingContext.Sample_Periodic),
+                                                      new SqlParameter("FormatId",(int)ValueFormat.Raw),
+                                                      new SqlParameter("MeasurandId",(int)Measurand.ChargingCost),
+                                                      new SqlParameter("PhaseId", -1),
+                                                      new SqlParameter("LocationId", -1),
+                                                      new SqlParameter("UnitId", -1),
+                                                      new SqlParameter("TransactionId",feedto.Id),
+                                                };
+
+
+                                                meterdb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
+                                            }
+
+                                        }
+
+
+                                    }
+
+
+                                }
+                                if (_request.messageId == "ID_GetTxUserInfo")
+                                {
+                                    var txUserInfo = JsonConvert.DeserializeObject<ID_GetTxUserInfo>(_confirm.data);
+                                    if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
+                                    {
+                                        var request = new
+                                        {
+                                            ChargeBoxId = session.ChargeBoxId,
+                                            ConnectorId = txUserInfo.ConnectorId,
+                                            SessionId = txUserInfo.TxId,
+                                            SerialNo = txUserInfo.SerialNo,
+                                            StartTime = txUserInfo.StartTime.ToString(GlobalConfig.UTC_DATETIMEFORMAT),
+                                            VEMData = txUserInfo.VEMData
+                                        };
+
+                                        var response = httpClient.Post(GlobalConfig.TCC_API_URL + "start_session", new Dictionary<string, string>()
+                                        {
+                                            { "PartnerId",session.CustomerId.ToString()}
+
+                                        }, request, GlobalConfig.TCC_SALTKEY);
+
+                                        logger.Debug(JsonConvert.SerializeObject(response));
+                                    }
+
+                                }
+                            }
+                        }
+                        break;
+                    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.UtcNow;
+                                    operation.Status = 1;//電樁有回覆
+                                    operation.EVSE_Status = (int)_confirm.status;
+                                    operation.EVSE_Value = _confirm.status.ToString();
+                                    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.UtcNow;
+                                    operation.Status = 1;//電樁有回覆
+                                    operation.EVSE_Status = (int)_confirm.status;
+                                    operation.EVSE_Value = _confirm.status.ToString();
+                                    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.UtcNow;
+                                    operation.Status = 1;//電樁有回覆
+                                    operation.EVSE_Status = (int)_confirm.status;
+                                    operation.EVSE_Value = _confirm.status.ToString();
+                                    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.UtcNow;
+                                    operation.Status = 1;//電樁有回覆
+                                    operation.EVSE_Status = (int)_confirm.status;
+                                    operation.EVSE_Value = _confirm.status.ToString();
+                                    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.UtcNow;
+                                    operation.Status = 1;//電樁有回覆
+                                    operation.EVSE_Status = (int)_confirm.status;
+                                    operation.EVSE_Value = _confirm.status.ToString();
+                                    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.UtcNow;
+                                    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.MachineConfiguration.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.MachineConfiguration.Add(new MachineConfiguration()
+                                        {
+                                            ChargeBoxId = session.ChargeBoxId,
+                                            ConfigureName = _request.key,
+                                            ReadOnly = false,
+                                            ConfigureSetting = _request.value
+                                        });
+                                    }
+                                }
+                                db.SaveChanges();
+                            }
+
+                        }
+                        break;
+                    case Actions.GetConfiguration:
+                        {
+
+                            try
+                            {
+                                GetConfigurationConfirmation _confirm = confirm as GetConfigurationConfirmation;
+                                //  GetConfigurationRequest _request = _confirm.GetRequest() as GetConfigurationRequest;
+
+                                using (var db = new MainDBContext())
+                                {
+                                    var configure = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToList();
+
+                                    if (_confirm.configurationKey != null)
+                                    {
+                                        foreach (var item in _confirm.configurationKey)
+                                        {
+                                            string oldValue = string.Empty;
+                                            if (item.key == null)
+                                            {
+                                                Console.WriteLine("*********************");
+                                            }
+                                            var foundConfig = configure.Find(x => x.ConfigureName == item.key);
+
+
+                                            if (foundConfig != null)
+                                            {
+                                                if (foundConfig.ConfigureName == null)
+                                                {
+                                                    Console.WriteLine("*********************");
+                                                }
+
+                                                if (foundConfig.ConfigureName == "SecurityProfile")
+                                                {
+                                                    oldValue = foundConfig.ConfigureSetting;
+                                                }
+
+                                                foundConfig.ReadOnly = item.IsReadOnly;
+                                                foundConfig.ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value;
+                                            }
+                                            else
+                                            {
+                                                db.MachineConfiguration.Add(new MachineConfiguration()
+                                                {
+                                                    ChargeBoxId = session.ChargeBoxId,
+                                                    ConfigureName = item.key,
+                                                    ReadOnly = item.IsReadOnly,
+                                                    ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value,
+                                                    Exists = true
+                                                });
+                                            }
+
+
+                                        }
+                                    }
+                                    if (_confirm.unknownKey != null)
+                                    {
+
+                                        foreach (var item in _confirm.unknownKey)
+                                        {
+                                            var foundConfig = configure.Find(x => x.ConfigureName == item);
+                                            if (foundConfig != null)
+                                            {
+                                                foundConfig.ReadOnly = true;
+                                                foundConfig.ConfigureSetting = string.Empty;
+                                                foundConfig.Exists = false;
+                                            }
+                                            else
+                                            {
+                                                db.MachineConfiguration.Add(new MachineConfiguration()
+                                                {
+                                                    ChargeBoxId = session.ChargeBoxId,
+                                                    ConfigureName = item
+                                                });
+                                            }
+                                        }
+                                    }
+
+
+
+                                    var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
+                                   x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
+
+                                    if (operation != null)
+                                    {
+                                        operation.FinishedOn = DateTime.UtcNow;
+                                        operation.Status = 1;//電樁有回覆
+                                        operation.EVSE_Status = 1;
+                                        operation.EVSE_Value = JsonConvert.SerializeObject(_confirm.configurationKey, Formatting.None);
+
+                                    }
+
+                                    db.SaveChanges();
+
+                                }
+                            }
+                            catch (Exception ex)
+                            {
+                                logger.Error(ex.ToString());
+                            }
+
+                        }
+                        break;
+                    case Actions.UnlockConnector:
+                        {
+                            UnlockConnectorConfirmation _confirm = confirm as UnlockConnectorConfirmation;
+                            UnlockConnectorRequest _request = _confirm.GetRequest() as UnlockConnectorRequest;
+
+                            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.UtcNow;
+                                    operation.Status = 1;//電樁有回覆
+                                    operation.EVSE_Status = (int)_confirm.status;
+                                    operation.EVSE_Value = _confirm.status.ToString();
+                                    db.SaveChanges();
+                                }
+
+                            }
+                        }
+                        break;
+                    default:
+                        {
+                            Console.WriteLine(string.Format("Not Implement {0} Logic(ExecuteCoreConfirm)", confirm.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
+                        }
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Debug("123 " + action + " " + ex.ToString());
+            }
+
+
+
+            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:
+                case Actions.UnlockConnector:
+                case Actions.DataTransfer:
+                    {
+                        if (action == Actions.DataTransfer)
+                        {
+                            logger.Debug(string.Format("DataTransfer Error {0}: {1}", session.ChargeBoxId, requestId));
+                        }
+
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)255;//錯誤
+                                operation.EVSE_Value = errorMsg;
+                                db.SaveChanges();
+                            }
+
+                        }
+
+                    }
+                    break;
+
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic(ReceivedCoreError)", action));
+                    }
+                    break;
+            }
+            return result;
+
+        }
+
+
+        /// <summary>
+        /// 依據幣值處理4捨5入
+        /// </summary>
+        /// <param name="money"></param>
+        /// <param name="currency"></param>
+        /// <returns></returns>
+        private decimal DollarRounding(decimal money, string currency)
+        {
+
+
+            if (currency == "USD" || currency == "EUR")
+            {
+
+                //0.4867
+                if ((double)((int)(money * 100) + 0.5) <= (double)(money * 100))
+                {
+
+                    //money = Decimal.Add(money, (decimal)0.01);//0.4967
+
+                }
+                money = Math.Round(money, 2, MidpointRounding.AwayFromZero);
+                money = Decimal.Parse(money.ToString("0.00"));
+
+
+            }
+            else
+            {
+                if ((double)((int)(money) + 0.5) <= (double)money)
+                {
+                    //  money = (int) money + 1;
+                }
+                money = Math.Round(money, 0, MidpointRounding.AwayFromZero);
+                money = Decimal.Parse(money.ToString("0"));
+            }
+
+            return money;
+        }
+    }
+}

+ 228 - 0
EVCB_OCPP.WSServer/Message/FirmwareManagementProfileHandler.cs

@@ -0,0 +1,228 @@
+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.FirmwareManagement;
+using Newtonsoft.Json;
+using OCPPServer.Protocol;
+using System;
+using System.Data.Entity;
+using System.Linq;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    internal partial class ProfileHandler
+    {
+        internal MessageResult ExecuteFirmwareManagementRequest(Actions action, ClientData session, IRequest request)
+        {
+            MessageResult result = new MessageResult() { Success = false };
+            try
+            {
+                switch (action)
+                {
+
+                    case Actions.FirmwareStatusNotification:
+                        {
+                            FirmwareStatusNotificationRequest _request = request as FirmwareStatusNotificationRequest;
+
+                            if (_request.status == Packet.Messages.SubTypes.FirmwareStatus.Idle)
+                            {
+                                string requestId = Guid.NewGuid().ToString();
+                                using (var db = new MainDBContext())
+                                {
+                                    var machine = db.Machine.Where(x => x.FW_AssignedVersion.HasValue == true && x.FW_AssignedVersion.HasValue
+                                       && x.FW_AssignedVersion != x.FW_VersionReport && x.ChargeBoxId == session.ChargeBoxId)
+                                        .Select(x => new { x.Id, x.FW_AssignedVersion }).AsNoTracking().FirstOrDefault();
+
+                                    if (machine != null)
+                                    {
+                                        var mv = db.MachineVersionFile.Include(c => c.UploadFile)
+                                         .Where(c => c.Id == machine.FW_AssignedVersion.Value).First();
+
+                                        string downloadUrl = mv.UploadFile.FileUrl;
+
+                                        var _updateFWrequest = new UpdateFirmwareRequest()
+                                        {
+                                            location = new Uri(downloadUrl),
+                                            retries = 3,
+                                            retrieveDate = DateTime.UtcNow,
+                                            retryInterval = 10
+                                        };
+
+                                        db.MachineOperateRecord.Add(new MachineOperateRecord()
+                                        {
+                                            CreatedOn = DateTime.UtcNow,
+                                            ChargeBoxId = session.ChargeBoxId,
+                                            SerialNo = requestId,
+                                            RequestContent = JsonConvert.SerializeObject(_updateFWrequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                            EVSE_Status = 0,
+                                            EVSE_Value = "Fw Version:" + machine.FW_AssignedVersion,
+                                            Status = 0,
+                                            RequestType = 0,
+                                            Action = _updateFWrequest.Action.ToString()
+
+                                        });
+
+                                        db.ServerMessage.Add(new ServerMessage()
+                                        {
+                                            ChargeBoxId = session.ChargeBoxId,
+                                            CreatedBy = "Server",
+                                            CreatedOn = DateTime.UtcNow,
+                                            OutAction = _updateFWrequest.Action.ToString(),
+                                            OutRequest = JsonConvert.SerializeObject(_updateFWrequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                            SerialNo = requestId,
+                                            InMessage = string.Empty
+
+                                        });
+
+                                        db.SaveChanges();
+
+                                    }
+
+                                }
+                            }
+                            else
+                            {
+                                using (var db = new MainDBContext())
+                                {
+                                    var item = db.MachineOperateRecord.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.RequestType == 0)
+                                        .OrderByDescending(x => x.CreatedOn).FirstOrDefault();
+                                    if (item != null)
+                                    {
+                                        item.EVSE_Status = (int)_request.status;
+                                        item.FinishedOn = DateTime.UtcNow;
+                                        if (string.IsNullOrEmpty(item.EVSE_Value))
+                                        {
+                                            Console.WriteLine("怎麼悾悾的");
+                                        }
+
+                                        if (!string.IsNullOrEmpty(item.EVSE_Value) && _request.status == Packet.Messages.SubTypes.FirmwareStatus.Installed)
+                                        {
+                                            int version = 0;
+                                            int.TryParse(item.EVSE_Value.Split(':').Last(), out version);
+                                            var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+                                            machine.FW_VersionReport = version;
+                                        }
+                                    }
+
+
+
+                                    db.SaveChanges();
+                                }
+                            }
+
+                            var confirm = new FirmwareStatusNotificationConfirmation() { };
+
+                            result.Message = confirm;
+                            result.Success = true;
+                        }
+                        break;
+                    case Actions.DiagnosticsStatusNotification:
+                        {
+                            DiagnosticsStatusNotificationRequest _request = request as DiagnosticsStatusNotificationRequest;
+
+                            var confirm = new DiagnosticsStatusNotificationConfirmation() { };
+
+                            result.Message = confirm;
+                            result.Success = true;
+
+                        }
+                        break;
+
+                    default:
+                        {
+                            Console.WriteLine(string.Format("Not Implement {0} Logic", request.GetType().ToString().Replace("OCPPPackage.Messages.FirmwareManagement.", "")));
+                        }
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Fatal(string.Format("chargeBoxId:{0} {1}", session.ChargeBoxId, action));
+                logger.Fatal(string.Format("Data {0}", request.ToString()));
+                logger.Fatal(string.Format("Error {0}", ex.ToString()));
+                result.Exception = ex;
+            }
+            return result;
+        }
+
+        internal MessageResult ExecuteFirmwareManagementConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.UpdateFirmware:
+                case Actions.GetDiagnostics:
+                    {
+                        string evse_rep = string.Empty;
+                        if (confirm is GetDiagnosticsConfirmation)
+                        {
+                            var confirmation = confirm as GetDiagnosticsConfirmation;
+                            evse_rep = confirmation.fileName;
+                        }
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)1;//OK
+                                operation.EVSE_Value = string.IsNullOrEmpty(evse_rep) ? operation.EVSE_Value : evse_rep;
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", confirm.GetType().ToString().Replace("OCPPPackage.Messages.FirmwareManagement.", "")));
+                    }
+                    break;
+            }
+            return result;
+        }
+
+
+        internal MessageResult ReceivedFirmwareManagementError(Actions action, string errorMsg, ClientData session, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.FirmwareStatusNotification:
+                case Actions.UpdateFirmware:
+                case Actions.GetDiagnostics:
+                case Actions.DiagnosticsStatusNotification:
+                    {
+                        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.UtcNow;
+                                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;
+
+        }
+    }
+}

+ 109 - 0
EVCB_OCPP.WSServer/Message/LocalAuthListManagementProfileHandler.cs

@@ -0,0 +1,109 @@
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.Packet.Features;
+using EVCB_OCPP.Packet.Messages;
+using EVCB_OCPP.Packet.Messages.LocalAuthListManagement;
+using OCPPServer.Protocol;
+using System;
+using System.Linq;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    internal partial class ProfileHandler
+    {
+
+
+
+
+        internal MessageResult ExecuteLocalAuthListManagementConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.GetLocalListVersion:
+                    {
+                        GetLocalListVersionConfirmation _confirm = confirm as GetLocalListVersionConfirmation;
+                        GetLocalListVersionRequest _request = _confirm.GetRequest() as GetLocalListVersionRequest;
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = 1;//OK
+                                operation.EVSE_Value = _confirm.listVersion.ToString();
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.SendLocalList:
+                    {
+                        SendLocalListConfirmation _confirm = confirm as SendLocalListConfirmation;
+                        SendLocalListRequest _request = _confirm.GetRequest() as SendLocalListRequest;
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;//OK     
+                                operation.EVSE_Value = _confirm.status.ToString();
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", confirm.GetType().ToString().Replace("OCPPPackage.Messages.RemoteTrigger.", "")));
+                    }
+                    break;
+            }
+            return result;
+        }
+
+
+        internal MessageResult ReceivedLocalAuthListManagementError(Actions action, string errorMsg, ClientData session, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.SendLocalList:
+                case Actions.GetLocalListVersion:
+                    {
+                        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.UtcNow;
+                                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;
+
+        }
+    }
+}

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

@@ -0,0 +1,54 @@
+using EVCB_OCPP.Packet.Messages;
+using System;
+
+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; }
+
+
+
+
+
+
+
+    }
+
+
+    internal class BasicMessageResult
+    {
+        internal IRequest Request { set; get; }
+
+        internal IConfirmation Confirmation { set; get; }
+
+        internal EVCB_OCPP20.Packet.Messages.IRequest Request20 { set; get; }
+
+        internal EVCB_OCPP20.Packet.Messages.IConfirmation Confirmation20 { set; get; }
+
+        internal Exception Exception { get; set; }
+    }
+
+}

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

@@ -0,0 +1,48 @@
+using System;
+
+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; }
+
+    }
+}

+ 341 - 0
EVCB_OCPP.WSServer/Message/OCPP16MessageHandler.cs

@@ -0,0 +1,341 @@
+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;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    /// <summary>
+    /// 實現 OCPP16 基本傳送規範,
+    /// 1.訊息 基本格式,將訊息包裝成 Call 、CallResult、CallError 三種格式
+    /// 2.OCPP 定義的傳送規則:交易相關的訊息必須依照時序性傳送,一個傳完才能接著送下一個(忽略規則 由Center System定義)
+    /// </summary>
+    internal class OCPP16MessageHandler
+    {
+        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(),
+             new SecurityProfile()
+        };
+
+
+        /// <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
+                                BasicMessageResult baseResult = UnPackPayloadbyCall(msg.Action, msg.Payload.ToString());
+                                Actions action = Actions.None;
+                                Enum.TryParse<Actions>(msg.Action, out action);
+                                result.Action = msg.Action;
+                                if (baseResult.Request != null)
+                                {
+                                    if (baseResult.Request.Validate())
+                                    {
+                                        result.Id = TYPENUMBER_CALL;
+                                        result.Message = baseResult.Request;
+
+                                    }
+                                    else
+                                    {
+
+                                        string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(),
+                                             OCPPErrorDescription.OccurenceConstraintViolation);
+                                        result.Id = TYPENUMBER_CALL;
+                                        result.Message = baseResult.Request;
+                                        result.Success = false;
+                                        result.CallErrorMsg = replyMsg;
+                                        result.Exception = new Exception("Validation Failed");
+
+                                    }
+                                }
+                                else
+                                {
+
+                                    string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation, OCPPErrorDescription.OccurenceConstraintViolation);
+                                    result.Id = TYPENUMBER_CALL;
+                                    result.Message = baseResult.Request;
+                                    result.Success = false;
+                                    result.CallErrorMsg = replyMsg;
+                                    result.Exception = baseResult.Exception;
+                                }
+
+                            }
+                            break;
+                        case TYPENUMBER_CALLRESULT:
+                            {
+                                BasicMessageResult baseResult = UnPackPayloadbyCallResult(client.queue, msg.Id, msg.Payload.ToString());
+
+                                if (baseResult.Confirmation != null)
+                                {
+
+                                    if (baseResult.Confirmation.Validate())
+                                    {
+                                        result.Id = TYPENUMBER_CALLRESULT;
+                                        result.Message = baseResult.Confirmation;
+                                        result.Action = baseResult.Confirmation.GetRequest().Action;
+                                        //return data
+                                    }
+                                    else
+                                    {
+                                        string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(),
+                                      OCPPErrorDescription.OccurenceConstraintViolation);
+                                        result.Id = TYPENUMBER_CALLRESULT;
+                                        result.Message = baseResult.Confirmation;
+                                        result.Success = false;
+                                        result.CallErrorMsg = replyMsg;
+                                        result.Exception = new Exception("Validate Failed");
+                                    }
+                                }
+                                else
+                                {
+                                    string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(),
+                                   OCPPErrorDescription.OccurenceConstraintViolation);
+                                    result.Id = TYPENUMBER_CALLRESULT;
+                                    result.Message = baseResult.Confirmation;
+                                    result.Success = false;
+                                    result.CallErrorMsg = replyMsg;
+                                    result.Exception = baseResult.Exception;
+                                }
+
+                            }
+                            break;
+                        case TYPENUMBER_CALLERROR:
+                            {
+                                result.Id = TYPENUMBER_CALLERROR;
+                                var sentRequest = UnPackPayloadbyCallError(client.queue, msg.Id);
+
+                                if (sentRequest != null)
+                                {
+                                    IRequest request = sentRequest as IRequest;
+                                    result.Action = request.Action;
+
+                                    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)
+            {
+                if (string.IsNullOrEmpty(result.UUID))
+                {
+                    result.UUID = data.Substring(4, 39);
+                    result.UUID = result.UUID.Split(new string[] { "\"," }, StringSplitOptions.None)[0];
+                }
+                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
+            {
+                if (message.StartsWith("[4,\""))
+                {
+                    message = message.Replace('{', '"');
+                    message = message.Replace('}', '"');
+                }
+                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("Parse Error=>  Problem: {0}", ex.Message));
+
+            }
+
+
+
+        }
+
+
+        private BasicMessageResult UnPackPayloadbyCall(string action, string payload)
+        {
+            BasicMessageResult result = new BasicMessageResult();
+            try
+            {
+                Feature feature = null;
+                foreach (var profile in profiles)
+                {
+                    feature = profile.GetFeaturebyAction(action);
+                    if (feature == null)
+                    {
+                        continue;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+
+                result.Request = JsonConvert.DeserializeObject(payload, feature.GetRequestType()) as IRequest;
+
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+                logger.Error(string.Format("[{0}]UnPackPayloadbyCall Ex: {1}", action, ex.Message), "UnPack");
+
+            }
+
+            return result;
+        }
+
+        private BasicMessageResult UnPackPayloadbyCallResult(Queue requestQueue, string uniqueId, string payload)
+        {
+            int i = 1;
+            BasicMessageResult result = new BasicMessageResult();
+            try
+            {
+                i = -1 ;
+                IRequest request = requestQueue.RestoreRequest(uniqueId);
+                i = 2;
+                Feature feature = null;
+                foreach (var profile in profiles)
+                {
+                    i = 3;
+                    feature = profile.GetFeaturebyType(request.GetType());
+                    i = 4;
+                    if (feature == null)
+                    {
+                        continue;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+                i = 5;
+                IConfirmation confrim = JsonConvert.DeserializeObject(payload, feature.GetConfirmationType()) as IConfirmation;
+                i = 6;
+                confrim.SetRequest(request);
+                i = 7;
+                result.Confirmation = confrim;
+
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+                logger.Error(string.Format(i+"UnPackPayloadbyCallResult Data:[{0},{1}] Ex: {2}", uniqueId, payload, ex.ToString()), "UnPack");
+
+            }
+            return result;
+
+        }
+
+        private IRequest UnPackPayloadbyCallError(Queue requestQueue, string uniqueId)
+        {
+            IRequest sentMsg = requestQueue.RestoreRequest(uniqueId);
+
+            return sentMsg;
+
+        }
+        #endregion
+
+
+    }
+}
+

+ 297 - 0
EVCB_OCPP.WSServer/Message/OCPP20MessageHandler.cs

@@ -0,0 +1,297 @@
+using EVCB_OCPP.Packet.Messages;
+using EVCB_OCPP.Packet.Messages.Basic;
+using EVCB_OCPP20.Packet.Features;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NLog;
+using OCPPServer.Protocol;
+using System;
+using I20Confirmation = EVCB_OCPP20.Packet.Messages.IConfirmation;
+using I20Request = EVCB_OCPP20.Packet.Messages.IRequest;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    /// <summary>
+    /// 實現 OCPP20 基本傳送規範,
+    /// 1.訊息 基本格式,將訊息包裝成 Call 、CallResult、CallError 三種格式
+    /// 2.OCPP 定義的傳送規則:交易相關的訊息必須依照時序性傳送,一個傳完才能接著送下一個(忽略規則 由Center System定義)
+    /// </summary>
+    internal class OCPP20MessageHandler
+    {
+        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
+
+
+
+
+        /// <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
+                                BasicMessageResult baseResult = UnPackPayloadbyCall(msg.Action, msg.Payload.ToString());
+                                Actions action = Actions.None;
+                                Enum.TryParse<Actions>(msg.Action, out action);
+                                result.Action = msg.Action;
+                                if (baseResult.Request20 != null)
+                                {
+                                    if (baseResult.Request20.Validate())
+                                    {
+                                        result.Id = TYPENUMBER_CALL;
+                                        result.Message = baseResult.Request20;
+
+                                    }
+                                    else
+                                    {
+
+                                        string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(),
+                                             OCPPErrorDescription.OccurenceConstraintViolation);
+                                        result.Id = TYPENUMBER_CALL;
+                                        result.Message = baseResult.Request20;
+                                        result.Success = false;
+                                        result.CallErrorMsg = replyMsg;
+                                        result.Exception = new Exception("Validation Failed");
+
+                                    }
+                                }
+                                else
+                                {
+
+                                    string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation, OCPPErrorDescription.OccurenceConstraintViolation);
+                                    result.Id = TYPENUMBER_CALL;
+                                    result.Message = baseResult.Request20;
+                                    result.Success = false;
+                                    result.CallErrorMsg = replyMsg;
+                                    result.Exception = baseResult.Exception;
+                                }
+
+                            }
+                            break;
+                        case TYPENUMBER_CALLRESULT:
+                            {
+                                BasicMessageResult baseResult = UnPackPayloadbyCallResult(client.queue20, msg.Id, msg.Payload.ToString());
+
+                                if (baseResult.Confirmation20 != null)
+                                {
+
+                                    if (baseResult.Confirmation20.Validate())
+                                    {
+                                        result.Id = TYPENUMBER_CALLRESULT;
+                                        result.Message = baseResult.Confirmation20;
+                                        result.Action = baseResult.Confirmation20.GetRequest().Action;
+                                        //return data
+                                    }
+                                    else
+                                    {
+                                        string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(),
+                                      OCPPErrorDescription.OccurenceConstraintViolation);
+                                        result.Id = TYPENUMBER_CALLRESULT;
+                                        result.Message = baseResult.Confirmation20;
+                                        result.Success = false;
+                                        result.CallErrorMsg = replyMsg;
+                                        result.Exception = new Exception("Validate Failed");
+                                    }
+                                }
+                                else
+                                {
+                                    string replyMsg = BasicMessageHandler.GenerateCallError(msg.Id, OCPPErrorCodes.OccurenceConstraintViolation.ToString(),
+                                   OCPPErrorDescription.OccurenceConstraintViolation);
+                                    result.Id = TYPENUMBER_CALLRESULT;
+                                    result.Message = baseResult.Confirmation20;
+                                    result.Success = false;
+                                    result.CallErrorMsg = replyMsg;
+                                    result.Exception = baseResult.Exception;
+                                }
+
+                            }
+                            break;
+                        case TYPENUMBER_CALLERROR:
+                            {
+                                result.Id = TYPENUMBER_CALLERROR;
+                                // var sentRequest = UnPackPayloadbyCallError(client.queue, msg.Id);
+
+                                //if (sentRequest != null)
+                                //{
+                                //    I20Request request = sentRequest as I20Request;
+                                //    result.Action = request.Action;
+
+                                //    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)
+            {
+                if (string.IsNullOrEmpty(result.UUID))
+                {
+                    result.UUID = data.Substring(4, 39);
+                    result.UUID = result.UUID.Split(new string[] { "\"," }, StringSplitOptions.None)[0];
+                }
+                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
+            {
+                if (message.StartsWith("[4,\""))
+                {
+                    message = message.Replace('{', '"');
+                    message = message.Replace('}', '"');
+                }
+                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("Parse Error=> {0} Problem: {0}", message, ex.Message));
+
+            }
+
+
+
+        }
+
+
+        private BasicMessageResult UnPackPayloadbyCall(string action, string payload)
+        {
+
+            BasicMessageResult result = new BasicMessageResult();
+            try
+            {
+
+                result.Request20 = (I20Request)(JsonConvert.DeserializeObject(payload, Type.GetType("EVCB_OCPP20.Packet.Messages." + action + "Request,EVCB_OCPP20.Packet")));
+
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+                logger.Error(string.Format("[{0}]UnPackPayloadbyCall Ex: {1}", action, ex.Message), "UnPack");
+
+            }
+
+            return result;
+
+
+        }
+
+        private BasicMessageResult UnPackPayloadbyCallResult(EVCB_OCPP20.Packet.Messages.Basic.Queue requestQueue, string uniqueId, string payload)
+        {
+            BasicMessageResult result = new BasicMessageResult();
+            try
+            {
+                I20Request request = requestQueue.RestoreRequest(uniqueId);
+                I20Confirmation confrim = JsonConvert.DeserializeObject(payload, Type.GetType("EVCB_OCPP20.Packet.Messages." + request.Action + "Response,EVCB_OCPP20.Packet")) as I20Confirmation;
+                confrim.SetRequest(request);
+                result.Confirmation20 = confrim;
+
+            }
+            catch (Exception ex)
+            {
+                result.Exception = ex;
+                logger.Error(string.Format("UnPackPayloadbyCallResult Data:[{0},{1}] Ex: {2}", uniqueId, payload, ex.ToString()), "UnPack");
+
+            }
+            return result;
+
+        }
+
+
+
+        #endregion
+
+
+
+    }
+}

+ 86 - 0
EVCB_OCPP.WSServer/Message/RemoteTriggerHandler.cs

@@ -0,0 +1,86 @@
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.Packet.Features;
+using EVCB_OCPP.Packet.Messages;
+using EVCB_OCPP.Packet.Messages.RemoteTrigger;
+using OCPPServer.Protocol;
+using System;
+using System.Linq;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    internal partial class ProfileHandler
+    {
+
+
+        internal MessageResult ExecuteRemoteTriggerConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.TriggerMessage:
+                    {
+                        TriggerMessageConfirmation _confirm = confirm as TriggerMessageConfirmation;
+                        TriggerMessageRequest _request = _confirm.GetRequest() as TriggerMessageRequest;
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;//OK
+                                operation.EVSE_Value = _confirm.status.ToString();
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", confirm.GetType().ToString().Replace("OCPPPackage.Messages.RemoteTrigger.", "")));
+                    }
+                    break;
+            }
+            return result;
+        }
+
+
+        internal MessageResult ReceivedRemoteTriggerError(Actions action, string errorMsg, ClientData session, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.TriggerMessage:
+                    {
+                        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.UtcNow;
+                                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;
+
+        }
+    }
+}

+ 125 - 0
EVCB_OCPP.WSServer/Message/ReservationProfileHandler.cs

@@ -0,0 +1,125 @@
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.Packet.Features;
+using EVCB_OCPP.Packet.Messages;
+using EVCB_OCPP.Packet.Messages.Reservation;
+using OCPPServer.Protocol;
+using System;
+using System.Linq;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    internal partial class ProfileHandler
+    {
+
+
+        internal MessageResult ExecuteReservationConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.ReserveNow:
+                    {
+                        ReserveNowConfirmation _confirm = confirm as ReserveNowConfirmation;
+                        ReserveNowRequest _request = _confirm.GetRequest() as ReserveNowRequest;
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;//OK
+                                operation.EVSE_Value = _confirm.status.ToString();
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.CancelReservation:
+                    {
+                        CancelReservationConfirmation _confirm = confirm as CancelReservationConfirmation;
+                        CancelReservationRequest _request = _confirm.GetRequest() as CancelReservationRequest;
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;//OK
+                                operation.EVSE_Value = _confirm.status.ToString();
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", confirm.GetType().ToString().Replace("OCPPPackage.Messages.RemoteTrigger.", "")));
+                    }
+                    break;
+            }
+            return result;
+        }
+
+
+        internal MessageResult ExecuteReservationError(Actions action, string errorMsg, ClientData session, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.ReserveNow:
+                    {
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)255;//錯誤
+                                operation.EVSE_Value = errorMsg;
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.CancelReservation:
+                    {
+                        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.UtcNow;
+                                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;
+
+        }
+    }
+}

+ 73 - 0
EVCB_OCPP.WSServer/Message/SecurityProfileHandler.cs

@@ -0,0 +1,73 @@
+using EVCB_OCPP.Packet.Features;
+using EVCB_OCPP.Packet.Messages;
+using OCPPServer.Protocol;
+using System;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    internal partial class ProfileHandler
+    {
+        internal MessageResult ExecuteSecurityRequest(Actions action, ClientData session, IRequest request)
+        {
+            MessageResult result = new MessageResult() { Success = false };
+
+            try
+            {
+                switch (action)
+                {
+
+
+                    default:
+                        {
+                            Console.WriteLine(string.Format("Not Implement {0} Logic(ExecuteCoreRequest)", request.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
+                        }
+                        break;
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Fatal(string.Format("chargeBoxId:{0} {1}", session.ChargeBoxId, action));
+                logger.Fatal(string.Format("Data {0}", request.ToString()));
+                logger.Fatal(string.Format("Error {0}", ex.ToString()));
+                result.Exception = ex;
+            }
+
+
+
+            return result;
+        }
+        internal MessageResult ExecuteSecurityConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = false };
+
+            switch (action)
+            {
+
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", confirm.GetType().ToString().Replace("OCPPPackage.Messages.RemoteTrigger.", "")));
+                    }
+                    break;
+            }
+            return result;
+        }
+
+
+        internal MessageResult ReceivedSecurityError(Actions action, string errorMsg, ClientData session, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", action));
+                    }
+                    break;
+            }
+            return result;
+
+        }
+    }
+}

+ 208 - 0
EVCB_OCPP.WSServer/Message/SmartChargingProfileHandler.cs

@@ -0,0 +1,208 @@
+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.SmartCharging;
+using EVCB_OCPP.Packet.Messages.SubTypes;
+using Newtonsoft.Json;
+using OCPPServer.Protocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace EVCB_OCPP.WSServer.Message
+{
+    internal partial class ProfileHandler
+    {
+
+
+        internal void SetChargingProfile(string chargeBoxId, decimal value, ChargingRateUnitType unit)
+        {
+            using (var db = new MainDBContext())
+            {
+                var _setProfileRequest = new SetChargingProfileRequest()
+                {
+                    connectorId = 0,
+                    csChargingProfiles = new Packet.Messages.SubTypes.csChargingProfiles()
+                    {
+                        chargingProfileId = 1,
+                        chargingProfileKind = Packet.Messages.SubTypes.ChargingProfileKindType.Recurring,
+                        chargingProfilePurpose = Packet.Messages.SubTypes.ChargingProfilePurposeType.ChargePointMaxProfile,
+                        chargingSchedule = new Packet.Messages.SubTypes.ChargingSchedule()
+                        {
+                            chargingRateUnit = unit,
+                            chargingSchedulePeriod = new List<Packet.Messages.SubTypes.ChargingSchedulePeriod>()
+                                                    {
+                                                        new Packet.Messages.SubTypes.ChargingSchedulePeriod(){  startPeriod=0, limit=value*1000}
+                                                    },
+                            duration = 60,
+
+                        },
+                        recurrencyKind = Packet.Messages.SubTypes.RecurrencyKindType.Daily,
+                        stackLevel = 1,
+
+                    }
+                };
+
+                db.ServerMessage.Add(new ServerMessage()
+                {
+                    ChargeBoxId = chargeBoxId,
+                    CreatedBy = "Server",
+                    CreatedOn = DateTime.UtcNow,
+                    OutAction = _setProfileRequest.Action.ToString(),
+                    OutRequest = JsonConvert.SerializeObject(_setProfileRequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                    SerialNo = Guid.Empty.ToString(),
+                    InMessage = string.Empty
+
+                });
+                db.SaveChanges();
+            }
+        }
+
+
+        internal void ClearChargingProfile(string chargeBoxId)
+        {
+            using (var db = new MainDBContext())
+            {
+                var _clearProfileRequest = new ClearChargingProfileRequest()
+                {
+                    connectorId = 0,
+                    chargingProfilePurpose = ChargingProfilePurposeType.ChargePointMaxProfile,
+
+                };
+
+                db.ServerMessage.Add(new ServerMessage()
+                {
+                    ChargeBoxId = chargeBoxId,
+                    CreatedBy = "Server",
+                    CreatedOn = DateTime.UtcNow,
+                    OutAction = _clearProfileRequest.Action.ToString(),
+                    OutRequest = JsonConvert.SerializeObject(_clearProfileRequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                    SerialNo = Guid.Empty.ToString(),
+                    InMessage = string.Empty
+
+                });
+
+                db.SaveChanges();
+            }
+        }
+
+
+
+
+
+        internal MessageResult ExecuteSmartChargingConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.ClearChargingProfile:
+
+                    {
+                        ClearChargingProfileConfirmation _confirm = confirm as ClearChargingProfileConfirmation;
+                        ClearChargingProfileRequest _request = _confirm.GetRequest() as ClearChargingProfileRequest;
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;//OK
+                                operation.EVSE_Value = _confirm.status.ToString();
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.SetChargingProfile:
+                    {
+                        SetChargingProfileConfirmation _confirm = confirm as SetChargingProfileConfirmation;
+                        SetChargingProfileRequest _request = _confirm.GetRequest() as SetChargingProfileRequest;
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;//OK
+                                operation.EVSE_Value = _confirm.status.ToString();
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                case Actions.GetCompositeSchedule:
+                    {
+                        GetCompositeScheduleConfirmation _confirm = confirm as GetCompositeScheduleConfirmation;
+                        GetCompositeScheduleRequest _request = _confirm.GetRequest() as GetCompositeScheduleRequest;
+                        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.UtcNow;
+                                operation.Status = 1;//電樁有回覆
+                                operation.EVSE_Status = (int)_confirm.status;//OK
+                                operation.EVSE_Value = JsonConvert.SerializeObject(_confirm.chargingSchedule, Formatting.None);
+                                db.SaveChanges();
+                            }
+
+                        }
+                    }
+                    break;
+                default:
+                    {
+                        Console.WriteLine(string.Format("Not Implement {0} Logic", confirm.GetType().ToString().Replace("OCPPPackage.Messages.RemoteTrigger.", "")));
+                    }
+                    break;
+            }
+            return result;
+        }
+
+
+        internal MessageResult ReceivedSmartChargingError(Actions action, string errorMsg, ClientData session, string requestId)
+        {
+            MessageResult result = new MessageResult() { Success = true };
+
+            switch (action)
+            {
+                case Actions.ClearChargingProfile:
+                case Actions.SetChargingProfile:
+                case Actions.GetCompositeSchedule:
+                    {
+                        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.UtcNow;
+                                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;
+
+        }
+    }
+}

+ 48 - 0
EVCB_OCPP.WSServer/NLog.config

@@ -0,0 +1,48 @@
+<?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}" />
+    <target xsi:type="Console" name="Console"
+            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" />
+    <logger name="*" minlevel="Trace" writeTo="Console" />
+  </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>

+ 46 - 0
EVCB_OCPP.WSServer/Program.cs

@@ -0,0 +1,46 @@
+using Newtonsoft.Json;
+using System;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer
+{
+   
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            Console.WriteLine("====================================================================================================");
+            Console.WriteLine("====================================================================================================");
+            Console.WriteLine("==                                                                                                ==");
+            Console.WriteLine("==       ------------               -----------      -------------         -------------          ==");
+            Console.WriteLine("==    ---            ---       ----                  ----------------      ----------------       ==");
+            Console.WriteLine("==    ---            ---     ----                    ----            ---   ----            ---    ==");
+            Console.WriteLine("==    ---            ---    ----                     ----            ---   ----            ---    ==");
+            Console.WriteLine("==    ---            ---    ----                     ---- -------------    ---- -------------     ==");
+            Console.WriteLine("==    ---            ---    ----                     ---- -----------      ---- -----------       ==");
+            Console.WriteLine("==    ---            ---      ----                   ----                  ----                   ==");
+            Console.WriteLine("==    ---            ---        ----                 ----                  ----                   ==");
+            Console.WriteLine("==       -----------                -----------      ----                  ----                   ==");
+            Console.WriteLine("==                                                                                                ==");
+            Console.WriteLine("====================================================================================================");
+            Console.WriteLine("====================================================================================================");
+            ProtalServer s = new ProtalServer();
+            Console.WriteLine("Starting Server...");
+            s.Start();
+
+            Console.Read();
+        }
+
+        public static object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+        {
+            DateTime? timevalue = null;
+            if (reader.Value != null)
+            {
+                DateTime date = ((DateTime)reader.Value).ToLocalTime();
+                timevalue = new DateTime(date.Year, date.Month, date.Day, date.TimeOfDay.Hours, date.TimeOfDay.Minutes, date.TimeOfDay.Seconds, 000);
+            }
+            return timevalue;
+        }
+    }
+}

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

@@ -0,0 +1,38 @@
+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.4.0")]
+[assembly: AssemblyVersion("1.0.4.0")]
+[assembly: AssemblyFileVersion("1.0.4.0")]
+
+[assembly: AssemblyInformationalVersion("499dc33")]

+ 2100 - 0
EVCB_OCPP.WSServer/ProtalServer.cs

@@ -0,0 +1,2100 @@
+using Dapper;
+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.Basic;
+using EVCB_OCPP.Packet.Messages.Core;
+using EVCB_OCPP.Packet.Messages.RemoteTrigger;
+using EVCB_OCPP.WSServer.Dto;
+using EVCB_OCPP.WSServer.Helper;
+using EVCB_OCPP.WSServer.Message;
+using EVCB_OCPP.WSServer.Service;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NLog;
+using OCPPServer.Protocol;
+using OCPPServer.SubProtocol;
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Config;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Data.Entity;
+using System.Data.SqlClient;
+using System.Diagnostics;
+using System.Linq;
+using System.Security.Authentication;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+
+namespace EVCB_OCPP.WSServer
+{
+    public class DestroyRequest : IRequest
+    {
+        public string Action { get; set; }
+
+        public bool TransactionRelated()
+        {
+            return false;
+        }
+
+        public bool Validate()
+        {
+            return true;
+        }
+    }
+    internal class ProtalServer
+    {
+        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        private OuterHttpClient httpClient = new OuterHttpClient();
+        private DateTime lastcheckdt = DateTime.UtcNow.AddSeconds(-20);
+        private Dictionary<string, ClientData> clientDic = new Dictionary<string, ClientData>();
+        private readonly Object _lockClientDic = new object();
+        private readonly Object _lockConfirmPacketList = new object();
+        private ProfileHandler profileHandler = new ProfileHandler();
+        private List<NeedConfirmMessage> needConfirmPacketList = new List<NeedConfirmMessage>();
+        private DateTime checkUpdateDt = DateTime.UtcNow;
+        private DateTime _CheckFeeDt = DateTime.UtcNow;
+        private DateTime _CheckWeatherDt = DateTime.UtcNow;
+        private LoadingBalanceService _loadingBalanceService = new LoadingBalanceService();
+        private Dictionary<string, TCCWeatherDto> TCCStationDic = new Dictionary<string, TCCWeatherDto>();
+
+        private List<string> needConfirmActions = new List<string>()
+        {
+             "GetConfiguration",
+             "ChangeConfiguration",
+             "RemoteStartTransaction",
+             "RemoteStopTransaction",
+             "ChangeAvailability",
+             "ClearCache",
+             "DataTransfer",
+             "Reset",
+             "UnlockConnector",
+             "TriggerMessage",
+             "GetDiagnostics",
+             "UpdateFirmware",
+             "GetLocalListVersion",
+             "SendLocalList",
+             "SetChargingProfile",
+             "ClearChargingProfile",
+             "GetCompositeSchedule",
+             "ReserveNow",
+             "CancelReservation",
+             "ExtendedTriggerMessage"
+        };
+        private List<Profile> profiles = new List<Profile>()
+        {
+             new CoreProfile(),
+             new FirmwareManagementProfile(),
+             new ReservationProfile(),
+             new RemoteTriggerProfile(),
+             new SmartChargingProfile(),
+             new LocalAuthListManagementProfile(),
+             new SecurityProfile(),
+        };
+        private CancellationTokenSource _cts = new CancellationTokenSource();
+        private CancellationToken _ct;
+        private string _ocpp20NetworkSetting = "";
+
+        internal ProtalServer()
+        {
+            _ct = _cts.Token;
+            WarmUpLog();
+
+
+
+
+        }
+
+
+        internal void Start()
+        {
+
+            if (!GlobalConfig.LoadAPPConfig())
+            {
+                Console.WriteLine("Please check App.Config setting .");
+                return;
+            }
+            ReadTCCSetting();
+            OpenNetwork();
+
+            Task serverCommandTask = new Task(ServerMessageTrigger, _ct);
+            serverCommandTask.Start();
+
+            Task serverUpdateTask = new Task(ServerUpdateTrigger, _ct);
+            serverUpdateTask.Start();
+
+            Task serverSetFeeTask = new Task(ServerSetFeeTrigger, _ct);
+            serverSetFeeTask.Start();
+
+            Task serverWeatherNotificationTask = new Task(ServerWeatherNotificationTrigger, _ct);
+            serverWeatherNotificationTask.Start();
+
+            Task serverBeatTask = new Task(HeartBeatCheckTrigger, _ct);
+            serverBeatTask.Start();
+
+            Task serverHealthTask = new Task(HealthCheckTrigger, _ct);
+            serverHealthTask.Start();
+
+            var ocpp20NetworkSetting = new EVCB_OCPP20.Packet.Messages.SetNetworkProfileRequest()
+            {
+                ConfigurationSlot = 0,
+                ConnectionData = new EVCB_OCPP20.Packet.DataTypes.NetworkConnectionProfileType()
+                {
+                    OcppVersion = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPVersionEnumType.OCPP20,
+                    OcppTransport = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPTransportEnumType.JSON,
+                    MessageTimeout = 30,
+                    OcppCsmsUrl = "ws://ocpp.phihong.com.tw:5004",
+                    OcppInterface = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPInterfaceEnumType.Wired0
+                }
+
+            };
+
+            _ocpp20NetworkSetting = JsonConvert.SerializeObject(ocpp20NetworkSetting, Formatting.None);
+
+            while (true)
+            {
+                var input = Console.ReadLine();
+
+                switch (input.ToLower())
+                {
+                    case "stop":
+                        Console.WriteLine("Command stop");
+                        Stop();
+                        break;
+
+                    case "gc":
+                        Console.WriteLine("Command GC");
+                        GC.Collect();
+                        break;
+
+                    case "lc":
+                        {
+                            Console.WriteLine("Command List Clients");
+                            Dictionary<string, ClientData> _copyClientDic = null;
+                            lock (_lockClientDic)
+                            {
+                                _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+
+                            }
+                            var list = _copyClientDic.Select(c => c.Value).ToList();
+                            var locations = _copyClientDic.Where(x => !string.IsNullOrEmpty(x.Value.StationLocation)).Distinct().Select(x => x.Value.StationLocation).ToList();
+                            int i = 1;
+                            foreach (var c in list)
+                            {
+                                Console.WriteLine(i + ":" + c.ChargeBoxId + " " + c.SessionID);
+                                i++;
+                            }
+
+                            foreach (var c in TCCStationDic)
+                            {
+                                Console.WriteLine(i + ":" + c.Key + "-" + c.Value.Temperature + "/" + c.Value.WeatherID);
+                                i++;
+                            }
+
+                        }
+                        break;
+                    case "lcn":
+                        {
+                            Console.WriteLine("Command List Customer Name");
+                            Dictionary<string, ClientData> _copyClientDic = null;
+                            lock (_lockClientDic)
+                            {
+                                _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+
+                            }
+                            var lcn = clientDic.Select(c => c.Value.CustomerName).Distinct().ToList();
+                            int iLcn = 1;
+                            foreach (var c in lcn)
+                            {
+                                Console.WriteLine(iLcn + ":" + c + ":" + clientDic.Where(z => z.Value.CustomerName == c).Count().ToString());
+                                iLcn++;
+                            }
+
+                        }
+                        break;
+                    case "help":
+                        Console.WriteLine("Command help!!");
+                        Console.WriteLine("lcn : List Customer Name");
+                        Console.WriteLine("gc : GC Collect");
+                        Console.WriteLine("lc : List Clients");
+                        Console.WriteLine("cls : clear console");
+                        Console.WriteLine("silent : silent");
+                        Console.WriteLine("show : show log");
+                        // logger.Info("rcl : show Real Connection Limit");
+                        break;
+                    case "cls":
+                        Console.WriteLine("Command clear");
+                        Console.Clear();
+                        break;
+
+                    case "silent":
+                        Console.WriteLine("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 = "Warn";
+                        }
+                        xe.Save("NLog.config");
+                        break;
+                    case "show":
+                        Console.WriteLine("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;
+                }
+            }
+        }
+
+        internal void Stop()
+        {
+            if (_cts != null)
+            {
+                _cts.Cancel();
+            }
+        }
+
+        private void CheckVersion(string chargeBoxId)
+        {
+            if (string.IsNullOrEmpty(chargeBoxId)) return;
+            using (var db = new MainDBContext())
+            {
+                db.ServerMessage.Add(new ServerMessage()
+                {
+                    ChargeBoxId = chargeBoxId,
+                    CreatedBy = "Server",
+                    CreatedOn = DateTime.UtcNow,
+                    OutAction = Actions.DataTransfer.ToString(),
+                    OutRequest = JsonConvert.SerializeObject(
+                          new DataTransferRequest()
+                          {
+                              messageId = "ID_FirmwareVersion",
+                              vendorId = "Phihong Technology"
+
+                          },
+                          new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                    SerialNo = Guid.NewGuid().ToString(),
+                    InMessage = string.Empty
+
+                });
+
+                db.SaveChanges();
+            }
+
+        }
+
+
+        private void CheckEVSEConfigure(string chargeBoxId)
+        {
+            if (string.IsNullOrEmpty(chargeBoxId)) return;
+            using (var db = new MainDBContext())
+            {
+                db.ServerMessage.Add(new ServerMessage()
+                {
+                    ChargeBoxId = chargeBoxId,
+                    CreatedBy = "Server",
+                    CreatedOn = DateTime.UtcNow,
+                    OutAction = Actions.GetConfiguration.ToString(),
+                    OutRequest = JsonConvert.SerializeObject(
+                            new GetConfigurationRequest()
+                            {
+                                key = new List<string>()
+
+                            },
+                            new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                    SerialNo = Guid.NewGuid().ToString(),
+                    InMessage = string.Empty
+
+                }); ;
+
+                db.SaveChanges();
+
+            }
+        }
+
+
+        #region 台泥
+
+
+        private void ReadTCCSetting()
+        {
+            using (var db = new MainDBContext())
+            {
+                var info = db.Customer.Where(x => x.Id == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8")).Select(x => new { x.ApiUrl, x.ApiKey }).FirstOrDefault();
+                GlobalConfig.TCC_API_URL = info.ApiUrl;
+                GlobalConfig.TCC_SALTKEY = info.ApiKey;
+
+            }
+        }
+        async private Task<TCCStationInfoDto> GetStationInfo(string machineId)
+        {
+            TCCStationInfoDto stationInfo = null;
+            if (string.IsNullOrEmpty(machineId)) return stationInfo;
+            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input);
+                string sql = " SELECT CAST([Latitude] as DECIMAL(5,2)) Lat,CAST([Longitude] as DECIMAL(5, 2))  Long ,SUBSTRING([Address],1,3) as zipcode FROM[StationMachine]  left join [dbo].[Station]" +
+               "  on[StationMachine].StationId = Station.[Id]  where StationMachine.MachineId=@MachineId; ";
+
+                var result = await conn.QueryAsync<TCCStationInfoDto>(sql, parameters);
+                stationInfo = result.FirstOrDefault();
+            }
+
+            return stationInfo;
+        }
+        #endregion
+
+        private void OpenNetwork()
+        {
+
+            //載入OCPP Protocol
+            var appServer = new OCPPWSServer(new List<OCPPSubProtocol>() { new OCPPSubProtocol(), new OCPPSubProtocol(" ocpp1.6"), new OCPPSubProtocol("ocpp2.0") });
+
+            List<IListenerConfig> llistener = new List<IListenerConfig>();
+            //System.Net.IPAddress.Any.ToString()
+            // llistener.Add(new ListenerConfig { Ip = "", Port = Convert.ToInt32(wssserverPort), Backlog = 100, Security = serverSecurity });
+            llistener.Add(new ListenerConfig { Ip = System.Net.IPAddress.Any.ToString(), Port = Convert.ToInt32(GlobalConfig.GetWS_Port()), Backlog = 100, Security = "None" });
+            llistener.Add(new ListenerConfig { Ip = System.Net.IPAddress.Any.ToString(), Port = Convert.ToInt32(GlobalConfig.GetWSS_Port()), Backlog = 100, Security = SslProtocols.Tls12.ToString() });
+
+            // llistener.Add(new ListenerConfig { Ip = System.Net.IPAddress.Any.ToString(), Port = Convert.ToInt32(GlobalConfig.GetWSS_Port()), Backlog = 100, Security = SslProtocols.Tls11.ToString() });
+            var config = ConfigurationManager.GetSection("superSocket") as IConfigurationSource;
+            ICertificateConfig Certificate = config.Servers.ElementAt(0).Certificate;
+            IEnumerable<IListenerConfig> listeners = llistener;
+
+            //設定server config
+            var serverConfig = new ServerConfig
+            {
+
+                //Port = Convert.ToInt32(2012),
+                //Ip = "172.17.40.13",
+                MaxRequestLength = 204800,
+                //Security = serverSecurity,
+                Certificate = Certificate,
+                Listeners = listeners,
+                //  LogAllSocketException = true,
+                KeepAliveTime = 10,
+                // LogBasicSessionActivity = true
+
+
+            };
+
+            //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)
+        {
+            //  Console.WriteLine(session.RemoteEndPoint.Address);
+            WriteMachineLog(session, string.Format("CloseReason: {0}", value), "Connection", "");
+            RemoveClient(session);
+            // close Connection
+        }
+
+        private void AppServer_NewSessionConnected(ClientData session)
+        {
+            Console.WriteLine("++++++++++++++++");
+            try
+            {
+                lock (_lockClientDic)
+                {
+                    bool isNotSupported = session.SecWebSocketProtocol.Contains("ocpp1.6") ? false : session.SecWebSocketProtocol.Contains("ocpp2.0") ? false : true;
+                    if (isNotSupported)
+                    {
+                        //logger.Debug(string.Format("ChargeBoxId:{0} SecWebSocketProtocol:{1} NotSupported", session.ChargeBoxId, session.SecWebSocketProtocol));
+                        WriteMachineLog(session, string.Format("SecWebSocketProtocol:{0} NotSupported", session.SecWebSocketProtocol), "Connection", "");
+                        return;
+                    }
+                    ClientData _removeClient = null;
+
+
+                    clientDic.TryGetValue(session.ChargeBoxId, out _removeClient);
+                    if (_removeClient != null)
+                    {
+                        WriteMachineLog(_removeClient, "Duplicate Logins", "Connection", "");
+                        _removeClient.Close(CloseReason.ServerShutdown);
+                        RemoveClient(_removeClient);
+                    }
+
+                    clientDic.Add(session.ChargeBoxId, session);
+                    session.m_ReceiveData += new ClientData.OCPPClientDataEventHandler<ClientData, String>(ReceivedMessage);
+                    // logger.Debug("------------New " + (session == null ? "Oops" : session.ChargeBoxId));
+                    WriteMachineLog(session, "NewSessionConnected", "Connection", "");
+
+                    using (var db = new MainDBContext())
+                    {
+                        var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+                        if (machine != null)
+                        {
+                            machine.ConnectionType = session.Origin.Contains("https") ? 2 : 1;
+                            db.SaveChanges();
+                        }
+                    }
+
+
+                    // CheckEVSEConfigure(session.ChargeBoxId);
+                }
+            }
+            catch (Exception ex)
+            {
+                logger.Error(string.Format("NewSessionConnected Ex: {0}", ex.ToString()));
+            }
+
+
+        }
+
+        async private void ReceivedMessage(ClientData session, string rawdata)
+        {
+            try
+            {
+                BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+                MessageResult analysisResult = msgAnalyser.AnalysisReceiveData(session, rawdata);
+
+                WriteMachineLog(session, rawdata,
+                     string.Format("{0} {1}", string.IsNullOrEmpty(analysisResult.Action) ? "unknown" : analysisResult.Action, analysisResult.Id == 2 ? "Request" : (analysisResult.Id == 3 ? "Confirmation" : "Error")), analysisResult.Exception == null ? "" : analysisResult.Exception.Message);
+
+                if (session.ResetSecurityProfile)
+                {
+                    logger.Error(string.Format("[{0}] ChargeBoxId:{1} ResetSecurityProfile", DateTime.UtcNow, session.ChargeBoxId));
+                    RemoveClient(session);
+                    return;
+                }
+
+
+                if (!analysisResult.Success)
+                {
+                    //解析RawData就發生錯誤
+                    if (!string.IsNullOrEmpty(analysisResult.CallErrorMsg))
+                    {
+                        Send(session, analysisResult.CallErrorMsg, string.Format("{0} {1}", analysisResult.Action, "Error"));
+                    }
+                    else
+                    {
+                        if (analysisResult.Message == null)
+                        {
+                            string replyMsg = BasicMessageHandler.GenerateCallError(analysisResult.UUID, OCPPErrorCodes.InternalError, OCPPErrorDescription.InternalError);
+                            string errorMsg = string.Empty;
+                            if (analysisResult.Exception != null)
+                            {
+                                errorMsg = analysisResult.Exception.ToString();
+                            }
+
+                            Send(session, replyMsg, string.Format("{0} {1}", "unknown", "Error"), "EVSE's sent essage has parsed Failed. ");
+                        }
+                        else
+                        {
+                            BaseMessage _baseMsg = analysisResult.Message as BaseMessage;
+
+
+                            string replyMsg = BasicMessageHandler.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
+                {
+
+
+
+                    //if (session.ISOCPP20 && !(analysisResult.Action != Actions.BootNotification.ToString() || analysisResult.Action != Actions.Heartbeat.ToString()))
+                    //{
+                    //    Send(session, "Backend doesn't support this message.", string.Format("{0} {1}", analysisResult.Action, "Error"));
+                    //    return;
+                    //}
+                    switch (analysisResult.Id)
+                    {
+                        case BasicMessageHandler.TYPENUMBER_CALL:
+                            {
+                                if (!session.ISOCPP20)
+                                {
+                                    Actions action = Convertor.GetAction(analysisResult.Action);
+                                    ProcessRequestMessage(analysisResult, session, action);
+                                }
+                                else
+                                {
+                                    EVCB_OCPP20.Packet.Features.Actions action = Convertor.GetActionby20(analysisResult.Action);
+                                    MessageResult result = new MessageResult() { Success = true };
+                                    //ocpp20 處理
+                                    switch (action)
+                                    {
+
+                                        case EVCB_OCPP20.Packet.Features.Actions.BootNotification:
+                                            {
+                                                EVCB_OCPP20.Packet.Messages.BootNotificationRequest _request = (EVCB_OCPP20.Packet.Messages.IRequest)analysisResult.Message as EVCB_OCPP20.Packet.Messages.BootNotificationRequest;
+
+                                                var confirm = new EVCB_OCPP20.Packet.Messages.BootNotificationResponse() { CurrentTime = DateTime.UtcNow, Interval = 180, Status = EVCB_OCPP20.Packet.DataTypes.EnumTypes.RegistrationStatusEnumType.Pending };
+
+                                                result.Message = confirm;
+                                                result.Success = true;
+
+                                                string response = BasicMessageHandler.GenerateConfirmationofOCPP20(analysisResult.UUID, (EVCB_OCPP20.Packet.Messages.IConfirmation)result.Message);
+                                                Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Response"), result.Exception == null ? string.Empty : result.Exception.ToString());
+
+
+                                                var request = new EVCB_OCPP20.Packet.Messages.SetNetworkProfileRequest()
+                                                {
+                                                    ConfigurationSlot = 1,
+                                                    ConnectionData = new EVCB_OCPP20.Packet.DataTypes.NetworkConnectionProfileType()
+                                                    {
+                                                        OcppVersion = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPVersionEnumType.OCPP20,
+                                                        OcppTransport = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPTransportEnumType.JSON,
+                                                        MessageTimeout = 30,
+                                                        OcppCsmsUrl = "ws://ocpp.phihong.com.tw:5004",
+                                                        OcppInterface = EVCB_OCPP20.Packet.DataTypes.EnumTypes.OCPPInterfaceEnumType.Wired0
+                                                    }
+
+                                                };
+                                                var uuid = session.queue20.store(request);
+                                                string requestText = BasicMessageHandler.GenerateRequestofOCPP20(uuid, "SetNetworkProfile", request);
+                                                Send(session, requestText, "SetNetworkProfile");
+
+                                            }
+                                            break;
+                                        default:
+                                            {
+                                                logger.Error(string.Format("We don't implement messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
+                                            }
+                                            break;
+                                    }
+
+                                }
+
+
+                            }
+                            break;
+                        case BasicMessageHandler.TYPENUMBER_CALLRESULT:
+                            {
+                                if (!session.ISOCPP20)
+                                {
+                                    Actions action = Convertor.GetAction(analysisResult.Action);
+                                    ProcessConfirmationMessage(analysisResult, session, action);
+                                }
+                                else
+                                {
+                                    EVCB_OCPP20.Packet.Features.Actions action = Convertor.GetActionby20(analysisResult.Action);
+                                    MessageResult result = new MessageResult() { Success = true };
+                                    //ocpp20 處理
+                                    switch (action)
+                                    {
+
+                                        case EVCB_OCPP20.Packet.Features.Actions.SetNetworkProfile:
+                                            {
+                                                EVCB_OCPP20.Packet.Messages.SetNetworkProfileResponse response = (EVCB_OCPP20.Packet.Messages.IConfirmation)analysisResult.Message as EVCB_OCPP20.Packet.Messages.SetNetworkProfileResponse;
+
+                                                if (response.Status == EVCB_OCPP20.Packet.DataTypes.EnumTypes.SetNetworkProfileStatusEnumType.Accepted)
+                                                {
+                                                    var request = new EVCB_OCPP20.Packet.Messages.SetVariablesRequest()
+                                                    {
+                                                        SetVariableData = new List<EVCB_OCPP20.Packet.DataTypes.SetVariableDataType>()
+                                                         {
+                                                              new EVCB_OCPP20.Packet.DataTypes.SetVariableDataType()
+                                                              {
+                                                                    Component=new EVCB_OCPP20.Packet.DataTypes.ComponentType()
+                                                                    {
+                                                                         Name="OCPPCommCtrlr",
+
+                                                                    },
+                                                                     AttributeType= EVCB_OCPP20.Packet.DataTypes.EnumTypes.AttributeEnumType.Actual,
+                                                                     AttributeValue= JsonConvert.SerializeObject(new List<int>(){ 1 }),
+                                                                     Variable=new EVCB_OCPP20.Packet.DataTypes.VariableType()
+                                                                    {
+                                                                            Name="NetworkConfigurationPriority",
+
+                                                                    }
+
+
+                                                              }
+                                                         }
+
+                                                    };
+                                                    var uuid = session.queue20.store(request);
+                                                    string requestText = BasicMessageHandler.GenerateRequestofOCPP20(uuid, "SetVariables", request);
+                                                    Send(session, requestText, "SetVariables");
+                                                }
+
+                                            }
+                                            break;
+                                        case EVCB_OCPP20.Packet.Features.Actions.SetVariables:
+                                            {
+                                                EVCB_OCPP20.Packet.Messages.SetVariablesResponse response = (EVCB_OCPP20.Packet.Messages.IConfirmation)analysisResult.Message as EVCB_OCPP20.Packet.Messages.SetVariablesResponse;
+
+                                                if (response.SetVariableResult[0].AttributeStatus == EVCB_OCPP20.Packet.DataTypes.EnumTypes.SetVariableStatusEnumType.RebootRequired)
+                                                {
+                                                    var request = new EVCB_OCPP20.Packet.Messages.ResetRequest()
+                                                    {
+                                                        Type = EVCB_OCPP20.Packet.DataTypes.EnumTypes.ResetEnumType.OnIdle
+
+                                                    };
+                                                    var uuid = session.queue20.store(request);
+                                                    string requestText = BasicMessageHandler.GenerateRequestofOCPP20(uuid, "Reset", request);
+                                                    Send(session, requestText, "Reset");
+
+                                                }
+                                            }
+                                            break;
+                                        default:
+                                            {
+                                                logger.Error(string.Format("We don't implement messagetype:{0} of raw data :{1} by {2}", analysisResult.Id, rawdata, session.ChargeBoxId));
+                                            }
+                                            break;
+                                    }
+                                }
+
+                            }
+                            break;
+                        case BasicMessageHandler.TYPENUMBER_CALLERROR:
+                            {
+                                //只處理 丟出Request 收到Error的訊息                              
+                                if (analysisResult.Success && analysisResult.Message != null)
+                                {
+                                    Actions action = Convertor.GetAction(analysisResult.Action);
+                                    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;
+
+                    }
+                }
+
+                await Task.Delay(10);
+            }
+            catch (Exception ex)
+            {
+
+
+                if (ex.InnerException != null)
+                {
+                    logger.Error(string.Format("{0} **Inner Exception :{1} ", session.ChargeBoxId + rawdata, ex.ToString()));
+
+
+                }
+                else
+                {
+                    logger.Error(string.Format("{0} **Exception :{1} ", session.ChargeBoxId, ex.ToString()));
+                }
+            }
+
+
+        }
+
+
+
+        async private void ProcessRequestMessage(MessageResult analysisResult, ClientData session, Actions action)
+        {
+            BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+            if (!session.IsCheckIn && action != Actions.BootNotification)
+            {
+                string response = BasicMessageHandler.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":
+                        {
+
+                            var replyResult = await profileHandler.ExecuteCoreRequest(action, session, (IRequest)analysisResult.Message).ConfigureAwait(false);
+                            if (replyResult.Success)
+                            {
+                                string response = BasicMessageHandler.GenerateConfirmation(analysisResult.UUID, (IConfirmation)replyResult.Message);
+
+
+                                Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Confirmation"), replyResult.Exception == null ? string.Empty : replyResult.Exception.ToString());
+
+                                if (action == Actions.BootNotification && replyResult.Message is BootNotificationConfirmation)
+                                {
+                                    if (((BootNotificationConfirmation)replyResult.Message).status == Packet.Messages.SubTypes.RegistrationStatus.Accepted)
+                                    {
+                                        session.IsCheckIn = true;
+
+                                      
+                                        CheckVersion(session.ChargeBoxId);
+                                        CheckEVSEConfigure(session.ChargeBoxId);
+
+                                        if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
+                                        {
+
+                                            var stationInfo = await GetStationInfo(session.MachineId);
+                                            if (stationInfo != null)
+                                            {
+                                                session.StationLocation = string.Format("{0},{1}", stationInfo.Lat, stationInfo.Long);
+                                                session.StationName = stationInfo.ZipCode;
+                                            }
+                                            using (var db = new MainDBContext())
+                                            {
+                                                db.ServerMessage.Add(new ServerMessage()
+                                                {
+                                                    ChargeBoxId = session.ChargeBoxId,
+                                                    CreatedBy = "Server",
+                                                    CreatedOn = DateTime.UtcNow,
+                                                    OutAction = Actions.DataTransfer.ToString(),
+                                                    OutRequest = JsonConvert.SerializeObject(
+                                                          new DataTransferRequest()
+                                                          {
+                                                              messageId = "ID_Station_Location",
+                                                              vendorId = "Phihong Technology",
+                                                              data = JsonConvert.SerializeObject(new { stationName = session.StationName })
+
+                                                          },
+                                                          new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                                    SerialNo = Guid.NewGuid().ToString(),
+                                                    InMessage = string.Empty
+
+                                                });
+
+
+                                                db.ServerMessage.Add(new ServerMessage()
+                                                {
+                                                    ChargeBoxId = session.ChargeBoxId,
+                                                    CreatedBy = "Server",
+                                                    CreatedOn = DateTime.UtcNow,
+                                                    OutAction = Actions.ChangeConfiguration.ToString(),
+                                                    OutRequest = JsonConvert.SerializeObject(
+                                                         new ChangeConfigurationRequest()
+                                                         {
+                                                             key = "TimeOffset",
+                                                             value = "+08:00"
+
+                                                         },
+                                                         new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                                    SerialNo = Guid.NewGuid().ToString(),
+                                                    InMessage = string.Empty
+
+                                                });
+
+
+                                                if (!TCCStationDic.ContainsKey(session.StationLocation))
+                                                {
+
+                                                    _CheckWeatherDt = DateTime.UtcNow.AddDays(-1);
+                                                }
+                                                else
+                                                {
+
+                                                    db.ServerMessage.Add(new ServerMessage()
+                                                    {
+                                                        ChargeBoxId = session.ChargeBoxId,
+                                                        CreatedBy = "Server",
+                                                        CreatedOn = DateTime.UtcNow,
+                                                        OutAction = Actions.DataTransfer.ToString(),
+                                                        OutRequest = JsonConvert.SerializeObject(
+                                                        new DataTransferRequest()
+                                                        {
+                                                            messageId = "ID_Weather_Info",
+                                                            vendorId = "Phihong Technology",
+                                                            data = JsonConvert.SerializeObject(
+                                                                new
+                                                                {
+                                                                    weatherId = TCCStationDic[session.StationLocation].WeatherID,
+                                                                    Temperature = TCCStationDic[session.StationLocation].Temperature
+                                                                })
+                                                        },
+                                                        new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                                        SerialNo = Guid.NewGuid().ToString(),
+                                                        InMessage = string.Empty
+
+                                                    });
+
+                                                }
+
+
+                                                db.SaveChanges();
+
+
+                                            }
+
+                                        }
+
+
+
+
+                                    }
+                                    else
+                                    {
+                                        using (var db = new MainDBContext())
+                                        {
+                                            var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
+                                            if (machine != null)
+                                            {
+                                                if (machine.ConnectorType.Contains("6") || machine.ConnectorType.Contains("7") || machine.ConnectorType.Contains("8") || machine.ConnectorType.Contains("9"))
+                                                {
+                                                    session.IsAC = false;
+                                                }
+                                                machine.ConnectionType = session.Origin.Contains("https") ? 2 : 1;
+                                                db.SaveChanges();
+                                            }
+                                        }
+
+                                        await SetDefaultFee(session);
+                                    }
+                                }
+
+
+                                if (action == Actions.StartTransaction && session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8") && replyResult.Message is StartTransactionConfirmation)
+                                {
+
+                                    StartTransactionConfirmation confirm = (StartTransactionConfirmation)replyResult.Message;
+                                    StartTransactionRequest request = (StartTransactionRequest)analysisResult.Message;
+                                    using (var db = new MainDBContext())
+                                    {
+                                        db.ServerMessage.Add(new ServerMessage()
+                                        {
+                                            ChargeBoxId = session.ChargeBoxId,
+                                            CreatedBy = "Server",
+                                            CreatedOn = DateTime.UtcNow,
+                                            OutAction = Actions.DataTransfer.ToString(),
+                                            OutRequest = JsonConvert.SerializeObject(
+                                                    new DataTransferRequest()
+                                                    {
+                                                        messageId = "ID_GetTxUserInfo",
+                                                        vendorId = "Phihong Technology",
+                                                        data = JsonConvert.SerializeObject(new { txId = confirm.transactionId, ConnectorId = request.connectorId })
+                                                    },
+                                                    new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                            SerialNo = Guid.NewGuid().ToString(),
+                                            InMessage = string.Empty
+
+                                        });
+
+                                        db.SaveChanges();
+                                    }
+                                }
+
+                            }
+                            else
+                            {
+                                string response = BasicMessageHandler.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);
+                            }
+
+                            if (action == Actions.StartTransaction)
+                            {
+                                var stationId = _loadingBalanceService.GetStationIdByMachineId(session.MachineId);
+                                var _powerDic = _loadingBalanceService.GetSettingPower(stationId, session.MachineId);
+                                if (_powerDic != null)
+                                {
+                                    foreach (var kv in _powerDic)
+                                    {
+                                        try
+                                        {
+                                            string chargeBoxId = string.Empty;
+                                            //set profile
+                                            lock (_lockClientDic)
+                                            {
+                                                chargeBoxId = clientDic.Where(x => x.Value.MachineId == kv.Key).Select(x => x.Value.ChargeBoxId).FirstOrDefault();
+                                            }
+
+                                            if (chargeBoxId != null && kv.Value.HasValue)
+                                            {
+                                                profileHandler.SetChargingProfile(chargeBoxId, kv.Value.Value, Packet.Messages.SubTypes.ChargingRateUnitType.W);
+                                            }
+                                        }
+                                        catch (Exception ex)
+                                        {
+                                            logger.Error(string.Format("Set Profile Exception: {0}", ex.ToString()));
+                                        }
+
+                                    }
+                                }
+                            }
+
+                            if (action == Actions.StopTransaction)
+                            {
+                                var stationId = _loadingBalanceService.GetStationIdByMachineId(session.MachineId);
+                                if (_loadingBalanceService.IsNeedtoCancelSetting(stationId, session.MachineId, session.ChargeBoxId))
+                                {
+                                    //Clear  current profile    
+                                    profileHandler.ClearChargingProfile(session.ChargeBoxId);
+
+                                    var _powerDic = _loadingBalanceService.GetRerangeSettingPower(stationId);
+                                    if (_powerDic != null)
+                                    {
+                                        foreach (var kv in _powerDic)
+                                        {
+                                            try
+                                            {
+                                                string chargeBoxId = string.Empty;
+                                                //set profile
+                                                lock (_lockClientDic)
+                                                {
+                                                    chargeBoxId = clientDic.Where(x => x.Value.MachineId == kv.Key).Select(x => x.Value.ChargeBoxId).FirstOrDefault();
+                                                }
+
+                                                if (chargeBoxId != null && kv.Value.HasValue)
+                                                {
+                                                    profileHandler.SetChargingProfile(chargeBoxId, kv.Value.Value, Packet.Messages.SubTypes.ChargingRateUnitType.W);
+                                                }
+                                            }
+                                            catch (Exception ex)
+                                            {
+                                                logger.Error(string.Format("Set Profile Exception: {0}", ex.ToString()));
+                                            }
+
+                                        }
+                                    }
+
+                                }
+
+                            }
+
+                        }
+                        break;
+                    case "FirmwareManagement":
+                        {
+                            var replyResult = profileHandler.ExecuteFirmwareManagementRequest(action, session, (IRequest)analysisResult.Message);
+                            if (replyResult.Success)
+                            {
+                                string response = BasicMessageHandler.GenerateConfirmation(analysisResult.UUID, (IConfirmation)replyResult.Message);
+                                Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Confirmation", replyResult.Exception == null ? string.Empty : replyResult.Exception.ToString()));
+
+                            }
+                            else
+                            {
+                                string response = BasicMessageHandler.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;
+                    case "Security":
+                        {
+                            var replyResult = profileHandler.ExecuteSecurityRequest(action, session, (IRequest)analysisResult.Message);
+                            if (replyResult.Success)
+                            {
+                                string response = BasicMessageHandler.GenerateConfirmation(analysisResult.UUID, (IConfirmation)replyResult.Message);
+                                Send(session, response, string.Format("{0} {1}", analysisResult.Action, "Confirmation", replyResult.Exception == null ? string.Empty : replyResult.Exception.ToString()));
+
+                            }
+                            else
+                            {
+                                string response = BasicMessageHandler.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 = BasicMessageHandler.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 (ReConfirmMessage(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;
+                    case "FirmwareManagement":
+                        {
+                            confirmResult = profileHandler.ExecuteFirmwareManagementConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId);
+                        }
+                        break;
+                    case "RemoteTrigger":
+                        {
+                            confirmResult = profileHandler.ExecuteRemoteTriggerConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId);
+                        }
+                        break;
+                    case "Reservation":
+                        {
+                            confirmResult = profileHandler.ExecuteReservationConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId);
+                        }
+                        break;
+                    case "LocalAuthListManagement":
+                        {
+                            confirmResult = profileHandler.ExecuteLocalAuthListManagementConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId);
+                        }
+                        break;
+                    case "SmartCharging":
+                        {
+                            confirmResult = profileHandler.ExecuteSmartChargingConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId);
+                        }
+                        break;
+                    case "Security":
+                        {
+                            confirmResult = profileHandler.ExecuteSecurityConfirm(action, session, (IConfirmation)analysisResult.Message, analysisResult.RequestId);
+                        }
+                        break;
+                    default:
+                        {
+                            string replyMsg = BasicMessageHandler.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 = BasicMessageHandler.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 (ReConfirmMessage(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;
+                    case "FirmwareManagement":
+                        {
+                            profileHandler.ReceivedFirmwareManagementError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId);
+                        }
+                        break;
+                    case "RemoteTrigger":
+                        {
+                            profileHandler.ReceivedRemoteTriggerError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId);
+                        }
+                        break;
+                    case "Reservation":
+                        {
+                            profileHandler.ExecuteReservationError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId);
+                        }
+                        break;
+                    case "LocalAuthListManagement":
+                        {
+                            profileHandler.ReceivedLocalAuthListManagementError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId);
+                        }
+                        break;
+                    case "SmartCharging":
+                        {
+                            profileHandler.ReceivedSmartChargingError(action, analysisResult.ReceivedErrorCode, session, analysisResult.RequestId);
+                        }
+                        break;
+                    default:
+                        {
+                            string replyMsg = BasicMessageHandler.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 = BasicMessageHandler.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, msg, messageType, errorMsg, true);
+                    session.Send(msg);
+                }
+
+            }
+            catch (Exception ex)
+            {
+                logger.Error(string.Format("Send Ex:{0}", ex.ToString()));
+            }
+
+
+        }
+
+        async private void ServerUpdateTrigger()
+        {
+            for (; ; )
+            {
+                if (_ct.IsCancellationRequested)
+                {
+                    break;
+                }
+
+                var min_Interval = (DateTime.UtcNow - checkUpdateDt).TotalMinutes;
+                if (min_Interval > 3)
+                {
+                    BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+                    Dictionary<string, ClientData> _copyClientDic = null;
+                    lock (_lockClientDic)
+                    {
+                        _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+
+                    }
+                    checkUpdateDt = DateTime.UtcNow;
+                    using (var db = new MainDBContext())
+                    {
+                        //var needUpdateChargers = db.Machine.Where(x => x.FW_AssignedMachineVersionId.HasValue == true &&
+                        //    x.FW_AssignedMachineVersionId != x.FW_VersionReport && x.Online == true)
+                        //    .Select(x => new { x.Id, x.ChargeBoxId, x.FW_AssignedMachineVersionId }).ToList();
+
+                        var needUpdateChargers = db.Machine.Where(x => x.FW_AssignedVersion.HasValue == true &&
+                          x.FW_AssignedVersion != x.FW_VersionReport && x.Online == true)
+                          .Select(x => x.ChargeBoxId).AsNoTracking().ToList();
+
+                        foreach (var chargeBoxId in needUpdateChargers)
+                        {
+                            try
+                            {
+
+
+                                ClientData session;
+                                if (_copyClientDic.TryGetValue(chargeBoxId, out session))
+                                {
+
+                                    string requestId = Guid.NewGuid().ToString();
+                                    // using (var db = new MainDBContext())
+
+                                    if (session.IsCheckIn && !session.ISOCPP20)
+                                    {
+
+                                        var _request = new TriggerMessageRequest()
+                                        {
+                                            requestedMessage = Packet.Messages.SubTypes.MessageTrigger.FirmwareStatusNotification
+                                        };
+
+                                        var uuid = session.queue.store(_request);
+                                        string rawRequest = BasicMessageHandler.GenerateRequest(uuid, _request.Action, _request);
+                                        Send(session, rawRequest, string.Format("{0} {1}", _request.Action, "Request"), "");
+
+                                        #region OCTT   ,測試韌體更新方式
+                                        //--------------------> OCTT   ,測試韌體更新方式
+                                        //{
+                                        //    var machine = db.Machine.Where(x => x.FW_AssignedMachineVersionId.HasValue == true &&
+                                        //        x.FW_AssignedMachineVersionId != x.FW_VersionReport && x.ChargeBoxId == session.ChargeBoxId)
+                                        //        .Select(x => new { x.Id, x.FW_AssignedMachineVersionId }).FirstOrDefault();
+
+                                        //    if (machine != null)
+                                        //    {
+                                        //        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();
+
+                                        //        string downloadUrl = mv.PublishVersion.PublishVersionFiles.FirstOrDefault().UploadFile.FileUrl;
+
+                                        //        var _updateFWrequest = new UpdateFirmwareRequest()
+                                        //        {
+                                        //            location = new Uri(downloadUrl),
+                                        //            retries = 3,
+                                        //            retrieveDate = DateTime.UtcNow,
+                                        //            retryInterval = 10
+                                        //        };
+                                        //        db.MachineOperateRecord.Add(new MachineOperateRecord()
+                                        //        {
+                                        //            CreatedOn = DateTime.UtcNow,
+                                        //            ChargeBoxId = session.ChargeBoxId,
+                                        //            SerialNo = requestId,
+                                        //            RequestContent = JsonConvert.SerializeObject(_updateFWrequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                        //            EVSE_Status = 0,
+                                        //            EVSE_Value = "Fw Version:" + machine.FW_AssignedMachineVersionId,
+                                        //            Status = 0,
+                                        //            RequestType = 0,
+
+                                        //        });
+
+                                        //        db.ServerMessage.Add(new ServerMessage()
+                                        //        {
+                                        //            ChargeBoxId = session.ChargeBoxId,
+                                        //            CreatedBy = "Server",
+                                        //            CreatedOn = DateTime.UtcNow,
+                                        //            OutAction = _updateFWrequest.Action.ToString(),
+                                        //            OutRequest = JsonConvert.SerializeObject(_updateFWrequest, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                        //            SerialNo = requestId,
+                                        //            InMessage = string.Empty
+
+                                        //        });
+
+                                        //        db.SaveChanges();
+
+                                        //    }
+
+                                        //}
+                                        #endregion
+                                    }
+
+
+
+                                }
+
+
+                            }
+                            catch (Exception ex)
+                            {
+                                logger.Error(string.Format("serverUpdateTrigger ChargeBoxId:{0}  Ex:{1}", chargeBoxId, ex.ToString()));
+                            }
+                        }
+                    }
+                    await Task.Delay(1000);
+                    //  Thread.CurrentThread.Join(1000);
+                }
+            }
+        }
+        string webConnectionString = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
+
+        async private void ServerMessageTrigger()
+        {
+            for (; ; )
+            {
+                if (_ct.IsCancellationRequested)
+                {
+                    break;
+                }
+
+                try
+                {
+                    RemoveConfirmMessage();
+
+                    BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+                    using (var db = new MainDBContext())
+                    {
+                        DateTime startDt = DateTime.UtcNow.AddSeconds(-30);
+                        DateTime dt = new DateTime(1991, 1, 1);
+                        DateTime currentTime = DateTime.UtcNow;
+                        var commandList = db.ServerMessage.Where(c => c.ReceivedOn == dt && c.UpdatedOn == dt && c.CreatedOn >= startDt && c.CreatedOn <= currentTime).AsNoTracking().ToList();
+
+
+                        //處理主機傳送的有指令
+                        var cmdMachineList = commandList.Select(c => c.ChargeBoxId).Distinct().ToList();
+                        if (commandList.Count > 0)
+                        {
+                            // Console.WriteLine(string.Format("Now:{0} commandList Count:{1} ", DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss"), commandList.Count));
+                        }
+
+                        foreach (var charger_SN in cmdMachineList)
+                        {
+                            ClientData session;
+                            string uuid = string.Empty;
+                            if (clientDic.TryGetValue(charger_SN, out session))
+                            {
+                                //logger.Debug(string.Format("charger_SN:{0} startDt:{1} CreatedOn:{2}", charger_SN, startDt.ToString("yyyy/MM/dd HH:mm:ss"), DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss")));
+
+                                if (session.IsCheckIn && !session.ISOCPP20)
+                                {
+                                    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 && item.CreatedBy != "Destroyer")
+                                        {
+                                            request = JsonConvert.DeserializeObject(item.OutRequest, _RequestType) as IRequest;
+                                            uuid = session.queue.store(request);
+                                            string rawRequest = BasicMessageHandler.GenerateRequest(uuid, item.OutAction, request);
+                                            Send(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
+                                        }
+
+
+                                        if (item.CreatedBy == "Destroyer")
+                                        {
+
+                                            if (_RequestType != null)
+                                            {
+                                                request = Activator.CreateInstance(_RequestType) as IRequest;
+                                                uuid = session.queue.store(request);
+                                                string rawRequest = BasicMessageHandler.GenerateDestroyRequest(uuid, item.OutAction, item.OutRequest);
+                                                Send(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
+
+                                            }
+                                            else
+                                            {
+
+                                                string rawRequest = BasicMessageHandler.GenerateDestroyRequest(Guid.NewGuid().ToString(), item.OutAction, item.OutRequest);
+                                                Send(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
+
+                                            }
+                                        }
+
+                                        AddConfirmMessage(charger_SN, item.Id, item.SerialNo, item.OutAction, uuid);
+
+                                        #region 更新資料表單一欄位
+                                        var _UpdatedItem = new ServerMessage() { Id = item.Id, UpdatedOn = DateTime.UtcNow };
+                                        db.Configuration.AutoDetectChangesEnabled = false;//自動呼叫DetectChanges()比對所有的entry集合的每一個屬性Properties的新舊值
+                                        db.Configuration.ValidateOnSaveEnabled = false;// 因為Entity有些欄位必填,若不避開會有Validate錯誤
+                                                                                       // var _UpdatedItem = db.ServerMessage.Where(x => x.Id == item.Id).FirstOrDefault();
+                                        db.ServerMessage.Attach(_UpdatedItem);
+                                        _UpdatedItem.UpdatedOn = DateTime.UtcNow;
+                                        db.Entry(_UpdatedItem).Property(x => x.UpdatedOn).IsModified = true;// 可以直接使用這方式強制某欄位要更新,只是查詢集合耗效能而己
+
+                                        db.SaveChanges();
+                                        await Task.Delay(100);
+                                        #endregion
+
+
+                                    }
+
+                                }
+                            }
+                        }
+
+                    }
+
+                    await Task.Delay(500);
+
+                }
+                catch (Exception ex)
+                {
+                    logger.Error(string.Format("ServerMessageTrigger  Ex:{0}", ex.ToString()));
+                }
+            }
+        }
+
+
+        async private void HeartBeatCheckTrigger()
+        {
+            for (; ; )
+            {
+                if (_ct.IsCancellationRequested)
+                {
+                    break;
+                }
+
+                try
+                {
+                    if (DateTime.UtcNow.Subtract(lastcheckdt).TotalSeconds > 30)
+                    {
+                        lastcheckdt = DateTime.UtcNow;
+                        Stopwatch watch = new Stopwatch();
+                        Dictionary<string, ClientData> _copyClientDic = null;
+                        lock (_lockClientDic)
+                        {
+                            _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+                        }
+
+                        var cdt = DateTime.UtcNow;
+                        var clients = _copyClientDic.Where(x => x.Value.LastActiveTime > cdt.AddSeconds(-120)).Select(x => x.Value).ToList();
+
+                        watch.Start();
+                        foreach (var session in clients)
+                        {
+                            using (var db = new MainDBContext())
+                            {
+                                var machine = new Machine() { Id = session.MachineId };
+                                if (machine != null)
+                                {
+                                    db.Configuration.AutoDetectChangesEnabled = false;
+                                    db.Configuration.ValidateOnSaveEnabled = false;
+                                    db.Machine.Attach(machine);
+                                    machine.HeartbeatUpdatedOn = DateTime.UtcNow;
+                                    machine.ConnectionType = session.UriScheme.Equals("wss") ? 2 : 1;
+                                    db.Entry(machine).Property(x => x.HeartbeatUpdatedOn).IsModified = true;
+                                    db.Entry(machine).Property(x => x.ConnectionType).IsModified = true;
+                                    await db.SaveChangesAsync();
+                                }
+
+                            }
+                        }
+                        watch.Stop();
+                        if (watch.ElapsedMilliseconds / 1000 > 5)
+                        {
+                            logger.Fatal("Update HeartBeatCheckTrigger cost " + watch.ElapsedMilliseconds / 1000 + " seconds.");
+                        }
+                    }
+
+
+                    await Task.Delay(10000);
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine("***********************************************************");
+                    logger.Error(string.Format("HeartBeatCheckTrigger  Ex:{0}", ex.ToString()));
+                }
+
+            }
+        }
+
+        async private void ServerWeatherNotificationTrigger()
+        {
+            for (; ; )
+            {
+                if (_ct.IsCancellationRequested)
+                {
+                    break;
+                }
+
+                var min_Interval = (DateTime.UtcNow - _CheckWeatherDt).TotalMinutes;
+
+                if (min_Interval > 30)
+                {
+                    // Console.WriteLine("in...............ServerWeatherNotificationTrigger");
+                    BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+                    Dictionary<string, ClientData> _copyClientDic = null;
+                    lock (_lockClientDic)
+                    {
+                        _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+
+                    }
+                    _CheckWeatherDt = DateTime.UtcNow;
+
+                    var locations = _copyClientDic.Where(x => !string.IsNullOrEmpty(x.Value.StationLocation)).Distinct().Select(x => x.Value.StationLocation).ToList();
+
+                    // Console.WriteLine("in...............ServerWeatherNotificationTrigger");
+                    foreach (var location in locations)
+                    {
+
+                        try
+                        {   //query weather
+                            var httpResult = await httpClient.GetWeather("https://api.weatherapi.com/v1/current.json?key=874346abc0874e69a9423510222201&q=" + location, null, null);
+
+                            string temp = "17";
+                            string weather_code = "1183";
+                            if (httpResult.Status == System.Net.HttpStatusCode.OK)
+                            {
+                                try
+                                {
+                                    var jsonResult = JsonConvert.DeserializeObject(httpResult.Response) as JObject;
+                                    temp = jsonResult["current"]["temp_c"].ToString();
+                                    weather_code = jsonResult["current"]["condition"]["code"].ToString();
+
+
+                                }
+                                catch (Exception ex)
+                                {
+                                    ;
+                                }
+
+                            }
+
+                            #region 台泥氣象Mapping
+                            switch (weather_code)
+                            {
+                                case "1000":
+                                    weather_code = "1";
+                                    break;
+                                case "1003":
+                                case "1006":
+                                case "1009":
+                                    weather_code = "2";
+                                    break;
+                                case "1063":
+                                case "1072":
+                                case "1150":
+                                case "1153":
+                                case "1168":
+                                case "1171":
+                                case "1180":
+                                case "1183":
+                                case "1186":
+                                case "1189":
+                                case "1192":
+                                case "1195":
+                                case "1198":
+                                case "1201":
+                                case "1237":
+                                case "1240":
+                                case "1243":
+                                case "1246":
+                                case "1261":
+                                case "1264":
+                                    weather_code = "3";
+                                    break;
+                                case "1087":
+                                case "1273":
+                                case "1276":
+                                case "1279":
+                                case "1282":
+                                    weather_code = "4";
+                                    break;
+                                case "1066":
+                                case "1069":
+                                case "1114":
+                                case "1117":
+                                case "1204":
+                                case "1207":
+                                case "1210":
+                                case "1213":
+                                case "1216":
+                                case "1219":
+                                case "1222":
+                                case "1225":
+                                case "1249":
+                                case "1252":
+                                case "1255":
+                                case "1258":
+                                    weather_code = "5";
+                                    break;
+                                case "1030":
+                                case "1135":
+                                case "1147":
+                                    weather_code = "2";
+                                    break;
+                                default:
+                                    weather_code = "2";
+                                    break;
+                            }
+                            #endregion
+
+
+                            if (TCCStationDic.ContainsKey(location))
+                            {
+                                TCCStationDic[location].Temperature = (int)double.Parse(temp);
+                                TCCStationDic[location].WeatherID = int.Parse(weather_code);
+                            }
+                            else
+                            {
+                                TCCStationDic.Add(location, new TCCWeatherDto() { WeatherID = int.Parse(weather_code), Temperature = (int)double.Parse(temp) });
+                            }
+
+                        }
+                        catch (Exception ex)
+                        {
+                            logger.Error(string.Format("ServerWeatherNotificationTrigger ChargeBoxId:{0}  Ex:{1}", location, ex.ToString()));
+                        }
+                    }
+
+                    var clients = _copyClientDic.Where(x => x.Value.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8")).
+                    Select(x => new { ChargeBoxId = x.Value.ChargeBoxId, StationLocation = x.Value.StationLocation }).ToList();
+
+                    using (var db = new MainDBContext())
+                    {
+
+                        foreach (var client in clients)
+                        {
+                            try
+                            {
+                                if (string.IsNullOrEmpty(client.StationLocation))
+                                {
+                                    Console.WriteLine(client.StationLocation + " is empty");
+                                    continue;
+                                }
+
+
+                                if (TCCStationDic.ContainsKey(client.StationLocation))
+                                {
+                                    db.ServerMessage.Add(new ServerMessage()
+                                    {
+                                        ChargeBoxId = client.ChargeBoxId,
+                                        CreatedBy = "Server",
+                                        CreatedOn = DateTime.UtcNow,
+                                        OutAction = Actions.DataTransfer.ToString(),
+                                        OutRequest = JsonConvert.SerializeObject(
+                                                           new DataTransferRequest()
+                                                           {
+                                                               messageId = "ID_Weather_Info",
+                                                               vendorId = "Phihong Technology",
+                                                               data = JsonConvert.SerializeObject(
+                                                                   new
+                                                                   {
+                                                                       weatherId = TCCStationDic[client.StationLocation].WeatherID,
+                                                                       Temperature = TCCStationDic[client.StationLocation].Temperature
+                                                                   })
+                                                           },
+                                                           new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                        SerialNo = Guid.NewGuid().ToString(),
+                                        InMessage = string.Empty
+
+                                    });
+
+                                    await db.SaveChangesAsync();
+                                }
+                            }
+                            catch (Exception ex)
+                            {
+                                logger.Error(string.Format("ServerWeatherNotificationTrigger ChargeBoxId:{0}  Ex:{1}", client.ChargeBoxId, ex.ToString()));
+                            }
+
+
+                        }
+                    }
+
+                }
+                await Task.Delay(1000);
+            }
+        }
+
+        async private void ServerSetFeeTrigger()
+        {
+            for (; ; )
+            {
+                if (_ct.IsCancellationRequested)
+                {
+                    break;
+                }
+
+                var min_Interval = (DateTime.UtcNow - _CheckFeeDt).TotalMinutes;
+                if (min_Interval > 1)
+                {
+                    BasicMessageHandler msgAnalyser = new BasicMessageHandler();
+                    Dictionary<string, ClientData> _copyClientDic = null;
+                    lock (_lockClientDic)
+                    {
+                        _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+
+                    }
+                    _CheckFeeDt = DateTime.UtcNow;
+                    foreach (var item in _copyClientDic)
+                    {
+                        try
+                        {
+                            ClientData session = item.Value;
+                            if (session.IsCheckIn)
+                            {
+
+                                string displayPriceText = await SetDefaultFee(session);
+                                if (!string.IsNullOrEmpty(displayPriceText) && displayPriceText != session.DisplayPrice)
+                                {
+                                    clientDic[item.Key].DisplayPrice = displayPriceText;
+
+                                    using (var db = new MainDBContext())
+                                    {
+                                        db.ServerMessage.Add(new ServerMessage()
+                                        {
+                                            ChargeBoxId = session.ChargeBoxId,
+                                            CreatedBy = "Server",
+                                            CreatedOn = DateTime.UtcNow,
+                                            OutAction = Actions.ChangeConfiguration.ToString(),
+                                            OutRequest = JsonConvert.SerializeObject(
+                                                    new ChangeConfigurationRequest()
+                                                    {
+                                                        key = "DefaultPrice",
+                                                        value = clientDic[item.Key].DisplayPrice
+                                                    },
+                                                    new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                            SerialNo = Guid.NewGuid().ToString(),
+                                            InMessage = string.Empty
+
+                                        }); ;
+
+                                        if (session.CustomerId == new Guid("10C7F5BD-C89A-4E2A-8611-B617E0B41A73"))
+                                        {
+                                            db.ServerMessage.Add(new ServerMessage()
+                                            {
+                                                ChargeBoxId = session.ChargeBoxId,
+                                                CreatedBy = "Server",
+                                                CreatedOn = DateTime.UtcNow,
+                                                OutAction = Actions.ChangeConfiguration.ToString(),
+                                                OutRequest = JsonConvert.SerializeObject(
+                                                   new ChangeConfigurationRequest()
+                                                   {
+                                                       key = "ConnectionTimeOut",
+                                                       value = "120"
+                                                   },
+                                                   new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                                SerialNo = Guid.NewGuid().ToString(),
+                                                InMessage = string.Empty
+
+                                            });
+                                            db.ServerMessage.Add(new ServerMessage()
+                                            {
+                                                ChargeBoxId = session.ChargeBoxId,
+                                                CreatedBy = "Server",
+                                                CreatedOn = DateTime.UtcNow,
+                                                OutAction = Actions.ChangeConfiguration.ToString(),
+                                                OutRequest = JsonConvert.SerializeObject(
+                                                   new ChangeConfigurationRequest()
+                                                   {
+                                                       key = "MeterValueSampleInterval",
+                                                       value = "3"
+                                                   },
+                                                   new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
+                                                SerialNo = Guid.NewGuid().ToString(),
+                                                InMessage = string.Empty
+
+                                            });
+                                        }
+
+                                        await db.SaveChangesAsync();
+
+                                    }
+                                }
+                            }
+
+                        }
+                        catch (Exception ex)
+                        {
+                            logger.Error(string.Format("ServerSetFeeTrigger ChargeBoxId:{0}  Ex:{1}", item.Key, ex.ToString()));
+                        }
+                    }
+                }
+                await Task.Delay(1000);
+            }
+        }
+
+        async private Task<string> SetDefaultFee(ClientData client)
+        {
+            string displayPriceText = string.Empty;
+            string charingPriceText = string.Empty;
+
+            if (string.IsNullOrEmpty(client.ChargeBoxId)) return displayPriceText;
+
+            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@MachineId", client.MachineId, DbType.String, ParameterDirection.Input);
+                string displayPricestrSql = "";
+                string strSql = "";
+               
+                if (client.IsAC)
+                {
+                    displayPricestrSql = "   SELECT  [AC_BillingMethod] as BillingMethod,[AC_FeeName] as FeeName,[AC_Fee] as ChargingFeebyHour" +
+                "  ,[AC_ParkingFee] as ParkingFee, [Currency]  FROM[StationMachine]  left join[dbo].[Station]" +
+                "  on[StationMachine].StationId = Station.[Id]  where StationMachine.MachineId=@MachineId and Station.IsBilling=1; ";
+
+                    strSql = " SELECT CAST( [StartTime] as varchar(5)) StartTime,CAST( [EndTime] as varchar(5)) EndTime,[Fee]  FROM[StationMachine]  left join [dbo].[StationFee]" +
+                   " on[StationMachine].StationId = StationFee.StationId  where StationMachine.MachineId =@MachineId and StationFee.IsAC=1; ";
+                }
+                else
+                {
+                    displayPricestrSql = "   SELECT  [DC_BillingMethod] as BillingMethod,[DC_FeeName] as FeeName,[DC_Fee] as ChargingFeebyHour" +
+               "  ,[DC_ParkingFee] as ParkingFee, [Currency]  FROM[StationMachine]  left join[dbo].[Station]" +
+               "  on[StationMachine].StationId = Station.[Id]  where StationMachine.MachineId=@MachineId and Station.IsBilling=1; ";
+
+                    strSql = " SELECT CAST( [StartTime] as varchar(5)) StartTime,CAST( [EndTime] as varchar(5)) EndTime,[Fee]  FROM[StationMachine]  left join [dbo].[StationFee]" +
+                   " on[StationMachine].StationId = StationFee.StationId  where StationMachine.MachineId =@MachineId and StationFee.IsAC=0; ";
+
+                }
+                var result = await conn.QueryAsync<StationFee>(displayPricestrSql, parameters);
+                if (result.Count() == 0)
+                {
+                    return string.Empty;
+                }
+                var stationPrice = result.First();
+              
+                if (stationPrice.BillingMethod == 1)
+                {
+                    var chargingPriceResult = await conn.QueryAsync<ChargingPrice>(strSql, parameters);
+                    client.ChargingPrices = chargingPriceResult.ToList();
+                    if (string.IsNullOrEmpty(client.ChargingPrices[0].StartTime))
+                    {
+                        client.ChargingPrices = new List<ChargingPrice>();
+                    }
+                }               
+
+                displayPriceText = stationPrice.FeeName;
+                client.BillingMethod = stationPrice.BillingMethod;
+                client.Currency = stationPrice.Currency;
+                client.ChargingFeebyHour = stationPrice.ChargingFeebyHour;
+                client.ParkingFee = stationPrice.ParkingFee;
+                client.IsBilling = true;             
+            }
+
+            return displayPriceText;
+        }
+
+        async private void HealthCheckTrigger()
+        {
+            for (; ; )
+            {
+                if (_ct.IsCancellationRequested)
+                {
+                    break;
+                }
+
+                try
+                {
+                    Dictionary<string, ClientData> _copyClientDic = null;
+                    lock (_lockClientDic)
+                    {
+                        _copyClientDic = new Dictionary<string, ClientData>(clientDic);
+                    }
+
+                    var removeClients = _copyClientDic.Where(x => x.Value.LastActiveTime < DateTime.UtcNow.AddSeconds(-300)).Select(x => x.Value).ToList();
+
+                    foreach (var session in removeClients)
+                    {
+
+                        Console.WriteLine(string.Format("Server forced to shut down ChargeBox ({0}: LastActiveTime{1})", session.ChargeBoxId, session.LastActiveTime));
+                        RemoveClient(session);
+                    }
+
+                    await Task.Delay(60000);
+                }
+                catch (Exception ex)
+                {
+                    logger.Error(string.Format("HealthAlarmTrigger  Ex:{0}", ex.ToString()));
+                }
+
+            }
+        }
+
+
+        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.UtcNow;
+            _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.UtcNow.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 ReConfirmMessage(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 != null && 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.None);
+                        sc.ReceivedOn = DateTime.UtcNow;
+                        db.SaveChanges();
+                        //  Console.WriteLine(string.Format("Now:{0} ServerMessage Id:{1} ", DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss"), foundRequest.Id));
+
+                    }
+                    confirmed = true;
+
+
+
+                }
+                else if (analysisResult.Action == Actions.TriggerMessage.ToString())
+                {
+                    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)
+        {
+
+
+            if (session != null)
+            {
+
+                logger.Trace("RemoveClient[" + session.ChargeBoxId + "]");
+                if (session.Connected)
+                {
+                    session.Close(CloseReason.ServerShutdown);
+                }
+                RemoveClientDic(session);
+                try
+                {
+                    session.m_ReceiveData -= new ClientData.OCPPClientDataEventHandler<ClientData, String>(ReceivedMessage);
+                    // session.Close(CloseReason.ServerShutdown);
+
+                }
+                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)
+        {
+            if (!string.IsNullOrEmpty(session.ChargeBoxId))
+            {
+                lock (_lockClientDic)
+                {
+
+                    if (clientDic.ContainsKey(session.ChargeBoxId))
+                    {
+                        if (clientDic[session.ChargeBoxId].SessionID == session.SessionID)
+                        {
+                            logger.Debug(String.Format("ChargeBoxId:{0} Remove SessionId:{1} Removed SessionId:{2}", session.ChargeBoxId, session.SessionID, clientDic[session.ChargeBoxId].SessionID));
+
+                            clientDic.Remove(session.ChargeBoxId);
+                            logger.Trace("RemoveClient ContainsKey " + session.ChargeBoxId);
+                        }
+
+                    }
+                }
+            }
+
+        }
+
+        private void WarmUpLog()
+        {
+
+            using (var log = new ConnectionLogDBContext())
+            {
+                log.MachineConnectionLog.ToList();
+            }
+        }
+
+        private void WriteMachineLog(ClientData clientData, string data, string messageType, string errorMsg = "", bool isSent = false)
+        {
+            try
+            {
+
+                if (clientData == null || string.IsNullOrEmpty(data)) return;
+
+                if (clientData.ChargeBoxId == null)
+                {
+                    logger.Fatal(clientData.Path + "]********************session ChargeBoxId null sessionId=" + clientData.SessionID);
+                }
+                using (var db = new ConnectionLogDBContext())
+                {
+                    string sp = "[dbo].[uspInsertMachineConnectionLog] @CreatedOn," +
+                          "@ChargeBoxId,@MessageType,@Data,@Msg,@IsSent,@EVSEEndPoint,@Session";
+                    var dd = DateTime.UtcNow;
+                    SqlParameter[] parameter =
+                    {
+                      new SqlParameter("CreatedOn",dd),
+                      new SqlParameter("ChargeBoxId",clientData.ChargeBoxId==null?"unknown":clientData.ChargeBoxId.Replace("'","''")),
+                      new SqlParameter("MessageType",messageType.Replace("'","''")),
+                      new SqlParameter("Data",data.Replace("'","''")),
+                      new SqlParameter("Msg",errorMsg.Replace("'","''")),
+                      new  SqlParameter("IsSent",isSent),
+                      new  SqlParameter("EVSEEndPoint",clientData.RemoteEndPoint==null?"123":clientData.RemoteEndPoint.ToString()),
+                      new  SqlParameter("Session",clientData.SessionID==null?"123":clientData.SessionID)
+               };
+
+                    db.Database.ExecuteSqlCommand(sp, parameter);
+                }
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex.ToString());
+            }
+
+
+        }
+    }
+}

+ 36 - 0
EVCB_OCPP.WSServer/Service/BusinessServiceFactory.cs

@@ -0,0 +1,36 @@
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.Packet.Messages.SubTypes;
+using EVCB_OCPP.WSServer.Dto;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Service
+{
+
+    public interface IBusinessService
+    {
+        Task<IdTagInfo> Authorize(string chargeBoxId, string idTag);
+
+        Task NotifyFaultStatus(ErrorDetails details);
+
+        Task NotifyConnectorUnplugged(string chargeBoxId,string data);
+
+    }
+
+    static public class BusinessServiceFactory
+    {
+
+        static public IBusinessService CreateBusinessService(string customerId)
+        {
+            bool isCallOut = false;
+            using (var db = new MainDBContext())
+            {
+                isCallOut = db.Customer.Where(x => x.Id == new Guid(customerId)).Select(x => x.CallPartnerApiOnSchedule).SingleOrDefault();
+            }
+
+            return isCallOut ? new OuterBusinessService(customerId) : (IBusinessService)new LocalBusinessService(customerId);
+        }
+
+    }
+}

+ 365 - 0
EVCB_OCPP.WSServer/Service/HttpClientService.cs

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

+ 393 - 0
EVCB_OCPP.WSServer/Service/LoadingBalanceService.cs

@@ -0,0 +1,393 @@
+using Dapper;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+
+namespace EVCB_OCPP.WSServer.Service
+{
+    public class LoadBalanceSetting
+    {
+        public int StationId { set; get; }
+
+        public int LBMode { set; get; }
+
+        public int LBCurrent { set; get; }
+
+    }
+
+    public class LoadingBalanceService
+    {
+        string mainConnectionString = ConfigurationManager.ConnectionStrings["MainDBContext"].ConnectionString;
+        string webConnectionString = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
+
+        ConcurrentDictionary<int, object> _lockDic = new ConcurrentDictionary<int, object>();
+
+
+        public LoadingBalanceService()
+        {
+
+        }
+
+        public int GetStationIdByMachineId(string machineId)
+        {
+            int stationId = 0;
+            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input);
+                string strSql = "Select StationId from [dbo].[StationMachine] where MachineId=@MachineId ; ";
+                stationId = conn.ExecuteScalar<Int32>(strSql, parameters);
+            }
+            return stationId;
+        }
+
+
+        public bool IsNeedtoCancelSetting(int stationId, string machineId, string chargeBoxId)
+        {
+            var setting = GetLoadBalance(stationId);
+            if (setting == null) return false;
+
+            lock (GetLock(stationId))
+            {
+                if (setting.LBMode > 0 && setting.LBMode < 3 && !IsStillInTransactions(chargeBoxId))
+                {
+                    // renew table
+                    UpdateLoadbalanceRecord(stationId, machineId, 0, DateTime.UtcNow);
+                    return true;
+
+                }
+
+                if (setting.LBMode >= 3 || setting.LBMode < 1)
+                {
+                    CloseLoadbalanceRecord(stationId);
+
+                }
+            }
+
+            return false;
+
+        }
+
+        private object GetLock(int stationId)
+        {
+
+            if (!_lockDic.ContainsKey(stationId))
+            {
+                _lockDic.TryAdd(stationId, new object());
+            }
+
+            return _lockDic[stationId];
+
+        }
+        private void CloseLoadbalanceRecord(int stationId)
+        {
+            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            {
+
+                var parameters = new DynamicParameters();
+                parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input);
+                parameters.Add("@FinishedOn", DateTime.UtcNow, DbType.DateTime, ParameterDirection.Input);
+                string strSql = "Update  [dbo].[LoadingBalance]  SET FinishedOn=@FinishedOn where StationId=@StationId and FinishedOn='1991/01/01'; ";
+                conn.Execute(strSql, parameters);
+
+            }
+        }
+
+
+        private void UpdateLoadbalanceRecord(int stationId, string machineId, decimal power, DateTime? finishedOn, bool keepgoing = false)
+        {
+            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            {
+                if (finishedOn.HasValue)
+                {
+                    var parameters = new DynamicParameters();
+                    parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input);
+                    parameters.Add("@FinishedOn", finishedOn.Value, DbType.DateTime, ParameterDirection.Input);
+                    string strSql = "Update  [dbo].[LoadingBalance]  SET FinishedOn=@FinishedOn where MachineId=@MachineId and FinishedOn='1991/01/01'; ";
+                    conn.Execute(strSql, parameters);
+                }
+                else
+                {
+                    if (keepgoing)
+                    {
+                        var parameters = new DynamicParameters();
+                        parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input);
+                        parameters.Add("@Power", power, DbType.Decimal, ParameterDirection.Input);
+                        string strSql = "Update  [dbo].[LoadingBalance]  SET Power=@Power where MachineId=@MachineId and FinishedOn='1991/01/01'; ";
+                        conn.Execute(strSql, parameters);
+                    }
+                    else
+                    {
+                        var parameters = new DynamicParameters();
+                        parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input);
+                        parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input);
+                        parameters.Add("@Power", power, DbType.Decimal, ParameterDirection.Input);
+                        parameters.Add("@CreatedOn", DateTime.UtcNow, DbType.DateTime, ParameterDirection.Input);
+                        parameters.Add("@FinishedOn", new DateTime(1991, 1, 1, 0, 0, 0, DateTimeKind.Utc), DbType.DateTime, ParameterDirection.Input);
+
+                        string strSql = "INSERT INTO [dbo].[LoadingBalance] " +
+                         "([StationId],[MachineId],[Power],[CreatedOn],[FinishedOn]) " +
+                         "VALUES(@StationId,@MachineId,@Power,@CreatedOn,@FinishedOn);";
+
+                        conn.Execute(strSql, parameters);
+                    }
+
+                }
+
+            }
+        }
+
+
+        private bool IsStillInTransactions(string chargeBoxId)
+        {
+            bool result = false;
+
+            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@ChargeBoxId", chargeBoxId, DbType.String, ParameterDirection.Input);
+                string strSql = "Select count(*) from [dbo].[TransactionRecord] where ChargeBoxId=@ChargeBoxId and StopTime='1991/01/01'; ";
+                result = conn.ExecuteScalar<bool>(strSql, parameters);
+            }
+
+            return result;
+        }
+
+        private decimal? GetCurrentSetting(string machineId)
+        {
+            decimal? result = (decimal?)null;
+
+            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@MachineId", machineId, DbType.String, ParameterDirection.Input);
+                string strSql = "Select Power from [dbo].[LoadingBalance] where MachineId=@MachineId and FinishedOn='1991/01/01'; ";
+                result = conn.ExecuteScalar<decimal>(strSql, parameters);
+            }
+
+            return result;
+        }
+
+
+
+        public Dictionary<string, decimal?> GetRerangeSettingPower(int stationId)
+        {
+            Dictionary<string, decimal?> dic = new Dictionary<string, decimal?>();
+            var setting = GetLoadBalance(stationId);
+            if (setting == null) return null;
+
+            lock (GetLock(stationId))
+            {
+                if (setting != null && setting.LBMode == 2)
+                {
+                    string machineId = string.Empty;
+                    decimal ratedPower = GetRatedPower(machineId);
+                    //找站內最早要充電的交易 下發充電Power & 填寫新給的Power
+                    using (SqlConnection conn = new SqlConnection(mainConnectionString))
+                    {
+                        var parameters = new DynamicParameters();
+                        parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input);
+                        string strSql = "Select M.Id from [dbo].[LoadingBalance] LB,  [dbo].[Machine] M  where LB.StationId=@StationId and  LB.MachineId=M.Id and LB.Power < M.RatedPower and LB.FinishedOn='1991/01/01' order by LB.Id asc; ";
+                        machineId = conn.ExecuteScalar<string>(strSql, parameters);
+                    }
+
+                    if (!string.IsNullOrEmpty(machineId))
+                    {
+                        decimal estimatedPwerValue = GetFCFSPower(stationId, machineId, setting.LBCurrent);
+
+                        // renew table
+                        UpdateLoadbalanceRecord(stationId, machineId, estimatedPwerValue, null, true);
+                        // UpdateLoadbalanceRecord(stationId, machineId, estimatedPwerValue, null);
+
+                        dic.Add(machineId, estimatedPwerValue);
+                    }
+
+
+                }
+
+                if (setting != null && setting.LBMode == 1)
+                {
+                    dic = GetAveragePower(stationId, setting.LBCurrent);
+                    foreach (var kv in dic)
+                    {
+                        if (kv.Value.HasValue)
+                        {
+                            UpdateLoadbalanceRecord(stationId, kv.Key, 0, DateTime.UtcNow);
+                            UpdateLoadbalanceRecord(stationId, kv.Key, kv.Value.Value, null);
+                        }
+
+                    }
+                }
+            }
+
+
+            return dic;
+
+        }
+
+        public Dictionary<string, decimal?> GetSettingPower(int stationId, string machineId)
+        {
+            Dictionary<string, decimal?> dic = new Dictionary<string, decimal?>();
+            var setting = GetLoadBalance(stationId);
+            if (setting == null) return null;
+
+            lock (GetLock(stationId))
+            {
+
+                if (setting != null)
+                {
+                    if (setting.LBMode == 1)
+                    {
+                        dic = GetAveragePower(stationId, setting.LBCurrent, machineId);
+                        foreach (var kv in dic)
+                        {
+                            if (kv.Value.HasValue)
+                            {
+                                UpdateLoadbalanceRecord(stationId, kv.Key, 0, DateTime.UtcNow);
+                                UpdateLoadbalanceRecord(stationId, kv.Key, kv.Value.Value, null);
+                            }
+
+                        }
+                    }
+                    else if (setting.LBMode == 2)
+                    {
+                        dic.Add(machineId, GetFCFSPower(stationId, machineId, setting.LBCurrent));
+
+                        UpdateLoadbalanceRecord(stationId, machineId, 0, DateTime.UtcNow);
+                        UpdateLoadbalanceRecord(stationId, machineId, dic[machineId].Value, null);
+
+                    }
+                    else
+                    {
+                        // 把LB TABLE 關閉
+                        CloseLoadbalanceRecord(stationId);
+
+                    }
+                }
+
+            }
+
+            return dic;
+        }
+
+
+        public LoadBalanceSetting GetLoadBalance(int stationId)
+        {
+            LoadBalanceSetting setting = null;
+            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input);
+
+                string strSql = "Select LBMode,LBCurrent from [dbo].[Station] where Id=@StationId ; ";
+                setting = conn.Query<LoadBalanceSetting>(strSql, parameters).FirstOrDefault();
+            }
+            return setting;
+        }
+
+        private List<string> GetIdsbyStationId(int stationId)
+        {
+            List<string> machineIds = new List<string>();
+            using (SqlConnection conn = new SqlConnection(webConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@StationId", stationId, DbType.Int16, ParameterDirection.Input);
+                string strSql = "Select MachineId from [dbo].[StationMachine] where StationId=@StationId; ";
+                machineIds = conn.Query<String>(strSql, parameters).ToList();
+            }
+            return machineIds;
+        }
+
+
+
+        private Dictionary<string, decimal?> GetAveragePower(int stationId, int availableCapacity, string machineId = "")
+        {
+            Dictionary<string, decimal?> dic = new Dictionary<string, decimal?>();
+            //總量 * 該樁的額定功率/該站充電中樁的總額定功率
+            List<string> _MachineIds = new List<string>();
+            int skipCount = 0;
+            int size = 200;
+            int takeCount = 0;
+            int totalRatePower = 0;
+
+            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input);
+                string strSql = "Select MachineId from [dbo].[LoadingBalance] where StationId=@StationId and FinishedOn='1991/01/01'; ";
+                _MachineIds = conn.Query<string>(strSql, parameters).ToList();
+            }
+
+            if (!string.IsNullOrEmpty(machineId) && !_MachineIds.Contains(machineId))
+            {
+                _MachineIds.Add(machineId);
+            }
+
+            while (skipCount < _MachineIds.Count())
+            {
+                takeCount = _MachineIds.Count() - skipCount > size ? size : _MachineIds.Count() - skipCount;
+
+                using (SqlConnection conn = new SqlConnection(mainConnectionString))
+                {
+                    string strSql = "Select Sum(RatedPower) from [dbo].[Machine] where Id in @machineIds and [Online]=1; ";
+                    totalRatePower += conn.ExecuteScalar<Int32>(strSql, new { machineIds = _MachineIds.ToArray() });
+                    skipCount += takeCount;
+                }
+            }
+
+            foreach (var id in _MachineIds)
+            {
+                int singleRatePower = (int)GetRatedPower(id);
+                var value = totalRatePower == 0 ? 0 : availableCapacity * singleRatePower / totalRatePower;
+                dic.Add(id, value);
+            }
+
+            return dic;
+
+        }
+
+        private decimal GetRatedPower(string machineId)
+        {
+            decimal ratedPower = 0;
+            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@machineId", machineId, DbType.String, ParameterDirection.Input);
+                string strSql = "Select RatedPower from [dbo].[Machine] where Id=@machineId; ";
+                ratedPower = conn.ExecuteScalar<Int32>(strSql, parameters);
+            }
+            return ratedPower;
+        }
+
+        private decimal GetFCFSPower(int stationId, string machineId, int availableCapacity)
+        {
+
+            decimal ongoingPower = 0;
+            decimal singleRatePower = GetRatedPower(machineId);
+
+            //先找LB 裡面目前下發的Power
+            decimal? currentPower = GetCurrentSetting(machineId);
+
+            if (!currentPower.HasValue) currentPower = 0;
+
+            //總量 - 所有正在進行的Power
+            using (SqlConnection conn = new SqlConnection(mainConnectionString))
+            {
+                var parameters = new DynamicParameters();
+                parameters.Add("@StationId", stationId, DbType.Int32, ParameterDirection.Input);
+                string strSql = "Select Sum(Power) from [dbo].[LoadingBalance] where StationId=@StationId and FinishedOn='1991/01/01'; ";
+                ongoingPower = conn.ExecuteScalar<Int32>(strSql, parameters);
+            }
+
+            return availableCapacity - (ongoingPower - currentPower.Value) > singleRatePower ? singleRatePower :
+                (availableCapacity - (ongoingPower - currentPower.Value) > 0 ? availableCapacity - (ongoingPower - currentPower.Value) : 0);
+        }
+
+
+    }
+}
+

+ 91 - 0
EVCB_OCPP.WSServer/Service/LocalBusinessService.cs

@@ -0,0 +1,91 @@
+using EVCB_OCPP.Packet.Messages.SubTypes;
+using EVCB_OCPP.WSServer.Dto;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Net;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Service
+{
+    public class LocalBusinessService : IBusinessService
+    {
+
+        string customerId = string.Empty;
+
+        public LocalBusinessService(string customerId)
+        {
+            this.customerId = customerId;
+        }
+
+        async public Task<IdTagInfo> Authorize(string chargeBoxId, string idTag)
+        {
+            await Task.Delay(10);
+            IdTagInfo info = new IdTagInfo() { status = AuthorizationStatus.Invalid };
+            try
+            {
+                if (customerId.ToUpper() == "009E603C-79CD-4620-A2B8-D9349C0E8AD8")
+                {
+                    info = new IdTagInfo() { status = AuthorizationStatus.Accepted };
+                    return info;
+                }
+
+
+                OuterHttpClient _client = new OuterHttpClient();
+
+
+                string url = ConfigurationManager.AppSettings["LocalAuthAPI"];
+
+
+                HttpClientService service = new HttpClientService();
+
+                Dictionary<string, string> postData = new Dictionary<string, string>()
+                {
+                  { "ChargeBoxId", chargeBoxId },
+                  { "IdTag", idTag },
+
+
+                };
+                var _innerresult = await service.PostFormDataAsync(url, postData, null);
+
+
+                if (_innerresult.StatusCode == HttpStatusCode.OK)
+                {
+                    JObject jo = JObject.Parse(_innerresult.Response);
+                    if (jo["code"].ToString() == "1")
+                    {
+                        try
+                        {
+                            info.status = (AuthorizationStatus)Enum.Parse(typeof(AuthorizationStatus), jo["message"].ToString());
+                        }
+                        catch (Exception)
+                        {
+                            ;
+                        }
+
+
+                    }
+
+                }
+
+            }
+            catch (Exception ex)
+            {
+                ;
+            }
+            return info;
+
+        }
+
+        async public Task NotifyConnectorUnplugged(string chargeBoxId, string data)
+        {
+            await Task.Delay(10);
+        }
+
+        async public Task NotifyFaultStatus(ErrorDetails details)
+        {
+            await Task.Delay(10);
+        }
+    }
+}

+ 204 - 0
EVCB_OCPP.WSServer/Service/OuterBusinessService.cs

@@ -0,0 +1,204 @@
+using EVCB_OCPP.Domain;
+using EVCB_OCPP.Packet.Messages.SubTypes;
+using EVCB_OCPP.WSServer.Dto;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NLog;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace EVCB_OCPP.WSServer.Service
+{
+    internal class CPOOuterResponse
+    {
+        public CPOOuterResponse()
+        {
+            StatusCode = 0;
+
+        }
+
+        public int StatusCode { set; get; }
+
+        public string StatusMessage { set; get; }
+
+        public string Data { set; get; }
+
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public string SerialNo { set; get; }
+
+
+        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
+        public string ErrorDetail { set; get; }
+
+
+    }
+    internal class CustomerSignMaterial
+    {
+        internal bool CallsThirdParty { set; get; }
+
+        internal string Id { set; get; }
+
+        internal string APIUrl { set; get; }
+
+        internal string SaltKey { set; get; }
+    }
+    public class OuterBusinessService : IBusinessService
+    {
+        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        private OuterHttpClient httpClient = new OuterHttpClient();
+        private CustomerSignMaterial signMaterial = null;
+        private string CustomerId = string.Empty;
+        public OuterBusinessService(string customerId)
+        {
+            CustomerId = customerId;
+            signMaterial = GetSign(customerId);
+        }
+
+
+        async public Task<IdTagInfo> Authorize(string chargeBoxId, string idTag)
+        {
+            Console.WriteLine(string.Format("Authorize: {0}", CustomerId));
+            IdTagInfo result = new IdTagInfo() { status = AuthorizationStatus.Invalid };
+            try
+            {
+               
+                string requestParams = string.Format("charging_auth?ChargeBoxId={0}&IdTag={1}", chargeBoxId, idTag);
+
+                if (CustomerId.ToLower() == "9e6bfdcc-09fb-4dab-a428-43fe507600a3")
+                {
+                    logger.Error(chargeBoxId+" Charging Monitor======================================>");
+                    logger.Error(signMaterial.APIUrl + requestParams);
+                }
+                var response = await httpClient.Post(signMaterial.APIUrl + requestParams, new Dictionary<string, string>()
+                            {
+                                { "PartnerId",signMaterial.Id}
+
+                            }, null, signMaterial.SaltKey).ConfigureAwait(false);
+                if (CustomerId.ToLower() == "9e6bfdcc-09fb-4dab-a428-43fe507600a3")
+                {                  
+                    logger.Error(JsonConvert.SerializeObject(response));
+                }
+                if (response.Success)
+                {
+                    Console.WriteLine(response.Response);
+                    var _httpResult = JsonConvert.DeserializeObject<CPOOuterResponse>(response.Response);
+                    JObject jo = JObject.Parse(_httpResult.Data);
+
+                    if (jo.ContainsKey("ExpiryDate"))
+                    {
+                        DateTime dt = jo["ExpiryDate"].Value<DateTime>();
+                        result.expiryDate = dt;
+                    }
+
+                    if (jo.ContainsKey("ParentIdTag"))
+                    {
+                        string _Message = jo["ParentIdTag"].Value<string>();
+                        result.parentIdTag = _Message;
+
+                    }
+
+
+                    if (jo.ContainsKey("Status"))
+                    {
+                        string _Message = jo["Status"].Value<string>();
+                        result.status = (AuthorizationStatus)Enum.Parse(typeof(AuthorizationStatus), _Message);
+                    }
+                }
+                else
+                {
+                    logger.Error(chargeBoxId + " OuterBusinessService.Authorize Fail: " + response.Response);
+                }
+
+            }
+            catch (Exception ex)
+            {
+                result.status = AuthorizationStatus.Invalid;
+
+                logger.Error(chargeBoxId + " OuterBusinessService.Authorize Ex: " + ex.ToString());
+            }
+         
+            return result;
+
+        }
+
+        async public Task NotifyFaultStatus(ErrorDetails details)
+        {
+
+            try
+            {
+                if (signMaterial.CallsThirdParty)
+                {
+                    var response = await httpClient.Post(signMaterial.APIUrl + "connectorfault", new Dictionary<string, string>()
+                            {
+                                { "PartnerId",signMaterial.Id}
+
+                            }, JsonConvert.SerializeObject(details, GlobalConfig.JSONSERIALIZER_FORMAT), signMaterial.SaltKey).ConfigureAwait(false);
+
+
+                }
+
+
+            }
+            catch (Exception ex)
+            {
+
+                logger.Error(details.ChargeBoxId + " OuterBusinessService.NotifyFaultStatus Ex: " + ex.ToString());
+            }
+
+
+        }
+
+        async public Task NotifyConnectorUnplugged(string chargeBoxId, string data)
+        {
+            try
+            {
+                JObject jo = JObject.Parse(data);
+
+                var details = new { ChargeBoxId = chargeBoxId, SessionId = jo["idTx"].Value<Int32>(), Timestamp = jo["timestamp"].Value<DateTime>() };
+                if (signMaterial.CallsThirdParty)
+                {
+                    var response = await httpClient.Post(signMaterial.APIUrl + "connectorunplugged", new Dictionary<string, string>()
+                            {
+                                { "PartnerId",signMaterial.Id}
+
+                            }, JsonConvert.SerializeObject(details, new JsonSerializerSettings()
+                            {
+                                DateTimeZoneHandling = DateTimeZoneHandling.Utc,
+                                NullValueHandling = NullValueHandling.Ignore,
+                                Formatting = Formatting.None
+                            }), signMaterial.SaltKey).ConfigureAwait(false);
+
+
+                }
+
+
+            }
+            catch (Exception ex)
+            {
+
+                logger.Error(chargeBoxId + " OuterBusinessService.NotifyConnectorUnplugged Ex: " + ex.ToString());
+            }
+
+        }
+
+        private CustomerSignMaterial GetSign(string customerId)
+        {
+            Guid Id = new Guid(customerId);
+            CustomerSignMaterial _customer = new CustomerSignMaterial();
+
+
+            using (var db = new MainDBContext())
+            {
+                _customer = db.Customer.Where(x => x.Id == Id).Select(x => new CustomerSignMaterial() { Id = x.Id.ToString(), APIUrl = x.ApiUrl, SaltKey = x.ApiKey, CallsThirdParty = x.CallPartnerApiOnSchedule }).FirstOrDefault();
+            }
+            return _customer;
+        }
+
+        public Task NotifyConnectorUnplugged(string data)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 214 - 0
EVCB_OCPP.WSServer/Service/OuterHttpClient.cs

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

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

@@ -0,0 +1,18 @@
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+
+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"; }
+        }
+    }
+}

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

@@ -0,0 +1,18 @@
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+
+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"; }
+        }
+    }
+}

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

@@ -0,0 +1,18 @@
+using OCPPServer.Protocol;
+using SuperWebSocket.SubProtocol;
+
+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"; }
+        }
+    }
+}

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

@@ -0,0 +1,123 @@
+
+using EVCB_OCPP.Packet.Messages.Basic;
+using EVCB_OCPP.WSServer.Dto;
+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 EVCB_OCPP20.Packet.Messages.Basic.Queue queue20 = new EVCB_OCPP20.Packet.Messages.Basic.Queue();
+
+        public bool IsPending { set; get; }
+        public bool IsCheckIn { set; get; }
+
+        public string ChargeBoxId { set; get; }
+
+        public Guid CustomerId { get; set; }
+
+        public string MachineId { set; get; }
+
+        public bool ISOCPP20 { set; get; }
+
+        public bool ResetSecurityProfile { set; get; }
+
+
+        public bool IsAC { set; get; }
+
+        #region Billing
+
+        public List<ChargingPrice> ChargingPrices { set; get; }
+
+        /// <summary>
+        /// 電樁顯示費率
+        /// </summary>
+        public string DisplayPrice { set; get; }
+
+        /// <summary>
+        /// 充電費率 以小時計費
+        /// </summary>
+        public decimal ChargingFeebyHour { set; get; }
+
+        /// <summary>
+        /// 停車費率 以小時計費
+        /// </summary>
+        public decimal ParkingFee { set; get; }
+
+        /// <summary>
+        /// 電樁是否計費
+        /// </summary>
+        public bool IsBilling { set; get; }
+
+        /// <summary>
+        /// 收費方式 1: 以度計費 2:以小時計費
+        /// </summary>
+        public int BillingMethod { set; get; }
+       
+
+        /// <summary>
+        /// 電樁適用幣別
+        /// </summary>
+        public string Currency { get; internal set; }
+
+        #endregion
+
+        public string CustomerName { get; set; }
+
+
+        public string StationName { set; get; }
+
+
+        public string StationLocation { set; get; }
+
+        public delegate void OCPPClientDataEventHandler<ClientData, String>(ClientData clientdata, String msg);
+
+        public event OCPPClientDataEventHandler<ClientData, String> m_ReceiveData;
+
+        public ClientData()
+        {
+            IsAC = true;
+            IsPending = false;
+            IsCheckIn = false;
+            ChargeBoxId = SessionID;
+            MachineId = SessionID;
+
+        }
+
+
+
+        /// <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)
+        {
+        }
+
+    }
+}

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

@@ -0,0 +1,441 @@
+
+
+using SuperSocket.SocketBase.Logging;
+using System;
+
+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)
+        {
+
+        }
+    }
+}

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

@@ -0,0 +1,31 @@
+using SuperSocket.SocketBase.Logging;
+using System.Collections.Generic;
+
+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
+    }
+}

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

@@ -0,0 +1,322 @@
+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;
+
+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
+    }
+}

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

@@ -0,0 +1,206 @@
+
+using EVCB_OCPP.Domain;
+using NLog;
+using OCPPPackage.Profiles;
+using SuperWebSocket;
+using SuperWebSocket.SubProtocol;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+
+namespace OCPPServer.Protocol
+{
+    public class OCPPWSServer : WebSocketServer<ClientData>
+    {
+
+        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
+        /// <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 ValidateClientCertificate(ClientData session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+        {
+            //  Console.WriteLine(string.Format("{0} :{1}", session.ChargeBoxId + " ValidateClientCertificate", sslPolicyErrors));
+            return true;
+            // return base.ValidateClientCertificate(session, sender, certificate, chain, sslPolicyErrors);
+        }
+
+        protected override bool ValidateHandshake(ClientData session, string origin)
+        {
+            session.ISOCPP20 = session.SecWebSocketProtocol.ToLower().Contains("ocpp2.0");
+
+            int securityProfile = 0;
+            string authorizationKey = string.Empty;
+            if (string.IsNullOrEmpty(session.Path))
+            {
+                logger.Warn("===========================================");
+                logger.Warn("session.Path EMPTY");
+                logger.Warn("===========================================");
+            }
+
+            string[] words = session.Path.Split('/');
+            session.ChargeBoxId = words.Last();
+            logger.Info(string.Format("ValidateHandshake: {0}", session.Path));
+            bool isExistedSN = false;
+            bool authorizated = false;
+            using (var db = new MainDBContext())
+            {
+                var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).FirstOrDefault();
+                session.CustomerName = machine == null ? "Unknown" : db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefault();
+                session.CustomerId = machine == null ? Guid.Empty : machine.CustomerId;
+                session.MachineId = machine == null ? String.Empty : machine.Id;
+                isExistedSN = machine == null ? false : true;
+
+                var configVaule = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.SecurityProfile)
+                                  .Select(x => x.ConfigureSetting).FirstOrDefault();
+                int.TryParse(configVaule, out securityProfile);
+
+                if (session.ISOCPP20)
+                {
+                    // 1.6 server only support change server  function
+                    securityProfile = 0;
+                }
+            }
+            if (securityProfile == 3 && session.UriScheme == "ws")
+            {
+                StringBuilder responseBuilder = new StringBuilder();
+
+                responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+                (int)HttpStatusCode.Unauthorized, @"Unauthorized");
+
+                responseBuilder.AppendWithCrCf();
+                string sb = responseBuilder.ToString();
+                byte[] data = Encoding.UTF8.GetBytes(sb);
+
+                ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
+
+                return false;
+            }
+
+            if ((securityProfile == 1 || securityProfile == 2))
+            {
+                if (securityProfile == 2 && session.UriScheme == "ws")
+                {
+                    authorizated = false;
+                }
+
+                if (session.Items.ContainsKey("Authorization") || session.Items.ContainsKey("authorization"))
+                {
+                   
+                    using (var db = new MainDBContext())
+                    {
+
+                        authorizationKey = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.AuthorizationKey)
+                                          .Select(x => x.ConfigureSetting).FirstOrDefault();
+                      
+
+                        if (session.ISOCPP20)
+                        {
+                            // 1.6 server only support change server  function
+                            securityProfile = 0;
+                        }
+                    }
+                    logger.Info("***********Authorization   ");
+
+                    if (!string.IsNullOrEmpty(authorizationKey))
+                    {
+                        string base64Encoded = session.Items.ContainsKey("Authorization") ? session.Items["Authorization"].ToString().Replace("Basic ", "") : session.Items["authorization"].ToString().Replace("Basic ", "");
+                        byte[] data = Convert.FromBase64String(base64Encoded);
+                        string[] base64Decoded = System.Text.ASCIIEncoding.ASCII.GetString(data).Split(':');
+                        logger.Info("***********Authorization   " + System.Text.ASCIIEncoding.ASCII.GetString(data));
+                        if (base64Decoded.Count() == 2 && base64Decoded[0] == session.ChargeBoxId && base64Decoded[1] == authorizationKey)
+                        {
+                            authorizated = true;
+                        }
+                    }
+
+
+                    
+                 
+
+                }
+                else
+                {
+                    authorizated = true;
+
+                }
+
+
+
+                if (!authorizated)
+                {
+                    StringBuilder responseBuilder = new StringBuilder();
+
+                    responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+                    (int)HttpStatusCode.Unauthorized, @"Unauthorized");
+
+                    responseBuilder.AppendWithCrCf();
+                    string sb = responseBuilder.ToString();
+                    byte[] data = Encoding.UTF8.GetBytes(sb);
+
+                    ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
+
+                    return false;
+                }
+            }
+
+
+            if (!isExistedSN)
+            {
+                StringBuilder responseBuilder = new StringBuilder();
+
+                responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
+                (int)HttpStatusCode.NotFound, @"Not Found");
+
+                responseBuilder.AppendWithCrCf();
+                string sb = responseBuilder.ToString();
+                byte[] data = Encoding.UTF8.GetBytes(sb);
+
+                ((IWebSocketSession)session).SendRawData(data, 0, data.Length);
+
+                return false;
+            }
+
+
+
+            return true;
+        }
+    }
+}

+ 27 - 0
EVCB_OCPP.WSServer/packages.config

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Dapper" version="2.0.30" targetFramework="net471" />
+  <package id="EntityFramework" version="6.2.0" targetFramework="net471" />
+  <package id="log4net" version="2.0.3" targetFramework="net471" />
+  <package id="Microsoft.Bcl.AsyncInterfaces" version="1.1.0" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Configuration" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Configuration.Abstractions" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Configuration.Binder" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.DependencyInjection" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Http" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Logging" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Logging.Abstractions" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Options" version="3.1.3" targetFramework="net471" />
+  <package id="Microsoft.Extensions.Primitives" version="3.1.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" />
+  <package id="System.Buffers" version="4.4.0" targetFramework="net471" />
+  <package id="System.ComponentModel.Annotations" version="4.7.0" targetFramework="net471" />
+  <package id="System.Memory" version="4.5.2" targetFramework="net471" />
+  <package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net471" />
+  <package id="System.Runtime.CompilerServices.Unsafe" version="4.7.1" targetFramework="net471" />
+  <package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net471" />
+</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);
+            }
+        }
+    }
+}

+ 24 - 0
SocketBase/Config/TypeProviderConfig.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SuperSocket.SocketBase.Config
+{
+    /// <summary>
+    /// TypeProviderConfig
+    /// </summary>
+    [Serializable]
+    public class TypeProviderConfig : ITypeProvider
+    {
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gets the type.
+        /// </summary>
+        public string Type { get; set; }
+    }
+}

+ 62 - 0
SocketBase/Extensions.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using SuperSocket.SocketBase.Metadata;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// Extensions class for SocketBase project
+    /// </summary>
+    public static class Extensions
+    {
+        /// <summary>
+        /// Gets the app server instance in the bootstrap by name, ignore case
+        /// </summary>
+        /// <param name="bootstrap">The bootstrap.</param>
+        /// <param name="name">The name of the appserver instance.</param>
+        /// <returns></returns>
+        /// <exception cref="System.ArgumentNullException"></exception>
+        public static IWorkItem GetServerByName(this IBootstrap bootstrap, string name)
+        {
+            if (string.IsNullOrEmpty(name))
+                throw new ArgumentNullException("name");
+
+            return bootstrap.AppServers.FirstOrDefault(s => name.Equals(s.Name, StringComparison.OrdinalIgnoreCase));
+        }
+
+        /// <summary>
+        /// Gets the status info metadata from the server type.
+        /// </summary>
+        /// <param name="serverType">Type of the server.</param>
+        /// <returns></returns>
+        /// <exception cref="System.ArgumentNullException"></exception>
+        public static StatusInfoAttribute[] GetStatusInfoMetadata(this Type serverType)
+        {
+            if (serverType == null)
+                throw new ArgumentNullException("serverType");
+
+            var attType = typeof(AppServerMetadataTypeAttribute);
+
+            while (true)
+            {
+                var atts = serverType.GetCustomAttributes(attType, false);
+
+                if (atts != null && atts.Length > 0)
+                {
+                    var serverMetadataTypeAtt = atts[0] as AppServerMetadataTypeAttribute;
+                    return serverMetadataTypeAtt
+                            .MetadataType
+                            .GetCustomAttributes(typeof(StatusInfoAttribute), true)
+                            .OfType<StatusInfoAttribute>().ToArray();
+                }
+
+                if (serverType.BaseType == null)
+                    return null;
+
+                serverType = serverType.BaseType;
+            }
+        }
+    }
+}

+ 52 - 0
SocketBase/IActiveConnector.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Net;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// Active connect result model
+    /// </summary>
+    public class ActiveConnectResult
+    {
+        /// <summary>
+        /// Gets or sets a value indicating whether the conecting is sucessfull
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if result; otherwise, <c>false</c>.
+        /// </value>
+        public bool Result { get; set; }
+
+        /// <summary>
+        /// Gets or sets the connected session.
+        /// </summary>
+        /// <value>
+        /// The connected session.
+        /// </value>
+        public IAppSession Session { get; set; }
+    }
+
+    /// <summary>
+    /// The inerface to connect the remote endpoint actively
+    /// </summary>
+    public interface IActiveConnector
+    {
+        /// <summary>
+        /// Connect the target endpoint actively.
+        /// </summary>
+        /// <param name="targetEndPoint">The target end point.</param>
+        /// <returns></returns>
+        Task<ActiveConnectResult> ActiveConnect(EndPoint targetEndPoint);
+
+        /// <summary>
+        /// Connect the target endpoint actively.
+        /// </summary>
+        /// <param name="targetEndPoint">The target end point.</param>
+        /// <param name="localEndPoint">The local end point.</param>
+        /// <returns></returns>
+        Task<ActiveConnectResult> ActiveConnect(EndPoint targetEndPoint, EndPoint localEndPoint);
+    }
+}

+ 199 - 0
SocketBase/IAppServer.cs

@@ -0,0 +1,199 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Security;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Command;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Logging;
+using SuperSocket.SocketBase.Protocol;
+using SuperSocket.SocketBase.Provider;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// The interface for AppServer
+    /// </summary>
+    public interface IAppServer : IWorkItem, ILoggerProvider
+    {
+        /// <summary>
+        /// Gets the started time.
+        /// </summary>
+        /// <value>
+        /// The started time.
+        /// </value>
+        DateTime StartedTime { get; }
+
+
+        /// <summary>
+        /// Gets or sets the listeners.
+        /// </summary>
+        /// <value>
+        /// The listeners.
+        /// </value>
+        ListenerInfo[] Listeners { get; }
+
+        /// <summary>
+        /// Gets the Receive filter factory.
+        /// </summary>
+        object ReceiveFilterFactory { get; }
+
+        /// <summary>
+        /// Gets the certificate of current server.
+        /// </summary>
+        X509Certificate Certificate { get; }
+
+        /// <summary>
+        /// Gets the transfer layer security protocol.
+        /// </summary>
+        SslProtocols BasicSecurity { get; }
+
+        /// <summary>
+        /// Creates the app session.
+        /// </summary>
+        /// <param name="socketSession">The socket session.</param>
+        /// <returns></returns>
+        IAppSession CreateAppSession(ISocketSession socketSession);
+
+
+        /// <summary>
+        /// Registers the new created app session into the appserver's session container.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <returns></returns>
+        bool RegisterSession(IAppSession session);
+
+        /// <summary>
+        /// Gets the app session by ID.
+        /// </summary>
+        /// <param name="sessionID">The session ID.</param>
+        /// <returns></returns>
+        IAppSession GetSessionByID(string sessionID);
+
+        /// <summary>
+        /// Resets the session's security protocol.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="security">The security protocol.</param>
+        void ResetSessionSecurity(IAppSession session, SslProtocols security);
+
+        /// <summary>
+        /// Gets the log factory.
+        /// </summary>
+        ILogFactory LogFactory { get; }
+    }
+
+    /// <summary>
+    /// The raw data processor
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    public interface IRawDataProcessor<TAppSession>
+        where TAppSession : IAppSession
+    {
+        /// <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> RawDataReceived;
+    }
+
+    /// <summary>
+    /// The interface for AppServer
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    public interface IAppServer<TAppSession> : IAppServer
+        where TAppSession : IAppSession
+    {
+        /// <summary>
+        /// Gets the matched sessions from sessions snapshot.
+        /// </summary>
+        /// <param name="critera">The prediction critera.</param>
+        /// <returns></returns>
+        IEnumerable<TAppSession> GetSessions(Func<TAppSession, bool> critera);
+
+        /// <summary>
+        /// Gets all sessions in sessions snapshot.
+        /// </summary>
+        /// <returns></returns>
+        IEnumerable<TAppSession> GetAllSessions();
+
+        /// <summary>
+        /// Gets/sets the new session connected event handler.
+        /// </summary>
+        event SessionHandler<TAppSession> NewSessionConnected;
+
+        /// <summary>
+        /// Gets/sets the session closed event handler.
+        /// </summary>
+        event SessionHandler<TAppSession, CloseReason> SessionClosed;
+    }
+
+    /// <summary>
+    /// The interface for AppServer
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    public interface IAppServer<TAppSession, TRequestInfo> : IAppServer<TAppSession>
+        where TRequestInfo : IRequestInfo
+        where TAppSession : IAppSession, IAppSession<TAppSession, TRequestInfo>, new()
+    {
+        /// <summary>
+        /// Occurs when [request comming].
+        /// </summary>
+        event RequestHandler<TAppSession, TRequestInfo> NewRequestReceived;
+    }
+
+    /// <summary>
+    /// The interface for handler of session request
+    /// </summary>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    public interface IRequestHandler<TRequestInfo>
+        where TRequestInfo : IRequestInfo
+    {
+        /// <summary>
+        /// Executes the command.
+        /// </summary>
+        /// <param name="session">The session.</param>
+        /// <param name="requestInfo">The request info.</param>
+        void ExecuteCommand(IAppSession session, TRequestInfo requestInfo);
+    }
+
+    /// <summary>
+    /// SocketServer Accessor interface
+    /// </summary>
+    public interface ISocketServerAccessor
+    {
+        /// <summary>
+        /// Gets the socket server.
+        /// </summary>
+        /// <value>
+        /// The socket server.
+        /// </value>
+        ISocketServer SocketServer { get; }
+    }
+
+    /// <summary>
+    /// The basic interface for RemoteCertificateValidator
+    /// </summary>
+    public interface IRemoteCertificateValidator
+    {
+        /// <summary>
+        /// Validates the remote certificate
+        /// </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></returns>
+        bool Validate(IAppSession session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors);
+    }
+}

+ 135 - 0
SocketBase/IAppSession.cs

@@ -0,0 +1,135 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using SuperSocket.Common;
+using SuperSocket.SocketBase.Logging;
+using SuperSocket.SocketBase.Command;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Protocol;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// The basic interface for appSession
+    /// </summary>
+    public interface IAppSession : ISessionBase
+    {
+        /// <summary>
+        /// Gets the app server.
+        /// </summary>
+        IAppServer AppServer { get; }
+        /// <summary>
+        /// Gets the socket session of the AppSession.
+        /// </summary>
+        ISocketSession SocketSession { get; }
+
+        /// <summary>
+        /// Gets the items.
+        /// </summary>
+        IDictionary<object, object> Items { get; }
+
+        /// <summary>
+        /// Gets the config of the server.
+        /// </summary>
+        IServerConfig Config { get; }
+
+        /// <summary>
+        /// Gets the local listening endpoint.
+        /// </summary>
+        IPEndPoint LocalEndPoint { get; }
+
+        /// <summary>
+        /// Gets or sets the last active time of the session.
+        /// </summary>
+        /// <value>
+        /// The last active time.
+        /// </value>
+        DateTime LastActiveTime { get; set; }
+
+        /// <summary>
+        /// Gets the start time of the session.
+        /// </summary>
+        DateTime StartTime { get; }
+
+        /// <summary>
+        /// Closes this session.
+        /// </summary>
+        void Close();
+
+        /// <summary>
+        /// Closes the session by the specified reason.
+        /// </summary>
+        /// <param name="reason">The close reason.</param>
+        void Close(CloseReason reason);
+
+        /// <summary>
+        /// Gets a value indicating whether this <see cref="IAppSession"/> is connected.
+        /// </summary>
+        /// <value>
+        ///   <c>true</c> if connected; otherwise, <c>false</c>.
+        /// </value>
+        bool Connected { get; }
+
+        /// <summary>
+        /// Gets or sets the charset which is used for transfering text message.
+        /// </summary>
+        /// <value>The charset.</value>
+        Encoding Charset { get; set; }
+
+        /// <summary>
+        /// Gets or sets the previous command.
+        /// </summary>
+        /// <value>
+        /// The prev command.
+        /// </value>
+        string PrevCommand { get; set; }
+
+        /// <summary>
+        /// Gets or sets the current executing command.
+        /// </summary>
+        /// <value>
+        /// The current command.
+        /// </value>
+        string CurrentCommand { get; set; }
+
+        /// <summary>
+        /// Gets the logger assosiated with this session.
+        /// </summary>
+        ILog Logger { get; }
+
+        /// <summary>
+        /// Processes 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>
+        /// <returns>return offset delta of next receiving buffer</returns>
+        int ProcessRequest(byte[] readBuffer, int offset, int length, bool toBeCopied);
+
+        /// <summary>
+        /// Starts the session.
+        /// </summary>
+        void StartSession();
+    }
+
+    /// <summary>
+    /// The interface for appSession
+    /// </summary>
+    /// <typeparam name="TAppSession">The type of the app session.</typeparam>
+    /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
+    public interface IAppSession<TAppSession, TRequestInfo> : IAppSession
+        where TRequestInfo : IRequestInfo
+        where TAppSession : IAppSession, IAppSession<TAppSession, TRequestInfo>, new()
+    {
+        /// <summary>
+        /// Initializes the specified session.
+        /// </summary>
+        /// <param name="server">The server.</param>
+        /// <param name="socketSession">The socket session.</param>
+        void Initialize(IAppServer<TAppSession, TRequestInfo> server, ISocketSession socketSession);
+    }
+}

+ 141 - 0
SocketBase/IBootstrap.cs

@@ -0,0 +1,141 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using SuperSocket.SocketBase;
+using SuperSocket.SocketBase.Config;
+using SuperSocket.SocketBase.Logging;
+using System.Net;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// The bootstrap start result
+    /// </summary>
+    public enum StartResult
+    {
+        /// <summary>
+        /// No appserver has been set in the bootstrap, so nothing was started
+        /// </summary>
+        None,
+        /// <summary>
+        /// All appserver instances were started successfully
+        /// </summary>
+        Success,
+        /// <summary>
+        /// Some appserver instances were started successfully, but some of them failed
+        /// </summary>
+        PartialSuccess,
+        /// <summary>
+        /// All appserver instances failed to start
+        /// </summary>
+        Failed
+    }
+
+    /// <summary>
+    /// SuperSocket bootstrap
+    /// </summary>
+    public interface IBootstrap
+    {
+        /// <summary>
+        /// Gets all the app servers running in this bootstrap
+        /// </summary>
+        IEnumerable<IWorkItem> AppServers { get; }
+
+        /// <summary>
+        /// Gets the config.
+        /// </summary>
+        IRootConfig Config { get; }
+
+        /// <summary>
+        /// Initializes the bootstrap with the configuration
+        /// </summary>
+        /// <returns></returns>
+        bool Initialize();
+
+
+        /// <summary>
+        /// Initializes the bootstrap with a listen endpoint replacement dictionary
+        /// </summary>
+        /// <param name="listenEndPointReplacement">The listen end point replacement.</param>
+        /// <returns></returns>
+        bool Initialize(IDictionary<string, IPEndPoint> listenEndPointReplacement);
+
+        /// <summary>
+        /// Initializes the bootstrap with the configuration
+        /// </summary>
+        /// <param name="serverConfigResolver">The server config resolver.</param>
+        /// <returns></returns>
+        bool Initialize(Func<IServerConfig, IServerConfig> serverConfigResolver);
+
+
+        /// <summary>
+        /// Initializes the bootstrap with the configuration
+        /// </summary>
+        /// <param name="logFactory">The log factory.</param>
+        /// <returns></returns>
+        bool Initialize(ILogFactory logFactory);
+
+        /// <summary>
+        /// Initializes the bootstrap with the configuration
+        /// </summary>
+        /// <param name="serverConfigResolver">The server config resolver.</param>
+        /// <param name="logFactory">The log factory.</param>
+        /// <returns></returns>
+        bool Initialize(Func<IServerConfig, IServerConfig> serverConfigResolver, ILogFactory logFactory);
+
+        /// <summary>
+        /// Starts this bootstrap.
+        /// </summary>
+        /// <returns></returns>
+        StartResult Start();
+
+        /// <summary>
+        /// Stops this bootstrap.
+        /// </summary>
+        void Stop();
+
+
+        /// <summary>
+        /// Gets the startup config file.
+        /// </summary>
+        string StartupConfigFile { get; }
+
+
+        /// <summary>
+        /// Gets the base directory.
+        /// </summary>
+        /// <value>
+        /// The base directory.
+        /// </value>
+        string BaseDirectory { get; }
+    }
+
+    /// <summary>
+    /// The bootstrap interface to support add new server instance in runtime
+    /// </summary>
+    public interface IDynamicBootstrap
+    {
+        /// <summary>
+        /// Adds a new server into the bootstrap.
+        /// </summary>
+        /// <param name="config">The new server's config.</param>
+        /// <returns></returns>
+        bool Add(IServerConfig config);
+
+        /// <summary>
+        /// Adds a new server into the bootstrap and then start it.
+        /// </summary>
+        /// <param name="config">The new server's config.</param>
+        /// <returns></returns>
+        bool AddAndStart(IServerConfig config);
+
+
+        /// <summary>
+        /// Removes the server instance which is specified by name.
+        /// </summary>
+        /// <param name="name">The name of the server instance to be removed.</param>
+        void Remove(string name);
+    }
+}

+ 35 - 0
SocketBase/IConnectionFilter.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Specialized;
+using System.Configuration.Provider;
+using System.Net;
+using SuperSocket.SocketBase.Config;
+
+namespace SuperSocket.SocketBase
+{
+    /// <summary>
+    /// The basic interface of connection filter
+    /// </summary>
+    public interface IConnectionFilter
+    {
+        /// <summary>
+        /// Initializes the connection filter
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <param name="appServer">The app server.</param>
+        /// <returns></returns>
+        bool Initialize(string name, IAppServer appServer);
+
+        /// <summary>
+        /// Gets the name of the filter.
+        /// </summary>
+        string Name { get; }
+
+        /// <summary>
+        /// Whether allows the connect according the remote endpoint
+        /// </summary>
+        /// <param name="remoteAddress">The remote address.</param>
+        /// <returns></returns>
+        bool AllowConnect(IPEndPoint remoteAddress);
+    }
+}
+

Some files were not shown because too many files changed in this diff