林豪 左 пре 1 година
родитељ
комит
04f5943b0f
30 измењених фајлова са 4770 додато и 0 уклоњено
  1. 21 0
      WCS.Service/AppSettings.cs
  2. 29 0
      WCS.Service/PLCAccessors/PLCAccessorsCreater.cs
  3. 52 0
      WCS.Service/PLCAccessors/SiemensS7PLC.cs
  4. 70 0
      WCS.Service/Program.cs
  5. 11 0
      WCS.Service/Properties/launchSettings.json
  6. 37 0
      WCS.Service/WCS.Service.csproj
  7. 173 0
      WCS.Service/Worker.cs
  8. 29 0
      WCS.Service/Worlds/initWorld.cs
  9. 8 0
      WCS.Service/appsettings.Development.json
  10. 13 0
      WCS.Service/appsettings.json
  11. 252 0
      WCS.Service/数据采集.cs
  12. 91 0
      WCS.WorkEngineering/Extensions/BCRExtension.cs
  13. 582 0
      WCS.WorkEngineering/Extensions/DeviceExtension.cs
  14. 43 0
      WCS.WorkEngineering/Extensions/FlagExtension.cs
  15. 12 0
      WCS.WorkEngineering/Extensions/RGVExtension.cs
  16. 26 0
      WCS.WorkEngineering/Extensions/RedisExtenshin.cs
  17. 60 0
      WCS.WorkEngineering/Extensions/SRMExtension.cs
  18. 60 0
      WCS.WorkEngineering/Extensions/StationExtension.cs
  19. 615 0
      WCS.WorkEngineering/Extensions/TaskExtension.cs
  20. 12 0
      WCS.WorkEngineering/Extensions/TrussExtebsion.cs
  21. 95 0
      WCS.WorkEngineering/Extensions/WorldExtension.cs
  22. 9 0
      WCS.WorkEngineering/LockHub.cs
  23. 239 0
      WCS.WorkEngineering/ProtocolProxy.cs
  24. 65 0
      WCS.WorkEngineering/Startup.cs
  25. 470 0
      WCS.WorkEngineering/Systems/DataCollectionSysyem.cs
  26. 44 0
      WCS.WorkEngineering/WCS.WorkEngineering.csproj
  27. 1236 0
      WCS.WorkEngineering/WorkStart.cs
  28. 133 0
      WCS.WorkEngineering/Worlds/MainWorld.cs
  29. 252 0
      WCS.WorkEngineering/数据采集.cs
  30. 31 0
      分拣中心数据采集.sln

+ 21 - 0
WCS.Service/AppSettings.cs

@@ -0,0 +1,21 @@
+namespace WCS.Service
+{
+    /// <summary>
+    /// 程序设置
+    /// </summary>
+    public class AppSettings
+    {
+        /// <summary>
+        /// 配置信息
+        /// </summary>
+        public static IConfiguration Config { get; private set; }
+
+        /// <summary>
+        /// 静态构造函数
+        /// </summary>
+        static AppSettings()
+        {
+            Config = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
+        }
+    }
+}

+ 29 - 0
WCS.Service/PLCAccessors/PLCAccessorsCreater.cs

@@ -0,0 +1,29 @@
+using WCS.Core;
+using PLCType = WCS.Core.PLCType;
+
+namespace WCS.Service.PLCAccessors
+{
+    /// <summary>
+    ///  PLC访问器创建者
+    /// </summary>
+    public class PLCAccessorsCreater : IPLCAccessorCreater
+    {
+        /// <summary>
+        ///  创建PLC访问器
+        /// </summary>
+        /// <param name="data">PLC信息</param>
+        /// <returns></returns>
+        /// <exception cref="Exception"> </exception>
+        public IPLCAccessor Create(PLCInfo data)
+        {
+            switch (data.Type)
+            {
+                case PLCType.Siemens:
+                    return new SiemensS7PLC(data.IP, data.Port, data.Rack, data.Slot);
+
+                default:
+                    return new VitrualRedisPLC(data, "127.0.0.1,database=1,prefix=Sorting:");
+            }
+        }
+    }
+}

+ 52 - 0
WCS.Service/PLCAccessors/SiemensS7PLC.cs

@@ -0,0 +1,52 @@
+
+using PlcSiemens.O;
+using PlcSiemens.Protocol.Common;
+using ServiceCenter;
+using ServiceCenter.Virtual_PLC;
+using WCS.Core;
+
+namespace WCS.Service.PLCAccessors
+{
+    public class SiemensS7PLC : IPLCAccessor
+    {
+        private SimenssPlc plc;
+
+        public SiemensS7PLC(string ip, int port, int rack, int slot)
+        {
+            plc = new SimenssPlc(ip, rack, slot);
+            plc.Connect();
+        }
+
+        public byte[] ReadBytes(ushort db, ushort address, ushort length)
+        {
+            if (ServiceHub.Any(SystemMode.虚拟plc))
+            {
+                return PlcData.Read(new PLCData { IP = plc.IP, DB = db, Length = length, DataLength = length });
+            }
+
+            if (!plc.Connected)
+                plc.Connect();
+
+            var res = plc.ReadArea(AreaType.DB, db, address, length, DataType.Byte);
+            if (res == null)
+                throw new Exception("读取DB块数据失败");
+            return res.Data;
+        }
+
+        public void WriteBytes(ushort db, ushort address, byte[] data)
+        {
+            if (ServiceHub.Any(SystemMode.虚拟plc))
+            {
+                PlcData.Write(new PLCData { IP = plc.IP, DB = db }, address, data);
+            }
+            else
+            {
+                if (!plc.Connected)
+                    plc.Connect();
+                var res = plc.WriteArea(AreaType.DB, db, address, (ushort)data.Length, DataType.Byte, data);
+                if (!res) throw new Exception("写入DB块数据失败");
+
+            }
+        }
+    }
+}

+ 70 - 0
WCS.Service/Program.cs

@@ -0,0 +1,70 @@
+using Microsoft.AspNetCore.Hosting;
+using System.Runtime.InteropServices;
+using ServiceCenter.Extensions;
+using ServiceCenter.Redis;
+using ServiceCenter.WebApi;
+
+namespace WCS.Service
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            #region 接入Redis
+
+            RedisHub.CreateContext(AppSettings.Config.GetConnectionString("Redis"), "default", true);
+
+            #endregion 接入Redis
+
+            //互斥锁检测
+            var mutexName = RedisHub.Default.Check("Mutex") ?? throw new Exception("请在Redis中配置互斥量值");
+
+            using var mt = new Mutex(true, mutexName);
+            if (mt.WaitOne())
+            {
+                CreateHostBuilder(args).Build().Run(); mt.ReleaseMutex();
+            }
+            else
+            {
+                Console.WriteLine("请勿重复运行");
+                //InfoLog.INFO_INIT("请勿重复运行");
+                Task.Delay(2000).Wait();
+            }
+        }
+
+        /// <summary>
+        /// 创建一个主机构建器
+        /// </summary>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        public static IHostBuilder CreateHostBuilder(string[] args)
+        {
+            //是否是win平台
+            var isWin = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+            Console.WriteLine($"win:{isWin}");
+            if (isWin)
+            {
+                var useUrls = RedisHub.Default.Check("UseUrls") ?? throw new Exception("请在Redis中配置网络访问端口");
+                //"http://*:8080"
+                return Host.CreateDefaultBuilder(args)
+                    .UseWindowsService()//win
+                    .ConfigureWebHostDefaults(web => //网络访问配置
+                    {
+                        web.UseUrls(useUrls); //设备访问端口
+                        web.UseStartup<Startup>(); //调用启动服务
+                    })
+                    .ConfigureServices((_, services) =>
+                    {
+                        services.AddHostedService<Worker>();
+                        services.AddHttpClient();
+                    });
+            }
+            return Host.CreateDefaultBuilder(args)
+                .UseSystemd()//linux
+                .ConfigureServices((_, services) =>
+                {
+                    services.AddHostedService<Worker>();
+                });
+        }
+    }
+}

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

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

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

@@ -0,0 +1,37 @@
+<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>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <NoWarn>1701;1702;8602;8616;8618;8625;8600;8603;8714;1591</NoWarn>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+    <NoWarn>1701;1702;8602;8616;8618;8625;8600;8603;8714;1591</NoWarn>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Remove="Logs\**" />
+    <Content Remove="Logs\**" />
+    <EmbeddedResource Remove="Logs\**" />
+    <None Remove="Logs\**" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MessagePack" Version="2.5.108" />
+    <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
+    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
+    <PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\WCS.WorkEngineering\WCS.WorkEngineering.csproj" />
+    <ProjectReference Include="..\业务工程\分拣库\WCS.WorkEngineering\WCS.WorkEngineering.csproj" />
+  </ItemGroup>
+</Project>

+ 173 - 0
WCS.Service/Worker.cs

@@ -0,0 +1,173 @@
+using MessagePack;
+using Newtonsoft.Json;
+using ServiceCenter;
+using ServiceCenter.Redis;
+using System.Text;
+using ServiceCenter.Extensions;
+using ServiceCenter.Logs;
+using ServiceCenter.SqlSugars;
+using SqlSugar;
+using WCS.Core;
+using WCS.WorkEngineering;
+
+namespace WCS.Service
+{
+    /// <summary>
+    /// 工作服务
+    /// </summary>
+    public class Worker : BackgroundService
+    {
+        /// <summary>
+        /// 记录器
+        /// </summary>
+        private readonly ILogger<Worker> _logger;
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="logger">记录器</param>
+        public Worker(ILogger<Worker> logger)
+        {
+            _logger = logger;
+        }
+
+        public static readonly string WcsDlog = "WCSDlog";
+        public static readonly string Wcsdb = "WCSDB";
+
+        /// <summary>
+        ///  执行
+        /// </summary>
+        /// <param name="stoppingToken">停止令牌</param>
+        /// <returns></returns>
+        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+        {
+            if (stoppingToken.IsCancellationRequested)
+                return;
+
+            #region 初始化Redis连接
+
+            var redisConnectionStrings = RedisHub.Default.Check("RedisConnectionStrings") ?? throw new Exception("请在Redis中配置RedisConnectionStrings");
+            var configs = JsonConvert.DeserializeObject<List<DataBaseConnectionString>>(redisConnectionStrings);
+            if (configs != null)
+            {
+                if (configs.All(v => v.Key != "Monitor")) throw new Exception("请在RedisConnectionStrings中配置监控RedisDB库连接字符串");
+            }
+
+            foreach (var redisConnection in configs!)
+            {
+                RedisHub.CreateContext(redisConnection.ConnectionString, redisConnection.Key);
+                switch (redisConnection.Key)
+                {
+                    case "Monitor":
+                        RedisHub.SetMonitorContextType(redisConnection.Key);
+                        RedisHub.Monitor.Serialize = obj =>
+                        {
+                            var bytes = MessagePackSerializer.Serialize(obj);
+                            return bytes;
+                        };
+                        RedisHub.Monitor.DeserializeRaw = (bytes, type) =>
+                        {
+                            var obj = MessagePackSerializer.Deserialize(type, bytes);
+                            return obj;
+                        };
+                        break;
+
+                    case "DebugRedisUrl":
+                        RedisHub.SetDebugContextType(redisConnection.Key);
+                        Configs.DebugRedisUrl = redisConnection.ConnectionString;
+                        break;
+
+                    case "WMS":
+                        RedisHub.SetWMSContextType(redisConnection.Key);
+                        break;
+                }
+            }
+
+            #endregion 初始化Redis连接
+
+            #region 启用日志
+
+            //var logConfigText = RedisHub.Default.Check("LogConfigText") ?? throw new Exception("请在Redis中配置log4net相关内容");
+            //var logConfig = JsonConvert.DeserializeObject<LogConfig>(logConfigText);
+            //LogHub.SetConfigInfo(logConfig!);
+
+            #endregion 启用日志
+
+            _logger.LogInformation("WCS开始启动");
+            Configs.ProtocolProxyBaseType = typeof(ProtocolProxy);
+            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+            Configs.StringEncoding = Encoding.UTF8;
+            //var warehouseName = RedisHub.Default.Check("WarehouseName") ?? throw new Exception("请在Redis中配置仓库名称");
+            //if (string.IsNullOrEmpty(warehouseName)) throw new Exception("请在Redis中配置仓库名称");
+            //ServiceHub.SetWarehouseName(warehouseName);
+
+            #region 初始化数据库连接
+
+            InstanceFactory.CustomDbName = "TDengine";
+            InstanceFactory.CustomDllName = "SqlSugar.BzTDengineCore";
+            InstanceFactory.CustomNamespace = "SqlSugar.BzTDengineCore";
+
+            var dbConnectionStrings = RedisHub.Default.Check("DbConnectionStrings") ?? throw new Exception("请在Redis中配置数据库连接相关内容");
+            ServiceHub.DbConnectionStrings = JsonConvert.DeserializeObject<List<DataBaseConnectionString>>(dbConnectionStrings);
+            if (ServiceHub.DbConnectionStrings != null)
+            {
+                if (ServiceHub.DbConnectionStrings.All(v => v.Key != Wcsdb)) throw new Exception("请在DbConnectionStrings中配置WCS基础数据库连接字符串");
+                if (ServiceHub.DbConnectionStrings.All(v => v.Key == Wcsdb && !v.IsDefault)) throw new Exception("请在DbConnectionStrings中配置WCS基础数据库为默认数据库");
+                // if (ServiceHub.DbConnectionStrings.All(v => v.Key != WcsDlog)) throw new Exception("请在DbConnectionStrings中配置WCS日志数据库连接字符串");
+            }
+
+            //设置连接信息
+            List<ConnectionConfig> connectionConfigs = new List<ConnectionConfig>();
+            foreach (var connectionString in ServiceHub.DbConnectionStrings!)
+            {
+                connectionConfigs.Add(new ConnectionConfig()
+                {
+                    ConfigId = connectionString.Key,
+                    ConnectionString = connectionString.ConnectionString,//连接符字串
+                    DbType = connectionString.DbType,//数据库类型
+                    IsAutoCloseConnection = true,//不设成true要手动close
+                    LanguageType = LanguageType.Chinese,
+                    MoreSettings = new ConnMoreSettings()
+                    {
+                        IsNoReadXmlDescription = true
+                    }
+                });
+            };
+            SqlSugarHelper.SetDb(new SqlSugarScope(connectionConfigs));
+
+            ServiceHub.DbConnectionStrings.InitDB();
+
+            #endregion 初始化数据库连接
+
+            #region 初始化设备信息
+
+            WorkStart.InitializeDeviceInfo();
+
+            #endregion 初始化设备信息
+
+            #region 初始化PLC访问器及PLC读取协议
+
+            //创建PLC访问器
+            Configs.PLCAccessorCreater = new PLCAccessors.PLCAccessorsCreater();
+
+            try
+            {
+                #region 唤醒所有的世界
+
+                World.StartAll();
+
+                #endregion 唤醒所有的世界
+
+                _logger.LogInformation("WCS启动成功");
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError("WCS启动失败{0}", ex.Message);
+            }
+
+            #endregion 初始化PLC访问器及PLC读取协议
+
+            LogHub.init();
+        }
+    }
+}

+ 29 - 0
WCS.Service/Worlds/initWorld.cs

@@ -0,0 +1,29 @@
+using WCS.Core;
+
+namespace WCS.Service.Worlds
+{
+    public class initWorld : World
+    {
+        protected override int Interval => 300;
+
+        protected override IEnumerable<string> GetChannelMsg(Channel channel)
+        {
+            throw new NotImplementedException();
+        }
+
+        protected override void OnError(Channel channel, Exception exception)
+        {
+            throw new NotImplementedException();
+        }
+
+        protected override void OnInternalLog(Channel channel, string msg)
+        {
+            throw new NotImplementedException();
+        }
+
+        protected override void OnLog(Channel channel, object logObj)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

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

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

+ 13 - 0
WCS.Service/appsettings.json

@@ -0,0 +1,13 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft": "Warning",
+      "Microsoft.Hosting.Lifetime": "Information"
+    }
+  },
+  "ConnectionStrings": {
+    "Redis": "10.30.37.2:6379,database=0,prefix=Sorting:"
+    //"Redis": "127.0.0.1,database=0,prefix=Sorting:"
+  }
+}

+ 252 - 0
WCS.Service/数据采集.cs

@@ -0,0 +1,252 @@
+using FreeRedis;
+using ServiceCenter.Extensions;
+using System.ComponentModel;
+using System.Diagnostics;
+using WCS.Core;
+using WCS.Entity.Protocol.BCR;
+using WCS.Entity.Protocol.DataStructure;
+using WCS.Entity.Protocol.Station;
+using WCS.WorkEngineering.Worlds;
+
+namespace WCS.WorkEngineering.Systems
+{
+    //[BelongTo(typeof(MainWorld))]
+    [Description("数据采集系统")]
+    public class 数据采集 : SystemBase
+    {
+        private RedisClient Redis = new RedisClient("");
+
+        public 数据采集()
+        {
+            var gs = Device.All.SelectMany(v => v.Protocols.Select(d => new { DB = $"{d.Value.DBInfo.No}:{d.Value.DBInfo.PLCInfo.IP}", d.Value.Position, TypeStr = d.Key, Dev = v }))
+                .GroupBy(v => v.DB);
+            foreach (var g in gs)
+            {
+                var min = g.OrderBy(v => v.Position).First();
+                var max = g.OrderByDescending(v => v.Position).First();
+                var t = Type.GetType(min.TypeStr);
+                min.Dev.Protocol(t, this.World);
+                max.Dev.Protocol(t, this.World);
+            }
+        }
+
+        public override List<object> GetObjects()
+        {
+            return new List<object>();
+        }
+
+        public override void Update(List<WorkTimes> list)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            var pack = new DeviceDataPack();
+            pack.Frame = DateTime.Now;
+            var ps = pack.GetType().GetProperties().OrderBy(x => x.Name);
+            foreach (var p in ps)
+            {
+                if (!p.PropertyType.IsArray&&p.PropertyType!= typeof(IBCR80[]))
+                    continue;
+                var dev = p.PropertyType.GetElementType();
+                if (dev.GetInterfaces().Any(v => v.GetInterfaces().Any(d => d.Name == "IProtocol")))
+                {
+                    var t = p.PropertyType.GetElementType();
+                    var protType = GetProtocolType(t);
+                    var arr = Device.All.Where(v => v.HasProtocol(protType))
+                    .Select(v =>
+                    {
+                        try
+                        {
+                            var obj = Activator.CreateInstance(t);
+                            t.GetProperty("Code").SetValue(obj, v.Code);
+                            dynamic protObj = v.Protocol(protType, World);
+                            if (protType == typeof(IBCR81))
+                            {
+                                var a = new Device<IBCR81>(v, World);
+                                var b = a.Data.Content;
+
+                            }
+                            var value = ServiceCenter.Extensions.TypeExtension.Copy(protObj, t);
+                            //t.GetProperty("Data").SetValue(obj, value);
+                            return obj;
+                        }
+                        catch (Exception ex)
+                        {
+                            return null;
+                        }
+                    }).Where(v => v != null).ToArray();
+
+                    var m = typeof(Enumerable).GetMethod("OfType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+                    m = m.MakeGenericMethod(t);
+                    var arr2 = m.Invoke(null, new object[] { arr });
+
+                    m = typeof(Enumerable).GetMethod("ToArray", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+                    m = m.MakeGenericMethod(t);
+                    var arr3 = m.Invoke(null, new object[] { arr2 });
+                    p.SetValue(pack, arr3);
+                }
+            }
+
+            //Redis.RPush("Packs", pack);
+            //if (Redis.LLen("Packs") > 50000)
+            //{
+            //    Redis.LTrim("Packs", 5000, -1);
+            //}
+
+            sw.Stop();
+            list.Add(new WorkTimes { Total = sw.ElapsedMilliseconds, Key = "采集数据" });
+            //var sw = new Stopwatch();
+            //sw.Start();
+            //var pack = new DeviceDataPack();
+            //pack.Frame = DateTime.Now;
+            //var ps = pack.GetType().GetProperties();
+            //foreach (var p in ps)
+            //{
+            //    if (!p.PropertyType.IsClass) continue;
+
+            //    var packAct = Activator.CreateInstance(p.PropertyType);
+            //    var prs = p.PropertyType.GetProperties();
+            //    foreach (var pr in prs)
+            //    {
+            //        if (!pr.PropertyType.IsArray) continue;
+
+            //        var yt = pr.PropertyType.GetElementType();
+            //        if (yt.IsClass)
+            //        {
+            //            var pros = yt.GetProperties();
+            //            //var entType = yt.GetGenericArguments()[0];
+            //            //var protType = GetProtocolType(entType);
+            //            var dataAct = Activator.CreateInstance(yt);
+            //            Parallel.ForEach(pros, pro =>
+            //            {
+            //                try
+            //                {
+            //                    if (pro.PropertyType != typeof(DateTime))
+            //                    {
+            //                        if (pro.PropertyType != typeof(string))
+            //                        {
+            //                            var protType = GetProtocolType(pro.PropertyType);
+            //                            var dev = Device.All
+            //                                .Where(v => v.HasProtocol(protType)).Select(v =>
+            //                                {
+            //                                    try
+            //                                    {
+            //                                        var obj = Activator.CreateInstance(pro.PropertyType);
+            //                                        pro.PropertyType.GetProperty("Code").SetValue(obj, v.Code);
+            //                                        var a = v.Protocol(protType, World);
+            //                                        var value = v.Protocol(protType, World).Copy(pro.PropertyType);
+            //                                        pro.SetValue(obj, value);
+            //                                        return obj;
+            //                                    }
+            //                                    catch (Exception ex)
+            //                                    {
+            //                                        return null;
+            //                                    }
+            //                                }).FirstOrDefault(v => v != null);
+
+            //                            if (dev != null)
+            //                            {
+            //                                pro.SetValue(dataAct, dev);
+            //                            }
+            //                        }
+            //                        else
+            //                        {
+            //                        }
+            //                    }
+            //                }
+            //                catch (Exception e)
+            //                {
+            //                    Console.WriteLine(e);
+            //                }
+            //            });
+
+            //            var a = 1;
+
+            //            //var m = typeof(Enumerable).GetMethod("OfType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //            //m = m.MakeGenericMethod(yt);
+            //            //var arr2 = m.Invoke(null, new object[] { datasAct });
+
+            //            //m = typeof(Enumerable).GetMethod("ToArray", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //            //m = m.MakeGenericMethod(yt);
+            //            //var arr3 = m.Invoke(null, new object[] { arr2 });
+            //            //p.SetValue(pack, arr3);
+
+            //            //var entType = yt.GetGenericArguments()[0];
+            //            //var protType = GetProtocolType(entType);
+            //            //var arr = Device.All.Where(v => v.HasProtocol(protType))
+            //            //    .Select(v =>
+            //            //    {
+            //            //        try
+            //            //        {
+            //            //            var obj = Activator.CreateInstance(yt);
+            //            //            yt.GetProperty("Code").SetValue(obj, v.Code);
+            //            //            //var value = v.Protocol(protType, World).Copy(entType);
+            //            //            //t.GetProperty("Data").SetValue(obj, value);
+            //            //            return obj;
+            //            //        }
+            //            //        catch (Exception ex)
+            //            //        {
+            //            //            return null;
+            //            //        }
+            //            //    }).Where(v => v != null).ToArray();
+
+            //            //var m = typeof(Enumerable).GetMethod("OfType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //            //m = m.MakeGenericMethod(yt);
+            //            //var arr2 = m.Invoke(null, new object[] { arr });
+
+            //            //m = typeof(Enumerable).GetMethod("ToArray", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //            //m = m.MakeGenericMethod(yt);
+            //            //var arr3 = m.Invoke(null, new object[] { arr2 });
+            //            //p.SetValue(pack, arr3);
+            //        }
+            //    }
+
+            //    //var t = p.PropertyType.GetElementType();
+            //    //if (t.IsGenericType)
+            //    //{
+            //    //    var entType = t.GetGenericArguments()[0];
+            //    //    var protType = GetProtocolType(entType);
+            //    //    var arr = Device.All.Where(v => v.HasProtocol(protType))
+            //    //    .Select(v =>
+            //    //    {
+            //    //        try
+            //    //        {
+            //    //            var obj = Activator.CreateInstance(t);
+            //    //            t.GetProperty("Code").SetValue(obj, v.Code);
+            //    //            //var value = v.Protocol(protType, World).Copy(entType);
+            //    //            //t.GetProperty("Data").SetValue(obj, value);
+            //    //            return obj;
+            //    //        }
+            //    //        catch (Exception ex)
+            //    //        {
+            //    //            return null;
+            //    //        }
+            //    //    }).Where(v => v != null).ToArray();
+
+            //    //    var m = typeof(Enumerable).GetMethod("OfType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //    //    m = m.MakeGenericMethod(t);
+            //    //    var arr2 = m.Invoke(null, new object[] { arr });
+
+            //    //    m = typeof(Enumerable).GetMethod("ToArray", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //    //    m = m.MakeGenericMethod(t);
+            //    //    var arr3 = m.Invoke(null, new object[] { arr2 });
+            //    //    p.SetValue(pack, arr3);
+            //    //}
+            //}
+
+            ////Redis.RPush("Packs", pack);
+            ////if (Redis.LLen("Packs") > 50000)
+            ////{
+            ////    Redis.LTrim("Packs", 5000, -1);
+            ////}
+
+            //sw.Stop();
+            //list.Add(new WorkTimes { Total = sw.ElapsedMilliseconds, Key = "采集数据" });
+        }
+
+        private Type GetProtocolType(Type source)
+        {
+            var t = source.GetInterfaces().FirstOrDefault(v => v.GetInterfaces().Any(d => d.Name == "IProtocol"));
+            return t;
+        }
+    }
+}

+ 91 - 0
WCS.WorkEngineering/Extensions/BCRExtension.cs

@@ -0,0 +1,91 @@
+using PlcSiemens.Core.Extension;
+using ServiceCenter.Logs;
+using WCS.Core;
+using WCS.Entity.Protocol.BCR;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    /// <summary>
+    ///  BCR扩展
+    /// </summary>
+    public class BCR : Device<IBCR81>
+    {
+        /// <summary>
+        ///   BCR扩展
+        /// </summary>
+        /// <param name="device"></param>
+        /// <param name="world"></param>
+        public BCR(Device device, World world) : base(device, world)
+        {
+        }
+
+        /// <summary>
+        ///  获取BCR码
+        /// </summary>
+        /// <returns></returns>
+        public string GetBCRCode()
+        {
+            var barcode = Data.Content.RemoveEscapeCharacters();
+
+            if (barcode.IsNullOrWhiteSpace()) throw new KnownException($"{Entity.Code}--扫码失败,内容为空", LogLevelEnum.High);
+
+            return barcode;
+        }
+    }
+
+    /// <summary>
+    ///  BCR83
+    /// </summary>
+    public class BCR83 : Device<IBCR83>
+    {
+        /// <summary>
+        ///   BCR扩展
+        /// </summary>
+        /// <param name="device"></param>
+        /// <param name="world"></param>
+        public BCR83(Device device, World world) : base(device, world)
+        {
+        }
+
+        /// <summary>
+        ///  获取BCR码
+        /// </summary>
+        /// <returns></returns>
+        public string GetBCRCode()
+        {
+            var barcode = Data.BcrCode.RemoveEscapeCharacters();
+
+            if (barcode.IsNullOrWhiteSpace()) throw new KnownException($"{Entity.Code}--扫码失败,内容为空", LogLevelEnum.High);
+
+            return barcode;
+        }
+    }
+
+    /// <summary>
+    ///  BCR扩展
+    /// </summary>
+    public static class BCRExtension
+    {
+        /// <summary>
+        ///  获取BCR码
+        /// </summary>
+        /// <param name="bs"></param>
+        /// <param name="code">BCR对应站点设备号</param>
+        /// <returns></returns>
+        public static string GetBCRCode(this List<BCR> bc, string code)
+        {
+            code = "BCR" + code;
+            var bcr = bc.FirstOrDefault(p => p.Entity.Code == code) ?? throw new KnownException($"未找到扫码器{code}", LogLevelEnum.High);
+            return bcr.GetBCRCode();
+        }
+
+        ///// <summary>
+        /////  获取BCR83码集合
+        ///// </summary>
+        ///// <param name="bc"></param>
+        ///// <returns></returns>
+        //public static string GetBCRCodeList(this BCR83 bc)
+        //{
+        //}
+    }
+}

+ 582 - 0
WCS.WorkEngineering/Extensions/DeviceExtension.cs

@@ -0,0 +1,582 @@
+using PlcSiemens.Core.Extension;
+using ServiceCenter.Extensions;
+using ServiceCenter.Logs;
+using ServiceCenter.Redis;
+using WCS.Core;
+using WCS.Entity.Protocol.BCR;
+using WCS.Entity.Protocol.Robot;
+using WCS.Entity.Protocol.Station;
+using WCS.Entity.Protocol.Truss;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    /// <summary>
+    ///  设备扩展
+    /// </summary>
+    public static class DeviceExtension
+    {
+        #region 设备类型
+
+        public static List<ProtocolInfo> ProtocolInfos = new List<ProtocolInfo>();
+
+        /// <summary>
+        ///  是否是巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static bool IsTunnel(this Device source)
+        {
+            return source.HasFlag(DeviceFlags.巷道);
+        }
+
+        #endregion 设备类型
+
+        #region 标签判断
+
+        public static bool HasFlag(this Device device, params DeviceFlags[] flag)
+        {
+            return flag.Any(device.HasFlag);
+        }
+
+        #endregion 标签判断
+
+        #region 协议操作扩展
+
+        public static void AddProtocol<T>(this Device device, int position, ushort db, string ip)
+        {
+            var info = new ProtocolInfo
+            {
+                Position = position,
+                DBInfo = new DBInfo
+                {
+                    No = db,
+                    PLCInfo = new PLCInfo
+                    {
+                        IP = ip,
+                        Port = 102,
+                        Rack = 0,
+                        Slot = 1,
+                        Type = PLCType.Siemens
+                    }
+                }
+            };
+            ProtocolInfos.Add(info);
+            device.AddProtocol<T>(info);
+        }
+
+        #endregion 协议操作扩展
+
+        #region 设备扩展方法
+
+        /// <summary>
+        ///  入库站点是否被禁止
+        /// </summary>
+        /// <returns></returns>
+        public static void 入库站点是否被禁止(this Device<IStation520, IStation521, IStation523> device)
+        {
+            var config = RedisHub.Default.Check("ForbidTubuEnter") ?? throw new Exception("请在Redis中配置入库口禁用");
+            var configs = config.Split(",");
+            if (configs.Contains(device.Entity.Code)) throw new KnownException("当前入库口已被禁用,请联系运维人员了解具体情况", LogLevelEnum.High);
+        }
+
+        /// <summary>
+        ///  入库站点是否被禁止
+        /// </summary>
+        /// <returns></returns>
+        public static void 入库站点是否被禁止(this Device<IStation520, IStation521, IStation523, IBCR81> device)
+        {
+            var config = RedisHub.Default.Check("ForbidTubuEnter") ?? throw new Exception("请在Redis中配置入库口禁用");
+            var configs = config.Split(",");
+            if (configs.Contains(device.Entity.Code)) throw new KnownException("当前入库口已被禁用,请联系运维人员了解具体情况", LogLevelEnum.High);
+        }
+
+        /// <summary>
+        ///  入库站点是否被禁止
+        /// </summary>
+        /// <returns></returns>
+        public static void 入库站点是否被禁止(this Device<IStation520, IStation521, IStation523, IStation91> device)
+        {
+            var config = RedisHub.Default.Check("ForbidTubuEnter") ?? throw new Exception("请在Redis中配置入库口禁用");
+            var configs = config.Split(",");
+            if (configs.Contains(device.Entity.Code)) throw new KnownException("当前入库口已被禁用,请联系运维人员了解具体情况", LogLevelEnum.High);
+        }
+
+        /// <summary>
+        ///  入库站点是否被禁止
+        /// </summary>
+        /// <returns></returns>
+        public static void 入库站点是否被禁止(this Device<IStation520, IStation521, IStation523, IStation91, IBCR81> device)
+        {
+            var config = RedisHub.Default.Check("ForbidTubuEnter") ?? throw new Exception("请在Redis中配置入库口禁用");
+            var configs = config.Split(",");
+            if (configs.Contains(device.Entity.Code)) throw new KnownException("当前入库口已被禁用,请联系运维人员了解具体情况", LogLevelEnum.High);
+        }
+
+        /// <summary>
+        ///  入库站点是否满足执行条件
+        /// </summary>
+        /// <returns></returns>
+        public static void 入库站点是否满足执行条件(this Device<IStation520, IStation521, IStation523> device)
+        {
+        }
+
+        /// <summary>
+        ///  入库站点是否满足执行条件
+        /// </summary>
+        /// <returns></returns>
+        public static void 入库站点是否满足执行条件(this Device<IStation520, IStation521, IStation523, IBCR81> device)
+        {
+            if (device.Data.VoucherNo != device.Data2.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{device.Data.VoucherNo}-DB521:{device.Data2.VoucherNo}", LogLevelEnum.High);
+            if (device.Data3.Status.HasFlag(StationStatus.Run)) throw new KnownException("设备运行中", LogLevelEnum.Low);
+            if (device.Data3.Status.HasFlag(StationStatus.PH_Status) && device.Data2.Request == 0) throw new KnownException("有光电无请求", LogLevelEnum.Mid);
+            if (!device.Data3.Status.HasFlag(StationStatus.PH_Status) && device.Data2.Request == 1) throw new KnownException("无光电有请求", LogLevelEnum.Mid);
+            if (!device.Data3.Status.HasFlag(StationStatus.OT_Status)) throw new KnownException("站台货物信息与实际占用不一致", LogLevelEnum.Low);
+            if (!device.Data3.Status.HasFlag(StationStatus.PH_Status)) throw new KnownException("无光电", LogLevelEnum.Mid);
+            if (device.Data2.Request != 1) throw new KnownException("无请求", LogLevelEnum.Mid);
+        }
+
+        /// <summary>
+        ///  入库站点是否满足执行条件
+        /// </summary>
+        /// <returns></returns>
+        public static void 入库站点是否满足执行条件(this Device<IStation520, IStation521, IStation523, IStation91> device)
+        {
+            if (device.Data.VoucherNo != device.Data2.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{device.Data.VoucherNo}-DB521:{device.Data2.VoucherNo}", LogLevelEnum.High);
+            if (device.Data3.Status.HasFlag(StationStatus.Run)) throw new KnownException("设备运行中", LogLevelEnum.Low);
+            if (device.Data3.Status.HasFlag(StationStatus.PH_Status) && device.Data2.Request == 0) throw new KnownException("有光电无请求", LogLevelEnum.Mid);
+            if (!device.Data3.Status.HasFlag(StationStatus.PH_Status) && device.Data2.Request == 1) throw new KnownException("无光电有请求", LogLevelEnum.Mid);
+            if (!device.Data3.Status.HasFlag(StationStatus.OT_Status)) throw new KnownException("站台货物信息与实际占用不一致", LogLevelEnum.Low);
+            if (!device.Data3.Status.HasFlag(StationStatus.PH_Status)) throw new KnownException("无光电", LogLevelEnum.Mid);
+            if (device.Data2.Request != 1) throw new KnownException("无请求", LogLevelEnum.Mid);
+        }
+
+        /// <summary>
+        ///  入库站点是否满足执行条件
+        /// </summary>
+        /// <returns></returns>
+        public static void 入库站点是否满足执行条件(this Device<IStation520, IStation521, IStation523, IStation91, IBCR81> device)
+        {
+            if (device.Data.VoucherNo != device.Data2.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{device.Data.VoucherNo}-DB521:{device.Data2.VoucherNo}", LogLevelEnum.High);
+            if (device.Data3.Status.HasFlag(StationStatus.Run)) throw new KnownException("设备运行中", LogLevelEnum.Low);
+            if (device.Data3.Status.HasFlag(StationStatus.PH_Status) && device.Data2.Request == 0) throw new KnownException("有光电无请求", LogLevelEnum.Mid);
+            if (!device.Data3.Status.HasFlag(StationStatus.PH_Status) && device.Data2.Request == 1) throw new KnownException("无光电有请求", LogLevelEnum.Mid);
+            if (!device.Data3.Status.HasFlag(StationStatus.OT_Status)) throw new KnownException("站台货物信息与实际占用不一致", LogLevelEnum.Low);
+            if (device.Data2.Request != 1.ToShort()) throw new KnownException("无请求", LogLevelEnum.Mid);
+            if (!device.Data3.Status.HasFlag(StationStatus.PH_Status)) throw new KnownException("无光电", LogLevelEnum.Mid);
+            if (device.Data2.Request != 1) throw new KnownException("无请求", LogLevelEnum.Mid);
+        }
+
+        /// <summary>
+        ///  获取BCR码
+        /// </summary>
+        /// <returns></returns>
+        public static string GetBCRCode(this IBCR81 bCR)
+        {
+            var barcode = bCR.Content.RemoveEscapeCharacters();
+            return barcode;
+        }
+
+        /// <summary>
+        ///  获取历史扫码记录
+        /// </summary>
+        /// <returns></returns>
+        public static List<string> GetBcrCodeList(this IBCR83 bCR)
+        {
+            return new List<string>() {
+                bCR.BcrCode1.RemoveEscapeCharacters(),
+                bCR.BcrCode2.RemoveEscapeCharacters(),
+                bCR.BcrCode3.RemoveEscapeCharacters(),
+                bCR.BcrCode4.RemoveEscapeCharacters(),
+                bCR.BcrCode5.RemoveEscapeCharacters(),
+                bCR.BcrCode6.RemoveEscapeCharacters(),
+                bCR.BcrCode7.RemoveEscapeCharacters(),
+                bCR.BcrCode8.RemoveEscapeCharacters(),
+                bCR.BcrCode9.RemoveEscapeCharacters(),
+                bCR.BcrCode10.RemoveEscapeCharacters(),
+                bCR.BcrCode11.RemoveEscapeCharacters() };
+        }
+
+        /// <summary>
+        ///  获取缓存列表
+        /// </summary>
+        /// <returns></returns>
+        public static IEnumerable<string> GetBcrCodeList(this IStation525 station525)
+        {
+            var properties = station525.GetType().GetProperties().ToArray();
+            var index = 1;
+            for (var i = 1; i <= 50; i++)
+            {
+                var value = properties[index].GetValue(station525).ToString().RemoveEscapeCharacters();
+                if (!value.IsNullOrEmpty()) yield return value;
+                index += 7;
+            }
+        }
+
+        /// <summary>
+        ///  获取缓存列表
+        /// </summary>
+        /// <returns></returns>
+        public static IEnumerable<int> GetTaskNoList(this ITruss531 station525)
+        {
+            var properties = station525.GetType().GetProperties().ToArray();
+            var index = 0;
+            for (var i = 0; i <= 59; i++)
+            {
+                var value = properties[index].GetValue(station525)!.ToInt();
+                if (value > 0) yield return value;
+                index += 1;
+            }
+        }
+
+        /// <summary>
+        ///  获取缓存列表
+        /// </summary>
+        /// <returns></returns>
+        public static IEnumerable<int> GetTaskNoList(this IRobot531 station525)
+        {
+            var properties = station525.GetType().GetProperties().ToArray();
+            var index = 0;
+            for (var i = 0; i <= 29; i++)
+            {
+                var value = properties[index].GetValue(station525)!.ToInt();
+                if (value > 0) yield return value;
+                index += 1;
+            }
+        }
+
+        /// <summary>
+        ///  通过仓库编码获取对应堆垛机信息
+        /// </summary>
+        /// <param name="warehouseCode">仓库编码</param>
+        /// <returns></returns>
+        public static string WarehouseToSrm(this string warehouseCode)
+        {
+            return warehouseCode switch
+            {
+                "1N" => "SRM1",
+                "1S" => "SRM2",
+                "2N" => "SRM3",
+                "2S" => "SRM4",
+                "3N" => "SRM5",
+                "3S" => "SRM6",
+                _ => "Error"
+            };
+        }
+
+        #endregion 设备扩展方法
+
+        /// <summary>
+        ///  获取仓库号
+        /// </summary>
+        public static string GetWareCode(this string add)
+        {
+            return add switch
+            {
+                #region 库一
+
+                //北
+                "SRM1" => "1N",
+                "2532" => "1N",
+                "2527" => "1N",
+                "2528" => "1N",
+                "418" => "1N",
+                "1606" => "1N",
+                "1666" => "1NR",
+                "1661" => "1NR",
+                "18" => "1N",
+                //南
+                "SRM2" => "1S",
+                "2732" => "1S",
+                "2727" => "1S",
+                "2728" => "1S",
+                "618" => "1S",
+                "1676" => "1SR",
+                "1681" => "1SR",
+
+                #endregion 库一
+
+                #region 库二
+
+                //北
+                "SRM3" => "2N",
+                "818" => "2N",
+                "1691" => "2NR",
+                "1696" => "2NR",
+                //南
+                "SRM4" => "2S",
+                "1018" => "2S",
+                "3128" => "2S",
+                "1711" => "2SR",
+                "1706" => "2SR",
+
+                #endregion 库二
+
+                #region 库二
+
+                //北
+                "SRM5" => "3N",
+                "1218" => "3N",
+                "1721" => "3NR",
+                "1726" => "3NR",
+                //南
+                "SRM6" => "3S",
+                "1418" => "3S",
+                "1736" => "3SR",
+                "1741" => "3SR",
+
+                #endregion 库二
+
+                _ => "",
+            };
+        }
+    }
+
+    /// <summary>
+    ///  设备标签
+    /// </summary>
+    [Flags]
+    public enum DeviceFlags : ulong
+
+    {
+        扫码 = 1L << 0,
+        称重 = 1L << 1,
+        外检 = 1L << 2,
+
+        顶升 = 1L << 3,
+        移栽 = 1L << 4,
+        旋转 = 1L << 5,
+
+        入库 = 1L << 6,
+        出库 = 1L << 7,
+
+        巷道口 = 1L << 8,
+        RGV口 = 1L << 9,
+        AGV取货站台口 = 1L << 10,
+
+        直轨 = 1L << 11,
+        弯轨 = 1L << 12,
+        环轨 = 1L << 13,
+
+        巷道 = 1L << 14,
+        堆垛机 = 1L << 15,
+        输送机 = 1L << 16,
+
+        #region 一轨双车堆垛机
+
+        一列堆垛机 = 1L << 23,
+        二列堆垛机 = 1L << 24,
+
+        #endregion 一轨双车堆垛机
+
+        RGV = 1L << 25,
+        桁架 = 1L << 26,
+        一楼扫码 = 1L << 27,
+        满轮主线第一次扫码 = 1L << 28,
+        主线分流点 = 1L << 29,
+        一楼叠盘机 = 1L << 30,
+        环形库分流点 = 1L << 31,
+        桁架分流点 = 1L << 32,
+        桁架缓存放行点 = 1L << 33,
+        桁架取货点 = 1L << 34,
+        拆盘机 = 1L << 35,
+        桁架码垛位 = 1L << 36,
+        环形库码垛工位 = 1L << 37,
+        Robot = 1L << 38,
+        桁架09缓存放行点 = 1L << 39,
+        桁架18缓存放行点 = 1L << 40,
+        桁架09异常缓存放行点 = 1L << 41,
+        二次码垛RGV取货口 = 1L << 42,
+        无交互触发设备 = 1L << 43,
+    }
+
+    /// <summary>
+    /// 输送机段信息
+    /// </summary>
+    public class StationSegmentInfo
+    {
+        /// <summary>
+        ///  构造函数
+        /// </summary>
+        /// <param name="start">起始设备号</param>
+        /// <param name="end">结束设备号</param>
+        /// <param name="ip">ip</param>
+        public StationSegmentInfo(int start, int end, string ip)
+        {
+            Start = start;
+            End = end;
+            Ip = ip;
+        }
+
+        /// <summary>
+        /// 起始设备编号
+        /// </summary>
+        public int Start { get; set; }
+
+        /// <summary>
+        ///结束设备编号
+        /// </summary>
+        public int End { get; set; }
+
+        /// <summary>
+        ///  输送机段所属IP
+        /// </summary>
+        public string Ip { get; set; }
+    }
+
+    /// <summary>
+    /// RGV信息
+    /// </summary>
+    public class RgvSegmentInfo
+    {
+        /// <summary>
+        ///  构造函数
+        /// </summary>
+        /// <param name="code"></param>
+        /// <param name="ip">ip</param>
+        public RgvSegmentInfo(int code, string ip)
+        {
+            Code = code;
+            Ip = ip;
+        }
+
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        public int Code { get; set; }
+
+        /// <summary>
+        ///  输送机段所属IP
+        /// </summary>
+        public string Ip { get; set; }
+    }
+
+    /// <summary>
+    /// 桁架信息
+    /// </summary>
+    public class TrussSegmentInfo
+    {
+        /// <summary>
+        ///  构造函数
+        /// </summary>
+        /// <param name="code"></param>
+        /// <param name="ip">ip</param>
+        public TrussSegmentInfo(int code, string ip)
+        {
+            Code = code;
+            Ip = ip;
+        }
+
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        public int Code { get; set; }
+
+        /// <summary>
+        ///  输送机段所属IP
+        /// </summary>
+        public string Ip { get; set; }
+    }
+
+    /// <summary>
+    ///  扫码器信息
+    /// </summary>
+    public class BcrInfo
+    {
+        /// <summary>
+        ///  构造函数
+        /// </summary>
+        /// <param name="deviceNo">设备编号</param>
+        /// <param name="ip">ip</param>
+        public BcrInfo(string[] deviceNo, string ip)
+        {
+            DeviceNo = deviceNo;
+            Ip = ip;
+        }
+
+        /// <summary>
+        ///  设备编号
+        /// </summary>
+        public string[] DeviceNo { get; set; }
+
+        /// <summary>
+        ///  ip
+        /// </summary>
+        public string Ip { get; set; }
+    }
+
+    /// <summary>
+    ///  外形信息
+    /// </summary>
+    public class ShapeInfo
+    {
+        /// <summary>
+        ///  构造函数
+        /// </summary>
+        /// <param name="deviceNo">设备编号</param>
+        /// <param name="ip">ip</param>
+        public ShapeInfo(int[] deviceNo, string ip)
+        {
+            DeviceNo = deviceNo;
+            Ip = ip;
+        }
+
+        /// <summary>
+        ///  设备编号
+        /// </summary>
+        public int[] DeviceNo { get; set; }
+
+        /// <summary>
+        ///  ip
+        /// </summary>
+        public string Ip { get; set; }
+    }
+
+    /// <summary>
+    /// 路径信息
+    /// </summary>
+    public class RouteInfo
+    {
+        /// <summary>
+        ///  构造函数
+        /// </summary>
+        /// <param name="deviceCode">起始点设备号</param>
+        /// <param name="nextList">下一个设备集合</param>
+        public RouteInfo(string deviceCode, string[] nextList)
+        {
+            DeviceCode = deviceCode;
+            NextList = nextList;
+        }
+
+        /// <summary>
+        ///  设备号
+        /// </summary>
+        public string DeviceCode { get; set; }
+
+        /// <summary>
+        ///  下一个设备
+        /// </summary>
+        public string[] NextList { get; set; }
+    }
+
+    /// <summary>
+    ///  缓存分流信息
+    /// </summary>
+    public class CacheBcr
+    {
+        public CacheBcr(string bcrCode, short nextAdd)
+        {
+            BcrCode = bcrCode;
+            NextAdd = nextAdd;
+        }
+
+        /// <summary>
+        ///  条码
+        /// </summary>
+        public string BcrCode { get; set; }
+
+        /// <summary>
+        ///  下一个地址
+        /// </summary>
+        public short NextAdd { get; set; }
+    }
+}

+ 43 - 0
WCS.WorkEngineering/Extensions/FlagExtension.cs

@@ -0,0 +1,43 @@
+using WCS.Core;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    /// <summary>
+    ///  设备标签扩展
+    /// </summary>
+    public static class DeviceFlagExtension
+    {
+        /// <summary>
+        ///  获取指定设备标签的值
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="device"></param>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        public static T GetFlag<T>(this Device device, string key)
+        {
+            if (!device.HasFlag(key))
+            {
+                if (typeof(T) == typeof(int)) device.AddFlag(key, default(int).ToString());
+                else if (typeof(T) == typeof(short)) device.AddFlag(key, default(bool).ToString());
+                else if (typeof(T) == typeof(bool)) device.AddFlag(key, default(bool).ToString());
+            }
+            var valeu = device.GetFlags(key).FirstOrDefault();
+
+            return (T)Convert.ChangeType(valeu, typeof(T));
+        }
+
+        /// <summary>
+        ///  设置指定设备标签的值
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="device"></param>
+        /// <param name="key"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public static void SetFlag<T>(this Device device, string key, T value)
+        {
+            device.SetFlag(key, value.ToString());
+        }
+    }
+}

+ 12 - 0
WCS.WorkEngineering/Extensions/RGVExtension.cs

@@ -0,0 +1,12 @@
+using WCS.Core;
+using WCS.Entity.Protocol.RGV;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    public class RGV : Device<IRGV520, IRGV521>
+    {
+        public RGV(Device device, World world) : base(device, world)
+        {
+        }
+    }
+}

+ 26 - 0
WCS.WorkEngineering/Extensions/RedisExtenshin.cs

@@ -0,0 +1,26 @@
+namespace WCS.WorkEngineering.Extensions
+{
+    public static class RedisExtenshin
+    {
+        ///// <summary>
+        /////  写入垛形信息
+        ///// </summary>
+        ///// <param name="redis"></param>
+        ///// <param name="stackInfoList"></param>
+        //public static void SetPalletizingInformation(this RedisClient redis, List<StackInfo> stackInfoList)
+        //{
+        //    var lockKey = nameof(SetPalletizingInformation);
+        //    try
+        //    {
+        //        if (redis.SetNx(lockKey, 1)) //设置锁
+        //        {
+        //            redis.Set("PalletizingInformation", JsonConvert.SerializeObject(stackInfoList));
+        //        }
+        //    }
+        //    finally
+        //    {
+        //        redis.Del(lockKey);
+        //    }
+        //}
+    }
+}

+ 60 - 0
WCS.WorkEngineering/Extensions/SRMExtension.cs

@@ -0,0 +1,60 @@
+using ServiceCenter.Extensions;
+using WCS.Core;
+using WCS.Entity;
+using WCS.Entity.Protocol.SRM;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    /// <summary>
+    /// 堆垛机扩展
+    /// </summary>
+    public class SRM : Device<ISRM520, ISRM521, ISRM537>
+    {
+        public SRM(Device device, World world) : base(device, world)
+        {
+        }
+
+        /// <summary>
+        ///
+        /// </summary>
+        /// <param name="taskInfo"></param>
+        public void 一轨两车任务下发前置条件判断及处理(WCS_TaskInfo taskInfo)
+        {
+            var col = 0;
+            switch (taskInfo.Type)
+            {
+                case TaskType.SetPlate: return;
+                case TaskType.EnterDepot:
+                    col = taskInfo.AddrTo.Split("-")[1].ToInt();
+                    break;
+
+                case TaskType.OutDepot:
+                    col = taskInfo.AddrFrom.Split("-")[1].ToInt();
+                    break;
+
+                case TaskType.TransferDepot: return;
+                case TaskType.Delivery: return;
+                case TaskType.EmptyInit: return;
+                default: return;
+            }
+            if (Entity.HasFlag(DeviceFlags.一列堆垛机))
+            {
+                if (col > 10)// 跑全程
+                {
+                    if (Data2.Status.HasFlag(SrmStatus.Stopper_Mark))
+                    {
+                    }
+                }
+            }
+            else if (Entity.HasFlag(DeviceFlags.二列堆垛机))
+            {
+                if (col < 11)// 跑全程
+                {
+                    if (Data2.Status.HasFlag(SrmStatus.Stopper_Mark))
+                    {
+                    }
+                }
+            }
+        }
+    }
+}

+ 60 - 0
WCS.WorkEngineering/Extensions/StationExtension.cs

@@ -0,0 +1,60 @@
+using ServiceCenter.Extensions;
+using ServiceCenter.Logs;
+using ServiceCenter.Redis;
+using WCS.Core;
+using WCS.Entity.Protocol.Station;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    public class Station : Device<IStation520, IStation521, IStation523>
+    {
+        public Station(Device device, World world) : base(device, world)
+        {
+        }
+
+        /// <summary>
+        ///  入库站点是否被禁止
+        /// </summary>
+        /// <returns></returns>
+        public void 入库站点是否被禁止()
+        {
+            var config = RedisHub.Default.Check("ForbidTubuEnter") ?? throw new Exception("请在Redis中配置入库口禁用");
+            var configs = config.Split(",");
+            if (configs.Contains(Entity.Code)) throw new KnownException("当前入库口已被禁用,请联系运维人员了解具体情况", LogLevelEnum.High);
+        }
+
+        /// <summary>
+        ///  入库站点是否满足执行条件
+        /// </summary>
+        /// <returns></returns>
+        public void 入库站点是否满足执行条件()
+        {
+            if (Data.VoucherNo != Data2.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{Data.VoucherNo}-DB521:{Data2.VoucherNo}", LogLevelEnum.High);
+            if (Data3.Status.HasFlag(StationStatus.Run)) throw new KnownException("设备运行中", LogLevelEnum.Low);
+            if (Data3.Status.HasFlag(StationStatus.PH_Status) && Data2.Request == 0) throw new KnownException("有光电无请求", LogLevelEnum.Mid);
+            if (!Data3.Status.HasFlag(StationStatus.PH_Status) && Data2.Request == 1) throw new KnownException("无光电有请求", LogLevelEnum.Mid);
+            if (!Data3.Status.HasFlag(StationStatus.OT_Status)) throw new KnownException("站台货物信息与实际占用不一致", LogLevelEnum.Low);
+        }
+
+        /// <summary>
+        ///  主线分流点是否满足执行条件
+        /// </summary>
+        /// <returns></returns>
+        public void 主线分流点是否满足执行条件()
+        {
+            if (Data.VoucherNo != Data2.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{Data.VoucherNo}-DB521:{Data2.VoucherNo}", LogLevelEnum.High);
+            if (Data3.Status.HasFlag(StationStatus.Run)) throw new KnownException("设备运行中", LogLevelEnum.Low);
+            if (Data3.Status.HasFlag(StationStatus.PH_Status) && Data2.Request == 0) throw new KnownException("有光电无请求", LogLevelEnum.Mid);
+            if (!Data3.Status.HasFlag(StationStatus.PH_Status) && Data2.Request == 1) throw new KnownException("无光电有请求", LogLevelEnum.Mid);
+            if (!Data3.Status.HasFlag(StationStatus.OT_Status)) throw new KnownException("站台货物信息与实际占用不一致", LogLevelEnum.Low);
+        }
+
+        public string GetBarCode(World world)
+        {
+            var code = "BCR" + Entity.Code;
+            var bcrdev = Device.All.FirstOrDefault(v => v.Code == code) ?? throw new KnownException($"未找到扫码器{code}", LogLevelEnum.High);
+            var bcr = new BCR(bcrdev, world);
+            return bcr.GetBCRCode();
+        }
+    }
+}

+ 615 - 0
WCS.WorkEngineering/Extensions/TaskExtension.cs

@@ -0,0 +1,615 @@
+using ServiceCenter.Extensions;
+using ServiceCenter.Logs;
+using ServiceCenter.Redis;
+using ServiceCenter.SqlSugars;
+using SqlSugar;
+using WCS.Core;
+using WCS.Entity;
+using TaskStatus = WCS.Entity.TaskStatus;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    /// <summary>
+    /// 任务扩展
+    /// </summary>
+    public static class TaskExtension
+    {
+        /// <summary>
+        /// 更新任务执行记录
+        /// 同步更新历史任务
+        /// </summary>
+        /// <param name="task">任务信息</param>
+        /// <param name="db">数据库上下文</param>
+        /// <param name="curPoint">当前地址</param>
+        /// <param name="desc">描述</param>
+        public static void AddWCS_TASK_DTL(this WCS_TaskInfo task, SqlSugarScopeProvider db, string curPoint, string desc) => task.AddWCS_TASK_DTL(db, curPoint, "", desc);
+
+        /// <summary>
+        /// 更新任务执行记录
+        /// 同步更新历史任务
+        /// </summary>
+        /// <param name="task">任务信息</param>
+        /// <param name="db">数据库上下文</param>
+        /// <param name="curPoint">当前地址</param>
+        /// <param name="nextPoint">下一个地址</param>
+        /// <param name="desc">描述</param>
+        public static void AddWCS_TASK_DTL(this WCS_TaskInfo task, SqlSugarScopeProvider db, string curPoint, string nextPoint, string desc)
+        {
+            db.InsertableRowLock(new WCS_TaskDtl
+            {
+                ID = Guid.NewGuid(),
+                ParentTaskCode = task.ID,
+                CurPoint = curPoint,
+                NextPoint = nextPoint,
+                Desc = desc,
+                AddWho = "WCS"
+            }).SplitTable().ExecuteCommand();
+            task.UpdateableOldTask(db);
+        }
+
+        /// <summary>
+        /// 更新任务执行记录
+        /// 同步更新历史任务
+        /// </summary>
+        /// <param name="task">任务信息</param>
+        /// <param name="db">数据库上下文</param>
+        /// <param name="curPoint">当前地址</param>
+        /// <param name="desc">描述</param>
+        public static void AddWCS_TASK_DTL(this WCS_TaskOld task, SqlSugarScopeProvider db, string curPoint, string desc) => task.AddWCS_TASK_DTL(db, curPoint, "", desc);
+
+        /// <summary>
+        /// 更新任务执行记录
+        /// 同步更新历史任务
+        /// </summary>
+        /// <param name="task">任务信息</param>
+        /// <param name="db">数据库上下文</param>
+        /// <param name="curPoint">当前地址</param>
+        /// <param name="nextPoint">下一个地址</param>
+        /// <param name="desc">描述</param>
+        public static void AddWCS_TASK_DTL(this WCS_TaskOld task, SqlSugarScopeProvider db, string curPoint, string nextPoint, string desc)
+        {
+            db.Insertable(new WCS_TaskDtl
+            {
+                ID = Guid.NewGuid(),
+                ParentTaskCode = task.Id,
+                CurPoint = curPoint,
+                NextPoint = nextPoint,
+                Desc = desc,
+                AddWho = "WCS"
+            }).SplitTable().ExecuteCommand();
+        }
+
+        /// <summary>
+        ///  更新历史表数据
+        /// </summary>
+        /// <param name="taskInfo"></param>
+        /// <param name="db"></param>
+        public static void UpdateableOldTask(this WCS_TaskInfo taskInfo, SqlSugarScopeProvider db)
+        {
+            if (taskInfo.Status == TaskStatus.NewBuild) return;
+
+            // 同步任务信息
+            var taskOld = db.Queryable<WCS_TaskOld>().Where(v => v.Id == taskInfo.ID).SplitTable(tabs => tabs.Take(2)).ToList().OrderByDescending(v => v.AddTime).First();
+            if (taskOld is not null)
+            {
+                if (taskInfo.Status >= TaskStatus.Finish) taskInfo.CompleteOrCancelTasks(db);
+                else
+                {
+                    taskOld = taskInfo.Mapper<WCS_TaskOld, WCS_TaskInfo>();
+                    taskOld.Id = taskInfo.ID;
+                    db.UpdateableRowLock(taskOld).Where(x => x.Id == taskOld.Id).SplitTable(tabs => tabs.Take(2)).ExecuteCommand();
+                }
+            }
+            else
+            {
+                throw new KnownException($"WCS_TaskOld表中不存在任务:{taskInfo.ID},无法执行WCS_TaskInfo与WCS_TaskOld同步动作", LogLevelEnum.Mid);
+            }
+        }
+
+        /// <summary>
+        /// 完成或取消任务
+        /// </summary>
+        /// <param name="taskInfo"></param>
+        /// <param name="db"></param>
+        public static void CompleteOrCancelTasks(this WCS_TaskInfo taskInfo, SqlSugarScopeProvider db)
+        {
+            if (taskInfo.Status is not Entity.TaskStatus.Finish and not Entity.TaskStatus.Cancel) throw new KnownException("任务未完成或取消,无法执行WCS_TaskInfo与WCS_TaskOld同步动作", LogLevelEnum.Mid);
+            // 任务完成或取消,进行相关同步动作
+            var taskOld = db.Queryable<WCS_TaskOld>().Where(v => v.Id == taskInfo.ID).SplitTable(tabs => tabs.Take(2)).ToList().OrderByDescending(v => v.AddTime).First();
+            if (taskOld is not null)
+            {
+                taskOld = taskInfo.Mapper<WCS_TaskOld, WCS_TaskInfo>();
+                taskOld.Id = taskInfo.ID;
+                //更新任务历史表,删除任务当前表
+                db.UpdateableRowLock(taskOld).Where(x => x.Id == taskOld.Id).SplitTable(tabs => tabs.Take(2)).ExecuteCommand();
+                db.DeleteableRowLock(taskInfo).ExecuteCommand();
+            }
+            else
+            {
+                throw new KnownException($"WCS_TaskOld表中不存在任务:{taskInfo.ID},无法执行WCS_TaskInfo与WCS_TaskOld同步动作", LogLevelEnum.Mid);
+            }
+        }
+
+        /// <summary>
+        ///  更新表数据
+        /// </summary>
+        /// <param name="taskInfo"></param>
+        /// <param name="db"></param>
+        public static void Updateable(this WCS_TaskInfo taskInfo, SqlSugarScopeProvider db)
+        {
+        }
+
+        ///// <summary>
+        /////  获取出库任务
+        ///// </summary>
+        ///// <param name="taskInfo">任务</param>
+        ///// <param name="db">db</param>
+        ///// <param name="allOutCode">可用出库站台</param>
+        ///// <param name="floor">楼层</param>
+        ///// <param name="obj">堆垛机</param>
+        ///// <param name="index">递归次数</param>
+        ///// <returns></returns>
+        ///// <exception cref="KnownException"></exception>
+        //public static WCS_TaskInfo GetOutTask(this WCS_TaskInfo taskInfo, SqlSugarHelper db, List<string> allOutCode, int floor, SRM obj, int index = 1)
+        //{
+        //    return task;
+        //}
+
+        /// <summary>
+        /// 获取AGV任务ID
+        /// </summary>
+        /// <param name="db">db</param>
+        /// <returns></returns>
+        public static int GetAgvTaskId(this SqlSugarHelper db)
+        {
+            //最多任务号不再连续
+            var id = db.Default.Queryable<WCS_AgvTaskInfo>().NoLock().SplitTable(v => v.Take(2)).Max(v => v.ID);
+            return id + 1;
+        }
+
+        /// <summary>
+        /// 更新任务执行记录
+        /// </summary>
+        /// <param name="task">任务信息</param>
+        public static void UpdateRedisHash(this WCS_TaskInfo task)
+        {
+            var key = $"Hash:{task.ID}";
+            if (task.Status >= TaskStatus.Finish)
+            {
+                RedisHub.WMS.Del(key);
+            }
+            else
+            {
+                RedisHub.WMS.HMSet(key, task.ToDic());
+            }
+        }
+
+        #region 工字轮支线分流
+
+        /// <summary>
+        ///  初始化码垛信息
+        /// </summary>
+        /// <param name="task"></param>
+        public static void InitStackStructure(this WCS_TaskInfo task, SqlSugarScopeProvider db, World world)
+        {
+            var billBomsetgrp = db.Queryable<BillBomsetgrp>().Single(x => x.IsStop == 0 && x.BomCode.Contains(task.MatCode));
+            if (billBomsetgrp == null)
+            {
+                world.Log($"物料规格[{task.MatCode}]无可用码垛垛形");
+                return;
+            };
+            var billBomsetinfos = db.Queryable<BillBomsetinfo>().Where(x => x.BomSetHdrId == billBomsetgrp.Id).ToList();
+
+            //开始构造垛形信息
+            var palletizing = new WCS_Palletizing()
+            {
+                Code = billBomsetgrp.Code,
+                ShortCode = billBomsetgrp.ShortCode,
+                ProMaterCode = billBomsetgrp.ProMaterCode,
+                TpTypeCode = billBomsetgrp.TpTypeCode,
+                LayerCountQty = 2,
+                StampType = billBomsetgrp.StampType,
+                Finish = false,
+                AddTime = DateTime.Now,
+                TaskId = task.ID,
+                WarehouseCode = task.WarehouseCode,
+                DeviceCode = task.Device
+            };
+            palletizing = db.InsertableRowLock(palletizing).ExecuteReturnEntity();
+            foreach (var item in billBomsetinfos.Where(x => x.IsEmpty == 0).GroupBy(x => x.Row).OrderBy(x => x.Key))
+            {
+                var layerNo = item.Key <= 6 ? 1 : 2;
+                //获取层信息
+                var palletizingLayer = db.Queryable<WCS_PalletizingLayer>().Single(x => x.PalletizingId == palletizing.Id && x.LayerNo == layerNo);
+
+                if (palletizingLayer == null)
+                {
+                    palletizingLayer = new WCS_PalletizingLayer()
+                    {
+                        LayerNo = layerNo,
+                        PalletizingId = palletizing.Id,
+                        WarehouseCode = palletizing.WarehouseCode,
+                    };
+                    palletizingLayer = db.InsertableRowLock(palletizingLayer).ExecuteReturnEntity();
+                }
+
+                //获取行信息
+                var palletizingRow = db.Queryable<WCS_PalletizingRow>().Single(x => x.PalletizingLayerId == palletizingLayer.Id && x.RowNo == item.Key);
+                if (palletizingRow == null)
+                {
+                    palletizingRow = new WCS_PalletizingRow()
+                    {
+                        RowNo = item.Key,
+                        PalletizingLayerId = palletizingLayer.Id,
+                        PalletizingId = palletizing.Id,
+                        WarehouseCode = palletizingLayer.WarehouseCode
+                    };
+                    palletizingRow = db.InsertableRowLock(palletizingRow).ExecuteReturnEntity();
+                }
+
+                //重新查询最新的数据
+                var layer = palletizingLayer;
+                palletizingLayer = db.Queryable<WCS_PalletizingLayer>().Single(x => x.Id == layer.Id);
+                var row = palletizingRow;
+                palletizingRow = db.Queryable<WCS_PalletizingRow>().Single(x => x.Id == row.Id);
+
+                //构造位信息
+                foreach (var loc in item)
+                {
+                    var palletizingLoc = db.Queryable<WCS_PalletizingLoc>().Single(x => x.PalletizingRowId == palletizingRow.Id && x.XYNo == loc.XYNo);
+
+                    if (palletizingLoc == null)
+                    {
+                        palletizingLoc = new WCS_PalletizingLoc()
+                        {
+                            IsEmpty = loc.IsEmpty != 0,
+                            XYNo = loc.XYNo,
+                            MatCode = loc.MatCode,
+                            SideNum = loc.SideNum,
+                            SpoolType = loc.SpoolType,
+                            TaskId = task.ID,
+                            PalletizingRowId = palletizingRow.Id,
+                            Finish = false,
+                            WarehouseCode = palletizingRow.WarehouseCode
+                        };
+                        db.InsertableRowLock(palletizingLoc).ExecuteReturnEntity();
+                    }
+
+                    //同步是否混合料行
+                    palletizingRow.IsMixRow = loc.IsMixRow != 0;
+                    db.UpdateableRowLock(palletizingRow).ExecuteCommand();
+                }
+                //更新行信息
+                palletizingRow = db.Queryable<WCS_PalletizingRow>().Includes(x => x.Locs).Single(x => x.Id == row.Id);
+                palletizingRow.QtyMaxCount = palletizingRow.Locs.Count(x => !x.IsEmpty);
+                palletizingRow.IsEmpty = palletizingRow.QtyMaxCount <= 0;
+                palletizingRow.MatCodeList = palletizingRow.Locs.Select(x => x.MatCode).ToList().GetMatList();
+                db.UpdateableRowLock(palletizingRow).ExecuteCommand();
+                //更新层信息
+                palletizingLayer = db.Queryable<WCS_PalletizingLayer>().Includes(x => x.Rows, l => l.Locs).Single(x => x.Id == layer.Id);
+                var count = palletizingLayer.Rows.Count(x => !x.IsEmpty); //计算所有不空数量
+                palletizingLayer.IsEmpty = count <= 0;
+                palletizingLayer.RowCountQty = palletizingLayer.Rows.Count;
+                palletizingLayer.Finish = false;
+                palletizingLayer.MatCodeList = palletizingLayer.Rows.SelectMany(x => x.Locs).Select(x => x.MatCode).ToList().GetMatList();
+
+                db.UpdateableRowLock(palletizingLayer).ExecuteCommand();
+            }
+
+            var palletizing1 = palletizing;
+            palletizing = db.Queryable<WCS_Palletizing>().Includes(x => x.Layers, r => r.Rows, l => l.Locs).Single(x => x.Id == palletizing1.Id);
+            //计算垛形信息
+            var goods = palletizing.Layers.Select(x => x.Rows).SelectMany(x => x).Select(x => x.Locs).SelectMany(x => x).ToList();
+            palletizing.CountQty = goods.Count(x => !x.IsEmpty);
+            palletizing.MatCodeList = palletizing.Layers.SelectMany(x => x.Rows).SelectMany(x => x.Locs).Select(x => x.MatCode).ToList().GetMatList();
+            db.UpdateableRowLock(palletizing).ExecuteCommand();
+        }
+
+        public static string GetMatList(this List<string> matList)
+        {
+            return matList.Distinct().Aggregate("", (current, mat) => current + $"[{mat}]");
+        }
+
+        /// <summary>
+        ///  去除转义字符
+        /// </summary>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public static string RemoveEscapeCharacters(this string? value)
+        {
+            return value.Trim('\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v').Trim();
+        }
+
+        #endregion 工字轮支线分流
+    }
+
+    /// <summary>
+    ///  垛形位信息
+    /// </summary>
+    public class StackPosInfo
+    {
+        /// <summary>
+        ///  任务号
+        /// </summary>
+        public int TaskNumber { get; set; }
+
+        /// <summary>
+        /// 是否空置
+        /// </summary>
+        public bool IsEmpty { get; set; }
+
+        /// <summary>
+        /// 坐标号
+        /// </summary>
+        public string XYNo { get; set; }
+
+        /// <summary>
+        /// 物料编码
+        /// </summary>
+        public string MatCode { get; set; }
+
+        /// <summary>
+        /// 正反面
+        /// </summary>
+        public int SideNum { get; set; }
+
+        /// <summary>
+        /// 工字轮类型
+        /// </summary>
+        public string SpoolType { get; set; }
+
+        /// <summary>
+        ///  是否结束
+        /// </summary>
+        public bool Finish { get; set; }
+    }
+
+    /// <summary>
+    /// 垛型明细表
+    /// </summary>
+    [SugarTable("Bill_BomSetInfo", tableDescription: "垛型明细表")]
+    public partial class BillBomsetinfo
+    {
+        /// <summary>
+        ///  ID
+        /// </summary>
+        [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, ColumnDescription = "ID")]
+        public virtual long Id { get; set; }
+
+        /// <summary>
+        ///  备注
+        /// </summary>
+        [SugarColumn(ColumnName = "Memo", Length = 500, IsNullable = true, ColumnDataType = "nvarchar", DefaultValue = "", ColumnDescription = "备注")]
+        public virtual string Memo { get; set; }
+
+        /// <summary>
+        ///  创建用户
+        /// </summary>
+        [SugarColumn(ColumnName = "AddWho", Length = 50, ColumnDataType = "nvarchar", DefaultValue = "", IsNullable = false, ColumnDescription = "创建用户")]
+        public virtual string AddWho { get; set; } = "";
+
+        /// <summary>
+        ///  更新用户
+        /// </summary>
+        [SugarColumn(ColumnName = "EditWho", Length = 50, ColumnDataType = "nvarchar", DefaultValue = "", IsNullable = false, ColumnDescription = "更新用户")]
+        public virtual string EditWho { get; set; } = "";
+
+        /// <summary>
+        ///  创建时间
+        /// </summary>
+        [SugarColumn(ColumnName = "AddTime", DefaultValue = "1900-1-1", IsNullable = false, ColumnDescription = "创建时间")]
+        public virtual DateTime AddTime { get; set; } = DateTime.Now;
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnName = "EditTime", DefaultValue = "1900-1-1", IsNullable = false, ColumnDescription = "更新时间")]
+        public virtual DateTime EditTime { get; set; } = DateTime.Now;
+
+        /// <summary>
+        /// 垛型id
+        /// </summary>
+        [SugarColumn(ColumnDataType = "bigint", IsNullable = false, ColumnDescription = "垛型id")]
+        public long BomSetHdrId { get; set; }
+
+        /// <summary>
+        /// 是否停用
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = false, ColumnDescription = "是否停用")]
+        public int IsStop { get; set; }
+
+        /// <summary>
+        /// 是否空置
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = false, ColumnDescription = "是否空置")]
+        public int IsEmpty { get; set; }
+
+        /// <summary>
+        /// 坐标号
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = false, ColumnDescription = "坐标号")]
+        public string XYNo { get; set; }
+
+        /// <summary>
+        /// 物料id
+        /// </summary>
+        [SugarColumn(ColumnDataType = "bigint", ColumnDescription = "物料id")]
+        public long MatId { get; set; }
+
+        /// <summary>
+        /// 物料编码
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", IsNullable = true, Length = 50, ColumnDescription = "物料编码")]
+        public string MatCode { get; set; }
+
+        /// <summary>
+        /// 正反面
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = true, ColumnDescription = "正反面")]
+        public int SideNum { get; set; }
+
+        /// <summary>
+        /// 工字轮类型
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true, ColumnDescription = "工字轮类型")]
+        public string SpoolType { get; set; }
+
+        /// <summary>
+        /// 钢丝类型
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = true, ColumnDescription = "钢丝类型")]
+        public string SilkType { get; set; }
+
+        /// <summary>
+        /// 钢丝直径
+        /// </summary>
+        [SugarColumn(ColumnDataType = "decimal", Length = 18, IsNullable = true, ColumnDescription = "钢丝直径")]
+        public decimal SilkDiam { get; set; }
+
+        /// <summary>
+        /// 钢丝直径上限
+        /// </summary>
+        [SugarColumn(ColumnDataType = "decimal", Length = 18, IsNullable = true, ColumnDescription = "钢丝直径上限")]
+        public decimal SilkDiamMaxCount { get; set; }
+
+        /// <summary>
+        /// 数量上限
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = true, ColumnDescription = "数量上限")]
+        public int QtyMaxCount { get; set; }
+
+        /// <summary>
+        /// 是否芯股
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = false, ColumnDescription = "是否芯股")]
+        public int IsCore { get; set; }
+
+        /// <summary>
+        /// 09垛型里面的行(1-12)
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = true, ColumnDescription = "09垛型里面的行(1-12)")]
+        public int Row { get; set; }
+
+        /// <summary>
+        /// 是否混合料行(09垛型使用,1是0否)
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = false, ColumnDescription = "是否混合料行(09垛型使用,1是0否)")]
+        public int IsMixRow { get; set; }
+
+        /// <summary>
+        /// 混合料行编码
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", IsNullable = true, Length = 50, ColumnDescription = "混合料行编码")]
+        public string MixRowCode { get; set; }
+
+        /// <summary>
+        ///  第一种第二种第三种
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = false, ColumnDescription = "第一种第二种第三种")]
+        public int CategoryId { get; set; }
+    }
+
+    /// <summary>
+    ///  垛形主表
+    /// </summary>
+    [Tenant("fj")]
+    [SugarTable("Bill_BomSetGrp", tableDescription: "垛形主表")]
+    public partial class BillBomsetgrp
+    {
+        /// <summary>
+        ///  ID
+        /// </summary>
+        [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, ColumnDescription = "ID")]
+        public virtual long Id { get; set; }
+
+        /// <summary>
+        ///  备注
+        /// </summary>
+        [SugarColumn(ColumnName = "Memo", Length = 500, IsNullable = true, ColumnDataType = "nvarchar", DefaultValue = "", ColumnDescription = "备注")]
+        public virtual string Memo { get; set; }
+
+        /// <summary>
+        ///  创建用户
+        /// </summary>
+        [SugarColumn(ColumnName = "AddWho", Length = 50, ColumnDataType = "nvarchar", DefaultValue = "", IsNullable = false, ColumnDescription = "创建用户")]
+        public virtual string AddWho { get; set; } = "";
+
+        /// <summary>
+        ///  更新用户
+        /// </summary>
+        [SugarColumn(ColumnName = "EditWho", Length = 50, ColumnDataType = "nvarchar", DefaultValue = "", IsNullable = false, ColumnDescription = "更新用户")]
+        public virtual string EditWho { get; set; } = "";
+
+        /// <summary>
+        ///  创建时间
+        /// </summary>
+        [SugarColumn(ColumnName = "AddTime", DefaultValue = "1900-1-1", IsNullable = false, ColumnDescription = "创建时间")]
+        public virtual DateTime AddTime { get; set; } = DateTime.Now;
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [SugarColumn(ColumnName = "EditTime", DefaultValue = "1900-1-1", IsNullable = false, ColumnDescription = "更新时间")]
+        public virtual DateTime EditTime { get; set; } = DateTime.Now;
+
+        /// <summary>
+        /// 是否停用
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = false, ColumnDescription = "是否停用")]
+        public int IsStop { get; set; }
+
+        /// <summary>
+        /// 垛型编码
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = false, ColumnDescription = "垛型编码")]
+        public string Code { get; set; }
+
+        /// <summary>
+        ///  短垛型编码
+        /// </summary>
+        [SugarColumn(ColumnDataType = "smallint", IsNullable = false, ColumnDescription = "短垛型编码")]
+        public short ShortCode { get; set; }
+
+        /// <summary>
+        /// 垛型名称
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 100, IsNullable = false, ColumnDescription = "垛型名称")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// BomCode(投料信息)
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 200, IsNullable = false, ColumnDescription = "BomCode(投料信息)")]
+        public string BomCode { get; set; }
+
+        /// <summary>
+        /// 帘线物料编码
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = false, ColumnDescription = "帘线物料编码")]
+        public string ProMaterCode { get; set; }
+
+        /// <summary>
+        /// 工字轮个数
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = false, ColumnDescription = "工字轮个数")]
+        public int HWCountQty { get; set; }
+
+        /// <summary>
+        /// 托盘类型
+        /// </summary>
+        [SugarColumn(ColumnDataType = "nvarchar", Length = 50, IsNullable = false, ColumnDescription = "托盘类型")]
+        public string TpTypeCode { get; set; }
+
+        /// <summary>
+        /// 层数
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = false, ColumnDescription = "层数")]
+        public int LayerCountQty { get; set; }
+
+        /// <summary>
+        /// 垛型大类
+        /// </summary>
+        [SugarColumn(ColumnDataType = "int", IsNullable = true, ColumnDescription = "垛型大类")]
+        public int StampType { get; set; }
+    }
+}

+ 12 - 0
WCS.WorkEngineering/Extensions/TrussExtebsion.cs

@@ -0,0 +1,12 @@
+using WCS.Core;
+using WCS.Entity.Protocol.Truss;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    public class Truss : Device<ITruss520, ITruss521, ITruss523>
+    {
+        public Truss(Device device, World world) : base(device, world)
+        {
+        }
+    }
+}

+ 95 - 0
WCS.WorkEngineering/Extensions/WorldExtension.cs

@@ -0,0 +1,95 @@
+using ServiceCenter.Logs;
+using WCS.Core;
+using LogInfo = ServiceCenter.Logs.LogInfo;
+
+namespace WCS.WorkEngineering.Extensions
+{
+    public static class WorldExtension
+    {
+        /// <summary>
+        ///  记录日志
+        ///  默认:低级别 已知 不上抛
+        /// </summary>
+        /// <param name="source">世界</param>
+        /// <param name="msg">消息</param>
+        public static void Log(this World source, string msg)
+        {
+            source.Log(new LogInfo { Level = LogLevelEnum.Low, Type = ErrorTypeEnum.Kown, LogUpLoad = LogUpLoadEnum.NotUpLoad, Message = msg });
+        }
+
+        /// <summary>
+        ///  记录日志
+        ///  默认:已知
+        ///  上抛根据日志级别决定:低级别不上抛,中高级别上抛WMS
+        /// </summary>
+        /// <param name="source">世界</param>
+        /// <param name="msg">消息</param>
+        /// <param name="level">日志级别</param>
+        public static void Log(this World source, string msg, LogLevelEnum level)
+        {
+            LogUpLoadEnum logUpLoad = LogUpLoadEnum.NotUpLoad;
+            switch (level)
+            {
+                case LogLevelEnum.Low:
+                    logUpLoad = LogUpLoadEnum.NotUpLoad;
+                    break;
+
+                case LogLevelEnum.Mid:
+                    logUpLoad = LogUpLoadEnum.UpLoadWMS;
+                    break;
+
+                case LogLevelEnum.High:
+                    logUpLoad = LogUpLoadEnum.UpLoadWMS;
+                    break;
+
+                default:
+                    break;
+            }
+            source.Log(new LogInfo { Level = level, Type = ErrorTypeEnum.Kown, LogUpLoad = logUpLoad, Message = msg });
+        }
+
+        /// <summary>
+        ///  记录日志
+        ///  上抛根据日志级别决定:低级别不上抛,中高级别上抛WMS
+        /// </summary>
+        /// <param name="source">世界</param>
+        /// <param name="msg">消息</param>
+        /// <param name="level">日志级别</param>
+        /// <param name="errorType">日志类型</param>
+        public static void Log(this World source, string msg, LogLevelEnum level, ErrorTypeEnum errorType)
+        {
+            LogUpLoadEnum logUpLoad = LogUpLoadEnum.NotUpLoad;
+            switch (level)
+            {
+                case LogLevelEnum.Low:
+                    logUpLoad = LogUpLoadEnum.NotUpLoad;
+                    break;
+
+                case LogLevelEnum.Mid:
+                    logUpLoad = LogUpLoadEnum.UpLoadWMS;
+                    break;
+
+                case LogLevelEnum.High:
+                    logUpLoad = LogUpLoadEnum.UpLoadWMS;
+                    break;
+
+                default:
+                    break;
+            }
+            source.Log(new LogInfo { Level = level, Type = errorType, LogUpLoad = logUpLoad, Message = msg });
+        }
+
+        /// <summary>
+        ///  记录日志
+        /// </summary>
+        /// <param name="source">世界</param>
+        /// <param name="msg">消息</param>
+        /// <param name="level">日志级别</param>
+        /// <param name="errorType">日志类型</param>
+        /// <param name="logUpLoad">日志上抛类型</param>
+        public static void Log(this World source, string msg, LogLevelEnum level, ErrorTypeEnum errorType, LogUpLoadEnum logUpLoad)
+        {
+            source.Log(new LogInfo { Level = level, Type = errorType, LogUpLoad = logUpLoad, Message = msg });
+        }
+    }
+}

+ 9 - 0
WCS.WorkEngineering/LockHub.cs

@@ -0,0 +1,9 @@
+namespace WCS.WorkEngineering
+{
+    public static class LockHub
+    {
+        public static object ApplyEmptySpoolLock = new object();
+
+        public static object AgvCallbackLock = new object();
+    }
+}

+ 239 - 0
WCS.WorkEngineering/ProtocolProxy.cs

@@ -0,0 +1,239 @@
+using ServiceCenter.SqlSugars;
+using WCS.Core;
+using WCS.Entity.Protocol;
+using WCS.Entity.Protocol.BCR;
+using WCS.Entity.Protocol.DataStructure;
+using WCS.Entity.Protocol.Protocol.DataStructure;
+using WCS.Entity.Protocol.RGV;
+using WCS.Entity.Protocol.Robot;
+using WCS.Entity.Protocol.SRM;
+using WCS.Entity.Protocol.Station;
+using WCS.Entity.Protocol.Truss;
+using WCS.WorkEngineering.Systems;
+
+namespace WCS.WorkEngineering
+{
+    public class ProtocolProxy : ProtocolProxyBase
+    {
+        public static DeviceDataPack DataPack { get; set; } = new DeviceDataPack();
+
+        public ProtocolProxy(Device dev, ProtocolInfo info, Type protocolType, World world) : base(dev, info, protocolType, world)
+        {
+        }
+
+        protected override void DataChanged()
+        {
+            //if (Device.Code == "RGV1" && Info.DBInfo.No == 520)
+            //{
+            //    var a = Items;
+            //    //var dev=Device.Protocol(ProtocolType)
+            //    var b = a;
+            //}
+
+            //try
+            //{
+            //    var datas = DataCollectionSysyem.AllDatas;
+            //    if (Device.Code.All(char.IsNumber))
+            //    {
+            //        if (!datas.ContainsKey(Device.Code)) datas[Device.Code] = new StationData { Code = Device.Code };
+            //    }
+            //    else if (Device.Code.Contains("SRM"))
+            //    {
+            //        if (!datas.ContainsKey(Device.Code)) datas[Device.Code] = new SRMData { Code = Device.Code };
+            //    }
+            //    else if (Device.Code.Contains("Truss"))
+            //    {
+            //        if (!datas.ContainsKey(Device.Code)) datas[Device.Code] = new TrussData() { Code = Device.Code };
+            //    }
+            //    else if (Device.Code.Contains("Robot"))
+            //    {
+            //        if (!datas.ContainsKey(Device.Code)) datas[Device.Code] = new RobotData() { Code = Device.Code };
+            //    }
+            //    else if (Device.Code.Contains("RGV"))
+            //    {
+            //        if (!datas.ContainsKey(Device.Code)) datas[Device.Code] = new RGVData { Code = Device.Code };
+            //    }
+
+            //    if (!datas.TryGetValue(Device.Code, out var data)) return;
+            //    data.Frame = DateTime.Now;
+            //    var p = data.GetType().GetProperties().FirstOrDefault(v => v.PropertyType == ProtocolDataType);
+            //    //var ty90 = WCS_Station90;
+            //    //if (p.getty is ty90 or)
+            //    //{
+            //    //}
+            //    if (p == null) return;
+            //    p.SetValue(data, DictionaryToEntity(ProtocolDataType, Items, data));
+            //}
+            //catch (Exception ex)
+            //{
+            //    Console.WriteLine(ex.Message);
+            //}
+        }
+
+        public void DictionaryToEntity(Type type, Dictionary<string, PlcItem> plcItems, DeviceData data)
+        {
+            //var entity = Activator.CreateInstance(type);
+            ////采集量
+            //Parallel.ForEach(type.GetProperties(), ty =>
+            //{
+            //    if (plcItems.Any(x => x.Key == ty.Name))
+            //    {
+            //        var item = plcItems.First(x => ty.Name == x.Key);
+            //        ty.SetValue(entity, item.Value.Value);
+            //    }
+            //});
+
+            //Parallel.ForEach(type.GetProperties(), ty =>
+            //{
+            //    var items = data.GetType().GetProperties();
+            //    if (items.Any(x => x.Name == ty.Name))
+            //    {
+            //        var item = items.First(x => ty.Name == x.Name);
+            //        ty.SetValue(entity, item.GetValue(data));
+            //    }
+            //});
+
+            //foreach (var ty in type.GetProperties().Where(x => x.Name == nameof(TDengineBaseEntity.CreateSql)))
+            //{
+            //    var db = new SqlSugarHelper().PLC;
+            //    switch (type.Name)
+            //    {
+            //        case nameof(WCS_SRM520):
+            //            var item = entity as WCS_SRM520;
+            //            ty.SetValue(entity, GetString(db.Insertable(item).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_SRM521):
+            //            var item1 = entity as WCS_SRM521;
+            //            ty.SetValue(entity, GetString(db.Insertable(item1).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_SRM537):
+            //            var item2 = entity as WCS_SRM537;
+            //            ty.SetValue(entity, GetString(db.Insertable(item2).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_RGV520):
+            //            var item3 = entity as WCS_RGV520;
+            //            ty.SetValue(entity, GetString(db.Insertable(item3).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_RGV521):
+            //            var item4 = entity as WCS_RGV521;
+            //            ty.SetValue(entity, GetString(db.Insertable(item4).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_BCR80):
+            //            var item5 = entity as WCS_BCR80;
+            //            ty.SetValue(entity, GetString(db.Insertable(item5).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_BCR81):
+            //            var item6 = entity as WCS_BCR81;
+            //            ty.SetValue(entity, GetString(db.Insertable(item6).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_BCR83):
+            //            var item7 = entity as WCS_BCR83;
+            //            ty.SetValue(entity, GetString(db.Insertable(item7).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Station520):
+            //            var item8 = entity as WCS_Station520;
+            //            ty.SetValue(entity, GetString(db.Insertable(item8).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Station521):
+            //            var item9 = entity as WCS_Station521;
+            //            ty.SetValue(entity, GetString(db.Insertable(item9).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Station523):
+            //            var item10 = entity as WCS_Station523;
+            //            ty.SetValue(entity, GetString(db.Insertable(item10).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Station524):
+            //            var item11 = entity as WCS_Station524;
+            //            ty.SetValue(entity, GetString(db.Insertable(item11).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Station525):
+            //            var item12 = entity as WCS_Station525;
+            //            ty.SetValue(entity, GetString(db.Insertable(item12).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Station90):
+            //            var item13 = entity as WCS_Station90;
+            //            ty.SetValue(entity, GetString(db.Insertable(item13).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Station91):
+            //            var item14 = entity as WCS_Station91;
+            //            ty.SetValue(entity, GetString(db.Insertable(item14).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Truss520):
+            //            var item15 = entity as WCS_Truss520;
+            //            ty.SetValue(entity, GetString(db.Insertable(item15).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Truss521):
+            //            var item16 = entity as WCS_Truss521;
+            //            ty.SetValue(entity, GetString(db.Insertable(item16).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Truss523):
+            //            var item17 = entity as WCS_Truss523;
+            //            ty.SetValue(entity, GetString(db.Insertable(item17).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Truss530):
+            //            var item18 = entity as WCS_Truss530;
+            //            ty.SetValue(entity, GetString(db.Insertable(item18).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Truss531):
+            //            var item19 = entity as WCS_Truss531;
+            //            ty.SetValue(entity, GetString(db.Insertable(item19).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Robot520):
+            //            var item20 = entity as WCS_Robot520;
+            //            ty.SetValue(entity, GetString(db.Insertable(item20).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Robot521):
+            //            var item21 = entity as WCS_Robot521;
+            //            ty.SetValue(entity, GetString(db.Insertable(item21).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Robot522):
+            //            var item22 = entity as WCS_Robot522;
+            //            ty.SetValue(entity, GetString(db.Insertable(item22).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Robot530):
+            //            var item23 = entity as WCS_Robot530;
+            //            ty.SetValue(entity, GetString(db.Insertable(item23).ToSqlString()));
+            //            break;
+
+            //        case nameof(WCS_Robot531):
+            //            var item24 = entity as WCS_Robot531;
+            //            ty.SetValue(entity, GetString(db.Insertable(item24).ToSqlString()));
+            //            break;
+            //    }
+            //}
+            //return entity;
+        }
+
+        public string GetString(string value)
+        {
+            return value.Replace("INSERT INTO ", "")
+                .Replace(",N'", ",'")
+                .Replace("\0", "")
+                .Replace("wcs_", "")
+                .Replace("(N'", "('") + "\r";
+        }
+    }
+}

+ 65 - 0
WCS.WorkEngineering/Startup.cs

@@ -0,0 +1,65 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.OpenApi.Models;
+using System.Reflection;
+
+namespace ServiceCenter.WebApi
+{
+    public class Startup
+    {
+        public Startup(IConfiguration configuration)
+        {
+            Configuration = configuration;
+        }
+
+        public IConfiguration Configuration { get; }
+
+        public string MyCors = "Cor";
+
+        // This method gets called by the runtime. Use this method to add services to the container.
+        public void ConfigureServices(IServiceCollection services)
+        {
+            services.AddControllers();
+            //跨域配置
+            services.AddCors(v => v.AddPolicy(MyCors, y =>
+            {
+                //声明跨域策略:允许所有域、允许任何请求头和允许全部http方法
+                y.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
+            }));
+            services.AddSwaggerGen(c =>
+            {
+                //c.SwaggerDoc("v1", new OpenApiInfo { Title = "WCSAPI", Version = "v1" });
+                c.SwaggerDoc("v1", new OpenApiInfo
+                {
+                    Version = "v1",
+                    Title = "WCSAPI",
+                    Description = "API描述"
+                });
+                var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
+                c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
+            });
+        }
+
+        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+        {
+            app.UseDeveloperExceptionPage();
+            app.UseSwagger();
+            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1"));
+            //http://localhost:8080/swagger/index.html
+
+            app.UseHttpsRedirection();
+
+            app.UseRouting();
+            app.UseCors(MyCors);
+            app.UseAuthorization();
+
+            app.UseEndpoints(endpoints =>
+            {
+                endpoints.MapControllers();
+            });
+        }
+    }
+}

+ 470 - 0
WCS.WorkEngineering/Systems/DataCollectionSysyem.cs

@@ -0,0 +1,470 @@
+using Newtonsoft.Json;
+using PlcSiemens.Core.Extension;
+using ServiceCenter.Extensions;
+using ServiceCenter.Redis;
+using ServiceCenter.SqlSugars;
+using SqlSugar;
+using System.Collections.Concurrent;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Text;
+using WCS.Core;
+using WCS.Entity.Protocol.BCR;
+using WCS.Entity.Protocol.DataStructure;
+using WCS.Entity.Protocol.RGV;
+using WCS.Entity.Protocol.Robot;
+using WCS.Entity.Protocol.SRM;
+using WCS.Entity.Protocol.Station;
+using WCS.Entity.Protocol.Truss;
+using WCS.WorkEngineering.Extensions;
+using WCS.WorkEngineering.Worlds;
+
+namespace WCS.WorkEngineering.Systems
+{
+    /// <summary>
+    ///  数据采集系统
+    /// </summary>
+    [BelongTo(typeof(MainWorld))]
+    [Description("数据采集系统")]
+    public class DataCollectionSysyem : DeviceSystem<Device<IStation520>>
+    {
+        public static DeviceDataPack pack = new DeviceDataPack();
+
+        private static object locker = new object();
+
+        public DataCollectionSysyem()
+        {
+            var gs = Device.All.SelectMany(v => v.Protocols.Select(d => new { DB = $"{d.Value.DBInfo.No}:{d.Value.DBInfo.PLCInfo.IP}", d.Value.Position, TypeStr = d.Key, Dev = v }))
+                .GroupBy(v => v.DB);
+            foreach (var g in gs)
+            {
+                var min = g.OrderBy(v => v.Position).First();
+                var max = g.OrderByDescending(v => v.Position).First();
+                var t = Type.GetType(min.TypeStr);
+                min.Dev.Protocol(t, this.World);
+                max.Dev.Protocol(t, this.World);
+            }
+        }
+
+        /// <summary>
+        ///  所有设备数据
+        ///  Key 是不同设备所使用的类型 例如DeviceDataCollection<SRMData>
+        ///  value 不同设备的具体数据
+        /// </summary>
+        public static ConcurrentDictionary<string, DeviceData> AllDatas = new ConcurrentDictionary<string, DeviceData>();
+
+        protected override bool ParallelDo => true;
+
+        protected override bool SaveLogsToFile => true;
+
+        public override bool Select(Device dev)
+        {
+            return dev.Code == "1";
+        }
+
+        public override void Do(Device<IStation520> objDev)
+        {
+            var sql = new StringBuilder();
+            try
+            {
+                var sw = new Stopwatch();
+                sw.Start();
+                var pack = new DeviceDataPack();
+                var frame = DateTime.Now;
+                pack.Frame = World.Frame;
+                sql.Append("INSERT INTO ");
+                var ps = pack.GetType().GetProperties().OrderBy(x => x.Name);
+                var db = new SqlSugarHelper().PLC;
+                Parallel.ForEach(ps, p =>
+                {
+                    if (!p.PropertyType.IsArray) return;
+
+                    var t = p.PropertyType.GetElementType();
+                    if (t.IsGenericType)
+                    {
+                        var entType = t.GetGenericArguments()[0];
+                        var protType = GetProtocolType(entType);
+                        if (protType == null) return;
+
+                        var devices = Device.All.Where(v => v.HasProtocol(protType));
+                        List<object> arr = new List<object>();
+                        Parallel.ForEach(devices, x =>
+                        {
+                            try
+                            {
+                                var protObj = x.Protocol(protType, World) as ProtocolProxyBase;
+                                if (protObj.Frame < DateTime.Now.AddYears(-24))
+                                {
+                                    protObj.Frame = frame;
+                                }
+                                if (protObj.Db.failed)
+                                {
+                                    return;
+                                }
+
+                                var obj = Activator.CreateInstance(t);
+                                t.GetProperty("Code").SetValue(obj, x.Code);
+                                var value = WCS.Core.Extentions.Copy(protObj, entType, frame);
+                                t.GetProperty("Data").SetValue(obj, value);
+                                t.GetProperty("Frame").SetValue(obj, protObj.Frame);
+                                entType.GetProperty("Code").SetValue(value, x.Code);
+                                arr.Add(obj);
+                            }
+                            catch
+                            {
+                            }
+                        });
+
+                        var m = typeof(Enumerable).GetMethod("OfType",
+                            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+                        m = m.MakeGenericMethod(t);
+                        var arr2 = m.Invoke(null, new object[] { arr });
+
+                        m = typeof(Enumerable).GetMethod("ToArray",
+                            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+                        m = m.MakeGenericMethod(t);
+                        var arr3 = m.Invoke(null, new object[] { arr2 });
+                        p.SetValue(pack, arr3);
+                    }
+                });
+                var sw3 = new Stopwatch();
+                sw3.Start();
+
+                //开始存储设备信息
+                RedisHub.Monitor.RPush("Packs", pack);
+                if (RedisHub.Monitor.LLen("Packs") > 50000)
+                {
+                    RedisHub.Monitor.LTrim("Packs", 5000, -1);
+                }
+
+                #region 存储设备报警信息
+
+                List<EquipmentAlarm> equipmentAlarms = new List<EquipmentAlarm>();
+                equipmentAlarms.AddRange(pack.Robot522.Where(x => x.Data.Alarm != 0).Select(x => new EquipmentAlarm()
+                {
+                    Code = x.Code,
+                    Msg = x.Data.Alarm.ToString(),
+                    Time = x.Frame
+                }));
+
+                equipmentAlarms.AddRange(pack.SRM537.Where(x => x.Data.Alarm != 0).Select(x => new EquipmentAlarm()
+                {
+                    Code = x.Code,
+                    Msg = x.Data.Alarm.ToString(),
+                    Time = x.Frame
+                }));
+
+                equipmentAlarms.AddRange(pack.Station523.Where(x => x.Data.Alarm != 0).Select(x => new EquipmentAlarm()
+                {
+                    Code = x.Code,
+                    Msg = x.Data.Alarm.ToString(),
+                    Time = x.Frame
+                }));
+
+                equipmentAlarms.AddRange(pack.Truss523.Where(x => x.Data.Alarm != 0).Select(x => new EquipmentAlarm()
+                {
+                    Code = x.Code,
+                    Msg = x.Data.Alarm.ToString(),
+                    Time = x.Frame
+                }));
+
+                RedisHub.Default.Set(nameof(EquipmentAlarm), JsonConvert.SerializeObject(equipmentAlarms));
+
+                #endregion 存储设备报警信息
+
+                #region 存储设备状态信息
+
+                List<EquipmentStatus> equipmentStatus = new List<EquipmentStatus>();
+
+                equipmentStatus.AddRange(pack.RGV521.Where(x => x.Data.WorkMode != 0).Select(x => new EquipmentStatus()
+                {
+                    Code = x.Code,
+                    con = x.Data.WorkMode.GetDescription(),
+                    Status = x.Data.WorkMode.ToInt(),
+                    Time = x.Frame
+                }));
+
+                equipmentStatus.AddRange(pack.Robot521.Where(x => x.Data.RobotMode != 0).Select(x => new EquipmentStatus()
+                {
+                    Code = x.Code,
+                    con = x.Data.RobotMode.GetDescription(),
+                    Status = x.Data.RobotMode.ToInt(),
+                    Time = x.Frame
+                }));
+
+                equipmentStatus.AddRange(pack.SRM521.Where(x => x.Data.AutoStatus != 0).Select(x => new EquipmentStatus()
+                {
+                    Code = x.Code,
+                    con = x.Data.AutoStatus.GetDescription(),
+                    Status = x.Data.AutoStatus.ToInt(),
+                    Time = x.Frame
+                }));
+
+                equipmentStatus.AddRange(pack.Station521.Where(x => x.Data.Mode != 0).Select(x => new EquipmentStatus()
+                {
+                    Code = x.Code,
+                    con = x.Data.Mode.GetDescription(),
+                    Status = x.Data.Mode.ToInt(),
+                    Time = x.Frame
+                }));
+
+                equipmentStatus.AddRange(pack.Truss521.Where(x => x.Data.Status != 0).Select(x => new EquipmentStatus()
+                {
+                    Code = x.Code,
+                    con = x.Data.Status.GetDescription(),
+                    Status = x.Data.Status.ToInt(),
+                    Time = x.Frame
+                }));
+
+                RedisHub.Default.Set(nameof(EquipmentStatus), JsonConvert.SerializeObject(equipmentStatus));
+
+                #endregion 存储设备状态信息
+
+                sw3.Stop();
+                World.Log($"redis存储耗时:{sw3.ElapsedMilliseconds}");
+
+                var sw4 = new Stopwatch();
+                sw4.Start();
+
+                Parallel.ForEach(pack.GetType().GetProperties().OrderBy(x => x.Name), ps =>
+                {
+                    try
+                    {
+                        if (ps.PropertyType == typeof(ProtocolData<WCS_BCR80>[]))
+                        {
+                            if (pack.BCR80.Any())
+                                db.Insertable(pack.BCR80.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_BCR81>[]))
+                        {
+                            if (pack.BCR81.Any())
+                                db.Insertable(pack.BCR81.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_BCR83>[]))
+                        {
+                            if (pack.BCR83.Any())
+                                db.Insertable(pack.BCR83.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_RGV520>[]))
+                        {
+                            if (pack.RGV520.Any())
+                                db.Insertable(pack.RGV520.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_RGV521>[]))
+                        {
+                            if (pack.RGV521.Any())
+                                db.Insertable(pack.RGV521.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot520>[]))
+                        {
+                            if (pack.Robot520.Any())
+                                db.Insertable(pack.Robot520.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot521>[]))
+                        {
+                            if (pack.Robot521.Any())
+                                db.Insertable(pack.Robot521.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot522>[]))
+                        {
+                            if (pack.Robot522.Any())
+                                db.Insertable(pack.Robot522.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot530>[]))
+                        {
+                            if (pack.Robot530.Any())
+                                db.Insertable(pack.Robot530.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot531>[]))
+                        {
+                            if (pack.Robot531.Any())
+                                db.Insertable(pack.Robot531.Select(x => x.Data).ToList()).ToSqlString();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM520>[]))
+                        {
+                            if (pack.SRM520.Any())
+                                db.Insertable(pack.SRM520.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM521>[]))
+                        {
+                            if (pack.SRM521.Any())
+                                db.Insertable(pack.SRM521.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM537>[]))
+                        {
+                            if (pack.SRM537.Any())
+                                db.Insertable(pack.SRM537.Select(x => x.Data).Where(x => x.Alarm == 0).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station520>[]))
+                        {
+                            if (pack.Station520.Any())
+                                db.Insertable(pack.Station520.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station521>[]))
+                        {
+                            if (pack.Station521.Any())
+                                db.Insertable(pack.Station521.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station523>[]))
+                        {
+                            if (pack.Station523.Any())
+                                db.Insertable(pack.Station523.Select(x => x.Data).ToList()).UseParameter()
+                                    .ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station524>[]))
+                        {
+                            if (pack.Station524.Any())
+                                db.Insertable(pack.Station524.Select(x => x.Data).ToList()).UseParameter()
+                                    .ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station525>[]))
+                        {
+                            if (pack.Station525.Any())
+                                db.Insertable(pack.Station525.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station90>[]))
+                        {
+                            if (pack.Station90.Any())
+                                db.Insertable(pack.Station90.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station91>[]))
+                        {
+                            if (pack.Station91.Any())
+                                db.Insertable(pack.Station91.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss520>[]))
+                        {
+                            if (pack.Truss520.Any())
+                                db.Insertable(pack.Truss520.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss521>[]))
+                        {
+                            if (pack.Truss521.Any())
+                                db.Insertable(pack.Truss521.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss523>[]))
+                        {
+                            if (pack.Truss523.Any())
+                                db.Insertable(pack.Truss523.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss530>[]))
+                        {
+                            if (pack.Truss530.Any())
+                                db.Insertable(pack.Truss530.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                        else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss531>[]))
+                        {
+                            if (pack.Truss531.Any())
+                                db.Insertable(pack.Truss531.Select(x => x.Data).ToList()).ExecuteCommand();
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        World.Log($"{e.Message}:{e.StackTrace}");
+                    }
+                });
+
+                sw4.Stop();
+                World.Log($"执行SQL耗时:{sw4.ElapsedMilliseconds}");
+
+                sw.Stop();
+
+                World.Log($"数据采集耗时:{sw.ElapsedMilliseconds}");
+            }
+            catch (Exception e)
+            {
+                World.Log($"错误内容:{e.Message}");
+            }
+        }
+
+        public void Set(StringBuilder sql, string cSql)
+        {
+            lock (locker)
+            {
+                sql.Append(cSql);
+            }
+        }
+
+        private Type GetProtocolType(Type source)
+        {
+            var t = source.GetInterfaces().FirstOrDefault(v => v.GetInterfaces().Any(d => d.Name == "IProtocol"));
+            var t1 = source.GetInterfaces().FirstOrDefault(v => v.GetInterfaces().Any(d => d.Name == "IProtocol"));
+            return t;
+        }
+
+        private object AppendLock = new object();
+
+        public StringBuilder Append(StringBuilder sql, string value)
+        {
+            lock (AppendLock)
+            {
+                return sql.Append(value);
+            }
+        }
+
+        public string GetString(string value)
+        {
+            return value.Replace("INSERT INTO ", "")
+                .Replace(",N'", ",'")
+                .Replace("\0", "")
+                .Replace("wcs_", "")
+                .Replace("(N'", "('") + "\r";
+        }
+    }
+
+    /// <summary>
+    ///  设备报警
+    /// </summary>
+    public class EquipmentAlarm
+    {
+        /// <summary>
+        ///  设备号
+        /// </summary>
+        public string Code { get; set; }
+
+        /// <summary>
+        ///  内容
+        /// </summary>
+        public string Msg { get; set; }
+
+        /// <summary>
+        ///  时间
+        /// </summary>
+        public DateTime Time { get; set; }
+    }
+
+    /// <summary>
+    ///  设备状态信息
+    /// </summary>
+    public class EquipmentStatus
+    {
+        /// <summary>
+        ///  设备号
+        /// </summary>
+        public string Code { get; set; }
+
+        /// <summary>
+        ///  内容
+        /// </summary>
+        public string con { get; set; }
+
+        /// <summary>
+        ///  内容
+        /// </summary>
+        public int Status { get; set; }
+
+        /// <summary>
+        ///  时间
+        /// </summary>
+        public DateTime Time { get; set; }
+    }
+
+    /// <summary>
+    ///
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class Quest<T>
+    {
+        public T Data { get; set; }
+    }
+}

+ 44 - 0
WCS.WorkEngineering/WCS.WorkEngineering.csproj

@@ -0,0 +1,44 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net7.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <GenerateDocumentationFile>true</GenerateDocumentationFile>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <NoWarn>1701;1702;8602;8616;8618;8625;8600;8603;8714;1591</NoWarn>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+    <NoWarn>1701;1702;8602;8616;8618;8625;8600;8603;8714;1591</NoWarn>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Remove="Systems\puiaxxwa.5zv~" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MessagePack" Version="2.5.108" />
+    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
+    <PackageReference Include="WCS.Entity.Protocol" Version="1.0.6.8" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\ServiceCenter\ServiceCenter.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Reference Include="wms.dto">
+      <HintPath>..\DLC\wms.dto.dll</HintPath>
+    </Reference>
+    <Reference Include="wms.service">
+      <HintPath>..\DLC\wms.service.dll</HintPath>
+    </Reference>
+    <Reference Include="wms.sqlsugar">
+      <HintPath>..\DLC\wms.sqlsugar.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+
+</Project>

+ 1236 - 0
WCS.WorkEngineering/WorkStart.cs

@@ -0,0 +1,1236 @@
+using ServiceCenter;
+using ServiceCenter.SqlSugars;
+using WCS.Core;
+using WCS.Entity.Protocol.BCR;
+using WCS.Entity.Protocol.HUB;
+using WCS.Entity.Protocol.RGV;
+using WCS.Entity.Protocol.Robot;
+using WCS.Entity.Protocol.SRM;
+using WCS.Entity.Protocol.Station;
+using WCS.Entity.Protocol.Truss;
+using WCS.WorkEngineering.Extensions;
+using DeviceFlags = WCS.WorkEngineering.Extensions.DeviceFlags;
+
+namespace WCS.WorkEngineering
+{
+    /// <summary>
+    /// 业务工程配置信息
+    /// </summary>
+    public static class WorkStart
+    {
+        /// <summary>
+        ///  初始化 设备信息
+        /// </summary>
+
+        public static void InitializeDeviceInfo()
+        {
+            #region 设置设备IP集合
+
+            ServiceHub.DeviceIPList = new List<string>() { };
+
+            #endregion 设置设备IP集合
+
+            #region 初始化RGV相关信息
+
+            List<RgvSegmentInfo> RgvInfo = new List<RgvSegmentInfo>
+            {
+                new RgvSegmentInfo( 1, "10.30.37.113"), //库一北
+                new RgvSegmentInfo( 2, "10.30.37.118"), //库一南
+                new RgvSegmentInfo( 3, "10.30.37.123"), //库二北
+                new RgvSegmentInfo( 4, "10.30.37.128"), //库二南
+                new RgvSegmentInfo( 5, "10.30.37.133"), //库三北
+                new RgvSegmentInfo( 6, "10.30.37.138"), //库三南
+            };
+
+            foreach (var item in RgvInfo)
+            {
+                var conv = new Device($"RGV{item.Code}");
+                conv.AddFlag(DeviceFlags.RGV);
+                conv.AddProtocol<IRGV520>(0, 520, item.Ip);
+                conv.AddProtocol<IRGV521>(0, 521, item.Ip);
+                //conv.AddProtocol<IStation523>(, 523, item.IP);
+            }
+
+            #endregion 初始化RGV相关信息
+
+            #region 初始化输送机相关信息
+
+            #region 基本信息
+
+            #region 托盘线输送线
+
+            var tuples = new List<Tuple<string, List<Tuple<int, int>>>>
+            {
+                //分拣库一
+                new("10.30.37.89",new List<Tuple<int, int>>()
+                {
+                    new(1601, 1620),
+                    new(2501, 2540),
+                    new(2701, 2740),
+                    new(1661, 1690),
+                }),
+                //分拣库二
+                new("10.30.37.97",new List<Tuple<int, int>>()
+                {
+                    new(1621, 1640),
+                    new(2901, 2940),
+                    new(3101, 3140),
+                    new(1691, 1720),
+                }),
+                //分拣库三
+                new("10.30.37.105",new List<Tuple<int, int>>()
+                {
+                    new(1641, 1660),
+                    new(3301, 3340),
+                    new(3501, 3540),
+                    new(1721, 1750)
+                })
+            };
+
+            foreach (var item in tuples)
+            {
+                var db520 = 0;
+                var db521 = 0;
+                var db523 = 0;
+                foreach (var item1 in item.Item2)
+                {
+                    for (var i = item1.Item1; i <= item1.Item2; i++)
+                    {
+                        var conv = new Device(i.ToString());
+                        conv.AddFlag(DeviceFlags.输送机);
+
+                        conv.AddProtocol<IStation520>(db520, 520, item.Item1);
+                        conv.AddProtocol<IStation521>(db521, 521, item.Item1);
+                        conv.AddProtocol<IStation523>(db523, 523, item.Item1);
+
+                        db520 += 14;
+                        db521 += 16;
+                        db523 += 12;
+                    }
+                }
+            }
+
+            #endregion 托盘线输送线
+
+            #region 满轮输送线
+
+            #region DB523,所有线体都会有DB523
+
+            var mLtuples523 = new List<Tuple<string, List<Tuple<int, int>>>>()
+            {
+                new("10.30.37.166",new List<Tuple<int, int>>() //北侧满轮主线
+                {
+                    new(1, 100),
+                    new(9001, 9010),//暂用,无意义
+                    new(401, 599),
+                    new(801,999),
+                    new(1201,1399),
+                }),
+                new("10.30.37.198",new List<Tuple<int, int>>() //南侧满轮主线
+                {
+                    new(101, 210),
+                    new(601, 799),
+                    new(1001, 1199),
+                    new(1401,1599),
+                    new(341,379),
+                })
+            };
+
+            foreach (var item in mLtuples523)
+            {
+                var db523 = 0;
+                var db524 = 0;
+                foreach (var item1 in item.Item2)
+                {
+                    for (var i = item1.Item1; i <= item1.Item2; i++)
+                    {
+                        var conv = new Device(i.ToString());
+                        conv.AddFlag(DeviceFlags.输送机);
+                        conv.AddProtocol<IStation523>(db523, 523, item.Item1);
+                        conv.AddProtocol<IStation524>(db524, 524, item.Item1);
+
+                        db523 += 12;
+                        db524 += 16;
+                    }
+                }
+            }
+
+            #endregion DB523,所有线体都会有DB523
+
+            #region 520、521 交互线体会有520、521
+
+            //Item2表示线体号集合,Item1表示IP
+            var mLTuples520 = new List<Tuple<string, List<int>>>()
+            {
+                //北侧
+                new("10.30.37.166",new List<int>() {1,22,41,61,418,426,435,444,455,466,480,494,508,522,536,550,564,578,591,818,826,835,844,855,866,880,894,908,922,936,950,964,978,991,1218,1226,1235,1244,1255,1266,1280,1294,1308,1322,1336,1350,1364,1378,1391}),
+                //南侧
+                new("10.30.37.198",new List<int>(){101,122,141,161,618,626,635,644,655,666,680,694,708,722,736,750,764,778,791,1018,1026,1035,1044,1055,1066,1080,1094,1108,1122,1136,1150,1164,1178,1191,1418,1426,1435,1444,1455,1466,1480,1494,1508,1522,1536,1550,1564,1578,1591})
+            };
+
+            foreach (var item in mLTuples520)
+            {
+                var db520 = 0;
+                var db521 = 0;
+
+                foreach (var device in item.Item2.Select(item1 => Device.All.FirstOrDefault(v => v.Code == item1.ToString())))
+                {
+                    if (device != null)
+                    {
+                        device.AddProtocol<IStation520>(db520, 520, item.Item1);
+                        device.AddProtocol<IStation521>(db521, 521, item.Item1);
+                    }
+
+                    db520 += 14;
+                    db521 += 16;
+                }
+            }
+
+            #endregion 520、521 交互线体会有520、521
+
+            #region 满轮扫码器
+
+            //Item2表示线体号集合,Item1表示IP
+            var mLTuples83 = new List<Tuple<string, List<int>>>()
+            {
+                new("10.30.37.166",new List<int>(){3,14,18,22,38,323,41,58,61}) ,
+                new("10.30.37.198",new List<int>(){101,114,118,122,138,363,141,158,161})
+            };
+
+            foreach (var item in mLTuples83)
+            {
+                var db83 = 0;
+
+                foreach (var device in item.Item2.Select(item1 => Device.All.FirstOrDefault(v => v.Code == item1.ToString())))
+                {
+                    device?.AddProtocol<IBCR83>(db83, 83, item.Item1);
+
+                    db83 += 604;
+                }
+            }
+
+            #endregion 满轮扫码器
+
+            #region 满轮线告诉分拣预分配
+
+            //Item2表示线体号集合,Item1表示IP
+            var mLTuples525 = new List<Tuple<string, List<int>>>()
+            {
+                new("10.30.37.166",new List<int>(){18,38,58}),
+                new("10.30.37.198",new List<int>(){118,138,158}),
+            };
+
+            foreach (var item in mLTuples525)
+            {
+                var db525 = 0;
+
+                foreach (var device in item.Item2.Select(item1 => Device.All.FirstOrDefault(v => v.Code == item1.ToString())))
+                {
+                    device?.AddProtocol<IStation525>(db525, 525, item.Item1);
+                    db525 += 3266;
+                }
+            }
+
+            #endregion 满轮线告诉分拣预分配
+
+            #region 外检信息
+
+            //Item2表示线体号集合,Item1表示IP
+            var mLTuples91 = new List<Tuple<string, List<int>>>()
+            {
+                new("10.30.37.166",new List<int>(){418,818,1218}),
+                new("10.30.37.198",new List<int>(){618,1018,1418})
+            };
+
+            foreach (var item in mLTuples91)
+            {
+                var db91 = 0;
+
+                foreach (var device in item.Item2.Select(item2 => Device.All.FirstOrDefault(v => v.Code == item2.ToString())))
+                {
+                    device?.AddProtocol<IStation91>(db91, 91, item.Item1);
+                    db91 += 14;
+                }
+            }
+
+            #endregion 外检信息
+
+            #endregion 满轮输送线
+
+            #endregion 基本信息
+
+            #region 托盘线扫码器
+
+            var bcrInfo = new List<BcrInfo>
+            {
+                new(new [] { "2532", "2732" }, "10.30.37.89"),
+                new(new [] { "2932", "3132" }, "10.30.37.97"),
+                //new(new [] { "2532", "2732" }, "10.30.37.105"),
+                new(new [] {"RGV1"},"10.30.37.113"),
+                new(new [] {"RGV2"},"10.30.37.118"),
+                new(new [] {"RGV3"},"10.30.37.123"),
+                new(new [] {"RGV4"},"10.30.37.128"),
+                new(new [] {"RGV5"},"10.30.37.133"),
+                new(new [] {"RGV6"},"10.30.37.138")
+            };
+
+            foreach (var item in bcrInfo)
+            {
+                for (var i = 0; i < item.DeviceNo.Length; i++)
+                {
+                    var device = Device.All.FirstOrDefault(v => v.Code == item.DeviceNo[i]);
+                    device.AddFlag(DeviceFlags.扫码);
+                    var pos = i * 20;
+                    device.AddProtocol<IBCR81>(pos, 81, item.Ip);
+                }
+            }
+
+            #endregion 托盘线扫码器
+
+            #region 外检信息
+
+            List<ShapeInfo> shapeInfo = new List<ShapeInfo>
+            {
+                new ShapeInfo(new int[] { 2732,2532 }, "10.30.37.89"),
+                new ShapeInfo(new int[] { 2932, 3132 }, "10.30.37.97"),
+                //new ShapeInfo(new int[] { 2732,2532 }, "10.30.37.105")
+            };
+
+            foreach (var item in shapeInfo)
+            {
+                for (int i = 0; i < item.DeviceNo.Length; i++)
+                {
+                    var conv = Device.All.FirstOrDefault(x => x.Code == item.DeviceNo[i].ToString());
+                    conv.AddFlag(DeviceFlags.外检);
+                    int pos = i * 14;
+                    conv.AddProtocol<IStation91>(pos, 91, item.Ip);
+                }
+            }
+
+            #endregion 外检信息
+
+            #endregion 初始化输送机相关信息
+
+            #region 初始化桁架相关信息
+
+            List<TrussSegmentInfo> TrussInfo = new List<TrussSegmentInfo>
+            {
+                new TrussSegmentInfo( 1, "10.30.37.211"),
+                new TrussSegmentInfo( 2, "10.30.37.217"),
+                new TrussSegmentInfo( 3, "10.30.37.223")
+            };
+
+            foreach (var item in TrussInfo)
+            {
+                var conv = new Device($"Truss{item.Code}");
+                conv.AddFlag(DeviceFlags.桁架);
+                conv.AddProtocol<ITruss520>(0, 520, item.Ip);
+                conv.AddProtocol<ITruss521>(0, 521, item.Ip);
+                conv.AddProtocol<ITruss523>(0, 523, item.Ip);
+            }
+
+            var tuples1 = new List<Tuple<string, List<int>>>
+            {
+                //桁架
+                new("10.30.37.211",new  List<int>(){1685,1686,1687,1688,1689,1690,1675,1674,1673,1672,1671,1670,1677,1678,1679,1680,1665,1664,1663,1662}), //分拣库一
+                new("10.30.37.217",new  List<int>(){1715,1716,1717,1718,1719,1720,1705,1704,1703,1702,1701,1700,1707,1708,1709,1710,1695,1694,1693,1692}), //分拣库二
+                new("10.30.37.223",new  List<int>(){1745,1746,1747,1748,1749,1750,1735,1734,1733,1732,1731,1730,1737,1738,1739,1740,1725,1724,1723,1722}), //分拣库三
+            };
+
+            foreach (var item in tuples1)
+            {
+                var db530 = 0;
+                var db531 = 0;
+                foreach (var conv in item.Item2.Select(item1 => Device.All.FirstOrDefault(x => x.Code == item1.ToString())))
+                {
+                    conv!.AddProtocol<ITruss530>(db530, 530, item.Item1);
+                    conv!.AddProtocol<ITruss531>(db531, 531, item.Item1);
+
+                    db530 += 18;
+                    db531 += 250;
+                    if (conv.Code == "1662")
+                    {
+                        var a = 0;
+                    }
+                }
+            }
+
+            var tuples21 = new List<Tuple<string, List<int>>>
+            {
+                //机械臂
+                new("10.30.37.230",new  List<int>(){ 1666, 1661}), //库一北
+                new("10.30.37.232",new  List<int>(){ 1681, 1676}), //库一南
+                new("10.30.37.234",new  List<int>(){ 1696, 1691}), //库二北
+                new("10.30.37.236",new  List<int>(){ 1711, 1706}), //库二南
+                new("10.30.37.238",new  List<int>(){ 1726, 1721}), //库三北
+                new("10.30.37.240",new  List<int>(){ 1736, 1741}), //库三南
+            };
+
+            foreach (var item in tuples21)
+            {
+                var db530 = 0;
+                var db531 = 0;
+                foreach (var conv in item.Item2.Select(item1 => Device.All.FirstOrDefault(x => x.Code == item1.ToString())))
+                {
+                    conv!.AddProtocol<IRobot530>(db530, 530, item.Item1);
+                    conv!.AddProtocol<IRobot531>(db531, 531, item.Item1);
+                    db530 += 8;
+                    db531 += 130;
+                }
+            }
+
+            #endregion 初始化桁架相关信息
+
+            #region 初始化机械臂相关信息
+
+            List<TrussSegmentInfo> TrussInfo1 = new List<TrussSegmentInfo>
+            {
+                new( 1, "10.30.37.230"),
+                new( 2, "10.30.37.232"),
+                new( 3, "10.30.37.234"),
+                new( 4, "10.30.37.236"),
+                new( 5, "10.30.37.238"),
+                new( 6, "10.30.37.240"),
+            };
+
+            foreach (var item in TrussInfo1)
+            {
+                var conv = new Device($"Robot{item.Code}");
+                conv.AddFlag(DeviceFlags.Robot);
+                conv.AddProtocol<IRobot520>(0, 520, item.Ip);
+                conv.AddProtocol<IRobot521>(0, 521, item.Ip);
+                conv.AddProtocol<IRobot522>(0, 522, item.Ip);
+            }
+
+            #endregion 初始化机械臂相关信息
+
+            #region 初始化堆垛机相关信息
+
+            int ip = 41;
+
+            for (int i = 0; i <= 5; i++)
+            {
+                var srm = new Device($"SRM{i + 1}");
+                srm.AddFlag(DeviceFlags.堆垛机);
+                ip = i == 0 ? ip : ip + 8;
+                //三台堆垛机IP主机位分别是 41、49、57、65、73、81
+                srm.AddProtocol<ISRM520>(0, 520, $"10.30.37.{ip}");
+                srm.AddProtocol<ISRM521>(0, 521, $"10.30.37.{ip}");
+                srm.AddProtocol<ISRM537>(0, 537, $"10.30.37.{ip}");
+
+                //增加巷道
+                var tunnel = new Device($"TY{i + 1}");
+                tunnel.AddFlag(DeviceFlags.巷道);
+            }
+
+            #endregion 初始化堆垛机相关信息
+
+            #region 配置路径信息
+
+            List<RouteInfo> routeInfos = new List<RouteInfo>();
+
+            routeInfos.AddRange(new List<RouteInfo>() {
+
+                #region 出库
+
+                #region 堆垛机--巷道
+
+                new RouteInfo("SRM1", new string[] { "TY1" }),
+                new RouteInfo("SRM2", new string[] { "TY2" }),
+                new RouteInfo("SRM3", new string[] { "TY3" }),
+                new RouteInfo("SRM4", new string[] { "TY4" }),
+                //new RouteInfo("SRM5", new string[] { "TY5" }),
+                //new RouteInfo("SRM6", new string[] { "TY6" }),
+
+                #endregion 堆垛机--巷道
+
+                #region 巷道--一楼堆垛机放货口
+
+                new RouteInfo("TY1", new string[] { "2532","2534","2533" }), //人工取货口
+                new RouteInfo("TY2", new string[] { "2732","2734","2733" }),
+                new RouteInfo("TY3", new string[] { "2932","2934","2933" }), //人工取货口
+                new RouteInfo("TY4", new string[] { "3132","3134","3133" }),
+                //new RouteInfo("TY5", new string[] { "3332","3334","3333" }), //人工取货口
+                //new RouteInfo("TY6", new string[]{ "3532","3534","3533" }),
+
+                #endregion 巷道--一楼堆垛机放货口
+
+                #region 巷道--二楼堆垛机放货口
+
+                new RouteInfo("TY1", new string[] { "1601","1605" }),
+                new RouteInfo("TY2", new string[] { "1611","1615" }),
+                new RouteInfo("TY3", new string[] { "1621","1625" }),
+                new RouteInfo("TY4", new string[] { "1631","1635" }),
+                //new RouteInfo("TY5", new string[] { "1641","1645" }),
+                //new RouteInfo("TY6", new string[] { "1651","1655" }),
+
+                #endregion 巷道--二楼堆垛机放货口
+
+                #region 二楼堆垛机放货口--二楼拆盘机
+
+                //库一
+                new RouteInfo("1601", new string[] { "1602" }),//非09拆盘机
+                new RouteInfo("1611", new string[] { "1612"}),
+                new RouteInfo("1605", new string[] { "1606" }),//09拆盘机
+                new RouteInfo("1615", new string[] { "1616" }),
+                ////库二
+                new RouteInfo("1621", new string[] { "1622" }),//非09拆盘机
+                new RouteInfo("1631", new string[] { "1632"}),
+                new RouteInfo("1625", new string[] { "1626" }),//09拆盘机
+                new RouteInfo("1635", new string[] { "1636" }),
+                //////库三
+                //new RouteInfo("1641", new string[] { "1642" }),//非09拆盘机
+                //new RouteInfo("1651", new string[] { "1652"}),
+                //new RouteInfo("1645", new string[] { "1646" }),//09拆盘机
+                //new RouteInfo("1655", new string[] { "1656" }),
+
+                #endregion 二楼堆垛机放货口--二楼拆盘机
+
+                #region 二楼拆盘机--RGV
+
+                //库一
+                new RouteInfo("1602", new string[] { "RGV1" }),//非09拆盘机
+                new RouteInfo("1612", new string[] { "RGV2"}),
+                new RouteInfo("1606", new string[] { "RGV1" }),//09拆盘机
+                new RouteInfo("1616", new string[] { "RGV2" }),
+                ////库二
+                new RouteInfo("1622", new string[] { "RGV3" }),//非09拆盘机
+                new RouteInfo("1632", new string[] { "RGV4"}),
+                new RouteInfo("1626", new string[] { "RGV3" }),//09拆盘机
+                new RouteInfo("1636", new string[] { "RGV4" }),
+                //////库三
+                //new RouteInfo("1642", new string[] { "RGV5" }),//非09拆盘机
+                //new RouteInfo("1652", new string[] { "RGV6"}),
+                //new RouteInfo("1646", new string[] { "RGV5" }),//09拆盘机
+                //new RouteInfo("1656", new string[] { "RGV6" }),
+
+                #endregion 二楼拆盘机--RGV
+
+                #region RGV--码垛区域
+
+                //库一
+                new RouteInfo("RGV1", new string[] { "1661","1666" }),//北区机械臂码垛区
+                new RouteInfo("RGV1", new string[] { "1662","1663","1664","1665", "1670","1671","1672","1673","1674","1675"}),//桁架北区码垛站台
+                new RouteInfo("RGV2", new string[] { "1676","1681" }),
+                new RouteInfo("RGV2", new string[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                ////库二
+                new RouteInfo("RGV3", new string[] { "1691","1696" }),//北区机械臂码垛区
+                new RouteInfo("RGV3", new string[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"}),//桁架北区码垛站台
+                new RouteInfo("RGV4", new string[] { "1706","1711" }),
+                new RouteInfo("RGV4", new string[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                //////库三
+                //new RouteInfo("RGV5", new string[] { "1721","1726" }),//北区机械臂码垛区
+                //new RouteInfo("RGV5", new string[] { "1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"}),//桁架北区码垛站台
+                //new RouteInfo("RGV6", new string[] { "1736","1741" }),
+                //new RouteInfo("RGV6", new string[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+
+                #endregion RGV--码垛区域
+
+                #region Robot--码垛工位
+
+                //库一
+                new RouteInfo("Robot1", new string[] { "1666" }),
+                new RouteInfo("Robot1", new string[] { "1661" }),
+                new RouteInfo("Robot2", new string[] { "1676" }),
+                new RouteInfo("Robot2", new string[] { "1681" }),
+                ////库二
+                new RouteInfo("Robot3", new string[] { "1691" }),
+                new RouteInfo("Robot3", new string[] { "1696" }),
+                new RouteInfo("Robot4", new string[] { "1706" }),
+                new RouteInfo("Robot4", new string[] { "1711" }),
+                ////库三
+                //new RouteInfo("Robot5", new string[] { "1721" }),
+                //new RouteInfo("Robot5", new string[] { "1726" }),
+                //new RouteInfo("Robot6", new string[] { "1736" }),
+                //new RouteInfo("Robot6", new string[] { "1741" }),
+
+                #endregion Robot--码垛工位
+
+                #endregion 出库
+
+                #region 入库
+
+                #region 一楼人工入库口-巷道
+
+                new RouteInfo("2532", new string[] { "TY1" }), //分拣库一
+                new RouteInfo("2732", new string[] { "TY2" }),
+                new RouteInfo("2932", new string[] { "TY3" }), //分拣库二
+                new RouteInfo("3132", new string[] { "TY4" }),
+                //new RouteInfo("3332", new string[] { "TY5" }), //分拣库三
+                //new RouteInfo("3532", new string[] { "TY6" }),
+
+                #endregion 一楼人工入库口-巷道
+
+                #region 一楼叠盘机(堆垛机入口)--巷道
+
+                new RouteInfo("2527", new string[] { "TY1" }), //分拣库一
+                new RouteInfo("2528", new string[] { "TY1" }),
+                new RouteInfo("2727", new string[] { "TY2" }),
+                new RouteInfo("2728", new string[] { "TY2" }),
+                new RouteInfo("2927", new string[] { "TY3" }), //分拣库二
+                new RouteInfo("2928", new string[] { "TY3" }),
+                new RouteInfo("3127", new string[] { "TY4" }),
+                new RouteInfo("3128", new string[] { "TY4" }),
+                //new RouteInfo("3327", new string[] { "TY5" }), //分拣库三
+                //new RouteInfo("3328", new string[] { "TY5" }),
+                //new RouteInfo("3527", new string[] { "TY6" }),
+                //new RouteInfo("3528", new string[] { "TY6" }),
+
+                #endregion 一楼叠盘机(堆垛机入口)--巷道
+
+                #region 主线
+
+                //北侧
+                new RouteInfo("3", new string[] { "14" }),
+                new RouteInfo("14", new string[] { "18" }),
+                new RouteInfo("18", new string[] { "22" }),
+                new RouteInfo("22", new string[] { "38" }),
+                //new RouteInfo("323", new string[] { "38" }),
+                new RouteInfo("38", new string[] { "41" }),
+                new RouteInfo("41", new string[] { "58" }),
+                new RouteInfo("58", new string[] { "61" }),
+                ////南侧
+                new RouteInfo("101", new string[] { "114" }),
+                new RouteInfo("114", new string[] { "118" }),
+                new RouteInfo("118", new string[] { "122" }),
+                new RouteInfo("122", new string[] { "138" }),
+                new RouteInfo("138", new string[] { "141" }),
+                new RouteInfo("141", new string[] { "158" }),
+                new RouteInfo("158", new string[] { "161" }),
+
+                #endregion 主线
+
+                #region 主线分流点--环形库分流点
+
+                new RouteInfo("22", new string[] { "418" }), //分拣库一北
+                new RouteInfo("122", new string[] { "618" }), //分拣库一南
+                new RouteInfo("41", new string[] { "818" }), //分拣库一北
+                new RouteInfo("141", new string[] { "1018" }), //分拣库一南
+                //new RouteInfo("61", new string[] { "1218" }), //分拣库一北
+                //new RouteInfo("161", new string[] { "1418" }), //分拣库一南
+
+                #endregion 主线分流点--环形库分流点
+
+                #region 环形库分流点--环形库取货点
+
+                new RouteInfo("418", new string[] { "424","433","442" }),
+                new RouteInfo("618", new string[] { "624", "633", "642" }),
+                new RouteInfo("818", new string[] { "824","833","842" }),
+                new RouteInfo("1018", new string[] { "1024","1033","1042" }),
+                //new RouteInfo("1218", new string[] { "1224","1233","1242" }),
+                //new RouteInfo("1418", new string[] { "1424","1433","1442" }),
+
+                #endregion 环形库分流点--环形库取货点
+
+                #region 环形库分流点--桁架分流点
+
+                new RouteInfo("418", new string[] { "455" }), //分拣库一北
+                new RouteInfo("618", new string[] { "655" }), //分拣库一南
+                new RouteInfo("818", new string[] { "855" }), //分拣库二北
+                new RouteInfo("1018", new string[] { "1055" }), //分拣库二南
+                //new RouteInfo("1218", new string[] { "1255" }), //分拣库三北
+                //new RouteInfo("1418", new string[] { "1455" }), //分拣库三南
+
+                #endregion 环形库分流点--桁架分流点
+
+                #region 桁架分流点--桁架缓存放行点
+
+                new RouteInfo("455", new string[] { "466","480","494","508", "522","536","550","564","578","591"}), //分拣库一北
+                new RouteInfo("655", new string[] { "666","680","694","708", "722","736","750","764","778","791"}), //分拣库一南
+                new RouteInfo("855", new string[] { "866","880","894","908", "922","936","950","964","978","991"}), //分拣库二北
+                new RouteInfo("1055", new string[] { "1066","1080","1094","1108", "1122","1136","1150","1164","1178","1191"}), //分拣库二南
+                //new RouteInfo("1255", new string[] { "1266","1280","1294","1308", "1322","1336","1350","1364","1378", "1391"}), //分拣库三北
+                //new RouteInfo("1455", new string[] { "1466","1480","1494","1408", "1422","1436","1450","1464","1478","1591"}), //分拣库三南
+
+                #endregion 桁架分流点--桁架缓存放行点
+
+                #region 桁架缓存放行点--桁架取货点
+
+                new("466", new[] { "461" }), //分拣一北侧
+                new("480", new[] { "475" }),
+                new("494", new[] { "489" }),
+                new("508", new[] { "503" }),
+                new("522", new[] { "517" }),
+                new("536", new[] { "531" }),
+                new("550", new[] { "545" }),
+                new("564", new[] { "559" }),
+                new("578", new[] { "573" }),
+                new("591", new[] { "586" }),
+                new("666", new[] { "661" }),//分拣一南侧
+                new("680", new[] { "675" }),
+                new("694", new[] { "689" }),
+                new("708", new[] { "703" }),
+                new("722", new[] { "717" }),
+                new("736", new[] { "731" }),
+                new("750", new[] { "745" }),
+                new("764", new[] { "759" }),
+                new("778", new[] { "773" }),
+                new("791", new[] { "786" }),
+                new("866", new[] { "861" }), //分拣二北侧
+                new("880", new[] { "875" }),
+                new("894", new[] { "889" }),
+                new("908", new[] { "903" }),
+                new("922", new[] { "917" }),
+                new("936", new[] { "931" }),
+                new("950", new[] { "945" }),
+                new("964", new[] { "959" }),
+                new("978", new[] { "973" }),
+                new("991", new[] { "986" }),
+                new("1066", new[] { "1061" }),//分拣二南侧
+                new("1080", new[] { "1075" }),
+                new("1094", new[] { "1089" }),
+                new("1108", new[] { "1103" }),
+                new("1122", new[] { "1117" }),
+                new("1136", new[] { "1131" }),
+                new("1150", new[] { "1145" }),
+                new("1164", new[] { "1159" }),
+                new("1178", new[] { "1173" }),
+                new("1191", new[] { "1186" }),
+                //new("1266", new[] { "1261" }), //分拣三北侧
+                //new("1280", new[] { "1275" }),
+                //new("1294", new[] { "1289" }),
+                //new("1308", new[] { "1303" }),
+                //new("1322", new[] { "1317" }),
+                //new("1336", new[] { "1331" }),
+                //new("1350", new[] { "1345" }),
+                //new("1364", new[] { "1359" }),
+                //new("1378", new[] { "1373" }),
+                //new("1391", new[] { "1386" }),
+                //new("1466", new[] { "1461" }),//分拣三南侧
+                //new("1480", new[] { "1475" }),
+                //new("1494", new[] { "1489" }),
+                //new("1508", new[] { "1503" }),
+                //new("1522", new[] { "1517" }),
+                //new("1536", new[] { "1531" }),
+                //new("1550", new[] { "1545" }),
+                //new("1564", new[] { "1559" }),
+                //new("1578", new[] { "1573" }),
+                //new("1591", new[] { "1586" }),
+
+                #endregion 桁架缓存放行点--桁架取货点
+
+                #region 桁架取货点--桁架
+
+                new("461", new[] { "Truss1" }),//分拣一北侧
+                new("475", new[] { "Truss1" }),
+                new("489", new[] { "Truss1" }),
+                new("503", new[] { "Truss1" }),
+                new("517", new[] { "Truss1" }),
+                new("531", new[] { "Truss1" }),
+                new("545", new[] { "Truss1" }),
+                new("559", new[] { "Truss1" }),
+                new("573", new[] { "Truss1" }),
+                new("586", new[] { "Truss1" }),//分拣一南侧
+                new("661", new[] { "Truss1" }),
+                new("675", new[] { "Truss1" }),
+                new("689", new[] { "Truss1" }),
+                new("703", new[] { "Truss1" }),
+                new("717", new[] { "Truss1" }),
+                new("731", new[] { "Truss1" }),
+                new("745", new[] { "Truss1" }),
+                new("759", new[] { "Truss1" }),
+                new("773", new[] { "Truss1" }),
+                new("786", new[] { "Truss1" }),
+                new("861", new[] { "Truss2" }), //分拣二北侧
+                new("875", new[] { "Truss2" }),
+                new("889", new[] { "Truss2" }),
+                new("903", new[] { "Truss2" }),
+                new("917", new[] { "Truss2" }),
+                new("931", new[] { "Truss2" }),
+                new("945", new[] { "Truss2" }),
+                new("959", new[] { "Truss2" }),
+                new("973", new[] { "Truss2" }),
+                new("986", new[] { "Truss2" }),
+                new("1061", new[] { "Truss2" }),//分拣二南侧
+                new("1075", new[] { "Truss2" }),
+                new("1089", new[] { "Truss2" }),
+                new("1103", new[] { "Truss2" }),
+                new("1117", new[] { "Truss2" }),
+                new("1131", new[] { "Truss2" }),
+                new("1145", new[] { "Truss2" }),
+                new("1159", new[] { "Truss2" }),
+                new("1173", new[] { "Truss2" }),
+                new("1186", new[] { "Truss2" }),
+                //new("1261", new[] { "Truss3" }), //分拣三北侧
+                //new("1275", new[] { "Truss3" }),
+                //new("1289", new[] { "Truss3" }),
+                //new("1303", new[] { "Truss3" }),
+                //new("1317", new[] { "Truss3" }),
+                //new("1331", new[] { "Truss3" }),
+                //new("1345", new[] { "Truss3" }),
+                //new("1359", new[] { "Truss3" }),
+                //new("1373", new[] { "Truss3" }),
+                //new("1386", new[] { "Truss3" }),
+                //new("1461", new[] { "Truss3" }),//分拣三南侧
+                //new("1475", new[] { "Truss3" }),
+                //new("1489", new[] { "Truss3" }),
+                //new("1503", new[] { "Truss3" }),
+                //new("1517", new[] { "Truss3" }),
+                //new("1531", new[] { "Truss3" }),
+                //new("1545", new[] { "Truss3" }),
+                //new("1559", new[] { "Truss3" }),
+                //new("1573", new[] { "Truss3" }),
+                //new("1586", new[] { "Truss3" }),
+
+                #endregion 桁架取货点--桁架
+
+                #region 桁架取货点--码垛工位
+
+                new("461", new[] { "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675" }),//分拣一北侧
+                new("475", new[] {  "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675"}),
+                new("489", new[] {  "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675"}),
+                new("503", new[] {  "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675"}),
+                new("517", new[] {  "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675"}),
+                new("531", new[] {  "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675"}),
+                new("545", new[] {  "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675"}),
+                new("559", new[] {  "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675"}),
+                new("573", new[] {  "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675"}),
+                new("586", new[] { "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675" }),
+                new("661", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),//分拣一南侧
+                new("675", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("689", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("703", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("717", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("731", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("745", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("759", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("773", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("786", new[] { "1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("861", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705" }), //分拣二北侧
+                new("875", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("889", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("903", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("917", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("931", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("945", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("959", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("973", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("986", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705"  }),
+                new("1061", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),//分拣二南侧
+                new("1075", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                new("1089", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                new("1103", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                new("1117", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                new("1131", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                new("1145", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                new("1159", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                new("1173", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                new("1186", new[] { "1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                //new("1261", new[] { "1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735" }), //分拣三北侧
+                //new("1275", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1289", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1303", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1317", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1331", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1345", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1359", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1373", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1386", new[] {"1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735"  }),
+                //new("1461", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),//分拣三南侧
+                //new("1475", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+                //new("1489", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+                //new("1503", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+                //new("1517", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+                //new("1531", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+                //new("1545", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+                //new("1559", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+                //new("1573", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+                //new("1586", new[] { "1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750" }),
+
+                #endregion 桁架取货点--码垛工位
+
+                #region 桁架--码垛工位
+
+                new("Truss1", new[] { "1662","1663","1664","1665","1670","1671","1672","1673", "1674","1675","1677","1678","1679","1680", "1685","1686","1687","1688","1689","1690" }),
+                new("Truss2", new[] { "1692","1693","1694","1695", "1700","1701","1702","1703","1704","1705","1707","1708","1709","1710", "1715","1716","1717","1718","1719","1720" }),
+                //new("Truss3", new[] { "1722","1723","1724","1725", "1730","1731","1732","1733","1734","1735","1737","1738","1739","1740", "1745","1746","1747","1748","1749","1750"}),
+
+                #endregion 桁架--码垛工位
+
+                #region 码垛工位--RGV
+
+                //分拣库一北
+                new RouteInfo("1661", new string[] { "RGV1" }),//机械手码垛工位
+                new RouteInfo("1666", new string[] { "RGV1" }),
+                new RouteInfo("1662", new string[] { "RGV1" }),//绗架码垛工位
+                new RouteInfo("1663", new string[] { "RGV1" }),
+                new RouteInfo("1664", new string[] { "RGV1" }),
+                new RouteInfo("1665", new string[] { "RGV1" }),
+                new RouteInfo("1670", new string[] { "RGV1" }),
+                new RouteInfo("1671", new string[] { "RGV1" }),
+                new RouteInfo("1672", new string[] { "RGV1" }),
+                new RouteInfo("1673", new string[] { "RGV1" }),
+                new RouteInfo("1674", new string[] { "RGV1" }),
+                new RouteInfo("1675", new string[] { "RGV1" }),
+                ////分拣库一南
+                new RouteInfo("1676", new string[] { "RGV2" }),//机械手码垛工位
+                new RouteInfo("1681", new string[] { "RGV2" }),
+                new RouteInfo("1677", new string[] { "RGV2" }),//绗架码垛工位
+                new RouteInfo("1678", new string[] { "RGV2" }),
+                new RouteInfo("1679", new string[] { "RGV2" }),
+                new RouteInfo("1680", new string[] { "RGV2" }),
+                new RouteInfo("1685", new string[] { "RGV2" }),
+                new RouteInfo("1686", new string[] { "RGV2" }),
+                new RouteInfo("1687", new string[] { "RGV2" }),
+                new RouteInfo("1688", new string[] { "RGV2" }),
+                new RouteInfo("1689", new string[] { "RGV2" }),
+                new RouteInfo("1690", new string[] { "RGV2" }),
+
+                //分拣库二北
+                new RouteInfo("1691", new string[] { "RGV3" }),//机械手码垛工位
+                new RouteInfo("1696", new string[] { "RGV3" }),
+                new RouteInfo("1692", new string[] { "RGV3" }),//绗架码垛工位
+                new RouteInfo("1693", new string[] { "RGV3" }),
+                new RouteInfo("1694", new string[] { "RGV3" }),
+                new RouteInfo("1695", new string[] { "RGV3" }),
+                new RouteInfo("1700", new string[] { "RGV3" }),
+                new RouteInfo("1701", new string[] { "RGV3" }),
+                new RouteInfo("1702", new string[] { "RGV3" }),
+                new RouteInfo("1703", new string[] { "RGV3" }),
+                new RouteInfo("1704", new string[] { "RGV3" }),
+                new RouteInfo("1705", new string[] { "RGV3" }),
+                //分拣库二南
+                new RouteInfo("1706", new string[] { "RGV4" }),//机械手码垛工位
+                new RouteInfo("1711", new string[] { "RGV4" }),
+                new RouteInfo("1707", new string[] { "RGV4" }),//绗架码垛工位
+                new RouteInfo("1708", new string[] { "RGV4" }),
+                new RouteInfo("1709", new string[] { "RGV4" }),
+                new RouteInfo("1710", new string[] { "RGV4" }),
+                new RouteInfo("1715", new string[] { "RGV4" }),
+                new RouteInfo("1716", new string[] { "RGV4" }),
+                new RouteInfo("1717", new string[] { "RGV4" }),
+                new RouteInfo("1718", new string[] { "RGV4" }),
+                new RouteInfo("1719", new string[] { "RGV4" }),
+                new RouteInfo("1720", new string[] { "RGV4" }),
+                ////分拣库三北
+                //new RouteInfo("1721", new string[] { "RGV5" }),//机械手码垛工位
+                //new RouteInfo("1726", new string[] { "RGV5" }),
+                //new RouteInfo("1722", new string[] { "RGV5" }),//绗架码垛工位
+                //new RouteInfo("1723", new string[] { "RGV5" }),
+                //new RouteInfo("1724", new string[] { "RGV5" }),
+                //new RouteInfo("1725", new string[] { "RGV5" }),
+                //new RouteInfo("1730", new string[] { "RGV5" }),
+                //new RouteInfo("1731", new string[] { "RGV5" }),
+                //new RouteInfo("1732", new string[] { "RGV5" }),
+                //new RouteInfo("1733", new string[] { "RGV5" }),
+                //new RouteInfo("1734", new string[] { "RGV5" }),
+                //new RouteInfo("1735", new string[] { "RGV5" }),
+                ////分拣库三南
+                //new RouteInfo("1736", new string[] { "RGV6" }),//机械手码垛工位
+                //new RouteInfo("1741", new string[] { "RGV6" }),
+                //new RouteInfo("1737", new string[] { "RGV6" }),//绗架码垛工位
+                //new RouteInfo("1738", new string[] { "RGV6" }),
+                //new RouteInfo("1739", new string[] { "RGV6" }),
+                //new RouteInfo("1740", new string[] { "RGV6" }),
+                //new RouteInfo("1745", new string[] { "RGV6" }),
+                //new RouteInfo("1746", new string[] { "RGV6" }),
+                //new RouteInfo("1747", new string[] { "RGV6" }),
+                //new RouteInfo("1748", new string[] { "RGV6" }),
+                //new RouteInfo("1749", new string[] { "RGV6" }),
+                //new RouteInfo("1750", new string[] { "RGV6" }),
+
+                #endregion 码垛工位--RGV
+
+                #region RGV--RGV放货点
+
+                new RouteInfo("RGV1", new string[] { "1604" }),
+                new RouteInfo("RGV2", new string[] { "1614" }),
+                new RouteInfo("RGV3", new string[] { "1624" }),
+                new RouteInfo("RGV4", new string[] { "1634" }),
+                //new RouteInfo("RGV5", new string[] { "1644" }),
+                //new RouteInfo("RGV6", new string[] { "1654" }),
+
+                #endregion RGV--RGV放货点
+
+                #region RGV放货点--堆垛机取货口
+
+                new RouteInfo("1604", new string[] { "1603" }),//北侧
+                new RouteInfo("1614", new string[] { "1613" }),//南侧
+                new RouteInfo("1624", new string[] { "1623" }),//北侧
+                new RouteInfo("1634", new string[] { "1633" }),//南侧
+                //new RouteInfo("1644", new string[] { "1643" }),//北侧
+                //new RouteInfo("1654", new string[] { "1653" }),//南侧
+
+                #endregion RGV放货点--堆垛机取货口
+
+                #region 二楼堆垛机入库--巷道
+
+                new RouteInfo("1603", new string[] { "TY1" }),//北侧
+                new RouteInfo("1613", new string[] { "TY2" }),//南侧
+                new RouteInfo("1623", new string[] { "TY3" }),//北侧
+                new RouteInfo("1633", new string[] { "TY4" }),//南侧
+                //new RouteInfo("1643", new string[] { "TY5" }),//北侧
+                //new RouteInfo("1653", new string[] { "TY6" }),//南侧
+
+                #endregion 二楼堆垛机入库--巷道
+
+                #region 巷道--堆垛机
+
+                new RouteInfo("TY1", new string[] { "SRM1" }),
+                new RouteInfo("TY2", new string[] { "SRM2" }),
+                new RouteInfo("TY3", new string[] { "SRM3" }),
+                new RouteInfo("TY4", new string[] { "SRM4" }),
+                //new RouteInfo("TY5", new string[] { "SRM5" }),
+                //new RouteInfo("TY6", new string[] { "SRM6" }),
+
+                #endregion 巷道--堆垛机
+
+                #region 线体取货位--Robot
+
+                new RouteInfo("442", new string[] { "Robot1" }),
+                new RouteInfo("433", new string[] { "Robot1" }),
+                new RouteInfo("424", new string[] { "Robot1" }),
+                new RouteInfo("624", new string[] { "Robot2" }),
+                new RouteInfo("633", new string[] { "Robot2" }),
+                new RouteInfo("642", new string[] { "Robot2" }),
+                new RouteInfo("824", new string[] { "Robot3" }),
+                new RouteInfo("833", new string[] { "Robot3" }),
+                new RouteInfo("842", new string[] { "Robot3" }),
+                new RouteInfo("1024", new string[] { "Robot4" }),
+                new RouteInfo("1033", new string[] { "Robot4" }),
+                new RouteInfo("1042", new string[] { "Robot4" }),
+                //new RouteInfo("1224", new string[] { "Robot5" }),
+                //new RouteInfo("1233", new string[] { "Robot5" }),
+                //new RouteInfo("1242", new string[] { "Robot5" }),
+                //new RouteInfo("1424", new string[] { "Robot6" }),
+                //new RouteInfo("1433", new string[] { "Robot6" }),
+                //new RouteInfo("1442", new string[] { "Robot6" })
+
+                #endregion 线体取货位--Robot
+
+                #endregion 入库
+            });
+
+            foreach (var routeInfo in routeInfos)
+            {
+                if (routeInfo != null)
+                {
+                    var device = Device.All.FirstOrDefault(x => x.Code == routeInfo.DeviceCode);
+                    device.AddTarget(routeInfo.NextList);
+
+                    Parallel.ForEach(routeInfo.NextList, next =>
+                    {
+                        var dev = Device.All.FirstOrDefault(x => x.Code == next);
+                        dev.Parent = device;
+                    });
+                }
+            }
+            //Parallel.ForEach(routeInfos, routeInfo =>
+            //{
+            //});
+
+            #endregion 配置路径信息
+
+            #region 添加设备组
+
+            var tuples2 = new List<Tuple<string, List<string>>>()
+            {
+                //环形库一北侧机械手取货位
+                new("424",new List<string> {"424","425"}),
+                new("433",new List<string> {"433","434"}),
+                new("442",new List<string> {"442","443"}),
+                //环形库一南侧侧机械手取货位
+                new("642",new List<string> {"642","643"}),
+                new("633",new List<string> {"633","634"}),
+                new("624",new List<string> {"624","625"}),
+                //环形库二北侧机械手取货位
+                new("824",new List<string> {"824","825"}),
+                new("833",new List<string> {"833","834"}),
+                new("842",new List<string> {"842","843"}),
+                //环形库二南侧侧机械手取货位
+                new("1042",new List<string> {"1042","1043"}),
+                new("1033",new List<string> {"1033","1034"}),
+                new("1024",new List<string> {"1024","1025"}),
+                ////环形库三北侧机械手取货位
+                //new("1224",new List<string> {"1224","1225"}),
+                //new("1233",new List<string> {"1233","1234"}),
+                //new("1242",new List<string> {"1242","1243"}),
+                ////环形库三南侧侧机械手取货位
+                //new("1442",new List<string> {"1442","1443"}),
+                //new("1433",new List<string> {"1433","1434"}),
+                //new("1424",new List<string> {"1424","1425"}),
+            };
+            Parallel.ForEach(tuples2, tuple =>
+            {
+                var device = Device.All.FirstOrDefault(x => x.Code == tuple.Item1);
+                device.AddDeviceGroup(tuple.Item2.ToArray());
+            });
+
+            #endregion 添加设备组
+
+            #region 标签配置
+
+            Dictionary<DeviceFlags, List<string>> devices = new Dictionary<DeviceFlags, List<string>>
+            {
+                { DeviceFlags.入库, new List<string>() { "2532", "2533", "2534", "2732", "2733", "2734"/*,"1603"*/ } },
+                { DeviceFlags.出库, new List<string>() { "2532", "2533", "2534", "2732", "2733", "2734" } },
+                { DeviceFlags.扫码, new List<string>() { "2532", "2732" } },
+                { DeviceFlags.一楼扫码,new List<string>(){ "2532","2732", "2932", "3132"/*, "3332", "3532"*/ } },
+                { DeviceFlags.一楼叠盘机,new List<string>() { "2527", "2528", "2727", "2728", "2927", "2928", "3127", "3128"/*, "3327", "3328", "3527", "3528"*/ } },
+                { DeviceFlags.主线分流点,new List<string>() { "22","41","61"} },
+                { DeviceFlags.满轮主线第一次扫码,new List<string>() { "1"} },
+                { DeviceFlags.环形库分流点,new List<string>(){ "418", "618", "818", "1018"/*, "1218", "1418"*/ } },
+                { DeviceFlags.桁架分流点,new List<string>() { "455", "655", "855", "1055"/*, "1255", "1455"*/ } },
+                { DeviceFlags.桁架缓存放行点, new List<string>(){ "466", "480", "494", "508", "522", "536", "550", "564", "578", "591", "666", "680", "694", "708", "722", "736", "750", "764", "778", "791",
+                    "866", "880", "894", "908", "922", "936", "950", "964", "978", "991", "1066", "1080", "1094", "1108", "1122", "1136", "1150", "1164", "1178", "1191"/*,
+                    "1266", "1280", "1294", "1308", "1322", "1336", "1350", "1364", "1378", "1391", "1466", "1480", "1494", "1408", "1422", "1436", "1450", "1464", "1478", "1591" */} },
+                { DeviceFlags.桁架09缓存放行点, new List<string>(){ "466", "480", "494", "508", "522", "536", "666", "680", "694", "708", "722", "736"
+                    , "866", "880", "894", "908", "922", "936", "1066", "1080", "1094", "1108", "1122", "1136",
+                     "1266", "1280", "1294", "1308", "1322", "1336",  "1466", "1480", "1494", "1408", "1422", "1436" } },
+                { DeviceFlags.桁架09异常缓存放行点, new List<string>(){ "591", "791", "991" , "1191"/* , "1391" , "1591" } },
+                { DeviceFlags.桁架18缓存放行点, new List<string>(){ "550", "564", "578", "750", "764", "778", "950", "964", "978" , "1150", "1164", "1178" /*, "1350", "1364", "1378" , "1450", "1464", "1478"*/ } },
+                { DeviceFlags.桁架取货点, new List<string>(){ "461", "475", "489", "503", "517", "531", "545", "559", "573", "586", "661", "675", "689", "703", "717", "731", "745", "759", "773", "786",
+                     "861", "875", "889", "903", "917", "931", "945", "959", "973", "986", "1061", "1075", "1089", "1103", "1117", "1131", "1145", "1159", "1173", "1186",
+                     "1261", "1275", "1289", "1303", "1317", "1331", "1345", "1359", "1373", "1386" , "1461", "1475", "1489", "1503", "1517", "1531", "1545", "1559", "1573", "1586" } },
+                //{ DeviceFlags.拆盘机09, new List<string>(){ /*"1606", "1616",*/"1626","1636","1646","1656" } },
+                { DeviceFlags.拆盘机, new List<string>(){ "1602", "1612", "1622", "1632", "1642", "1652" } },
+                { DeviceFlags.桁架码垛位, new List<string>(){ /*"1670", "1671",*/ "1672", "1673", "1674", "1675", "1664","1662","1663","1665","1677","1678","1679","1680",/*"1685","1686",*/"1687","1688","1689",
+                    "1690", "1692", "1693", "1694", "1695", "1700", "1701", "1702", "1703", "1704", "1705", "1707", "1708", "1709", "1710", "1715", "1716", "1717", "1718", "1719", "1720", "1722", "1723", "1724", "1725", "1730", "1731", "1732", "1733", "1734", "1735", "1737", "1738", "1739", "1740", "1745", "1746", "1747", "1748", "1749", "1750" } },
+                { DeviceFlags.环形库码垛工位, new List<string>(){ "1666", "1661", "1676", "1681", "1691", "1696", "1706", "1711"/*, "1721", "1726", "1736", "1741"*/ } },
+                { DeviceFlags.AGV取货站台口, new List<string>(){ "2533", "2534", "2734", "2733","2934","2933","3133", "3134"/*,"3333","3334","3533","3534" */} },
+                { DeviceFlags.Robot, new List<string>(){ "Robot1", "Robot2", "Robot3", "Robot4"/* , "Robot5", "Robot6"*/ } },
+                {DeviceFlags.二次码垛RGV取货口, new List<string>(){ "1606", "1616", "1626","1636"/*,"1646","1656"*/}}
+        };
+
+            Parallel.ForEach(devices, device =>
+            {
+                Parallel.ForEach(device.Value, value =>
+                {
+                    var dev = Device.All.FirstOrDefault(v => v.Code == value);
+                    if (dev != null)
+                    {
+                        dev.AddFlag(device.Key);
+                    }
+                });
+            });
+
+            #endregion 标签配置
+        }
+
+        /// <summary>
+        ///  初始化数据库连接
+        /// </summary>
+        /// <param name="datas"></param>
+        public static void InitDB(this List<DataBaseConnectionString> datas)
+        {
+            //初始化数据库
+            SqlSugarHelper.Do(db =>
+            {
+                foreach (var connectionString in datas!)
+                {
+                    var _db = db.Connect.GetConnectionScope(connectionString.Key);
+                    switch (connectionString.Key)
+                    {
+                        case "WCSDB"://WCS基本数据库
+                            //SqlSugarHelper.SetDefault(connectionString.Key);
+                            //_db.CodeFirst.InitTables(typeof(WCS_PlcData));
+                            //_db.CodeFirst.InitTables(typeof(WCS_TaskInfo));
+                            //_db.CodeFirst.InitTables(typeof(WCS_TaskDtl));
+                            //_db.CodeFirst.InitTables(typeof(WCS_TaskOld));
+                            //_db.CodeFirst.InitTables(typeof(WCS_AgvTaskInfo));
+                            //_db.CodeFirst.InitTables(typeof(WCS_Palletizing));
+                            //_db.CodeFirst.InitTables(typeof(WCS_PalletizingLayer));
+                            //_db.CodeFirst.InitTables(typeof(WCS_PalletizingRow));
+                            //_db.CodeFirst.InitTables(typeof(WCS_PalletizingLoc));
+                            //_db.CodeFirst.InitTables(typeof(WCS_CacheLine));
+                            //_db.CodeFirst.InitTables(typeof(WCS_CacheLineLoc));
+
+                            break;
+
+                        case "WCSDlog"://WCS日志数据库
+
+                            break;
+
+                        case "PLC"://PLC
+
+                            SqlSugarHelper.SetPLC(connectionString.Key);
+
+                            _db.DbMaintenance.CreateDatabase();
+                            _db.CodeFirst.InitTables<WCS_Log>();
+                            _db.CodeFirst.InitTables<DevRunInfo>();
+                            _db.CodeFirst.InitTables<WCS_SRM520>();
+                            _db.CodeFirst.InitTables<WCS_SRM521>();
+                            _db.CodeFirst.InitTables<WCS_SRM537>();
+                            _db.CodeFirst.InitTables<WCS_RGV520>();
+                            _db.CodeFirst.InitTables<WCS_RGV521>();
+                            _db.CodeFirst.InitTables<WCS_BCR80>();
+                            _db.CodeFirst.InitTables<WCS_BCR81>();
+                            _db.CodeFirst.InitTables<WCS_BCR83>();
+                            _db.CodeFirst.InitTables<WCS_Station520>();
+                            _db.CodeFirst.InitTables<WCS_Station521>();
+                            _db.CodeFirst.InitTables<WCS_Station523>();
+                            _db.CodeFirst.InitTables<WCS_Station524>();
+                            _db.CodeFirst.InitTables<WCS_Station525>();
+                            _db.CodeFirst.InitTables<WCS_Station90>();
+                            _db.CodeFirst.InitTables<WCS_Station91>();
+                            _db.CodeFirst.InitTables<WCS_Truss520>();
+                            _db.CodeFirst.InitTables<WCS_Truss521>();
+                            _db.CodeFirst.InitTables<WCS_Truss523>();
+                            _db.CodeFirst.InitTables<WCS_Truss530>();
+                            _db.CodeFirst.InitTables<WCS_Truss531>();
+                            _db.CodeFirst.InitTables<WCS_Robot520>();
+                            _db.CodeFirst.InitTables<WCS_Robot521>();
+                            _db.CodeFirst.InitTables<WCS_Robot522>();
+                            _db.CodeFirst.InitTables<WCS_Robot530>();
+                            _db.CodeFirst.InitTables<WCS_Robot531>();
+                            var a = false;
+                            break;
+
+                        default: //其他库
+                            break;
+                    };
+                };
+            });
+        }
+    }
+
+    public class DevDbConfig<T>
+    {
+        public DevDbConfig()
+        {
+        }
+
+        public DevDbConfig(string ip, T code)
+        {
+            IP = ip;
+            Code = code;
+        }
+
+        public DevDbConfig(string ip, List<DevInterval<T>> devIntervalList)
+        {
+            IP = ip;
+            DevIntervalList = devIntervalList;
+        }
+
+        public DevDbConfig(string ip, List<T> devCodeList)
+        {
+            IP = ip;
+            DevCodeList = devCodeList;
+        }
+
+        public string IP { get; set; }
+
+        public T Code { get; set; }
+
+        public T StartCode { get; set; }
+
+        public T EndCode { get; set; }
+
+        public List<T> DevCodeList { get; set; }
+
+        public List<DevInterval<T>> DevIntervalList { get; set; }
+    }
+
+    public class DevInterval<T>
+    {
+        public DevInterval(T s, T e)
+        {
+            StartCode = s;
+            EndCode = e;
+        }
+
+        public T StartCode { get; set; }
+
+        public T EndCode { get; set; }
+    }
+}

+ 133 - 0
WCS.WorkEngineering/Worlds/MainWorld.cs

@@ -0,0 +1,133 @@
+using ServiceCenter.Logs;
+using System.Collections.Concurrent;
+using System.ComponentModel;
+using WCS.Core;
+using LogInfo = ServiceCenter.Logs.LogInfo;
+
+namespace WCS.WorkEngineering.Worlds
+{
+    /// <summary>
+    /// 主世界,所有的系统(交互点)默认在该世界下执行。
+    /// 如有系统需独立,请自行增加对应世界
+    /// 新增世界应当继承此世界,而不是直接继承World
+    /// </summary>
+    [Description("主世界")]
+    public class MainWorld : World
+    {
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        public MainWorld()
+        {
+        }
+
+        /// <summary>
+        ///  日志队列
+        /// </summary>
+        protected ConcurrentQueue<KeyLog> Logs = new ConcurrentQueue<KeyLog>();
+
+        /// <summary>
+        ///  世界执行周期间隔
+        ///  单位:毫秒
+        /// </summary>
+        protected override int Interval => 500;
+
+        /// <summary>
+        ///  更新前执行,重写改方法后请自行添加执行内容
+        ///  执行内容:清空日志队列
+        /// </summary>
+        protected override void BeforeUpdate()
+        {
+            // 清空日志队列,确保日志队列中只会有当前周期日志
+            Logs.Clear();
+        }
+
+        /// <summary>
+        /// 更新后执行,重写改方法后请自行添加执行内容
+        /// 执行内容:清空日志队列
+        /// </summary>
+        protected override void AfterUpdate()
+        {
+            LogHub.WorldPublish(Logs, this.GetType().Name);
+        }
+
+        /// <summary>
+        ///  异常处理,重写改方法后请自行添加执行内容
+        ///  执行内容:Exception as KnownException并添加至日志队列
+        /// </summary>
+        /// <param name="channel"></param>
+        /// <param name="exception"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        protected override void OnError(Channel channel, Exception exception)
+        {
+            if (exception is KnownException)
+            {
+                var ex = exception as KnownException;
+                var log = new LogInfo { Level = ex.Level, Type = ErrorTypeEnum.Kown, LogUpLoad = ex.logUpLoad, Message = ex.Message };
+                Logs.Enqueue(new KeyLog { Channel = channel, Log = log, Time = DateTime.Now });
+            }
+            else
+            {
+                var log = new LogInfo { Level = LogLevelEnum.High, Type = ErrorTypeEnum.Unkown, LogUpLoad = LogUpLoadEnum.UpLoadWMS, Message = exception.Message };
+                Logs.Enqueue(new KeyLog { Channel = channel, Log = log, Time = DateTime.Now });
+            }
+        }
+
+        /// <summary>
+        ///  日志处理,重写改方法后请自行添加执行内容
+        ///  执行内容:LogInfo as KeyLog并添加至日志队列
+        /// </summary>
+        /// <param name="channel"></param>
+        /// <param name="logObj"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        protected override void OnLog(Channel channel, object logObj)
+        {
+            if (channel == null) return;
+            if (logObj.GetType() == typeof(string))
+            {
+                Logs.Enqueue(new KeyLog
+                {
+                    Channel = channel,
+                    Log = new LogInfo()
+                    {
+                        Level = LogLevelEnum.High,
+                        LogUpLoad = LogUpLoadEnum.UpLoadWMS,
+                        Message = logObj as string,
+                    },
+                    Time = DateTime.Now
+                });
+            }
+            else
+            {
+                var log = (LogInfo)logObj;
+                Logs.Enqueue(new KeyLog { Channel = channel, Log = log, Time = DateTime.Now });
+            }
+        }
+
+        /// <summary>
+        ///  日志处理,重写改方法后请自行添加执行内容
+        /// </summary>
+        /// <param name="channel"></param>
+        /// <param name="msg"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        protected override void OnInternalLog(Channel channel, string msg)
+        {
+            var log = new LogInfo { Level = LogLevelEnum.Low, Message = msg };
+            if (msg != "开始" && msg != "结束")
+            {
+                Logs.Enqueue(new KeyLog { Channel = channel, Log = log, Time = DateTime.Now });
+            }
+        }
+
+        /// <summary>
+        ///  获取日志,重写改方法后请自行添加执行内容
+        /// </summary>
+        /// <param name="channel"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        protected override IEnumerable<string> GetChannelMsg(Channel channel)
+        {
+            return Logs.Where(v => v.Channel.ToString() == channel.ToString()).Select(v => v.Log.ToString());
+        }
+    }
+}

+ 252 - 0
WCS.WorkEngineering/数据采集.cs

@@ -0,0 +1,252 @@
+using FreeRedis;
+using ServiceCenter.Extensions;
+using System.ComponentModel;
+using System.Diagnostics;
+using WCS.Core;
+using WCS.Entity.Protocol.BCR;
+using WCS.Entity.Protocol.DataStructure;
+using WCS.Entity.Protocol.Station;
+using WCS.WorkEngineering.Worlds;
+
+namespace WCS.WorkEngineering.Systems
+{
+    //[BelongTo(typeof(MainWorld))]
+    [Description("数据采集系统")]
+    public class 数据采集 : SystemBase
+    {
+        private RedisClient Redis = new RedisClient("");
+
+        public 数据采集()
+        {
+            var gs = Device.All.SelectMany(v => v.Protocols.Select(d => new { DB = $"{d.Value.DBInfo.No}:{d.Value.DBInfo.PLCInfo.IP}", d.Value.Position, TypeStr = d.Key, Dev = v }))
+                .GroupBy(v => v.DB);
+            foreach (var g in gs)
+            {
+                var min = g.OrderBy(v => v.Position).First();
+                var max = g.OrderByDescending(v => v.Position).First();
+                var t = Type.GetType(min.TypeStr);
+                min.Dev.Protocol(t, this.World);
+                max.Dev.Protocol(t, this.World);
+            }
+        }
+
+        public override List<object> GetObjects()
+        {
+            return new List<object>();
+        }
+
+        public override void Update(List<WorkTimes> list)
+        {
+            var sw = new Stopwatch();
+            sw.Start();
+            var pack = new DeviceDataPack();
+            pack.Frame = DateTime.Now;
+            var ps = pack.GetType().GetProperties().OrderBy(x => x.Name);
+            foreach (var p in ps)
+            {
+                if (!p.PropertyType.IsArray&&p.PropertyType!= typeof(IBCR80[]))
+                    continue;
+                var dev = p.PropertyType.GetElementType();
+                if (dev.GetInterfaces().Any(v => v.GetInterfaces().Any(d => d.Name == "IProtocol")))
+                {
+                    var t = p.PropertyType.GetElementType();
+                    var protType = GetProtocolType(t);
+                    var arr = Device.All.Where(v => v.HasProtocol(protType))
+                    .Select(v =>
+                    {
+                        try
+                        {
+                            var obj = Activator.CreateInstance(t);
+                            t.GetProperty("Code").SetValue(obj, v.Code);
+                            dynamic protObj = v.Protocol(protType, World);
+                            if (protType == typeof(IBCR81))
+                            {
+                                var a = new Device<IBCR81>(v, World);
+                                var b = a.Data.Content;
+
+                            }
+                            var value = ServiceCenter.Extensions.TypeExtension.Copy(protObj, t);
+                            //t.GetProperty("Data").SetValue(obj, value);
+                            return obj;
+                        }
+                        catch (Exception ex)
+                        {
+                            return null;
+                        }
+                    }).Where(v => v != null).ToArray();
+
+                    var m = typeof(Enumerable).GetMethod("OfType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+                    m = m.MakeGenericMethod(t);
+                    var arr2 = m.Invoke(null, new object[] { arr });
+
+                    m = typeof(Enumerable).GetMethod("ToArray", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+                    m = m.MakeGenericMethod(t);
+                    var arr3 = m.Invoke(null, new object[] { arr2 });
+                    p.SetValue(pack, arr3);
+                }
+            }
+
+            //Redis.RPush("Packs", pack);
+            //if (Redis.LLen("Packs") > 50000)
+            //{
+            //    Redis.LTrim("Packs", 5000, -1);
+            //}
+
+            sw.Stop();
+            list.Add(new WorkTimes { Total = sw.ElapsedMilliseconds, Key = "采集数据" });
+            //var sw = new Stopwatch();
+            //sw.Start();
+            //var pack = new DeviceDataPack();
+            //pack.Frame = DateTime.Now;
+            //var ps = pack.GetType().GetProperties();
+            //foreach (var p in ps)
+            //{
+            //    if (!p.PropertyType.IsClass) continue;
+
+            //    var packAct = Activator.CreateInstance(p.PropertyType);
+            //    var prs = p.PropertyType.GetProperties();
+            //    foreach (var pr in prs)
+            //    {
+            //        if (!pr.PropertyType.IsArray) continue;
+
+            //        var yt = pr.PropertyType.GetElementType();
+            //        if (yt.IsClass)
+            //        {
+            //            var pros = yt.GetProperties();
+            //            //var entType = yt.GetGenericArguments()[0];
+            //            //var protType = GetProtocolType(entType);
+            //            var dataAct = Activator.CreateInstance(yt);
+            //            Parallel.ForEach(pros, pro =>
+            //            {
+            //                try
+            //                {
+            //                    if (pro.PropertyType != typeof(DateTime))
+            //                    {
+            //                        if (pro.PropertyType != typeof(string))
+            //                        {
+            //                            var protType = GetProtocolType(pro.PropertyType);
+            //                            var dev = Device.All
+            //                                .Where(v => v.HasProtocol(protType)).Select(v =>
+            //                                {
+            //                                    try
+            //                                    {
+            //                                        var obj = Activator.CreateInstance(pro.PropertyType);
+            //                                        pro.PropertyType.GetProperty("Code").SetValue(obj, v.Code);
+            //                                        var a = v.Protocol(protType, World);
+            //                                        var value = v.Protocol(protType, World).Copy(pro.PropertyType);
+            //                                        pro.SetValue(obj, value);
+            //                                        return obj;
+            //                                    }
+            //                                    catch (Exception ex)
+            //                                    {
+            //                                        return null;
+            //                                    }
+            //                                }).FirstOrDefault(v => v != null);
+
+            //                            if (dev != null)
+            //                            {
+            //                                pro.SetValue(dataAct, dev);
+            //                            }
+            //                        }
+            //                        else
+            //                        {
+            //                        }
+            //                    }
+            //                }
+            //                catch (Exception e)
+            //                {
+            //                    Console.WriteLine(e);
+            //                }
+            //            });
+
+            //            var a = 1;
+
+            //            //var m = typeof(Enumerable).GetMethod("OfType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //            //m = m.MakeGenericMethod(yt);
+            //            //var arr2 = m.Invoke(null, new object[] { datasAct });
+
+            //            //m = typeof(Enumerable).GetMethod("ToArray", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //            //m = m.MakeGenericMethod(yt);
+            //            //var arr3 = m.Invoke(null, new object[] { arr2 });
+            //            //p.SetValue(pack, arr3);
+
+            //            //var entType = yt.GetGenericArguments()[0];
+            //            //var protType = GetProtocolType(entType);
+            //            //var arr = Device.All.Where(v => v.HasProtocol(protType))
+            //            //    .Select(v =>
+            //            //    {
+            //            //        try
+            //            //        {
+            //            //            var obj = Activator.CreateInstance(yt);
+            //            //            yt.GetProperty("Code").SetValue(obj, v.Code);
+            //            //            //var value = v.Protocol(protType, World).Copy(entType);
+            //            //            //t.GetProperty("Data").SetValue(obj, value);
+            //            //            return obj;
+            //            //        }
+            //            //        catch (Exception ex)
+            //            //        {
+            //            //            return null;
+            //            //        }
+            //            //    }).Where(v => v != null).ToArray();
+
+            //            //var m = typeof(Enumerable).GetMethod("OfType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //            //m = m.MakeGenericMethod(yt);
+            //            //var arr2 = m.Invoke(null, new object[] { arr });
+
+            //            //m = typeof(Enumerable).GetMethod("ToArray", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //            //m = m.MakeGenericMethod(yt);
+            //            //var arr3 = m.Invoke(null, new object[] { arr2 });
+            //            //p.SetValue(pack, arr3);
+            //        }
+            //    }
+
+            //    //var t = p.PropertyType.GetElementType();
+            //    //if (t.IsGenericType)
+            //    //{
+            //    //    var entType = t.GetGenericArguments()[0];
+            //    //    var protType = GetProtocolType(entType);
+            //    //    var arr = Device.All.Where(v => v.HasProtocol(protType))
+            //    //    .Select(v =>
+            //    //    {
+            //    //        try
+            //    //        {
+            //    //            var obj = Activator.CreateInstance(t);
+            //    //            t.GetProperty("Code").SetValue(obj, v.Code);
+            //    //            //var value = v.Protocol(protType, World).Copy(entType);
+            //    //            //t.GetProperty("Data").SetValue(obj, value);
+            //    //            return obj;
+            //    //        }
+            //    //        catch (Exception ex)
+            //    //        {
+            //    //            return null;
+            //    //        }
+            //    //    }).Where(v => v != null).ToArray();
+
+            //    //    var m = typeof(Enumerable).GetMethod("OfType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //    //    m = m.MakeGenericMethod(t);
+            //    //    var arr2 = m.Invoke(null, new object[] { arr });
+
+            //    //    m = typeof(Enumerable).GetMethod("ToArray", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
+            //    //    m = m.MakeGenericMethod(t);
+            //    //    var arr3 = m.Invoke(null, new object[] { arr2 });
+            //    //    p.SetValue(pack, arr3);
+            //    //}
+            //}
+
+            ////Redis.RPush("Packs", pack);
+            ////if (Redis.LLen("Packs") > 50000)
+            ////{
+            ////    Redis.LTrim("Packs", 5000, -1);
+            ////}
+
+            //sw.Stop();
+            //list.Add(new WorkTimes { Total = sw.ElapsedMilliseconds, Key = "采集数据" });
+        }
+
+        private Type GetProtocolType(Type source)
+        {
+            var t = source.GetInterfaces().FirstOrDefault(v => v.GetInterfaces().Any(d => d.Name == "IProtocol"));
+            return t;
+        }
+    }
+}

+ 31 - 0
分拣中心数据采集.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.8.34525.116
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WCS.Service", "WCS.Service\WCS.Service.csproj", "{B850DA16-00E7-4557-93BD-CC48B63D413F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WCS.WorkEngineering", "WCS.WorkEngineering\WCS.WorkEngineering.csproj", "{8649A293-18DF-47CB-BB15-B16B0A8FB74F}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{B850DA16-00E7-4557-93BD-CC48B63D413F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B850DA16-00E7-4557-93BD-CC48B63D413F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B850DA16-00E7-4557-93BD-CC48B63D413F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B850DA16-00E7-4557-93BD-CC48B63D413F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8649A293-18DF-47CB-BB15-B16B0A8FB74F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8649A293-18DF-47CB-BB15-B16B0A8FB74F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8649A293-18DF-47CB-BB15-B16B0A8FB74F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8649A293-18DF-47CB-BB15-B16B0A8FB74F}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {321FE45C-63A6-41FA-99D4-7D7D6945A06D}
+	EndGlobalSection
+EndGlobal