Browse Source

添加项目文件。

林豪 左 3 năm trước cách đây
mục cha
commit
94c25de75a
47 tập tin đã thay đổi với 4433 bổ sung0 xóa
  1. 120 0
      DbHelper/DB.cs
  2. 54 0
      DbHelper/DbContext.cs
  3. 13 0
      DbHelper/DbHelper.csproj
  4. 54 0
      DbHelper/DbLog.cs
  5. 60 0
      WCS Pedestal.sln
  6. 57 0
      WCS.Core/Configs.cs
  7. 462 0
      WCS.Core/DataTrans/DataBlock.cs
  8. 150 0
      WCS.Core/DataTrans/DataField.cs
  9. 580 0
      WCS.Core/DataTrans/Extentions.cs
  10. 28 0
      WCS.Core/DataTrans/IPLCAccessor.cs
  11. 355 0
      WCS.Core/DataTrans/ProtocolProxyBase.cs
  12. 24 0
      WCS.Core/DebugPublisher.cs
  13. 27 0
      WCS.Core/Device.cs
  14. 100 0
      WCS.Core/EntityEx.cs
  15. 71 0
      WCS.Core/Exception.cs
  16. 132 0
      WCS.Core/ExpressionExtensions.cs
  17. 334 0
      WCS.Core/IL/Generator.cs
  18. 18 0
      WCS.Core/IL/IProtocolProxy.cs
  19. 148 0
      WCS.Core/LogHelper.cs
  20. 370 0
      WCS.Core/LogicHandler.cs
  21. 19 0
      WCS.Core/WCS.Core.csproj
  22. 32 0
      WCS.Core/WorkInfo.cs
  23. 123 0
      WCS.DbHelper/DB.cs
  24. 54 0
      WCS.DbHelper/DbContext.cs
  25. 57 0
      WCS.DbHelper/DbLog.cs
  26. 15 0
      WCS.DbHelper/WCS.DbHelper.csproj
  27. 14 0
      WCS.Entity/IDATA.cs
  28. 12 0
      WCS.Entity/IgnoreChangingAttribute.cs
  29. 28 0
      WCS.Entity/LocInfo.cs
  30. 66 0
      WCS.Entity/OBJ.cs
  31. 24 0
      WCS.Entity/Playerback.cs
  32. 15 0
      WCS.Entity/WCS.Entity.csproj
  33. 47 0
      WCS.Entity/WCS_CMD.cs
  34. 78 0
      WCS.Entity/WCS_DATABLOCK.cs
  35. 58 0
      WCS.Entity/WCS_DEVICE.cs
  36. 41 0
      WCS.Entity/WCS_DEVICEPROTOCOL.cs
  37. 59 0
      WCS.Entity/WCS_EXCEPTION.cs
  38. 35 0
      WCS.Entity/WCS_GROUPMEMBER.cs
  39. 41 0
      WCS.Entity/WCS_MAPPINGENTRY.cs
  40. 41 0
      WCS.Entity/WCS_PATH.cs
  41. 71 0
      WCS.Entity/WCS_PATHPOINT.cs
  42. 116 0
      WCS.Entity/WCS_PLC.cs
  43. 41 0
      WCS.Entity/WCS_PROTOCOLDATA.cs
  44. 35 0
      WCS.Entity/WCS_ROUTE.cs
  45. 31 0
      WCS.Entity/WCS_Users.cs
  46. 109 0
      WCS.RedisHelper/RedisHelper.cs
  47. 14 0
      WCS.RedisHelper/WCS.Redis.csproj

+ 120 - 0
DbHelper/DB.cs

@@ -0,0 +1,120 @@
+using SqlSugar;
+
+namespace DbHelper
+{
+    /// <summary>
+    /// DB,禁止跨上下文使用
+    /// 1.异步情况: 在同一串await 中是一个上下文 (await 会改变线程和同步是不一样的)
+    /// 2.同步情况: 在同一个线程是同一个上下文
+    /// </summary>
+    public class Db
+    {
+        /// <summary>
+        /// 上下文集合
+        /// </summary>
+        private static List<ContextList> _contexts = new List<ContextList>();
+
+        /// <summary>
+        /// 默认上下文类类型
+        /// </summary>
+        public static string DefaultDbContextType { get; private set; } = null!;
+
+        public static T Do<T>(Func<Db, T> func)
+        {
+            var db = new Db();
+            try
+            {
+                db.Default.BeginTran();
+                var res = func(db);
+                db.Default.CommitTran();
+                return res;
+            }
+            catch (Exception 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(ex.Message);
+                throw new Exception(ex.Message);
+            }
+        }
+
+        public static void Do(Action<Db> act)
+        {
+            Do(db =>
+            {
+                act(db);
+                return 1;
+            });
+        }
+
+        /// <summary>
+        /// 设置默认链接
+        /// </summary>
+        /// <param name="key"></param>
+        public static void SetDefaultDbContextType(string key)
+        {
+            DefaultDbContextType = key;
+        }
+
+        /// <summary>
+        /// 默认链接
+        /// </summary>
+        public SqlSugarScope Default
+        {
+            get
+            {
+                if (DefaultDbContextType == null)
+                    throw new Exception("请先设置默认数据库,调用静态方法SetDefaultDbContextType()");
+                return Context(DefaultDbContextType);
+            }
+        }
+
+        /// <summary>
+        /// 创建一个上下文
+        /// </summary>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        public static SqlSugarScope CreateContext(ConnectionConfig config, string key)
+        {
+            var ctx = _contexts.FirstOrDefault(v => v.Key == key);
+            if (ctx != null) return ctx.Client;
+            ctx = new ContextList(key, new SqlSugarScope(new ConnectionConfig()
+            {
+                ConnectionString = config.ConnectionString,
+                DbType = config.DbType,
+                IsAutoCloseConnection = true
+            }), config);
+
+            _contexts.Add(ctx);
+            return ctx.Client;
+        }
+
+        public SqlSugarScope Context(string key)
+        {
+            var ctx = _contexts.FirstOrDefault(v => v.Key == key);
+            if (ctx == null) throw new Exception("没有对应的链接,请先调用创建");
+            return ctx.Client;
+        }
+    }
+
+    /// <summary>
+    /// 链接
+    /// </summary>
+    public class ContextList
+
+    {
+        public ContextList(string key, SqlSugarScope client, ConnectionConfig connectionConfig)
+        {
+            this.Key = key;
+            Client = client;
+            ConnectionConfig = connectionConfig;
+        }
+
+        public string Key { get; set; }
+
+        public SqlSugarScope Client { get; set; }
+
+        public ConnectionConfig ConnectionConfig { get; set; }
+    }
+}

+ 54 - 0
DbHelper/DbContext.cs

@@ -0,0 +1,54 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DbHelper
+{
+    public class DbContext
+    {
+        private static ConnectionConfig? _config = null;
+
+        public static void SetConfig(ConnectionConfig value)
+        {
+            _config = value;
+        }
+
+        /// <summary>
+        /// 用单例模式
+        /// </summary>
+        private static readonly SqlSugarScope SqlSugarScope = new SqlSugarScope(new ConnectionConfig()
+        {
+            ConnectionString = _config!.ConnectionString, //连接符字串
+            DbType = _config.DbType, //数据库类型
+            IsAutoCloseConnection = true //不设成true要手动close
+        }, db =>
+        {
+            //(A)全局生效配置点
+            //调试SQL事件,可以删掉
+            db.Aop.OnLogExecuting = (sql, pars) =>
+            {
+                DbLog.DBEX(sql);
+                //输出sql,查看执行sql
+                //5.0.8.2 获取无参数化 SQL
+                //UtilMethods.GetSqlString(DbType.SqlServer,sql,pars)
+            };
+        });
+
+        /// <summary>
+        /// 获取db链接
+        /// </summary>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public static SqlSugarScope Db
+        {
+            get
+            {
+                if (_config == null) throw new Exception("请使用SetConfig方法写入数据库链接信息");
+                return SqlSugarScope;
+            }
+        }
+    }
+}

+ 13 - 0
DbHelper/DbHelper.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="SqlSugarCore" Version="5.1.2.3" />
+  </ItemGroup>
+
+</Project>

+ 54 - 0
DbHelper/DbLog.cs

@@ -0,0 +1,54 @@
+namespace DbHelper
+{
+    public class DbLog : ILogType
+    {
+        /// <summary>
+        /// 日志名称
+        /// </summary>
+        public string LogName => "Db";
+
+        /// <summary>
+        /// 日志
+        /// </summary>
+        public static ILogType Log { get; set; }
+
+        public Dictionary<string, string> SubLogNames { get; set; }
+
+        static DbLog()
+        {
+            Log = new DbLog();
+        }
+
+        public DbLog()
+        {
+            SubLogNames = new Dictionary<string, string>
+            {
+                ["DB"] = "Db"
+            };
+            LogHelper.Init(this);
+        }
+
+        /// <summary>
+        /// 用于记录DB执行内容
+        /// </summary>
+        /// <param name="msg"></param>
+        public static void DBEX(string msg)
+        {
+            Log.Info(msg, "DB_EX");
+        }
+
+        /// <summary>
+        /// 用于记录DB清理记录
+        /// </summary>
+        /// <param name="msg"></param>
+        public static void DB_CLEAN(string msg)
+        {
+            Log.Info(msg, "DB_CLEAN");
+        }
+
+        public static void INFO_PLCREADLOG(string msg)
+        {
+            Log.Info(msg, "INFO_PLCREADLOG");
+        }
+    }
+}

+ 60 - 0
WCS Pedestal.sln

@@ -0,0 +1,60 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32630.192
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "框架", "框架", "{43005E7B-7FC3-407D-B098-E58D0B5B465F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "项目", "项目", "{9D01B749-E4C4-4AD4-82E1-5C3CA65295E5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCS.DbHelper", "WCS.DbHelper\WCS.DbHelper.csproj", "{5178BC6C-A3BF-4B36-A1C6-1B7DC5937474}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCS.Log", "WCS.Log\WCS.Log.csproj", "{B0A743B0-881E-4997-BE75-B5C4ED4A9BE4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCS.Redis", "WCS.RedisHelper\WCS.Redis.csproj", "{A93E66D2-2A49-410C-BDA6-AF0EF230A958}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCS.Entity", "WCS.Entity\WCS.Entity.csproj", "{E20F0C8A-619A-4D78-835C-909E81338458}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCS.Core", "WCS.Core\WCS.Core.csproj", "{17CF21B9-84AC-4FB3-9BC1-EF96CF8381CA}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{5178BC6C-A3BF-4B36-A1C6-1B7DC5937474}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5178BC6C-A3BF-4B36-A1C6-1B7DC5937474}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5178BC6C-A3BF-4B36-A1C6-1B7DC5937474}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5178BC6C-A3BF-4B36-A1C6-1B7DC5937474}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B0A743B0-881E-4997-BE75-B5C4ED4A9BE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B0A743B0-881E-4997-BE75-B5C4ED4A9BE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B0A743B0-881E-4997-BE75-B5C4ED4A9BE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B0A743B0-881E-4997-BE75-B5C4ED4A9BE4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A93E66D2-2A49-410C-BDA6-AF0EF230A958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A93E66D2-2A49-410C-BDA6-AF0EF230A958}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A93E66D2-2A49-410C-BDA6-AF0EF230A958}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A93E66D2-2A49-410C-BDA6-AF0EF230A958}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E20F0C8A-619A-4D78-835C-909E81338458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E20F0C8A-619A-4D78-835C-909E81338458}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E20F0C8A-619A-4D78-835C-909E81338458}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E20F0C8A-619A-4D78-835C-909E81338458}.Release|Any CPU.Build.0 = Release|Any CPU
+		{17CF21B9-84AC-4FB3-9BC1-EF96CF8381CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{17CF21B9-84AC-4FB3-9BC1-EF96CF8381CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{17CF21B9-84AC-4FB3-9BC1-EF96CF8381CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{17CF21B9-84AC-4FB3-9BC1-EF96CF8381CA}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{5178BC6C-A3BF-4B36-A1C6-1B7DC5937474} = {43005E7B-7FC3-407D-B098-E58D0B5B465F}
+		{B0A743B0-881E-4997-BE75-B5C4ED4A9BE4} = {43005E7B-7FC3-407D-B098-E58D0B5B465F}
+		{A93E66D2-2A49-410C-BDA6-AF0EF230A958} = {43005E7B-7FC3-407D-B098-E58D0B5B465F}
+		{E20F0C8A-619A-4D78-835C-909E81338458} = {43005E7B-7FC3-407D-B098-E58D0B5B465F}
+		{17CF21B9-84AC-4FB3-9BC1-EF96CF8381CA} = {43005E7B-7FC3-407D-B098-E58D0B5B465F}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {BF8ACC41-E6AD-46E2-8A85-2BD2AE0990C0}
+	EndGlobalSection
+EndGlobal

+ 57 - 0
WCS.Core/Configs.cs

@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WCS.Entity;
+
+namespace WCS.Core
+{
+    public static class Configs
+    {
+        public static Type ProtocolProxyBaseType { get; set; }
+
+        private static List<SystemMode> SystemModes { get; set; } = new List<SystemMode>();
+
+        /// <summary>
+        /// 添加一种模式
+        /// </summary>
+        /// <param name="mode"></param>
+        public static void AddSystemMode(SystemMode mode)
+        {
+            if (SystemModes.Contains(mode)) return;
+            SystemModes.Add(mode);
+        }
+
+        /// <summary>
+        /// 是否有
+        /// </summary>
+        /// <param name="mode"></param>
+        /// <returns></returns>
+        public static bool Any(SystemMode mode)
+        {
+            return SystemModes.Contains(mode);
+        }
+
+        public static Encoding StringEncoding { get; set; }
+
+        public static Action<Action<WCS_CMD>> DoCmds { get; set; }
+
+        public static string DebugRedisUrl { get; set; }
+
+        public static event Action PublishEvent;
+
+        internal static void Publish()
+        {
+            PublishEvent?.Invoke();
+        }
+
+        public static Action<string, string> UploadException { get; set; }
+    }
+
+    /// <summary>
+    /// 系统模式
+    /// </summary>
+    public enum SystemMode
+    {
+        虚拟PLC = 1,
+    }
+}

+ 462 - 0
WCS.Core/DataTrans/DataBlock.cs

@@ -0,0 +1,462 @@
+using DbHelper;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using WCS.Entity;
+
+namespace WCS.Core
+{
+    public class DataBlock : EntityEx<WCS_DATABLOCK>
+    {
+        protected byte[] Data;
+        //{
+        //    get { return DataServer.Get(Entity); }
+        //}
+
+        private List<PlcItem> Values = new List<PlcItem>();
+
+        internal event Action<Db, byte[]> DataChanged;
+
+        /// <summary>
+        /// 是否失败,默认成功
+        /// </summary>
+        private bool failed = false;
+
+        public DataBlock(WCS_DATABLOCK entity) : base(entity)
+        {
+        }
+
+        private DateTime AliveTime = DateTime.Now;
+
+        public void DataRefresh()
+        {
+            var sw = new Stopwatch();
+            try
+            {
+                if (Entity.NOUPDATE && Data != null)
+                    return;
+                if (failed && (DateTime.Now - AliveTime).TotalMilliseconds < 3000)
+                {
+                    throw new Exception(Entity.NAME + "连接失败");
+                }
+                AliveTime = DateTime.Now;
+                byte[] data = null;
+                lock (Entity.PLC)
+                {
+                    sw.Start();
+                    data = Entity.PLC.Ex().Accessor.ReadBytes((ushort)Entity.NO, 0, (ushort)Entity.LENGTH, (ushort)Entity.DATALENGTH);
+                    sw.Stop();
+                }
+
+                failed = false;
+                if (Data != null && data.SequenceEqual(Data)) return;
+                Data = data;
+
+                Db.Do(db =>
+                {
+                    DataChanged?.Invoke(db, Data);
+                });
+            }
+            catch (Exception)
+            {
+                sw.Stop();
+                failed = true;
+                throw;
+            }
+            finally
+            {
+                if (sw.ElapsedMilliseconds > 500)
+                {
+                    Console.ForegroundColor = ConsoleColor.Yellow;
+                    Console.WriteLine($"{Entity.PLC.NAME}{Entity.PLC.IP}访问耗时:{sw.ElapsedMilliseconds}");
+                    Console.ResetColor();
+                }
+            }
+        }
+
+        public PlcItem<T> Regist<T>(string objid, string name, int start, byte arrLen = 1, byte strLen = 0)
+        {
+            var pv = new PlcItem<T>(objid, name, this, start, arrLen, strLen);
+            Values.Add(pv);
+            return pv;
+        }
+
+        private ushort GetByteStart(int bitStart)
+        {
+            var res = bitStart / 8;
+            return (ushort)res;
+        }
+
+        private ushort GetBitIndex(int bitStart)
+        {
+            return (ushort)(bitStart % 8);
+        }
+
+        #region 读取
+
+        public T Read<T>(int bitStart, int strLength, int arrLength)
+        {
+            return (T)Read(typeof(T), ref bitStart, strLength, arrLength);
+        }
+
+        private object Read(Type type, ref int bitStart, int strLength, int arrLength)
+        {
+            if (failed)
+                throw new Exception(this.Entity.NAME + "连接失败");
+
+            if (type.IsArray)
+            {
+                var t = type.GetElementType();
+                if (t.IsArray)
+                {
+                    throw new Exception("只支持一维数组");
+                }
+                var arr = Array.CreateInstance(t, arrLength);
+
+                for (int i = 0; i < arr.Length; i++)
+                {
+                    var value = Read(t, ref bitStart, strLength, 0);
+                    arr.SetValue(value, i);
+                }
+                return arr;
+            }
+            else if (typeof(IList).IsAssignableFrom(type))
+            {
+                var t = type.GetGenericArguments().First();
+                var res = Activator.CreateInstance(type) as IList;
+                while (bitStart < Entity.LENGTH * 8)
+                {
+                    var value = Read(t, ref bitStart, strLength, 0);
+                    res.Add(value);
+                }
+                return res;
+            }
+            else if (type == typeof(bool))
+            {
+                return ReadBit(ref bitStart);
+            }
+            else if (type == typeof(string))
+            {
+                return ReadString(ref bitStart, strLength);
+            }
+            else if (type.IsEnum)
+            {
+                return ReadEnum(type, ref bitStart);
+            }
+            else if (type.IsPrimitive)
+            {
+                var revers = Entity.PLC.TYPE == PLCType.西门子;
+
+                return ReadPrimitive(type, ref bitStart, revers);
+
+                //var revers = Entity.PLC.TYPE == PLCType.西门子;
+
+                //return ReadPrimitive(type, ref bitStart, false);
+            }
+            else
+            {
+                if (type.IsValueType)
+                {
+                    //ushort size = (ushort)Marshal.SizeOf(type);
+                    //var s = bitStart;
+                    //var bytes = ReadBytes(ref s, size).Reverse().ToArray();
+                    //var o = ByteToStruct(type, bytes);
+
+                    var res = Activator.CreateInstance(type);
+                    foreach (var p in type.GetProperties())
+                    {
+                        var value = Read(p.PropertyType, ref bitStart, strLength, arrLength);
+                        p.SetValue(res, value);
+                    }
+                    return res;
+                }
+                else
+                    throw new Exception("类型不支持");
+            }
+        }
+
+        public static object ByteToStruct(Type type, byte[] dataBuffer)
+        {
+            int size = Marshal.SizeOf(type);
+            IntPtr allocIntPtr = Marshal.AllocHGlobal(size);
+            try
+            {
+                Marshal.Copy(dataBuffer, 0, allocIntPtr, size);
+                return Marshal.PtrToStructure(allocIntPtr, type);
+            }
+            finally
+            {
+                Marshal.FreeHGlobal(allocIntPtr);
+            }
+        }
+
+        private string ReadString(ref int bitStart, int strLen)
+        {
+            if (Entity.PLC.TYPE == PLCType.西门子)
+            {
+                var data = ReadBytes(ref bitStart, (ushort)(strLen + 2));
+
+                var len = data[1];
+                data = data.Skip(2).Take(len).ToArray();
+                var str = Configs.StringEncoding.GetString(data);
+                return str;
+            }
+            else
+            {
+                var data = ReadBytes(ref bitStart, (ushort)strLen);
+                var str = Configs.StringEncoding.GetString(data);
+                return str;
+            }
+        }
+
+        private object ReadEnum(Type enumType, ref int bitStart)
+        {
+            var type = enumType.GetEnumUnderlyingType();
+
+            var reverse = false;
+
+            if (enumType.GetCustomAttribute<FlagsAttribute>() == null)
+            {
+                reverse = Entity.PLC.TYPE == PLCType.西门子;
+            }
+
+            var value = ReadPrimitive(type, ref bitStart, reverse);
+            var res = Enum.Parse(enumType, value.ToString());
+            return res;
+        }
+
+        private object ReadPrimitive(Type type, ref int bitStart, bool reverse)
+        {
+            var size = (ushort)Marshal.SizeOf(type);
+            if (type == typeof(bool))
+                size = 1;
+            var data = ReadBytes(ref bitStart, size);
+            if (reverse)
+                data = data.Reverse().ToArray();
+            if (Entity.CODE == "SRM2_521" && type == typeof(short))
+            {
+                var c = 1;
+            }
+            if (type == typeof(byte))
+            {
+                return data.First();
+            }
+            else if (type == typeof(bool))
+            {
+                return BitConverter.ToBoolean(data, 0);
+            }
+            else if (type == typeof(char))
+            {
+                return BitConverter.ToChar(data, 0);
+            }
+            else if (type == typeof(short))
+            {
+                return BitConverter.ToInt16(data, 0);
+            }
+            else if (type == typeof(ushort))
+            {
+                return BitConverter.ToUInt16(data, 0);
+            }
+            else if (type == typeof(int))
+            {
+                return BitConverter.ToInt32(data, 0);
+            }
+            else if (type == typeof(uint))
+            {
+                return BitConverter.ToUInt32(data, 0);
+            }
+            else if (type == typeof(long))
+            {
+                return BitConverter.ToInt64(data, 0);
+            }
+            else if (type == typeof(ulong))
+            {
+                return BitConverter.ToUInt64(data, 0);
+            }
+            else if (type == typeof(float))
+            {
+                return BitConverter.ToSingle(data, 0);
+            }
+            else if (type == typeof(double))
+            {
+                return BitConverter.ToDouble(data, 0);
+            }
+            else
+            {
+                throw new Exception("类型不支持");
+            }
+        }
+
+        private byte[] ReadBytes(ref int bitStart, ushort length)
+        {
+            var start = GetByteStart(bitStart);
+            var data = Data.Skip(start).Take(length).ToArray();
+            bitStart += length * 8;
+            return data;
+        }
+
+        private bool ReadBit(ref int bitStart)
+        {
+            var start = GetByteStart(bitStart);
+            var b = Data.Skip(start).First();
+            var bitIndex = GetBitIndex(bitStart);
+            bitStart++;
+            return b.GetBit(bitIndex);
+        }
+
+        #endregion 读取
+
+        #region 写入
+
+        public void Write<T>(int bitStart, T value, int strLength, int arrLength)
+        {
+            Write(typeof(T), ref bitStart, value, strLength, arrLength);
+        }
+
+        private void Write(Type type, ref int bitStart, object value, int strLength, int arrLength)
+        {
+            if (type.IsArray)
+            {
+                var t = type.GetElementType();
+                if (t.IsArray)
+                {
+                    throw new Exception("只支持一维数组");
+                }
+                var arr = value as Array;
+                if (arr.Length > arrLength)
+                    throw new Exception("数组长度超出");
+                foreach (var obj in arr)
+                {
+                    Write(obj.GetType(), ref bitStart, obj, strLength, arrLength);
+                }
+            }
+            else if (type == typeof(bool))
+            {
+                WriteBit(ref bitStart, (bool)value);
+            }
+            else if (type == typeof(string))
+            {
+                WriteString(ref bitStart, value.ToString(), strLength);
+            }
+            else if (type.IsEnum)
+            {
+                WriteEnum(ref bitStart, value);
+            }
+            else if (type.IsPrimitive)
+            {
+                var revers = Entity.PLC.TYPE == PLCType.西门子;
+                WritePrimitive(ref bitStart, value, revers);
+            }
+            else
+                throw new Exception("类型不支持");
+        }
+
+        private void WriteString(ref int bitStart, string value, int strLen)
+        {
+            var data = Configs.StringEncoding.GetBytes(value);
+            if (data.Length > strLen)
+                throw new Exception("字符串长度超出");
+            if (Entity.PLC.TYPE == PLCType.西门子)
+            {
+                var title = new byte[] { (byte)strLen, (byte)data.Length };
+                data = title.Concat(data).ToArray();
+            }
+            WriteBytes(ref bitStart, data);
+        }
+
+        private void WriteEnum(ref int bitStart, object value)
+        {
+            var etype = value.GetType();
+            var type = etype.GetEnumUnderlyingType();
+            var obj = Convert.ChangeType(value, type);
+            var reverse = false;
+            if (etype.GetCustomAttribute<FlagsAttribute>() == null)
+            {
+                reverse = Entity.PLC.TYPE == PLCType.西门子;
+            }
+            WritePrimitive(ref bitStart, obj, reverse);
+        }
+
+        private void WritePrimitive(ref int bitStart, object value, bool reverse)
+        {
+            var type = value.GetType();
+            byte[] data;
+            if (type == typeof(char))
+            {
+                data = BitConverter.GetBytes((char)value);
+            }
+            else if (type == typeof(byte))
+            {
+                data = new byte[] { (byte)value };
+            }
+            else if (type == typeof(short))
+            {
+                data = BitConverter.GetBytes((short)value);
+            }
+            else if (type == typeof(ushort))
+            {
+                data = BitConverter.GetBytes((ushort)value);
+            }
+            else if (type == typeof(int))
+            {
+                data = BitConverter.GetBytes((int)value);
+            }
+            else if (type == typeof(uint))
+            {
+                data = BitConverter.GetBytes((uint)value);
+            }
+            else if (type == typeof(long))
+            {
+                data = BitConverter.GetBytes((long)value);
+            }
+            else if (type == typeof(ulong))
+            {
+                data = BitConverter.GetBytes((ulong)value);
+            }
+            else
+            {
+                throw new Exception("类型不支持");
+            }
+            if (reverse)
+                data = data.Reverse().ToArray();
+            WriteBytes(ref bitStart, data);
+        }
+
+        private void WriteBytes(ref int bitStart, byte[] data)
+        {
+            lock (Entity.PLC)
+            {
+                var start = GetByteStart(bitStart);
+
+                //DataServer.WriteBytes(Entity, start, data);
+                Entity.PLC.Ex().Accessor.WriteBytes((ushort)Entity.NO, start, data);
+                data.CopyTo(Data, start);
+                bitStart += data.Length * 8;
+            }
+        }
+
+        private void WriteBit(ref int bitStart, bool flag)
+        {
+            lock (Entity.PLC)
+            {
+                var start = GetByteStart(bitStart);
+                var bitIndex = GetBitIndex(bitStart);
+
+                var b = Data.Skip(start).First();
+                b = b.SetBit(bitIndex, flag);
+                var data = new byte[] { b };
+
+                Entity.PLC.Ex().Accessor.WriteBytes((ushort)Entity.NO, (ushort)start, data);
+                data.CopyTo(Data, start);
+
+                bitStart += 1;
+            }
+        }
+
+        #endregion 写入
+    }
+}

+ 150 - 0
WCS.Core/DataTrans/DataField.cs

@@ -0,0 +1,150 @@
+using System;
+using System.Collections;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace WCS.Core
+{
+    public delegate void ValueChangedHandler<T>(PlcItem<T> sender, T value);
+
+    public delegate void ValueChangedHandler(PlcItem sender, object value);
+
+    public abstract class PlcItem
+    {
+        protected DataBlock Db;
+        public int Start;//按位计算 如,第2个字节,Start=8
+
+        public byte ArrayLength, StringLength;
+
+        public string Id;
+        public string Name;
+        public string IP;
+        public ushort DB;
+        public Type DataType { get; private set; }
+
+        /// <summary>
+        /// 数据类型所占的字节数
+        /// </summary>
+        public byte DataSize { get; private set; }
+
+        public int DataSizeOfBits { get; private set; }
+
+        public PlcItem(string id, string name, DataBlock db, Type type, int start, byte arrLen = 1, byte strLength = 0)
+        {
+            this.Id = id;
+            this.Name = name;
+            this.Db = db;
+            this.DataType = type;
+            //this.Db.DBChanged += Db_DBChanged;
+            this.Start = start;
+            this.ArrayLength = arrLen;
+            this.StringLength = strLength;
+
+            DataSize = (byte)GetTypeLen(DataType);
+
+            DataSizeOfBits = _getBitLen(DataType);
+        }
+
+        private int GetTypeLen(Type type)
+        {
+            var bitLen = _getBitLen(type);
+            var mod = bitLen % 8;
+            if (mod > 0)
+                bitLen += 8 - mod;
+            return bitLen / 8;
+        }
+
+        private int _getBitLen(Type type)
+        {
+            if (type.IsArray)
+                return _getBitLen(type.GetElementType()) * ArrayLength;
+            if (type == typeof(bool))
+                return 1;
+            else if (type.IsEnum)
+                return Marshal.SizeOf(type.GetEnumUnderlyingType()) * 8;
+            else if (type == typeof(string))
+                return (StringLength + 2) * 8;
+            else
+            {
+                if (typeof(IList).IsAssignableFrom(type))
+                    return 0;
+                return Marshal.SizeOf(type) * 8;
+            }
+        }
+
+        public object Value
+        {
+            get { return getValue(); }
+            set
+            {
+                setValue(value);
+            }
+        }
+
+        protected abstract void setValue(object value);
+
+        protected abstract object getValue();
+
+        private bool ValueEquals(object obj1, object obj2)
+        {
+            if (!obj1.GetType().IsArray) return obj1.Equals(obj2);
+            var arr1 = obj1 as Array;
+            var arr2 = obj2 as Array;
+            if (arr2 == null)
+            {
+                return true;
+            }
+
+            return !arr1!.Cast<object>().Where((t, i) => !arr1.GetValue(i)!.Equals(arr2.GetValue(2))).Any();
+
+        }
+    }
+
+    public class PlcItem<T> : PlcItem
+    {
+        public PlcItem(string id, string name, DataBlock db, int start, byte arrLen = 1, byte strLen = 0) : base(id, name, db, typeof(T), start, arrLen, strLen)
+        {
+        }
+
+        public new T Value
+        {
+            get
+            {
+                return (T)base.Value;
+            }
+            set
+            {
+                base.Value = value;
+            }
+        }
+
+        protected override void setValue(object value)
+        {
+            int i = 0;
+            while (true)
+            {
+                try
+                {
+                    Db.Write<T>(Start, (T)value, StringLength, ArrayLength);
+                    return;
+                }
+                catch
+                {
+                    if (i >= 3)
+                        throw;
+                    else
+                    {
+                        i++;
+                        Thread.Sleep(100);
+                    }
+                }
+            }
+        }
+
+        protected override object getValue()
+        {
+            return Db.Read<T>(Start, StringLength, ArrayLength);
+        }
+    }
+}

+ 580 - 0
WCS.Core/DataTrans/Extentions.cs

@@ -0,0 +1,580 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using WCS.Core.DataTrans;
+using WCS.Entity;
+
+namespace WCS.Core
+{
+    public static class Extentions
+    {
+        //public static PlcDB Regist(this IPlcConn plc, ushort dbNo, ushort length)
+        //{
+        //    var plc= new PlcDB(plc, dbNo, length);
+        //}
+
+        #region GetData
+
+        public static byte[] GetData(this bool value)
+        {
+            return BitConverter.GetBytes(value);
+        }
+
+        public static byte[] GetData(this byte value)
+        {
+            return new byte[] { value };
+        }
+
+        public static byte[] GetData(this char value)
+        {
+            return BitConverter.GetBytes(value);
+        }
+
+        public static byte[] GetData(this short value)
+        {
+            return BitConverter.GetBytes(value);
+        }
+
+        public static byte[] GetData(this ushort value)
+        {
+            return BitConverter.GetBytes(value);
+        }
+
+        public static byte[] GetData(this int value)
+        {
+            return BitConverter.GetBytes(value);
+        }
+
+        public static byte[] GetData(this uint value)
+        {
+            return BitConverter.GetBytes(value);
+        }
+
+        public static byte[] GetData(this long value)
+        {
+            return BitConverter.GetBytes(value);
+        }
+
+        public static byte[] GetData(this ulong value)
+        {
+            return BitConverter.GetBytes(value);
+        }
+
+        internal static Encoding enCoding = Encoding.GetEncoding("gb2312");
+
+        private static Dictionary<Type, MethodInfo> ToBytesMethods = new Dictionary<Type, MethodInfo>();
+
+        private static byte[] GetPrimitiveData(object obj)
+        {
+            try
+            {
+                var t = obj.GetType();
+                if (t == typeof(byte))
+                    return new byte[] { (byte)obj };
+                MethodInfo mi;
+                lock (ToBytesMethods)
+                {
+                    if (!ToBytesMethods.TryGetValue(t, out mi))
+                    {
+                        var ms = typeof(BitConverter).GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
+                        ms = ms.Where(v => v.Name == "GetBytes").ToArray();
+                        mi = ms.FirstOrDefault(v => v.GetParameters()[0].ParameterType == t);
+                        ToBytesMethods.Add(t, mi);
+                    }
+                }
+                var res = mi.Invoke(null, new object[] { obj }) as byte[];
+                //if (!BitConverter.IsLittleEndian)
+                {
+                    res = res.Reverse().ToArray();
+                }
+                return res;
+            }
+            catch (Exception)
+            {
+                throw;
+            }
+        }
+
+        public static byte[] GetData(this object obj, byte dataSize = 0)
+        {
+            var type = obj.GetType();
+            System.IO.MemoryStream st = new System.IO.MemoryStream();
+            if (type.IsArray)
+            {
+                foreach (var o in obj as Array)
+                {
+                    var data = GetData(o);
+                    st.Write(data, 0, data.Length);
+                }
+            }
+            else
+            {
+                if (type == typeof(string))
+                {
+                    var data = enCoding.GetBytes(obj.ToString());
+                    st.WriteByte(dataSize);
+                    st.WriteByte((byte)data.Length);
+                    st.Write(data, 0, data.Length);
+                }
+                else
+                {
+                    var data = GetPrimitiveData(obj);
+                    st.Write(data, 0, data.Length);
+                }
+            }
+            return st.ToArray();
+        }
+
+        #endregion GetData
+
+        /// <summary>
+        /// 转换基元类型或者String
+        /// </summary>
+        /// <param name="data"></param>
+        /// <param name="type">类型为基元类型或者String</param>
+        /// <returns></returns>
+        public static object GetObj(this byte[] data, Type type)
+        {
+            if (type == typeof(string))
+            {
+                var len = data.Skip(1).First();
+                var bytes = data.Skip(2).Take(len).ToArray();
+                return Configs.StringEncoding.GetString(bytes);
+            }
+            else
+            {
+                if (type.IsEnum)
+                    type = type.GetEnumUnderlyingType();
+                data = data.Reverse().ToArray();
+                if (type == typeof(bool))
+                {
+                    return BitConverter.ToBoolean(data, 0);
+                }
+                else if (type == typeof(char))
+                {
+                    return BitConverter.ToChar(data, 0);
+                }
+                else if (type == typeof(byte))
+                {
+                    return data.First();
+                }
+                else if (type == typeof(short))
+                {
+                    return BitConverter.ToInt16(data, 0);
+                }
+                else if (type == typeof(ushort))
+                {
+                    return BitConverter.ToUInt16(data, 0);
+                }
+                else if (type == typeof(int))
+                {
+                    return BitConverter.ToInt32(data, 0);
+                }
+                else if (type == typeof(uint))
+                {
+                    return BitConverter.ToUInt32(data, 0);
+                }
+                else if (type == typeof(long))
+                {
+                    return BitConverter.ToInt64(data, 0);
+                }
+                else if (type == typeof(ulong))
+                {
+                    return BitConverter.ToUInt64(data, 0);
+                }
+                else
+                {
+                    throw new Exception("类型不支持");
+                }
+            }
+        }
+
+        /// <summary>
+        /// 转换基元类型或者String
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public static T GetObj<T>(this byte[] data)
+        {
+            return (T)data.GetObj(typeof(T));
+        }
+
+        /// <summary>
+        /// 转换基元类型或者String,或数组
+        /// </summary>
+        /// <param name="data"></param>
+        /// <param name="type"></param>
+        /// <param name="datasize">String长度</param>
+        /// <returns></returns>
+        public static object GetObj(this byte[] data, Type type, byte datasize)
+        {
+            if (type.IsArray)
+            {
+                var t = type.GetElementType();
+                var lsttype = typeof(List<>).MakeGenericType(t);
+                var lst = Activator.CreateInstance(lsttype) as System.Collections.IList;
+                var i = 0;
+
+                if (t == typeof(bool))
+                    datasize = 1;
+                else
+                    datasize = (byte)Marshal.SizeOf(t);
+
+                while (i < data.Length)
+                {
+                    var obj = GetObj(data.Skip(i).Take(datasize).ToArray(), t, datasize);
+                    lst.Add(obj);
+                    i += datasize;
+                }
+                var array = Array.CreateInstance(t, lst.Count) as Array;
+                lst.CopyTo(array, 0);
+                return array;
+            }
+            else
+                return data.GetObj(type);
+        }
+
+        /// <summary>
+        /// 转换基元类型或者String,或数组
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="data"></param>
+        /// <param name="datasize">String长度</param>
+        /// <returns></returns>
+        public static T GetObj<T>(this byte[] data, byte datasize = 0)
+        {
+            return (T)data.GetObj(typeof(T), datasize);
+        }
+    }
+
+    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 ushort
+
+        #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 byte
+
+        #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 uint
+
+        #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 ulong
+    }
+
+    public static class Extensions
+    {
+        private static ConcurrentDictionary<WCS_DEVICEPROTOCOL, object> Datas = new ConcurrentDictionary<WCS_DEVICEPROTOCOL, object>();
+
+        private static ConcurrentDictionary<object, object> ExObjs = new ConcurrentDictionary<object, object>();
+
+        public static T Data<T>(this WCS_DEVICEPROTOCOL obj)
+        {
+            return (T)Data(obj);
+        }
+
+        public static object Data(this WCS_DEVICEPROTOCOL obj)
+        {
+            if (!Datas.ContainsKey(obj))
+            {
+                var type = typeof(Generator<,>);
+                type = type.MakeGenericType(obj.DB.GetProtocolType(), Configs.ProtocolProxyBaseType);
+                var m = type.GetMethod("Create", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
+                Datas[obj] = m.Invoke(null, new object[] { new object[] { obj.DEVICE.CODE.ToString(), obj.DB, (ushort)obj.POSITION, obj } });
+            }
+            return Datas[obj];
+        }
+
+        //public static WCS_DEVICE Device(this IDATA obj)
+        //{
+        //    return (obj as PLCData).Device;
+        //}
+
+        public static T GetEx<T>(object entity)
+        {
+            if (!ExObjs.ContainsKey(entity))
+            {
+                var obj = Activator.CreateInstance(typeof(T), entity);
+                ExObjs[entity] = obj;
+            }
+            return (T)ExObjs[entity];
+        }
+
+        public static DataBlock Ex(this WCS_DATABLOCK source)
+        {
+            return GetEx<DataBlock>(source);
+        }
+
+        public static PlcAccessor Ex(this WCS_PLC source)
+        {
+            return GetEx<PlcAccessor>(source);
+        }
+
+        public static WCS_DEVICEPROTOCOL PROTOCOL(this IProtocol obj)
+        {
+            return (obj as ProtocolProxyBase).Protocol;
+        }
+
+        public static T Create<T>(this WCS_DEVICE source)
+        {
+            return (T)Activator.CreateInstance(typeof(T), source);
+        }
+
+        public static Device<T> Device<T>(this WCS_DEVICE source) where T : IProtocol
+        {
+            var res = (Device<T>)Activator.CreateInstance(typeof(Device<T>), source);
+            return res;
+        }
+
+        public static Device<T, T2> Device<T, T2>(this WCS_DEVICE source) where T : IProtocol where T2 : IProtocol
+        {
+            return (Device<T, T2>)Activator.CreateInstance(typeof(Device<T, T2>), source);
+        }
+
+        public static Device<T, T2, T3> Device<T, T2, T3>(this WCS_DEVICE source) where T : IProtocol where T2 : IProtocol where T3 : IProtocol
+        {
+            return (Device<T, T2, T3>)Activator.CreateInstance(typeof(Device<T, T2, T3>), source);
+        }
+
+        public static DeviceGroup<T> DeviceGroup<T>(this WCS_DEVICE source) where T : IProtocol
+        {
+            return (DeviceGroup<T>)Activator.CreateInstance(typeof(DeviceGroup<T>), source);
+        }
+
+        public static DeviceGroup<T, T2> DeviceGroup<T, T2>(this WCS_DEVICE source) where T : IProtocol where T2 : IProtocol
+        {
+            return (DeviceGroup<T, T2>)Activator.CreateInstance(typeof(DeviceGroup<T, T2>), source);
+        }
+
+        public static DeviceGroup<T, T2, T3> DeviceGroup<T, T2, T3>(this WCS_DEVICE source) where T : IProtocol where T2 : IProtocol where T3 : IProtocol
+        {
+            return (DeviceGroup<T, T2, T3>)Activator.CreateInstance(typeof(DeviceGroup<T, T2, T3>), source);
+        }
+
+        public static DateTime GetUpdateTime(this IProtocol source)
+        {
+            dynamic obj = source;
+            return obj.UpdateTime;
+        }
+    }
+}

+ 28 - 0
WCS.Core/DataTrans/IPLCAccessor.cs

@@ -0,0 +1,28 @@
+using HslCommunication.Core;
+using WCS.Entity;
+
+namespace WCS.Core.DataTrans
+{
+    public interface IPlcAccessorCreater
+    {
+        IReadWriteNet Create(WCS_PLC data);
+    }
+
+    public interface IPlcAccessor : IReadWriteNet
+    {
+        void WriteBytes(ushort db, ushort address, byte[] data);
+
+        byte[] ReadBytes(ushort db, ushort address, ushort length, ushort dataLength);
+    }
+
+    public class PlcAccessor : EntityEx<WCS_PLC>
+    {
+        public static IPlcAccessorCreater Creater;
+        public IPlcAccessor Accessor { get; private set; }
+
+        public PlcAccessor(WCS_PLC entity) : base(entity)
+        {
+            Accessor = (IPlcAccessor)Creater.Create(entity);
+        }
+    }
+}

+ 355 - 0
WCS.Core/DataTrans/ProtocolProxyBase.cs

@@ -0,0 +1,355 @@
+using DbHelper;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using WCS.Entity;
+
+namespace WCS.Core.DataTrans
+{
+    /// <summary>
+    /// IL动态生成的类型所继承的基类
+    /// </summary>
+    public abstract class ProtocolProxyBase : IProtocolProxy
+    {
+        protected Type ProtocolType, ProtocolDataType;
+
+        public static Func<WCS_PROTOCOLDATA, string, WCS_PROTOCOLDATA> DataChanged;
+
+        private readonly Dictionary<string, PlcItem> _items = new Dictionary<string, PlcItem>();
+
+        public string Id { get; set; }
+
+        public WCS_DEVICEPROTOCOL Protocol { get; private set; }
+
+        /// <summary>
+        /// 起始便宜量,按字节算
+        /// </summary>
+        public readonly ushort _start;
+
+        /// <summary>
+        /// 长度,按字节算
+        /// </summary>
+        public readonly ushort _length;
+
+        public WCS_DATABLOCK Db;
+
+        private static readonly Dictionary<Type, object> LockObjs = new Dictionary<Type, object>();
+
+        protected ProtocolProxyBase(string id, WCS_DATABLOCK db, ushort start, WCS_DEVICEPROTOCOL deviceItem)
+        {
+            var type = this.GetType();
+            if (!LockObjs.ContainsKey(type))
+                LockObjs[type] = new object();
+
+            this.Db = db;
+            db.Ex().DataChanged += PLCData_DataChanged;
+            this._start = start;
+            var bitStart = start * 8;//偏移量,按位算
+            this.Id = id;
+            this.Protocol = deviceItem;
+            var lst = this.GetType().GetInterfaces().ToList();
+
+            var ps = lst.SelectMany(v => v.GetProperties()).ToArray();
+            foreach (var p in ps)
+            {
+                if (p.GetIndexParameters().Length > 0)
+                    continue;
+                if (typeof(ProtocolProxyBase).GetProperty(p.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null)
+                {
+                    continue;
+                }
+
+                #region 计算偏移量
+
+                var modeNum = 0;
+
+                var t = p.PropertyType;
+                if (t.IsEnum)
+                    t = t.GetEnumUnderlyingType();
+                if (t == typeof(bool))//bit类型,db块中无须补齐16位
+                {
+                    modeNum = 1;
+                }
+                else if (t.IsPrimitive && Marshal.SizeOf(t) == 1)//byte char 等单字节类型,db块中无须补齐16位
+                {
+                    modeNum = 8;
+                }
+                else//其他类型,db块中必须补齐16位
+                {
+                    modeNum = 16;
+                }
+                var mod = bitStart % modeNum;
+                if (mod > 0)
+                {
+                    bitStart += modeNum - mod;
+                }
+
+                #endregion 计算偏移量
+
+                byte arrlen = 0;
+                byte strLen = 0;
+                if (t.IsArray)
+                {
+                    var attr = p.GetCustomAttributes(typeof(MaxLengthAttribute), true).FirstOrDefault() as MaxLengthAttribute;
+                    if (attr != null)
+                        arrlen = (byte)attr.Length;
+                }
+                else if (p.PropertyType == typeof(string))
+                {
+                    var attr = p.GetCustomAttributes(typeof(StringLengthAttribute), true).FirstOrDefault() as StringLengthAttribute;
+                    strLen = (byte)attr.MaximumLength;
+                }
+                var m = typeof(DataBlock).GetMethod("Regist");
+                m = m.MakeGenericMethod(p.PropertyType);
+                var item = m.Invoke(db.Ex(), new object[] { this.Id, p.Name, bitStart, arrlen, strLen }) as PlcItem;
+                _items.Add(p.Name, item);
+                bitStart += item.DataSizeOfBits;
+                _length += item.DataSize;
+            }
+            ProtocolType = this.Protocol.DB.GetProtocolType();
+            ProtocolDataType = ProtocolType.Assembly.GetTypes().Where(v => v.IsClass).First(v => v.GetInterface(ProtocolType.Name) != null && v != this.GetType());
+        }
+
+        private byte[] Data = null;
+        private static object _locobj = new object();
+
+        private void InvokeUpdate(string user, Db db)
+        {
+            //TODO:怀疑会有问题,需要再看一下
+            Task.Run(() =>
+            {
+                SaveChangs(user, db);
+            });
+        }
+
+        private WCS_PROTOCOLDATA _last = null;
+
+        public DateTime UpdateTime;
+
+        protected virtual void SaveChangs(string user, Db db)
+        {
+            var obj = Copy(this, ProtocolDataType) as WCS_PROTOCOLDATA;
+            obj!.WCSVERSION = this.WcsVersion;
+            if (_last == null)
+            {
+                Publish(this.Protocol.DEVICE.CODE, obj);
+                _last = GetLastData(db);
+            }
+            if (_last != null)
+            {
+                if (Equals(obj, _last, ProtocolType))
+                    return;
+            }
+            this.UpdateTime = DateTime.Now;
+
+            Publish(this.Protocol.DEVICE.CODE, obj);
+            _last = SaveNewData(db, _last, obj, user);
+        }
+
+        public virtual void Publish(string code, WCS_PROTOCOLDATA obj)
+        {
+        }
+
+        protected abstract WCS_PROTOCOLDATA GetLastData(Db db);
+
+        protected abstract WCS_PROTOCOLDATA SaveNewData(Db db, WCS_PROTOCOLDATA last, WCS_PROTOCOLDATA newobj, string user);
+
+        private void PLCData_DataChanged(Db db, byte[] data)
+        {
+            var temp = data.Skip(_start).Take(_length).ToArray();
+            try
+            {
+                if (Data != null && Data.SequenceEqual(temp))
+                    return;
+                var d1 = DateTime.Now;
+                InvokeUpdate("PLC", db);
+                var d = (DateTime.Now - d1).TotalMilliseconds;
+            }
+            finally { Data = temp; }
+        }
+
+        private static object Copy(object obj, Type type)
+        {
+            var res = Activator.CreateInstance(type);
+            foreach (var p in type.GetProperties())
+            {
+                var p2 = obj.GetType().GetProperty(p.Name);
+                if (p2 != null && p2.PropertyType == p.PropertyType)
+                {
+                    p.SetValue(res, p2.GetValue(obj));
+                }
+            }
+            return res;
+        }
+
+        private static bool Equals(object a, object b, Type type)
+        {
+            foreach (var p in type.GetProperties())
+            {
+                var v1 = p.GetValue(a);
+                var v2 = p.GetValue(b);
+
+                var attr = p.GetCustomAttribute<IgnoreChangingAttribute>();
+                if (attr != null)
+                {
+                    if (attr.IgnoreValueRange == 0)
+                        continue;
+                    try
+                    {
+                        var d = Math.Abs((decimal.Parse(v1.ToString())) - (decimal.Parse(v2.ToString())));
+                        if (d < attr.IgnoreValueRange)
+                            continue;
+                    }
+                    catch
+                    {
+                        Console.WriteLine("IgnoreValueRange属性只能用于数字类型");
+                    }
+                }
+
+                if (p.PropertyType == typeof(string))
+                {
+                    v1 = v1 == null ? "" : v1.ToString();
+                    v2 = v2 == null ? "" : v2.ToString();
+                }
+                if (p.PropertyType.IsArray)
+                {
+                    var m = typeof(Enumerable).GetMethods().Where(v => v.Name == nameof(Enumerable.SequenceEqual)).First(v => v.GetParameters().Length == 2);
+                    m = m.MakeGenericMethod(p.PropertyType.GetElementType()!);
+                    var res = (bool)m.Invoke(null, new[] { v1, v2 })!;
+                    if (!res)
+                        return false;
+                }
+                else
+                {
+                    if (!v1!.Equals(v2))
+                        return false;
+                }
+            }
+            return true;
+        }
+
+        #region IGenerator
+
+        /// <summary>
+        /// 索引器Get
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        protected T GetItem<T>(params object[] args)
+        {
+            Console.WriteLine("GetItem:" + string.Join(",", args.Select(v => v.ToString())));
+            return default(T);
+        }
+
+        /// <summary>
+        /// 索引器Set
+        /// </summary>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        protected void SetItem(params object[] args)
+        {
+            Console.WriteLine("SetItem:" + ":" + string.Join(",", args.Select(v => v.ToString())));
+        }
+
+        /// <summary>
+        /// 版本号,WCS每修改一次数据,版本号变化一次
+        /// </summary>
+        public int WcsVersion { get; private set; }
+
+        public void Set<T>(string propertyName, T value)
+        {
+            var str = Protocol.DEVICE.CODE + "." + ProtocolType.Name + "." + propertyName;
+            try
+            {
+                str += " = " + (value == null ? "null" : value.ToString());
+                var item = _items[propertyName] as PlcItem<T>;
+                item.Value = value;
+                DbHelper.Db.Do(db =>
+               {
+                   InvokeUpdate("WCS", db);
+               });
+
+                Data = null;
+                WcsVersion++;
+                str += " 写入成功";
+            }
+            catch
+            {
+                str += " 写入失败";
+                throw;
+            }
+            finally
+            {
+                Ltc.Log(str);
+            }
+        }
+
+        public void Update(string prop, object value, string user)
+        {
+            var item = _items[prop];
+            item.Value = value;
+            DbHelper.Db.Do(db =>
+            {
+                InvokeUpdate(user, db);
+            });
+
+            Data = null;
+            if (user == "PLC")
+                return;
+            WcsVersion++;
+        }
+
+        public T Get<T>(string propertyName)
+        {
+            var str = Protocol.DEVICE.CODE + "." + ProtocolType.Name + "." + propertyName;
+            try
+            {
+                var item = _items[propertyName] as PlcItem<T>;
+                var res = item.Value;
+                str += " : " + (res == null ? "null" : res.ToString());
+                return res;
+            }
+            catch
+            {
+                str += " 读取失败";
+                throw;
+            }
+        }
+
+        public T CallReturn<T>(string methodName, params object[] args)
+        {
+            Console.WriteLine(methodName + ":" + string.Join(",", args.Select(v => v.ToString())));
+            return (T)(object)1.321f;
+        }
+
+        public void CallVoid(string methodName, params object[] args)
+        {
+            Console.WriteLine(methodName + ":" + string.Join(",", args.Select(v => v.ToString())));
+        }
+
+        public void AddEvent(string eventName, Delegate delgate)
+        {
+            Console.WriteLine("Add:" + eventName);
+        }
+
+        public void RemoveEvent(string eventName, Delegate delgate)
+        {
+            Console.WriteLine("Remove:" + eventName);
+        }
+
+        #endregion IGenerator
+
+        public override string ToString()
+
+        {
+            var str = this.GetType().GetProperties().Aggregate("", (current, p) => current + (p.Name + ":" + p.GetValue(this, null)!.ToString() + ","));
+            str = str[..^1];
+            return str;
+        }
+    }
+}

+ 24 - 0
WCS.Core/DebugPublisher.cs

@@ -0,0 +1,24 @@
+using WCS.Redis;
+
+namespace WCS.Core
+{
+    public class DebugPublisher
+    {
+        static DebugPublisher()
+        {
+        }
+
+        public static void Publish(string channel, string msg)
+        {
+            try
+
+            {
+                RedisHelper.Default().Publish(channel, msg);
+            }
+            catch
+            {
+                // TODO:日常日志
+            }
+        }
+    }
+}

+ 27 - 0
WCS.Core/Device.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+using WCS.Core;
+using WCS.Entity;
+
+public static class Device
+{
+    #region 静态方法
+
+    public static WCS_DEVICE[] Where(Func<WCS_DEVICE, bool> func)
+    {
+        var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(func).ToArray();
+        return arr;
+    }
+
+    public static WCS_DEVICE Find(string code)
+    {
+        return Where(v => v.CODE == code).Single();
+    }
+
+    public static WCS_DEVICE[] Find(params string[] codes)
+    {
+        return Where(v => codes.Contains(v.CODE)).ToArray();
+    }
+
+    #endregion 静态方法
+}

+ 100 - 0
WCS.Core/EntityEx.cs

@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using WCS.Entity;
+
+namespace WCS.Core
+{
+    public abstract class EntityEx<T>
+    {
+        public T Entity
+        {
+            get;
+        }
+
+        protected EntityEx(T entity)
+        {
+            Entity = entity;
+        }
+
+        public virtual DateTime UpdateTime { get; set; }
+
+        public double Times => (DateTime.Now - UpdateTime).TotalMilliseconds;
+
+        public override string ToString()
+        {
+            return Entity.ToString();
+        }
+    }
+
+    public class Device<T> : EntityEx<WCS_DEVICE> where T : IProtocol
+    {
+        public T Data { get; }
+
+        public Device(WCS_DEVICE entity) : base(entity)
+        {
+            Data = entity.DEVICEPROTOCOLS.Single(v => v.DB.PROTOCOL == typeof(T).AssemblyQualifiedName).Data<T>();
+        }
+    }
+
+    public class Device<T, T2> : Device<T> where T : IProtocol where T2 : IProtocol
+    {
+        public T2 Data2 { get; }
+
+        public Device(WCS_DEVICE entity) : base(entity)
+        {
+            Data2 = entity.DEVICEPROTOCOLS.Single(v => v.DB.PROTOCOL == typeof(T2).AssemblyQualifiedName).Data<T2>();
+        }
+    }
+
+    public class Device<T, T2, T3> : Device<T, T2> where T : IProtocol where T2 : IProtocol where T3 : IProtocol
+    {
+        public T3 Data3 { get; }
+
+        public Device(WCS_DEVICE entity) : base(entity)
+        {
+            Data3 = entity.DEVICEPROTOCOLS.Single(v => v.DB.PROTOCOL == typeof(T3).AssemblyQualifiedName).Data<T3>();
+        }
+    }
+
+    public class Device<T, T2, T3, T4> : Device<T, T2, T3> where T : IProtocol where T2 : IProtocol where T3 : IProtocol where T4 : IProtocol
+    {
+        public T4 Data4 { get; }
+
+        public Device(WCS_DEVICE entity) : base(entity)
+        {
+            Data4 = entity.DEVICEPROTOCOLS.Single(v => v.DB.PROTOCOL == typeof(T4).AssemblyQualifiedName).Data<T4>();
+        }
+    }
+
+    public class Group<T> : EntityEx<WCS_DEVICE> where T : EntityEx<WCS_DEVICE>
+    {
+        public IEnumerable<T> Items { get; }
+
+        public Group(WCS_DEVICE entity) : base(entity)
+        {
+            Items = entity.DEVICEGROUP.Select(v => Device.Find(v.MEMBER.CODE).Create<T>()).OrderBy(p => p.Entity.CODE).ToList();
+        }
+    }
+
+    public class DeviceGroup<T> : Group<Device<T>> where T : IProtocol
+    {
+        public DeviceGroup(WCS_DEVICE entity) : base(entity)
+        {
+        }
+    }
+
+    public class DeviceGroup<T, T2> : Group<Device<T, T2>> where T : IProtocol where T2 : IProtocol
+    {
+        public DeviceGroup(WCS_DEVICE entity) : base(entity)
+        {
+        }
+    }
+
+    public class DeviceGroup<T, T2, T3> : Group<Device<T, T2, T3>> where T : IProtocol where T2 : IProtocol where T3 : IProtocol
+    {
+        public DeviceGroup(WCS_DEVICE entity) : base(entity)
+        {
+        }
+    }
+}

+ 71 - 0
WCS.Core/Exception.cs

@@ -0,0 +1,71 @@
+using System;
+
+namespace WCS.Service
+{
+    /// <summary>
+    /// 警告异常
+    /// </summary>
+    public class WarnException : Exception
+    {
+        /// <summary>
+        /// 报告WCS
+        /// </summary>
+        public bool REPORTWCS { get; set; }
+
+        /// <summary>
+        /// 报告WMS
+        /// </summary>
+        public bool REPORTWMS { get; set; }
+
+        /// <summary>
+        /// 记录文本日志
+        /// </summary>
+        public bool RECORDTXT { get; set; }
+
+        /// <summary>
+        /// 记录数据库
+        /// </summary>
+        public bool RECORDDB { get; set; }
+
+        /// <summary>
+        /// 警告异常 默认都记录
+        /// </summary>
+        /// <param name="message"></param>
+        public WarnException(string message) : base(message)
+        {
+            REPORTWCS = true;
+            REPORTWMS = true;
+            RECORDTXT = true;
+            RECORDDB = true;
+        }
+
+        /// <summary>
+        /// 警告异常,自定义上报
+        /// </summary>
+        /// <param name="message"></param>
+        /// <param name="rEPORTWCS"></param>
+        /// <param name="rEPORTWMS"></param>
+        /// <param name="rECORDTXT"></param>
+        public WarnException(string message, bool rEPORTWCS, bool rEPORTWMS, bool rECORDTXT, bool rECORDDB) : base(message)
+        {
+            REPORTWCS = rEPORTWCS;
+            REPORTWMS = rEPORTWMS;
+            RECORDTXT = rECORDTXT;
+            RECORDDB = rECORDDB;
+        }
+    }
+
+    /// <summary>
+    /// 执行记录
+    /// </summary>
+    public class DoException : Exception
+    {
+        /// <summary>
+        /// 警告异常
+        /// </summary>
+        /// <param name="message"></param>
+        public DoException(string message) : base(message)
+        {
+        }
+    }
+}

+ 132 - 0
WCS.Core/ExpressionExtensions.cs

@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Core
+{
+    public static class ExpressionExtensions
+    {
+        public static string ExpToString(this LambdaExpression source)
+        {
+            var str = "";
+            try
+            {
+                str= LambdaToString(source);
+            }
+            catch 
+            {
+                str = source.ToString();  
+            }
+            var arr = str.Split("Convert(");
+            arr = arr.Select(v =>
+            {
+                if (!v.Contains(','))
+                    return v;
+                var index = v.IndexOf(',');
+                var index2 = v.IndexOf(')', index);
+                var sub = v.Substring(index, index2 - index + 1);
+                var str = v.Replace(sub, "");
+                return str;
+
+            }).ToArray();
+            var res = string.Join("", arr);
+            return res;
+        }
+
+        public static string LambdaToString(LambdaExpression expression)
+        {
+            var replacements = new Dictionary<string, string>();
+            WalkExpression(replacements, expression);
+            string body = expression.ToString();
+            foreach (var parm in expression.Parameters)
+            {
+                var parmName = parm.Name;
+                var parmTypeName = parm.Type.Name;
+                body = body.Replace(parmName + " =>", "(" + parmTypeName + " v) =>");
+            }
+            foreach (var replacement in replacements)
+            {
+                body = body.Replace(replacement.Key, replacement.Value);
+            } 
+            return body;
+        }
+        private static void WalkExpression(Dictionary<string, string> replacements, Expression expression)
+        {
+            switch (expression.NodeType)
+            {
+                case ExpressionType.MemberAccess:
+                    string replacementExpression = expression.ToString();
+                    if (replacementExpression.Contains("value("))
+                    {
+                        string replacementValue = Expression.Lambda(expression).Compile().DynamicInvoke().ToString();
+                        if (!replacements.ContainsKey(replacementExpression))
+                        {
+                            replacements.Add(replacementExpression, replacementValue.ToString());
+                        }
+                    }
+                    break;
+                case ExpressionType.GreaterThan:
+                case ExpressionType.GreaterThanOrEqual:
+                case ExpressionType.LessThan:
+                case ExpressionType.LessThanOrEqual:
+                case ExpressionType.OrElse:
+                case ExpressionType.AndAlso:
+                case ExpressionType.Equal:
+                case ExpressionType.NotEqual:
+                    var bexp = expression as BinaryExpression;
+                    WalkExpression(replacements, bexp.Left);
+                    WalkExpression(replacements, bexp.Right);
+                    break;
+                case ExpressionType.Call:
+                    var mcexp = expression as MethodCallExpression;
+                    foreach (var argument in mcexp.Arguments)
+                    {
+                        WalkExpression(replacements, argument);
+                    }
+                    break;
+                case ExpressionType.Lambda:
+                    var lexp = expression as LambdaExpression;
+                    WalkExpression(replacements, lexp.Body);
+                    break;
+                case ExpressionType.Constant:
+                    //do nothing
+                    break;
+                case ExpressionType.Convert:
+                    var exp = expression as UnaryExpression;
+                    WalkExpression(replacements, exp.Operand);
+                    break;
+                default:
+                    //Trace.WriteLine("Unknown type");
+                    break;
+            }
+        }
+
+
+
+        static bool When<T>(this T source, Expression<Func<T, bool>> exp) where T : class
+        {
+            var str = exp.ExpToString();
+            try
+            {
+                var res = exp.Compile().Invoke(source);
+                str += res ? " 成立" : " 不成立";
+                return res;
+            }
+            catch (Exception ex)
+            {
+                str += ex.GetBaseException().Message;
+                throw;
+            }
+            finally
+            {
+                Console.WriteLine(str);
+                Console.WriteLine("------------------------------------");
+            }
+        }
+    }
+
+   
+}

+ 334 - 0
WCS.Core/IL/Generator.cs

@@ -0,0 +1,334 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace WCS.Core
+{
+    /// <summary>
+    /// 动态创建接口实例
+    /// </summary>
+    /// <typeparam name="T">接口</typeparam>
+    /// <typeparam name="TBase">基类</typeparam>
+    public static class Generator<T, TBase> where TBase : IProtocolProxy
+    {
+        private static TypeBuilder tb;
+        private static Type Type;
+
+        static Generator()
+        {
+            if (!typeof(T).IsInterface)
+                throw new Exception("T必须是interface");
+            if (!typeof(TBase).IsClass)
+                throw new Exception("TBase必须是class");
+
+            AssemblyName myAssemblyName = new AssemblyName();
+            myAssemblyName.Name = typeof(T).Name + "_Generate";
+            AssemblyBuilder myAssembly = AssemblyBuilder.DefineDynamicAssembly(myAssemblyName, AssemblyBuilderAccess.RunAndCollect);
+            //AssemblyBuilder myAssembly = AssemblyBuilder.DefineDynamicAssembly(myAssemblyName, AssemblyBuilderAccess.RunAndSave);
+
+            ModuleBuilder myModule = myAssembly.DefineDynamicModule("Main");
+            tb = myModule.DefineType(typeof(T).Name + "_Generate", TypeAttributes.Public, typeof(TBase), new Type[] { typeof(T) });
+            InitConstructors();
+            InitMethods();
+            InitProperties();
+            InitEvents();
+            try
+            {
+                Type = tb.CreateTypeInfo().AsType();//.CreateType();
+            }
+            catch (Exception)
+            {
+            }
+            //myAssembly.Save(typeof(T).Name + ".dll");
+        }
+
+        private const MethodAttributes METHOD_ATTRIBUTES =
+    MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig;
+
+        private static void InitConstructors()
+        {
+            foreach (var cons in typeof(TBase).GetConstructors())
+            {
+                var pTypes = cons.GetParameters().Select(v => v.ParameterType).ToArray();
+                var build = tb.DefineConstructor(cons.Attributes, CallingConventions.Standard, pTypes);
+                for (int i = 0; i < pTypes.Length; i++)
+                {
+                    build.DefineParameter(i + 1, ParameterAttributes.In, "p" + i);
+                }
+                var il = build.GetILGenerator();
+                il.Emit(OpCodes.Ldarg_0);
+                for (int i = 0; i < pTypes.Length; i++)
+                {
+                    il.Emit(OpCodes.Ldarg, i + 1);
+                }
+                il.Emit(OpCodes.Call, cons);
+                //il.Emit(OpCodes.Pop);
+                il.Emit(OpCodes.Ret);
+            }
+        }
+
+        private static void InitMethods()
+        {
+            var ms = GetInterfaces().SelectMany(v => v.GetMethods()).ToArray();
+            ms = ms.Where(v => !v.IsSpecialName).ToArray();
+            foreach (var m in ms)
+            {
+                GenerateMethod(m);
+            }
+        }
+
+        private static void InitProperties()
+        {
+            var ps = GetInterfaces().SelectMany(v => v.GetProperties());
+            ps = GetMembers().OfType<PropertyInfo>();
+            foreach (var p in ps)
+            {
+                if (typeof(TBase).GetProperty(p.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null)
+                {
+                    continue;
+                }
+                var pTypes = p.GetIndexParameters().Select(v => v.ParameterType).ToArray();
+                var pb = tb.DefineProperty(p.Name, p.Attributes, p.PropertyType, pTypes);
+                if (p.CanRead)
+                {
+                    if (pTypes.Length > 0)
+                    {//索引器
+                        var mb = GenerateIndexer(p.GetGetMethod());
+                        pb.SetGetMethod(mb);
+                    }
+                    else
+                    {
+                        var mb = GenerateProperty(p.GetGetMethod());
+                        pb.SetGetMethod(mb);
+                    }
+                }
+                if (p.CanWrite)
+                {
+                    if (pTypes.Length > 0)
+                    {
+                        var mb = GenerateIndexer(p.GetSetMethod());
+                        pb.SetSetMethod(mb);
+                    }
+                    else
+                    {
+                        var mb = GenerateProperty(p.GetSetMethod());
+                        pb.SetSetMethod(mb);
+                    }
+                }
+            }
+        }
+
+        private static void InitEvents()
+        {
+            var evts = GetInterfaces().SelectMany(v => v.GetEvents());
+
+            foreach (var evt in evts)
+            {
+                var eb = tb.DefineEvent(evt.Name, evt.Attributes, evt.EventHandlerType);
+                var add = GenerateEventEnabled(evt, evt.GetAddMethod());
+                eb.SetAddOnMethod(add);
+                var remove = GenerateEventEnabled(evt, evt.GetRemoveMethod());
+                eb.SetRemoveOnMethod(remove);
+            }
+        }
+
+        private static MethodBuilder GenerateMethod(MethodInfo m)
+        {
+            var pTypes = m.GetParameters().Select(v => v.ParameterType).ToArray();
+            var mb = tb.DefineMethod(m.Name, METHOD_ATTRIBUTES, CallingConventions.Standard, m.ReturnType, pTypes);
+            var ilGen = mb.GetILGenerator();
+
+            ilGen.DeclareLocal(typeof(object[]));//声明object数组变量
+
+            #region 参数准备
+
+            ilGen.Emit(OpCodes.Ldarg_0);  //this
+            ilGen.Emit(OpCodes.Ldstr, m.Name);  //string name
+
+            ilGen.Emit(OpCodes.Ldc_I4_S, pTypes.Length);//数组长度
+            ilGen.Emit(OpCodes.Newarr, typeof(object));//数组
+            ilGen.Emit(OpCodes.Stloc_0);
+
+            for (int i = 0; i < pTypes.Length; i++)
+            {
+                ilGen.Emit(OpCodes.Ldloc_0);
+                ilGen.Emit(OpCodes.Ldc_I4_S, i);
+                ilGen.Emit(OpCodes.Ldarg_S, 1 + i);
+                ilGen.Emit(OpCodes.Box, pTypes[i]);
+                ilGen.Emit(OpCodes.Stelem_Ref);
+            }
+
+            #endregion 参数准备
+
+            ilGen.Emit(OpCodes.Ldloc_0);
+
+            if (m.ReturnType != typeof(void))
+            {
+                var mi = typeof(TBase).GetMethod("CallReturn", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+                mi = mi.MakeGenericMethod(m.ReturnType);
+                ilGen.Emit(OpCodes.Call, mi);
+                LocalBuilder local = ilGen.DeclareLocal(m.ReturnType);
+                ilGen.Emit(OpCodes.Stloc_1, local);
+                ilGen.Emit(OpCodes.Ldloc_1, local);
+            }
+            else
+            {
+                var mi = typeof(TBase).GetMethod("CallVoid", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+                ilGen.Emit(OpCodes.Call, mi);
+            }
+            ilGen.Emit(OpCodes.Ret);
+            return mb;
+        }
+
+        private static MethodBuilder GenerateProperty(MethodInfo m)
+        {
+            var pTypes = m.GetParameters().Select(v => v.ParameterType).ToArray();
+            var mb = tb.DefineMethod(m.Name, METHOD_ATTRIBUTES, CallingConventions.Standard, m.ReturnType, pTypes);
+            var ilGen = mb.GetILGenerator();
+
+            #region 参数准备
+
+            ilGen.Emit(OpCodes.Ldarg_0);  //this
+            ilGen.Emit(OpCodes.Ldstr, m.Name.Replace("set_", "").Replace("get_", ""));  //string name
+
+            #endregion 参数准备
+
+            if (m.Name.StartsWith("set_"))
+            {
+                ilGen.Emit(OpCodes.Ldarg_1);//value  准备参数
+                var mi = typeof(TBase).GetMethod("Set", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+                mi = mi.MakeGenericMethod(pTypes);
+                ilGen.Emit(OpCodes.Call, mi); //调用基类方法
+                //ilGen.Emit(OpCodes.Pop);
+            }
+            else
+            {
+                var mi = typeof(TBase).GetMethod("Get", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+                mi = mi.MakeGenericMethod(m.ReturnType);
+                ilGen.Emit(OpCodes.Call, mi);//调用方法
+
+                #region 返回值
+
+                LocalBuilder local = ilGen.DeclareLocal(m.ReturnType);
+                ilGen.Emit(OpCodes.Stloc_0, local);
+                ilGen.Emit(OpCodes.Ldloc_0, local);
+
+                #endregion 返回值
+            }
+            ilGen.Emit(OpCodes.Ret);//return
+            return mb;
+        }
+
+        private static MethodBuilder GenerateIndexer(MethodInfo m)
+        {
+            var pTypes = m.GetParameters().Select(v => v.ParameterType).ToArray();
+            var mb = tb.DefineMethod(m.Name, METHOD_ATTRIBUTES, CallingConventions.Standard, m.ReturnType, pTypes);
+            var ilGen = mb.GetILGenerator();
+
+            #region 参数准备
+
+            ilGen.DeclareLocal(typeof(object[]));//声明object数组变量
+
+            ilGen.Emit(OpCodes.Ldarg_0);  //this
+
+            ilGen.Emit(OpCodes.Ldc_I4_S, pTypes.Length);//数组长度
+            ilGen.Emit(OpCodes.Newarr, typeof(object));//数组
+            ilGen.Emit(OpCodes.Stloc_0);
+
+            int i = 0;
+            for (i = 0; i < pTypes.Length; i++)
+            {
+                ilGen.Emit(OpCodes.Ldloc_0);
+                ilGen.Emit(OpCodes.Ldc_I4_S, i);
+                ilGen.Emit(OpCodes.Ldarg_S, 1 + i);
+                ilGen.Emit(OpCodes.Box, pTypes[i]);
+                ilGen.Emit(OpCodes.Stelem_Ref);
+            }
+
+            #endregion 参数准备
+
+            ilGen.Emit(OpCodes.Ldloc_0);
+
+            if (m.Name.StartsWith("set"))
+            {
+                //ilGen.Emit(OpCodes.Ldarg_S, 1 + i);//value  准备参数
+                var mi = typeof(TBase).GetMethod("SetItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+                ilGen.Emit(OpCodes.Call, mi); //调用基类方法
+                //ilGen.Emit(OpCodes.Pop);
+            }
+            else
+            {
+                var mi = typeof(TBase).GetMethod("GetItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+                mi = mi.MakeGenericMethod(m.ReturnType);
+                ilGen.Emit(OpCodes.Call, mi);//调用方法
+
+                #region 返回值
+
+                LocalBuilder local = ilGen.DeclareLocal(m.ReturnType);
+                ilGen.Emit(OpCodes.Stloc_1, local);
+                ilGen.Emit(OpCodes.Ldloc_1, local);
+
+                #endregion 返回值
+            }
+            ilGen.Emit(OpCodes.Ret);//return
+            return mb;
+        }
+
+        private static MethodBuilder GenerateEventEnabled(EventInfo evt, MethodInfo m)
+        {
+            bool add = m.Name.StartsWith("add_");
+            var n = m.DeclaringType.Name;
+            var pTypes = m.GetParameters().Select(v => v.ParameterType).ToArray();
+            var mb = tb.DefineMethod(m.Name, METHOD_ATTRIBUTES, CallingConventions.Standard, m.ReturnType, pTypes);
+            var ilGen = mb.GetILGenerator();
+
+            //ilGen.DeclareLocal(typeof(EventInfo));//声明字符串变量
+            //ilGen.DeclareLocal(typeof(Delegate)); //value Delegate
+
+            //ilGen.Emit(OpCodes.Ldstr, evt.Name);//string evtName 字符串值
+            //ilGen.Emit(OpCodes.Stloc_0);//保存至变量
+            //ilGen.Emit(OpCodes.Ldarg_1);//Delegate dlg
+            //ilGen.Emit(OpCodes.Stloc_1);//保存至变量
+
+            #region 参数准备
+
+            ilGen.Emit(OpCodes.Ldarg_0);
+            ilGen.Emit(OpCodes.Ldstr, evt.Name);
+            ilGen.Emit(OpCodes.Ldarg_1);
+
+            #endregion 参数准备
+
+            var mname = add ? "AddEvent" : "RemoveEvent";
+            ilGen.Emit(OpCodes.Call, typeof(TBase).GetMethod(mname, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));
+
+            ilGen.Emit(OpCodes.Ret);
+            return mb;
+        }
+
+        private static Type[] GetInterfaces()
+        {
+            var tInterface = typeof(T);
+            var ts = tInterface.GetInterfaces().ToList();
+            ts.Add(tInterface);
+            return ts.ToArray();
+        }
+
+        private static MemberInfo[] GetMembers()
+        {
+            var members = GetInterfaces().SelectMany(v => v.GetMembers()).Distinct().ToArray();
+            return members;
+        }
+
+        public static T Create(params object[] args)
+        {
+            return (T)Activator.CreateInstance(Type, args);
+        }
+
+        public static Type GetGenerateType()
+        {
+            return Type;
+        }
+    }
+}

+ 18 - 0
WCS.Core/IL/IProtocolProxy.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Core
+{
+    public interface IProtocolProxy
+    {
+        void Set<T>(string propertyName, T value);
+        T Get<T>(string propertyName);
+        T CallReturn<T>(string methodName, params object[] args);
+        void CallVoid(string methodName, params object[] args);
+        void AddEvent(string eventName, Delegate delgate);
+        void RemoveEvent(string eventName, Delegate delgate);
+    }
+}

+ 148 - 0
WCS.Core/LogHelper.cs

@@ -0,0 +1,148 @@
+using System;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Linq.Expressions;
+using System.Threading;
+
+namespace WCS.Core
+{
+    /// <summary>
+    /// Rdeis 数据上抛
+    /// </summary>
+    public static class Ltc
+    {
+        private static ConcurrentDictionary<Thread, string> Channels = new ConcurrentDictionary<Thread, string>();
+
+        public static void SetChannel(string channel)
+        {
+            Channels[Thread.CurrentThread] = channel;
+        }
+
+        public static string GetChannel()
+        {
+            return Channels[Thread.CurrentThread];
+        }
+
+        public static void Log(string msg)
+        {
+            var channel = GetChannel();
+            if (channel == "刷新")
+            {
+                return;
+            }
+            DebugPublisher.Publish(GetChannel(), msg);
+        }
+
+        private static string ResultString<T>(T obj)
+        {
+            if (obj == null)
+            {
+                return "null";
+            }
+            else if (obj is bool)
+            {
+                var b = obj as Boolean?;
+                return b.Value ? "成立" : "不成立";
+            }
+            else if (obj is ICollection)
+            {
+                var coll = obj as ICollection;
+                return coll.Count.ToString() + "元素";
+            }
+
+            return obj.ToString();
+        }
+
+        public static T Do<T>(Expression<Func<T>> exp)
+        {
+            var msg = exp.ExpToString();
+            msg += "  结果:";
+            try
+            {
+                var res = exp.Compile().Invoke();
+
+                return res;
+            }
+            catch (Exception ex)
+            {
+                msg += ex.GetBaseException().Message;
+                throw;
+            }
+            finally
+            {
+                Log(msg);
+            }
+        }
+
+        public static T Do<T1, T>(T1 obj, Expression<Func<T1, T>> exp)
+        {
+            var msg = "";
+            try
+            {
+                try
+                {
+                    msg = exp.ExpToString();
+                }
+                catch (Exception)
+                {
+                    //TODO:日志记录
+                }
+                msg += "  结果:";
+                var res = exp.Compile().Invoke(obj);
+                msg += ResultString(res);
+                return res;
+            }
+            catch (Exception ex)
+            {
+                msg += ex.GetBaseException().Message;
+                throw;
+            }
+            finally
+            {
+                Log(msg);
+            }
+        }
+
+        public static T Do<T1, T2, T>(T1 obj, T2 obj2, Expression<Func<T1, T2, T>> exp)
+        {
+            var msg = exp.ExpToString();
+            msg += "  结果:";
+            try
+            {
+                var res = exp.Compile().Invoke(obj, obj2);
+                msg += ResultString(res);
+                return res;
+            }
+            catch (Exception ex)
+            {
+                msg += ex.GetBaseException().Message;
+                throw;
+            }
+            finally
+            {
+                Log(msg);
+            }
+        }
+
+        public static T Do<T1, T2, T3, T>(T1 obj, T2 obj2, T3 obj3, Expression<Func<T1, T2, T3, T>> exp)
+        {
+            var msg = exp.ExpToString();
+            msg += "  结果:";
+            try
+            {
+                var res = exp.Compile().Invoke(obj, obj2, obj3);
+                msg += ResultString(res);
+                return res;
+            }
+            catch (Exception ex)
+            {
+                msg += ex.GetBaseException().Message;
+                throw;
+            }
+            finally
+            {
+                Log(msg);
+            }
+        }
+    }
+}

+ 370 - 0
WCS.Core/LogicHandler.cs

@@ -0,0 +1,370 @@
+using Log;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using WCS.Entity;
+using WCS.Service;
+
+namespace WCS.Core
+{
+    /// <summary>
+    /// 逻辑处理器
+    /// </summary>
+    public abstract class LogicHandler
+    {
+        /// <summary>
+        /// 停止
+        /// </summary>
+        protected bool Stoped = false;
+
+        /// <summary>
+        /// 描述
+        /// </summary>
+        protected string Description { get; }
+
+        /// <summary>
+        /// 所有对象
+        /// </summary>
+        public static List<object> AllObjects = new List<object>();
+
+        /// <summary>
+        /// 所有处理器
+        /// </summary>
+        private static readonly List<LogicHandler> Handlers = new List<LogicHandler>();
+
+        /// <summary>
+        /// 日志委托,暂时弃用
+        /// </summary>
+        [Obsolete]
+        public static Action<string, string, string> DbLog;
+
+        /// <summary>
+        /// 逻辑处理器
+        /// </summary>
+        protected LogicHandler()
+        {
+            var attr = this.GetType().GetCustomAttributes(false).OfType<DescriptionAttribute>().FirstOrDefault();
+            Description = attr != null ? attr.Description : this.GetType().Name;
+        }
+
+        /// <summary>
+        /// 开始
+        /// </summary>
+        public abstract void Start();
+
+        /// <summary>
+        /// 是否并行
+        /// </summary>
+        public virtual bool ParallelRun => false;
+
+        /// <summary>
+        /// 数据刷新时触发
+        /// </summary>
+        /// <param name="milliseconds">离上次触发的间隔时间(毫秒)</param>
+        public abstract void Update(double milliseconds);
+
+        /// <summary>
+        /// 停止
+        /// </summary>
+        public void Stop()
+        {
+            Stoped = true;
+        }
+
+        /// <summary>
+        /// 添加处理器
+        /// </summary>
+        /// <param name="handler">处理器</param>
+        public static void AddManager(LogicHandler handler)
+        {
+            Handlers.Add(handler);
+        }
+
+        /// <summary>
+        /// 启动所有的处理器
+        /// </summary>
+        public static void StartAll()
+        {
+            foreach (var h in Handlers)
+            {
+                h.Start();
+            }
+            var th = new Thread(Loop)
+            {
+                IsBackground = true //前台线程即主线程关闭,立即结束该线程
+            };
+            th.Start();
+        }
+
+        /// <summary>
+        /// 最后一次执行结束的时间
+        /// </summary>
+        private static DateTime _last = DateTime.Now;
+
+        /// <summary>
+        ///逻辑耗时
+        /// </summary>
+        private static int _logicTimes;
+
+        public static DateTime Frame { get; private set; }
+
+        /// <summary>
+        /// 主循环体,用于PLC数据的读取与逻辑执行
+        /// </summary>
+        private static void Loop()
+        {
+            var arr = AllObjects.OfType<WCS_DEVICE>().Where(v => v.ENABLED).SelectMany(v => v.DEVICEPROTOCOLS)
+                .Where(v => v.ENABLED && v.DB.ENABLED && v.DB.PLC.ENABLED)
+                .GroupBy(v => v.DB).Select(v => v.Key)
+                .ToArray();
+
+            while (true)
+            {
+                var time = new Stopwatch();
+                time.Start();
+                Frame = DateTime.Now;
+
+                Parallel.ForEach(arr, db =>
+                {
+                    try
+                    {
+                        Ltc.SetChannel("刷新");
+                        db.Ex().DataRefresh();
+                    }
+                    catch (Exception ex)
+                    {
+                        Console.WriteLine("更新" + db.NAME + "数据失败:" + ex.Message);
+                    }
+                });
+                time.Stop();
+
+                var total = (DateTime.Now - _last).TotalMilliseconds;
+                var s = (int)(600 - total);
+                if (s > 0)
+                    Thread.Sleep(s);
+                total = (DateTime.Now - _last).TotalMilliseconds;
+                Console.ForegroundColor = ConsoleColor.Blue;
+                //此处打印的logicTimes实际是上一个周期的业务处理时长
+                Console.WriteLine("------刷新DB块数据耗时:" + time.ElapsedMilliseconds + ";业务逻辑耗时:" + _logicTimes.ToString().PadRight(4, ' ') + ";周期总耗时" + ((int)total).ToString().PadRight(4, ' ') + "");
+                Console.ResetColor();
+                _last = DateTime.Now;
+
+                Parallel.ForEach(Handlers, m =>
+                {
+                    var dm = DateTime.Now;
+                    try
+                    {
+                        m.Update(total);
+                    }
+                    catch (Exception)
+                    {
+                        //TODO:增加一个异常记录
+                    }
+                    var dm2 = (DateTime.Now - dm).TotalMilliseconds;
+                });
+
+                Configs.Publish();
+                _logicTimes = (int)(DateTime.Now - _last).TotalMilliseconds;
+            }
+        }
+
+        /// <summary>
+        /// 停止所有的处理器
+        /// </summary>
+        public static void StopAll()
+        {
+            foreach (var h in Handlers)
+            {
+                try
+                {
+                    h.Stop();
+                }
+                catch
+                {
+                    // TODO:等待处理
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    /// 逻辑处理器 泛型
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public abstract class LogicHandler<T> : LogicHandler where T : EntityEx<WCS_DEVICE>
+    {
+        /// <summary>
+        /// 业务类集合
+        /// </summary>
+        protected List<WorkInfo<T>> Works = new List<WorkInfo<T>>();
+
+        /// <summary>
+        /// TODO:?
+        /// </summary>
+        private IEnumerable<T> _objects = null;
+
+        protected IEnumerable<T> Objects
+        {
+            get
+            {
+                _objects ??= AllObjects.OfType<WCS_DEVICE>().Where(SelectDevice)
+                    .Select(v => Activator.CreateInstance(typeof(T), v)).OfType<T>().ToArray();
+                return _objects.Where(v => v.Entity.ENABLED && v.Entity.DEVICEPROTOCOLS.All(d => d.ENABLED && d.DB.ENABLED && d.DB.PLC.ENABLED));
+            }
+        }
+
+        /// <summary>
+        /// 写入设备信息
+        /// </summary>
+        /// <param name="dev">设备</param>
+        /// <returns></returns>
+        private static bool SelectDevice(WCS_DEVICE dev)
+        {
+            var typenames = typeof(T).GenericTypeArguments.Select(v => v.AssemblyQualifiedName).ToArray();
+            var res = typenames.All(v => dev.DEVICEPROTOCOLS.Any(d => d.DB.PROTOCOL == v));
+            return res;
+        }
+
+        /// <summary>
+        /// 添加Work
+        /// </summary>
+        /// <param name="condition"></param>
+        /// <param name="work">work</param>
+        /// <param name="parallel">是否并发</param>
+        public void AddWork(Func<T, bool> condition, Action<T> work, bool parallel = false)
+        {
+            var title = work.Method.Name;
+            var attr = work.Method.GetCustomAttributes(false).OfType<DescriptionAttribute>().FirstOrDefault();
+            if (attr != null)
+                title = attr.Description;
+            var arr = Objects.Where(condition).ToArray();
+            this.Works.Add(new WorkInfo<T> { Work = work, Params = arr, Title = title, Parallel = parallel });
+        }
+
+        /// <summary>
+        ///
+        /// </summary>
+        /// <param name="arr"></param>
+        /// <param name="work"></param>
+        /// <param name="parallel"></param>
+        public void AddWork(IEnumerable<T> arr, Action<T> work, bool parallel = false)
+        {
+            var title = work.Method.Name;
+            var attr = work.Method.GetCustomAttributes(false).OfType<DescriptionAttribute>().FirstOrDefault();
+            if (attr != null)
+                title = attr.Description;
+
+            this.Works.Add(new WorkInfo<T> { Work = work, Params = arr, Title = title, Parallel = parallel });
+        }
+
+        /// <summary>
+        /// 开始执行业务流程
+        /// </summary>
+        /// <param name="milliseconds"></param>
+        public override void Update(double milliseconds)
+        {
+            if (ParallelRun)
+            {
+                Parallel.ForEach(Works, DoWork);
+            }
+            else
+            {
+                foreach (var w in Works)
+                {
+                    DoWork(w);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 执行Work
+        /// </summary>
+        /// <param name="work"></param>
+        protected virtual void DoWork(WorkInfo<T> work)
+        {
+            if (work.Parallel)
+            {
+                Parallel.ForEach(work.Params, p =>
+                {
+                    Do(work, p);
+                });
+            }
+            else
+            {
+                foreach (var p in work.Params)
+                {
+                    Do(work, p);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 开始执行
+        /// </summary>
+        /// <param name="wi"></param>
+        /// <param name="p"></param>
+        protected virtual void Do(WorkInfo<T> wi, T p)
+        {
+            var channel = Description + "." + wi.Title + "." + p;
+            try
+            {
+                Ltc.SetChannel(channel);
+                Ltc.Log("开始---------------------------------------");
+
+                wi.Work(p);
+            }
+            catch (DoException ex)
+            {
+                InfoLog.INFO_INFO($"[{channel}]--{ex.Message}");
+            }
+            //WarnException进阶条件未满足,添加数据库,记录文本日志、数据库,上抛WCS,上抛WMS
+            catch (WarnException ex)
+            {
+                InfoLog.INFO_WARN($"[{channel}]--{ex.Message}");
+                Ltc.Log(ex.GetBaseException().Message);
+                Configs.UploadException?.Invoke(p.ToString(), ex.GetBaseException().Message);
+            }
+            //未知异常,仅记录文本日志,需定期排查该文件,检查系统是否有未知异常,并处理
+            catch (Exception ex)
+            {
+                InfoLog.INFO_ERROR($"[{channel}]--{ex.Message}--{ex.StackTrace}");
+            }
+            finally
+            {
+                Ltc.Log("结束\n");
+            }
+        }
+
+        /// <summary>
+        /// 此方法不会被自动调用,请在Start方法中使用AddWork将其添加至工作队列
+        /// </summary>
+        /// <param name="dev"></param>
+        [Description("执行")]
+        protected abstract void Execute(T dev);
+    }
+
+    public class WorkTitleAttribute : Attribute
+    {
+        public Type Handler { get; set; }
+        public string Title { get; set; }
+        public bool Parallel { get; set; }
+
+        public WorkTitleAttribute(Type handler, string title, bool parallel = false)
+        {
+            this.Handler = handler;
+            this.Title = title;
+            this.Parallel = parallel;
+        }
+    }
+
+    public abstract class Work
+    {
+        public abstract IEnumerable<object> GetObjs();
+
+        public abstract void Execute(object obj);
+    }
+}

+ 19 - 0
WCS.Core/WCS.Core.csproj

@@ -0,0 +1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.1</TargetFramework>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Content Remove="C:\Users\ZUOLINHAO\.nuget\packages\hslcommunication\6.2.2\contentFiles\any\netstandard2.0\Doc.txt" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="HslCommunication" Version="6.2.2" />
+    <PackageReference Include="WCS.DbHelper" Version="1.0.0" />
+    <PackageReference Include="WCS.Entity" Version="1.0.2" />
+    <PackageReference Include="WCS.Redis" Version="1.0.2" />
+  </ItemGroup>
+
+</Project>

+ 32 - 0
WCS.Core/WorkInfo.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Core
+{
+    public class WorkInfo<T>
+    {
+        public Action<T> Work { get; set; }
+
+        public IEnumerable<T> Params { get; set; }
+
+        public string Title { get; set; }
+
+        public bool Parallel { get; set; }
+    }
+
+    public class WorkInfo
+    { 
+        public virtual Action<object> Work { get; set; }
+
+        public IEnumerable<object> Params { get; set; }
+
+        public string Title { get; set; }
+
+        public bool Parallel { get; set; }
+    }
+   
+}

+ 123 - 0
WCS.DbHelper/DB.cs

@@ -0,0 +1,123 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace DbHelper
+{
+    /// <summary>
+    /// DB,禁止跨上下文使用
+    /// 1.异步情况: 在同一串await 中是一个上下文 (await 会改变线程和同步是不一样的)
+    /// 2.同步情况: 在同一个线程是同一个上下文
+    /// </summary>
+    public class Db
+    {
+        /// <summary>
+        /// 上下文集合
+        /// </summary>
+        private static List<ContextList> _contexts = new List<ContextList>();
+
+        /// <summary>
+        /// 默认上下文类类型
+        /// </summary>
+        public static string DefaultDbContextType { get; private set; } = null!;
+
+        public static T Do<T>(Func<Db, T> func)
+        {
+            var db = new Db();
+            try
+            {
+                db.Default.BeginTran();
+                var res = func(db);
+                db.Default.CommitTran();
+                return res;
+            }
+            catch (Exception 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(ex.Message);
+                throw new Exception(ex.Message);
+            }
+        }
+
+        public static void Do(Action<Db> act)
+        {
+            Do(db =>
+            {
+                act(db);
+                return 1;
+            });
+        }
+
+        /// <summary>
+        /// 设置默认链接
+        /// </summary>
+        /// <param name="key"></param>
+        public static void SetDefaultDbContextType(string key)
+        {
+            DefaultDbContextType = key;
+        }
+
+        /// <summary>
+        /// 默认链接
+        /// </summary>
+        public SqlSugarScope Default
+        {
+            get
+            {
+                if (DefaultDbContextType == null)
+                    throw new Exception("请先设置默认数据库,调用静态方法SetDefaultDbContextType()");
+                return Context(DefaultDbContextType);
+            }
+        }
+
+        /// <summary>
+        /// 创建一个上下文
+        /// </summary>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        public static SqlSugarScope CreateContext(ConnectionConfig config, string key)
+        {
+            var ctx = _contexts.FirstOrDefault(v => v.Key == key);
+            if (ctx != null) return ctx.Client;
+            ctx = new ContextList(key, new SqlSugarScope(new ConnectionConfig()
+            {
+                ConnectionString = config.ConnectionString,
+                DbType = config.DbType,
+                IsAutoCloseConnection = true
+            }), config);
+
+            _contexts.Add(ctx);
+            return ctx.Client;
+        }
+
+        public SqlSugarScope Context(string key)
+        {
+            var ctx = _contexts.FirstOrDefault(v => v.Key == key);
+            if (ctx == null) throw new Exception("没有对应的链接,请先调用创建");
+            return ctx.Client;
+        }
+    }
+
+    /// <summary>
+    /// 链接
+    /// </summary>
+    public class ContextList
+
+    {
+        public ContextList(string key, SqlSugarScope client, ConnectionConfig connectionConfig)
+        {
+            this.Key = key;
+            Client = client;
+            ConnectionConfig = connectionConfig;
+        }
+
+        public string Key { get; set; }
+
+        public SqlSugarScope Client { get; set; }
+
+        public ConnectionConfig ConnectionConfig { get; set; }
+    }
+}

+ 54 - 0
WCS.DbHelper/DbContext.cs

@@ -0,0 +1,54 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DbHelper
+{
+    public class DbContext
+    {
+        private static ConnectionConfig? _config = null;
+
+        public static void SetConfig(ConnectionConfig value)
+        {
+            _config = value;
+        }
+
+        /// <summary>
+        /// 用单例模式
+        /// </summary>
+        private static readonly SqlSugarScope SqlSugarScope = new SqlSugarScope(new ConnectionConfig()
+        {
+            ConnectionString = _config!.ConnectionString, //连接符字串
+            DbType = _config.DbType, //数据库类型
+            IsAutoCloseConnection = true //不设成true要手动close
+        }, db =>
+        {
+            //(A)全局生效配置点
+            //调试SQL事件,可以删掉
+            db.Aop.OnLogExecuting = (sql, pars) =>
+            {
+                DbLog.DBEX(sql);
+                //输出sql,查看执行sql
+                //5.0.8.2 获取无参数化 SQL
+                //UtilMethods.GetSqlString(DbType.SqlServer,sql,pars)
+            };
+        });
+
+        /// <summary>
+        /// 获取db链接
+        /// </summary>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public static SqlSugarScope Db
+        {
+            get
+            {
+                if (_config == null) throw new Exception("请使用SetConfig方法写入数据库链接信息");
+                return SqlSugarScope;
+            }
+        }
+    }
+}

+ 57 - 0
WCS.DbHelper/DbLog.cs

@@ -0,0 +1,57 @@
+using Log;
+using System.Collections.Generic;
+
+namespace DbHelper
+{
+    public class DbLog : ILogType
+    {
+        /// <summary>
+        /// 日志名称
+        /// </summary>
+        public string LogName => "Db";
+
+        /// <summary>
+        /// 日志
+        /// </summary>
+        public static ILogType Log { get; set; }
+
+        public Dictionary<string, string> SubLogNames { get; set; }
+
+        static DbLog()
+        {
+            Log = new DbLog();
+        }
+
+        public DbLog()
+        {
+            SubLogNames = new Dictionary<string, string>
+            {
+                ["DB"] = "Db"
+            };
+            LogHelper.Init(this);
+        }
+
+        /// <summary>
+        /// 用于记录DB执行内容
+        /// </summary>
+        /// <param name="msg"></param>
+        public static void DBEX(string msg)
+        {
+            Log.Info(msg, "DB_EX");
+        }
+
+        /// <summary>
+        /// 用于记录DB清理记录
+        /// </summary>
+        /// <param name="msg"></param>
+        public static void DB_CLEAN(string msg)
+        {
+            Log.Info(msg, "DB_CLEAN");
+        }
+
+        public static void INFO_PLCREADLOG(string msg)
+        {
+            Log.Info(msg, "INFO_PLCREADLOG");
+        }
+    }
+}

+ 15 - 0
WCS.DbHelper/WCS.DbHelper.csproj

@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.1</TargetFramework>
+    <Nullable>enable</Nullable>
+    <GenerateDocumentationFile>True</GenerateDocumentationFile>
+    <Description>$(describe)</Description>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="SqlSugarCore" Version="5.1.2.3" />
+    <PackageReference Include="WCS.Log" Version="1.0.1" />
+  </ItemGroup>
+
+</Project>

+ 14 - 0
WCS.Entity/IDATA.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 协议
+    /// </summary>
+    public interface IProtocol
+    {
+
+    }
+}

+ 12 - 0
WCS.Entity/IgnoreChangingAttribute.cs

@@ -0,0 +1,12 @@
+using System;
+
+namespace WCS.Entity
+{
+    [AttributeUsage(AttributeTargets.Property)]
+    public class IgnoreChangingAttribute : Attribute
+    {
+        public IgnoreChangingAttribute() { }
+
+        public long IgnoreValueRange { get; set; }
+    }
+}

+ 28 - 0
WCS.Entity/LocInfo.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Entity
+{
+    public class LocInfo
+    {
+        public LocInfo()
+        { 
+        
+        }
+
+        public LocInfo(short line,short col,short layer,short depth)
+        {
+            this.Line = line;
+            this.Col = col;
+            this.Layer = layer;
+            this.Depth = depth;
+        }
+        public short Line { get; set; }
+        public short Col { get; set; }
+        public short Layer { get; set; }
+        public short Depth { get; set; }
+    }
+}

+ 66 - 0
WCS.Entity/OBJ.cs

@@ -0,0 +1,66 @@
+using SqlSugar;
+using System;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 实体类通用基础类
+    /// </summary>
+    [Serializable]
+    public abstract class OBJ
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "ID")]
+        [Column(Order = 0)]
+        public virtual int ID { get; set; }
+
+        /// <summary>
+        /// 是否可用
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否可用")]
+        public bool ENABLED { get; set; } = true;
+
+        /// <summary>
+        /// 更新用户
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新用户", Length = 50)]
+        public string UPDATEUSER { get; set; }
+
+        /// <summary>
+        /// 更新用户详细信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(UPDATEUSER))]
+        public WCS_USERS USER { get; set; }
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "更新时间")]
+        public DateTime UPDATETIME { get; set; } = DateTime.Now;
+
+        /// <summary>
+        /// 版本号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "版本号", ColumnDataType = "timestamp", IsNullable = true, IsOnlyIgnoreInsert = true, IsOnlyIgnoreUpdate = true)]
+        public byte[] VER { get; set; }
+
+        /// <summary>
+        ///
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <returns></returns>
+        public T Copy<T>() where T : OBJ
+        {
+            var instance = Activator.CreateInstance<T>();
+            foreach (var property in GetType().GetProperties())
+            {
+                var obj = property.GetValue(this);
+                typeof(T).GetProperty(property.Name)!.SetValue(instance, obj);
+            }
+            return instance;
+        }
+    }
+}

+ 24 - 0
WCS.Entity/Playerback.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace WCS.Entity
+{
+    [DataContract]
+    public class Playerback
+    {
+        [DataMember(Order = 0)]
+        public string ClientID { get; set; }
+
+        [DataMember(Order = 1)]
+        public int Secondes { get; set; }
+
+        [DataMember(Order = 2)]
+        public int Speed { get; set; } = 1;
+
+        [DataMember(Order = 3)]
+        public DateTime HeartBeat { get; set; }
+         
+    }
+}

+ 15 - 0
WCS.Entity/WCS.Entity.csproj

@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.1</TargetFramework>
+    <Nullable>enable</Nullable>
+    <GenerateDocumentationFile>True</GenerateDocumentationFile>
+    <Description>$(describe)</Description>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="SqlSugarCore" Version="5.1.2.3" />
+    <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
+  </ItemGroup>
+
+</Project>

+ 47 - 0
WCS.Entity/WCS_CMD.cs

@@ -0,0 +1,47 @@
+using SqlSugar;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// WCS用户用写入PLC记录表
+    /// </summary>
+    [SugarTable(nameof(WCS_CMD), "WCS用户用写入PLC记录表")]
+    public class WCS_CMD : OBJ
+    {
+        /// <summary>
+        /// 关联设备号
+        /// </summary>
+        [SugarColumn(Length = 20, ColumnDescription = "关联设备号")]
+        public string DEVICE_CODE { get; set; }
+
+        /// <summary>
+        /// 关联设备信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, "DEVICE_CODE")]
+        public WCS_DEVICE DEVICE { get; set; }
+
+        /// <summary>
+        /// 协议
+        /// </summary>
+        [SugarColumn(Length = 256, ColumnDescription = "协议")]
+        public string PROTOCOL { get; set; }
+
+        /// <summary>
+        /// 属性
+        /// </summary>
+        [SugarColumn(Length = 32, ColumnDescription = "属性")]
+        public string PROPERTY { get; set; }
+
+        /// <summary>
+        /// 值
+        /// </summary>
+        [SugarColumn(Length = 512, ColumnDescription = "值")]
+        public string VALUE { get; set; }
+
+        /// <summary>
+        /// WCS版本号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "WCS版本号")]
+        public int WCSVERSION { get; set; }
+    }
+}

+ 78 - 0
WCS.Entity/WCS_DATABLOCK.cs

@@ -0,0 +1,78 @@
+using SqlSugar;
+using System;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// PLC DB块配置表
+    /// </summary>
+    [SugarTable(nameof(WCS_DATABLOCK), "PLC DB块配置表")]
+    public class WCS_DATABLOCK : OBJ
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        [Obsolete]
+        public override int ID { get; set; }
+
+        /// <summary>
+        /// 编号
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, Length = 20, ColumnDescription = "编号")]
+        public string CODE { get; set; }
+
+        /// <summary>
+        /// 名称
+        /// </summary>
+        [SugarColumn(Length = 20, ColumnDescription = "名称")]
+        public string NAME { get; set; }
+
+        /// <summary>
+        /// 关联PLC信息编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "关联PLC信息编号")]
+        public string PLCCODE { get; set; }
+
+        /// <summary>
+        /// 关联PLC信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(PLCCODE))]
+        public WCS_PLC PLC { get; set; }
+
+        /// <summary>
+        /// 西门子:DB520,其它:D0
+        /// </summary>
+        [SugarColumn(ColumnDescription = "编号")]
+        public int NO { get; set; }
+
+        /// <summary>
+        /// 总长度
+        /// </summary>
+        [SugarColumn(ColumnDescription = "总长度")]
+        public int LENGTH { get; set; }
+
+        /// <summary>
+        /// 结构体长度
+        /// </summary>
+        [SugarColumn(ColumnDescription = "结构体长度")]
+        public int DATALENGTH { get; set; }
+
+        /// <summary>
+        /// 协议类型
+        /// </summary>
+        [SugarColumn(Length = 200, ColumnDescription = "协议类型")]
+        public string PROTOCOL { get; set; }
+
+        /// <summary>
+        /// 无更新
+        /// </summary>
+        [SugarColumn(ColumnDescription = "无更新")]
+        public bool NOUPDATE { get; set; }
+
+        public Type GetProtocolType()
+        {
+            return Type.GetType(PROTOCOL);
+        }
+    }
+}

+ 58 - 0
WCS.Entity/WCS_DEVICE.cs

@@ -0,0 +1,58 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 设备列表
+    /// </summary>
+    [SugarTable(nameof(WCS_DEVICE), "设备列表")]
+    public class WCS_DEVICE : OBJ
+    {
+        [SugarColumn(IsIgnore = true)]
+        [Obsolete]
+        public override int ID { get; set; }
+
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, ColumnDescription = "设备编号", Length = 50)]
+        public string CODE { get; set; }
+
+        /// <summary>
+        /// 设备名称
+        /// </summary>
+        [SugarColumn(Length = 50, ColumnDescription = "设备名称")]
+        public string NAME { get; set; }
+
+        /// <summary>
+        /// 设备可用协议集合
+        /// </summary>
+        [Navigate(NavigateType.OneToMany, nameof(WCS_DEVICEPROTOCOL.DEVICECODE))]
+        public List<WCS_DEVICEPROTOCOL> DEVICEPROTOCOLS { get; set; }
+
+        /// <summary>
+        /// 设备可用路由集合
+        /// </summary>
+        [Navigate(NavigateType.OneToMany, nameof(WCS_ROUTE.DEVICECODE))]
+        public List<WCS_ROUTE> ROUTES { get; set; }
+
+        /// <summary>
+        /// 设备可用路径集合
+        /// </summary>
+        [Navigate(NavigateType.OneToMany, nameof(WCS_PATH.STARTCODE))]
+        public List<WCS_PATH> PATHS { get; set; }
+
+        /// <summary>
+        /// 设备对应设备组信息
+        /// </summary>
+        [Navigate(NavigateType.OneToMany, nameof(WCS_GROUPMEMBER.GROUPCODE))]
+        public List<WCS_GROUPMEMBER> DEVICEGROUP { get; set; }
+
+        public override string ToString()
+        {
+            return CODE;
+        }
+    }
+}

+ 41 - 0
WCS.Entity/WCS_DEVICEPROTOCOL.cs

@@ -0,0 +1,41 @@
+using SqlSugar;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// DB块读写协议
+    /// </summary>
+    [SugarTable(nameof(WCS_DEVICEPROTOCOL), "DB块读写协议")]
+    public class WCS_DEVICEPROTOCOL : OBJ
+    {
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "设备")]
+        public string DEVICECODE { get; set; }
+
+        /// <summary>
+        /// 设备信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(DEVICECODE))]
+        public WCS_DEVICE DEVICE { get; set; }
+
+        /// <summary>
+        /// DB编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "DB编号")]
+        public string DATABLOCKCODE { get; set; }
+
+        /// <summary>
+        /// DB信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(DATABLOCKCODE))]
+        public WCS_DATABLOCK DB { get; set; }
+
+        /// <summary>
+        /// 起始位置
+        /// </summary>
+        [SugarColumn(ColumnDescription = "起始位置")]
+        public short POSITION { get; set; }
+    }
+}

+ 59 - 0
WCS.Entity/WCS_EXCEPTION.cs

@@ -0,0 +1,59 @@
+using SqlSugar;
+using System;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 错误/警告
+    /// </summary>
+    [SugarTable(nameof(WCS_EXCEPTION), "错误/警告")]
+    public class WCS_EXCEPTION : OBJ
+    {
+        /// <summary>
+        /// 异常信息
+        /// </summary>
+        [SugarColumn(ColumnDescription = "异常信息", Length = 1000)]
+        public string MSG { get; set; }
+
+        /// <summary>
+        /// 关联设备号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "关联设备号", Length = 50)]
+        public string DEVICECODE { get; set; }
+
+        /// <summary>
+        /// 关联设备详细信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(DEVICECODE))]
+        public WCS_DEVICE DEVICE { get; set; }
+
+        /// <summary>
+        /// 为了展示方便直接使用 WCS_EXCEPTIONTYPE 枚举的描述
+        /// </summary>
+        [SugarColumn(ColumnDescription = "关联设备号", Length = 200)]
+        public string EXCEPTIONTYPE { get; set; }
+
+        /// <summary>
+        /// 开始时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "开始时间")]
+        public DateTime STARTTIME { get; set; }
+
+        /// <summary>
+        /// 触发次数
+        /// </summary>
+        [SugarColumn(ColumnDescription = "触发次数")]
+        public int TIMES { get; set; }
+    }
+
+    /// <summary>
+    /// 异常类型
+    /// </summary>
+    public enum WCS_EXCEPTIONTYPE : uint
+    {
+        无 = 0,
+        设备异常 = 10,
+        逻辑异常 = 20,
+        操作记录 = 30,
+    }
+}

+ 35 - 0
WCS.Entity/WCS_GROUPMEMBER.cs

@@ -0,0 +1,35 @@
+using SqlSugar;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 设备组
+    /// </summary>
+    [SugarTable(nameof(WCS_GROUPMEMBER), "设备组")]
+    public class WCS_GROUPMEMBER : OBJ
+    {
+        /// <summary>
+        /// 组设备编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "组设备编号")]
+        public string GROUPCODE { get; set; }
+
+        /// <summary>
+        /// 组设备信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(GROUPCODE))]
+        public WCS_DEVICE GROUP { get; set; }
+
+        /// <summary>
+        /// 子设备编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "子设备编号")]
+        public string MEMBERCODE { get; set; }
+
+        /// <summary>
+        /// 子设备信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(MEMBERCODE))]
+        public WCS_DEVICE MEMBER { get; set; }
+    }
+}

+ 41 - 0
WCS.Entity/WCS_MAPPINGENTRY.cs

@@ -0,0 +1,41 @@
+using SqlSugar;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 枚举映射表
+    /// </summary>
+    [SugarTable(nameof(WCS_MAPPINGENTRY), "枚举映射表")]
+    public class WCS_MAPPINGENTRY
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, ColumnDescription = "ID")]
+        public int MEP_ID { get; set; }
+
+        /// <summary>
+        /// 编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "编号")]
+        public int MEP_MAPPINGNO { get; set; }
+
+        /// <summary>
+        /// 枚举类
+        /// </summary>
+        [SugarColumn(Length = 50, ColumnDescription = "枚举类")]
+        public string MEP_MAPPINGTYPE { get; set; }
+
+        /// <summary>
+        /// 枚举名称
+        /// </summary>
+        [SugarColumn(Length = 50, ColumnDescription = "枚举名称")]
+        public string MEP_MAPPINGCHNAME { get; set; }
+
+        /// <summary>
+        /// 是否停用
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否停用")]
+        public bool MEP_ISSTOP { get; set; }
+    }
+}

+ 41 - 0
WCS.Entity/WCS_PATH.cs

@@ -0,0 +1,41 @@
+using SqlSugar;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 设备路径信息
+    /// </summary>
+    [SugarTable(nameof(WCS_PATH), "路径")]
+    public class WCS_PATH : OBJ
+    {
+        /// <summary>
+        /// 起点地址编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "起点")]
+        public string STARTCODE { get; set; }
+
+        /// <summary>
+        /// 起始点设备信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, "STARTCODE")]
+        public WCS_DEVICE START { get; set; }
+
+        /// <summary>
+        /// 终点设备编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "终点")]
+        public string ENDCODE { get; set; }
+
+        /// <summary>
+        /// 终点设备信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, "ENDCODE")]
+        public WCS_DEVICE END { get; set; }
+
+        /// <summary>
+        /// 路径
+        /// </summary>
+        [SugarColumn(ColumnDescription = "路径")]
+        public string PATH { get; set; }
+    }
+}

+ 71 - 0
WCS.Entity/WCS_PATHPOINT.cs

@@ -0,0 +1,71 @@
+using SqlSugar;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 路径详细信息
+    /// </summary>
+    [SugarTable(nameof(WCS_PATHPOINT), "路径详细信息")]
+    public class WCS_PATHPOINT : OBJ
+    {
+        /// <summary>
+        /// 起始地址设备号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "起始地址设备号")]
+        public string STARTCODE { get; set; }
+
+        /// <summary>
+        /// 起点设备详细信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(STARTCODE))]
+        public WCS_DEVICE START { get; set; }
+
+        /// <summary>
+        /// 终点地址设备号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "终点地址设备号")]
+        public string ENDCODE { get; set; }
+
+        /// <summary>
+        /// 终点设备详细信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(ENDCODE))]
+        public WCS_DEVICE END { get; set; }
+
+        /// <summary>
+        /// 路径点设备号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "路径点设备号")]
+        public string DEVICECODE { get; set; }
+
+        /// <summary>
+        /// 路径点详细信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(DEVICECODE))]
+        public WCS_DEVICE DEVICE { get; set; }
+
+        /// <summary>
+        /// 前路径点设备号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "前路径点设备号")]
+        public string PREVCODE { get; set; }
+
+        /// <summary>
+        /// 前路径点详细信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(PREVCODE))]
+        public WCS_DEVICE PREV { get; set; }
+
+        /// <summary>
+        /// 后路径点设备号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "后路径点设备号")]
+        public string NEXTCODE { get; set; }
+
+        /// <summary>
+        /// 后路径点详细信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(NEXTCODE))]
+        public WCS_DEVICE NEXT { get; set; }
+    }
+}

+ 116 - 0
WCS.Entity/WCS_PLC.cs

@@ -0,0 +1,116 @@
+using SqlSugar;
+using System;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// WCS_PLC
+    /// </summary>
+    [SugarTable(nameof(WCS_PLC), "PLC信息")]
+    public class WCS_PLC : OBJ
+    {
+        [SugarColumn(IsIgnore = true)]
+        [Obsolete]
+        public override int ID { get; set; }
+
+        /// <summary>
+        /// 编号
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, ColumnDescription = "编号", Length = 20)]
+        public string CODE { get; set; }
+
+        /// <summary>
+        /// 名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "名称", Length = 20)]
+        public string NAME { get; set; }
+
+        /// <summary>
+        /// IP
+        /// </summary>
+        [SugarColumn(ColumnDescription = "IP", Length = 20)]
+        public string IP { get; set; }
+
+        /// <summary>
+        /// 端口
+        /// </summary>
+        [SugarColumn(ColumnDescription = "端口")]
+        public int PORT { get; set; } = 102;
+
+        /// <summary>
+        /// 插槽号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "插槽号")]
+        public int SLOT { get; set; }
+
+        /// <summary>
+        /// 机架号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "机架号")]
+        public int RACK { get; set; }
+
+        /// <summary>
+        /// 厂商
+        /// </summary>
+        [SugarColumn(ColumnDescription = "厂商")]
+        public PLCType TYPE { get; set; }
+
+        /// <summary>
+        /// 型号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "型号")]
+        public PLCMODEL MODEL { get; set; }
+    }
+
+    /// <summary>
+    /// 设备厂商
+    /// </summary>
+    public enum PLCType
+    {
+        西门子 = 1,
+        三菱 = 2,
+        AB = 3,
+        欧姆龙 = 4,
+        汇川 = 5, 
+    }
+
+    /// <summary>
+    /// PLC型号
+    /// </summary>
+    public enum PLCMODEL
+    {
+        #region 西门子
+
+        /// <summary>
+        /// 1200系列
+        /// </summary>
+        S1200 = 1,
+
+        /// <summary>
+        /// 300系列
+        /// </summary>
+        S300 = 2,
+
+        /// <summary>
+        /// 400系列
+        /// </summary>
+        S400 = 3,
+
+        /// <summary>
+        /// 1500系列PLC
+        /// </summary>
+        S1500 = 4,
+
+        /// <summary>
+        /// 200的smart系列
+        /// </summary>
+        S200Smart = 5,
+
+        /// <summary>
+        /// 200系统,需要额外配置以太网模块
+        /// </summary>
+        S200 = 6
+
+        #endregion 西门子
+    }
+}

+ 41 - 0
WCS.Entity/WCS_PROTOCOLDATA.cs

@@ -0,0 +1,41 @@
+using SqlSugar;
+using System;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 协议数据抽象类,所有的设备具体协议信息都需继承该类
+    /// </summary>
+    public abstract class WCS_PROTOCOLDATA : OBJ
+    {
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "设备编号")]
+        public string DEVICECOD { get; set; }
+
+        /// <summary>
+        /// 设备详细信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(DEVICECOD))]
+        public WCS_DEVICE DEVICE { get; set; }
+
+        /// <summary>
+        /// 确认时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "确认时间")]
+        public DateTime FRAME { get; set; }
+
+        /// <summary>
+        /// 最后更新
+        /// </summary>
+        [SugarColumn(ColumnDescription = "最后更新")]
+        public bool ISLAST { get; set; }
+
+        /// <summary>
+        /// WCS版本号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "WCS版本号")]
+        public int WCSVERSION { get; set; }
+    }
+}

+ 35 - 0
WCS.Entity/WCS_ROUTE.cs

@@ -0,0 +1,35 @@
+using SqlSugar;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 设备路由表
+    /// </summary>
+    [SugarTable(nameof(WCS_ROUTE), "设备路由表")]
+    public class WCS_ROUTE : OBJ
+    {
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "设备编号")]
+        public string DEVICECODE { get; set; }
+
+        /// <summary>
+        /// 所属设备信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(DEVICECODE))]
+        public WCS_DEVICE DEVICE { get; set; }
+
+        /// <summary>
+        /// 下一个设备的编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "下一个设备的编号")]
+        public string NEXTCODE { get; set; }
+
+        /// <summary>
+        /// 下一个设备的信息
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, "NEXTCODE")]
+        public WCS_DEVICE NEXT { get; set; }
+    }
+}

+ 31 - 0
WCS.Entity/WCS_Users.cs

@@ -0,0 +1,31 @@
+using SqlSugar;
+
+namespace WCS.Entity
+{
+    /// <summary>
+    /// 用户
+    /// </summary>
+    [SugarTable(nameof(WCS_USERS), "用户")]
+    public class WCS_USERS
+    {
+        /// <summary>
+        /// 用户账号
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, Length = 128, ColumnDescription = "用户账号")]
+        public string USER_NO { get; set; }
+
+        /// <summary>
+        /// 密码
+        /// </summary>
+        [SugarColumn(Length = 128, ColumnDescription = "密码")]
+        public string USER_PWD { get; set; }
+
+        /// <summary>
+        /// 文本
+        /// </summary>
+        [SugarColumn(ColumnDescription = "文本")]
+        public byte[] USER_TEXT { get; set; }
+    }
+
+  
+}

+ 109 - 0
WCS.RedisHelper/RedisHelper.cs

@@ -0,0 +1,109 @@
+using FreeRedis;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace WCS.Redis
+{
+    /// <summary>
+    /// Redis操作类
+    /// </summary>
+    public class RedisHelper
+    {
+        /// <summary>
+        /// 连接集合
+        /// </summary>
+        private static List<RedisClientList> RedisClients = new List<RedisClientList>();
+
+        /// <summary>
+        /// 默认上下文类类型
+        /// </summary>
+        public static string DefaultDbContextType { get; private set; } = null!;
+
+        /// <summary>
+        /// 设置默认链接
+        /// </summary>
+        /// <param name="key"></param>
+        public static void SetDefaultDbContextType(string key)
+        {
+            DefaultDbContextType = key;
+        }
+
+        /// <summary>
+        /// 默认链接
+        /// </summary>
+        public static RedisClient Default()
+        {
+            if (DefaultDbContextType == null)
+                throw new Exception("请先设置默认RedisDB库,调用静态方法SetDefaultDbContextType()");
+            return Context(DefaultDbContextType);
+        }
+
+        /// <summary>
+        /// 创建一个连接
+        /// </summary>
+        /// <param name="connectionString"> 连接字符串</param>
+        /// <param name="key">标识</param>
+        /// <param name="defaultClient"> 是否为默认连接,默认为false</param>
+        /// <returns></returns>
+        public static RedisClient CreateContext(string connectionString, string key, bool defaultClient = false)
+        {
+            var ctx = RedisClients.FirstOrDefault(v => v.Key == key);
+            if (ctx != null) return ctx.Client;
+            ctx = new RedisClientList(key, new RedisClient(connectionString), connectionString);
+            RedisClients.Add(ctx);
+            if (defaultClient)
+            {
+                SetDefaultDbContextType(key);
+            }
+            return ctx.Client;
+        }
+
+        /// <summary>
+        /// 获取指定的连接
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public static RedisClient Context(string key)
+        {
+            var ctx = RedisClients.FirstOrDefault(v => v.Key == key);
+            if (ctx == null) throw new Exception("没有对应的链接,请先调用创建");
+            return ctx.Client;
+        }
+    }
+
+    /// <summary>
+    /// 链接集合
+    /// </summary>
+    public class RedisClientList
+    {
+        /// <summary>
+        /// 连接集合
+        /// </summary>
+        /// <param name="key">连接标识</param>
+        /// <param name="client">连接</param>
+        /// <param name="connectionConfig">连接字符串</param>
+        public RedisClientList(string key, RedisClient client, string connectionConfig)
+        {
+            this.Key = key;
+            Client = client;
+            ConnectionConfig = connectionConfig;
+        }
+
+        /// <summary>
+        /// 连接标识
+        /// </summary>
+        public string Key { get; set; }
+
+        /// <summary>
+        /// 连接
+        /// </summary>
+        public RedisClient Client { get; set; }
+
+        /// <summary>
+        /// 连接字符串
+        /// </summary>
+        public string ConnectionConfig { get; set; }
+    }
+}

+ 14 - 0
WCS.RedisHelper/WCS.Redis.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.1</TargetFramework>
+    <Nullable>enable</Nullable>
+    <GenerateDocumentationFile>True</GenerateDocumentationFile>
+    <Description>$(describe)</Description>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="FreeRedis" Version="0.6.5" />
+  </ItemGroup>
+
+</Project>