Browse Source

添加项目文件。

林豪 左 2 years ago
parent
commit
c25db98edf

+ 39 - 0
WCS.Core/Attributes.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Core
+{
+    public class BelongToAttribute:Attribute
+    {  
+        public Type WorldType { get; set; }
+
+        public BelongToAttribute(Type worldType)
+        {
+            this.WorldType = worldType;
+        }
+    }
+
+    public class OrderAttribute : Attribute
+    {
+        public OrderType Order { get; set; }
+        public Type SystemType { get; set; }
+
+        public OrderAttribute(OrderType order,Type sysType)
+        {
+            this.Order = order;
+            this.SystemType = sysType;
+        }
+    }
+
+    public enum OrderType
+    {
+        Before = -1,
+        After = 1
+    }
+     
+
+
+}

+ 33 - 0
WCS.Core/Configs.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text; 
+
+namespace WCS.Core
+{
+    public static class Configs
+    {
+       /// 设置协议通讯代理类的基类,继承至DataObject,IBaseType
+        /// </summary>
+        public static Type? ProtocolProxyBaseType { get; set; }
+
+
+        public static Encoding StringEncoding { 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      /// <summary>
+    { get; set; }
+
+        public static IPLCAccessorCreater? PLCAccessorCreater { get; set; }
+
+        public static Action<IEnumerable<LogInfo>> OnLog;
+    }
+}

+ 55 - 0
WCS.Core/Data.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks; 
+
+namespace WCS.Core
+{
+    public enum PLCType
+    {
+        西门子 = 1,
+        三菱 = 2,
+        欧姆龙 = 3,
+    }
+
+    public struct PLCInfo
+    {
+        public string IP;
+        public int Port;
+        public int Slot;
+        public int Rack;
+        public PLCType Type;
+    }
+
+    public struct DBInfo
+    {
+        public PLCInfo PLCInfo;
+        public ushort No;
+    }
+
+    public struct ProtocolInfo
+    {
+        public DBInfo DBInfo;
+        public int Position { get; set; } 
+    }
+
+
+    public abstract class DeviceData
+    {
+        public string Code { get; set; } = "";
+        public DateTime Frame { get; set; }
+        public string Message { get; set; } = "";
+    } 
+    public abstract class DeviceData<T> : DeviceData
+    { 
+        public T Data { get; private set; }
+
+
+
+        protected abstract T Convert<TSource>(TSource source);
+    }
+}

+ 456 - 0
WCS.Core/DataBlock.cs

@@ -0,0 +1,456 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
+namespace WCS.Core
+{
+    public class DataBlock : EntityEx<DBInfo>
+    {
+        public event Action<byte[]> DbChanged;
+        public static List<DataBlock> DBList = new List<DataBlock>();
+
+        List<ProtocolProxyBase> ProxyList = new List<ProtocolProxyBase>();
+
+        public ushort Start { get; private set; }
+        public ushort Length { get; private set; }
+        bool failed = false;
+        byte[] Data=new byte[0];
+        int id = 0;
+        public override string ToString()
+        {
+            return id.ToString();
+        }
+
+        public DataBlock(DBInfo ent) : base(ent)
+        {
+            DBList.Add(this);
+            id = DBList.Count;
+        }
+
+        public void RefreshData()
+        {
+            try
+            {
+                if (Length == 0)
+                {
+                    Start = (ushort)ProxyList.Min(v => v.Info.Position);
+                    var last = ProxyList.OrderBy(v => v.Info.Position).Last();
+                    Length = (ushort)(last.Info.Position + last.BytesCount);
+                }
+                var data = new byte[0];
+                lock (Entity.PLCInfo.Ex())
+                {
+                    data= Entity.PLCInfo.Ex().Accessor.ReadBytes(Entity.No, Start, (ushort)(Length-Start));
+                }
+                if (!Data.SequenceEqual(data))
+                {
+                    Data = data;
+                    DbChanged?.Invoke(Data);
+                }
+                failed = false;
+            }
+            catch 
+            {
+                failed = true;
+                throw;
+            }
+
+        }
+        public PlcItem<T> Regist<T>(ProtocolProxyBase proxy, string objid, string name, int start, byte arrLen = 1, byte strLen = 0)
+        {
+            if (!ProxyList.Contains(proxy))
+                ProxyList.Add(proxy);
+            var t = typeof(T);
+            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)
+        {
+            try
+            {
+                bitStart -= Start * 8;
+                return (T)Read(typeof(T), ref bitStart, strLength, arrLength);
+            }
+            catch (Exception ex)
+            {
+                throw;
+            }
+        }
+
+        private object Read(Type type, ref int bitStart, int strLength, int arrLength)
+        {
+            if (failed)
+                throw new Exception(this.Entity.No + "连接失败");
+
+            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 < 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.PLCInfo.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.PLCInfo.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.PLCInfo.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 (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);
+            if (Data.Length < start + length)
+                throw new Exception("读取数据的位置超出数据总长度");
+            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.PLCInfo.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.PLCInfo.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.PLCInfo.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.PLCInfo.Ex())
+            {
+                var start = GetByteStart(bitStart);
+
+                Entity.PLCInfo.Ex().Accessor.WriteBytes(Entity.No, start, data);
+                data.CopyTo(Data, start-Start);
+                bitStart += data.Length * 8;
+            }
+        }
+
+        private void WriteBit(ref int bitStart, bool flag)
+        {
+            lock (Entity.PLCInfo.Ex())
+            {
+                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.PLCInfo.Ex().Accessor.WriteBytes(Entity.No, (ushort)start, data);
+                data.CopyTo(Data, start-Start);
+
+                bitStart += 1;
+            }
+        }
+
+        #endregion 写入
+
+    }
+
+}

+ 96 - 0
WCS.Core/Device.cs

@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Core
+{
+    public class EntityEx<T>
+    {
+        public T Entity { get; private set; }
+        public EntityEx(T ent)
+        {
+            this.Entity = ent;
+        }
+    }
+
+
+    public class Device
+    {
+        public string Code { get; set; } = "";
+        public World World { get; set; }
+
+        ConcurrentDictionary<Type, object> ProtocolObjs = new ConcurrentDictionary<Type, object>();
+
+        public bool HasProtocol(Type type)
+        {
+            return Protocols.HasProtocol(type, Code);
+            //return ProtocolObjs.ContainsKey(type);
+        }
+        public bool HasProtocol<T>()
+        { 
+            return HasProtocol(typeof(T));
+        }
+
+        public T Protocol<T>()
+        {
+            return (T)Protocol(typeof(T));
+        }
+
+        public object Protocol(Type protocolType)
+        {
+            if (!ProtocolObjs.ContainsKey(protocolType))
+            {
+                var type = typeof(Generator<,>);
+                type = type.MakeGenericType(protocolType, Configs.ProtocolProxyBaseType);
+                var m = type.GetMethod("Create", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
+
+                var info = Protocols.Get(protocolType, Code);
+                var obj = m.Invoke(null, new object[] { new object[] { this, info, protocolType } });
+                ProtocolObjs[protocolType] = obj;
+            }
+            return ProtocolObjs[protocolType];
+        }
+
+        public override string ToString()
+        {
+            return Code;
+        } 
+         
+    }
+
+    public class Device<T> : EntityEx<Device>
+    {
+        public T Data { get; set; }
+        public Device(Device device) : base(device)
+        {
+            Data = Entity.Protocol<T>();
+        }
+
+        public override string ToString()
+        {
+            return Entity.Code;
+        }
+    }
+
+    public class Device<T, T2> : Device<T>
+    {
+        public T2 Data2 { get; set; }
+        public Device(Device device) : base(device)
+        {
+            Data2 = Entity.Protocol<T2>();
+        }
+
+    }
+
+    public class Device<T, T2, T3> : Device<T, T2>
+    {
+        public T3 Data3 { get; set; }
+        public Device(Device device) : base(device)
+        {
+            Data3 = Entity.Protocol<T3>();
+        }
+    }
+}

+ 438 - 0
WCS.Core/Extentions.cs

@@ -0,0 +1,438 @@
+using System;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading.Tasks; 
+
+namespace WCS.Core
+{
+    public static class Extentions
+    { 
+        static ConcurrentDictionary<object, object> ExObjs = new ConcurrentDictionary<object, object>();
+
+        public static T GetEx<T>(object entity)
+        {
+            lock (entity)
+            {
+                if (!ExObjs.ContainsKey(entity))
+                {
+                    var obj = Activator.CreateInstance(typeof(T), entity);
+                    ExObjs[entity] = obj;
+                }
+                return (T)ExObjs[entity];
+            }
+        }
+
+        public static PLC Ex(this PLCInfo source)
+        {
+            return GetEx<PLC>(source);
+        }
+
+        static ConcurrentDictionary<World, ConcurrentDictionary<object, object>> ExObjsOfWorld = new ConcurrentDictionary<World, ConcurrentDictionary<object, object>>(); 
+
+        static T GetExOfWorld<T>(this object source, World world)
+        {
+            if (!ExObjsOfWorld.TryGetValue(world, out var objs))
+            {
+                objs = new ConcurrentDictionary<object, object>();
+                ExObjsOfWorld[world] = objs;
+            }
+            if (!objs.TryGetValue(source, out var obj))
+            {
+                obj = Activator.CreateInstance(typeof(T), source);
+                objs[source] = obj;
+            }
+            return (T)obj;
+        }
+
+        public static DataBlock Ex(this DBInfo source, World world)
+        {
+            return GetExOfWorld<DataBlock>(source, world);
+        }
+
+        public static DataBlock[] GetDataBlocks(this World source)
+        {
+            ExObjsOfWorld.TryGetValue(source, out var dic);
+            var res= dic.Values.OfType<DataBlock>().ToArray();
+            return res;
+        }
+
+
+        public static WorldEx Ex(this World source)
+        {
+            return GetEx<WorldEx>(source);
+        }
+
+        public static void AddSafe<T>(this List<T> source,T item)
+        {
+            lock (source)
+            {
+                source.Add(item);
+            }
+        }
+
+        public static void AddSafe<T>(this List<T> source,IEnumerable<T> item)
+        {
+            lock (source)
+            {
+                source.AddRange(item);
+            }
+        } 
+
+    }
+
+
+    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 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;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Core
+{
+    /// <summary>
+    /// 动态创建接口实例
+    /// </summary>
+    /// <typeparam name="T">接口</typeparam>
+    /// <typeparam name="TBase">基类</typeparam>
+    public static class Generator<T, TBase> where TBase : IProtocolProxy
+    {
+        static TypeBuilder tb;
+        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 ex)
+            {
+
+            }
+            //myAssembly.Save(typeof(T).Name + ".dll");
+        }
+
+        private const MethodAttributes METHOD_ATTRIBUTES =
+    MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig;
+
+        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);
+            }
+        }
+        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);
+            }
+        }
+        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);
+                    }
+                }
+            }
+        }
+        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);
+            }
+        }
+        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;
+        }
+        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;
+        }
+
+        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;
+        }
+
+        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;
+        }
+
+
+        static Type[] GetInterfaces()
+        {
+            var tInterface = typeof(T);
+            var ts = tInterface.GetInterfaces().ToList();
+            ts.Add(tInterface);
+            return ts.ToArray();
+        }
+        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);
+    }
+}

+ 234 - 0
WCS.Core/Ltc.cs

@@ -0,0 +1,234 @@
+using FreeRedis;
+using System;
+using System.Collections.Concurrent;
+using System.Linq.Expressions;
+using System.Threading;
+using System.Threading.Channels;
+
+namespace WCS.Core
+{
+    public static class Ltc
+    {
+        private static ConcurrentDictionary<Thread, Channel> Channels = new ConcurrentDictionary<Thread, Channel>(); 
+
+        public static void SetChannel(Channel channel)
+        {
+            Channels[Thread.CurrentThread] = channel;
+        }
+
+        public static Channel GetChannel()
+        {
+            return Channels[Thread.CurrentThread];
+        }
+
+        static ConcurrentDictionary<Channel, List<LogInfo>> Msgs = new ConcurrentDictionary<Channel, List<LogInfo>>();
+
+
+        public static void ClearChannel()
+        {
+            if (Msgs.TryGetValue(GetChannel(), out var list))
+                list.Clear();
+        }
+
+        public static string GetLogStr()
+        {
+            var channel = GetChannel();
+            if (Msgs.TryGetValue(channel, out var list))
+            {
+                var msg = "-------------------"+channel + "--------------------\n" + string.Join('\n', list.Select(v => $"{string.Join('\n', v)}"));
+                return msg;
+            }
+            else
+            {
+                return "";
+            } 
+        }
+
+        public static List<LogInfo> GetLogInfo()
+        {
+            var channel = GetChannel();
+            if (Msgs.TryGetValue(channel, out var list))
+            {
+                return list;
+            }
+            return new List<LogInfo>();
+        }
+
+        public static void Log(string msg,LogLevel level,ErrorType type)
+        { 
+            var channel = GetChannel();
+            
+            if (!Msgs.TryGetValue(channel, out var list))
+            {
+                list = new List<LogInfo>();
+                Msgs[channel] = list;
+            }
+            list.Add(new LogInfo { Message = msg, Level = level, Type = type, Channel = channel });
+        }
+  
+        public static void Publish(World world)
+        { 
+            var channel = GetChannel();
+            if (Msgs.TryGetValue(channel, out var list))
+            {
+                var msg = string.Join('\n', list); 
+                world.Ex().Publish(channel, 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 System.Collections.ICollection)
+            {
+                var coll = obj as System.Collections.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();
+                msg += res;
+                return res;
+            }
+            catch (Exception ex)
+            { 
+                throw;
+            }
+            finally
+            {
+                Log(msg, LogLevel.低, ErrorType.已知);
+            }
+        }
+
+        public static T Do<T1, T>(T1 obj, Expression<Func<T1, T>> exp)
+        {
+            var msg = "";
+            try
+            {
+                try
+                {
+                    msg = exp.ExpToString();
+                }
+                catch (Exception ex2)
+                {
+                }
+                msg += "  结果:";
+                var res = exp.Compile().Invoke(obj);
+                msg += ResultString(res);
+                return res;
+            }
+            catch (Exception ex)
+            {  
+                throw;
+            }
+            finally
+            {
+                Log(msg, LogLevel.低, ErrorType.已知);
+            }
+        }
+
+        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)
+            { 
+                throw;
+            }
+            finally
+            {
+                Log(msg, LogLevel.低, ErrorType.已知);
+            }
+        }
+
+        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)
+            { 
+                throw;
+            }
+            finally
+            {
+                Log(msg, LogLevel.低, ErrorType.已知);
+            }
+        }
+    }
+
+    public class LogInfo
+    {
+        public ErrorType Type { get; set; }
+        public LogLevel Level { get; set; }
+        public Channel Channel { get; set; }
+        public string Message { get; set; } = "";
+
+        public override string ToString()
+        {
+            return $"类型:{Type},级别:{Level},内容:{Message}";
+        }
+    }
+
+    public class Channel
+    {
+        public string World;
+        public string System;
+        public string Item;
+
+        public override string ToString()
+        {
+            return $"{World}-{System}-{Item}";
+        }
+    }
+
+    public enum ErrorType
+    {
+        未知 = 0,
+        已知 = 1
+    }
+
+    public enum LogLevel
+    {
+        低 = 0,
+        中 = 1,
+        高 = 2
+    }
+
+    public class KnownException : Exception
+    { 
+        public LogLevel Level { get; set; }
+
+        public KnownException(string msg, LogLevel level) : base(msg)
+        {
+            this.Level = level;
+        }
+    }
+}

+ 34 - 0
WCS.Core/PLC.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks; 
+
+namespace WCS.Core
+{
+    public class PLC : EntityEx<PLCInfo>
+    {
+        public PLC(PLCInfo ent) : base(ent)
+        {
+            if (Configs.PLCAccessorCreater != null)
+            {
+                Accessor = Configs.PLCAccessorCreater.Create(ent);
+            }
+            else
+                throw new Exception("Configs.PLCAccessorCreater未赋值");
+           
+        }      
+        public IPLCAccessor Accessor { get; private set; }
+      
+    }
+
+    public interface IPLCAccessorCreater
+    {
+        IPLCAccessor Create(PLCInfo data);
+    }
+    public interface IPLCAccessor
+    {
+        void WriteBytes(ushort db, ushort address, byte[] data);
+        byte[] ReadBytes(ushort db, ushort address, ushort length);
+    }
+}

+ 160 - 0
WCS.Core/PlcItem.cs

@@ -0,0 +1,160 @@
+using System;
+using System.Collections;
+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 object lastValue;
+
+        private bool ValueEquals(object obj1, object obj2)
+        {
+            if (obj1.GetType().IsArray)
+            {
+                var arr1 = obj1 as Array;
+                var arr2 = obj2 as Array;
+                if (arr2 == null)
+                {
+                    return true;
+                }
+                for (int i = 0; i < arr1.Length; i++)
+                {
+                    if (!arr1.GetValue(i).Equals(arr2.GetValue(2)))
+                        return false;
+                }
+                return true;
+            }
+            else
+            {
+                return obj1.Equals(obj2);
+            }
+        }
+    }
+
+    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);
+        }
+    }
+}

+ 150 - 0
WCS.Core/ProtocolProxyBase.cs

@@ -0,0 +1,150 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Core
+{
+    public abstract class ProtocolProxyBase : IProtocolProxy
+    {
+        string Id = Guid.NewGuid().ToString();
+        public ProtocolInfo Info { get; private set; }
+        public ushort BytesCount { get; private set; }
+        Dictionary<string, PlcItem> Items = new Dictionary<string, PlcItem>();
+        Type ProtocolType, ProtocolDataType;
+        public Device Device { get; private set; }
+
+        DataBlock Db;
+
+        public ProtocolProxyBase(Device dev, ProtocolInfo info,Type protocolType)
+        {
+            this.Device = dev;
+            this.Info = info;
+            Db = info.DBInfo.Ex(Device.World);
+            Db.DbChanged += Db_DbChanged;
+            ProtocolType = protocolType;
+
+
+            var bitStart = info.Position * 8;//偏移量,按位算
+
+            //this.Start = start;
+            //PlcDB db = new PlcDB(null, 50, 100);
+            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, new object[] { this, Id, p.Name, bitStart, arrlen, strLen }) as PlcItem;
+                Items.Add(p.Name, item);
+                bitStart += item.DataSizeOfBits;
+                BytesCount += item.DataSize;
+            }
+            //ProtocolType = this.PROTOCOL.DB.GetProtocolType();
+            ProtocolDataType = ProtocolType.Assembly.GetTypes().Where(v => v.IsClass).Where(v => v.GetInterface(ProtocolType.Name) != null && v != this.GetType()).First();
+        }
+
+        byte[] Data = new byte[0];
+        private void Db_DbChanged(byte[] data)
+        {
+            var pos = Info.Position - Db.Start;
+            var bytes = data.Skip(pos).Take(BytesCount).ToArray();
+            if (!Data.SequenceEqual(bytes))
+            {
+                Data = bytes;
+                DataChanged();
+            }
+        }
+
+        protected abstract void DataChanged();
+
+        #region
+        public void AddEvent(string eventName, Delegate delgate)
+        {
+            throw new NotImplementedException();
+        }
+        public void RemoveEvent(string eventName, Delegate delgate)
+        {
+            throw new NotImplementedException();
+        }
+
+        public T CallReturn<T>(string methodName, params object[] args)
+        {
+            throw new NotImplementedException();
+        }
+
+        public void CallVoid(string methodName, params object[] args)
+        {
+            throw new NotImplementedException();
+        }
+
+
+        public T Get<T>(string propertyName)
+        {
+            var item = Items[propertyName] as PlcItem<T>;
+            var res = item.Value;
+            Ltc.Log($"{propertyName}:{res}", LogLevel.低, ErrorType.已知);
+            return res;
+        }
+
+        public void Set<T>(string propertyName, T value)
+        {
+            var item = Items[propertyName] as PlcItem<T>;
+            item.Value = value;
+            Ltc.Log($"{propertyName}={value}", LogLevel.低, ErrorType.已知);
+        }
+        #endregion
+    }
+}

+ 73 - 0
WCS.Core/Protocols.cs

@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.Core
+{
+
+    public class Protocols
+    {
+        protected static ConcurrentDictionary<Type, ConcurrentDictionary<string, ProtocolInfo>> ItemOfType = new ConcurrentDictionary<Type, ConcurrentDictionary<string, ProtocolInfo>>();
+
+        public static bool HasProtocol(Type type, string code)
+        {
+            return ItemOfType[type].ContainsKey(code);
+        }
+
+        public static ProtocolInfo Get(Type type, string code)
+        {
+            ItemOfType[type].TryGetValue(code, out var info);
+            return info;
+        }
+
+        public static List<Device> Generate(World world)
+        {
+            List<Device> list = new List<Device>();
+            var gs = ItemOfType.SelectMany(v => v.Value.Select(d => new { code = d.Key, proto = d.Value, type = v.Key })).GroupBy(v=>v.code).ToList();
+            foreach (var g in gs)
+            {
+                var dev = new Device { Code = g.Key, World = world };
+                foreach (var i in g)
+                {
+                    //dev.Protocol(i.type);
+                }
+                list.Add(dev);
+            }
+
+            return list;
+        }
+    }
+
+    public class Protocols<T> : Protocols
+    {
+        static ConcurrentDictionary<string, ProtocolInfo> Items
+        {
+            get
+            {
+                if (!ItemOfType.TryGetValue(typeof(T), out var item))
+                {
+                    item = new ConcurrentDictionary<string, ProtocolInfo>();
+                    ItemOfType[typeof(T)] = item;
+                }
+                return item;
+            }
+        } 
+
+        public static void Add(string code, ProtocolInfo info)
+        {
+            if (!Items.TryAdd(code, info))
+                throw new Exception($"Protocols<{typeof(T)}>.Add()添加了重复的设备号");
+        }
+
+
+        public static ProtocolInfo Get(string code)
+        {
+            return Get(typeof(T), code);
+        }
+
+    }
+}

+ 159 - 0
WCS.Core/System.cs

@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks; 
+
+namespace WCS.Core
+{
+    public abstract class SystemBase: DescriptionClass
+    {
+        public World World { get; private set; }
+        public SystemBase()
+        {
+            var attr = this.GetType().GetCustomAttribute<BelongToAttribute>();
+            var wt = typeof(World);
+            if (attr != null)
+            {
+                wt = attr.WorldType;
+            }
+            this.World = World.Worlds.Where(v => v.GetType() == wt).First(); 
+        }
+
+        public abstract List<object> GetObjects();
+        public abstract void Update(List<WorkTimes> list);
+
+        public void Log(string msg)
+        { 
+            Ltc.Log(msg, LogLevel.低, ErrorType.已知);
+        } 
+        public void Error(string msg,LogLevel level)
+        {
+            throw new KnownException(msg, level);
+        }
+    }
+
+   
+
+    public abstract class SystemBase<T> : SystemBase
+    { 
+        public List<T> Objects { get; set; }
+        
+        /// <summary>
+        /// 对所有Objects并行循环执行Do
+        /// </summary>
+        protected abstract bool ParallelDo { get; }
+
+        /// <summary>
+        /// 保存日志到文件
+        /// </summary>
+        protected abstract bool SaveLogsToFile { get; }
+
+        public SystemBase()
+        {
+            Objects = Create();//.Select(v=>Activator.CreateInstance(typeof(T),v)).OfType<T>().ToList();
+        }
+
+        public override void Update(List<WorkTimes> list)
+        {
+            var logs = new List<LogInfo>();
+            if (ParallelDo)
+            {
+                Parallel.ForEach(Objects, new ParallelOptions { MaxDegreeOfParallelism = 256 }, obj =>
+                {
+                    var sw= new Stopwatch();
+                    sw.Start();
+                    InvokeDo(obj);
+                    sw.Stop();
+                    list.AddSafe(new WorkTimes { Key = $"{obj?.ToString()}", Total = sw.ElapsedMilliseconds });
+                    //var log = Ltc.GetLogStr();
+                    logs.AddSafe(Ltc.GetLogInfo());
+                });
+            }
+            else
+            {
+                foreach (var obj in Objects)
+                {
+                    var sw = new Stopwatch();
+                    sw.Start();
+                    InvokeDo(obj);
+                    sw.Stop();
+                    list.AddSafe(new WorkTimes { Key = $"{obj?.ToString()}", Total = sw.ElapsedMilliseconds });
+                    //var log = Ltc.GetLogStr();
+                    logs.AddSafe(Ltc.GetLogInfo());
+                }
+            }
+            if (SaveLogsToFile)
+            { 
+                var arr = logs.GroupBy(v => v.Channel).Select(v => new LogInfo { Channel = v.Key, Level = v.Max(d => d.Level), Type = v.Any(d => d.Type == ErrorType.未知) ? ErrorType.未知 : ErrorType.已知, Message = "\n"+string.Join('\n', v.Select(d => d.Message)) }).ToArray(); 
+                Configs.OnLog?.Invoke(arr);
+            }
+        }
+         
+        void InvokeDo(T obj)
+        {
+            var channel = new Channel
+            {
+                World = World.Description,
+                System = Description,
+                Item = obj.ToString()
+            };
+            try
+            { 
+                Ltc.SetChannel(channel);
+                Ltc.ClearChannel();
+                Ltc.Log("开始", LogLevel.低, ErrorType.已知);
+                Do(obj);
+            }
+            catch (KnownException ex)
+            {
+                Ltc.Log(ex.Message, ex.Level, ErrorType.已知);
+            }
+            catch (Exception ex)
+            {
+                //Console.WriteLine($"{channel}:\n{ex.GetBaseException().Message}");
+                Ltc.Log(ex.GetBaseException().Message, LogLevel.高, ErrorType.未知);
+            }
+            finally
+            {
+                Ltc.Log("结束", LogLevel.低, ErrorType.已知);
+                Ltc.Publish(this.World);
+            }
+        }
+
+
+        public abstract List<T> Create();
+        public abstract void Do(T obj);
+
+        public override List<object> GetObjects()
+        {
+            return Objects.OfType<object>().ToList();
+        } 
+    }
+
+    public abstract class DeviceSystem<T> : SystemBase<T> where T : EntityEx<Device>
+    {
+        public override List<T> Create()
+        {
+            var types = typeof(T).GetGenericArguments();
+            var list= World.Devices.Where(v => types.All(d => v.HasProtocol(d)))
+                .Where(v => Select(v))
+                .Select(v => Activator.CreateInstance(typeof(T), v)).OfType<T>().ToList();
+            if (list.Count == 0)
+            {
+                Log($"{GetType().Name}系统未匹配到任何设备"); 
+            }
+            return list;
+        }
+
+        public abstract bool Select(Device dev);
+
+        //public abstract List<Device> CreateDevices();
+    }
+
+  
+}

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

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

+ 412 - 0
WCS.Core/World.cs

@@ -0,0 +1,412 @@
+using FreeRedis;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Xml.Schema; 
+
+namespace WCS.Core
+{
+    /// <summary>
+    /// 世界用来管理下属System的执行周期,此为默认世界。也可以通过继承此类创建多个不同世界,不同世界的执行周期相互独立,不受其它世界延迟干扰。
+    /// </summary>
+    [Description("默认世界")]
+    public class World: DescriptionClass
+    {
+        [DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
+        public static extern uint MM_BeginPeriod(uint uMilliseconds);
+        [DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
+        public static extern uint MM_EndPeriod(uint uMilliseconds);
+
+        #region Static
+        static List<World> _Worlds;
+        public static List<World> Worlds
+        {
+            get
+            {
+                if (_Worlds == null)
+                {
+                    _Worlds = new List<World>();
+                    _Worlds.Add(new World());//默认世界
+                    var arr = Assembly.GetEntryAssembly().GetTypes().Where(v => typeof(World).IsAssignableFrom(v)).Select(v => Activator.CreateInstance(v)).OfType<World>().ToArray();//自定义世界
+                    _Worlds.AddRange(arr);
+                }
+                return _Worlds;
+            }
+        }
+
+        public static void StartAll()
+        { 
+            //MM_BeginPeriod(1);
+            Worlds.ForEach(w => w.Init());
+            foreach (World w in Worlds)
+            {
+                if (w.SystemTypes.Length == 0)
+                    continue; 
+                w.Start();
+            }
+            var arr = Worlds.SelectMany(v => v.SystemGroups).SelectMany(v => v.Value)
+                .SelectMany(v => v.GetObjects().OfType<EntityEx<Device>>().Select(d => new { Sys = v, Obj = d }))
+                .GroupBy(v => v.Obj.Entity.Code)
+                .Select(v => new { Code = v.Key, Systems = v.Select(d => d.Sys.GetType().Name).ToArray(), Worlds = v.Select(d => d.Sys.World.GetType().Name).Distinct().ToArray() })
+                .Where(v => v.Systems.Length > 1).ToArray();
+            if (arr.Length > 0)
+            {
+                var msgs = arr.Select(v => $"设备{v.Code}同时存在于{v.Systems.Length}个系统:({string.Join(',', v.Systems)}),{v.Worlds.Length}个世界:({string.Join(',', v.Worlds)})中").ToArray();
+                var str = string.Join('\n', msgs);
+                Console.ForegroundColor = ConsoleColor.DarkYellow;
+                Console.WriteLine(str);
+                Console.ResetColor();
+            }
+        }
+
+
+
+        public static void StopAll()
+        {
+            //MM_EndPeriod(1);
+            foreach (World w in Worlds)
+                w.Stop();
+        }
+        #endregion
+
+        #region Dynamic
+        public bool Stoped;
+        Dictionary<Type, int> TypeOrder = new Dictionary<Type, int>();
+        Dictionary<int, List<SystemBase>> SystemGroups = new Dictionary<int, List<SystemBase>>();
+        protected Type[] SystemTypes;
+          
+
+        /// <summary>
+        /// 周期最小间隔时间(毫秒)
+        /// </summary>
+        protected int Interval = 800; 
+        public World()
+        {
+            SystemTypes = GetSystemTypes(); 
+        }
+
+        protected virtual Type[] GetSystemTypes()
+        { 
+            var sysTypes = Assembly.GetEntryAssembly().GetTypes()
+                .Where(v => !v.IsAbstract)
+                .Where(v => typeof(SystemBase).IsAssignableFrom(v))
+                .Where(v =>
+                {
+                    var attr = v.GetCustomAttribute<BelongToAttribute>();
+                    if (attr == null)
+                        return GetType() == typeof(World);
+                    if (attr.WorldType == this.GetType())
+                        return true;
+                    return false;
+                }).ToArray();
+            return sysTypes; 
+        }
+
+
+        public List<Device> Devices;
+        
+
+        /// <summary>
+        /// 初始化,实例化世界下的所有系统
+        /// </summary>
+        public virtual void Init()
+        {
+            Ltc.SetChannel(new Channel { World = Description, Item = "初始化" });
+            Ltc.ClearChannel();
+            try
+            {
+                Devices = Protocols.Generate(this);
+                foreach (var type in SystemTypes)
+                {
+                    Set(type, 0);
+                }
+                var arr = TypeOrder.OrderBy(v => v.Value).Select(v => v.Key).ToArray();
+                var gs = TypeOrder.GroupBy(v => v.Value).OrderBy(v => v.Key).ToArray();
+                for (int i = 0; i < gs.Length; i++)
+                {
+                    var g = gs[i];
+                    var list = new List<SystemBase>();
+                    var sysArr = g.Select(v => Activator.CreateInstance(v.Key)).OfType<SystemBase>().ToArray();
+                    list.AddRange(sysArr);
+                    SystemGroups.Add(i, list);
+                }
+            }
+            catch (Exception ex)
+            {
+                Ltc.Log(ex.GetBaseException().Message, LogLevel.高, ErrorType.未知);
+                throw;
+            }
+            finally 
+            {
+                Configs.OnLog?.Invoke(Ltc.GetLogInfo());
+            }
+        }
+
+        int Set(Type type,int level)
+        {
+            if (!SystemTypes.Contains(type))
+                throw new Exception($"OrderAttribute设置错误,与目标不属于同一世界。类型:{type}。");
+            if (level > 10)
+            { 
+                throw new Exception($"OrderAttribute设置错误,导致死循环。类型:{type}。");
+            }
+            var attr = type.GetCustomAttribute<OrderAttribute>();
+            if (attr != null)
+            {
+                level++;
+                var num = Set(attr.SystemType, level);
+                TypeOrder[type] = num + (int)attr.Order;
+            }
+            else
+            {
+                TypeOrder[type] = 0;
+            }
+            return TypeOrder[type];
+        }
+
+         /// <summary>
+         /// 开启世界主循环
+         /// </summary>
+        public void Start()
+        { 
+            Stoped = false; 
+            Task.Run(Loop);//不要使用Thread,可以使用ThreadPool
+        }
+
+        void Loop()
+        { 
+            var sw = new Stopwatch();
+            while (!Stoped)
+            {
+                WorkTimes wt = new WorkTimes();
+                wt.Key = this.GetType().Name;
+                sw.Restart();
+                BeforeUpdate();
+                Update(wt.Items);
+                AfterUpdate();
+                sw.Stop();
+                var workTimes = (int)sw.ElapsedMilliseconds;
+                var ms = Interval - workTimes;
+                sw.Start();
+                if (ms > 0)
+                {
+                    Thread.Sleep(ms);//不要使用Task.Delay().Wait()      
+                }
+                else
+                {
+                    Console.ForegroundColor = ConsoleColor.Red;
+                }
+                sw.Stop();
+                var total = sw.ElapsedMilliseconds;
+                wt.Total = total; 
+                var aa = wt.GetInfo();
+                Console.WriteLine(aa); 
+                Console.ResetColor(); 
+            } 
+        }
+
+        public void Stop()
+        {
+            Stoped = true;
+        }
+
+        void Update(List<WorkTimes> list)
+        {
+            var wt = new WorkTimes();
+            wt.Key = "读取PLC数据";
+            var sw=new Stopwatch();
+            sw.Start();
+            LoadPlcData(wt.Items);
+            sw.Stop();
+            wt.Total = sw.ElapsedMilliseconds;
+            list.AddSafe(wt);
+
+            wt = new WorkTimes();
+            wt.Key = "系统业务";
+            sw.Restart();
+            DoLogics(wt.Items);
+            sw.Stop();
+            wt.Total = sw.ElapsedMilliseconds;
+            list.AddSafe(wt); 
+        }
+
+        void LoadPlcData(List<WorkTimes> list)
+        {
+            var logs = new List<LogInfo>();
+            Parallel.ForEach(this.GetDataBlocks(), db =>
+            {
+                var channel = new Channel
+                {
+                    World = GetType().Name,
+                    System = "读取PLC",
+                    Item = $"{db.Entity.PLCInfo.IP}_{db.Entity.No}"
+                };
+                var sw = new Stopwatch();
+                sw.Start();
+                try
+                {
+                    db.RefreshData();
+                }
+                catch (KnownException ex)
+                {
+                    logs.AddSafe(new LogInfo { Channel = channel, Message = $"{ex.GetBaseException().Message}", Level = ex.Level, Type = ErrorType.已知 });
+                    this.Ex().Publish(channel, ex.GetBaseException().Message);
+                }
+                catch (Exception ex)
+                {
+                    logs.AddSafe(new LogInfo { Channel = channel, Message = $"{ex.GetBaseException().Message}", Level = LogLevel.高, Type = ErrorType.未知 });
+                    this.Ex().Publish(channel, ex.GetBaseException().Message);
+                }
+                sw.Stop();
+                list.AddSafe(new WorkTimes { Key = $"{db.Entity.PLCInfo.IP}/{db.Entity.No}", Total = sw.ElapsedMilliseconds }); 
+            }); 
+            Configs.OnLog?.Invoke(logs);
+        }
+
+        void DoLogics(List<WorkTimes> list)
+        {   
+            foreach(var group in SystemGroups)
+            {
+                var wt=new WorkTimes();
+                wt.Key = $"组{group.Key}";
+                var sw = new Stopwatch();
+                sw.Restart(); 
+                Parallel.ForEach(group.Value, sys =>
+                {
+                    var wt2=new WorkTimes();
+                    wt2.Key = sys.GetType().Name;
+                    var sw2 = new Stopwatch();
+                    sw2.Start();
+                    try
+                    {
+                        sys.Update(wt2.Items);
+                    }catch (Exception ex)
+                    {
+                        Console.ForegroundColor = ConsoleColor.Red;
+                        Console.WriteLine(ex.GetBaseException().Message);
+                        Console.ResetColor();
+                    }
+                    sw2.Stop();
+                    wt2.Total = sw2.ElapsedMilliseconds;
+                    list.AddSafe(wt2);
+                }); 
+                sw.Stop();  
+                wt.Total= sw.ElapsedMilliseconds;
+                //list.AddSafe(wt);
+            } 
+        }
+         
+        protected virtual void BeforeUpdate()
+        { 
+            
+        }
+
+        protected virtual void AfterUpdate()
+        {
+
+        }
+
+        #endregion
+         
+        public T GetSystem<T>() where T : SystemBase
+        {
+            var sys = SystemGroups.SelectMany(v => v.Value).Where(v => v.GetType() == typeof(T)).FirstOrDefault() as T;
+            if (sys == null)
+                throw new Exception($"世界{GetType().Name}中不存在系统{typeof(T).Name}");
+            return sys;
+        }
+        
+    }
+
+
+    public class WorldEx : EntityEx<World>
+    { 
+        RedisClient Redis = new RedisClient(Configs.DebugRedisUrl);
+        List<string> ChannelList = new List<string>();
+        DateTime SubTime = DateTime.Now;
+
+        public WorldEx(World ent) : base(ent)
+        {
+            Redis.Subscribe("Login", (channel, msg) =>
+            {
+                lock (ChannelList)
+                {
+                    ChannelList.Clear();
+                    ChannelList.AddSafe(msg.ToString().Split(','));
+                }
+                SubTime = DateTime.Now;
+                Console.WriteLine($"调试工具正在使用中,已订阅:{msg}");
+            });
+        }
+
+        public void Publish(Channel channel, string msg)
+        {
+            if ((DateTime.Now - SubTime).TotalSeconds > 20)
+                return;
+
+            var flag = false;
+            lock (ChannelList)
+            {
+                flag=ChannelList.Any(v =>
+                {
+                    var b = Regex.Match(channel.ToString(), $"^{v.Replace("*", ".*")}$");
+                    return b.Success;
+                });
+            }
+            if (flag)
+                Redis.Publish(channel.ToString(), msg);
+        }
+    }
+
+    public class WorkTimes
+    {
+        public string Key { get; set; } = "";
+        public long Total { get; set; } 
+        public List<WorkTimes> Items { get; set; } = new List<WorkTimes>();
+
+        public override string ToString()
+        {
+            return $"{Key},明细:{Items.Count},耗时:{Total}";
+        }
+
+        public string GetInfo()
+        {
+            var str = $"[{ToString()}]";
+            if (Items.Count > 0)
+                str += $" > {Items.OrderBy(v => v.Total).LastOrDefault()?.GetInfo()}";
+            return str;
+        }
+    }
+
+    public abstract class AttrClass<T> where T : Attribute
+    {
+        public T? Attr { get; private set; }
+        public AttrClass()
+        {
+            Attr = GetType().GetCustomAttribute<T>();
+        }
+    }
+
+    public abstract class DescriptionClass:AttrClass<DescriptionAttribute> 
+    {
+        public string Description
+        {
+            get 
+            {
+                if (Attr != null)
+                    return Attr.Description;
+                else
+                    return GetType().Name;
+            }
+        }
+    }
+}

+ 17 - 0
WCS.Service/Program.cs

@@ -0,0 +1,17 @@
+namespace WCS.Service
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            IHost host = Host.CreateDefaultBuilder(args)
+                .ConfigureServices(services =>
+                {
+                    services.AddHostedService<Worker>();
+                })
+                .Build();
+
+            host.Run();
+        }
+    }
+}

+ 11 - 0
WCS.Service/Properties/launchSettings.json

@@ -0,0 +1,11 @@
+{
+  "profiles": {
+    "WCS.Service": {
+      "commandName": "Project",
+      "dotnetRunMessages": true,
+      "environmentVariables": {
+        "DOTNET_ENVIRONMENT": "Development"
+      }
+    }
+  }
+}

+ 13 - 0
WCS.Service/WCS.Service.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk.Worker">
+
+  <PropertyGroup>
+    <TargetFramework>net7.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <UserSecretsId>dotnet-WCS.Service-ee485c84-3250-46d0-8109-1d37d3b27230</UserSecretsId>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
+  </ItemGroup>
+</Project>

+ 21 - 0
WCS.Service/Worker.cs

@@ -0,0 +1,21 @@
+namespace WCS.Service
+{
+    public class Worker : BackgroundService
+    {
+        private readonly ILogger<Worker> _logger;
+
+        public Worker(ILogger<Worker> logger)
+        {
+            _logger = logger;
+        }
+
+        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+        {
+            while (!stoppingToken.IsCancellationRequested)
+            {
+                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
+                await Task.Delay(1000, stoppingToken);
+            }
+        }
+    }
+}

+ 8 - 0
WCS.Service/appsettings.Development.json

@@ -0,0 +1,8 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  }
+}

+ 8 - 0
WCS.Service/appsettings.json

@@ -0,0 +1,8 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  }
+}

+ 39 - 0
WcsFramework.sln

@@ -0,0 +1,39 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33205.214
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WCS.Service", "WCS.Service\WCS.Service.csproj", "{F942507C-3DB2-47BC-B4DF-496E52E3C908}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "框架", "框架", "{C783651F-7EB6-40BA-8E68-525F93B8FCED}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "项目", "项目", "{1A069C59-845C-492A-A939-95741CB8DC12}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WCS.Core", "WCS.Core\WCS.Core.csproj", "{A869A35B-8811-4660-90E8-A143D168D894}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{F942507C-3DB2-47BC-B4DF-496E52E3C908}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F942507C-3DB2-47BC-B4DF-496E52E3C908}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F942507C-3DB2-47BC-B4DF-496E52E3C908}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F942507C-3DB2-47BC-B4DF-496E52E3C908}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A869A35B-8811-4660-90E8-A143D168D894}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A869A35B-8811-4660-90E8-A143D168D894}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A869A35B-8811-4660-90E8-A143D168D894}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A869A35B-8811-4660-90E8-A143D168D894}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{F942507C-3DB2-47BC-B4DF-496E52E3C908} = {1A069C59-845C-492A-A939-95741CB8DC12}
+		{A869A35B-8811-4660-90E8-A143D168D894} = {C783651F-7EB6-40BA-8E68-525F93B8FCED}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {2C009DED-1CDE-4D15-A895-5013ED32E8FC}
+	EndGlobalSection
+EndGlobal