Browse Source

添加项目文件。

林豪 左 3 years ago
commit
f60acd384a
100 changed files with 8977 additions and 0 deletions
  1. 4 0
      .editorconfig
  2. 63 0
      .gitattributes
  3. 363 0
      .gitignore
  4. 394 0
      DBHelper/DB.cs
  5. 16 0
      DBHelper/DBHelper.csproj
  6. 40 0
      DBHelper/EFLoggerProvider.cs
  7. 14 0
      DBHelper/EntityValidationException.cs
  8. BIN
      DLL/DBHelper.dll
  9. BIN
      DLL/PLC.Siemens.dll
  10. BIN
      DLL/PLCConnecter.dll
  11. BIN
      DLL/WCS.Core.dll
  12. BIN
      DLL/WCS.Entity.dll
  13. 286 0
      DLL/net5.0/DBHelper.deps.json
  14. BIN
      DLL/net5.0/DBHelper.dll
  15. 329 0
      DLL/net5.0/WCS.Core.deps.json
  16. BIN
      DLL/net5.0/WCS.Core.dll
  17. 23 0
      DLL/net5.0/WCS.Entity.deps.json
  18. BIN
      DLL/net5.0/WCS.Entity.dll
  19. BIN
      DLL/net5.0/ref/DBHelper.dll
  20. BIN
      DLL/net5.0/ref/WCS.Core.dll
  21. BIN
      DLL/net5.0/ref/WCS.Entity.dll
  22. 250 0
      PLC.Siemens/Communication/IsoSocket.cs
  23. 339 0
      PLC.Siemens/Core/Common/Argument.cs
  24. 20 0
      PLC.Siemens/Core/Common/Constants.cs
  25. 262 0
      PLC.Siemens/Core/Common/DefaultConvert.cs
  26. 176 0
      PLC.Siemens/Core/Common/DelegateFactory.cs
  27. 25 0
      PLC.Siemens/Core/Common/FlowControlUtil.cs
  28. 14 0
      PLC.Siemens/Core/Common/IControlAble.cs
  29. 22 0
      PLC.Siemens/Core/Common/IDisposableEx.cs
  30. 154 0
      PLC.Siemens/Core/Common/ModelHandler.cs
  31. 539 0
      PLC.Siemens/Core/Common/ObjectId.cs
  32. 70 0
      PLC.Siemens/Core/Common/Rand.cs
  33. 42 0
      PLC.Siemens/Core/Common/SingleBase.cs
  34. 12 0
      PLC.Siemens/Core/Common/SystemTime.cs
  35. 51 0
      PLC.Siemens/Core/Extension/ArrayExtension.cs
  36. 235 0
      PLC.Siemens/Core/Extension/BitExtension.cs
  37. 322 0
      PLC.Siemens/Core/Extension/ByteExtenstion.cs
  38. 235 0
      PLC.Siemens/Core/Extension/CollectionExtension.cs
  39. 35 0
      PLC.Siemens/Core/Extension/DateTimeExtension.cs
  40. 78 0
      PLC.Siemens/Core/Extension/EncodingHelper.cs
  41. 95 0
      PLC.Siemens/Core/Extension/EnumExtension.cs
  42. 33 0
      PLC.Siemens/Core/Extension/GuidExtension.cs
  43. 36 0
      PLC.Siemens/Core/Extension/IEnumerableExtensions.cs
  44. 46 0
      PLC.Siemens/Core/Extension/ObjectExtension.cs
  45. 121 0
      PLC.Siemens/Core/Extension/ReaderWriterLockSlimExtensions.cs
  46. 17 0
      PLC.Siemens/Core/Extension/StringBuilderExtension.cs
  47. 789 0
      PLC.Siemens/Core/Extension/StringHelper.cs
  48. 56 0
      PLC.Siemens/Core/Extension/TypeExtension.cs
  49. 640 0
      PLC.Siemens/O/ByteBuffer.cs
  50. 25 0
      PLC.Siemens/O/HelperFunction.cs
  51. 10 0
      PLC.Siemens/O/IBuildRequest.cs
  52. 9 0
      PLC.Siemens/O/IBuildResponse.cs
  53. 27 0
      PLC.Siemens/O/IIsoSender.cs
  54. 14 0
      PLC.Siemens/O/MessageEvent.cs
  55. 24 0
      PLC.Siemens/O/RequestHandle.cs
  56. 18 0
      PLC.Siemens/O/ResponseHandle.cs
  57. 934 0
      PLC.Siemens/O/SimenssPlc.cs
  58. 13 0
      PLC.Siemens/O/StaticConst.cs
  59. 206 0
      PLC.Siemens/PLC.Siemens.csproj
  60. 36 0
      PLC.Siemens/Properties/AssemblyInfo.cs
  61. 30 0
      PLC.Siemens/Protocol/Common/AreaType.cs
  62. 14 0
      PLC.Siemens/Protocol/Common/CodeControl.cs
  63. 35 0
      PLC.Siemens/Protocol/Common/CodeError.cs
  64. 90 0
      PLC.Siemens/Protocol/Common/ConnectionType.cs
  65. 31 0
      PLC.Siemens/Protocol/Common/DataType.cs
  66. 25 0
      PLC.Siemens/Protocol/Common/ErrorType.cs
  67. 14 0
      PLC.Siemens/Protocol/Common/GrType.cs
  68. 32 0
      PLC.Siemens/Protocol/Common/PduFuncType.cs
  69. 12 0
      PLC.Siemens/Protocol/Common/PduKind.cs
  70. 46 0
      PLC.Siemens/Protocol/Common/PduType.cs
  71. 9 0
      PLC.Siemens/Protocol/Common/S7CpuStatus.cs
  72. 35 0
      PLC.Siemens/Protocol/Common/S7OpType.cs
  73. 11 0
      PLC.Siemens/Protocol/Common/TsType.cs
  74. 33 0
      PLC.Siemens/Protocol/Control/CompressParamsRequest.cs
  75. 33 0
      PLC.Siemens/Protocol/Control/CompressRequest.cs
  76. 37 0
      PLC.Siemens/Protocol/Control/CopyRamToRomParamsRequest.cs
  77. 33 0
      PLC.Siemens/Protocol/Control/CopyRamToRomRequest.cs
  78. 37 0
      PLC.Siemens/Protocol/Control/PlcColdStartParamsRequest.cs
  79. 33 0
      PLC.Siemens/Protocol/Control/PlcColdStartRequest.cs
  80. 37 0
      PLC.Siemens/Protocol/Control/PlcHotStartParamsRequest.cs
  81. 33 0
      PLC.Siemens/Protocol/Control/PlcHotStartRequest.cs
  82. 31 0
      PLC.Siemens/Protocol/Control/PlcStopParamsRequest.cs
  83. 33 0
      PLC.Siemens/Protocol/Control/PlcStopRequest.cs
  84. 23 0
      PLC.Siemens/Protocol/DateTime/ControlResponse.cs
  85. 62 0
      PLC.Siemens/Protocol/DateTime/DateTimeData.cs
  86. 16 0
      PLC.Siemens/Protocol/DateTime/FunCtrlResponse.cs
  87. 36 0
      PLC.Siemens/Protocol/DateTime/GetDateTimeRequest.cs
  88. 50 0
      PLC.Siemens/Protocol/DateTime/GetDateTimeResponse.cs
  89. 10 0
      PLC.Siemens/Protocol/DateTime/HandleRequest.cs
  90. 26 0
      PLC.Siemens/Protocol/DateTime/HandleResponse.cs
  91. 39 0
      PLC.Siemens/Protocol/DateTime/SetDateTimeRequest.cs
  92. 21 0
      PLC.Siemens/Protocol/Header/CotpDataHeader.cs
  93. 85 0
      PLC.Siemens/Protocol/Header/CotpHeader.cs
  94. 54 0
      PLC.Siemens/Protocol/Header/HeaderPacket.cs
  95. 21 0
      PLC.Siemens/Protocol/Header/HeaderPacketWithErrorCode.cs
  96. 27 0
      PLC.Siemens/Protocol/Header/TpktHeader.cs
  97. 37 0
      PLC.Siemens/Protocol/Iso/CoptParams.cs
  98. 115 0
      PLC.Siemens/Protocol/Iso/IsoControlPdu.cs
  99. 50 0
      PLC.Siemens/Protocol/Iso/IsoDataPdu.cs
  100. 19 0
      PLC.Siemens/Protocol/ListBlocks/BlockItem.cs

+ 4 - 0
.editorconfig

@@ -0,0 +1,4 @@
+[*.cs]
+
+# IDE1006: 命名样式
+dotnet_diagnostic.IDE1006.severity = none

+ 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

+ 363 - 0
.gitignore

@@ -0,0 +1,363 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Oo]ut/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.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
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# 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
+# Note: 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
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable 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
+*.appx
+*.appxbundle
+*.appxupload
+
+# 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
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# 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
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# 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/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd

+ 394 - 0
DBHelper/DB.cs

@@ -0,0 +1,394 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Transactions;
+
+namespace DBHelper
+{
+    public class DB2 : IDisposable
+    {
+        public static Type DefaultDbContextType { get; private set; }
+
+        private static object lockObj = new object();
+
+        public static T Do<T>(Func<DB2, T> func)
+        {
+            lock (lockObj)
+                using (var db = new DB2())
+                {
+                    try
+                    {
+                        var res = func(db);
+                        db.Commit();
+                        return res;
+                    }
+                    catch (EntityValidationException ex)
+                    {
+                        var qty = ex.EntityValidationErrors.Count();
+                        var info = ex.EntityValidationErrors.First();
+                        var msg = "有" + qty + "条数据验证失败,首条错误信息:\n" + string.Join("\n", info.MemberNames) + "\n" + (info.ErrorMessage ?? "");
+                        Console.WriteLine(msg);
+                        throw new Exception(msg);
+                    }
+                }
+        }
+
+        public static void Do(Action<DB2> act)
+        {
+            Do(db =>
+            {
+                act(db);
+                return 1;
+            });
+        }
+
+        public static void SetDefaultDbContextType(Type dbContextType)
+        {
+            DefaultDbContextType = dbContextType;
+        }
+
+        public static void SetDefaultDbContextType<T>()
+        {
+            SetDefaultDbContextType(typeof(T));
+        }
+
+        private Dictionary<Type, DbContext> Contexts = new Dictionary<Type, DbContext>();
+
+        public T Context<T>() where T : DbContext
+        {
+            return (T)Context(typeof(T));
+        }
+
+        public DbContext Default
+        {
+            get
+            {
+                if (DefaultDbContextType == null)
+                    throw new Exception("请先设置默认数据库,调用静态方法SetDefaultDbContextType()");
+                return Context(DefaultDbContextType);
+            }
+        }
+
+        public DbContext Context(Type type)
+        {
+            if (Contexts.ContainsKey(type))
+                return Contexts[type];
+            else
+            {
+                DbContext ctx = Activator.CreateInstance(type) as DbContext;
+                ctx.Database.BeginTransaction();
+                Contexts.Add(type, ctx);
+                return ctx;
+            }
+        }
+
+        private void Commit()
+        {
+            Exception exception = null;
+            try
+            {
+                foreach (var ctx in Contexts.Values)
+                {
+                    ctx.SaveChanges();
+                }
+            }
+            catch (EntityValidationException ex)
+            {
+                var qty = ex.EntityValidationErrors.Count();
+                var info = ex.EntityValidationErrors.First();
+                var msg = "有" + qty + "条数据验证失败,首条错误信息:\n" + string.Join("\n", info.MemberNames) + "\n" + (info.ErrorMessage ?? "");
+                Console.WriteLine(msg);
+                exception = new Exception(msg);
+            }
+            catch (Exception ex)
+            {
+                exception = ex.GetBaseException();
+            }
+
+            foreach (var ctx in Contexts.Values)
+            {
+                try
+                {
+                    if (exception == null)
+                        ctx.Database.CurrentTransaction.Commit();
+                    else
+                    {
+                        ctx.Database.CurrentTransaction.Rollback();
+                        throw exception;
+                    }
+                }
+                finally
+                {
+                    ctx.Dispose();
+                }
+            }
+        }
+
+        public void Dispose()
+        {
+            foreach (var ctx in Contexts.Values)
+                ctx.Dispose();
+            Contexts.Clear();
+            Contexts = null;
+        }
+    }
+
+    public class DB : IDisposable
+    {
+        private List<DbContext> Contexts = new List<DbContext>();
+        public static Type DefaultDbContextType { get; private set; }
+
+        public static T Do<T>(Func<DB, T> func)
+        {
+            TransactionOptions transactionOptions = new TransactionOptions();
+            transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
+            transactionOptions.Timeout = new TimeSpan(0, 2, 0);
+            using (var trans = new TransactionScope(TransactionScopeOption.RequiresNew, transactionOptions))
+            {
+                using (var db = new DB())
+                {
+                    try
+                    {
+                        var res = func(db);
+                        trans.Complete();
+                        return res;
+                    }
+                    catch (EntityValidationException ex)
+                    {
+                        var qty = ex.EntityValidationErrors.Count();
+                        var info = ex.EntityValidationErrors.First();
+                        var msg = "有" + qty + "条数据验证失败,首条错误信息:\n" + string.Join("\n", info.MemberNames) + "\n" + (info.ErrorMessage ?? "");
+                        Console.WriteLine(msg);
+                        throw new Exception(msg);
+                    }
+                }
+            }
+        }
+
+        public static void Do(Action<DB> act)
+        {
+            Do(db =>
+            {
+                act(db);
+                return 1;
+            });
+        }
+
+        public static void SetDefaultDbContextType(Type dbContextType)
+        {
+            DefaultDbContextType = dbContextType;
+        }
+
+        public static void SetDefaultDbContextType<T>()
+        {
+            SetDefaultDbContextType(typeof(T));
+        }
+
+        public DbContext Default
+        {
+            get
+            {
+                if (DefaultDbContextType == null)
+                    throw new Exception("请先设置默认数据库,调用静态方法SetDefaultDbContextType()");
+                return Context(DefaultDbContextType);
+            }
+        }
+
+        public DbContext Context(Type type)
+        {
+            var ctx = Contexts.Where(v => v.GetType() == type).FirstOrDefault();
+            if (ctx == null)
+            {
+                ctx = Activator.CreateInstance(type) as DbContext;
+                Contexts.Add(ctx);
+            }
+            return ctx;
+        }
+
+        public DbContext Context<TDbContext>()
+        {
+            return Context(typeof(TDbContext));
+        }
+
+        public void Dispose()
+        {
+            foreach (var ctx in Contexts)
+            {
+                ctx.Dispose();
+            }
+            Contexts.Clear();
+            Contexts = null;
+        }
+    }
+
+    public class DB<TContext> where TContext : DbContext
+    {
+        public static IEnumerable<Type> EntityTypes
+        {
+            get
+            {
+                var ps = typeof(TContext).GetProperties().Where(v => v.PropertyType.Name == "DbSet`1").ToArray();
+                var ts = ps.Select(v => v.PropertyType.GenericTypeArguments[0]).ToArray();
+                return ts;
+            }
+        }
+
+        /// <summary>
+        /// 单数据库事务
+        /// </summary>
+        /// <typeparam name="TR"></typeparam>
+        /// <param name="func"></param>
+        /// <returns></returns>
+        public static T Do<T>(Func<TContext, T> func)
+        {
+            using (var ctx = Activator.CreateInstance<TContext>())
+            {
+                var trans = ctx.Database.BeginTransaction(System.Data.IsolationLevel.Serializable);
+                try
+                {
+                    try
+                    {
+                        var result = func(ctx);
+                        ctx.SaveChanges();
+                        trans.Commit();
+                        return result;
+                    }
+                    catch (Exception ex)
+                    {
+                        trans.Rollback();
+                        throw ex.GetBaseException();
+                    }
+                }
+                catch (EntityValidationException ex)
+                {
+                    var qty = ex.EntityValidationErrors.Count();
+                    var info = ex.EntityValidationErrors.First();
+                    var msg = "有" + qty + "条数据验证失败,首条错误信息:\n" + string.Join("\n", info.MemberNames) + "\n" + (info.ErrorMessage ?? "");
+                    Console.WriteLine(msg);
+                    throw new Exception(msg);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 单数据库事务
+        /// </summary>
+        /// <param name="act"></param>
+        public static void Do(Action<TContext> act)
+        {
+            Do(ctx =>
+            {
+                act(ctx);
+                return 1;
+            });
+        }
+    }
+
+    public class DB<TContext1, TContext2> where TContext1 : DbContext where TContext2 : DbContext
+    {
+        public static T Do<T>(Func<TContext1, TContext2, T> func)
+        {
+            using (var ctx = Activator.CreateInstance<TContext1>())
+            {
+                using (var ctx2 = Activator.CreateInstance<TContext2>())
+                {
+                    var trans = ctx.Database.BeginTransaction(System.Data.IsolationLevel.Serializable);
+                    var trans2 = ctx2.Database.BeginTransaction(System.Data.IsolationLevel.Serializable);
+                    try
+                    {
+                        try
+                        {
+                            var result = func(ctx, ctx2);
+                            ctx.SaveChanges();
+                            ctx2.SaveChanges();
+                            trans.Commit();
+                            trans2.Commit();
+                            return result;
+                        }
+                        catch (Exception ex)
+                        {
+                            trans.Rollback();
+                            trans2.Rollback();
+                            throw ex.GetBaseException();
+                        }
+                    }
+                    catch (EntityValidationException ex)
+                    {
+                        var qty = ex.EntityValidationErrors.Count();
+                        var info = ex.EntityValidationErrors.First();
+                        var msg = "有" + qty + "条数据验证失败,首条错误信息:\n" + string.Join("\n", info.MemberNames) + "\n" + (info.ErrorMessage ?? "");
+                        Console.WriteLine(msg);
+                        throw new Exception(msg);
+                    }
+                }
+            }
+        }
+
+        public static void Do(Action<TContext1, TContext2> act)
+        {
+            Do((ctx, ctx2) =>
+            {
+                act(ctx, ctx2);
+                return 1;
+            });
+        }
+    }
+
+    public class DB<TContext1, TContext2, TContext3> where TContext1 : DbContext where TContext2 : DbContext where TContext3 : DbContext
+    {
+        public static T Do<T>(Func<TContext1, TContext2, TContext3, T> func)
+        {
+            using (var ctx = Activator.CreateInstance<TContext1>())
+            {
+                using (var ctx2 = Activator.CreateInstance<TContext2>())
+                {
+                    using (var ctx3 = Activator.CreateInstance<TContext3>())
+                    {
+                        var trans = ctx.Database.BeginTransaction(System.Data.IsolationLevel.Serializable);
+                        var trans2 = ctx2.Database.BeginTransaction(System.Data.IsolationLevel.Serializable);
+                        var trans3 = ctx3.Database.BeginTransaction(System.Data.IsolationLevel.Serializable);
+                        try
+                        {
+                            try
+                            {
+                                var result = func(ctx, ctx2, ctx3);
+                                ctx.SaveChanges();
+                                ctx2.SaveChanges();
+                                ctx3.SaveChanges();
+                                trans.Commit();
+                                trans2.Commit();
+                                trans3.Commit();
+                                return result;
+                            }
+                            catch (Exception ex)
+                            {
+                                trans.Rollback();
+                                trans2.Rollback();
+                                trans3.Rollback();
+                                throw ex.GetBaseException();
+                            }
+                        }
+                        catch (EntityValidationException ex)
+                        {
+                            var qty = ex.EntityValidationErrors.Count();
+                            var info = ex.EntityValidationErrors.First();
+                            var msg = "有" + qty + "条数据验证失败,首条错误信息:\n" + string.Join("\n", info.MemberNames) + "\n" + (info.ErrorMessage ?? "");
+                            Console.WriteLine(msg);
+                            throw new Exception(msg);
+                        }
+                    }
+                }
+            }
+        }
+
+        public static void Do(Action<TContext1, TContext2, TContext3> act)
+        {
+            Do((ctx, ctx2, ctx3) =>
+            {
+                act(ctx, ctx2, ctx3);
+                return 1;
+            });
+        }
+    }
+}

+ 16 - 0
DBHelper/DBHelper.csproj

@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net5.0</TargetFramework>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <OutputPath>D:\Source\WCS\DLL\</OutputPath>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.5" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.5" />
+  </ItemGroup>
+
+</Project>

+ 40 - 0
DBHelper/EFLoggerProvider.cs

@@ -0,0 +1,40 @@
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DBHelper
+{
+    public class EFLoggerProvider : ILoggerProvider
+    {
+        public ILogger CreateLogger(string categoryName) => new EFLogger(categoryName);
+        public void Dispose() { }
+    }
+
+    public class EFLogger : ILogger
+    {
+        private readonly string categoryName;
+
+        public EFLogger(string categoryName) => this.categoryName = categoryName;
+
+        public bool IsEnabled(LogLevel logLevel) => true;
+
+        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
+        {
+            //ef core执行数据库查询时的categoryName为Microsoft.EntityFrameworkCore.Database.Command,日志级别为Information
+            if (categoryName == "Microsoft.EntityFrameworkCore.Database.Command"
+                    && logLevel == LogLevel.Information)
+            {
+                var logContent = formatter(state, exception);
+                //TODO: 拿到日志内容想怎么玩就怎么玩吧
+                Console.WriteLine();
+                Console.ForegroundColor = ConsoleColor.Green;
+                Console.WriteLine(logContent);
+                Console.ResetColor();
+            }
+        }
+        public IDisposable BeginScope<TState>(TState state) => null;
+    }
+}

+ 14 - 0
DBHelper/EntityValidationException.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DBHelper
+{
+    public class EntityValidationException : Exception
+    {
+        public List<ValidationResult> EntityValidationErrors = new List<ValidationResult>(); 
+    }
+}

BIN
DLL/DBHelper.dll


BIN
DLL/PLC.Siemens.dll


BIN
DLL/PLCConnecter.dll


BIN
DLL/WCS.Core.dll


BIN
DLL/WCS.Entity.dll


+ 286 - 0
DLL/net5.0/DBHelper.deps.json

@@ -0,0 +1,286 @@
+{
+  "runtimeTarget": {
+    "name": ".NETCoreApp,Version=v5.0",
+    "signature": ""
+  },
+  "compilationOptions": {},
+  "targets": {
+    ".NETCoreApp,Version=v5.0": {
+      "DBHelper/1.0.0": {
+        "dependencies": {
+          "Microsoft.EntityFrameworkCore": "5.0.5",
+          "Microsoft.EntityFrameworkCore.Relational": "5.0.5"
+        },
+        "runtime": {
+          "DBHelper.dll": {}
+        }
+      },
+      "Microsoft.EntityFrameworkCore/5.0.5": {
+        "dependencies": {
+          "Microsoft.EntityFrameworkCore.Abstractions": "5.0.5",
+          "Microsoft.EntityFrameworkCore.Analyzers": "5.0.5",
+          "Microsoft.Extensions.Caching.Memory": "5.0.0",
+          "Microsoft.Extensions.DependencyInjection": "5.0.1",
+          "Microsoft.Extensions.Logging": "5.0.0",
+          "System.Collections.Immutable": "5.0.0",
+          "System.ComponentModel.Annotations": "5.0.0",
+          "System.Diagnostics.DiagnosticSource": "5.0.1"
+        },
+        "runtime": {
+          "lib/netstandard2.1/Microsoft.EntityFrameworkCore.dll": {
+            "assemblyVersion": "5.0.5.0",
+            "fileVersion": "5.0.521.16102"
+          }
+        }
+      },
+      "Microsoft.EntityFrameworkCore.Abstractions/5.0.5": {
+        "runtime": {
+          "lib/netstandard2.1/Microsoft.EntityFrameworkCore.Abstractions.dll": {
+            "assemblyVersion": "5.0.5.0",
+            "fileVersion": "5.0.521.16102"
+          }
+        }
+      },
+      "Microsoft.EntityFrameworkCore.Analyzers/5.0.5": {},
+      "Microsoft.EntityFrameworkCore.Relational/5.0.5": {
+        "dependencies": {
+          "Microsoft.EntityFrameworkCore": "5.0.5",
+          "Microsoft.Extensions.Configuration.Abstractions": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.1/Microsoft.EntityFrameworkCore.Relational.dll": {
+            "assemblyVersion": "5.0.5.0",
+            "fileVersion": "5.0.521.16102"
+          }
+        }
+      },
+      "Microsoft.Extensions.Caching.Abstractions/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Caching.Memory/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.Caching.Abstractions": "5.0.0",
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Options": "5.0.0",
+          "Microsoft.Extensions.Primitives": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Configuration.Abstractions/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection/5.0.1": {
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0"
+        },
+        "runtime": {
+          "lib/net5.0/Microsoft.Extensions.DependencyInjection.dll": {
+            "assemblyVersion": "5.0.0.1",
+            "fileVersion": "5.0.120.57516"
+          }
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Logging/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection": "5.0.1",
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Options": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.1/Microsoft.Extensions.Logging.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions/5.0.0": {
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Options/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Primitives": "5.0.0"
+        },
+        "runtime": {
+          "lib/net5.0/Microsoft.Extensions.Options.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Primitives/5.0.0": {
+        "runtime": {
+          "lib/netcoreapp3.0/Microsoft.Extensions.Primitives.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "System.Collections.Immutable/5.0.0": {},
+      "System.ComponentModel.Annotations/5.0.0": {},
+      "System.Diagnostics.DiagnosticSource/5.0.1": {
+        "runtime": {
+          "lib/net5.0/System.Diagnostics.DiagnosticSource.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.220.61120"
+          }
+        }
+      }
+    }
+  },
+  "libraries": {
+    "DBHelper/1.0.0": {
+      "type": "project",
+      "serviceable": false,
+      "sha512": ""
+    },
+    "Microsoft.EntityFrameworkCore/5.0.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-lBIGKXhduT94Qats0ImMma9NWnsSLhz04JdD+Mi7DeCDPd3hiA668jREzCiLb9FqcmAIrLx8pxIo9cwjEHfYtQ==",
+      "path": "microsoft.entityframeworkcore/5.0.5",
+      "hashPath": "microsoft.entityframeworkcore.5.0.5.nupkg.sha512"
+    },
+    "Microsoft.EntityFrameworkCore.Abstractions/5.0.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-jS7+hySzNS9wCmREbWTVaa5EA+aLT7wb1Si1jhMgAiGrbLQSj6wPnS8WbhO5a7iAq45pDEK0KghtrPDFjfTxVw==",
+      "path": "microsoft.entityframeworkcore.abstractions/5.0.5",
+      "hashPath": "microsoft.entityframeworkcore.abstractions.5.0.5.nupkg.sha512"
+    },
+    "Microsoft.EntityFrameworkCore.Analyzers/5.0.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-czVyD5h0etXlrFHln0GPwlhmNm4yFe8vA/xfW/oN26OT8AAWkA6YPjr+z68/kmPqMWFo8UHxvDBZ9g2TiJY9qQ==",
+      "path": "microsoft.entityframeworkcore.analyzers/5.0.5",
+      "hashPath": "microsoft.entityframeworkcore.analyzers.5.0.5.nupkg.sha512"
+    },
+    "Microsoft.EntityFrameworkCore.Relational/5.0.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-D+9me6olfUKpti0WkMFsKk9T4uTPZ+3mg/NIwq1Fhp/qQAu9yg2FZTUa0O8ugGuiGyJHfi8tMvh+iCL0LTpG4g==",
+      "path": "microsoft.entityframeworkcore.relational/5.0.5",
+      "hashPath": "microsoft.entityframeworkcore.relational.5.0.5.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Caching.Abstractions/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-bu8As90/SBAouMZ6fJ+qRNo1X+KgHGrVueFhhYi+E5WqEhcnp2HoWRFnMzXQ6g4RdZbvPowFerSbKNH4Dtg5yg==",
+      "path": "microsoft.extensions.caching.abstractions/5.0.0",
+      "hashPath": "microsoft.extensions.caching.abstractions.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Caching.Memory/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-/1qPCleFOkJe0O+xmFqCNLFYQZTJz965sVw8CUB/BQgsApBwzAUsL2BUkDvQW+geRUVTXUS9zLa0pBjC2VJ1gA==",
+      "path": "microsoft.extensions.caching.memory/5.0.0",
+      "hashPath": "microsoft.extensions.caching.memory.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Configuration.Abstractions/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-ETjSBHMp3OAZ4HxGQYpwyGsD8Sw5FegQXphi0rpoGMT74S4+I2mm7XJEswwn59XAaKOzC15oDSOWEE8SzDCd6Q==",
+      "path": "microsoft.extensions.configuration.abstractions/5.0.0",
+      "hashPath": "microsoft.extensions.configuration.abstractions.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.DependencyInjection/5.0.1": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-//mDNrYeiJ0eh/awFhDFJQzkRVra/njU5Y4fyK7X29g5HScrzbUkKOKlyTtygthcGFt4zNC8G5CFCjb/oizomA==",
+      "path": "microsoft.extensions.dependencyinjection/5.0.1",
+      "hashPath": "microsoft.extensions.dependencyinjection.5.0.1.nupkg.sha512"
+    },
+    "Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
+      "path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
+      "hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Logging/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-MgOwK6tPzB6YNH21wssJcw/2MKwee8b2gI7SllYfn6rvTpIrVvVS5HAjSU2vqSku1fwqRvWP0MdIi14qjd93Aw==",
+      "path": "microsoft.extensions.logging/5.0.0",
+      "hashPath": "microsoft.extensions.logging.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Logging.Abstractions/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-NxP6ahFcBnnSfwNBi2KH2Oz8Xl5Sm2krjId/jRR3I7teFphwiUoUeZPwTNA21EX+5PtjqmyAvKaOeBXcJjcH/w==",
+      "path": "microsoft.extensions.logging.abstractions/5.0.0",
+      "hashPath": "microsoft.extensions.logging.abstractions.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Options/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-CBvR92TCJ5uBIdd9/HzDSrxYak+0W/3+yxrNg8Qm6Bmrkh5L+nu6m3WeazQehcZ5q1/6dDA7J5YdQjim0165zg==",
+      "path": "microsoft.extensions.options/5.0.0",
+      "hashPath": "microsoft.extensions.options.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Primitives/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-cI/VWn9G1fghXrNDagX9nYaaB/nokkZn0HYAawGaELQrl8InSezfe9OnfPZLcJq3esXxygh3hkq2c3qoV3SDyQ==",
+      "path": "microsoft.extensions.primitives/5.0.0",
+      "hashPath": "microsoft.extensions.primitives.5.0.0.nupkg.sha512"
+    },
+    "System.Collections.Immutable/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
+      "path": "system.collections.immutable/5.0.0",
+      "hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
+    },
+    "System.ComponentModel.Annotations/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==",
+      "path": "system.componentmodel.annotations/5.0.0",
+      "hashPath": "system.componentmodel.annotations.5.0.0.nupkg.sha512"
+    },
+    "System.Diagnostics.DiagnosticSource/5.0.1": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-uXQEYqav2V3zP6OwkOKtLv+qIi6z3m1hsGyKwXX7ZA7htT4shoVccGxnJ9kVRFPNAsi1ArZTq2oh7WOto6GbkQ==",
+      "path": "system.diagnostics.diagnosticsource/5.0.1",
+      "hashPath": "system.diagnostics.diagnosticsource.5.0.1.nupkg.sha512"
+    }
+  }
+}

BIN
DLL/net5.0/DBHelper.dll


+ 329 - 0
DLL/net5.0/WCS.Core.deps.json

@@ -0,0 +1,329 @@
+{
+  "runtimeTarget": {
+    "name": ".NETCoreApp,Version=v5.0",
+    "signature": ""
+  },
+  "compilationOptions": {},
+  "targets": {
+    ".NETCoreApp,Version=v5.0": {
+      "WCS.Core/1.0.0": {
+        "dependencies": {
+          "DBHelper": "1.0.0",
+          "FreeRedis": "0.3.5",
+          "WCS.Entity": "1.0.0"
+        },
+        "runtime": {
+          "WCS.Core.dll": {}
+        }
+      },
+      "FreeRedis/0.3.5": {
+        "runtime": {
+          "lib/net5.0/FreeRedis.dll": {
+            "assemblyVersion": "0.3.5.0",
+            "fileVersion": "0.3.5.0"
+          }
+        }
+      },
+      "Microsoft.EntityFrameworkCore/5.0.5": {
+        "dependencies": {
+          "Microsoft.EntityFrameworkCore.Abstractions": "5.0.5",
+          "Microsoft.EntityFrameworkCore.Analyzers": "5.0.5",
+          "Microsoft.Extensions.Caching.Memory": "5.0.0",
+          "Microsoft.Extensions.DependencyInjection": "5.0.1",
+          "Microsoft.Extensions.Logging": "5.0.0",
+          "System.Collections.Immutable": "5.0.0",
+          "System.ComponentModel.Annotations": "5.0.0",
+          "System.Diagnostics.DiagnosticSource": "5.0.1"
+        },
+        "runtime": {
+          "lib/netstandard2.1/Microsoft.EntityFrameworkCore.dll": {
+            "assemblyVersion": "5.0.5.0",
+            "fileVersion": "5.0.521.16102"
+          }
+        }
+      },
+      "Microsoft.EntityFrameworkCore.Abstractions/5.0.5": {
+        "runtime": {
+          "lib/netstandard2.1/Microsoft.EntityFrameworkCore.Abstractions.dll": {
+            "assemblyVersion": "5.0.5.0",
+            "fileVersion": "5.0.521.16102"
+          }
+        }
+      },
+      "Microsoft.EntityFrameworkCore.Analyzers/5.0.5": {},
+      "Microsoft.EntityFrameworkCore.Relational/5.0.5": {
+        "dependencies": {
+          "Microsoft.EntityFrameworkCore": "5.0.5",
+          "Microsoft.Extensions.Configuration.Abstractions": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.1/Microsoft.EntityFrameworkCore.Relational.dll": {
+            "assemblyVersion": "5.0.5.0",
+            "fileVersion": "5.0.521.16102"
+          }
+        }
+      },
+      "Microsoft.Extensions.Caching.Abstractions/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Caching.Memory/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.Caching.Abstractions": "5.0.0",
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Options": "5.0.0",
+          "Microsoft.Extensions.Primitives": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Configuration.Abstractions/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection/5.0.1": {
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0"
+        },
+        "runtime": {
+          "lib/net5.0/Microsoft.Extensions.DependencyInjection.dll": {
+            "assemblyVersion": "5.0.0.1",
+            "fileVersion": "5.0.120.57516"
+          }
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Logging/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection": "5.0.1",
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Logging.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Options": "5.0.0"
+        },
+        "runtime": {
+          "lib/netstandard2.1/Microsoft.Extensions.Logging.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions/5.0.0": {
+        "runtime": {
+          "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Options/5.0.0": {
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "5.0.0",
+          "Microsoft.Extensions.Primitives": "5.0.0"
+        },
+        "runtime": {
+          "lib/net5.0/Microsoft.Extensions.Options.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "Microsoft.Extensions.Primitives/5.0.0": {
+        "runtime": {
+          "lib/netcoreapp3.0/Microsoft.Extensions.Primitives.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.20.51904"
+          }
+        }
+      },
+      "System.Collections.Immutable/5.0.0": {},
+      "System.ComponentModel.Annotations/5.0.0": {},
+      "System.Diagnostics.DiagnosticSource/5.0.1": {
+        "runtime": {
+          "lib/net5.0/System.Diagnostics.DiagnosticSource.dll": {
+            "assemblyVersion": "5.0.0.0",
+            "fileVersion": "5.0.220.61120"
+          }
+        }
+      },
+      "DBHelper/1.0.0": {
+        "dependencies": {
+          "Microsoft.EntityFrameworkCore": "5.0.5",
+          "Microsoft.EntityFrameworkCore.Relational": "5.0.5"
+        },
+        "runtime": {
+          "DBHelper.dll": {}
+        }
+      },
+      "WCS.Entity/1.0.0": {
+        "dependencies": {
+          "System.ComponentModel.Annotations": "5.0.0"
+        },
+        "runtime": {
+          "WCS.Entity.dll": {}
+        }
+      }
+    }
+  },
+  "libraries": {
+    "WCS.Core/1.0.0": {
+      "type": "project",
+      "serviceable": false,
+      "sha512": ""
+    },
+    "FreeRedis/0.3.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-A7duHefVZrsyVyPtm39KMmZ5QbJj0l/m6Z8EyFmPld1+fAvVlSpfbDYHOTnMBaXrY9qYGO8VfsRlZEv7cK3QKw==",
+      "path": "freeredis/0.3.5",
+      "hashPath": "freeredis.0.3.5.nupkg.sha512"
+    },
+    "Microsoft.EntityFrameworkCore/5.0.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-lBIGKXhduT94Qats0ImMma9NWnsSLhz04JdD+Mi7DeCDPd3hiA668jREzCiLb9FqcmAIrLx8pxIo9cwjEHfYtQ==",
+      "path": "microsoft.entityframeworkcore/5.0.5",
+      "hashPath": "microsoft.entityframeworkcore.5.0.5.nupkg.sha512"
+    },
+    "Microsoft.EntityFrameworkCore.Abstractions/5.0.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-jS7+hySzNS9wCmREbWTVaa5EA+aLT7wb1Si1jhMgAiGrbLQSj6wPnS8WbhO5a7iAq45pDEK0KghtrPDFjfTxVw==",
+      "path": "microsoft.entityframeworkcore.abstractions/5.0.5",
+      "hashPath": "microsoft.entityframeworkcore.abstractions.5.0.5.nupkg.sha512"
+    },
+    "Microsoft.EntityFrameworkCore.Analyzers/5.0.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-czVyD5h0etXlrFHln0GPwlhmNm4yFe8vA/xfW/oN26OT8AAWkA6YPjr+z68/kmPqMWFo8UHxvDBZ9g2TiJY9qQ==",
+      "path": "microsoft.entityframeworkcore.analyzers/5.0.5",
+      "hashPath": "microsoft.entityframeworkcore.analyzers.5.0.5.nupkg.sha512"
+    },
+    "Microsoft.EntityFrameworkCore.Relational/5.0.5": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-D+9me6olfUKpti0WkMFsKk9T4uTPZ+3mg/NIwq1Fhp/qQAu9yg2FZTUa0O8ugGuiGyJHfi8tMvh+iCL0LTpG4g==",
+      "path": "microsoft.entityframeworkcore.relational/5.0.5",
+      "hashPath": "microsoft.entityframeworkcore.relational.5.0.5.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Caching.Abstractions/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-bu8As90/SBAouMZ6fJ+qRNo1X+KgHGrVueFhhYi+E5WqEhcnp2HoWRFnMzXQ6g4RdZbvPowFerSbKNH4Dtg5yg==",
+      "path": "microsoft.extensions.caching.abstractions/5.0.0",
+      "hashPath": "microsoft.extensions.caching.abstractions.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Caching.Memory/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-/1qPCleFOkJe0O+xmFqCNLFYQZTJz965sVw8CUB/BQgsApBwzAUsL2BUkDvQW+geRUVTXUS9zLa0pBjC2VJ1gA==",
+      "path": "microsoft.extensions.caching.memory/5.0.0",
+      "hashPath": "microsoft.extensions.caching.memory.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Configuration.Abstractions/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-ETjSBHMp3OAZ4HxGQYpwyGsD8Sw5FegQXphi0rpoGMT74S4+I2mm7XJEswwn59XAaKOzC15oDSOWEE8SzDCd6Q==",
+      "path": "microsoft.extensions.configuration.abstractions/5.0.0",
+      "hashPath": "microsoft.extensions.configuration.abstractions.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.DependencyInjection/5.0.1": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-//mDNrYeiJ0eh/awFhDFJQzkRVra/njU5Y4fyK7X29g5HScrzbUkKOKlyTtygthcGFt4zNC8G5CFCjb/oizomA==",
+      "path": "microsoft.extensions.dependencyinjection/5.0.1",
+      "hashPath": "microsoft.extensions.dependencyinjection.5.0.1.nupkg.sha512"
+    },
+    "Microsoft.Extensions.DependencyInjection.Abstractions/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-ORj7Zh81gC69TyvmcUm9tSzytcy8AVousi+IVRAI8nLieQjOFryRusSFh7+aLk16FN9pQNqJAiMd7BTKINK0kA==",
+      "path": "microsoft.extensions.dependencyinjection.abstractions/5.0.0",
+      "hashPath": "microsoft.extensions.dependencyinjection.abstractions.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Logging/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-MgOwK6tPzB6YNH21wssJcw/2MKwee8b2gI7SllYfn6rvTpIrVvVS5HAjSU2vqSku1fwqRvWP0MdIi14qjd93Aw==",
+      "path": "microsoft.extensions.logging/5.0.0",
+      "hashPath": "microsoft.extensions.logging.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Logging.Abstractions/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-NxP6ahFcBnnSfwNBi2KH2Oz8Xl5Sm2krjId/jRR3I7teFphwiUoUeZPwTNA21EX+5PtjqmyAvKaOeBXcJjcH/w==",
+      "path": "microsoft.extensions.logging.abstractions/5.0.0",
+      "hashPath": "microsoft.extensions.logging.abstractions.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Options/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-CBvR92TCJ5uBIdd9/HzDSrxYak+0W/3+yxrNg8Qm6Bmrkh5L+nu6m3WeazQehcZ5q1/6dDA7J5YdQjim0165zg==",
+      "path": "microsoft.extensions.options/5.0.0",
+      "hashPath": "microsoft.extensions.options.5.0.0.nupkg.sha512"
+    },
+    "Microsoft.Extensions.Primitives/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-cI/VWn9G1fghXrNDagX9nYaaB/nokkZn0HYAawGaELQrl8InSezfe9OnfPZLcJq3esXxygh3hkq2c3qoV3SDyQ==",
+      "path": "microsoft.extensions.primitives/5.0.0",
+      "hashPath": "microsoft.extensions.primitives.5.0.0.nupkg.sha512"
+    },
+    "System.Collections.Immutable/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==",
+      "path": "system.collections.immutable/5.0.0",
+      "hashPath": "system.collections.immutable.5.0.0.nupkg.sha512"
+    },
+    "System.ComponentModel.Annotations/5.0.0": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==",
+      "path": "system.componentmodel.annotations/5.0.0",
+      "hashPath": "system.componentmodel.annotations.5.0.0.nupkg.sha512"
+    },
+    "System.Diagnostics.DiagnosticSource/5.0.1": {
+      "type": "package",
+      "serviceable": true,
+      "sha512": "sha512-uXQEYqav2V3zP6OwkOKtLv+qIi6z3m1hsGyKwXX7ZA7htT4shoVccGxnJ9kVRFPNAsi1ArZTq2oh7WOto6GbkQ==",
+      "path": "system.diagnostics.diagnosticsource/5.0.1",
+      "hashPath": "system.diagnostics.diagnosticsource.5.0.1.nupkg.sha512"
+    },
+    "DBHelper/1.0.0": {
+      "type": "project",
+      "serviceable": false,
+      "sha512": ""
+    },
+    "WCS.Entity/1.0.0": {
+      "type": "project",
+      "serviceable": false,
+      "sha512": ""
+    }
+  }
+}

BIN
DLL/net5.0/WCS.Core.dll


+ 23 - 0
DLL/net5.0/WCS.Entity.deps.json

@@ -0,0 +1,23 @@
+{
+  "runtimeTarget": {
+    "name": ".NETCoreApp,Version=v5.0",
+    "signature": ""
+  },
+  "compilationOptions": {},
+  "targets": {
+    ".NETCoreApp,Version=v5.0": {
+      "WCS.Entity/1.0.0": {
+        "runtime": {
+          "WCS.Entity.dll": {}
+        }
+      }
+    }
+  },
+  "libraries": {
+    "WCS.Entity/1.0.0": {
+      "type": "project",
+      "serviceable": false,
+      "sha512": ""
+    }
+  }
+}

BIN
DLL/net5.0/WCS.Entity.dll


BIN
DLL/net5.0/ref/DBHelper.dll


BIN
DLL/net5.0/ref/WCS.Core.dll


BIN
DLL/net5.0/ref/WCS.Entity.dll


+ 250 - 0
PLC.Siemens/Communication/IsoSocket.cs

@@ -0,0 +1,250 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using PLC.Siemens.Protocol.Iso;
+using ByteBuffer = Core.Communication.Transport.ByteBuffer;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Communication
+{
+    /// <summary>
+    /// 采用同步通信机制
+    /// </summary>
+    public class IsoSocket : IIsoSender
+    { 
+
+        private Socket _socket;
+        public bool Connected
+        {
+            get
+            {
+                try
+                {
+                    if (_socket == null)
+                        return false;
+                    //return _socket.Connected;
+                    //return _socket.Connected && (!(_socket.Available == 0) || !_socket.Poll(1000, SelectMode.SelectRead));
+                    return !(!_socket.Connected || (_socket.Poll(1000, SelectMode.SelectRead) && (_socket.Available == 0)));
+                }
+                catch
+                {
+                    return false;
+                }
+            }
+        }
+
+
+        private Action<MessageEvent> _messageAction;
+
+        public void RegisterMessage(Action<MessageEvent> messageAction)
+        {
+            if (messageAction == null) throw new ArgumentNullException("messageAction");
+            _messageAction = messageAction;
+        }
+
+        private void OnMessage(string methode, string message)
+        {
+            if (_messageAction != null)
+                _messageAction.Invoke(new MessageEvent(methode, message));
+        }
+
+        private int _connecting;//连接中
+
+        bool conneted = false;
+        public bool Connect(string ip, int port)
+        {
+            try
+            {
+                conneted = false;
+                if (Interlocked.CompareExchange(ref _connecting, 1, 0) != 0)
+                {
+                    OnMessage("Connect", "连接正在处理中");
+                    return false;
+                }
+
+                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+                _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);
+                _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 1000);
+                IPEndPoint server = new IPEndPoint(IPAddress.Parse(ip), 102);
+                //_socket.Connect(server);
+                //return true;
+                timeoutObject.Reset();
+                _socket.BeginConnect(server, new AsyncCallback(Callback), _socket);
+                if (!timeoutObject.WaitOne(1000))
+                { 
+                    _socket.Close();
+                    conneted = false;
+                }
+                return conneted;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("Connect", "连接异常:{ex.Message}");
+                return false;
+            }
+            finally
+            {
+                Interlocked.CompareExchange(ref _connecting, 0, 1);
+            }
+        }
+        ManualResetEvent timeoutObject = new ManualResetEvent(false);
+        void Callback(IAsyncResult result)
+        {
+            try
+            {
+                var client = result.AsyncState as Socket;
+                if (client != null)
+                {
+                    client.EndConnect(result);
+                    conneted = true;
+                }
+            }
+            catch(Exception ex)
+            {
+                conneted = false;
+            }
+            finally
+            {
+                timeoutObject.Set();
+            }
+        }
+
+        private bool IsActive()
+        {
+            if (!Connected)
+            {
+                OnMessage("SendRecive", "未连接到PLC设备");
+                return false;
+            }
+            if (Interlocked.CompareExchange(ref _sending, 1, 0) != 0)
+            {
+                OnMessage("SendRecive", "正在处理中");
+                return false;
+            }
+
+            return true;
+        }
+
+        private int _sending;//发送中
+        public byte[] SendRecive(byte[] sendBytes, int length)
+        {
+            try
+            {
+                if (!IsActive())
+                    return null;
+
+                var sendStr = BitConverter.ToString(sendBytes, 0, length);
+                //Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss ffff")}[Send]({length}):{sendStr}");
+
+                _socket.Send(sendBytes, length, SocketFlags.None);
+
+                byte[] bReceive = new byte[1024 * 8];
+                var resLength = _socket.Receive(bReceive, SocketFlags.None);
+
+                var recvStr = BitConverter.ToString(bReceive, 0, resLength);
+                //Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss ffff")}[Recv]({resLength}):{recvStr}");
+
+                var receiveBytes = new byte[resLength];
+                Array.Copy(bReceive, 0, receiveBytes, 0, resLength);
+
+                return receiveBytes;
+            }
+            catch (System.Net.Sockets.SocketException ex)
+            {
+                _socket.Close();
+                OnMessage("Send", "连接断开:" + ex.Message);
+                return null;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("Send", "发送异常:" + ex.Message);
+                return null;
+            }
+            finally
+            {
+                Interlocked.CompareExchange(ref _sending, 0, 1);
+            }
+        }
+        
+        private TResponse GetResponse<TResponse>(byte[] resBytes, bool isContainHeader = true)
+            where TResponse : IBuildResponse, new()
+        {
+            ByteBuffer buffer= ByteBuffer.Allocate();
+            buffer.Push(resBytes);
+
+            if(isContainHeader)
+            {
+                IsoDataPdu pdu = new IsoDataPdu();
+                pdu.Build(buffer);
+
+                if (resBytes.Length != pdu.Length)
+                    return default(TResponse);
+            }
+
+            TResponse response = new TResponse();
+            response.Build(buffer);
+
+            return response;
+        }
+
+        private ByteBuffer GetByteBuffer<TRequest>(TRequest request, bool isContainHeader=true)
+            where TRequest : IBuildRequest, new()
+        {
+            if(request==null) throw  new ArgumentNullException("request");
+
+            request.Build();
+            var bufferData = request.GetBuffer();
+            ByteBuffer buffer = ByteBuffer.Allocate();
+
+            if(isContainHeader)
+            {
+                //构建ISO头部
+                IsoDataPdu pdu = new IsoDataPdu { Length = (ushort)bufferData.WriteIndex };
+                pdu.Build();
+                pdu.GetBuffer(buffer);
+            }
+
+            buffer.Push(bufferData.Buffer, bufferData.WriteIndex);
+
+            return buffer;
+        }
+
+        /// <summary>
+        /// 不含ISO头的请求
+        /// </summary>
+        /// <typeparam name="TRequest"></typeparam>
+        /// <typeparam name="TResponse"></typeparam>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        public TResponse Send<TRequest, TResponse>(TRequest request) where TRequest : IBuildRequest, new() where TResponse : IBuildResponse, new()
+        {
+            var buffer = GetByteBuffer(request, false);
+            var resByte = SendRecive(buffer.Buffer, buffer.WriteIndex);
+
+            if (resByte == null || resByte.Length == 0) return default(TResponse);
+
+
+            return GetResponse<TResponse>(resByte, false);
+        }
+
+        /// <summary>
+        /// 包含ISO头的请求
+        /// </summary>
+        /// <typeparam name="TRequest"></typeparam>
+        /// <typeparam name="TResponse"></typeparam>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        public TResponse IsoSend<TRequest, TResponse>(TRequest request) where TRequest : IBuildRequest, new() where TResponse : IBuildResponse, new()
+        {
+            var buffer = GetByteBuffer(request);
+ 
+            var resByte = SendRecive(buffer.Buffer, buffer.WriteIndex);
+         
+            if (resByte == null)
+                return default(TResponse);
+
+            return GetResponse<TResponse>(resByte);
+        }
+    }
+}

+ 339 - 0
PLC.Siemens/Core/Common/Argument.cs

@@ -0,0 +1,339 @@
+using System;
+using System.Collections.Generic;
+using Core.Util.Extension;
+
+namespace Core.Util.Common
+{
+    /// <summary>
+    /// 参数检查
+    /// </summary>
+    public static class Argument
+    {
+        
+        /// <summary>
+        /// 非法Guid
+        /// </summary>
+        /// <param name="argument">Guid变量</param>
+        /// <param name="argumentName">Guid变量名</param>
+        /// <exception cref="ArgumentException">非法参数</exception>
+        /// <example>Argument.IsNotEmptyGuid(argument,argumentName)</example>
+        public static void IsNotEmptyGuid(Guid argument, string argumentName)
+        {
+            if (argument.IsEmpty())
+            {
+                throw new ArgumentException("\"{0}\" cannot be empty guid.".FormatWith(argumentName), argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 非法string
+        /// </summary>
+        /// <param name="argument">Guid变量</param>
+        /// <param name="argumentName">Guid变量名</param>
+        /// <exception cref="ArgumentException">非法参数</exception>
+        /// <example>Argument.IsNotEmpty(argument,argumentName)</example>
+        public static void IsNotEmpty(string argument, string argumentName)
+        {
+            if (argument.IsNullOrEmpty())
+            {
+                throw new ArgumentException("\"{0}\" cannot be blank.".FormatWith(argumentName), argumentName);
+            }
+        }
+
+        /// <summary>
+        /// string超出长度
+        /// </summary>
+        /// <param name="argument">Guid变量</param>
+        /// <param name="length">最大长度</param>
+        /// <param name="argumentName">Guid变量名</param>
+        /// <exception cref="ArgumentException">非法参数</exception>
+        /// <example>Argument.IsNotOutOfLength(argument,length,argumentName)</example>
+        public static void IsNotOutOfLength(string argument, int length, string argumentName)
+        {
+            IsNotEmpty(argument, argumentName);
+            if (argument.Trim().Length > length)
+            {
+                throw new ArgumentOutOfRangeException(
+                    "\"{0}\" cannot be more than {1} character.".FormatWith(argumentName, length), argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查对象不为空
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentNullException">非法参数</exception>
+        /// <example>Argument.IsNotNull(argument,argumentName)</example>
+        public static void IsNotNull(object argument, string argumentName)
+        {
+            if (argument == null)
+            {
+                throw new ArgumentNullException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查int对象不为负数
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegative(argument,argumentName)</example>
+        public static void IsNotNegative(int argument, string argumentName)
+        {
+            if (argument < 0)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查int对象大于0
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegativeOrZero(argument,argumentName)</example>
+        public static void IsNotNegativeOrZero(int argument, string argumentName)
+        {
+            if (argument <= 0)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查long对象不为负数
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegative(argument,argumentName)</example>
+        public static void IsNotNegative(long argument, string argumentName)
+        {
+            if (argument < 0)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查long对象大于0
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegativeOrZero(argument,argumentName)</example>
+        public static void IsNotNegativeOrZero(long argument, string argumentName)
+        {
+            if (argument <= 0)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查float对象不为负数
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegative(argument,argumentName)</example>
+        public static void IsNotNegative(float argument, string argumentName)
+        {
+            if (argument < 0)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查float对象大于0
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegativeOrZero(argument,argumentName)</example>
+        public static void IsNotNegativeOrZero(float argument, string argumentName)
+        {
+            if (argument <= 0)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查decimal对象不为负数
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegative(argument,argumentName)</example>
+        public static void IsNotNegative(decimal argument, string argumentName)
+        {
+            if (argument < 0)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查decimal对象大于0
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegativeOrZero(argument,argumentName)</example>
+        public static void IsNotNegativeOrZero(decimal argument, string argumentName)
+        {
+            if (argument <= 0)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查时间是否有效
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotInvalidDate(argument,argumentName)</example>
+        public static void IsNotInvalidDate(DateTime argument, string argumentName)
+        {
+            if (!argument.IsValid())
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查时间过期
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotInPast(argument,argumentName)</example>
+        public static void IsNotInPast(DateTime argument, string argumentName)
+        {
+            if (argument < SystemTime.Now())
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查时间超期
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotInFuture(argument,argumentName)</example>
+        public static void IsNotInFuture(DateTime argument, string argumentName)
+        {
+            if (argument > SystemTime.Now())
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查TimeSpan是否有效
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegative(argument,argumentName)</example>
+        public static void IsNotNegative(TimeSpan argument, string argumentName)
+        {
+            if (argument < TimeSpan.Zero)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查TimeSpan是否有效
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException">非法参数</exception>
+        /// <example>Argument.IsNotNegativeOrZero(argument,argumentName)</example>
+        public static void IsNotNegativeOrZero(TimeSpan argument, string argumentName)
+        {
+            if (argument <= TimeSpan.Zero)
+            {
+                throw new ArgumentOutOfRangeException(argumentName);
+            }
+        }
+        /// <summary>
+        /// 检查ICollection不为空
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentException">非法参数</exception>
+        /// <example>Argument.IsNotEmpty(argument,argumentName)</example>
+        public static void IsNotEmpty<T>(ICollection<T> argument, string argumentName)
+        {
+            IsNotNull(argument, argumentName);
+
+            if (argument.Count == 0)
+            {
+                throw new ArgumentException("Collection cannot be empty.", argumentName);
+            }
+        }
+
+        /// <summary>
+        /// 检查int值是否超出范围
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="min"></param>
+        /// <param name="max"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentOutOfRangeException"></exception>
+        /// <example>Argument.IsNotOutOfRange(argument,min,max,argumentName)</example>
+        public static void IsNotOutOfRange(int argument, int min, int max, string argumentName)
+        {
+            if ((argument < min) || (argument > max))
+            {
+                throw new ArgumentOutOfRangeException(argumentName,
+                    "{0} must be between \"{1}\"-\"{2}\".".FormatWith(argumentName, min, max));
+            }
+        }
+
+        /// <summary>
+        /// 非法邮箱地址
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentException"></exception>
+        /// <example>Argument.IsNotOutOfRange(argument ,argumentName)</example>
+        public static void IsNotInvalidEmail(string argument, string argumentName)
+        {
+            IsNotEmpty(argument, argumentName);
+
+            if (!argument.IsEmail())
+            {
+                throw new ArgumentException("\"{0}\" is not a valid email address.".FormatWith(argumentName),
+                    argumentName);
+            }
+        }
+        /// <summary>
+        /// 非法Web地址
+        /// </summary>
+        /// <param name="argument"></param>
+        /// <param name="argumentName"></param>
+        /// <exception cref="ArgumentException"></exception>
+        /// <example>Argument.IsNotOutOfRange(argument ,argumentName)</example>
+        public static void IsNotInvalidWebUrl(string argument, string argumentName)
+        {
+            IsNotEmpty(argument, argumentName);
+
+            if (!argument.IsWebUrl())
+            {
+                throw new ArgumentException("\"{0}\" is not a valid web url.".FormatWith(argumentName), argumentName);
+            }
+        }
+    }
+}

+ 20 - 0
PLC.Siemens/Core/Common/Constants.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+
+namespace Core.Util.Common
+{
+    //获取表示当前线程使用的区域性的 System.Globalization.CultureInfo。
+    public static class Constants
+    {
+        
+        public static readonly DateTime ProductionDate = new DateTime(2008, 1, 11);
+        //获取表示当前线程使用的区域性的 System.Globalization.CultureInfo。
+        public static CultureInfo CurrentCulture
+        {
+            get
+            {
+                return CultureInfo.CurrentCulture;
+            }
+        }
+    }
+}

+ 262 - 0
PLC.Siemens/Core/Common/DefaultConvert.cs

@@ -0,0 +1,262 @@
+using System;
+using System.Globalization;
+using System.Text;
+
+namespace Core.Util.Common
+{
+    /// <summary>
+    /// 一些转换功能
+    /// </summary>
+    public class DefaultConvert
+    {
+        /// <summary>
+        /// obj对象转int
+        /// </summary>
+        /// <param name="value">转换对象</param>
+        /// <param name="defaultValue">缺省值</param>
+        /// <returns></returns>
+        public virtual int ToInt(object value, int defaultValue)
+        {
+            if (value == null) return defaultValue;
+
+            var s = value as string;
+            if (s != null)
+            {
+                var str = s;
+                str = ToDbc(str).Trim();
+                if (string.IsNullOrEmpty(str)) return defaultValue;
+
+                int n;
+                if (int.TryParse(str, out n)) return n;
+
+                return defaultValue;
+            }
+            var bytes = value as byte[];
+            if (bytes != null)
+            {
+                var buf = bytes;
+                if (buf.Length < 1) return defaultValue;
+
+                switch (buf.Length)
+                {
+                    case 1:
+                        return buf[0];
+                    case 2:
+                        return BitConverter.ToInt16(buf, 0);
+                    case 3:
+                        return BitConverter.ToInt32(new byte[] {buf[0], buf[1], buf[2], 0}, 0);
+                    case 4:
+                        return BitConverter.ToInt32(buf, 0);
+                }
+            }
+
+            try
+            {
+                return Convert.ToInt32(value);
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+
+        /// <summary>
+        /// obj对象转double
+        /// </summary>
+        /// <param name="value">转换对象</param>
+        /// <param name="defaultValue">缺省值</param>
+        /// <returns></returns>
+        public virtual double ToDouble(object value, double defaultValue)
+        {
+            if (value == null) return defaultValue;
+
+            // 特殊处理字符串,也是最常见的
+            var s = value as string;
+            if (s != null)
+            {
+                var str = s;
+                str = ToDbc(str).Trim();
+                if (string.IsNullOrEmpty(str)) return defaultValue;
+
+                double n;
+                if (double.TryParse(str, out n)) return n;
+                return defaultValue;
+            }
+            var bytes = value as byte[];
+            if (bytes != null)
+            {
+                var buf = bytes;
+                if (buf.Length < 1) return defaultValue;
+
+                switch (buf.Length)
+                {
+                    case 1:
+                        return buf[0];
+                    case 2:
+                        return BitConverter.ToInt16(buf, 0);
+                    case 3:
+                        return BitConverter.ToInt32(new byte[] {buf[0], buf[1], buf[2], 0}, 0);
+                    case 4:
+                        return BitConverter.ToInt32(buf, 0);
+                    default:
+                        // 凑够8字节
+                        if (buf.Length < 8)
+                        {
+                            var bts = new byte[8];
+                            Buffer.BlockCopy(buf, 0, bts, 0, buf.Length);
+                            buf = bts;
+                        }
+                        return BitConverter.ToDouble(buf, 0);
+                }
+            }
+
+            try
+            {
+                return Convert.ToDouble(value);
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+
+        /// <summary>
+        /// obj对象转bool
+        /// </summary>
+        /// <param name="value">转换对象</param>
+        /// <param name="defaultValue">缺省值</param>
+        /// <returns></returns>
+        public virtual bool ToBoolean(object value, bool defaultValue)
+        {
+            if (value == null) return defaultValue;
+
+            // 特殊处理字符串,也是最常见的
+            var s = value as string;
+            if (s != null)
+            {
+                var str = s;
+                str = ToDbc(str).Trim();
+                if (string.IsNullOrEmpty(str)) return defaultValue;
+
+                bool b;
+                if (bool.TryParse(str, out b)) return b;
+
+                if (string.Equals(str, bool.TrueString, StringComparison.OrdinalIgnoreCase)) return true;
+                if (string.Equals(str, bool.FalseString, StringComparison.OrdinalIgnoreCase)) return false;
+
+                // 特殊处理用数字0和1表示布尔型
+                int n;
+                if (int.TryParse(str, out n)) return n > 0;
+
+                return defaultValue;
+            }
+
+            try
+            {
+                return Convert.ToBoolean(value);
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+
+        /// <summary>
+        /// obj对象转DateTime
+        /// </summary>
+        /// <param name="value">转换对象</param>
+        /// <param name="defaultValue">缺省值</param>
+        /// <returns></returns>
+        public virtual DateTime ToDateTime(object value, DateTime defaultValue)
+        {
+            if (value == null) return defaultValue;
+
+            // 特殊处理字符串,也是最常见的
+            var s = value as string;
+            if (s != null)
+            {
+                var str = s;
+                str = ToDbc(str).Trim();
+                if (string.IsNullOrEmpty(str)) return defaultValue;
+
+                DateTime n;
+                if (DateTime.TryParse(str, out n)) return n;
+                if (str.Contains("-") && DateTime.TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out n))
+                    return n;
+                if (str.Contains("/") && DateTime.TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out n))
+                    return n;
+                if (DateTime.TryParse(str, out n)) return n;
+                return defaultValue;
+            }
+
+            try
+            {
+                return Convert.ToDateTime(value);
+            }
+            catch
+            {
+                return defaultValue;
+            }
+        }
+
+        private static string ToDbc(string str)
+        {
+            var ch = str.ToCharArray();
+            for (var i = 0; i < ch.Length; i++)
+            {
+                // 全角空格
+                if (ch[i] == 0x3000)
+                    ch[i] = (char) 0x20;
+                else if (ch[i] > 0xFF00 && ch[i] < 0xFF5F)
+                    ch[i] = (char) (ch[i] - 0xFEE0);
+            }
+            return new string(ch);
+        }
+
+        /// <summary>
+        /// obj对象转DateTime
+        /// </summary>
+        /// <param name="value">转换对象</param>
+        /// <param name="emptyValue">缺省值</param>
+        /// <returns></returns>
+        public virtual string ToFullString(DateTime value, string emptyValue = null)
+        {
+            if (emptyValue != null && value <= DateTime.MinValue) return emptyValue;
+
+            //return value.ToString("yyyy-MM-dd HH:mm:ss");
+
+            var dt = value;
+            var sb = new StringBuilder();
+            sb.Append(dt.Year.ToString().PadLeft(4, '0'));
+            sb.Append("-");
+            sb.Append(dt.Month.ToString().PadLeft(2, '0'));
+            sb.Append("-");
+            sb.Append(dt.Day.ToString().PadLeft(2, '0'));
+            sb.Append(" ");
+
+            sb.Append(dt.Hour.ToString().PadLeft(2, '0'));
+            sb.Append(":");
+            sb.Append(dt.Minute.ToString().PadLeft(2, '0'));
+            sb.Append(":");
+            sb.Append(dt.Second.ToString().PadLeft(2, '0'));
+
+            return sb.ToString();
+        }
+
+        /// <summary>
+        /// obj对象转DateTime
+        /// </summary>
+        /// <param name="value">转换对象</param>
+        /// <param name="format">转换格式</param>
+        /// <param name="emptyValue">缺省值</param>
+        /// <returns></returns>
+        public virtual string ToString(DateTime value, string format, string emptyValue)
+        {
+            if (emptyValue != null && value <= DateTime.MinValue) return emptyValue;
+
+            if (format == null || format == "yyyy-MM-dd HH:mm:ss") return ToFullString(value, emptyValue);
+
+            return value.ToString(format);
+        }
+    }
+}

+ 176 - 0
PLC.Siemens/Core/Common/DelegateFactory.cs

@@ -0,0 +1,176 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace Core.Util.Common
+{
+    /// <summary>A dynamic delegate factory.
+    /// </summary>
+    public class DelegateFactory
+    {
+        /// <summary>Creates a delegate from the given methodInfo and parameterTypes.
+        /// </summary>
+        /// <typeparam name="D"></typeparam>
+        /// <param name="methodInfo"></param>
+        /// <param name="parameterTypes"></param>
+        /// <returns></returns>
+        public static D CreateDelegate<D>(MethodInfo methodInfo, Type[] parameterTypes) where D : class
+        {
+            if (methodInfo == null)
+            {
+                throw new ArgumentNullException("methodInfo");
+            }
+            if (parameterTypes == null)
+            {
+                throw new ArgumentNullException("parameterTypes");
+            }
+            var parameters = methodInfo.GetParameters();
+            var dynamicMethod = new DynamicMethod(
+                methodInfo.Name,
+                MethodAttributes.Static | MethodAttributes.Public,
+                CallingConventions.Standard,
+                methodInfo.ReturnType,
+                parameterTypes,
+                typeof(object),
+                true)
+            {
+                InitLocals = false
+            };
+            var dynamicEmit = new DynamicEmit(dynamicMethod);
+            if (!methodInfo.IsStatic)
+            {
+                dynamicEmit.LoadArgument(0);
+                dynamicEmit.CastTo(typeof(object), methodInfo.DeclaringType);
+            }
+            for (int index = 0; index < parameters.Length; index++)
+            {
+                dynamicEmit.LoadArgument(index + 1);
+                dynamicEmit.CastTo(parameterTypes[index + 1], parameters[index].ParameterType);
+            }
+            dynamicEmit.Call(methodInfo);
+            dynamicEmit.Return();
+
+            return dynamicMethod.CreateDelegate(typeof(D)) as D;
+        }
+
+        class DynamicEmit
+        {
+            private ILGenerator _ilGenerator;
+            private static readonly Dictionary<Type, OpCode> _converts = new Dictionary<Type, OpCode>();
+
+            static DynamicEmit()
+            {
+                _converts.Add(typeof(sbyte), OpCodes.Conv_I1);
+                _converts.Add(typeof(short), OpCodes.Conv_I2);
+                _converts.Add(typeof(int), OpCodes.Conv_I4);
+                _converts.Add(typeof(long), OpCodes.Conv_I8);
+                _converts.Add(typeof(byte), OpCodes.Conv_U1);
+                _converts.Add(typeof(ushort), OpCodes.Conv_U2);
+                _converts.Add(typeof(uint), OpCodes.Conv_U4);
+                _converts.Add(typeof(ulong), OpCodes.Conv_U8);
+                _converts.Add(typeof(float), OpCodes.Conv_R4);
+                _converts.Add(typeof(double), OpCodes.Conv_R8);
+                _converts.Add(typeof(bool), OpCodes.Conv_I1);
+                _converts.Add(typeof(char), OpCodes.Conv_U2);
+            }
+            public DynamicEmit(DynamicMethod dynamicMethod)
+            {
+                this._ilGenerator = dynamicMethod.GetILGenerator();
+            }
+            public DynamicEmit(ILGenerator ilGen)
+            {
+                this._ilGenerator = ilGen;
+            }
+
+            public void LoadArgument(int argumentIndex)
+            {
+                switch (argumentIndex)
+                {
+                    case 0:
+                        this._ilGenerator.Emit(OpCodes.Ldarg_0);
+                        break;
+                    case 1:
+                        this._ilGenerator.Emit(OpCodes.Ldarg_1);
+                        break;
+                    case 2:
+                        this._ilGenerator.Emit(OpCodes.Ldarg_2);
+                        break;
+                    case 3:
+                        this._ilGenerator.Emit(OpCodes.Ldarg_3);
+                        break;
+                    default:
+                        if (argumentIndex < 0x100)
+                        {
+                            this._ilGenerator.Emit(OpCodes.Ldarg_S, (byte)argumentIndex);
+                        }
+                        else
+                        {
+                            this._ilGenerator.Emit(OpCodes.Ldarg, argumentIndex);
+                        }
+                        break;
+                }
+            }
+            public void CastTo(Type fromType, Type toType)
+            {
+                if (fromType != toType)
+                {
+                    if (toType == typeof(void))
+                    {
+                        if (!(fromType == typeof(void)))
+                        {
+                            this.Pop();
+                        }
+                    }
+                    else
+                    {
+                        if (fromType.IsValueType)
+                        {
+                            if (toType.IsValueType)
+                            {
+                                this.Convert(toType);
+                                return;
+                            }
+                            this._ilGenerator.Emit(OpCodes.Box, fromType);
+                        }
+                        this.CastTo(toType);
+                    }
+                }
+            }
+            public void CastTo(Type toType)
+            {
+                if (toType.IsValueType)
+                {
+                    this._ilGenerator.Emit(OpCodes.Unbox_Any, toType);
+                }
+                else
+                {
+                    this._ilGenerator.Emit(OpCodes.Castclass, toType);
+                }
+            }
+            public void Pop()
+            {
+                this._ilGenerator.Emit(OpCodes.Pop);
+            }
+            public void Convert(Type toType)
+            {
+                this._ilGenerator.Emit(_converts[toType]);
+            }
+            public void Return()
+            {
+                this._ilGenerator.Emit(OpCodes.Ret);
+            }
+            public void Call(MethodInfo method)
+            {
+                if (method.IsFinal || !method.IsVirtual)
+                {
+                    this._ilGenerator.EmitCall(OpCodes.Call, method, null);
+                }
+                else
+                {
+                    this._ilGenerator.EmitCall(OpCodes.Callvirt, method, null);
+                }
+            }
+        }
+    }
+}

+ 25 - 0
PLC.Siemens/Core/Common/FlowControlUtil.cs

@@ -0,0 +1,25 @@
+namespace Core.Util.Common
+{
+    public static class FlowControlUtil
+    {
+        public static int CalculateFlowControlTimeMilliseconds(int pendingCount, int thresholdCount, int stepPercent, int baseWaitMilliseconds, int maxWaitMilliseconds = 10000)
+        {
+            var exceedCount = pendingCount - thresholdCount;
+            exceedCount = exceedCount <= 0 ? 1 : exceedCount;
+
+            var stepCount = stepPercent * thresholdCount / 100;
+            stepCount = stepCount <= 0 ? 1 : stepCount;
+
+            var times = exceedCount / stepCount;
+            times = times <= 0 ? 1 : times;
+
+            var waitMilliseconds = times * baseWaitMilliseconds;
+
+            if (waitMilliseconds > maxWaitMilliseconds)
+            {
+                return maxWaitMilliseconds;
+            }
+            return waitMilliseconds;
+        }
+    }
+}

+ 14 - 0
PLC.Siemens/Core/Common/IControlAble.cs

@@ -0,0 +1,14 @@
+namespace Core.Util.Common
+{
+    public interface IControlAble
+    {
+        /// <summary>
+        /// 启动控制
+        /// </summary>
+        void Start();
+        /// <summary>
+        /// 停止控制
+        /// </summary>
+        void Stop();
+    }
+}

+ 22 - 0
PLC.Siemens/Core/Common/IDisposableEx.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Serialization;
+
+namespace Core.Util.Common
+{
+    public interface IDisposableEx : IDisposable
+    {
+        /// <summary>
+        /// 是否释放
+        /// </summary>
+        [XmlIgnore]
+        bool Disposed { get; }
+
+        /// <summary>
+        /// 释放完事件
+        /// </summary>
+        event EventHandler OnDisposed;
+    }
+}

+ 154 - 0
PLC.Siemens/Core/Common/ModelHandler.cs

@@ -0,0 +1,154 @@
+using System.Collections.Generic;
+using System.Data;
+using System.Reflection;
+
+namespace Core.Util.Common
+{
+    /// <summary>
+    /// DataTable与实体类互相转换
+    /// </summary>
+    /// <typeparam name="T">实体类</typeparam>
+    public class ModelHandler<T> where T : new()
+    {
+        #region DataTable转换成实体类
+
+        /// <summary>
+        /// 填充对象列表:用DataSet的第一个表填充实体类
+        /// </summary>
+        /// <param name="ds">DataSet</param>
+        /// <returns></returns>
+        public List<T> FillModel(DataSet ds)
+        {
+            if (ds == null || ds.Tables[0] == null || ds.Tables[0].Rows.Count == 0)
+            {
+                return null;
+            }
+            else
+            {
+                return FillModel(ds.Tables[0]);
+            }
+        }
+
+        /// <summary>  
+        /// 填充对象列表:用DataSet的第index个表填充实体类
+        /// </summary>  
+        public List<T> FillModel(DataSet ds, int index)
+        {
+            if (ds == null || ds.Tables.Count <= index || ds.Tables[index].Rows.Count == 0)
+            {
+                return null;
+            }
+            else
+            {
+                return FillModel(ds.Tables[index]);
+            }
+        }
+
+        /// <summary>  
+        /// 填充对象列表:用DataTable填充实体类
+        /// </summary>  
+        public List<T> FillModel(DataTable dt)
+        {
+            if (dt == null || dt.Rows.Count == 0)
+            {
+                return null;
+            }
+            List<T> modelList = new List<T>();
+            foreach (DataRow dr in dt.Rows)
+            {
+                //T model = (T)Activator.CreateInstance(typeof(T));  
+                T model = new T();
+                foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
+                {
+                    model.GetType().GetProperty(propertyInfo.Name).SetValue(model, dr[propertyInfo.Name], null);
+                }
+                modelList.Add(model);
+            }
+            return modelList;
+        }
+
+        /// <summary>  
+        /// 填充对象:用DataRow填充实体类
+        /// </summary>  
+        public T FillModel(DataRow dr)
+        {
+            if (dr == null)
+            {
+                return default(T);
+            }
+
+            //T model = (T)Activator.CreateInstance(typeof(T));  
+            T model = new T();
+            foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
+            {
+                model.GetType().GetProperty(propertyInfo.Name).SetValue(model, dr[propertyInfo.Name], null);
+            }
+            return model;
+        }
+
+        #endregion
+
+        #region 实体类转换成DataTable
+
+        /// <summary>
+        /// 实体类转换成DataSet
+        /// </summary>
+        /// <param name="modelList">实体类列表</param>
+        /// <returns></returns>
+        public DataSet FillDataSet(List<T> modelList)
+        {
+            if (modelList == null || modelList.Count == 0)
+            {
+                return null;
+            }
+            else
+            {
+                DataSet ds = new DataSet();
+                ds.Tables.Add(FillDataTable(modelList));
+                return ds;
+            }
+        }
+
+        /// <summary>
+        /// 实体类转换成DataTable
+        /// </summary>
+        /// <param name="modelList">实体类列表</param>
+        /// <returns></returns>
+        public DataTable FillDataTable(List<T> modelList)
+        {
+            if (modelList == null || modelList.Count == 0)
+            {
+                return null;
+            }
+            DataTable dt = CreateData(modelList[0]);
+
+            foreach (T model in modelList)
+            {
+                DataRow dataRow = dt.NewRow();
+                foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
+                {
+                    dataRow[propertyInfo.Name] = propertyInfo.GetValue(model, null);
+                }
+                dt.Rows.Add(dataRow);
+            }
+            return dt;
+        }
+
+        /// <summary>
+        /// 根据实体类得到表结构
+        /// </summary>
+        /// <param name="model">实体类</param>
+        /// <returns></returns>
+        private DataTable CreateData(T model)
+        {
+            DataTable dataTable = new DataTable(typeof(T).Name);
+            foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
+            {
+                dataTable.Columns.Add(new DataColumn(propertyInfo.Name, propertyInfo.PropertyType));
+            }
+            return dataTable;
+        }
+
+        #endregion
+    }
+}

+ 539 - 0
PLC.Siemens/Core/Common/ObjectId.cs

@@ -0,0 +1,539 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+
+namespace Core.Util.Common
+{
+    /// <summary>Represents an ObjectId
+    /// </summary>
+    [Serializable]
+    public struct ObjectId : IComparable<ObjectId>, IEquatable<ObjectId>
+    {
+        // private static fields
+        private static readonly DateTime __unixEpoch;
+        private static readonly long __dateTimeMaxValueMillisecondsSinceEpoch;
+        private static readonly long __dateTimeMinValueMillisecondsSinceEpoch;
+        private static ObjectId __emptyInstance = default(ObjectId);
+        private static int __staticMachine;
+        private static short __staticPid;
+        private static int __staticIncrement; // high byte will be masked out when generating new ObjectId
+        private static uint[] _lookup32 = Enumerable.Range(0, 256).Select(i =>
+        {
+            string s = i.ToString("x2");
+            return ((uint)s[0]) + ((uint)s[1] << 16);
+        }).ToArray();
+
+        // we're using 14 bytes instead of 12 to hold the ObjectId in memory but unlike a byte[] there is no additional object on the heap
+        // the extra two bytes are not visible to anyone outside of this class and they buy us considerable simplification
+        // an additional advantage of this representation is that it will serialize to JSON without any 64 bit overflow problems
+        private int _timestamp;
+        private int _machine;
+        private short _pid;
+        private int _increment;
+
+        // static constructor
+        static ObjectId()
+        {
+            __unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+            __dateTimeMaxValueMillisecondsSinceEpoch = (DateTime.MaxValue - __unixEpoch).Ticks / 10000;
+            __dateTimeMinValueMillisecondsSinceEpoch = (DateTime.MinValue - __unixEpoch).Ticks / 10000;
+            __staticMachine = GetMachineHash();
+            __staticIncrement = (new Random()).Next();
+            __staticPid = (short)GetCurrentProcessId();
+        }
+
+        // constructors
+        /// <summary>
+        /// Initializes a new instance of the ObjectId class.
+        /// </summary>
+        /// <param name="bytes">The bytes.</param>
+        public ObjectId(byte[] bytes)
+        {
+            if (bytes == null)
+            {
+                throw new ArgumentNullException("bytes");
+            }
+            Unpack(bytes, out _timestamp, out _machine, out _pid, out _increment);
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the ObjectId class.
+        /// </summary>
+        /// <param name="timestamp">The timestamp (expressed as a DateTime).</param>
+        /// <param name="machine">The machine hash.</param>
+        /// <param name="pid">The PID.</param>
+        /// <param name="increment">The increment.</param>
+        public ObjectId(DateTime timestamp, int machine, short pid, int increment)
+            : this(GetTimestampFromDateTime(timestamp), machine, pid, increment)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the ObjectId class.
+        /// </summary>
+        /// <param name="timestamp">The timestamp.</param>
+        /// <param name="machine">The machine hash.</param>
+        /// <param name="pid">The PID.</param>
+        /// <param name="increment">The increment.</param>
+        public ObjectId(int timestamp, int machine, short pid, int increment)
+        {
+            if ((machine & 0xff000000) != 0)
+            {
+                throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
+            }
+            if ((increment & 0xff000000) != 0)
+            {
+                throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
+            }
+
+            _timestamp = timestamp;
+            _machine = machine;
+            _pid = pid;
+            _increment = increment;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the ObjectId class.
+        /// </summary>
+        /// <param name="value">The value.</param>
+        public ObjectId(string value)
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException("value");
+            }
+            Unpack(ParseHexString(value), out _timestamp, out _machine, out _pid, out _increment);
+        }
+
+        // public static properties
+        /// <summary>
+        /// Gets an instance of ObjectId where the value is empty.
+        /// </summary>
+        public static ObjectId Empty
+        {
+            get { return __emptyInstance; }
+        }
+
+        // public properties
+        /// <summary>
+        /// Gets the timestamp.
+        /// </summary>
+        public int Timestamp
+        {
+            get { return _timestamp; }
+        }
+
+        /// <summary>
+        /// Gets the machine.
+        /// </summary>
+        public int Machine
+        {
+            get { return _machine; }
+        }
+
+        /// <summary>
+        /// Gets the PID.
+        /// </summary>
+        public short Pid
+        {
+            get { return _pid; }
+        }
+
+        /// <summary>
+        /// Gets the increment.
+        /// </summary>
+        public int Increment
+        {
+            get { return _increment; }
+        }
+
+        /// <summary>
+        /// Gets the creation time (derived from the timestamp).
+        /// </summary>
+        public DateTime CreationTime
+        {
+            get { return __unixEpoch.AddSeconds(_timestamp); }
+        }
+
+        // public operators
+        /// <summary>
+        /// Compares two ObjectIds.
+        /// </summary>
+        /// <param name="lhs">The first ObjectId.</param>
+        /// <param name="rhs">The other ObjectId</param>
+        /// <returns>True if the first ObjectId is less than the second ObjectId.</returns>
+        public static bool operator <(ObjectId lhs, ObjectId rhs)
+        {
+            return lhs.CompareTo(rhs) < 0;
+        }
+
+        /// <summary>
+        /// Compares two ObjectIds.
+        /// </summary>
+        /// <param name="lhs">The first ObjectId.</param>
+        /// <param name="rhs">The other ObjectId</param>
+        /// <returns>True if the first ObjectId is less than or equal to the second ObjectId.</returns>
+        public static bool operator <=(ObjectId lhs, ObjectId rhs)
+        {
+            return lhs.CompareTo(rhs) <= 0;
+        }
+
+        /// <summary>
+        /// Compares two ObjectIds.
+        /// </summary>
+        /// <param name="lhs">The first ObjectId.</param>
+        /// <param name="rhs">The other ObjectId.</param>
+        /// <returns>True if the two ObjectIds are equal.</returns>
+        public static bool operator ==(ObjectId lhs, ObjectId rhs)
+        {
+            return lhs.Equals(rhs);
+        }
+
+        /// <summary>
+        /// Compares two ObjectIds.
+        /// </summary>
+        /// <param name="lhs">The first ObjectId.</param>
+        /// <param name="rhs">The other ObjectId.</param>
+        /// <returns>True if the two ObjectIds are not equal.</returns>
+        public static bool operator !=(ObjectId lhs, ObjectId rhs)
+        {
+            return !(lhs == rhs);
+        }
+
+        /// <summary>
+        /// Compares two ObjectIds.
+        /// </summary>
+        /// <param name="lhs">The first ObjectId.</param>
+        /// <param name="rhs">The other ObjectId</param>
+        /// <returns>True if the first ObjectId is greather than or equal to the second ObjectId.</returns>
+        public static bool operator >=(ObjectId lhs, ObjectId rhs)
+        {
+            return lhs.CompareTo(rhs) >= 0;
+        }
+
+        /// <summary>
+        /// Compares two ObjectIds.
+        /// </summary>
+        /// <param name="lhs">The first ObjectId.</param>
+        /// <param name="rhs">The other ObjectId</param>
+        /// <returns>True if the first ObjectId is greather than the second ObjectId.</returns>
+        public static bool operator >(ObjectId lhs, ObjectId rhs)
+        {
+            return lhs.CompareTo(rhs) > 0;
+        }
+
+        // public static methods
+        /// <summary>
+        /// Generates a new ObjectId with a unique value.
+        /// </summary>
+        /// <returns>An ObjectId.</returns>
+        public static ObjectId GenerateNewId()
+        {
+            return GenerateNewId(GetTimestampFromDateTime(DateTime.UtcNow));
+        }
+
+        /// <summary>
+        /// Generates a new ObjectId with a unique value (with the timestamp component based on a given DateTime).
+        /// </summary>
+        /// <param name="timestamp">The timestamp component (expressed as a DateTime).</param>
+        /// <returns>An ObjectId.</returns>
+        public static ObjectId GenerateNewId(DateTime timestamp)
+        {
+            return GenerateNewId(GetTimestampFromDateTime(timestamp));
+        }
+
+        /// <summary>
+        /// Generates a new ObjectId with a unique value (with the given timestamp).
+        /// </summary>
+        /// <param name="timestamp">The timestamp component.</param>
+        /// <returns>An ObjectId.</returns>
+        public static ObjectId GenerateNewId(int timestamp)
+        {
+            int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff; // only use low order 3 bytes
+            return new ObjectId(timestamp, __staticMachine, __staticPid, increment);
+        }
+
+        /// <summary>
+        /// Generates a new ObjectId string with a unique value.
+        /// </summary>
+        /// <returns>The string value of the new generated ObjectId.</returns>
+        public static string GenerateNewStringId()
+        {
+            return GenerateNewId().ToString();
+        }
+
+        /// <summary>
+        /// Packs the components of an ObjectId into a byte array.
+        /// </summary>
+        /// <param name="timestamp">The timestamp.</param>
+        /// <param name="machine">The machine hash.</param>
+        /// <param name="pid">The PID.</param>
+        /// <param name="increment">The increment.</param>
+        /// <returns>A byte array.</returns>
+        public static byte[] Pack(int timestamp, int machine, short pid, int increment)
+        {
+            if ((machine & 0xff000000) != 0)
+            {
+                throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
+            }
+            if ((increment & 0xff000000) != 0)
+            {
+                throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
+            }
+
+            byte[] bytes = new byte[12];
+            bytes[0] = (byte)(timestamp >> 24);
+            bytes[1] = (byte)(timestamp >> 16);
+            bytes[2] = (byte)(timestamp >> 8);
+            bytes[3] = (byte)(timestamp);
+            bytes[4] = (byte)(machine >> 16);
+            bytes[5] = (byte)(machine >> 8);
+            bytes[6] = (byte)(machine);
+            bytes[7] = (byte)(pid >> 8);
+            bytes[8] = (byte)(pid);
+            bytes[9] = (byte)(increment >> 16);
+            bytes[10] = (byte)(increment >> 8);
+            bytes[11] = (byte)(increment);
+            return bytes;
+        }
+
+        /// <summary>
+        /// Parses a string and creates a new ObjectId.
+        /// </summary>
+        /// <param name="s">The string value.</param>
+        /// <returns>A ObjectId.</returns>
+        public static ObjectId Parse(string s)
+        {
+            if (s == null)
+            {
+                throw new ArgumentNullException("s");
+            }
+            if (s.Length != 24)
+            {
+                throw new ArgumentOutOfRangeException("s", "ObjectId string value must be 24 characters.");
+            }
+            return new ObjectId(ParseHexString(s));
+        }
+
+        /// <summary>
+        /// Unpacks a byte array into the components of an ObjectId.
+        /// </summary>
+        /// <param name="bytes">A byte array.</param>
+        /// <param name="timestamp">The timestamp.</param>
+        /// <param name="machine">The machine hash.</param>
+        /// <param name="pid">The PID.</param>
+        /// <param name="increment">The increment.</param>
+        public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment)
+        {
+            if (bytes == null)
+            {
+                throw new ArgumentNullException("bytes");
+            }
+            if (bytes.Length != 12)
+            {
+                throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long.");
+            }
+            timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
+            machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6];
+            pid = (short)((bytes[7] << 8) + bytes[8]);
+            increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11];
+        }
+
+        // private static methods
+        /// <summary>
+        /// Gets the current process id.  This method exists because of how CAS operates on the call stack, checking
+        /// for permissions before executing the method.  Hence, if we inlined this call, the calling method would not execute
+        /// before throwing an exception requiring the try/catch at an even higher level that we don't necessarily control.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static int GetCurrentProcessId()
+        {
+            return Process.GetCurrentProcess().Id;
+        }
+
+        private static int GetMachineHash()
+        {
+            var hostName = Environment.MachineName; // use instead of Dns.HostName so it will work offline
+            var md5 = MD5.Create();
+            var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(hostName));
+            return (hash[0] << 16) + (hash[1] << 8) + hash[2]; // use first 3 bytes of hash
+        }
+
+        private static int GetTimestampFromDateTime(DateTime timestamp)
+        {
+            return (int)Math.Floor((ToUniversalTime(timestamp) - __unixEpoch).TotalSeconds);
+        }
+
+        // public methods
+        /// <summary>
+        /// Compares this ObjectId to another ObjectId.
+        /// </summary>
+        /// <param name="other">The other ObjectId.</param>
+        /// <returns>A 32-bit signed integer that indicates whether this ObjectId is less than, equal to, or greather than the other.</returns>
+        public int CompareTo(ObjectId other)
+        {
+            int r = _timestamp.CompareTo(other._timestamp);
+            if (r != 0) { return r; }
+            r = _machine.CompareTo(other._machine);
+            if (r != 0) { return r; }
+            r = _pid.CompareTo(other._pid);
+            if (r != 0) { return r; }
+            return _increment.CompareTo(other._increment);
+        }
+
+        /// <summary>
+        /// Compares this ObjectId to another ObjectId.
+        /// </summary>
+        /// <param name="rhs">The other ObjectId.</param>
+        /// <returns>True if the two ObjectIds are equal.</returns>
+        public bool Equals(ObjectId rhs)
+        {
+            return
+                _timestamp == rhs._timestamp &&
+                _machine == rhs._machine &&
+                _pid == rhs._pid &&
+                _increment == rhs._increment;
+        }
+
+        /// <summary>
+        /// Compares this ObjectId to another object.
+        /// </summary>
+        /// <param name="obj">The other object.</param>
+        /// <returns>True if the other object is an ObjectId and equal to this one.</returns>
+        public override bool Equals(object obj)
+        {
+            if (obj is ObjectId)
+            {
+                return Equals((ObjectId)obj);
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Gets the hash code.
+        /// </summary>
+        /// <returns>The hash code.</returns>
+        public override int GetHashCode()
+        {
+            int hash = 17;
+            hash = 37 * hash + _timestamp.GetHashCode();
+            hash = 37 * hash + _machine.GetHashCode();
+            hash = 37 * hash + _pid.GetHashCode();
+            hash = 37 * hash + _increment.GetHashCode();
+            return hash;
+        }
+
+        /// <summary>
+        /// Converts the ObjectId to a byte array.
+        /// </summary>
+        /// <returns>A byte array.</returns>
+        public byte[] ToByteArray()
+        {
+            return Pack(_timestamp, _machine, _pid, _increment);
+        }
+
+        /// <summary>
+        /// Returns a string representation of the value.
+        /// </summary>
+        /// <returns>A string representation of the value.</returns>
+        public override string ToString()
+        {
+            return ToHexString(ToByteArray());
+        }
+
+        /// <summary>
+        /// Parses a hex string into its equivalent byte array.
+        /// </summary>
+        /// <param name="s">The hex string to parse.</param>
+        /// <returns>The byte equivalent of the hex string.</returns>
+        public static byte[] ParseHexString(string s)
+        {
+            if (s == null)
+            {
+                throw new ArgumentNullException("s");
+            }
+
+            if (s.Length % 2 == 1)
+            {
+                throw new Exception("The binary key cannot have an odd number of digits");
+            }
+
+            byte[] arr = new byte[s.Length >> 1];
+
+            for (int i = 0; i < s.Length >> 1; ++i)
+            {
+                arr[i] = (byte)((GetHexVal(s[i << 1]) << 4) + (GetHexVal(s[(i << 1) + 1])));
+            }
+
+            return arr;
+        }
+        /// <summary>
+        /// Converts a byte array to a hex string.
+        /// </summary>
+        /// <param name="bytes">The byte array.</param>
+        /// <returns>A hex string.</returns>
+        public static string ToHexString(byte[] bytes)
+        {
+            if (bytes == null)
+            {
+                throw new ArgumentNullException("bytes");
+            }
+            var result = new char[bytes.Length * 2];
+            for (int i = 0; i < bytes.Length; i++)
+            {
+                var val = _lookup32[bytes[i]];
+                result[2 * i] = (char)val;
+                result[2 * i + 1] = (char)(val >> 16);
+            }
+            return new string(result);
+        }
+        /// <summary>
+        /// Converts a DateTime to number of milliseconds since Unix epoch.
+        /// </summary>
+        /// <param name="dateTime">A DateTime.</param>
+        /// <returns>Number of seconds since Unix epoch.</returns>
+        public static long ToMillisecondsSinceEpoch(DateTime dateTime)
+        {
+            var utcDateTime = ToUniversalTime(dateTime);
+            return (utcDateTime - __unixEpoch).Ticks / 10000;
+        }
+        /// <summary>
+        /// Converts a DateTime to UTC (with special handling for MinValue and MaxValue).
+        /// </summary>
+        /// <param name="dateTime">A DateTime.</param>
+        /// <returns>The DateTime in UTC.</returns>
+        public static DateTime ToUniversalTime(DateTime dateTime)
+        {
+            if (dateTime == DateTime.MinValue)
+            {
+                return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
+            }
+            else if (dateTime == DateTime.MaxValue)
+            {
+                return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc);
+            }
+            else
+            {
+                return dateTime.ToUniversalTime();
+            }
+        }
+
+        private static int GetHexVal(char hex)
+        {
+            int val = (int)hex;
+            //For uppercase A-F letters:
+            //return val - (val < 58 ? 48 : 55);
+            //For lowercase a-f letters:
+            //return val - (val < 58 ? 48 : 87);
+            //Or the two combined, but a bit slower:
+            return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
+        }
+    }
+}

+ 70 - 0
PLC.Siemens/Core/Common/Rand.cs

@@ -0,0 +1,70 @@
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Core.Util.Common
+{
+    /// <summary>随机数</summary>
+    public static class Rand
+    {
+        private static readonly RandomNumberGenerator Rnd;
+
+        static Rand()
+        {
+            Rnd = new RNGCryptoServiceProvider();
+        }
+
+        /// <summary>返回一个小于所指定最大值的非负随机数</summary>
+        /// <param name="max">返回的随机数的上界(随机数不能取该上界值)</param>
+        /// <returns></returns>
+        public static int Next(int max = int.MaxValue)
+        {
+            if (max <= 0) throw new ArgumentOutOfRangeException("max");
+            
+            return Next(0, max);
+        }
+
+        /// <summary>返回一个指定范围内的随机数</summary>
+        /// <param name="min">返回的随机数的下界(随机数可取该下界值)</param>
+        /// <param name="max">返回的随机数的上界(随机数不能取该上界值)</param>
+        /// <returns></returns>
+        public static int Next(int min, int max)
+        {
+            if (max <= min) throw new ArgumentOutOfRangeException("max");
+
+            var buf = new byte[4];
+            Rnd.GetBytes(buf);
+
+            var n = BitConverter.ToInt32(buf, 0);
+            if (min == int.MinValue && max == int.MaxValue) return n;
+            if (min == 0 && max == int.MaxValue) return Math.Abs(n);
+            if (min == int.MinValue && max == 0) return -Math.Abs(n);
+
+            var num = max - min;
+            return (int)(((num * (uint)n) >> 32) + min);
+        }
+
+        /// <summary>返回指定长度随机字节数组</summary>
+        public static byte[] NextBytes(int count)
+        {
+            var buf = new byte[count];
+            Rnd.GetBytes(buf);
+            return buf;
+        }
+
+        /// <summary>返回指定长度随机字符串</summary>
+        /// <param name="length"></param>
+        /// <returns></returns>
+        public static string NextString(int length)
+        {
+            var sb = new StringBuilder();
+            for (int i = 0; i < length; i++)
+            {
+                var ch = (char)Next(' ', 0x7F);
+                sb.Append(ch);
+            }
+
+            return sb.ToString();
+        }
+    }
+}

+ 42 - 0
PLC.Siemens/Core/Common/SingleBase.cs

@@ -0,0 +1,42 @@
+using System;
+
+namespace Core.Util.Common
+{
+    /// <summary>
+    /// 单例基类
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class SingleBase<T> where T : new()
+    {
+        // ReSharper disable once InconsistentNaming
+        private static readonly T _instance;
+
+        // ReSharper disable once StaticFieldInGenericType
+        private static readonly object ObjLock = new object();
+
+        /// <summary>
+        /// 获取单例句柄
+        /// </summary>
+        public static T Instance { get { return _instance; } }
+
+        static SingleBase()
+        {
+            if (_instance != null) return;
+            lock (ObjLock)
+            {
+                if (_instance == null)
+                {
+                    _instance = new T();
+                }
+            }
+        }
+
+        public SingleBase()
+        {
+            if (_instance != null)
+            {
+                throw new InvalidOperationException("单例模式设计,不允许重复实例化对象");
+            }
+        }
+    }
+}

+ 12 - 0
PLC.Siemens/Core/Common/SystemTime.cs

@@ -0,0 +1,12 @@
+using System;
+
+namespace Core.Util.Common
+{
+    public static class SystemTime
+    {
+        /// <summary>
+        /// 获取当前系统时间 方便UTC和本地时间的切换
+        /// </summary>
+        public static Func<DateTime> Now = () => DateTime.UtcNow;
+    }
+}

+ 51 - 0
PLC.Siemens/Core/Extension/ArrayExtension.cs

@@ -0,0 +1,51 @@
+using System;
+using Core.Util.Common;
+
+namespace Core.Util.Extension
+{
+    /// <summary>
+    /// 数组集合扩展功能
+    /// </summary>
+    public static class ArrayExtension
+    {
+        /// <summary>
+        /// 判断数组是否为空
+        /// </summary>
+        /// <typeparam name="T">数组类型</typeparam>
+        /// <param name="targer">目标对象</param>
+        /// <returns></returns>
+        public static bool IsNullOrEmpty<T>(this T[] targer)
+        {
+            if (targer == null || targer.Length < 1)
+                return true;
+            return false;
+        }
+
+
+        /// <summary>
+        /// 判断数组是否为空
+        /// </summary>
+        /// <typeparam name="T">数组类型</typeparam>
+        /// <param name="targer">目标对象</param>
+        /// <param name="predicate">搜索条件</param>
+        /// <returns></returns>
+        public static T Find<T>(this T[] targer, Predicate<T> predicate)
+        {
+            Argument.IsNotEmpty(targer, "targer");
+            return Array.Find(targer, predicate);
+        }
+
+        /// <summary>
+        /// 判断数组是否为空
+        /// </summary>
+        /// <typeparam name="T">数组类型</typeparam>
+        /// <param name="targer">目标对象</param>
+        /// <param name="predicate">搜索条件</param>
+        /// <returns></returns>
+        public static T[] FindAll<T>(this T[] targer, Predicate<T> predicate)
+        {
+            Argument.IsNotEmpty(targer, "targer");
+            return Array.FindAll(targer, predicate);
+        }
+    }
+}

+ 235 - 0
PLC.Siemens/Core/Extension/BitExtension.cs

@@ -0,0 +1,235 @@
+namespace Core.Util.Extension
+{
+    /// <summary>
+    ///     位运算操作
+    /// </summary>
+    public static class BitExtension
+    {
+        #region ushort 
+
+        /// <summary>
+        /// 设置指定位置的位值的值
+        /// </summary>
+        /// <param name="value">ushort对象</param>
+        /// <param name="position">指定位置</param>
+        /// <param name="flag">值</param>
+        /// <returns></returns>
+        public static ushort SetBit(this ushort value, int position, bool flag)
+        {
+            return SetBits(value, position, 1, flag ? (byte) 1 : (byte) 0);
+        }
+
+        /// <summary>
+        /// 批量设置指定位置的位值的值
+        /// </summary>
+        /// <param name="value">ushort对象</param>
+        /// <param name="position">开始位置</param>
+        /// <param name="length">长度</param>
+        /// <param name="bits">值</param>
+        /// <returns></returns>
+        public static ushort SetBits(this ushort value, int position, int length, ushort bits)
+        {
+            if (length <= 0 || position >= 16) return value;
+
+            var mask = (2 << (length - 1)) - 1;
+
+            value &= (ushort) ~(mask << position);
+            value |= (ushort) ((bits & mask) << position);
+
+            return value;
+        }
+
+        /// <summary>
+        /// 获取指定位置的值
+        /// </summary>
+        /// <param name="value">ushort对象</param>
+        /// <param name="position">指定位置</param>
+        /// <returns></returns>
+        public static bool GetBit(this ushort value, int position)
+        {
+            return GetBits(value, position, 1) == 1;
+        }
+
+        /// <summary>
+        /// 批量获取指定位置的值
+        /// </summary>
+        /// <param name="value">ushort对象</param>
+        /// <param name="position">开始位值</param>
+        /// <param name="length">长度</param>
+        /// <returns></returns>
+        public static ushort GetBits(this ushort value, int position, int length)
+        {
+            if (length <= 0 || position >= 16) return 0;
+
+            var mask = (2 << (length - 1)) - 1;
+
+            return (ushort) ((value >> position) & mask);
+        }
+
+        #endregion
+
+        #region byte
+
+        /// <summary>
+        /// 设置指定位置的值
+        /// </summary>
+        /// <param name="value">byte对象</param>
+        /// <param name="position">指定位置</param>
+        /// <param name="flag">设置值</param>
+        /// <returns></returns>
+        public static byte SetBit(this byte value, int position, bool flag)
+        {
+            if (position >= 8) return value;
+
+            var mask = (2 << (1 - 1)) - 1;
+
+            value &= (byte) ~(mask << position);
+            value |= (byte) (((flag ? 1 : 0) & mask) << position);
+
+            return value;
+        }
+
+        /// <summary>
+        /// 获取指定位置的值
+        /// </summary>
+        /// <param name="value">byte对象</param>
+        /// <param name="position">指定位置</param>
+        /// <returns></returns>
+        public static bool GetBit(this byte value, int position)
+        {
+            if (position >= 8) return false;
+
+            var mask = (2 << (1 - 1)) - 1;
+
+            return (byte) ((value >> position) & mask) == 1;
+        }
+
+        #endregion
+
+        #region uint
+
+        /// <summary>
+        /// 设置指定位置的位值的值
+        /// </summary>
+        /// <param name="value">uint对象</param>
+        /// <param name="position">指定位置</param>
+        /// <param name="flag">值</param>
+        /// <returns></returns>
+        public static uint SetBit(this uint value, int position, bool flag)
+        {
+            return SetBits(value, position, 1, flag ? (byte) 1 : (byte) 0);
+        }
+
+        /// <summary>
+        /// 批量设置指定位置的位值的值
+        /// </summary>
+        /// <param name="value">uint对象</param>
+        /// <param name="position">开始位置</param>
+        /// <param name="length">长度</param>
+        /// <param name="bits">值</param>
+        /// <returns></returns>
+        public static uint SetBits(this uint value, int position, int length, uint bits)
+        {
+            if (length <= 0 || position >= 32) return value;
+
+            var mask = (2 << (length - 1)) - 1;
+
+            value &= (uint) ~(mask << position);
+            value |= (uint) ((bits & mask) << position);
+
+            return value;
+        }
+
+        /// <summary>
+        /// 获取指定位置的值
+        /// </summary>
+        /// <param name="value">uint对象</param>
+        /// <param name="position">指定位置</param>
+        /// <returns></returns>
+        public static bool GetBit(this uint value, int position)
+        {
+            return GetBits(value, position, 1) == 1;
+        }
+
+        /// <summary>
+        /// 批量获取指定位置的值
+        /// </summary>
+        /// <param name="value">uint对象</param>
+        /// <param name="position">开始位值</param>
+        /// <param name="length">长度</param>
+        /// <returns></returns>
+        public static uint GetBits(this uint value, int position, int length)
+        {
+            if (length <= 0 || position >= 32) return 0;
+
+            var mask = (2 << (length - 1)) - 1;
+
+            return (uint) ((value >> position) & mask);
+        }
+
+        #endregion
+
+        #region ulong
+
+        /// <summary>
+        /// 设置指定位置的位值的值
+        /// </summary>
+        /// <param name="value">ulong对象</param>
+        /// <param name="position">指定位置</param>
+        /// <param name="flag">值</param>
+        /// <returns></returns>
+        public static ulong SetBit(this ulong value, int position, bool flag)
+        {
+            return SetBits(value, position, 1, flag ? (byte) 1 : (byte) 0);
+        }
+
+        /// <summary>
+        /// 批量设置指定位置的位值的值
+        /// </summary>
+        /// <param name="value">ulong对象</param>
+        /// <param name="position">开始位置</param>
+        /// <param name="length">长度</param>
+        /// <param name="bits">值</param>
+        /// <returns></returns>
+        public static ulong SetBits(this ulong value, int position, int length, ulong bits)
+        {
+            if (length <= 0 || position >= 64) return value;
+
+            var mask = (ulong) (2 << (length - 1)) - 1;
+
+            value &= ~(mask << position);
+            value |= (bits & mask) << position;
+
+            return value;
+        }
+
+        /// <summary>
+        /// 获取指定位置的值
+        /// </summary>
+        /// <param name="value">ulong对象</param>
+        /// <param name="position">指定位置</param>
+        /// <returns></returns>
+        public static bool GetBit(this ulong value, int position)
+        {
+            return GetBits(value, position, 1) == 1;
+        }
+
+        /// <summary>
+        /// 批量获取指定位置的值
+        /// </summary>
+        /// <param name="value">ulong对象</param>
+        /// <param name="position">开始位值</param>
+        /// <param name="length">长度</param>
+        /// <returns></returns>
+        public static ulong GetBits(this ulong value, int position, int length)
+        {
+            if (length <= 0 || position >= 64) return 0;
+
+            var mask = (ulong) (2 << (length - 1)) - 1;
+
+            return (value >> position) & mask;
+        }
+
+        #endregion
+    }
+}

+ 322 - 0
PLC.Siemens/Core/Extension/ByteExtenstion.cs

@@ -0,0 +1,322 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Core.Util.Extension
+{
+    public static class ByteExtenstion
+    {
+        /// <summary>检测字节数组编码</summary>
+        public static Encoding Detect(this byte[] data)
+        {
+            // 探测BOM头
+            var encoding = DetectBOM(data);
+            if (encoding != null) return encoding;
+
+            return DetectInternal(data);
+        }
+
+       public  static Encoding DetectInternal(this byte[] data)
+        {
+            Encoding encoding = null;
+            // 最笨的办法尝试
+            var encs = new[] {
+                // 常用
+                Encoding.UTF8,
+                // 用户界面选择语言编码
+                Encoding.GetEncoding(CultureInfo.CurrentUICulture.TextInfo.ANSICodePage),
+                // 本地默认编码
+                Encoding.Default
+            };
+            encs = encs.Where(s => s != null).GroupBy(s => s.CodePage).Select(s => s.First()).ToArray();
+
+            // 如果有单字节编码,优先第一个非单字节的编码
+            foreach (var enc in encs)
+            {
+                if (IsMatch(data, enc))
+                {
+                    if (!enc.IsSingleByte) return enc;
+
+                    if (encoding == null) encoding = enc;
+                }
+            }
+            if (encoding != null) return encoding;
+
+            // 探测Unicode编码
+            encoding = DetectUnicode(data);
+            if (encoding != null) return encoding;
+
+            // 简单方法探测ASCII
+            encoding = DetectASCII(data);
+           return encoding;
+        }
+
+        /// <summary>检测BOM字节序</summary>
+        /// <param name="boms"></param>
+        /// <returns></returns>
+        // ReSharper disable once InconsistentNaming
+        public static Encoding DetectBOM(this byte[] boms)
+        {
+            if (boms.Length < 2) return null;
+
+            if (boms[0] == 0xff && boms[1] == 0xfe && (boms.Length < 4 || boms[2] != 0 || boms[3] != 0)) return Encoding.Unicode;
+
+            if (boms[0] == 0xfe && boms[1] == 0xff) return Encoding.BigEndianUnicode;
+
+            if (boms.Length < 3) return null;
+
+            if (boms[0] == 0xef && boms[1] == 0xbb && boms[2] == 0xbf) return Encoding.UTF8;
+
+            if (boms[0] == 0x2b && boms[1] == 0x2f && boms[2] == 0x76) return Encoding.UTF7;
+
+            if (boms.Length < 4) return null;
+
+            if (boms[0] == 0xff && boms[1] == 0xfe && boms[2] == 0 && boms[3] == 0) return Encoding.UTF32;
+
+            if (boms[0] == 0 && boms[1] == 0 && boms[2] == 0xfe && boms[3] == 0xff) return Encoding.GetEncoding(12001);
+
+            return null;
+        }
+
+        /// <summary>检测是否ASCII</summary>
+        // ReSharper disable once InconsistentNaming
+        static Encoding DetectASCII(byte[] data)
+        {
+            // 如果所有字节都小于128,则可以使用ASCII编码
+            if (data.Any(t => t >= 128))
+            {
+                return null;
+            }
+
+            return Encoding.ASCII;
+        }
+
+        static bool IsMatch(byte[] data, Encoding encoding)
+        {
+            if (encoding == null) encoding = Encoding.Default;
+
+            try
+            {
+                var str = encoding.GetString(data);
+                var buf = encoding.GetBytes(str);
+
+                // 考虑到噪声干扰,只要0.9
+                var score = buf.Length * 9 / 10;
+                var match = 0;
+                for (var i = 0; i < buf.Length; i++)
+                {
+                    if (data[i] == buf[i])
+                    {
+                        match++;
+                        if (match >= score) return true;
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+               // XTrace.WriteException(ex);
+                // ignored
+            }
+
+            return false;
+        }
+
+        /// <summary>启发式探测Unicode编码</summary>
+        static Encoding DetectUnicode(byte[] data)
+        {
+            var oddBinaryNullsInSample = 0;
+            var evenBinaryNullsInSample = 0;
+            var suspiciousUtf8SequenceCount = 0;
+            var suspiciousUtf8BytesTotal = 0;
+            // ReSharper disable once InconsistentNaming
+            var likelyUSASCIIBytesInSample = 0;
+
+            // Cycle through, keeping count of binary null positions, possible UTF-8
+            // sequences from upper ranges of Windows-1252, and probable US-ASCII
+            // character counts.
+
+            long pos = 0;
+            // ReSharper disable once InconsistentNaming
+            var skipUTF8Bytes = 0;
+
+            while (pos < data.Length)
+            {
+                // 二进制空分布
+                if (data[pos] == 0)
+                {
+                    if (pos % 2 == 0)
+                        evenBinaryNullsInSample++;
+                    else
+                        oddBinaryNullsInSample++;
+                }
+
+                // 可见 ASCII 字符
+                if (IsCommonASCII(data[pos]))
+                    likelyUSASCIIBytesInSample++;
+
+                // 类似UTF-8的可疑序列
+                if (skipUTF8Bytes == 0)
+                {
+                    int len = DetectSuspiciousUTF8SequenceLength(data, pos);
+                    if (len > 0)
+                    {
+                        suspiciousUtf8SequenceCount++;
+                        suspiciousUtf8BytesTotal += len;
+                        skipUTF8Bytes = len - 1;
+                    }
+                }
+                else
+                {
+                    skipUTF8Bytes--;
+                }
+
+                pos++;
+            }
+
+            // UTF-16
+            // LE 小端 在英语或欧洲环境,经常使用奇数个0(以0开始),而很少用偶数个0
+            // BE 大端 在英语或欧洲环境,经常使用偶数个0(以0开始),而很少用奇数个0
+            if (((evenBinaryNullsInSample * 2.0) / data.Length) < 0.2
+                && ((oddBinaryNullsInSample * 2.0) / data.Length) > 0.6
+                )
+                return Encoding.Unicode;
+
+            if (((oddBinaryNullsInSample * 2.0) / data.Length) < 0.2
+                && ((evenBinaryNullsInSample * 2.0) / data.Length) > 0.6
+                )
+                return Encoding.BigEndianUnicode;
+
+            // UTF-8
+            // 使用正则检测,参考http://www.w3.org/International/questions/qa-forms-utf-8
+            string potentiallyMangledString = Encoding.ASCII.GetString(data);
+            var reg = new Regex(@"\A("
+                + @"[\x09\x0A\x0D\x20-\x7E]"            // ASCII
+                + @"|[\xC2-\xDF][\x80-\xBF]"            // 不太长的2字节
+                + @"|\xE0[\xA0-\xBF][\x80-\xBF]"        // 排除太长
+                + @"|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}" // 连续的3字节
+                + @"|\xED[\x80-\x9F][\x80-\xBF]"        // 排除代理
+                + @"|\xF0[\x90-\xBF][\x80-\xBF]{2}"     // 1~3
+                + @"|[\xF1-\xF3][\x80-\xBF]{3}"         // 4~15
+                + @"|\xF4[\x80-\x8F][\x80-\xBF]{2}"     // 16
+                + @")*\z");
+            if (reg.IsMatch(potentiallyMangledString))
+            {
+                //Unfortunately, just the fact that it CAN be UTF-8 doesn't tell you much about probabilities.
+                //If all the characters are in the 0-127 range, no harm done, most western charsets are same as UTF-8 in these ranges.
+                //If some of the characters were in the upper range (western accented characters), however, they would likely be mangled to 2-byte by the UTF-8 encoding process.
+                // So, we need to play stats.
+
+                // The "Random" likelihood of any pair of randomly generated characters being one
+                // of these "suspicious" character sequences is:
+                // 128 / (256 * 256) = 0.2%.
+                //
+                // In western text data, that is SIGNIFICANTLY reduced - most text data stays in the <127
+                // character range, so we assume that more than 1 in 500,000 of these character
+                // sequences indicates UTF-8. The number 500,000 is completely arbitrary - so sue me.
+                //
+                // We can only assume these character sequences will be rare if we ALSO assume that this
+                // IS in fact western text - in which case the bulk of the UTF-8 encoded data (that is
+                // not already suspicious sequences) should be plain US-ASCII bytes. This, I
+                // arbitrarily decided, should be 80% (a random distribution, eg binary data, would yield
+                // approx 40%, so the chances of hitting this threshold by accident in random data are
+                // VERY low).
+
+                // 很不幸运,事实上,它仅仅可能是UTF-8。如果所有字符都在0~127范围,那是没有问题的,绝大部分西方字符在UTF-8都在这个范围。
+                // 然而如果部分字符在大写区域(西方口语字符),用UTF-8编码处理可能造成误伤。所以我们需要继续分析。
+                // 随机生成字符成为可疑序列的可能性是:128 / (256 * 256) = 0.2%
+                // 在西方文本数据,这要小得多,绝大部分文本数据停留在小于127的范围。所以我们假定在500000个字符中多余一个UTF-8字符
+
+                if ((suspiciousUtf8SequenceCount * 500000.0 / data.Length >= 1) // 可疑序列
+                    && (
+                           // 所有可疑情况,无法平率ASCII可能性
+                           data.Length - suspiciousUtf8BytesTotal == 0
+                           ||
+                           likelyUSASCIIBytesInSample * 1.0 / (data.Length - suspiciousUtf8BytesTotal) >= 0.8
+                       )
+                    )
+                    return Encoding.UTF8;
+            }
+
+            return null;
+        }
+
+        /// <summary>是否可见ASCII</summary>
+        /// <param name="bt"></param>
+        /// <returns></returns>
+        // ReSharper disable once InconsistentNaming
+        static bool IsCommonASCII(byte bt)
+        {
+            if (bt == 0x0A // 回车
+                || bt == 0x0D // 换行
+                || bt == 0x09 // 制表符
+                || (bt >= 0x20 && bt <= 0x2F) // 符号
+                || (bt >= 0x30 && bt <= 0x39) // 数字
+                || (bt >= 0x3A && bt <= 0x40) // 符号
+                || (bt >= 0x41 && bt <= 0x5A) // 大写字母
+                || (bt >= 0x5B && bt <= 0x60) // 符号
+                || (bt >= 0x61 && bt <= 0x7A) // 小写字母
+                || (bt >= 0x7B && bt <= 0x7E) // 符号
+                )
+                return true;
+            else
+                return false;
+        }
+
+        /// <summary>检测可能的UTF8序列长度</summary>
+        /// <param name="buf"></param>
+        /// <param name="pos"></param>
+        /// <returns></returns>
+        // ReSharper disable once InconsistentNaming
+        private static int DetectSuspiciousUTF8SequenceLength(byte[] buf, Int64 pos)
+        {
+            if (buf.Length > pos + 1)
+            {
+                var first = buf[pos];
+                var second = buf[pos + 1];
+                if (first == 0xC2)
+                {
+                    if (second == 0x81 || second == 0x8D || second == 0x8F || second == 0x90 || second == 0x9D || second >= 0xA0 && second <= 0xBF)
+                        return 2;
+                }
+                else if (first == 0xC3)
+                {
+                    if (second >= 0x80 && second <= 0xBF) return 2;
+                }
+                else if (first == 0xC5)
+                {
+                    if (second == 0x92 || second == 0x93 || second == 0xA0 || second == 0xA1 || second == 0xB8 || second == 0xBD || second == 0xBE)
+                        return 2;
+                }
+                else if (first == 0xC6)
+                {
+                    if (second == 0x92) return 2;
+                }
+                else if (first == 0xCB)
+                {
+                    if (second == 0x86 || second == 0x9C) return 2;
+                }
+                else if (buf.Length >= pos + 2 && first == 0xE2)
+                {
+                    var three = buf[pos + 2];
+                    if (second == 0x80)
+                    {
+                        if (three == 0x93 || three == 0x94 || three == 0x98 || three == 0x99 || three == 0x9A)
+                            return 3;
+                        if (three == 0x9C || three == 0x9D || three == 0x9E)
+                            return 3;
+                        if (three == 0xA0 || three == 0xA1 || three == 0xA2)
+                            return 3;
+                        if (three == 0xA6 || three == 0xB0 || three == 0xB9 || three == 0xBA)
+                            return 3;
+                    }
+                    else if (second == 0x82 && three == 0xAC || second == 0x84 && three == 0xA2)
+                        return 3;
+                }
+            }
+
+            return 0;
+        }
+    }
+}

+ 235 - 0
PLC.Siemens/Core/Extension/CollectionExtension.cs

@@ -0,0 +1,235 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Core.Util.Extension
+{
+    /// <summary>集合扩展</summary>
+    public static class CollectionExtension
+    {
+        /// <summary>
+        /// 确保某一类型的元素必须位于开始位置
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="checkedItem">想要位于首位的元素的类型</param>
+        /// <returns>调整后的集合</returns>
+        /// <remarks>
+        /// 本方法适用于要确认的元素的类型在集合中唯一的情况。
+        /// </remarks>
+        public static void EnsureFirstPosition<T>(this IEnumerable<T> source, Type checkedItem)
+        {
+            if (source == null)
+            {
+                return ;
+            }
+            var sourceArray = new List<T>(source);
+            var itemIndex = sourceArray.FindIndex(m => m.GetType() == checkedItem);
+            if (itemIndex <= 0) return ;
+            var changeItem = sourceArray[itemIndex];
+            sourceArray.RemoveAt(itemIndex);
+            sourceArray.Insert(0, changeItem);
+            source= sourceArray;
+        }
+        /// <summary>
+        /// 确保某一类型的元素必须位于末尾位置
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="checkedItem">想要位于首位的元素的类型</param>
+        /// <returns>调整后的集合</returns>
+        /// <remarks>
+        /// 本方法适用于要确认的元素的类型在集合中唯一的情况。
+        /// </remarks>
+        public static void EnsureLastPosition<T>(this IEnumerable<T> source, Type checkedItem)
+        {
+            if (source == null)
+            {
+                return ;
+            }
+            var sourceArray = new List<T>(source);
+            var itemIndex = sourceArray.FindIndex(m => m.GetType() == checkedItem);
+            if (itemIndex == sourceArray.Count-1) return ;
+            var changeItem = sourceArray[itemIndex];
+            sourceArray.RemoveAt(itemIndex);
+            sourceArray.Insert(sourceArray.Count-1, changeItem);
+            source = sourceArray;
+        }
+        /// <summary>
+        /// 集合转换
+        /// </summary>
+        /// <typeparam name="T">对象类型</typeparam>
+        /// <typeparam name="TResult">转换后的对象</typeparam>
+        /// <param name="items">对象</param>
+        /// <param name="transformation">转换表达式</param>
+        /// <returns></returns>
+        public static IEnumerable<TResult> ConvertAll<T, TResult>(this IEnumerable<T> items,
+            Converter<T, TResult> transformation)
+        {
+            if (items == null) return null;
+
+            var arr = items as T[];
+            if (arr != null)
+            {
+                return Array.ConvertAll(arr, transformation);
+            }
+            var list = items as List<T>;
+            if (list != null)
+            {
+                return list.ConvertAll(transformation);
+            }
+
+            return items.Select(_ => transformation(_));
+        }
+
+        /// <summary>
+        ///     Func转Predicate
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <returns></returns>
+        public static Predicate<T> ToPredicate<T>(this Func<T, bool> source)
+        {
+            var result = new Predicate<T>(source);
+            return result;
+        }
+
+        /// <summary>
+        /// IEnumerable<T>对象的轮询操作
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="items"></param>
+        /// <param name="action"></param>
+        public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
+        {
+            if (items == null)
+            {
+                return;
+            }
+
+            var arr = items as T[];
+            if (arr != null)
+            {
+                Array.ForEach(arr, action);
+                return;
+            }
+            var list = items as List<T>;
+            if (list != null)
+            {
+                list.ForEach(action);
+                return;
+            }
+            foreach (var item in items)
+            {
+                action(item);
+            }
+        }
+
+        /// <summary>
+        /// IEnumerable<T>是否为空
+        /// </summary>
+        /// <param name="targer"></param>
+        /// <returns></returns>
+        public static bool IsNullOrEmpty(this IEnumerable targer)
+        {
+            return targer == null || targer.GetEnumerator().MoveNext() == false;
+        }
+        
+
+        /// <summary>
+        ///     ICollection<T />转换成数组T[]
+        /// </summary>
+        public static T[] ToArray<T>(this ICollection<T> collection, int index = 0)
+        {
+            if (collection.IsNullOrEmpty()) return null;
+            var arr = new T[collection.Count];
+            collection.CopyTo(arr, index);
+            return arr;
+        }
+
+        /// <summary>把一个列表组合成为一个字符串,默认逗号分隔</summary>
+        /// <param name="value"></param>
+        /// <param name="separator">组合分隔符,默认逗号</param>
+        /// <returns></returns>
+        public static string Join(this IEnumerable value, string separator = ",")
+        {
+            if (value == null) return null;
+            var sb = new StringBuilder();
+            foreach (var item in value)
+            {
+                sb.Separate(separator).Append(item + "");
+            }
+            return sb.ToString();
+        }
+
+        /// <summary>把一个列表组合成为一个字符串,默认逗号分隔</summary>
+        /// <param name="value"></param>
+        /// <param name="separator">组合分隔符,默认逗号</param>
+        /// <param name="func">把对象转为字符串的委托</param>
+        /// <returns></returns>
+        public static string Join<T>(this IEnumerable<T> value, string separator = ",", Func<T, string> func = null)
+        {
+            if (value == null) return null;
+            var sb = new StringBuilder();
+            if (func == null) func = obj => obj + "";
+            foreach (var item in value)
+            {
+                sb.Separate(separator).Append(func(item));
+            }
+            return sb.ToString();
+        }
+        /// <summary>
+        /// 在考虑依赖关系的情况下,使用托普法进行排序
+        /// </summary>
+        /// <typeparam name="T">要排序的类型.</typeparam>
+        /// <param name="source">要排序的数据</param>
+        /// <param name="getDependencies">依赖项查询</param>
+        /// <returns></returns>
+        public static List<T> SortByDependencies<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies)
+        {
+
+            var sorted = new List<T>();
+            var visited = new Dictionary<T, bool>();
+
+            foreach (var item in source)
+            {
+                SortByDependenciesVisit(item, getDependencies, sorted, visited);
+            }
+
+            return sorted;
+        }
+
+       
+        private static void SortByDependenciesVisit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited)
+        {
+            bool inProcess;
+            var alreadyVisited = visited.TryGetValue(item, out inProcess);
+
+            if (alreadyVisited)
+            {
+                if (inProcess)
+                {
+                    throw new ArgumentException("Cyclic dependency found!");
+                }
+            }
+            else
+            {
+                visited[item] = true;
+
+                var dependencies = getDependencies(item);
+                if (dependencies != null)
+                {
+                    foreach (var dependency in dependencies)
+                    {
+                        SortByDependenciesVisit(dependency, getDependencies, sorted, visited);
+                    }
+                }
+
+                visited[item] = false;
+                sorted.Add(item);
+            }
+        }
+    }
+}

+ 35 - 0
PLC.Siemens/Core/Extension/DateTimeExtension.cs

@@ -0,0 +1,35 @@
+using System;
+using Core.Util.Common;
+
+namespace Core.Util.Extension
+{
+    /// <summary>
+    ///     时间帮助扩展类
+    /// </summary>
+    public static class DateTimeExtension
+    {
+        private static readonly DateTime MinDate = new DateTime(1970, 1, 1);
+        private static readonly DateTime MaxDate = new DateTime(9999, 12, 31, 23, 59, 59, 999);
+
+        /// <summary>
+        /// 当前时间是否有效范围之内
+        /// </summary>
+        /// <param name="target"></param>
+        /// <returns></returns>
+        public static bool IsValid(this DateTime target)
+        {
+            return (target >= MinDate) && (target <= MaxDate);
+        }
+        /// <summary>时间日期转为yyyy-MM-dd HH:mm:ss完整字符串,支持指定最小时间的字符串</summary>
+        public static string ToFullString(this DateTime value, string emptyValue = null)
+        {
+            return new DefaultConvert().ToFullString(value, emptyValue);
+        }
+
+        /// <summary>时间日期转为指定格式字符串</summary>
+        public static string ToString(this DateTime value, string format, string emptyValue)
+        {
+            return new DefaultConvert().ToString(value, format, emptyValue);
+        }
+    }
+}

+ 78 - 0
PLC.Siemens/Core/Extension/EncodingHelper.cs

@@ -0,0 +1,78 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace Core.Util.Extension
+{
+    public static class StreamExtension
+    {
+        /// <summary>
+        /// 压入一个布尔量,当前位置提升1
+        /// </summary>
+        /// <param name="stream"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public static Stream Push(this Stream stream, bool value)
+        {
+            //stream.WriteArray()
+            return stream;
+        }
+    }
+
+    /// <summary>编码助手</summary>
+    public static class EncodingHelper
+    {
+        #region 编码检测
+        /// <summary>检测文件编码</summary>
+        public static Encoding Detect(this string filename)
+        {
+            using (var fs = File.OpenRead(filename))
+            {
+                return Detect(fs);
+            }
+        }
+
+        /// <summary>检测文件编码</summary>
+        public static Encoding DetectEncoding(this FileInfo file)
+        {
+            using (var fs = file.OpenRead())
+            {
+                return fs.Detect();
+            }
+        }
+
+        /// <summary>检测数据流编码</summary>
+        /// <param name="stream">数据流</param>
+        /// <param name="sampleSize">BOM检测失败时用于启发式探索的数据大小</param>
+        /// <returns></returns>
+        public static Encoding Detect(this Stream stream, long sampleSize = 0x400)
+        {
+            // 记录数据流原始位置,后面需要复原
+            var pos = stream.Position;
+            stream.Position = 0;
+
+            // 首先检查BOM
+            var boms = new byte[stream.Length > 4 ? 4 : stream.Length];
+            stream.Read(boms, 0, boms.Length);
+
+            var encoding = boms.Detect();
+            if (encoding != null)
+            {
+                stream.Position = pos;
+                return encoding;
+            }
+
+            // BOM检测失败,开始启发式探测
+            // 抽查一段字节数组
+            var data = new byte[sampleSize > stream.Length ? stream.Length : sampleSize];
+            Array.Copy(boms, data, boms.Length);
+            if (stream.Length > boms.Length) stream.Read(data, boms.Length, data.Length - boms.Length);
+            stream.Position = pos;
+
+            return data.DetectInternal();
+        }
+
+        
+        #endregion
+    }
+}

+ 95 - 0
PLC.Siemens/Core/Extension/EnumExtension.cs

@@ -0,0 +1,95 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Reflection;
+
+namespace System
+{
+    /// <summary>枚举类型助手类</summary>
+    public static class EnumHelper
+    {
+        /// <summary>枚举变量是否包含指定标识</summary>
+        public static bool Has(this Enum value, Enum flag)
+        {
+            if (value.GetType() != flag.GetType()) throw new ArgumentException("flag", "枚举标识判断必须是相同的类型!");
+
+            ulong num = Convert.ToUInt64(flag);
+            return (Convert.ToUInt64(value) & num) == num;
+        }
+
+        /// <summary>设置标识位</summary>
+        public static T Set<T>(this Enum source, T flag, bool value)
+        {
+            if (!(source is T)) throw new ArgumentException("source", "枚举标识判断必须是相同的类型!");
+
+            ulong s = Convert.ToUInt64(source);
+            ulong f = Convert.ToUInt64(flag);
+
+            if (value)
+            {
+                s |= f;
+            }
+            else
+            {
+                s &= ~f;
+            }
+
+            return (T)Enum.ToObject(typeof(T), s);
+        }
+
+        /// <summary>获取枚举字段的注释</summary>
+        //public static string GetDescription(this Enum value)
+        //{
+        //    var type = value.GetType();
+        //    var item = type.GetField(value.ToString(), BindingFlags.Public | BindingFlags.Static);
+
+        //    var att = item.GetCustomAttribute<DescriptionAttribute>(false);
+        //    if (att != null && !string.IsNullOrEmpty(att.Description)) return att.Description;
+
+        //    return null;
+        //}
+
+        ///// <summary>获取枚举类型的所有字段注释</summary>
+        //public static Dictionary<TEnum, string> GetDescriptions<TEnum>()
+        //{
+        //    var dic = new Dictionary<TEnum, string>();
+
+        //    foreach (var item in GetDescriptions(typeof(TEnum)))
+        //    {
+        //        dic.Add((TEnum)(object)item.Key, item.Value);
+        //    }
+
+        //    return dic;
+        //}
+
+        ///// <summary>获取枚举类型的所有字段注释</summary>
+        //public static Dictionary<int, string> GetDescriptions(Type enumType)
+        //{
+        //    var dic = new Dictionary<int, string>();
+        //    foreach (var item in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
+        //    {
+        //        if (!item.IsStatic) continue;
+
+        //        // 这里的快速访问方法会报错
+        //        //FieldInfoX fix = FieldInfoX.Create(item);
+        //        //PermissionFlags value = (PermissionFlags)fix.GetValue(null);
+        //        int value = Convert.ToInt32(item.GetValue(null));
+
+        //        string des = item.Name;
+
+        //        //var dna = AttributeX.GetCustomAttribute<DisplayNameAttribute>(item, false);
+        //        var dna = item.GetCustomAttribute<DisplayNameAttribute>(false);
+        //        if (dna != null && !string.IsNullOrEmpty(dna.DisplayName)) des = dna.DisplayName;
+
+        //        //var att = AttributeX.GetCustomAttribute<DescriptionAttribute>(item, false);
+        //        var att = item.GetCustomAttribute<DescriptionAttribute>(false);
+        //        if (att != null && !string.IsNullOrEmpty(att.Description)) des = att.Description;
+        //        //dic.Add(value, des);
+        //        // 有些枚举可能不同名称有相同的值
+        //        dic[value] = des;
+        //    }
+
+        //    return dic;
+        //}
+    }
+}
+

+ 33 - 0
PLC.Siemens/Core/Extension/GuidExtension.cs

@@ -0,0 +1,33 @@
+using System;
+
+namespace Core.Util.Extension
+{
+    public static class GuidExtension
+    {
+        /// <summary>
+        /// GuidÊÕËõµ½string
+        /// </summary>
+        /// <param name="target"></param>
+        /// <returns></returns>
+        public static string Shrink(this Guid target)
+        {
+            if (target.IsEmpty()) return "";
+            var base64 = Convert.ToBase64String(target.ToByteArray());
+
+            var encoded = base64.Replace("/", "_").Replace("+", "-");
+
+
+            return encoded.Substring(0, 22);
+        }
+
+        /// <summary>
+        /// GuidÊÇ·ñΪ¿Õ
+        /// </summary>
+        /// <param name="target"></param>
+        /// <returns></returns>
+        public static bool IsEmpty(this Guid target)
+        {
+            return target == Guid.Empty;
+        }
+    }
+}

+ 36 - 0
PLC.Siemens/Core/Extension/IEnumerableExtensions.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Core.Util.Extension
+{
+    public static class IEnumerableExtensions
+    {
+       
+        public static IEnumerable<T> Safe<T>(this IEnumerable<T> collection)
+        {
+            return collection ?? Enumerable.Empty<T>();
+        }
+
+        public static bool Contains<T>(this IEnumerable<T> collection, Predicate<T> condition)
+        {
+            return collection.Any(x => condition(x));
+        }
+
+        public static bool IsEmpty<T>(this IEnumerable<T> collection)
+        {
+            if (collection == null)
+                return true;
+            var coll = collection as ICollection;
+            if (coll != null)
+                return coll.Count == 0;
+            return !collection.Any();
+        }
+
+        public static bool IsNotEmpty<T>(this IEnumerable<T> collection)
+        {
+            return !IsEmpty(collection);
+        }
+    }
+}

+ 46 - 0
PLC.Siemens/Core/Extension/ObjectExtension.cs

@@ -0,0 +1,46 @@
+using System;
+using Core.Util.Common;
+
+namespace Core.Util.Extension
+{
+    public static class ObjectExtension
+    {
+        #region 类型转换
+
+        public static DefaultConvert Convert= new DefaultConvert();
+
+        /// <summary>转为整数,转换失败时返回默认值。支持字符串、全角、字节数组(小端)</summary>
+        public static int ToInt(this object value, int defaultValue = 0)
+        {
+            return Convert.ToInt(value, defaultValue);
+        }
+
+        /// <summary>转为浮点数,转换失败时返回默认值。支持字符串、全角、字节数组(小端)</summary>
+        public static double ToDouble(this object value, double defaultValue = 0)
+        {
+            return Convert.ToDouble(value, defaultValue);
+        }
+
+        /// <summary>转为布尔型,转换失败时返回默认值。支持大小写True/False、0和非零</summary>
+        public static bool ToBoolean(this object value, bool defaultValue = false)
+        {
+            return Convert.ToBoolean(value, defaultValue);
+        }
+
+        /// <summary>转为时间日期,转换失败时返回最小时间</summary>
+        public static DateTime ToDateTime(this object value)
+        {
+            return Convert.ToDateTime(value, DateTime.MinValue);
+        }
+
+        /// <summary>转为时间日期,转换失败时返回默认值</summary>
+        public static DateTime ToDateTime(this object value, DateTime defaultValue)
+        {
+            return Convert.ToDateTime(value, defaultValue);
+        }
+
+       
+
+        #endregion
+    }
+}

+ 121 - 0
PLC.Siemens/Core/Extension/ReaderWriterLockSlimExtensions.cs

@@ -0,0 +1,121 @@
+using System;
+using System.Threading;
+
+namespace Core.Util.Extension
+{
+    public static class ReaderWriterLockSlimExtensions
+    {
+        /// <summary>An atom read action wrapper.
+        /// </summary>
+        /// <param name="readerWriterLockSlim"></param>
+        /// <param name="action"></param>
+        /// <exception cref="ArgumentNullException"></exception>
+        public static void AtomRead(this ReaderWriterLockSlim readerWriterLockSlim, Action action)
+        {
+            if (readerWriterLockSlim == null)
+            {
+                throw new ArgumentNullException("readerWriterLockSlim");
+            }
+            if (action == null)
+            {
+                throw new ArgumentNullException("action");
+            }
+
+            readerWriterLockSlim.EnterReadLock();
+
+            try
+            {
+                action();
+            }
+            finally
+            {
+                readerWriterLockSlim.ExitReadLock();
+            }
+        }
+        /// <summary>An atom read func wrapper.
+        /// </summary>
+        /// <param name="readerWriterLockSlim"></param>
+        /// <param name="function"></param>
+        /// <typeparam name="T"></typeparam>
+        /// <returns></returns>
+        /// <exception cref="ArgumentNullException"></exception>
+        public static T AtomRead<T>(this ReaderWriterLockSlim readerWriterLockSlim, Func<T> function)
+        {
+            if (readerWriterLockSlim == null)
+            {
+                throw new ArgumentNullException("readerWriterLockSlim");
+            }
+            if (function == null)
+            {
+                throw new ArgumentNullException("function");
+            }
+
+            readerWriterLockSlim.EnterReadLock();
+
+            try
+            {
+                return function();
+            }
+            finally
+            {
+                readerWriterLockSlim.ExitReadLock();
+            }
+        }
+        /// <summary>An atom write action wrapper.
+        /// </summary>
+        /// <param name="readerWriterLockSlim"></param>
+        /// <param name="action"></param>
+        /// <exception cref="ArgumentNullException"></exception>
+        public static void AtomWrite(this ReaderWriterLockSlim readerWriterLockSlim, Action action)
+        {
+            if (readerWriterLockSlim == null)
+            {
+                throw new ArgumentNullException("readerWriterLockSlim");
+            }
+            if (action == null)
+            {
+                throw new ArgumentNullException("action");
+            }
+
+            readerWriterLockSlim.EnterWriteLock();
+
+            try
+            {
+                action();
+            }
+            finally
+            {
+                readerWriterLockSlim.ExitWriteLock();
+            }
+        }
+        /// <summary>An atom write func wrapper.
+        /// </summary>
+        /// <param name="readerWriterLockSlim"></param>
+        /// <param name="function"></param>
+        /// <typeparam name="T"></typeparam>
+        /// <returns></returns>
+        /// <exception cref="ArgumentNullException"></exception>
+        public static T AtomWrite<T>(this ReaderWriterLockSlim readerWriterLockSlim, Func<T> function)
+        {
+            if (readerWriterLockSlim == null)
+            {
+                throw new ArgumentNullException("readerWriterLockSlim");
+            }
+            if (function == null)
+            {
+                throw new ArgumentNullException("function");
+            }
+
+            readerWriterLockSlim.EnterWriteLock();
+
+            try
+            {
+                return function();
+            }
+            finally
+            {
+                readerWriterLockSlim.ExitWriteLock();
+            }
+        }
+    }
+}

+ 17 - 0
PLC.Siemens/Core/Extension/StringBuilderExtension.cs

@@ -0,0 +1,17 @@
+using System.Text;
+
+namespace Core.Util.Extension
+{
+    public static class StringBuilderExtension
+    {
+        /// <summary>追加分隔符字符串,忽略开头,常用于拼接</summary>
+        public static StringBuilder Separate(this StringBuilder sb, string separator)
+        {
+            if (sb == null || separator.IsNullOrEmpty()) return sb;
+
+            if (sb.Length > 0) sb.Append(separator);
+
+            return sb;
+        }
+    }
+}

+ 789 - 0
PLC.Siemens/Core/Extension/StringHelper.cs

@@ -0,0 +1,789 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Web;
+using Core.Util.Common;
+
+namespace Core.Util.Extension
+{
+    public static class StringHelper
+    {
+        #region 正则表达式
+
+        private static readonly Regex WebUrlExpression = new Regex(
+            @"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", RegexOptions.Singleline | RegexOptions.Compiled);
+
+        private static readonly Regex EmailExpression =
+            new Regex(@"^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$",
+                RegexOptions.Singleline | RegexOptions.Compiled);
+
+        private static readonly Regex StripHtmlExpression = new Regex("<\\S[^><]*>",
+            RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant |
+            RegexOptions.Compiled);
+
+        private static readonly char[] IllegalUrlCharacters =
+        {
+            ';', '/', '\\', '?', ':', '@', '&', '=', '+', '$', ',',
+            '<', '>', '#', '%', '.', '!', '*', '\'', '"', '(', ')', '[', ']', '{', '}', '|', '^', '`', '~', '–', '‘',
+            '’', '“', '”', '»', '«'
+        };
+
+        #endregion
+
+        #region 字符串判断
+
+        public static bool IsNullOrEmpty(this string target)
+        {
+            return target == null || target.Length <= 0 || string.IsNullOrEmpty(target);
+        }
+
+        /// <summary>是否空或者空白字符串</summary>
+        public static bool IsNullOrWhiteSpace(this string value)
+        {
+            return value == null || value.All(char.IsWhiteSpace);
+        }
+
+        /// <summary> 合法URL </summary>
+        public static bool IsWebUrl(this string target)
+        {
+            return !target.IsNullOrEmpty() && WebUrlExpression.IsMatch(target);
+        }
+
+        /// <summary> 合法邮箱地址 </summary>
+        public static bool IsEmail(this string target)
+        {
+            return !target.IsNullOrEmpty() && EmailExpression.IsMatch(target);
+        }
+
+        #endregion
+
+        #region 格式转换
+
+        /// <summary> 字符串转Hash </summary>
+        public static string Hash(this string target)
+        {
+            Argument.IsNotEmpty(target, "target");
+
+            using (var md5 = MD5.Create())
+            {
+                var data = Encoding.Unicode.GetBytes(target);
+                var hash = md5.ComputeHash(data);
+
+                return Convert.ToBase64String(hash);
+            }
+        }
+
+        /// <summary>字符串转数组 默认UTF8</summary>
+        public static byte[] GetBytes(this string target, Encoding encoding = null)
+        {
+            if (target == null) return null;
+            if (target == string.Empty) return new byte[0];
+
+            if (encoding == null) encoding = Encoding.UTF8;
+            return encoding.GetBytes(target);
+        }
+
+        /// <summary> 转脚本 </summary>
+        public static string StripHtml(this string target)
+        {
+            return StripHtmlExpression.Replace(target, string.Empty);
+        }
+
+        /// <summary> 转GUID </summary>
+        public static Guid ToGuid(this string target)
+        {
+            var result = Guid.Empty;
+
+            if (!string.IsNullOrEmpty(target) && (target.Trim().Length == 22))
+            {
+                var encoded = string.Concat(target.Trim().Replace("-", "+").Replace("_", "/"), "==");
+
+                try
+                {
+                    var base64 = Convert.FromBase64String(encoded);
+
+                    result = new Guid(base64);
+                }
+                catch (FormatException)
+                {
+                }
+            }
+
+            return result;
+        }
+
+        /// <summary> 字符串转枚举 </summary>
+        public static T ToEnum<T>(this string target, T defaultValue) where T : IComparable, IFormattable
+        {
+            var convertedValue = defaultValue;
+
+            if (!string.IsNullOrEmpty(target))
+            {
+                try
+                {
+                    convertedValue = (T) Enum.Parse(typeof (T), target.Trim(), true);
+                }
+                catch (ArgumentException)
+                {
+                }
+            }
+
+            return convertedValue;
+        }
+
+        /// <summary> 合法URL </summary>
+        public static string ToLegalUrl(this string target)
+        {
+            if (string.IsNullOrEmpty(target))
+            {
+                return target;
+            }
+
+            target = target.Trim();
+
+            if (target.IndexOfAny(IllegalUrlCharacters) > -1)
+            {
+                foreach (var character in IllegalUrlCharacters)
+                {
+                    target = target.Replace(character.ToString(Constants.CurrentCulture), string.Empty);
+                }
+            }
+
+            target = target.Replace(" ", "-");
+
+            while (target.Contains("--"))
+            {
+                target = target.Replace("--", "-");
+            }
+
+            return target;
+        }
+
+        /// <summary> 对URL字符串进行编码 </summary>
+        public static string UrlEncode(this string target)
+        {
+            return target;// HttpUtility.UrlEncode(target);
+        }
+
+        /// <summary> 对URL字符串进行解码 </summary>
+        public static string UrlDecode(this string target)
+        {
+            return target;// HttpUtility.UrlDecode(target);
+        }
+
+        /// <summary> 将字符串最小限度地转换为 HTML 编码的字符串。 </summary>
+        public static string AttributeEncode(this string target)
+        {
+            return target;// HttpUtility.HtmlAttributeEncode(target);
+        }
+
+        /// <summary> 将字符串转换为 HTML 编码的字符串。 </summary>
+        public static string HtmlEncode(this string target)
+        {
+            return target;// HttpUtility.HtmlEncode(target);
+        }
+
+        /// <summary> 将已经为 HTTP 传输进行过 HTML 编码的字符串转换为已解码的字符串。 </summary>
+        public static string HtmlDecode(this string target)
+        {
+            return target;// HttpUtility.HtmlDecode(target);
+        }
+
+        #endregion
+
+        #region 截取扩展
+
+        /// <summary>
+        ///     截断补.
+        /// </summary>
+        public static string WrapAt(this string target, int index)
+        {
+            const int dotCount = 3;
+
+            Argument.IsNotEmpty(target, "target");
+            Argument.IsNotNegativeOrZero(index, "index");
+
+            return target.Length <= index
+                ? target
+                : string.Concat(target.Substring(0, index - dotCount), new string('.', dotCount));
+        }
+
+        /// <summary>确保字符串以指定的另一字符串开始,不区分大小写</summary>
+        public static string EnsureStart(this string target, string start)
+        {
+            if (start.IsNullOrEmpty()) return target;
+            if (target.IsNullOrEmpty()) return start;
+
+            if (target.StartsWith(start, StringComparison.OrdinalIgnoreCase)) return target;
+
+            return start + target;
+        }
+
+        /// <summary>确保字符串以指定的另一字符串结束,不区分大小写</summary>
+        public static string EnsureEnd(this string target, string end)
+        {
+            if (end.IsNullOrEmpty()) return target;
+            if (target.IsNullOrEmpty()) return end;
+
+            if (target.EndsWith(end, StringComparison.OrdinalIgnoreCase)) return target;
+
+            return target + end;
+        }
+
+        /// <summary>从当前字符串开头移除另一字符串,不区分大小写,循环多次匹配前缀</summary>
+        public static string TrimStart(this string target, params string[] starts)
+        {
+            if (target.IsNullOrEmpty()) return target;
+            if (starts == null || starts.Length < 1 || string.IsNullOrEmpty(starts[0])) return target;
+
+            for (var i = 0; i < starts.Length; i++)
+            {
+                if (target.StartsWith(starts[i], StringComparison.OrdinalIgnoreCase))
+                {
+                    target = target.Substring(starts[i].Length);
+                    if (string.IsNullOrEmpty(target)) break;
+
+                    // 从头开始
+                    i = -1;
+                }
+            }
+            return target;
+        }
+
+        /// <summary>从当前字符串结尾移除另一字符串,不区分大小写,循环多次匹配后缀</summary>
+        public static string TrimEnd(this string target, params string[] ends)
+        {
+            if (target.IsNullOrEmpty()) return target;
+            if (ends == null || ends.Length < 1 || string.IsNullOrEmpty(ends[0])) return target;
+
+            for (var i = 0; i < ends.Length; i++)
+            {
+                if (target.EndsWith(ends[i], StringComparison.OrdinalIgnoreCase))
+                {
+                    target = target.Substring(0, target.Length - ends[i].Length);
+                    if (string.IsNullOrEmpty(target)) break;
+
+                    // 从头开始
+                    i = -1;
+                }
+            }
+            return target;
+        }
+
+        /// <summary>从字符串中检索子字符串,在指定头部字符串之后,指定尾部字符串之前</summary>
+        public static string Substring(this string target, string after, string before = null, int startIndex = 0,
+            int[] positions = null)
+        {
+            if (target.IsNullOrEmpty()) return target;
+            if (after.IsNullOrEmpty() && before.IsNullOrEmpty()) return target;
+
+
+            var p = -1;
+            if (!string.IsNullOrEmpty(after))
+            {
+                p = target.IndexOf(after, startIndex, StringComparison.Ordinal);
+                if (p < 0) return null;
+                p += after.Length;
+
+                // 记录位置
+                if (positions != null && positions.Length > 0) positions[0] = p;
+            }
+
+            if (string.IsNullOrEmpty(before)) return target.Substring(p);
+
+            var f = target.IndexOf(before, p >= 0 ? p : startIndex, StringComparison.Ordinal);
+            if (f < 0) return null;
+
+            // 记录位置
+            if (positions != null && positions.Length > 1) positions[1] = f;
+
+            if (p >= 0)
+                return target.Substring(p, f - p);
+            return target.Substring(0, f);
+        }
+
+        /// <summary>根据最大长度截取字符串,并允许以指定空白填充末尾</summary>
+        public static string Cut(this string str, int maxLength, string pad = null)
+        {
+            if (str.IsNullOrEmpty() || maxLength <= 0 || str.Length < maxLength) return str;
+
+            // 计算截取长度
+            var len = maxLength;
+            if (!pad.IsNullOrEmpty()) len -= pad.Length;
+            if (len <= 0) return pad;
+
+            return str.Substring(0, len) + pad;
+        }
+
+        /// <summary>根据最大长度截取字符串(二进制计算长度),并允许以指定空白填充末尾</summary>
+        /// <remarks>默认采用Default编码进行处理,其它编码请参考本函数代码另外实现</remarks>
+        /// <param name="str">字符串</param>
+        /// <param name="maxLength">截取后字符串的最大允许长度,包含后面填充</param>
+        /// <param name="pad">需要填充在后面的字符串,比如几个圆点</param>
+        /// <param name="strict">严格模式时,遇到截断位置位于一个字符中间时,忽略该字符,否则包括该字符。默认true</param>
+        /// <returns></returns>
+        public static string CutBinary(this string str, int maxLength, string pad = null, bool strict = true)
+        {
+            if (string.IsNullOrEmpty(str) || maxLength <= 0 || str.Length < maxLength) return str;
+
+            var encoding = Encoding.Default;
+
+            var buf = encoding.GetBytes(str);
+            if (buf.Length < maxLength) return str;
+
+            // 计算截取字节长度
+            var len = maxLength;
+            if (!string.IsNullOrEmpty(pad)) len -= encoding.GetByteCount(pad);
+            if (len <= 0) return pad;
+
+            // 计算截取字符长度。避免把一个字符劈开
+            int clen;
+            while (true)
+            {
+                try
+                {
+                    clen = encoding.GetCharCount(buf, 0, len);
+                    break;
+                }
+                catch (DecoderFallbackException)
+                {
+                    // 发生了回退,减少len再试
+                    len--;
+                }
+            }
+            // 可能过长,修正
+            if (strict) while (encoding.GetByteCount(str.ToCharArray(), 0, clen) > len) clen--;
+
+            return str.Substring(0, clen) + pad;
+        }
+
+        /// <summary>从当前字符串开头移除另一字符串以及之前的部分</summary>
+        /// <param name="str">当前字符串</param>
+        /// <param name="starts">另一字符串</param>
+        /// <returns></returns>
+        public static string CutStart(this string str, params string[] starts)
+        {
+            if (string.IsNullOrEmpty(str)) return str;
+            if (starts == null || starts.Length < 1 || string.IsNullOrEmpty(starts[0])) return str;
+
+            for (var i = 0; i < starts.Length; i++)
+            {
+                var p = str.IndexOf(starts[i], StringComparison.Ordinal);
+                if (p >= 0)
+                {
+                    str = str.Substring(p + starts[i].Length);
+                    if (string.IsNullOrEmpty(str)) break;
+                }
+            }
+            return str;
+        }
+
+        /// <summary>从当前字符串结尾移除另一字符串以及之后的部分</summary>
+        /// <param name="str">当前字符串</param>
+        /// <param name="ends">另一字符串</param>
+        /// <returns></returns>
+        public static string CutEnd(this string str, params string[] ends)
+        {
+            if (string.IsNullOrEmpty(str)) return str;
+            if (ends == null || ends.Length < 1 || string.IsNullOrEmpty(ends[0])) return str;
+
+            for (var i = 0; i < ends.Length; i++)
+            {
+                var p = str.LastIndexOf(ends[i], StringComparison.Ordinal);
+                if (p >= 0)
+                {
+                    str = str.Substring(0, p);
+                    if (string.IsNullOrEmpty(str)) break;
+                }
+            }
+            return str;
+        }
+
+        #endregion
+
+        #region 匹配查找
+
+        /// <summary>忽略大小写的字符串相等比较,判断是否以任意一个待比较字符串相等</summary>
+        public static bool EqualIgnoreCase(this string target, params string[] strs)
+        {
+            return !target.IsNullOrEmpty() &&
+                   strs.Any(item => string.Equals(target, item, StringComparison.OrdinalIgnoreCase));
+        }
+
+        /// <summary>忽略大小写的字符串开始比较,判断是否以任意一个待比较字符串开始</summary>
+        public static bool StartsWithIgnoreCase(this string target, params string[] strs)
+        {
+            return !target.IsNullOrEmpty() &&
+                   strs.Any(item => target.StartsWith(item, StringComparison.OrdinalIgnoreCase));
+        }
+
+        /// <summary>忽略大小写的字符串结束比较,判断是否以任意一个待比较字符串结束</summary>
+        public static bool EndsWithIgnoreCase(this string target, params string[] strs)
+        {
+            return !target.IsNullOrEmpty() &&
+                   strs.Any(item => target.EndsWith(item, StringComparison.OrdinalIgnoreCase));
+        }
+        public static int GetHashcode2(this string s)
+        {
+            if (string.IsNullOrEmpty(s)) return 0;
+
+            unchecked
+            {
+                int hash = 23;
+                foreach (char c in s)
+                {
+                    hash = (hash << 5) - hash + c;
+                }
+                if (hash < 0)
+                {
+                    hash = Math.Abs(hash);
+                }
+                return hash;
+            }
+        }
+        #endregion
+
+        #region 分隔
+
+        /// <summary>拆分字符串,过滤空格,无效时返回空数组</summary>
+        public static string[] Split(this string target, params string[] separators)
+        {
+            if (target.IsNullOrEmpty()) return new string[0];
+            if (separators == null || separators.Length < 1 || separators.Length == 1 && separators[0].IsNullOrEmpty())
+                separators = new[] {",", ";"};
+
+            return target.Split(separators, StringSplitOptions.RemoveEmptyEntries);
+        }
+
+        /// <summary>拆分字符串成为整型数组,默认逗号分号分隔,无效时返回空数组</summary>
+        public static int[] SplitAsInt(this string target, params string[] separators)
+        {
+            if (target.IsNullOrEmpty()) return new int[0];
+            if (separators == null || separators.Length < 1) separators = new[] {",", ";"};
+
+            var ss = target.Split(separators, StringSplitOptions.RemoveEmptyEntries);
+            var list = new List<int>();
+            foreach (var item in ss)
+            {
+                int id;
+                if (!int.TryParse(item.Trim(), out id)) continue;
+
+                list.Add(id);
+            }
+
+            return list.ToArray();
+        }
+
+        /// <summary>拆分字符串成为名值字典。逗号分号分组,等号分隔</summary>
+        public static IDictionary<string, string> SplitAsDictionary(this string target, string nameValueSeparator = "=",
+            params string[] separators)
+        {
+            var dic = new Dictionary<string, string>();
+            if (target.IsNullOrWhiteSpace()) return dic;
+
+            if (nameValueSeparator.IsNullOrEmpty()) nameValueSeparator = "=";
+            if (separators == null || separators.Length < 1) separators = new[] {",", ";"};
+
+            var ss = target.Split(separators, StringSplitOptions.RemoveEmptyEntries);
+            if (ss.Length < 1) return null;
+
+            foreach (var item in ss)
+            {
+                var p = item.IndexOf(nameValueSeparator, StringComparison.Ordinal);
+                // 在前后都不行
+                if (p <= 0 || p >= item.Length - 1) continue;
+
+                var key = item.Substring(0, p).Trim();
+                dic[key] = item.Substring(p + nameValueSeparator.Length).Trim();
+            }
+
+            return dic;
+        }
+
+        #endregion
+
+        #region 功能扩展
+
+        /// <summary> 安全字符串 </summary>
+        public static string NullSafe(this string target)
+        {
+            return (target ?? string.Empty).Trim();
+        }
+
+        /// <summary> 字符串格式化 </summary>
+        public static string FormatWith(this string target, params object[] args)
+        {
+            Argument.IsNotEmpty(target, "target");
+
+            for (var i = 0; i < args.Length; i++)
+            {
+                if (args[i] is DateTime)
+                {
+                    if (target.Contains("{" + i + "}")) args[i] = ((DateTime) args[i]).ToFullString();
+                }
+            }
+
+            return string.Format(target, args);
+        }
+
+        public static string Replace(this string target, ICollection<string> oldValues, string newValue)
+        {
+            oldValues.ForEach(oldValue => target = target.Replace(oldValue, newValue));
+            return target;
+        }
+
+        #endregion
+
+        #region 语音播放
+
+        /// <summary>调用语音引擎说出指定话</summary>
+        /// <param name="value"></param>
+        public static void Speak(this string value)
+        {
+            //Speecher.Speak(value);
+        }
+
+        /// <summary>调用语音引擎说出指定话</summary>
+        /// <param name="value"></param>
+        public static void SpeakAsync(this string value)
+        {
+            //Speecher.SpeakAsync(value);
+        }
+
+        #endregion
+
+        #region LD编辑距离算法
+
+        /// <summary>编辑距离搜索,从词组中找到最接近关键字的若干匹配项</summary>
+        /// <remarks>
+        ///     算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
+        /// </remarks>
+        /// <param name="key">关键字</param>
+        /// <param name="words">词组</param>
+        /// <returns></returns>
+        public static string[] LevenshteinSearch(string key, string[] words)
+        {
+            if (IsNullOrWhiteSpace(key)) return new string[0];
+
+            var keys = key.Split(new[] {' ', ' '}, StringSplitOptions.RemoveEmptyEntries);
+
+            foreach (var item in keys)
+            {
+                var maxDist = (item.Length - 1)/2;
+
+                var q = from str in words
+                    where item.Length <= str.Length
+                          && Enumerable.Range(0, maxDist + 1)
+                              .Any(dist =>
+                              {
+                                  return Enumerable.Range(0, Math.Max(str.Length - item.Length - dist + 1, 0))
+                                      .Any(
+                                          f =>
+                                          {
+                                              return LevenshteinDistance(item, str.Substring(f, item.Length + dist)) <=
+                                                     maxDist;
+                                          });
+                              })
+                    orderby str
+                    select str;
+                words = q.ToArray();
+            }
+
+            return words;
+        }
+
+        /// <summary>编辑距离</summary>
+        /// <remarks>
+        ///     又称Levenshtein距离(也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。
+        ///     许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
+        ///     算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
+        /// </remarks>
+        /// <param name="str1"></param>
+        /// <param name="str2"></param>
+        /// <returns></returns>
+        public static int LevenshteinDistance(string str1, string str2)
+        {
+            var n = str1.Length;
+            var m = str2.Length;
+            var C = new int[n + 1, m + 1];
+            int i, j, x, y, z;
+            for (i = 0; i <= n; i++)
+                C[i, 0] = i;
+            for (i = 1; i <= m; i++)
+                C[0, i] = i;
+            for (i = 0; i < n; i++)
+                for (j = 0; j < m; j++)
+                {
+                    x = C[i, j + 1] + 1;
+                    y = C[i + 1, j] + 1;
+                    if (str1[i] == str2[j])
+                        z = C[i, j];
+                    else
+                        z = C[i, j] + 1;
+                    C[i + 1, j + 1] = Math.Min(Math.Min(x, y), z);
+                }
+            return C[n, m];
+        }
+
+        #endregion
+
+        #region LCS算法
+
+        /// <summary>最长公共子序列搜索,从词组中找到最接近关键字的若干匹配项</summary>
+        /// <remarks>
+        ///     算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
+        /// </remarks>
+        /// <param name="key"></param>
+        /// <param name="words"></param>
+        /// <returns></returns>
+        public static string[] LCSSearch(string key, string[] words)
+        {
+            if (IsNullOrWhiteSpace(key) || words == null || words.Length == 0) return new string[0];
+
+            var keys = key
+                .Split(new[] {' ', '\u3000'}, StringSplitOptions.RemoveEmptyEntries)
+                .OrderBy(s => s.Length)
+                .ToArray();
+
+            //var q = from sentence in items.AsParallel()
+            var q = from word in words
+                let MLL = LCSDistance(word, keys)
+                where MLL >= 0
+                orderby (MLL + 0.5)/word.Length, word
+                select word;
+
+            return q.ToArray();
+        }
+
+        /// <summary>
+        ///     最长公共子序列问题是寻找两个或多个已知数列最长的子序列。
+        ///     一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
+        ///     The longest common subsequence (LCS) problem is to find the longest subsequence common to all sequences in a set of
+        ///     sequences (often just two). Note that subsequence is different from a substring, see substring vs. subsequence. It
+        ///     is a classic computer science problem, the basis of diff (a file comparison program that outputs the differences
+        ///     between two files), and has applications in bioinformatics.
+        /// </summary>
+        /// <remarks>
+        ///     算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
+        /// </remarks>
+        /// <param name="word"></param>
+        /// <param name="keys">多个关键字。长度必须大于0,必须按照字符串长度升序排列。</param>
+        /// <returns></returns>
+        public static int LCSDistance(string word, string[] keys)
+        {
+            var sLength = word.Length;
+            var result = sLength;
+            var flags = new bool[sLength];
+            var C = new int[sLength + 1, keys[keys.Length - 1].Length + 1];
+            //int[,] C = new int[sLength + 1, words.Select(s => s.Length).Max() + 1];
+            foreach (var key in keys)
+            {
+                var wLength = key.Length;
+                int first = 0, last = 0;
+                int i = 0, j = 0, LCS_L;
+                //foreach 速度会有所提升,还可以加剪枝
+                for (i = 0; i < sLength; i++)
+                    for (j = 0; j < wLength; j++)
+                        if (word[i] == key[j])
+                        {
+                            C[i + 1, j + 1] = C[i, j] + 1;
+                            if (first < C[i, j])
+                            {
+                                last = i;
+                                first = C[i, j];
+                            }
+                        }
+                        else
+                            C[i + 1, j + 1] = Math.Max(C[i, j + 1], C[i + 1, j]);
+
+                LCS_L = C[i, j];
+                if (LCS_L <= wLength >> 1)
+                    return -1;
+
+                while (i > 0 && j > 0)
+                {
+                    if (C[i - 1, j - 1] + 1 == C[i, j])
+                    {
+                        i--;
+                        j--;
+                        if (!flags[i])
+                        {
+                            flags[i] = true;
+                            result--;
+                        }
+                        first = i;
+                    }
+                    else if (C[i - 1, j] == C[i, j])
+                        i--;
+                    else // if (C[i, j - 1] == C[i, j])
+                        j--;
+                }
+
+                if (LCS_L <= (last - first + 1) >> 1)
+                    return -1;
+            }
+
+            return result;
+        }
+
+        #endregion
+
+        #region 执行命令行
+
+        ///// <summary>以隐藏窗口执行命令行</summary>
+        ///// <param name="cmd">文件名</param>
+        ///// <param name="arguments">命令参数</param>
+        ///// <param name="msWait">等待毫秒数</param>
+        ///// <param name="output">进程输出内容。默认为空时输出到日志</param>
+        ///// <param name="onExit">进程退出时执行</param>
+        ///// <returns>进程退出代码</returns>
+        //public static int Run(this string cmd, string arguments = null, int msWait = 0, Action<string> output = null, Action<Process> onExit = null)
+        //{
+        //    if (XTrace.Debug) XTrace.WriteLine("Run {0} {1} {2}", cmd, arguments, msWait);
+
+        //    var p = new Process();
+        //    var si = p.StartInfo;
+        //    si.FileName = cmd;
+        //    si.Arguments = arguments;
+        //    si.WindowStyle = ProcessWindowStyle.Hidden;
+
+        //    // 对于控制台项目,这里需要捕获输出
+        //    if (msWait > 0)
+        //    {
+        //        si.RedirectStandardOutput = true;
+        //        si.RedirectStandardError = true;
+        //        si.UseShellExecute = false;
+        //        if (output != null)
+        //        {
+        //            p.OutputDataReceived += (s, e) => output(e.Data);
+        //            p.ErrorDataReceived += (s, e) => output(e.Data);
+        //        }
+        //        else if (HouDa.Runtime.IsConsole)
+        //        {
+        //            p.OutputDataReceived += (s, e) => XTrace.WriteLine(e.Data);
+        //            p.ErrorDataReceived += (s, e) => XTrace.Current.Error(e.Data);
+        //        }
+        //    }
+        //    if (onExit != null) p.Exited += (s, e) => onExit(s as Process);
+
+        //    p.Start();
+        //    if (msWait > 0 && (output != null || HouDa.Runtime.IsConsole))
+        //    {
+        //        p.BeginOutputReadLine();
+        //        p.BeginErrorReadLine();
+        //    }
+
+        //    if (msWait <= 0) return -1;
+
+        //    // 如果未退出,则不能拿到退出代码
+        //    if (!p.WaitForExit(msWait)) return -1;
+
+        //    return p.ExitCode;
+        //}
+
+        #endregion
+    }
+}

+ 56 - 0
PLC.Siemens/Core/Extension/TypeExtension.cs

@@ -0,0 +1,56 @@
+using System;
+using System.Linq;
+
+namespace Core.Util.Extension
+{
+    /// <summary>
+    /// Type扩展类型
+    /// </summary>
+    public static class TypeExtension
+    {
+        public static bool InheritsOrImplements(this Type child, Type parent)
+        {
+            parent = ResolveGenericTypeDefinition(parent);
+            var currentChild = child.IsGenericType
+                                   ? child.GetGenericTypeDefinition()
+                                   : child;
+            while (currentChild != typeof(object))
+            {
+                if (parent == currentChild || HasAnyInterfaces(parent, currentChild))
+                    return true;
+
+                currentChild = currentChild.BaseType != null
+                               && currentChild.BaseType.IsGenericType
+                                   ? currentChild.BaseType.GetGenericTypeDefinition()
+                                   : currentChild.BaseType;
+
+                if (currentChild == null)
+                    return false;
+            }
+            return false;
+        }
+
+        private static bool HasAnyInterfaces(Type parent, Type child)
+        {
+            return child.GetInterfaces()
+                .Any(childInterface =>
+                {
+                    var currentInterface = childInterface.IsGenericType
+                        ? childInterface.GetGenericTypeDefinition()
+                        : childInterface;
+
+                    return currentInterface == parent;
+                });
+        }
+
+        private static Type ResolveGenericTypeDefinition(Type parent)
+        {
+            bool shouldUseGenericType = !(parent.IsGenericType && parent.GetGenericTypeDefinition() != parent);
+
+            if (parent.IsGenericType && shouldUseGenericType)
+                parent = parent.GetGenericTypeDefinition();
+            return parent;
+        }
+
+    }
+}

+ 640 - 0
PLC.Siemens/O/ByteBuffer.cs

@@ -0,0 +1,640 @@
+using System;
+using System.IO;
+
+namespace Core.Communication.Transport
+{
+    public static class ExtensionHelper
+    {
+        public static byte[] Reverse(this byte[] buf, int index, int length)
+        {
+            Array.Reverse(buf, index, length);
+            return buf;
+        }
+
+        public static int CompareTo(this byte[] source, Int64 start, Int64 count, byte[] buffer, Int64 offset = 0, Int64 length = -1)
+        {
+            if (source == buffer) return 0;
+
+            if (start < 0) start = 0;
+            if (count <= 0 || count > source.Length - start) count = source.Length - start;
+            if (length <= 0 || length > buffer.Length - offset) length = buffer.Length - offset;
+
+            // 逐字节比较
+            for (int i = 0; i < count && i < length; i++)
+            {
+                int rs = source[start + i].CompareTo(buffer[offset + i]);
+                if (rs != 0) return i > 0 ? i : 1;
+            }
+
+            // 比较完成。如果长度不相等,则较长者较大
+            if (count != length) return count > length ? 1 : -1;
+
+            return 0;
+        }
+    }
+    public class ByteBuffer
+    {
+        private readonly byte[] _bufferCache;
+        /// <summary>
+        /// 缓冲区
+        /// </summary>
+        public byte[] Buffer { get; private set; }
+        /// <summary>
+        /// 当前读取位置
+        /// </summary>
+        public int ReadIndex { get; private set; }
+        /// <summary>
+        /// 当前写入位置
+        /// </summary>
+        public int WriteIndex { get; private set; }
+        /// <summary>
+        /// 设置读取位置标记
+        /// </summary>
+        public int MarkReadIndex { get; private set; }
+        /// <summary>
+        /// 设置写入位置标记
+        /// </summary>
+        public int MarkWirteIndex { get; private set; }
+
+        /// <summary>
+        /// 当前缓冲大小
+        /// </summary>
+        public int Capacity { get; private set; }
+
+        /// <summary>
+        /// 缓冲扩充单位
+        /// </summary>
+        public int CapacityUnit { get; set; }
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="capacity"></param>
+        private ByteBuffer(int capacity = 1024)
+        {
+            _bufferCache = new byte[0x10];
+            Buffer = new byte[capacity];
+            Capacity = capacity;
+            CapacityUnit = Capacity;
+        }
+
+        /// <summary>
+        /// 构建一个<see cref="ByteBuffer"/>对象
+        /// </summary>
+        /// <param name="capacity"></param>
+        /// <returns></returns>
+        public static ByteBuffer Allocate(int capacity = 1024)
+        {
+            return new ByteBuffer(capacity);
+        }
+
+        /// <summary>
+        /// 计算扩容需量
+        /// </summary>
+        /// <param name="length"></param>
+        /// <returns></returns>
+        private int FixLength(int length)
+        {
+            int n = 2;
+            int b = 2;
+            while (b < length)
+            {
+                b = 2 << n;
+                n++;
+            }
+            return (b / CapacityUnit + 1) * CapacityUnit;
+        }
+
+        /// <summary>
+        /// 长度不够扩充<see cref="Buffer"/>容量
+        /// </summary>
+        /// <param name="currLen"></param>
+        /// <param name="futureLen"></param>
+        /// <returns></returns>
+        private int FixSizeAndReset(int currLen, int futureLen)
+        {
+            if (futureLen > currLen)
+            {
+                int size = FixLength(currLen) + Capacity;
+                byte[] newbuf = new byte[size];
+                Array.Copy(Buffer, 0, newbuf, 0, currLen);
+                Buffer = newbuf;
+                Capacity = newbuf.Length;
+            }
+            return futureLen;
+        }
+
+        #region 压入对象
+        public void Push(byte[] bytes, int startIndex, int length)
+        {
+            lock (this)
+            {
+                int offset = length - startIndex;
+                if (offset <= 0) return;
+                int total = offset + WriteIndex;
+                int len = Buffer.Length;
+                FixSizeAndReset(len, total);
+                for (int i = WriteIndex, j = startIndex; i < total; i++, j++)
+                {
+                    Buffer[i] = bytes[j];
+                }
+                WriteIndex = total;
+            }
+        }
+
+        /// <summary>
+        /// 写入字节数组
+        /// </summary>
+        /// <param name="bytes"></param>
+        /// <param name="length"></param>
+        /// <param name="reversed"></param>
+        public void Push(byte[] bytes, int length, bool reversed = false)
+        {
+            if (reversed) bytes = bytes.Reverse(0,length);
+            Push(bytes, 0, length);
+        }
+
+        /// <summary>
+        /// 写入字节数组
+        /// </summary>
+        /// <param name="bytes"></param>
+        /// <param name="reversed"></param>
+        public void Push(byte[] bytes, bool reversed = false)
+        {
+            Push(bytes, bytes.Length, reversed);
+        }
+
+        /// <summary>
+        /// 写入另一个<see cref="ByteBuffer"/>对象
+        /// </summary>
+        /// <param name="buffer"></param>
+        /// <param name="reversed"></param>
+        public void Push(ByteBuffer buffer, bool reversed = false)
+        {
+            if (buffer == null) return;
+            if (buffer.ReadableBytes() <= 0) return;
+            Push(buffer.ToArray(), reversed);
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="short"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="reversed"></param>
+        public void Push(short value, bool reversed = false)
+        {
+            Push((ushort)value, reversed);
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="ushort"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="reversed"></param>
+        public void Push(ushort value, bool reversed = false)
+        {
+            _bufferCache[0] = (byte)(value >> 8);
+            _bufferCache[1] = (byte)value;
+
+            Push(_bufferCache, 2, reversed);
+        }
+
+        public void Push(char value, bool reversed = false)
+        {
+            Push((short)value, reversed);
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="int"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="reversed"></param>
+        public void Push(int value, bool reversed = false)
+        {
+            Push((uint)value);
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="uint"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="reversed"></param>
+        public void Push(uint value, bool reversed = false)
+        {
+            _bufferCache[0] = (byte)(value >> 0x18);
+            _bufferCache[1] = (byte)(value >> 0x10);
+            _bufferCache[2] = (byte)(value >> 8);
+            _bufferCache[3] = (byte)value;
+
+            Push(_bufferCache, 4, reversed);
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="long"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="reversed"></param>
+        public void Push(long value, bool reversed = false)
+        {
+            Push((ulong)value);
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="ulong"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="reversed"></param>
+        public void Push(ulong value, bool reversed = false)
+        {
+            _bufferCache[0] = (byte)(value >> 0x38);
+            _bufferCache[1] = (byte)(value >> 0x30);
+            _bufferCache[2] = (byte)(value >> 0x28);
+            _bufferCache[3] = (byte)(value >> 0x20);
+            _bufferCache[4] = (byte)(value >> 0x18);
+            _bufferCache[5] = (byte)(value >> 0x10);
+            _bufferCache[6] = (byte)(value >> 8);
+            _bufferCache[7] = (byte)value;
+
+            Push(_bufferCache, 8, reversed);
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="float"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="reversed"></param>
+        public void Push(float value, bool reversed = false)
+        {
+            var buf = BitConverter.GetBytes(value);
+
+            Push(buf, reversed);
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="byte"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        public void Push(byte value)
+        {
+            lock (this)
+            {
+                int afterLen = WriteIndex + 1;
+                int len = Buffer.Length;
+                FixSizeAndReset(len, afterLen);
+                Buffer[WriteIndex] = value;
+                WriteIndex = afterLen;
+            }
+        }
+
+        /// <summary>
+        /// 写入一个<see cref="double"/>对象
+        /// </summary>
+        /// <param name="value"></param>
+        /// <param name="reversed"></param>
+        public void Push(double value, bool reversed = false)
+        {
+            var buf = BitConverter.GetBytes(value);
+
+            Push(buf, reversed);
+        }
+        #endregion
+
+
+        #region pop出一个对象
+        /// <summary>
+        /// 读取一个<see cref="byte"/>
+        /// </summary>
+        /// <returns></returns>
+        public byte PopByte()
+        {
+            byte b = Buffer[ReadIndex];
+            ReadIndex++;
+            return b;
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="byte"/>[]
+        /// </summary>
+        /// <param name="len"></param>
+        /// <param name="reversed"></param>
+        /// <returns></returns>
+        private byte[] Pop(int len, bool reversed = false)
+        {
+            byte[] bytes = new byte[len];
+            Array.Copy(Buffer, ReadIndex, bytes, 0, len);
+            if (reversed)
+                Array.Reverse(bytes);
+            ReadIndex += len;
+            return bytes;
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="ushort"/>
+        /// </summary>
+        /// <returns></returns>
+        public ushort PopUshort(bool reversed = false)
+        {
+            var buf = Pop(2, reversed);
+            return (ushort)(buf[0] << 8 | buf[1]);
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="ushort"/>
+        /// </summary>
+        /// <returns></returns>
+        public char PopChar(bool reversed = false)
+        {
+            var buf = Pop(2, reversed);
+            return (char)(buf[0] << 8 | buf[1]);
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="short"/>
+        /// </summary>
+        /// <param name="reversed"></param>
+        /// <returns></returns>
+        public short PopShort(bool reversed = false)
+        {
+            var buf = Pop(2, reversed);
+            return (short)(buf[0] << 8 | buf[1]);
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="uint"/>
+        /// </summary>
+        /// <param name="reversed"></param>
+        /// <returns></returns>
+        public uint PopUint(bool reversed = false)
+        {
+            var buf = Pop(4, reversed);
+            return (uint)(buf[0] << 0x18 | buf[1] << 0x10 | buf[2] << 0x8 | buf[3]);
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="int"/>
+        /// </summary>
+        /// <param name="reversed"></param>
+        /// <returns></returns>
+        public int PopInt(bool reversed = false)
+        {
+            var buf = Pop(4, reversed);
+            return buf[0] << 0x18 |
+                   buf[1] << 0x10 |
+                   buf[2] << 0x8 |
+                   buf[3];
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="ulong"/>
+        /// </summary>
+        /// <param name="reversed"></param>
+        /// <returns></returns>
+        public ulong PopUlong(bool reversed = false)
+        {
+            var buf = Pop(8, reversed);
+            return (ulong)(buf[0] << 0x38 |
+                           buf[1] << 0x30 |
+                           buf[2] << 0x28 |
+                           buf[3] << 0x20 |
+                           buf[4] << 0x18 |
+                           buf[5] << 0x10 |
+                           buf[6] << 0x08 |
+                           buf[7]);
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="long"/>
+        /// </summary>
+        /// <param name="reversed"></param>
+        /// <returns></returns>
+        public long PopLong(bool reversed = false)
+        {
+            var buf = Pop(8, reversed);
+            return buf[0] << 0x38 |
+                   buf[1] << 0x30 |
+                   buf[2] << 0x28 |
+                   buf[3] << 0x20 |
+                   buf[4] << 0x18 |
+                   buf[5] << 0x10 |
+                   buf[6] << 0x08 |
+                   buf[7];
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="float"/>
+        /// </summary>
+        /// <param name="reversed"></param>
+        /// <returns></returns>
+        public float PopFloat(bool reversed = false)
+        {
+            return BitConverter.ToSingle(Pop(4, reversed), 0);
+        }
+
+        /// <summary>
+        /// 读取指定的<see cref="double"/>
+        /// </summary>
+        /// <param name="reversed"></param>
+        /// <returns></returns>
+        public double PopDouble(bool reversed = false)
+        {
+            return BitConverter.ToDouble(Pop(8, reversed), 0);
+        }
+
+        /// <summary>
+        /// 读取指定长度的<see cref="byte"/>[]
+        /// </summary>
+        /// <param name="desBytes"></param>
+        /// <param name="desStart"></param>
+        /// <param name="len"></param>
+        public void PopBytes(byte[] desBytes, int desStart, int len)
+        {
+            int size = desStart + len;
+            for (int i = desStart; i < size; i++)
+            {
+                desBytes[i] = PopByte();
+            }
+        }
+
+        public byte[] PopBytes(int len)
+        {
+            var buf = new byte[len];
+            for (int i = 0; i < len; i++)
+            {
+                buf[i] = PopByte();
+            }
+
+            return buf;
+        }
+        #endregion
+
+
+        /// <summary>
+        /// 清除已读缓冲 后续队列前移
+        /// </summary>
+        public void DiscardReadBytes()
+        {
+            if (ReadIndex <= 0) return;
+            int len = Buffer.Length - ReadIndex;
+            byte[] newbuf = new byte[Buffer.Length];
+            Array.Copy(Buffer, ReadIndex, newbuf, 0, len);
+            Buffer = newbuf;
+            WriteIndex -= ReadIndex;
+            MarkReadIndex -= ReadIndex;
+            if (MarkReadIndex < 0)
+            {
+                MarkReadIndex = ReadIndex;
+            }
+            MarkWirteIndex -= ReadIndex;
+            if (MarkWirteIndex < 0 || MarkWirteIndex < ReadIndex || MarkWirteIndex < MarkReadIndex)
+            {
+                MarkWirteIndex = WriteIndex;
+            }
+            ReadIndex = 0;
+        }
+
+        /// <summary>
+        /// 清空缓存
+        /// </summary>
+        public void Clear()
+        {
+            Buffer = new byte[Buffer.Length];
+            ReadIndex = 0;
+            WriteIndex = 0;
+            MarkReadIndex = 0;
+            MarkWirteIndex = 0;
+        }
+
+        /// <summary>
+        /// 设置缓存读取位置
+        /// </summary>
+        /// <param name="index"></param>
+        public void SetReaderIndex(int index)
+        {
+            if (index < 0) return;
+            ReadIndex = index;
+        }
+
+
+        /// <summary>
+        /// 标记当前读取的索引位置
+        /// </summary>
+        public void MarkReaderIndex()
+        {
+            MarkReadIndex = ReadIndex;
+        }
+
+        /// <summary>
+        /// 标记当前写入的索引位置
+        /// </summary>
+        public void MarkWriterIndex()
+        {
+            MarkWirteIndex = WriteIndex;
+        }
+
+
+        /// <summary>
+        /// 将读取的索引位置重置为标记的读取索引位置
+        /// </summary>
+        public void ResetReaderIndex()
+        {
+            ReadIndex = MarkReadIndex;
+        }
+
+        /// <summary>
+        /// 将写入的索引位置重置为标记的写入索引位置
+        /// </summary>
+        public void ResetWriterIndex()
+        {
+            WriteIndex = MarkWirteIndex;
+        }
+
+
+        /// <summary>
+        /// 剩余可读数量
+        /// </summary>
+        /// <returns></returns>
+        public int ReadableBytes()
+        {
+            return WriteIndex - ReadIndex;
+        }
+
+
+        /// <summary>
+        /// 剩余可读缓存转数组
+        /// </summary>
+        /// <returns></returns>
+        public byte[] ToArray()
+        {
+            byte[] bytes = new byte[WriteIndex];
+            Array.Copy(Buffer, 0, bytes, 0, bytes.Length);
+            return bytes;
+        }
+
+        public int IndexOf(byte[] buf, int offset = 0, int length = -1)
+        {
+
+            if (length <= 0) length = buf.Length - offset;
+
+            // 位置
+            int p = -1;
+            for (int i = 0; i < length;)
+            {
+                if (ReadableBytes() < 1) return -1;
+                int c = PopByte();
+                if (c == -1) return -1;
+
+                p++;
+                if (c == buf[offset + i])
+                {
+                    i++;
+
+                    // 全部匹配,退出
+                    if (i >= length) return p - length + 1;
+                }
+                else
+                {
+                    int n = i;
+                    i = 0;
+                    for (int j = 1; j < n; j++)
+                    {
+                        // 在字节数组前(j,n)里面找自己(0,n-j)
+                        if (buf.CompareTo(j, n, buf, 0, n - j) == 0)
+                        {
+                            // 前面(0,n-j)相等,窗口退回到这里
+                            i = n - j;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            return -1;
+        }
+
+        public bool EnsureStartWith(byte[] buf, int offset = 0, int length = -1)
+        {
+            if (buf.Length < offset) throw new ArgumentOutOfRangeException("offset");
+            if (offset < 0) offset = 0;
+            if (length < 0 || buf.Length < (offset + length)) length = buf.Length - offset;
+            int index = IndexOf(buf, offset, length);
+            if (index == -1)
+            {
+                //重建缓冲清除数据
+                //DiscardReadBytes(); 
+                //不能重建缓冲 有可能尾部有数据部分匹配而导致数据丢失
+                //重置ReaderIndex即可
+                SetReaderIndex(0);
+
+                return false;
+            }
+
+            if (index == 0) return true;
+
+            SetReaderIndex(index);
+            DiscardReadBytes();
+
+            return true;
+        }
+
+    }
+}

+ 25 - 0
PLC.Siemens/O/HelperFunction.cs

@@ -0,0 +1,25 @@
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.O
+{
+    public static class HelperFunction
+    {
+        public static byte DataSizeByte(DataType dataType)
+        {
+            switch (dataType)
+            {
+                case DataType.Bit: return 1;  // S7 sends 1 byte per bit
+                case DataType.Byte: return 1;
+                case DataType.Char: return 1;
+                case DataType.Word: return 2;
+                case DataType.DWord: return 4;
+                case DataType.Int: return 2;
+                case DataType.DInt: return 4;
+                case DataType.Real: return 4;
+                case DataType.Counter: return 2;
+                case DataType.Timer: return 2;
+                default: return 0;
+            }
+        }
+    }
+}

+ 10 - 0
PLC.Siemens/O/IBuildRequest.cs

@@ -0,0 +1,10 @@
+using Core.Communication.Transport;
+
+namespace PLC.Siemens.O
+{
+    public interface IBuildRequest
+    {
+        void Build();
+        ByteBuffer GetBuffer();
+    }
+}

+ 9 - 0
PLC.Siemens/O/IBuildResponse.cs

@@ -0,0 +1,9 @@
+using Core.Communication.Transport;
+
+namespace PLC.Siemens.O
+{
+    public interface IBuildResponse
+    {
+        void Build(ByteBuffer buffer);
+    }
+}

+ 27 - 0
PLC.Siemens/O/IIsoSender.cs

@@ -0,0 +1,27 @@
+namespace PLC.Siemens.O
+{
+    public interface IIsoSender
+    {
+        /// <summary>
+        /// 不含ISO头的请求
+        /// </summary>
+        /// <typeparam name="TRequest"></typeparam>
+        /// <typeparam name="TResponse"></typeparam>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        TResponse Send<TRequest, TResponse>(TRequest request)
+            where TResponse : IBuildResponse, new()
+            where TRequest : IBuildRequest, new();
+        /// <summary>
+        /// 包含ISO头的请求
+        /// </summary>
+        /// <typeparam name="TRequest"></typeparam>
+        /// <typeparam name="TResponse"></typeparam>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        TResponse IsoSend<TRequest, TResponse>(TRequest request)
+            where TResponse : IBuildResponse, new()
+            where TRequest : IBuildRequest, new();
+
+    }
+}

+ 14 - 0
PLC.Siemens/O/MessageEvent.cs

@@ -0,0 +1,14 @@
+namespace PLC.Siemens.O
+{
+    public class MessageEvent
+    {
+        public string Method { get; set; }
+        public string Message { get; set; }
+
+        public MessageEvent(string method, string message)
+        {
+            Method = method;
+            Message = message;
+        }
+    }
+}

+ 24 - 0
PLC.Siemens/O/RequestHandle.cs

@@ -0,0 +1,24 @@
+using Core.Communication.Transport;
+
+namespace PLC.Siemens.O
+{
+    public class RequestHandle<TRequest> where TRequest : IBuildRequest, new()
+    {
+        public TRequest Request { get; set; }
+
+        public RequestHandle()
+        {
+            Request = new TRequest();
+        }
+
+        public void Build()
+        {
+            Request.Build();
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            return Request.GetBuffer();
+        }
+    }
+}

+ 18 - 0
PLC.Siemens/O/ResponseHandle.cs

@@ -0,0 +1,18 @@
+using Core.Communication.Transport;
+
+namespace PLC.Siemens.O
+{
+    public class ResponseHandle<TResponse> where TResponse : IBuildResponse, new()
+    {
+        private TResponse Response { get; set; }
+
+        public void Build(byte[] responseBytes)
+        {
+            ByteBuffer buffer = ByteBuffer.Allocate();
+            buffer.Push(responseBytes);
+            Response = new TResponse();
+            Response.Build(buffer);
+        }
+    }
+
+}

+ 934 - 0
PLC.Siemens/O/SimenssPlc.cs

@@ -0,0 +1,934 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using PLC.Siemens.Communication;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Iso;
+using PLC.Siemens.Protocol.Negotiate;
+using PLC.Siemens.Protocol.ReadData;
+using PLC.Siemens.Protocol.Szl;
+using PLC.Siemens.ProtocolHandle;
+using PLC.Siemens.O;
+using System.Linq;
+
+namespace PLC.Siemens.O
+   
+{
+    public class SimenssPlc
+    {
+        public ConnectionType ConnectionType { get; private set; }
+        public ushort SrcRef { get; set; }
+        public ushort DstRef { get; set; }
+        public ushort SrcTSap { get; private set; }
+        public ushort DstTSap { get; private set; }
+        public ushort PduRequest { get; set; }
+        public int RecvTimeout { get; set; }
+        public int RemotePort { get; set; }
+        public int IsoPduSize { get; set; }
+        // ReSharper disable once InconsistentNaming
+        public string IP { get; private set; }
+
+        public bool Connected
+        {
+            get
+            {
+                if (_socket == null)
+                    return false;
+                return _socket.Connected;
+
+            }
+        }
+
+        private Action<PLC.Siemens.O.MessageEvent> _onMessageEvent;
+
+        private readonly IsoSocket _socket;
+
+        public SimenssPlc()
+        {
+            _socket = new IsoSocket();
+            PduRequest = 480;
+            IsoPduSize = 1024;
+            RecvTimeout = 3000;
+            RemotePort = 102;
+            SrcRef = 0x0001; // RFC0983 states that SrcRef and DetRef should be 0
+                             // and, in any case, they are ignored.
+                             // S7 instead requires a number != 0
+                             // Libnodave uses 0x0100
+                             // S7Manager uses 0x0D00
+                             // TIA Portal V12 uses 0x1D00
+                             // WinCC     uses 0x0300
+                             // Seems that every non zero value is good enough...
+            DstRef = 0x0000;
+            SrcTSap = 0x0100;
+            DstTSap = 0x0000; // It's filled by connection functions
+            ConnectionType = ConnectionType.PG; 
+        }
+        public SimenssPlc(string ip, int rack, int slot)
+        {
+            _socket = new IsoSocket();
+            PduRequest = 480*2;
+            IsoPduSize = 1024;
+            RecvTimeout = 3000;
+            RemotePort = 102;
+            SrcRef = 0x0001; // RFC0983 states that SrcRef and DetRef should be 0
+                             // and, in any case, they are ignored.
+                             // S7 instead requires a number != 0
+                             // Libnodave uses 0x0100
+                             // S7Manager uses 0x0D00
+                             // TIA Portal V12 uses 0x1D00
+                             // WinCC     uses 0x0300
+                             // Seems that every non zero value is good enough...
+            DstRef = 0x0000;
+            SrcTSap = 0x0100;
+            DstTSap = 0x0000; // It's filled by connection functions
+            ConnectionType = ConnectionType.PG;
+
+            var remoteTsap = (ushort)(((ushort)ConnectionType << 8) + (rack * 0x20) + slot);
+            SetConnectionParams(ip, SrcTSap, remoteTsap); 
+        }
+
+        #region 连接
+        public void RegisterMessageEvent(Action<MessageEvent> messageAction)
+        {
+            if (messageAction == null) throw new ArgumentNullException("messageAction");
+            _onMessageEvent = messageAction;
+        }
+
+        private void OnMessage(string method, string message)
+        {
+            if(_onMessageEvent!=null)
+                _onMessageEvent.Invoke(new MessageEvent(method, message));
+        }
+
+        public void SetConnectionType(ConnectionType connectionType)
+        {
+            ConnectionType = connectionType;
+        }
+
+        public void SetConnectionParams(string ip, ushort localTsap, ushort remoteTsap)
+        {
+            SrcTSap = localTsap;
+            DstTSap = remoteTsap;
+            IP = ip;
+        }
+
+        public bool ConnectTo(string ip, int rack, int slot)
+        {
+            var remoteTsap = (ushort)(((ushort)ConnectionType << 8) + (rack * 0x20) + slot);
+            SetConnectionParams(ip, SrcTSap, remoteTsap);
+            return Connect();
+        }
+
+        //暂时用系统SOCKET测试
+        private int _connecting;//连接中
+        private int _processing;//处理中
+        private string _processMehtod;
+        public bool Connect()
+        {
+            try
+            {
+                if (Interlocked.CompareExchange(ref _connecting, 1, 0) != 0)
+                    return false;
+                OnMessage("Connect", "正在连接");
+                if (!_socket.Connect(IP, 102))
+                    return false;
+                 
+                IsoControlPduHandle pduHandle = new IsoControlPduHandle()
+                {
+                    IsoPduSize = IsoPduSize,
+                    DstRef = DstRef,
+                    DstTSap = DstTSap,
+                    SrcRef = SrcRef,
+                    SrcTSap = SrcTSap
+                };
+                pduHandle.Handle(_socket);
+                if (pduHandle.ResultCode == ResultCode.OK)
+                {
+                    NegotiateHandle negotiateHandle = new NegotiateHandle() { PduRequest = PduRequest };
+                    negotiateHandle.Handle(_socket);
+                    if (negotiateHandle.ResultCode == ResultCode.OK)
+                    {
+                        PduRequest = negotiateHandle.Responses[0].Params.PduLength;
+                        return true;
+                    }
+                    else
+                    {
+                        OnMessage("Connect", "PDU握手失败");
+                    }
+                }
+                else
+                {
+                    OnMessage("Connect", "PDU握手失败");
+                }
+            }
+            catch (Exception ex)
+            {
+                OnMessage("Connect", ex.Message);
+            }
+            finally
+            {
+                Interlocked.CompareExchange(ref _connecting, 0, 1);
+            }
+            return false;
+        }
+        #endregion
+        
+        public bool GetLock(string method)
+        {
+            if (Interlocked.CompareExchange(ref _processing, 1, 0) != 0)
+            {
+                OnMessage(method, "执行失败,{_processMehtod}处理正在执行中");
+                return false;
+            }
+            _processMehtod = method;
+            return true;
+        }
+
+        public bool ReleaseLock()
+        {
+            _processMehtod = "";
+            return Interlocked.CompareExchange(ref _processing, 0, 1) == 0;
+        }
+
+        #region [System Info functions]
+
+        private SzlResponse ReadSzlHelper(SzlCmd szlcmd)
+        {
+            SzlHandle handle = new SzlHandle { SzlCmd = szlcmd };
+            handle.Handle(_socket);
+            return handle.SzlResponseFirst;
+        }
+        
+        public SzlResponse ReadSzl(SzlCmd szlcmd)
+        {
+            if (!GetLock("ReadSzl"))
+            {
+                return null;
+            }
+            try
+            {
+                SzlHandle handle = new SzlHandle { SzlCmd = szlcmd };
+                handle.Handle(_socket);
+
+                OnMessage("ReadSzl", "{handle.ResultCode}");
+                return handle.SzlResponseFirst;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("ReadSzl", ex.Message);
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+            return null;
+        }
+
+        public OrderInfo GetOrderCode()
+        {
+            OrderInfo orderCode = new OrderInfo();
+            if (!GetLock("GetOrderCode"))
+            {
+                return orderCode;
+            }
+            try
+            {
+                var aaaa = ReadSzlHelper(SzlInfo.OrderCode);
+
+                OnMessage("GetOrderCode", aaaa.Params.Err.ToString());
+                if (aaaa.Params.Err != ResultCode.OK)
+                    return orderCode;
+                orderCode.Code = Encoding.UTF8.GetString(aaaa.Data.Data, 2, 20);
+                orderCode.V1 = aaaa.Data.Data[aaaa.Data.Data.Length - 3];
+                orderCode.V2 = aaaa.Data.Data[aaaa.Data.Data.Length - 2];
+                orderCode.V3 = aaaa.Data.Data[aaaa.Data.Data.Length - 1];
+                return orderCode;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("GetOrderCode", ex.Message);
+                return orderCode;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public CpuInfo GetCpuInfo()
+        {
+            CpuInfo cpuInfo = new CpuInfo();
+            if (!GetLock("GetCpuInfo"))
+                return cpuInfo;
+            try
+            {
+                var aaaa = ReadSzlHelper(SzlInfo.CpuInfo);
+
+                OnMessage("GetCpuInfo", aaaa.Params.Err.ToString());
+                if (aaaa.Params.Err != ResultCode.OK)
+                    return cpuInfo;
+                cpuInfo.ModuleTypeName = Encoding.UTF8.GetString(aaaa.Data.Data, 172, 32).TrimEnd('\0');
+                cpuInfo.SerialNumber = Encoding.UTF8.GetString(aaaa.Data.Data, 138, 24).TrimEnd('\0');
+                cpuInfo.AsName = Encoding.UTF8.GetString(aaaa.Data.Data, 2, 24).TrimEnd('\0');
+                cpuInfo.Copyright = Encoding.UTF8.GetString(aaaa.Data.Data, 104, 26).TrimEnd('\0');
+                cpuInfo.ModuleName = Encoding.UTF8.GetString(aaaa.Data.Data, 36, 24).TrimEnd('\0');
+
+            }
+            catch (Exception ex)
+            {
+                OnMessage("GetCpuInfo", ex.Message);
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+            return cpuInfo;
+        }
+
+        public CpInfo GetCpInfo()
+        {
+            CpInfo info = new CpInfo();
+            if (!GetLock("GetCpuInfo"))
+                return info;
+
+            try
+            {
+                var aaaa = ReadSzlHelper(SzlInfo.CpInfo);
+                OnMessage("GetCpInfo", aaaa.Params.Err.ToString());
+                if (aaaa.Params.Err != ResultCode.OK)
+                    return info;
+                info.MaxPduLengt = DWordAt(aaaa.Data.Data, 2, 2);
+                //aaaa.ResDataFirst.Data[2] * 256 + aaaa.ResDataFirst.Data[3];
+                info.MaxConnections = DWordAt(aaaa.Data.Data, 4, 2);
+                // aaaa.ResDataFirst.Data[4] * 256 + aaaa.ResDataFirst.Data[5];
+                info.MaxMpiRate = DWordAt(aaaa.Data.Data, 6, 4);
+                //aaaa.ResDataFirst.Data[6] * 256 + aaaa.ResDataFirst.Data[7];
+                info.MaxBusRate = DWordAt(aaaa.Data.Data, 10, 4);
+                //aaaa.ResDataFirst.Data[10] * 256 + aaaa.ResDataFirst.Data[7];
+            }
+            catch (Exception ex)
+            {
+                OnMessage("GetCpInfo", ex.Message);
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+            return info;
+        }
+        #endregion
+
+        private int DWordAt(byte[] buffer, int index, int length)
+        {
+            int value = 0;
+            for (int i = 0; i < length; i++)
+            {
+                value = (value << 8) + buffer[index + i];
+            }
+
+            return value;
+        }
+
+        #region [Date/Time functions]
+        public DateTime GetS7DateTime()
+        {
+            if (!GetLock("GetS7DateTime"))
+                return DateTime.MinValue;
+            try
+            {
+                GetDateTimeHandle handle = new GetDateTimeHandle();
+                handle.Handle(_socket);
+                OnMessage("GetS7DateTime", handle.ResultCode.ToString());
+                if (handle.ResultCode == ResultCode.OK)
+                {
+                    return handle.Response.GetDateTime();
+                }
+                else
+                {
+                    return DateTime.MinValue;
+                }
+            }
+            catch (Exception ex)
+            {
+                OnMessage("GetS7DateTime", ex.Message);
+                return DateTime.MinValue;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool SetS7DateTime(DateTime curDateTime)
+        {
+            if (!GetLock("SetS7DateTime"))
+                return false;
+
+            try
+            {
+                SetDateTimeHandle handle = new SetDateTimeHandle { DateTime = curDateTime };
+                handle.Handle(_socket);
+
+                OnMessage("SetS7DateTime", handle.ResultCode.ToString());
+                return handle.ResultCode == ResultCode.OKFF;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("SetS7DateTime", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+        #endregion
+
+        #region [Control functions]
+        public S7CpuStatus GetPlcStatus()
+        {
+            S7CpuStatus status = S7CpuStatus.Unknown;
+            if (!GetLock("GetPlcStatus"))
+            {
+                return status;
+            }
+            try
+            {
+                SzlResponse first = ReadSzlHelper(SzlInfo.PlcStatus);
+                OnMessage("GetPlcStatus", first.Params.Err.ToString());
+
+                if (first.Params.Err == ResultCode.OK)
+                {
+                    status = (S7CpuStatus)first.Data.Data[3];
+                    if (status != S7CpuStatus.Run)
+                        status = S7CpuStatus.Stop;
+                }
+                return status;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("GetPlcStatus", ex.Message);
+                return status;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool PlcStop()
+        {
+            if (!GetLock("PlcStop"))
+                return false;
+
+            try
+            {
+                var handle = new PlcStopHandle();
+                handle.Handle(_socket);
+
+                OnMessage("PlcStop", handle.ResultCode.ToString());
+                return handle.ResultCode == ResultCode.OK;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("PlcStop", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool PlcHotStart()
+        {
+            if (!GetLock("PlcHotStart"))
+                return false;
+
+            try
+            {
+                var handle = new PlcHotStartHandle();
+                handle.Handle(_socket);
+
+                OnMessage("PlcHotStart", handle.ResultCode.ToString());
+                return handle.ResultCode == ResultCode.OK;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("PlcHotStart", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool PlcColdStart()
+        {
+            if (!GetLock("PlcColdStart"))
+                return false;
+
+            try
+            {
+                var handle = new PlcColdStartHandle();
+                handle.Handle(_socket);
+
+                OnMessage("PlcColdStart", handle.ResultCode.ToString());
+                return handle.ResultCode == ResultCode.OK;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("PlcColdStart", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool CopyRamToRom()
+        {
+            if (!GetLock("CopyRamToRom"))
+                return false;
+
+            try
+            {
+                var handle = new CopyRamToRomHandle();
+                handle.Handle(_socket);
+
+                OnMessage("CopyRamToRom", handle.ResultCode.ToString());
+                return handle.ResultCode == ResultCode.OK;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("CopyRamToRom", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool Compress()
+        {
+
+            if (!GetLock("Compress"))
+                return false;
+
+            try
+            {
+                var handle = new CompressHandle();
+                handle.Handle(_socket);
+
+                OnMessage("Compress", handle.Response.Header.Error.ToString());
+                return handle.ResultCode == ResultCode.OK;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("Compress", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+
+        }
+
+        #endregion
+
+        #region [Security functions]
+        public ProtectionInfo GetProtection()
+        {
+            ProtectionInfo info = new ProtectionInfo();
+            if (!GetLock("GetProtection"))
+            {
+                return info;
+            }
+            try
+            {
+                var sss = ReadSzlHelper(SzlInfo.Protection);
+
+                OnMessage("GetProtection", sss.Data.Ret.ToString());
+                if (sss.Params.Err != ResultCode.OK) return info;
+                info.SchSchal = (RWLevel)DWordAt(sss.Data.Data, 2, 2);
+                info.SchPar = (ProtectionLevel)DWordAt(sss.Data.Data, 4, 2);
+                info.SchRel = (CpuLevel)DWordAt(sss.Data.Data, 6, 2);
+                info.BartSch = (RunStatus)DWordAt(sss.Data.Data, 8, 2);
+                info.AnlSch = (StartupSwitch)DWordAt(sss.Data.Data, 10, 2);
+                return info;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("GetProtection", ex.Message);
+                return info;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool SetPwd(string password)
+        {
+            if (!GetLock("SetPwd")) return false;
+
+            try
+            {
+                var handle = new SetPasswordHandle { Password = password };
+                handle.Handle(_socket);
+
+                OnMessage("SetPwd", handle.Response.Params.Err.ToString());
+                return handle.ResultCode == ResultCode.OK;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("SetPwd", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool ClearPwd()
+        {
+            if (!GetLock("ClearPwd")) return false;
+
+            try
+            {
+                var handle = new SetPasswordHandle();
+                handle.Handle(_socket);
+
+                OnMessage("ClearPwd", handle.ResultCode.ToString());
+                return handle.ResultCode == ResultCode.OK;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("ClearPwd", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+        #endregion
+
+        #region [Blocks]
+        public ListBlocksHandle ListBlocks()
+        {
+            if (!GetLock("ListBlocks")) return null;
+            try
+            {
+                var handle = new ListBlocksHandle();
+                handle.Handle(_socket);
+                OnMessage("ListBlocks", handle.ResultCode.ToString());
+                return handle;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("ListBlocks", ex.Message);
+                return null;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public DataBlockOfTypeHandle ListBlocksOfType(BlockType blockType)
+        {
+            if (!GetLock("ListBlocksOfType")) return null;
+            try
+            {
+                var handle = new DataBlockOfTypeHandle();
+                handle.BlockType = blockType;
+                handle.Handle(_socket);
+                OnMessage("ListBlocksOfType", handle.ResultCode.ToString());
+                return handle;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("ListBlocksOfType", ex.Message);
+                return null;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public DataBlockInfoHandle BlockInfoGet(ushort db, BlockType blockType)
+        {
+            if (!GetLock("BlockInfoHandle")) return null;
+            try
+            {
+                var handle = new DataBlockInfoHandle();
+                handle.BlockType = blockType;
+                handle.DB = db;
+                handle.Handle(_socket);
+                OnMessage("BlockInfoHandle", handle.ResultCode.ToString());
+                return handle;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("BlockInfoHandle", ex.Message);
+                return null;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+
+        }
+        #endregion
+
+        object readlock = new object();
+        public DataItem ReadArea(AreaType area, ushort db, ushort start, ushort amount, DataType dataType)
+        {
+            lock (readlock)
+            {
+
+                if (!GetLock("ReadArea"))
+                {
+                    return null;
+                }
+                else
+                {
+
+                }
+                try
+                {
+                    var handle = new ReadHandle
+                    {
+                        PduLength = PduRequest,
+                        DataItem = new DataItem()
+                        {
+                            AreaType = area,
+                            Db = db,
+                            Start = start,
+                            Length = amount,
+                            DataType = dataType
+                        }
+                    };
+                    handle.Handle(_socket);
+                    OnMessage("ReadArea", handle.ResultCode.ToString());
+                    if (handle.ResultCode != ResultCode.OK)
+                    {
+                        throw new Exception(handle.ResultCode.ToString());
+                    }
+                    return handle.DataItem;
+                }
+                catch (Exception ex)
+                {
+                    OnMessage("ReadArea", ex.Message);
+                }
+                finally
+                {
+                    ReleaseLock();
+                }
+                return null;
+            }
+        }
+
+        public ReadMultiHandle ReadMulti(List<DataItem> items)
+        {
+            if (!GetLock("ReadMulti")) return null;
+            try
+            {
+                var handle = new ReadMultiHandle { DataItem = items };
+
+                handle.Handle(_socket);
+                OnMessage("ReadMulti", handle.ResultCode.ToString());
+
+                return handle;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("ReadMulti", ex.Message);
+                return null;
+            }
+            finally
+
+
+            {
+                ReleaseLock();
+            }
+        }
+
+        public bool WriteArea(AreaType area, ushort db, ushort start, ushort amount, DataType dataType, byte[] writeData)
+        {
+            if (IP == "192.168.0.120" && db == 520)
+            {
+                if (start % 36 == 16)
+                {
+                    if (writeData.Max() == 0)
+                    {
+
+                    }
+                }
+                else if (start % 36 == 0)
+                {
+                    if (writeData.Length > 16)
+                    {
+                        if (writeData.Skip(16).Take(2).Max() == 0)
+                        {
+
+                        }
+                    }
+                    else 
+                    { 
+                    
+                    }
+                }
+            }
+
+            if (!GetLock("WriteArea"))
+                return false;
+            try
+            {
+                var handle = new WriteHandle()
+                {
+                    PduLength = PduRequest,
+                    DataItem = new DataItem()
+                    {
+                        AreaType = area,
+                        Db = db,
+                        Start = start,
+                        Length = amount,
+                        DataType = dataType,
+                        Data = writeData
+                    }
+                };
+                OnMessage("WriteArea", handle.ResultCode.ToString());
+                handle.Handle(_socket);
+                return handle.ResultCode == ResultCode.OK|| handle.ResultCode == ResultCode.OKFF;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("WriteArea", ex.Message);
+                return false;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+
+        public WriteMultiHandle WriteMulti(List<DataItem> items)
+        {
+            if (!GetLock("WriteMulti")) return null;
+
+            try
+            {
+                var handle = new WriteMultiHandle { DataItem = items };
+
+                handle.Handle(_socket);
+                OnMessage("WriteMulti", handle.ResultCode.ToString());
+
+                return handle;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("WriteMulti", ex.Message);
+                return null;
+            }
+            finally
+            {
+                ReleaseLock();
+            }
+        }
+        
+        // ReSharper disable once InconsistentNaming
+        public DataItem DBGet(ushort db)
+        {
+            //if (!GetLock("DBGet")) return null;
+            try
+            {
+                var handle = new DataBlockInfoHandle();
+                handle.BlockType = BlockType.DB;
+                handle.DB = db;
+                handle.Handle(_socket);
+                if (handle.ResultCode == ResultCode.OK)
+                {
+                    var res = ReadArea(AreaType.DB, handle.DB, 0, handle.Response.Data.Mc7Len, DataType.Byte);
+                    if (res.Err == ResultCode.OKFF)
+                    {
+                        return res;
+                    }
+                    OnMessage("DBGet", "ReadArea:{handle.ResultCode}");
+                }
+                else
+                    OnMessage("DBGet", "RequestBlock:{handle.ResultCode}");
+
+            }
+            catch (Exception ex)
+            {
+                OnMessage("DBGet", ex.Message);
+            }
+            //finally
+            //{
+            //    ReleaseLock();
+            //}
+            return null;
+        }
+
+        // ReSharper disable once InconsistentNaming
+        public bool DBFill(ushort db, byte value)
+        {
+            try
+            {
+                var handle = new DataBlockInfoHandle();
+                handle.BlockType = BlockType.DB;
+                handle.DB = db;
+                handle.Handle(_socket);
+                if (handle.ResultCode == ResultCode.OK)
+                {
+                    byte[] datas = new byte[handle.Response.Data.Mc7Len];
+                    for (int i = 0; i < handle.Response.Data.Mc7Len; i++)
+                    {
+                        datas[i] = value;
+                    }
+                    return WriteArea(AreaType.DB, handle.DB, 0, handle.Response.Data.Mc7Len, DataType.Byte, datas);
+                }
+                else
+                    OnMessage("DBFill", "RequestBlock:{handle.ResultCode}");
+
+                return false;
+            }
+            catch (Exception ex)
+            {
+                OnMessage("DBFill", ex.Message);
+                return false;
+            }
+            //finally
+            //{
+            //    ReleaseLock();
+            //}
+        }
+
+        public bool UpLoad(BlockType blockType, ushort db, bool isFull)
+        {
+            UploadStartHandle handle = new UploadStartHandle();
+            handle.BlockType = blockType;
+            handle.BlockNum = db;
+
+            handle.Handle(_socket);
+            if (handle.ResultCode != ResultCode.OK)
+                return false;
+
+            UploadHandle handle1 = new UploadHandle();
+            handle1.UploadID = handle.Response.Params.UploadID;
+            handle1.Handle(_socket);
+
+
+            return false;
+        }
+    }
+}

+ 13 - 0
PLC.Siemens/O/StaticConst.cs

@@ -0,0 +1,13 @@
+namespace PLC.Siemens.O
+{
+    public static class StaticConst
+    {
+        public const byte IsoTcpVersion = 0x03;// RFC 1006
+        public const int IsoTcpPort = 102;// RFC 1006
+        public const int IsoInvalidHandle = 0;
+        public const int MaxTsapLength = 16;// Max Lenght for Src and Dst TSAP
+        public const int MaxIsoFragments = 64; // Max fragments
+        public const int IsoPayloadSize = 4096;// Iso telegram Buffer size
+        public const int NoError = 0;
+    }
+}

+ 206 - 0
PLC.Siemens/PLC.Siemens.csproj

@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{8F6C4A79-98EA-4019-B72A-29290328185A}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>PLC.Siemens</RootNamespace>
+    <AssemblyName>PLC.Siemens</AssemblyName>
+    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>..\DLL\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="O\ByteBuffer.cs" />
+    <Compile Include="Communication\IsoSocket.cs" />
+    <Compile Include="Core\Common\Argument.cs" />
+    <Compile Include="Core\Common\Constants.cs" />
+    <Compile Include="Core\Common\DefaultConvert.cs" />
+    <Compile Include="Core\Common\DelegateFactory.cs" />
+    <Compile Include="Core\Common\FlowControlUtil.cs" />
+    <Compile Include="Core\Common\IControlAble.cs" />
+    <Compile Include="Core\Common\IDisposableEx.cs" />
+    <Compile Include="Core\Common\ModelHandler.cs" />
+    <Compile Include="Core\Common\ObjectId.cs" />
+    <Compile Include="Core\Common\Rand.cs" />
+    <Compile Include="Core\Common\SingleBase.cs" />
+    <Compile Include="Core\Common\SystemTime.cs" />
+    <Compile Include="Core\Extension\ArrayExtension.cs" />
+    <Compile Include="Core\Extension\BitExtension.cs" />
+    <Compile Include="Core\Extension\ByteExtenstion.cs" />
+    <Compile Include="Core\Extension\CollectionExtension.cs" />
+    <Compile Include="Core\Extension\DateTimeExtension.cs" />
+    <Compile Include="Core\Extension\EncodingHelper.cs" />
+    <Compile Include="Core\Extension\EnumExtension.cs" />
+    <Compile Include="Core\Extension\GuidExtension.cs" />
+    <Compile Include="Core\Extension\IEnumerableExtensions.cs" />
+    <Compile Include="Core\Extension\ObjectExtension.cs" />
+    <Compile Include="Core\Extension\ReaderWriterLockSlimExtensions.cs" />
+    <Compile Include="Core\Extension\StringBuilderExtension.cs" />
+    <Compile Include="Core\Extension\StringHelper.cs" />
+    <Compile Include="Core\Extension\TypeExtension.cs" />
+    <Compile Include="O\HelperFunction.cs" />
+    <Compile Include="O\IBuildRequest.cs" />
+    <Compile Include="O\IBuildResponse.cs" />
+    <Compile Include="O\IIsoSender.cs" />
+    <Compile Include="O\MessageEvent.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ProtocolHandle\CompressHandle.cs" />
+    <Compile Include="ProtocolHandle\CopyRamToRomHandle.cs" />
+    <Compile Include="ProtocolHandle\DataBlockInfoHandle.cs" />
+    <Compile Include="ProtocolHandle\DataBlockOfTypeHandle.cs" />
+    <Compile Include="ProtocolHandle\GetDateTimeHandle.cs" />
+    <Compile Include="ProtocolHandle\IPacketHandle.cs" />
+    <Compile Include="ProtocolHandle\IsoControlPduHandle.cs" />
+    <Compile Include="ProtocolHandle\ListBlocksHandle.cs" />
+    <Compile Include="ProtocolHandle\NegotiateHandle.cs" />
+    <Compile Include="ProtocolHandle\PlcColdStartHandle.cs" />
+    <Compile Include="ProtocolHandle\PlcHotStartHandle.cs" />
+    <Compile Include="ProtocolHandle\PlcStopHandle.cs" />
+    <Compile Include="ProtocolHandle\ReadHandle.cs" />
+    <Compile Include="ProtocolHandle\ReadMultiHandle.cs" />
+    <Compile Include="ProtocolHandle\SetDateTimeHandle.cs" />
+    <Compile Include="ProtocolHandle\SetPasswordHandle.cs" />
+    <Compile Include="ProtocolHandle\SzlHandle.cs" />
+    <Compile Include="ProtocolHandle\UploadHandle.cs" />
+    <Compile Include="ProtocolHandle\UploadStartHandle.cs" />
+    <Compile Include="ProtocolHandle\WriteHandle.cs" />
+    <Compile Include="ProtocolHandle\WriteMultiHandle.cs" />
+    <Compile Include="Protocol\Common\AreaType.cs" />
+    <Compile Include="Protocol\Common\CodeControl.cs" />
+    <Compile Include="Protocol\Common\CodeError.cs" />
+    <Compile Include="Protocol\Common\ConnectionType.cs" />
+    <Compile Include="Protocol\Common\DataType.cs" />
+    <Compile Include="Protocol\Common\ErrorType.cs" />
+    <Compile Include="Protocol\Common\GrType.cs" />
+    <Compile Include="Protocol\Common\PduFuncType.cs" />
+    <Compile Include="Protocol\Common\PduKind.cs" />
+    <Compile Include="Protocol\Common\PduType.cs" />
+    <Compile Include="Protocol\Common\S7CpuStatus.cs" />
+    <Compile Include="Protocol\Common\S7OpType.cs" />
+    <Compile Include="Protocol\Common\TsType.cs" />
+    <Compile Include="Protocol\Control\CompressParamsRequest.cs" />
+    <Compile Include="Protocol\Control\CompressRequest.cs" />
+    <Compile Include="Protocol\Control\CopyRamToRomParamsRequest.cs" />
+    <Compile Include="Protocol\Control\CopyRamToRomRequest.cs" />
+    <Compile Include="Protocol\Control\PlcColdStartParamsRequest.cs" />
+    <Compile Include="Protocol\Control\PlcColdStartRequest.cs" />
+    <Compile Include="Protocol\Control\PlcHotStartParamsRequest.cs" />
+    <Compile Include="Protocol\Control\PlcHotStartRequest.cs" />
+    <Compile Include="Protocol\Control\PlcStopParamsRequest.cs" />
+    <Compile Include="Protocol\Control\PlcStopRequest.cs" />
+    <Compile Include="Protocol\DateTime\ControlResponse.cs" />
+    <Compile Include="Protocol\DateTime\DateTimeData.cs" />
+    <Compile Include="Protocol\DateTime\FunCtrlResponse.cs" />
+    <Compile Include="Protocol\DateTime\GetDateTimeRequest.cs" />
+    <Compile Include="Protocol\DateTime\GetDateTimeResponse.cs" />
+    <Compile Include="Protocol\DateTime\HandleRequest.cs" />
+    <Compile Include="Protocol\DateTime\HandleResponse.cs" />
+    <Compile Include="Protocol\DateTime\SetDateTimeRequest.cs" />
+    <Compile Include="Protocol\Header\CotpDataHeader.cs" />
+    <Compile Include="Protocol\Header\CotpHeader.cs" />
+    <Compile Include="Protocol\Header\HeaderPacket.cs" />
+    <Compile Include="Protocol\Header\HeaderPacketWithErrorCode.cs" />
+    <Compile Include="Protocol\Header\TpktHeader.cs" />
+    <Compile Include="Protocol\Iso\CoptParams.cs" />
+    <Compile Include="Protocol\Iso\IsoControlPdu.cs" />
+    <Compile Include="Protocol\Iso\IsoDataPdu.cs" />
+    <Compile Include="Protocol\ListBlocks\BlockItem.cs" />
+    <Compile Include="Protocol\ListBlocks\BlockItemListAll.cs" />
+    <Compile Include="Protocol\ListBlocks\BotItem.cs" />
+    <Compile Include="Protocol\ListBlocks\BotItemListAll.cs" />
+    <Compile Include="Protocol\ListBlocks\DataBlockInfoDataRequest.cs" />
+    <Compile Include="Protocol\ListBlocks\DataBlockInfoDataResponse.cs" />
+    <Compile Include="Protocol\ListBlocks\DataBlockInfoRequest.cs" />
+    <Compile Include="Protocol\ListBlocks\DataBlockInfoResponse.cs" />
+    <Compile Include="Protocol\ListBlocks\DataBlockOfTypeDataRequest.cs" />
+    <Compile Include="Protocol\ListBlocks\DataBlockOfTypeRequest.cs" />
+    <Compile Include="Protocol\ListBlocks\DataBlockOfTypeResponse.cs" />
+    <Compile Include="Protocol\ListBlocks\ListBlocksRequest.cs" />
+    <Compile Include="Protocol\ListBlocks\ListBlocksResponse.cs" />
+    <Compile Include="Protocol\Negotiate\NegotiateRequest.cs" />
+    <Compile Include="Protocol\Negotiate\NegotiateRequestParams.cs" />
+    <Compile Include="Protocol\Negotiate\NegotiateResponse.cs" />
+    <Compile Include="Protocol\Negotiate\NegotiateResponseParams.cs" />
+    <Compile Include="Protocol\ParamsRequest.cs" />
+    <Compile Include="Protocol\ParamsResponse.cs" />
+    <Compile Include="Protocol\ReadData\DataItem.cs" />
+    <Compile Include="Protocol\ReadData\ReadItemRequest.cs" />
+    <Compile Include="Protocol\ReadData\ReadItemResponse.cs" />
+    <Compile Include="Protocol\ReadData\ReadParamsRequest.cs" />
+    <Compile Include="Protocol\ReadData\ReadParamsResponse.cs" />
+    <Compile Include="Protocol\ReadData\ReadRequest.cs" />
+    <Compile Include="Protocol\ReadData\ReadResponse.cs" />
+    <Compile Include="Protocol\Security\DataSecurityRequest.cs" />
+    <Compile Include="Protocol\Security\SetPasswordRequest.cs" />
+    <Compile Include="Protocol\Szl\CpInfo.cs" />
+    <Compile Include="Protocol\Szl\CpuInfo.cs" />
+    <Compile Include="Protocol\Szl\OrderCode.cs" />
+    <Compile Include="Protocol\Szl\ProtectionInfo.cs" />
+    <Compile Include="Protocol\Szl\SzlDataNextResponse.cs" />
+    <Compile Include="Protocol\Szl\SzlDataRequest.cs" />
+    <Compile Include="Protocol\Szl\SzlDataResponse.cs" />
+    <Compile Include="Protocol\Szl\SzlInfo.cs" />
+    <Compile Include="Protocol\Szl\SzlNextResponse.cs" />
+    <Compile Include="Protocol\Szl\SzlRequest.cs" />
+    <Compile Include="Protocol\Szl\SzlResponse.cs" />
+    <Compile Include="Protocol\Upload\StartUploadParamsRequest.cs" />
+    <Compile Include="Protocol\Upload\StartUploadParamsResponse.cs" />
+    <Compile Include="Protocol\Upload\StartUploadRequest.cs" />
+    <Compile Include="Protocol\Upload\StartUploadResponse.cs" />
+    <Compile Include="Protocol\Upload\UploadDataResponse.cs" />
+    <Compile Include="Protocol\Upload\UploadParamsRequest.cs" />
+    <Compile Include="Protocol\Upload\UploadParamsResponse.cs" />
+    <Compile Include="Protocol\Upload\UploadRequest.cs" />
+    <Compile Include="Protocol\Upload\UploadResponse.cs" />
+    <Compile Include="Protocol\WriteData\WriteDataRequest.cs" />
+    <Compile Include="Protocol\WriteData\WriteItemRequest.cs" />
+    <Compile Include="Protocol\WriteData\WriteItemResponse.cs" />
+    <Compile Include="Protocol\WriteData\WriteParamsRequest.cs" />
+    <Compile Include="Protocol\WriteData\WriteParamsResponse.cs" />
+    <Compile Include="Protocol\WriteData\WriteRequest.cs" />
+    <Compile Include="Protocol\WriteData\WriteResponse.cs" />
+    <Compile Include="O\RequestHandle.cs" />
+    <Compile Include="O\ResponseHandle.cs" />
+    <Compile Include="O\SimenssPlc.cs" />
+    <Compile Include="O\StaticConst.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 36 - 0
PLC.Siemens/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("PLC.Siemens.O")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("PLC.Siemens.O")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+//将 ComVisible 设置为 false 将使此程序集中的类型
+//对 COM 组件不可见。  如果需要从 COM 访问此程序集中的类型,
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("8f6c4a79-98ea-4019-b72a-29290328185a")]
+
+// 程序集的版本信息由下列四个值组成: 
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
+// 方法是按如下所示使用“*”: :
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 30 - 0
PLC.Siemens/Protocol/Common/AreaType.cs

@@ -0,0 +1,30 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum AreaType
+    {
+        /// <summary>
+        /// DigitalInputs
+        /// </summary>
+        PE = 0x81,
+        /// <summary>
+        /// Digital Outputs
+        /// </summary>
+        PA = 0x82,
+        /// <summary>
+        /// Merkers
+        /// </summary>
+        MK = 0x83,
+        /// <summary>
+        /// DB
+        /// </summary>
+        DB = 0x84,
+        /// <summary>
+        /// Counters
+        /// </summary>
+        CT = 0x1C,
+        /// <summary>
+        /// Times
+        /// </summary>
+        TM = 0x1D,
+    }
+}

+ 14 - 0
PLC.Siemens/Protocol/Common/CodeControl.cs

@@ -0,0 +1,14 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum CodeControl
+    {
+        // Control codes
+        Unknown = 0,
+        ColdStart = 1,      // Cold start
+        WarmStart = 2,      // Warm start
+        Stop = 3,      // Stop
+        Compress = 4,      // Compress
+        CpyRamRom = 5,     // Copy Ram to Rom
+        InsDel = 6,      // Insert in working ram the block downloaded
+    }
+}

+ 35 - 0
PLC.Siemens/Protocol/Common/CodeError.cs

@@ -0,0 +1,35 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum ResultCode
+    {
+        /// <summary>
+        /// ²Ù×÷³É¹¦
+        /// </summary>
+        OKFF = 0xFF,
+
+        OK=00,
+
+        /// <summary>
+        /// PDUÈ·ÈÏʧ°Ü
+        /// </summary>
+        PduConfirmFault = 0x0999,
+        CannotCopyRamToRom = 0x8001,
+        ReadDataFalut = 0x9999,
+        Unknown = 0xFFFF,
+        
+        AddressOutOfRange = 0x0005,
+        InvalidTransportSize = 0x0006,
+        WriteDataSizeMismatch = 0x0007,
+        ItemNotAvailable = 0x000A,
+        ItemNotAvailable1 = 0xD209,
+        InvalidValue = 0xDC01,
+        NeedPassword = 0xD241,
+        InvalidPassword = 0xD602,
+        NoPasswordToClear = 0xD604,
+        NoPasswordToSet = 0xD605,
+        FunNotAvailable = 0x8104,
+        DataOverPdu = 0x8500,
+
+        
+    }
+}

+ 90 - 0
PLC.Siemens/Protocol/Common/ConnectionType.cs

@@ -0,0 +1,90 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum ConnectionType
+    {
+        // ReSharper disable once InconsistentNaming
+        PG = 1,
+        // ReSharper disable once InconsistentNaming
+        OP = 2,
+        // ReSharper disable once InconsistentNaming
+        S7BASIC = 3,
+    }
+
+    /// <summary>
+    /// 读写等级
+    /// </summary>
+    // ReSharper disable once InconsistentNaming
+    public enum RWLevel
+    {
+        Unknown = 0x00,
+        // ReSharper disable once InconsistentNaming
+        RW = 0x01,
+        R = 0x02,
+    }
+
+    public enum ProtectionLevel
+    {
+        NoPassword = 0x00,
+        SelectorPassword = 0x01,
+        WritePassword = 0x02,
+        ReadWritePasswod = 0x03
+    }
+
+    public enum CpuLevel
+    {
+        Unknown = 0x00,
+        AccessGrant = 0x01,
+        ReadOnly = 0x02,
+        ReadWritePasswod = 0x03
+    }
+
+    public enum RunStatus
+    {
+        Unknown = 0x00,
+        Run = 0x01,
+        RunP = 0x02,
+        Stop = 0x03,
+        Mres = 0x04,
+    }
+
+    public enum StartupSwitch
+    {
+        Unknown = 0x00,
+        Crst = 0x01,
+        Wrst = 0x02
+    }
+
+    public enum BlockType
+    {
+        OB = 0x38,
+        DB = 0x41,
+        SDB = 0x42,
+        FC = 0x43,
+        SFC = 0x44,
+        FB = 0x45,
+        SFB = 0x46,
+    }
+
+    public enum SubBlockType
+    {
+        OB = 0x08,
+        DB = 0x0A,
+        SDB = 0x0B,
+        FC = 0x0C,
+        SFC = 0x0D,
+        FB = 0x0E,
+        SFB = 0x0F
+    }
+
+    public enum BlockLangType
+    {
+        // Block languages
+        AWL = 0x01,
+        KOP = 0x02,
+        FUP = 0x03,
+        SCL = 0x04,
+        DB = 0x05,
+        GRAPH = 0x06,
+
+    }
+}

+ 31 - 0
PLC.Siemens/Protocol/Common/DataType.cs

@@ -0,0 +1,31 @@
+using System;
+
+namespace PLC.Siemens.Protocol.Common
+{
+    public class CSTypeAttribute : Attribute
+    {
+        public Type Type { get; set; }
+
+        public CSTypeAttribute(Type type)
+        {
+            this.Type = type;
+        }
+
+
+     
+
+    }
+    public enum DataType
+    {
+        Bit = 0x01,
+        Byte = 0x02,
+        Char = 0x03,
+        Word = 0x04,
+        Int = 0x05,
+        DWord = 0x06,
+        DInt = 0x07,
+        Real = 0x08,
+        Counter = 0x1C,
+        Timer = 0x1D,
+    } 
+}

+ 25 - 0
PLC.Siemens/Protocol/Common/ErrorType.cs

@@ -0,0 +1,25 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum ErrorType
+    {
+        NoError=0,
+        ErrIsoMask = 0x000F0000,
+        ErrIsoBase = 0x0000FFFF,
+
+        ErrIsoConnect = 0x00010000, // Connection error
+        ErrIsoDisconnect = 0x00020000, // Disconnect error
+        ErrIsoInvalidPdu = 0x00030000, // Bad format
+        ErrIsoInvalidDataSize = 0x00040000, // Bad Datasize passed to send/recv : buffer is invalid
+        ErrIsoNullPointer = 0x00050000, // Null passed as pointer
+        ErrIsoShortPacket = 0x00060000, // A short packet received
+        ErrIsoTooManyFragments = 0x00070000, // Too many packets without EoT flag
+        ErrIsoPduOverflow = 0x00080000, // The sum of fragments data exceded maximum packet size
+        ErrIsoSendPacket = 0x00090000, // An error occurred during send
+        ErrIsoRecvPacket = 0x000A0000, // An error occurred during recv
+        ErrIsoInvalidParams = 0x000B0000, // Invalid TSAP params
+        ErrIsoResvd1 = 0x000C0000, // Unassigned
+        ErrIsoResvd2 = 0x000D0000, // Unassigned
+        ErrIsoResvd3 = 0x000E0000, // Unassigned
+        ErrIsoResvd4 = 0x000F0000, // Unassigned
+    }
+}

+ 14 - 0
PLC.Siemens/Protocol/Common/GrType.cs

@@ -0,0 +1,14 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum GrType
+    {
+        GrProgrammer = 0x41,
+        GrCyclicData = 0x42,
+        GrBlocksInfo = 0x43,
+        GrSzl = 0x44,
+        GrPassword = 0x45,
+        GrBSend = 0x46,
+        GrClock = 0x47,
+        GrSecurity = 0x45,
+    }
+}

+ 32 - 0
PLC.Siemens/Protocol/Common/PduFuncType.cs

@@ -0,0 +1,32 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum PduFuncType
+    {
+        // PDU Functions
+        PduResponse = 0x02,   // Response (when error)
+        PduFuncRead = 0x04,   // Read area
+        PduFuncWrite = 0x05,   // Write area
+        PduNegotiate = 0xF0,   // Negotiate PDU length
+        PduStart = 0x28,   // CPU start
+        PduStop = 0x29,   // CPU stop
+        PduStartUpload = 0x1D,   // Start Upload
+        PduUpload = 0x1E,   // Upload
+        PduEndUpload = 0x1F,   // EndUpload
+        PduReqDownload = 0x1A,   // Start Download request
+        PduDownload = 0x1B,   // Download request
+        PduDownloadEnded = 0x1C,   // Download end request
+        PduControl = 0x28,   // Control (insert/delete..)
+
+        // PDU SubFunctions
+        SFunListAll = 0x01,   // List all blocks
+        SFunListBoT = 0x02,   // List Blocks of type
+        SFunBlkInfo = 0x03,   // Get Block info
+        SFunReadSzl = 0x01,   // Read SZL 
+        SFunReadClock = 0x01,   // Read Clock (Date and Time)
+        SFunSetClock = 0x02,   // Set Clock (Date and Time)
+        SFunEnterPwd = 0x01,   // Enter password    for this session
+        SFunCancelPwd = 0x02,   // Cancel password    for this session
+        SFunInsert = 0x50,   // Insert block
+        SFunDelete = 0x42,   // Delete block
+    }
+}

+ 12 - 0
PLC.Siemens/Protocol/Common/PduKind.cs

@@ -0,0 +1,12 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum PduKind
+    {
+        PkConnectionRequest,
+        PkDisconnectRequest,
+        PkEmptyFragment,
+        PkInvalidPdu,
+        PkUnrecognizedType,
+        PkValidData
+    }
+}

+ 46 - 0
PLC.Siemens/Protocol/Common/PduType.cs

@@ -0,0 +1,46 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum PduType
+    {
+        // PDU Type consts (Code + Credit)
+        ConnRequest = 0xE0,  // Connection request
+        ConnConfirm = 0xD0,  // Connection confirm
+        DiscRequest = 0x80,  // Disconnect request
+        DiscConfirm = 0xC0,  // Disconnect confirm
+        DataTransfer = 0xF0,  // Data transfer
+
+        EndTransfer = 0x80,  // End of Trasmission Packet (This packet is complete)
+        
+        // PDU Type
+        Request = 1,     // family request
+        Response = 3,      // family response
+        Userdata = 7,      // family user data
+        
+        // PDU Functions
+        PduResponse = 0x02,   // Response (when error)
+        PduFuncRead = 0x04,   // Read area
+        PduFuncWrite = 0x05,   // Write area
+        PduNegotiate = 0xF0,   // Negotiate PDU length
+        PduStart = 0x28,   // CPU start
+        PduStop = 0x29,   // CPU stop
+        PduStartUpload = 0x1D,   // Start Upload
+        PduUpload = 0x1E,   // Upload
+        PduEndUpload = 0x1F,   // EndUpload
+        PduReqDownload = 0x1A,   // Start Download request
+        PduDownload = 0x1B,   // Download request
+        PduDownloadEnded = 0x1C,   // Download end request
+        PduControl = 0x28,   // Control (insert/delete..)
+
+        // PDU SubFunctions
+        SFunListAll = 0x01,   // List all blocks
+        SFunListBoT = 0x02,   // List Blocks of type
+        SFunBlkInfo = 0x03,   // Get Block info
+        SFunReadSzl = 0x01,   // Read SZL 
+        SFunReadClock = 0x01,   // Read Clock (Date and Time)
+        SFunSetClock = 0x02,   // Set Clock (Date and Time)
+        SFunEnterPwd = 0x01,   // Enter password    for this session
+        SFunCancelPwd = 0x02,   // Cancel password    for this session
+        SFunInsert = 0x50,   // Insert block
+        SFunDelete = 0x42,   // Delete block
+    }
+}

+ 9 - 0
PLC.Siemens/Protocol/Common/S7CpuStatus.cs

@@ -0,0 +1,9 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum S7CpuStatus
+    {
+        Unknown = 0x00,
+        Run = 0x08,
+        Stop = 0x04,
+    }
+}

+ 35 - 0
PLC.Siemens/Protocol/Common/S7OpType.cs

@@ -0,0 +1,35 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum S7OpType
+    {
+        S7OpNone = 0,
+        S7OpReadArea = 1,
+        S7OpWriteArea = 2,
+        S7OpReadMultiVars = 3,
+        S7OpWriteMultiVars = 4,
+        S7OpDbGet = 5,
+        S7OpUpload = 6,
+        S7OpDownload = 7,
+        S7OpDelete = 8,
+        S7OpListBlocks = 9,
+        S7OpAgBlockInfo = 10,
+        S7OpListBlocksOfType = 11,
+        S7OpReadSzlList = 12,
+        S7OpReadSzl = 13,
+        S7OpGetDateTime = 14,
+        S7OpSetDateTime = 15,
+        S7OpGetOrderCode = 16,
+        S7OpGetCpuInfo = 17,
+        S7OpGetCpInfo = 18,
+        S7OpGetPlcStatus = 19,
+        S7OpPlcHotStart = 20,
+        S7OpPlcColdStart = 21,
+        S7OpCOpyRamToRom = 22,
+        S7OpCompress = 23,
+        S7OpPlcStOp = 24,
+        S7OpGetProtection = 25,
+        S7OpSetPassword = 26,
+        S7OpClearPassword = 27,
+        S7OpDbFill = 28
+    }
+}

+ 11 - 0
PLC.Siemens/Protocol/Common/TsType.cs

@@ -0,0 +1,11 @@
+namespace PLC.Siemens.Protocol.Common
+{
+    public enum TsType
+    {
+        TsResBit = 0x03,
+        TsResByte = 0x04,
+        TsResInt = 0x05,
+        TsResReal = 0x07,
+        TsResOctet = 0x09,
+    }
+}

+ 33 - 0
PLC.Siemens/Protocol/Control/CompressParamsRequest.cs

@@ -0,0 +1,33 @@
+using Core.Communication.Transport;
+using Core.Util.Extension;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class CompressParamsRequest
+    {
+        public byte Fun;     // pduControl 0x28
+        public byte[] Uk7; // unknown 7
+        public ushort Len1;   // Length part 1 : 0x00
+        public byte Len2;   // Length part 2 : 0x05
+        public char[] Cmd;  // ascii '_GARB'
+        public ushort PacketLength   = 0x10;
+        public void Build()
+        {
+            Fun = (byte)PduFuncType.PduControl;
+            Uk7 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD };
+            Len1 = 0x0000;
+            Len2 = 0x05;
+            Cmd = new[] { '_', 'G', 'A', 'R', 'B' };
+        }
+
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Fun);
+            buffer.Push(Uk7);
+            buffer.Push(Len1);
+            buffer.Push(Len2);
+            Cmd.ForEach(t => buffer.Push((byte)t));
+        }
+    }
+}

+ 33 - 0
PLC.Siemens/Protocol/Control/CompressRequest.cs

@@ -0,0 +1,33 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class CompressRequest:IBuildRequest
+    {
+        public HeaderPacket Header { get; set; }
+        public CompressParamsRequest Params { get; set; }
+
+        public CompressRequest()
+        {
+            Header = new HeaderPacket();
+            Params = new CompressParamsRequest();
+        }
+
+        public void Build()
+        {
+            Header.Build(PduType.Request, Params.PacketLength, 0x0000);
+            Params.Build();
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            ByteBuffer buffer = ByteBuffer.Allocate();
+            Header.GetByteBuffer(buffer);
+            Params.GetByteBuffer(buffer);
+            return buffer;
+        }
+    }
+}

+ 37 - 0
PLC.Siemens/Protocol/Control/CopyRamToRomParamsRequest.cs

@@ -0,0 +1,37 @@
+using Core.Communication.Transport;
+using Core.Util.Extension;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class CopyRamToRomParamsRequest
+    {
+        public byte Fun { get; set; }     // pduControl 0x28
+        public byte[] Uk7 { get; set; }   // unknown 7
+        public ushort Len1 { get; set; }     // Length part 1 : 0x0002
+        public ushort SFun { get; set; }      // 'EP' 0x4550
+        public byte Len2 { get; set; }     // Length part 2 : 0x05
+        public char[] Cmd { get; set; }    // ascii '_MODU' 5
+        public ushort PacketLength   = 0x12;
+
+        public void Build()
+        {
+            Fun = (byte)PduFuncType.PduControl;
+            Uk7 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD };
+            Len1 = 0x0002;
+            SFun = 0x4550;
+            Len2 = 0x05;
+            Cmd = new[] { '_', 'M', 'O', 'D', 'U' };
+        }
+
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Fun);
+            buffer.Push(Uk7);
+            buffer.Push(Len1);
+            buffer.Push(SFun);
+            buffer.Push(Len2);
+            Cmd.ForEach(t => buffer.Push((byte)t));
+        }
+    }
+}

+ 33 - 0
PLC.Siemens/Protocol/Control/CopyRamToRomRequest.cs

@@ -0,0 +1,33 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class CopyRamToRomRequest:IBuildRequest
+    {
+        public HeaderPacket Header { get; set; }
+        public CopyRamToRomParamsRequest Params { get; set; }
+
+        public CopyRamToRomRequest()
+        {
+            Header = new HeaderPacket();
+            Params=new CopyRamToRomParamsRequest();
+        }
+
+        public void Build()
+        {
+            Header.Build(PduType.Request, Params.PacketLength, 0x0000);
+            Params.Build();
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            ByteBuffer buffer = ByteBuffer.Allocate();
+            Header.GetByteBuffer(buffer);
+            Params.GetByteBuffer(buffer);
+            return buffer;
+        }
+    }
+}

+ 37 - 0
PLC.Siemens/Protocol/Control/PlcColdStartParamsRequest.cs

@@ -0,0 +1,37 @@
+using Core.Communication.Transport;
+using Core.Util.Extension;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class PlcColdStartParamsRequest
+    {
+        public byte Fun;     // start 0x28
+        public byte[] Uk7; // unknown 7
+        public ushort Len1;   // Length part 1 : 0x0002
+        public ushort SFun;    // 'C ' 0x4320
+        public byte Len2;   // Length part 2 : 0x09
+        public char[] Cmd;  // ascii 'P_PROGRAM'
+        public ushort PacketLength   = 0x16;
+
+        public void Build()
+        {
+            Fun = (byte)PduFuncType.PduStart;
+            Uk7 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD };
+            Len1 = 0x0002;
+            SFun = 0x4320;
+            Len2 = 0x09;
+            Cmd = new[] { 'P', '_', 'P', 'R', 'O', 'G', 'R', 'A', 'M' };
+        }
+
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Fun);
+            buffer.Push(Uk7);
+            buffer.Push(Len1);
+            buffer.Push(SFun);
+            buffer.Push(Len2);
+            Cmd.ForEach(t => buffer.Push((byte)t));
+        }
+    }
+}

+ 33 - 0
PLC.Siemens/Protocol/Control/PlcColdStartRequest.cs

@@ -0,0 +1,33 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class PlcColdStartRequest:IBuildRequest
+    {
+        public HeaderPacket Header { get; set; }
+        public PlcColdStartParamsRequest Params { get; set; }
+
+        public PlcColdStartRequest()
+        {
+            Header = new HeaderPacket();
+            Params = new PlcColdStartParamsRequest();
+        }
+
+        public void Build()
+        {
+            Header.Build(PduType.Request, Params.PacketLength, 0x0000);
+            Params.Build();
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            ByteBuffer buffer = ByteBuffer.Allocate();
+            Header.GetByteBuffer(buffer);
+            Params.GetByteBuffer(buffer);
+            return buffer;
+        }
+    }
+}

+ 37 - 0
PLC.Siemens/Protocol/Control/PlcHotStartParamsRequest.cs

@@ -0,0 +1,37 @@
+using Core.Communication.Transport;
+using Core.Util.Extension;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class PlcHotStartParamsRequest
+    {
+        public byte Fun;     // start 0x28
+        public byte[] Uk7; // unknown 7
+        public ushort Len1;   // Length part 1 : 0x0002
+        //public ushort SFun;    // 'C ' 0x4320
+        public byte Len2;   // Length part 2 : 0x09
+        public char[] Cmd;  // ascii 'P_PROGRAM'
+        public ushort PacketLength   = 0x14;
+
+        public void Build()
+        {
+            Fun = (byte)PduFuncType.PduStart;
+            Uk7 = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD };
+            Len1 = 0x0000;
+            //SFun = 0x00;
+            Len2 = 0x09;
+            Cmd = new[] { 'P', '_', 'P', 'R', 'O', 'G', 'R', 'A', 'M' };
+        }
+
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Fun);
+            buffer.Push(Uk7);
+            buffer.Push(Len1);
+            //buffer.Push(SFun);
+            buffer.Push(Len2);
+            Cmd.ForEach(t => buffer.Push((byte)t));
+        }
+    }
+}

+ 33 - 0
PLC.Siemens/Protocol/Control/PlcHotStartRequest.cs

@@ -0,0 +1,33 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class PlcHotStartRequest:IBuildRequest
+    {
+        public HeaderPacket Header { get; set; }
+        public PlcHotStartParamsRequest Params { get; set; }
+
+        public PlcHotStartRequest()
+        {
+            Header = new HeaderPacket();
+            Params = new PlcHotStartParamsRequest();
+        }
+
+        public void Build()
+        {
+            Header.Build(PduType.Request, Params.PacketLength, 0x0000);
+            Params.Build();
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            ByteBuffer buffer = ByteBuffer.Allocate();
+            Header.GetByteBuffer(buffer);
+            Params.GetByteBuffer(buffer);
+            return buffer;
+        }
+    }
+}

+ 31 - 0
PLC.Siemens/Protocol/Control/PlcStopParamsRequest.cs

@@ -0,0 +1,31 @@
+using Core.Communication.Transport;
+using Core.Util.Extension;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class PlcStopParamsRequest
+    {
+        public byte Fun { get; set; }     // stop 0x29
+        public byte[] Uk5 { get; set; } // unknown 5 bytes 0x00
+        public byte Len2 { get; set; }   // Length part 2 : 0x09
+        public char[] Cmd { get; set; }  // ascii 'P_PROGRAM' 0x09
+        public ushort PacketLength { get{return 0x10;} } 
+
+        public void Build()
+        {
+            Fun = (byte) PduFuncType.PduStop;
+            Uk5 = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00};
+            Len2 = 0x09;
+            Cmd = new[] {'P', '_', 'P', 'R', 'O', 'G', 'R', 'A', 'M'};
+        }
+
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Fun);
+            buffer.Push(Uk5);
+            buffer.Push(Len2);
+            Cmd.ForEach(t=>buffer.Push((byte)t));
+        }
+    }
+}

+ 33 - 0
PLC.Siemens/Protocol/Control/PlcStopRequest.cs

@@ -0,0 +1,33 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.Control
+{
+    public class PlcStopRequest:IBuildRequest
+    {
+        public HeaderPacket Header { get; set; }
+        public PlcStopParamsRequest Params { get; set; }
+
+        public PlcStopRequest()
+        {
+            Header = new HeaderPacket();
+            Params = new PlcStopParamsRequest();
+        }
+
+        public void Build()
+        {
+            Header.Build(PduType.Request, Params.PacketLength, 0x0000);
+            Params.Build();
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            ByteBuffer buffer = ByteBuffer.Allocate();
+            Header.GetByteBuffer(buffer);
+            Params.GetByteBuffer(buffer);
+            return buffer;
+        }
+    }
+}

+ 23 - 0
PLC.Siemens/Protocol/DateTime/ControlResponse.cs

@@ -0,0 +1,23 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.DateTime
+{
+    public class ControlResponse : IBuildResponse
+    {
+        public HeaderPacketWithErrorCode Header { get; set; }
+        public FunCtrlResponse Params { get; set; }
+
+        public ControlResponse()
+        {
+            Header = new HeaderPacketWithErrorCode();
+            Params = new FunCtrlResponse();
+        }
+        public void Build(ByteBuffer buffer)
+        {
+            Header.Build(buffer);
+            Params.Build(buffer);
+        }
+    }
+}

+ 62 - 0
PLC.Siemens/Protocol/DateTime/DateTimeData.cs

@@ -0,0 +1,62 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.DateTime
+{
+    public class DateTimeData
+    {
+        public byte Ret { get; set; }//0xFF
+        public byte Size { get; set; }
+        public ushort Length { get; set; }
+        public byte Rsvd { get; set; }
+        public byte HiYear { get; set; }
+        public byte[] Time { get; set; }//³¤¶È8
+        public ushort PacketLength { get { return 0x0E; } }
+        public DateTimeData()
+        {
+            Time = new byte[8];
+        }
+
+        public void Build(ByteBuffer buffer)
+        {
+            Ret = buffer.PopByte();
+            Size = buffer.PopByte();
+            Length = buffer.PopUshort();
+            Rsvd = buffer.PopByte();
+            HiYear = buffer.PopByte();
+            Time = buffer.PopBytes(8);
+        }
+
+        public void Build(System.DateTime dt)
+        {
+            Ret = 0xFF;
+            Size = (byte) TsType.TsResOctet;
+            Length = 0x000A;
+            Rsvd = 0x00;
+            HiYear = 0x19; // *must* be 19 tough it's not the Hi part of the year...
+            
+            Time[0] = WordToBcd((ushort)(dt.Year - 2000));
+            Time[1] = WordToBcd((ushort)(dt.Month));
+            Time[2] = WordToBcd((ushort)(dt.Day));
+            Time[3] = WordToBcd((ushort)(dt.Hour));
+            Time[4] = WordToBcd((ushort)(dt.Minute));
+            Time[5] = WordToBcd((ushort)(dt.Second));
+            Time[6] = 0x00;
+            Time[7] = WordToBcd((ushort)(dt.DayOfWeek));
+        }
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Ret);
+            buffer.Push(Size);
+            buffer.Push(Length);
+            buffer.Push(Rsvd);
+            buffer.Push(HiYear);
+            buffer.Push(Time);
+        }
+
+        private static byte WordToBcd(ushort value)
+        {
+            return (byte)(((value / 10) << 4) | (value % 10));
+        }
+    }
+}

+ 16 - 0
PLC.Siemens/Protocol/DateTime/FunCtrlResponse.cs

@@ -0,0 +1,16 @@
+using Core.Communication.Transport;
+
+namespace PLC.Siemens.Protocol.DateTime
+{
+    public class FunCtrlResponse
+    {
+        public byte Fun;
+        public byte Para;
+
+        public void Build(ByteBuffer buffer)
+        {
+            Fun= buffer.PopByte();
+            Para = buffer.PopByte();
+        }
+    }
+}

+ 36 - 0
PLC.Siemens/Protocol/DateTime/GetDateTimeRequest.cs

@@ -0,0 +1,36 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.DateTime
+{
+    public class GetDateTimeRequest:IBuildRequest
+    {
+        public HeaderPacket Header { get; set; }
+        public ParamsRequest Params { get; set; }
+        public uint Data { get; set; }
+
+        public GetDateTimeRequest()
+        {
+            Header = new HeaderPacket();
+            Params = new ParamsRequest();
+        }
+
+        public void Build()
+        {
+            Header.Build(PduType.Userdata, Params.PacketLength, 4);
+            Params.Build(true, 0x00, GrType.GrClock, PduFuncType.SFunReadClock);
+            Data = 0x0A000000;
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            ByteBuffer buffer = ByteBuffer.Allocate();
+            Header.GetByteBuffer(buffer);
+            Params.GetByteBuffer(buffer);
+            buffer.Push(Data);
+            return buffer;
+        }
+    }
+}

+ 50 - 0
PLC.Siemens/Protocol/DateTime/GetDateTimeResponse.cs

@@ -0,0 +1,50 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.DateTime
+{
+    public class GetDateTimeResponse :IBuildResponse
+    {
+        public HeaderPacket Header { get; set; }
+        public ParamsResponse Params { get; set; }
+        public DateTimeData Data { get; set; }
+
+        public GetDateTimeResponse()
+        {
+            Header = new HeaderPacket();
+            Params = new ParamsResponse();
+            Data = new DateTimeData();
+        }
+
+
+        public void Build(ByteBuffer buffer)
+        {
+            Header.Build(buffer);
+            Params.Build(buffer);
+            Data.Build(buffer);
+        }
+
+
+        private int BcDtoByte(byte B)
+        {
+            return ((B >> 4) * 10) + (B & 0x0F);
+        }
+
+        public System.DateTime GetDateTime()
+        {
+            var year = BcDtoByte(Data.Time[0]);
+            if (year < 90)
+                year = year + 2000;
+            var mon = BcDtoByte(Data.Time[1]);
+            var day = BcDtoByte(Data.Time[2]);
+            var hour = BcDtoByte(Data.Time[3]);
+            var min = BcDtoByte(Data.Time[4]);
+            var sec = BcDtoByte(Data.Time[5]);
+            var wday = (Data.Time[7] & 0x0F);
+            System.DateTime dt = new System.DateTime(year, mon, day, hour, min, sec, wday);
+
+            return dt;
+        }
+    }
+}

+ 10 - 0
PLC.Siemens/Protocol/DateTime/HandleRequest.cs

@@ -0,0 +1,10 @@
+using PLC.Siemens.Protocol.Header;
+
+namespace PLC.Siemens.Protocol.DateTime
+{
+    public class HandleRequest
+    {
+        public HeaderPacket Header { get; set; }
+        public ParamsResponse Params { get; set; }
+    }
+}

+ 26 - 0
PLC.Siemens/Protocol/DateTime/HandleResponse.cs

@@ -0,0 +1,26 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.DateTime
+{
+    public class HandleResponse<TResponseHeader>:IBuildResponse  where TResponseHeader : HeaderPacket, new()
+    {
+        public TResponseHeader Header { get; set; }
+        public ParamsResponse Params { get; set; }
+        public byte[] Data { get; set; }
+
+        public HandleResponse()
+        {
+            Header = new TResponseHeader();
+            Params = new ParamsResponse();
+        }
+
+        public void Build(ByteBuffer buffer)
+        {
+            Header.Build(buffer);
+            Params.Build(buffer);
+            Data = buffer.PopBytes(Header.DataLen);
+        }
+    }
+}

+ 39 - 0
PLC.Siemens/Protocol/DateTime/SetDateTimeRequest.cs

@@ -0,0 +1,39 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.DateTime
+{
+    public class SetDateTimeRequest:IBuildRequest
+    {
+        public HeaderPacket Header { get; set; }
+        public ParamsRequest Params { get; set; }
+        public DateTimeData Data { get; set; }
+
+        public System.DateTime DateTime { get; set; }
+
+        public SetDateTimeRequest()
+        {
+            Header = new HeaderPacket();
+            Params = new ParamsRequest();
+            Data = new DateTimeData();
+        }
+
+        public void Build()
+        {
+            Header.Build(PduType.Userdata, Params.PacketLength, Data.PacketLength);
+            Params.Build(true, 0x00, GrType.GrClock, PduFuncType.SFunSetClock);
+            Data.Build(DateTime);
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            ByteBuffer buffer = ByteBuffer.Allocate();
+            Header.GetByteBuffer(buffer);
+            Params.GetByteBuffer(buffer);
+            Data.GetByteBuffer(buffer);
+            return buffer;
+        }
+    }
+}

+ 21 - 0
PLC.Siemens/Protocol/Header/CotpDataHeader.cs

@@ -0,0 +1,21 @@
+using Core.Communication.Transport;
+
+namespace PLC.Siemens.Protocol.Header
+{
+    public class CotpDataHeader
+    {
+        public byte Length { get; set; }   // Header length : 3 for this header
+        public byte PduType { get; set; }   // 0xF0 for this header
+        public byte EoTNum { get; set; }   // EOT (bit 7) + PDU Number (bits 0..6)
+                                           // EOT = 1 -> End of Trasmission Packet (This packet is complete)
+                                           // PDU Number : Always 0
+
+        public ushort PacketLength { get{return 0x03;} } 
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Length);
+            buffer.Push(PduType);
+            buffer.Push(EoTNum);
+        }
+    }
+}

+ 85 - 0
PLC.Siemens/Protocol/Header/CotpHeader.cs

@@ -0,0 +1,85 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Iso;
+
+namespace PLC.Siemens.Protocol.Header
+{
+    public class CotpHeader
+    {
+        public byte Length { get; set; }     // Header length : initialized to 6 (length without params - 1)
+        // descending classes that add values in params field must update it.
+        public byte PduType { get; set; }    // 0xE0 Connection request
+        // 0xD0 Connection confirm
+        // 0x80 Disconnect request
+        // 0xDC Disconnect confirm
+        public ushort DstRef { get; set; }     // Destination reference : Always 0x0000
+        public ushort SrcRef { get; set; }      // Source reference : Always 0x0000
+        public byte CoR { get; set; }       // If the telegram is used for Connection request/Confirm,
+        // the meaning of this field is CLASS+OPTION :
+        //   Class (High 4 bits) + Option (Low 4 bits)
+        //   Class : Always 4 (0100) but is ignored in input (RFC States this)
+        //   Option : Always 0, also this in ignored.
+        // If the telegram is used for Disconnect request,
+        // the meaning of this field is REASON :
+        //    1     Congestion at TSAP
+        //    2     Session entity not attached to TSAP
+        //    3     Address unknown (at TCP connect time)
+        //  128+0   Normal disconnect initiated by the session
+        //          entity.
+        //  128+1   Remote transport entity congestion at connect
+        //          request time
+        //  128+3   Connection negotiation failed
+        //  128+5   Protocol Error
+        //  128+8   Connection request refused on this network
+        //          connection
+        // Parameter data : depending on the protocol implementation.
+        // ISO 8073 define several type of parameters, but RFC 1006 recognizes only
+        // TSAP related parameters and PDU size.  See RFC 0983 for more details.
+        public CoptParams Params { get; set; }
+        /* Other params not used here, list only for completeness
+            ACK_TIME     	   = 0x85,  1000 0101 Acknowledge Time
+            RES_ERROR    	   = 0x86,  1000 0110 Residual Error Rate
+            PRIORITY           = 0x87,  1000 0111 Priority
+            TRANSIT_DEL  	   = 0x88,  1000 1000 Transit Delay
+            THROUGHPUT   	   = 0x89,  1000 1001 Throughput
+            SEQ_NR       	   = 0x8A,  1000 1010 Subsequence Number (in AK)
+            REASSIGNMENT 	   = 0x8B,  1000 1011 Reassignment Time
+            FLOW_CNTL    	   = 0x8C,  1000 1100 Flow Control Confirmation (in AK)
+            TPDU_SIZE    	   = 0xC0,  1100 0000 TPDU Size
+            SRC_TSAP     	   = 0xC1,  1100 0001 TSAP-ID / calling TSAP ( in CR/CC )
+            DST_TSAP     	   = 0xC2,  1100 0010 TSAP-ID / called TSAP
+            CHECKSUM     	   = 0xC3,  1100 0011 Checksum
+            VERSION_NR   	   = 0xC4,  1100 0100 Version Number
+            PROTECTION   	   = 0xC5,  1100 0101 Protection Parameters (user defined)
+            OPT_SEL            = 0xC6,  1100 0110 Additional Option Selection
+            PROTO_CLASS  	   = 0xC7,  1100 0111 Alternative Protocol Classes
+            PREF_MAX_TPDU_SIZE = 0xF0,  1111 0000
+            INACTIVITY_TIMER   = 0xF2,  1111 0010
+            ADDICC             = 0xe0   1110 0000 Additional Information on Connection Clearing
+        */
+
+        public CotpHeader()
+        {
+            Params = new CoptParams();
+        }
+
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Length);
+            buffer.Push(PduType);
+            buffer.Push(DstRef);
+            buffer.Push(SrcRef);
+            buffer.Push(CoR);
+            Params.GetByteBuffer(buffer);
+        }
+
+        public void InitByBuffer(ByteBuffer buffer)
+        {
+            Length = buffer.PopByte();
+            PduType = buffer.PopByte();
+            DstRef = buffer.PopUshort();
+            SrcRef = buffer.PopUshort();
+            CoR = buffer.PopByte();
+            Params.InitByBuffer(buffer);
+        }
+    }
+}

+ 54 - 0
PLC.Siemens/Protocol/Header/HeaderPacket.cs

@@ -0,0 +1,54 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.Header
+{
+    public class HeaderPacket
+    {
+        public byte P { get; set; }        // Telegram ID, always 32
+        public byte PduType { get; set; }  // Header type 1 or 7
+        public ushort AbEx { get; set; }    // AB currently unknown, maybe it can be used for long numbers.
+        public ushort Sequence { get; set; }// Message ID. This can be used to make sure a received answer
+        public ushort ParLen { get; set; }   // Length of parameters which follow this header
+        public ushort DataLen { get; set; }  // Length of data which follow the parameters
+        public ushort PacketLength { get{return 0x0A;} } 
+
+        public virtual void Build(ByteBuffer buffer)
+        {
+            P = buffer.PopByte();
+            PduType = buffer.PopByte();
+            AbEx = buffer.PopUshort();
+            Sequence = buffer.PopUshort();
+            ParLen = buffer.PopUshort(); //ReqFunNegotiateParams自身数据长度 不包含head的
+            DataLen = buffer.PopUshort();
+        }
+
+        public void Build(PduType pduType, ushort parLen, ushort dataLen)
+        {
+            P = 0x32;
+            PduType = (byte)pduType;
+            AbEx = 0x0000;
+            Sequence = GetNextWord();
+            ParLen = parLen;
+            DataLen = dataLen;
+        }
+
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(P);
+            buffer.Push(PduType);
+            buffer.Push(AbEx);
+            buffer.Push(Sequence);
+            buffer.Push(ParLen);
+            buffer.Push(DataLen);
+        }
+
+        private static ushort _cntword = 1;
+        protected static ushort GetNextWord()
+        {
+            if (_cntword == 0xFFFF)
+                _cntword = 1;
+            return _cntword++;
+        }
+    }
+}

+ 21 - 0
PLC.Siemens/Protocol/Header/HeaderPacketWithErrorCode.cs

@@ -0,0 +1,21 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.Header
+{
+    public class HeaderPacketWithErrorCode : HeaderPacket
+    {
+        public ResultCode Error { get; set; }    // Error code
+
+        public override void Build(ByteBuffer buffer)
+        {
+            P = buffer.PopByte();
+            PduType = buffer.PopByte();
+            AbEx = buffer.PopUshort();
+            Sequence = buffer.PopUshort();
+            ParLen = buffer.PopUshort();
+            DataLen = buffer.PopUshort();
+            Error = (ResultCode)buffer.PopUshort();
+        }
+    }
+}

+ 27 - 0
PLC.Siemens/Protocol/Header/TpktHeader.cs

@@ -0,0 +1,27 @@
+using Core.Communication.Transport;
+
+namespace PLC.Siemens.Protocol.Header
+{
+    // TPKT Header - ISO on TCP - RFC 1006 (4 bytes)
+    // Packet length : min 7 max 65535
+    public class TpktHeader
+    {
+        public byte Version { get; set; }   // Always 3 for RFC 1006
+        public byte Reserved { get; set; }  // 0
+        public ushort Length { get; set; } //整个数据包的长度
+        public ushort PacketLength { get{return 0x04;} } 
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(Version);
+            buffer.Push(Reserved);
+            buffer.Push(Length);
+        }
+
+        public void InitByBuffer(ByteBuffer buffer)
+        {
+            Version = buffer.PopByte();
+            Reserved = buffer.PopByte();
+            Length = buffer.PopUshort();
+        }
+    }
+}

+ 37 - 0
PLC.Siemens/Protocol/Iso/CoptParams.cs

@@ -0,0 +1,37 @@
+using Core.Communication.Transport;
+
+namespace PLC.Siemens.Protocol.Iso
+{
+    public class CoptParams
+    {
+        public byte PduSizeCode { get; set; }
+        public byte PduSizeLen { get; set; }
+        public byte PduSizeVal { get; set; }
+        public byte[] Tsap { get; set; } 
+        public int TsapLenth { get; set; }
+
+        public CoptParams()
+        {
+            Tsap = new byte[245]; // We don't know in advance these fields....
+        }
+
+        public void GetByteBuffer(ByteBuffer buffer)
+        {
+            buffer.Push(PduSizeCode);
+            buffer.Push(PduSizeLen);
+            buffer.Push(PduSizeVal);
+            buffer.Push(Tsap, 0, TsapLenth);
+        }
+
+        public void InitByBuffer(ByteBuffer buffer)
+        {
+            PduSizeCode = buffer.PopByte();
+            PduSizeLen = buffer.PopByte();
+            PduSizeVal = buffer.PopByte();
+            TsapLenth = buffer.WriteIndex - buffer.ReadIndex;
+            Tsap = buffer.PopBytes(TsapLenth);
+        }
+    }
+
+    // COTP Header for DATA EXCHANGE
+}

+ 115 - 0
PLC.Siemens/Protocol/Iso/IsoControlPdu.cs

@@ -0,0 +1,115 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+using PLC.Siemens.O;
+
+namespace PLC.Siemens.Protocol.Iso
+{
+    public class IsoControlPdu :IBuildRequest,IBuildResponse
+    {
+        public TpktHeader Tpkt { get; set; } // TPKT Header
+        public CotpHeader Cotp { get; set; } // COPT Header for CONNECTION stuffs
+
+        
+        public int IsoPduSize { get; set; }
+        public ushort SrcRef { get; set; }
+        public ushort DstRef { get; set; }
+        public ushort SrcTSap { get; set; }
+        public ushort DstTSap { get; set; }
+        /// <summary>
+        /// 该报发送长度和接受长度都应该为22
+        /// </summary>
+        public IsoControlPdu()
+        {
+            Tpkt = new TpktHeader();
+            Cotp = new CotpHeader();
+        }
+
+        public void Build()
+        {
+            Cotp.Params.PduSizeCode = 0xC0;
+            Cotp.Params.PduSizeLen = 0x01;
+            switch (IsoPduSize)
+            {
+                case 128:
+                    Cotp.Params.PduSizeVal = 0x07;
+                    break;
+                case 256:
+                    Cotp.Params.PduSizeVal = 0x08;
+                    break;
+                case 512:
+                    Cotp.Params.PduSizeVal = 0x09;
+                    break;
+                case 1024:
+                    Cotp.Params.PduSizeVal = 0x0A;
+                    break;
+                case 2048:
+                    Cotp.Params.PduSizeVal = 0x0B;
+                    break;
+                case 4096:
+                    Cotp.Params.PduSizeVal = 0x0C;
+                    break;
+                case 8192:
+                    Cotp.Params.PduSizeVal = 0x0D;
+                    break;
+                default:
+                    Cotp.Params.PduSizeVal = 0x0B;  // Our Default
+                    break;
+            }
+            // Build TSAPs
+            Cotp.Params.Tsap[0] = 0xC1;   // code that identifies source TSAP
+            Cotp.Params.Tsap[1] = 2;      // source TSAP Len
+            Cotp.Params.Tsap[2] = (byte)((SrcTSap >> 8) & 0xFF); // HI part
+            Cotp.Params.Tsap[3] = (byte)(SrcTSap & 0xFF); // LO part
+
+            Cotp.Params.Tsap[4] = 0xC2; // code that identifies dest TSAP
+            Cotp.Params.Tsap[5] = 2;    // dest TSAP Len
+            Cotp.Params.Tsap[6] = (byte)((DstTSap >> 8) & 0xFF); // HI part
+            Cotp.Params.Tsap[7] = (byte)(DstTSap & 0xFF); // LO part
+            Cotp.Params.TsapLenth = 8;
+            // Params length
+            var parLen = 11;            // 2 Src TSAP (Code+field Len)      +
+                                        // 2 Src TSAP len                   +
+                                        // 2 Dst TSAP (Code+field Len)      +
+                                        // 2 Src TSAP len                   +
+                                        // 3 PDU size (Code+field Len+Val)  = 11
+                                        // Telegram length
+            var isoLen = 4 + // TPKT Header
+                    7 +           // COTP Header Size without params
+                    parLen;       // COTP params
+
+            Tpkt.Version = StaticConst.IsoTcpVersion;
+            Tpkt.Reserved = 0;
+            Tpkt.Length = (ushort)isoLen;
+
+            Cotp.Length = (byte)(parLen + 6);  // <-- 6 = 7 - 1 (COTP Header size - 1)
+            Cotp.PduType = (byte)PduType.ConnRequest; // Connection Request
+            Cotp.DstRef = DstRef;      // Destination reference
+            Cotp.SrcRef = SrcRef;      // Source reference
+            Cotp.CoR = 0x00;        // Class + Option : RFC0983 states that it must be always 0x40
+                                    // but for some equipment (S7) must be 0 in disaccord of specifications !!!
+        }
+
+        public void Build(byte[] resBytes)
+        {
+            ByteBuffer buffer=ByteBuffer.Allocate();
+            buffer.Push(resBytes);
+            Tpkt.InitByBuffer(buffer);
+            Cotp.InitByBuffer(buffer);
+        }
+
+        public ByteBuffer GetBuffer()
+        {
+            ByteBuffer buffer=ByteBuffer.Allocate();
+            Tpkt.GetByteBuffer(buffer);
+            Cotp.GetByteBuffer(buffer);
+            return buffer;
+        }
+
+        public void Build(ByteBuffer buffer)
+        {
+            Tpkt.InitByBuffer(buffer);
+            Cotp.InitByBuffer(buffer);
+        }
+    }
+}

+ 50 - 0
PLC.Siemens/Protocol/Iso/IsoDataPdu.cs

@@ -0,0 +1,50 @@
+using Core.Communication.Transport;
+using PLC.Siemens.O;
+using PLC.Siemens.Protocol.Common;
+using PLC.Siemens.Protocol.Header;
+
+namespace PLC.Siemens.Protocol.Iso
+{
+    public class IsoDataPdu
+    {
+        public TpktHeader Tpkt { get; set; } // TPKT Header
+        public CotpDataHeader Cotp { get; set; }// COPT Header for DATA EXCHANGE
+        public ushort Length { get; set; }
+
+        public IsoDataPdu()
+        {
+            Tpkt = new TpktHeader();
+            Cotp = new CotpDataHeader();
+        }
+
+        public void GetBuffer(ByteBuffer buffer)
+        {
+            Tpkt.GetByteBuffer(buffer);
+            Cotp.GetByteBuffer(buffer);
+        }
+
+        public void Build()
+        {
+            Tpkt.Version = StaticConst.IsoTcpVersion;
+            Tpkt.Reserved = 0;
+            Tpkt.Length = (ushort)(Length+0x07);
+
+            Cotp.Length = (byte)(Cotp.PacketLength - 1);//去掉长度占用的一个字节
+            Cotp.PduType = (byte)PduType.DataTransfer;
+            Cotp.EoTNum = (byte)PduType.EndTransfer;
+        }
+
+        public void Build(ByteBuffer buffer)
+        {
+            Tpkt.Version = buffer.PopByte();
+            Tpkt.Reserved = buffer.PopByte();
+            Tpkt.Length = buffer.PopUshort();
+            Length = Tpkt.Length;
+
+            Cotp.Length = buffer.PopByte();
+            Cotp.PduType = buffer.PopByte();
+            Cotp.EoTNum = buffer.PopByte();
+
+        }
+    }
+}

+ 19 - 0
PLC.Siemens/Protocol/ListBlocks/BlockItem.cs

@@ -0,0 +1,19 @@
+using Core.Communication.Transport;
+using PLC.Siemens.Protocol.Common;
+
+namespace PLC.Siemens.Protocol.ListBlocks
+{
+    public class BlockItem
+    {
+        public byte Zero { get; set; }   // always 0x30 -> Ascii 0
+        public BlockType Type { get; set; }  // Block Type
+        public ushort Count { get; set; } // Block count
+
+        public void Build(ByteBuffer buffer)
+        {
+            Zero = buffer.PopByte();
+            Type = (BlockType)buffer.PopByte();
+            Count = buffer.PopUshort();
+        }
+    }
+}

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