Explorar el Código

添加项目文件。

林豪 左 hace 1 año
padre
commit
7aa93afbd0
Se han modificado 32 ficheros con 4773 adiciones y 0 borrados
  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. 34 0
      WCS.Service/WCS.Service.csproj
  7. 175 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. 705 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. 339 0
      WCS.WorkEngineering/Systems/DataCollectionSysyem.cs
  26. 698 0
      WCS.WorkEngineering/Systems/MainSysyem.cs
  27. 43 0
      WCS.WorkEngineering/WCS.WorkEngineering.csproj
  28. 533 0
      WCS.WorkEngineering/WorkStart.cs
  29. 19 0
      WCS.WorkEngineering/Worlds/DataWorld.cs
  30. 135 0
      WCS.WorkEngineering/Worlds/MainWorld.cs
  31. 252 0
      WCS.WorkEngineering/数据采集.cs
  32. 61 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;
+using MessagePack;
+
+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"
+      }
+    }
+  }
+}

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

@@ -0,0 +1,34 @@
+<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="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
+    <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" />
+  </ItemGroup>
+</Project>

+ 175 - 0
WCS.Service/Worker.cs

@@ -0,0 +1,175 @@
+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;
+using MessagePack.ImmutableCollection;
+using MessagePack.Resolvers;
+
+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, ISRM523>
+    {
+        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();
+        }
+    }
+}

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

@@ -0,0 +1,705 @@
+using ServiceCenter.Extensions;
+using ServiceCenter.Logs;
+using ServiceCenter.Redis;
+using ServiceCenter.SqlSugars;
+using SqlSugar;
+using SqlSugar.Extensions;
+using System.Text;
+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 工字轮支线分流
+
+
+        public static string ToSqlString(this QuestDBInsertBuilder questDb)
+        {
+            if (questDb.IsNoInsertNull)
+            {
+                questDb.DbColumnInfoList = questDb.DbColumnInfoList.Where(it => it.Value != null).ToList();
+            }
+            var groupList = questDb.DbColumnInfoList.GroupBy(it => it.TableId).ToList();
+            var isSingle = groupList.Count() == 1;
+            string columnsString = string.Join(",", groupList.First().Select(it => questDb.Builder.GetTranslationColumnName(it.DbColumnName)));
+            if (isSingle)
+            {
+                string columnParametersString = string.Join(",", questDb.DbColumnInfoList.Select(it =>
+                {
+                    var spk = questDb.Builder.SqlParameterKeyWord + it.DbColumnName;
+                    //if (it.Value is DateTime)
+                    //{
+                    //    return $"to_timestamp('{it.Value.ObjToString("yyyy-MM-ddTHH:mm:ss")}', 'yyyy-MM-ddTHH:mm:ss')";
+                    //}
+                    return questDb.GetDbColumn(it, spk);
+                }
+
+                ));
+                questDb.ActionMinDate();
+                return string.Format(questDb.SqlTemplate, questDb.GetTableNameString, columnsString, columnParametersString);
+            }
+            else
+            {
+                StringBuilder batchInsetrSql = new StringBuilder();
+                int pageSize = 200;
+                int pageIndex = 1;
+                int totalRecord = groupList.Count;
+                int pageCount = (totalRecord + pageSize - 1) / pageSize;
+                while (pageCount >= pageIndex)
+                {
+                    batchInsetrSql.AppendFormat(questDb.SqlTemplateBatch, questDb.GetTableNameString, columnsString);
+                    int i = 0;
+                    foreach (var columns in groupList.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList())
+                    {
+                        var isFirst = i == 0;
+                        if (isFirst)
+                        {
+                            batchInsetrSql.Append(questDb.SqlTemplateBatchUnion);
+                        }
+                        batchInsetrSql.Append("\r\n ( " + string.Join(",", columns.Select(it =>
+                        {
+                            if (it.InsertServerTime || it.InsertSql.HasValue())
+                            {
+                                return questDb.GetDbColumn(it, null);
+                            }
+                            object value = null;
+                            if (it.Value is DateTime)
+                            {
+                                return $" cast('{it.Value.ObjToDate().ToString("yyyy-MM-dd HH:mm:ss.ffffff")}' as timestamp)";
+                            }
+                            else if (it.Value is int || it.Value is long || it.Value is short || it.Value is short || it.Value is byte || it.Value is double)
+                            {
+                                return it.Value;
+                            }
+                            else if (it.Value is bool)
+                            {
+                                return it.Value.ObjToString().ToLower();
+                            }
+                            else
+                            {
+                                value = it.Value;
+                            }
+                            if (value == null || value == DBNull.Value)
+                            {
+                                return string.Format(questDb.SqlTemplateBatchSelect, "NULL");
+                            }
+                            return string.Format(questDb.SqlTemplateBatchSelect, "'" + value.ObjToString().ToSqlFilter() + "'");
+                        })) + "),");
+                        ++i;
+                    }
+                    pageIndex++;
+                    batchInsetrSql.Remove(batchInsetrSql.Length - 1, 1).Append("\r\n;\r\n");
+                }
+                return batchInsetrSql.ToString();
+            }
+        }
+
+        public static bool HasValue(this object thisValue)
+        {
+            if (thisValue == null || thisValue == DBNull.Value) return false;
+            return thisValue.ToString() != "";
+        }
+    }
+
+    /// <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();
+            });
+        }
+    }
+}

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

@@ -0,0 +1,339 @@
+using Newtonsoft.Json;
+using PlcSiemens.Core.Extension;
+using ServiceCenter.Extensions;
+using ServiceCenter.Logs;
+using ServiceCenter.Redis;
+using ServiceCenter.SqlSugars;
+using System.Collections.Concurrent;
+using System.ComponentModel;
+using System.Data.SqlTypes;
+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(DataWorld))]
+    [Description("数据采集系统")]
+    public class DataCollectionSysyem : SystemBase
+    {
+        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);
+            }
+        }
+
+        public override List<object> GetObjects()
+        {
+            return new List<object>();
+        }
+
+        public override void Update(List<WorkTimes> list)
+        {
+            try
+            {
+                var sw = new Stopwatch();
+                sw.Start();
+
+                #region 处理数据
+
+                var sw1 = new Stopwatch();
+                sw1.Start();
+                var pack = new DeviceDataPack();
+                var frame = DateTime.Now;
+                pack.Frame = World.Frame;
+                var ps = pack.GetType().GetProperties().OrderBy(x => x.Name);
+                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, TimeZoneInfo.ConvertTimeToUtc(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);
+                    }
+                });
+                sw1.Stop();
+
+                #endregion 处理数据
+
+                #region 存储监控数据
+
+                var sw2 = new Stopwatch();
+                sw2.Start();
+
+                //开始存储设备信息
+                RedisHub.Monitor.RPush("Packs", pack);
+                if (RedisHub.Monitor.LLen("Packs") > 70000)
+                {
+                    RedisHub.Monitor.LTrim("Packs", 5000, -1);
+                }
+                sw2.Stop();
+
+                #endregion 存储监控数据
+
+                #region 存储设备报警信息
+
+                var sw3 = new Stopwatch();
+                sw3.Start();
+                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.SRM523.Where(x => x.Data.Alarm1 != 0 || x.Data.Alarm2 != 0).Select(x => new EquipmentAlarm()
+                {
+                    Code = x.Code,
+                    Msg = $"{Convert.ToString(x.Data.Alarm1)},{Convert.ToString(x.Data.Alarm2)}",
+                    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));
+                sw3.Stop();
+
+                #endregion 存储设备报警信息
+
+                #region 存储设备状态信息
+
+                var sw4 = new Stopwatch();
+                sw4.Start();
+                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));
+                sw4.Stop();
+
+                #endregion 存储设备状态信息
+
+                #region 存储数采数据
+
+                var sw5 = new Stopwatch();
+                sw5.Start();
+                RedisHub.Monitor.RPush("DataCollectionpacks", pack);
+                sw5.Stop();
+
+                #endregion 存储数采数据
+
+                sw.Stop();
+
+                World.Log($"业务耗时:[{sw.ElapsedMilliseconds}]--处理数据:[{sw1.ElapsedMilliseconds}]--存储监控数据:[{sw2.ElapsedMilliseconds}]--存储设备报警信息:[{sw3.ElapsedMilliseconds}]--存储设备状态数据:[{sw4.ElapsedMilliseconds}]--存储数采数据:[{sw5.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; }
+    }
+}

+ 698 - 0
WCS.WorkEngineering/Systems/MainSysyem.cs

@@ -0,0 +1,698 @@
+using Newtonsoft.Json;
+using PlcSiemens.Core.Extension;
+using ServiceCenter.Extensions;
+using ServiceCenter.Logs;
+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 MainSysyem : SystemBase
+    {
+        public override void Update(List<WorkTimes> list)
+        {
+            var sql = new StringBuilder();
+            try
+            {
+                var sw = new Stopwatch();
+                sw.Start();
+
+                var number = new List<int>();
+                number.AddRange(new[] { 1, 2, 3, 4, 5 });
+                Parallel.ForEach(number, n =>
+                {
+                    var db = new SqlSugarHelper().PLC;
+                    var pack = RedisHub.Monitor.LPop<DeviceDataPack>("DataCollectionpacks"); ;
+                    if (pack == null) return;
+
+                    Parallel.ForEach(pack.GetType().GetProperties().OrderBy(x => x.Name), ps =>
+                    {
+                        var sw10 = new Stopwatch();
+                        sw10.Start();
+                        var typeName = "";
+                        try
+                        {
+                            //if (ps.PropertyType == typeof(ProtocolData<WCS_BCR80>[]))
+                            //{
+                            //    if (pack.BCR80.Any())
+                            //    {
+                            //        var value = pack.BCR80.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_BCR80).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_BCR81>[]))
+                            //{
+                            //    if (pack.BCR81.Any())
+                            //    {
+                            //        var value = pack.BCR81.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_BCR81).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_BCR83>[]))
+                            //{
+                            //    if (pack.BCR83.Any())
+                            //    {
+                            //        var value = pack.BCR83.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_BCR83).Name;
+                            //    }
+                            //}
+                            if (ps.PropertyType == typeof(ProtocolData<WCS_RGV520>[]))
+                            {
+                                if (pack.RGV520.Any())
+                                {
+                                    var value = pack.RGV520.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_RGV520).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_RGV521>[]))
+                            {
+                                if (pack.RGV521.Any())
+                                {
+                                    var value = pack.RGV521.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_RGV521).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_RGV523>[]))
+                            {
+                                if (pack.RGV523.Any())
+                                {
+                                    var value = pack.RGV523.Select(x => new QuestDb_RGV523()
+                                    {
+                                        Alarm = Convert.ToString(x.Data.Alarm),
+                                        Code = x.Data.Code,
+                                        Frame = x.Data.Frame.ToLocalTime()
+                                    }).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(GetString(sql));
+                                    typeName = typeof(WCS_RGV523).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot520>[]))
+                            {
+                                if (pack.Robot520.Any())
+                                {
+                                    var value = pack.Robot520.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_Robot520).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot521>[]))
+                            {
+                                if (pack.Robot521.Any())
+                                {
+                                    var value = pack.Robot521.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_Robot521).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot522>[]))
+                            {
+                                if (pack.Robot522.Any())
+                                {
+                                    var value = pack.Robot522.Select(x => new QuestDb_Robot522()
+                                    {
+                                        Alarm = Convert.ToString(x.Data.Alarm),
+                                        Code = x.Data.Code,
+                                        Frame = x.Data.Frame.ToLocalTime()
+                                    }).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_Robot522).Name;
+                                }
+                            }
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot530>[]))
+                            //{
+                            //    if (pack.Robot530.Any())
+                            //    {
+                            //        var value = pack.Robot530.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Robot530).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot531>[]))
+                            //{
+                            //    if (pack.Robot531.Any())
+                            //    {
+                            //        var value = pack.Robot531.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Robot531).Name;
+                            //    }
+                            //}
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM520>[]))
+                            {
+                                if (pack.SRM520.Any())
+                                {
+                                    var value = pack.SRM520.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_SRM520).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM521>[]))
+                            {
+                                if (pack.SRM521.Any())
+                                {
+                                    var value = pack.SRM521.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_SRM521).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM523>[]))
+                            {
+                                if (pack.SRM523.Any())
+                                {
+                                    var value = pack.SRM523.Select(x => new QuestDb_SRM523()
+                                    {
+                                        Alarm = $"{Convert.ToString(x.Data.Alarm1)},{Convert.ToString(x.Data.Alarm1)}",
+                                        Code = x.Data.Code,
+                                        Frame = x.Data.Frame.ToLocalTime()
+                                    }).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(GetString(sql));
+                                    typeName = typeof(WCS_SRM523).Name;
+                                }
+                            }
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Station520>[]))
+                            //{
+                            //    if (pack.Station520.Any())
+                            //    {
+                            //        var value = pack.Station520.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Station520).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Station521>[]))
+                            //{
+                            //    if (pack.Station521.Any())
+                            //    {
+                            //        var value = pack.Station521.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Station521).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Station523>[]))
+                            //{
+                            //    if (pack.Station523.Any())
+                            //    {
+                            //        var value = pack.Station523.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Station523).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Station524>[]))
+                            //{
+                            //    if (pack.Station524.Any())
+                            //    {
+                            //        var value = pack.Station524.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Station524).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Station525>[]))
+                            //{
+                            //    if (pack.Station525.Any())
+                            //    {
+                            //        var value = pack.Station525.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Station525).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Station90>[]))
+                            //{
+                            //    if (pack.Station90.Any())
+                            //    {
+                            //        var value = pack.Station90.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Station90).Name;
+                            //    }
+                            //}
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_Station91>[]))
+                            {
+                                if (pack.Station91.Any())
+                                {
+                                    var value = pack.Station91.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_Station91).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss520>[]))
+                            {
+                                if (pack.Truss520.Any())
+                                {
+                                    var value = pack.Truss520.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_Truss520).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss521>[]))
+                            {
+                                if (pack.Truss521.Any())
+                                {
+                                    var value = pack.Truss521.Select(x => x.Data).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_Truss521).Name;
+                                }
+                            }
+                            else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss523>[]))
+                            {
+                                if (pack.Truss523.Any())
+                                {
+                                    var value = pack.Truss523.Select(x => new QuestDb_Truss523()
+                                    {
+                                        Alarm = Convert.ToString(x.Data.Alarm),
+                                        Code = x.Data.Code,
+                                        Frame = x.Data.Frame.ToLocalTime()
+                                    }).ToList();
+                                    var sql = db.Insertable(value).ToSqlString();
+                                    db.Ado.ExecuteCommand(sql);
+                                    typeName = typeof(WCS_Truss523).Name;
+                                }
+                            }
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss530>[]))
+                            //{
+                            //    if (pack.Truss530.Any())
+                            //    {
+                            //        var value = pack.Truss530.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Truss530).Name;
+                            //    }
+                            //}
+                            //else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss531>[]))
+                            //{
+                            //    if (pack.Truss531.Any())
+                            //    {
+                            //        var value = pack.Truss531.Select(x => x.Data).ToList();
+                            //        var sql = db.Insertable(value).ToSqlString();
+                            //        db.Ado.ExecuteCommand(sql);
+                            //        typeName = typeof(WCS_Truss531).Name;
+                            //    }
+                            //}
+                        }
+                        catch (Exception e)
+                        {
+                            World.Log($"错误内容:{e.Message}");
+                        }
+                        finally
+                        {
+                            sw10.Stop();
+                            World.Log($"执行耗时:{typeName}:{sw10.ElapsedMilliseconds}");
+                        }
+                    });
+                });
+
+                //var db = new SqlSugarHelper().PLC;
+                //var pack = RedisHub.Monitor.LPop<DeviceDataPack>("DataCollectionpacks"); ;
+                //if (pack == null) return;
+
+                //Parallel.ForEach(pack.GetType().GetProperties().OrderBy(x => x.Name), ps =>
+                //{
+                //    var sw10 = new Stopwatch();
+                //    sw10.Start();
+                //    var typeName = "";
+                //    try
+                //    {
+                //        //if (ps.PropertyType == typeof(ProtocolData<WCS_BCR80>[]))
+                //        //{
+                //        //    if (pack.BCR80.Any())
+                //        //    {
+                //        //        var value = pack.BCR80.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_BCR80).Name;
+                //        //    }
+                //        //}
+                //        //else if (ps.PropertyType == typeof(ProtocolData<WCS_BCR81>[]))
+                //        //{
+                //        //    if (pack.BCR81.Any())
+                //        //    {
+                //        //        var value = pack.BCR81.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_BCR81).Name;
+                //        //    }
+                //        //}
+                //        //else if (ps.PropertyType == typeof(ProtocolData<WCS_BCR83>[]))
+                //        //{
+                //        //    if (pack.BCR83.Any())
+                //        //    {
+                //        //        var value = pack.BCR83.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_BCR83).Name;
+                //        //    }
+                //        //}
+                //        if (ps.PropertyType == typeof(ProtocolData<WCS_RGV520>[]))
+                //        {
+                //            if (pack.RGV520.Any())
+                //            {
+                //                var value = pack.RGV520.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_RGV520).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_RGV521>[]))
+                //        {
+                //            if (pack.RGV521.Any())
+                //            {
+                //                var value = pack.RGV521.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_RGV521).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_RGV523>[]))
+                //        {
+                //            if (pack.RGV523.Any())
+                //            {
+                //                var value = pack.RGV523.Select(x => new QuestDb_RGV523()
+                //                {
+                //                    Alarm = Convert.ToString(x.Data.Alarm),
+                //                    Code = x.Data.Code,
+                //                    Frame = x.Data.Frame.ToLocalTime()
+                //                }).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(GetString(sql));
+                //                typeName = typeof(WCS_RGV523).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot520>[]))
+                //        {
+                //            if (pack.Robot520.Any())
+                //            {
+                //                var value = pack.Robot520.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Robot520).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot521>[]))
+                //        {
+                //            if (pack.Robot521.Any())
+                //            {
+                //                var value = pack.Robot521.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Robot521).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot522>[]))
+                //        {
+                //            if (pack.Robot522.Any())
+                //            {
+                //                var value = pack.Robot522.Select(x => new QuestDb_Robot522()
+                //                {
+                //                    Alarm = Convert.ToString(x.Data.Alarm),
+                //                    Code = x.Data.Code,
+                //                    Frame = x.Data.Frame.ToLocalTime()
+                //                }).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Robot522).Name;
+                //            }
+                //        }
+                //        //else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot530>[]))
+                //        //{
+                //        //    if (pack.Robot530.Any())
+                //        //    {
+                //        //        var value = pack.Robot530.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_Robot530).Name;
+                //        //    }
+                //        //}
+                //        //else if (ps.PropertyType == typeof(ProtocolData<WCS_Robot531>[]))
+                //        //{
+                //        //    if (pack.Robot531.Any())
+                //        //    {
+                //        //        var value = pack.Robot531.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_Robot531).Name;
+                //        //    }
+                //        //}
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM520>[]))
+                //        {
+                //            if (pack.SRM520.Any())
+                //            {
+                //                var value = pack.SRM520.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_SRM520).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM521>[]))
+                //        {
+                //            if (pack.SRM521.Any())
+                //            {
+                //                var value = pack.SRM521.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_SRM521).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_SRM523>[]))
+                //        {
+                //            if (pack.SRM523.Any())
+                //            {
+                //                var value = pack.SRM523.Select(x => new QuestDb_SRM523()
+                //                {
+                //                    Alarm = $"{Convert.ToString(x.Data.Alarm1)},{Convert.ToString(x.Data.Alarm1)}",
+                //                    Code = x.Data.Code,
+                //                    Frame = x.Data.Frame.ToLocalTime()
+                //                }).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(GetString(sql));
+                //                typeName = typeof(WCS_SRM523).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station520>[]))
+                //        {
+                //            if (pack.Station520.Any())
+                //            {
+                //                var value = pack.Station520.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Station520).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station521>[]))
+                //        {
+                //            if (pack.Station521.Any())
+                //            {
+                //                var value = pack.Station521.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Station521).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station523>[]))
+                //        {
+                //            if (pack.Station523.Any())
+                //            {
+                //                var value = pack.Station523.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Station523).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station524>[]))
+                //        {
+                //            if (pack.Station524.Any())
+                //            {
+                //                var value = pack.Station524.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Station524).Name;
+                //            }
+                //        }
+                //        //else if (ps.PropertyType == typeof(ProtocolData<WCS_Station525>[]))
+                //        //{
+                //        //    if (pack.Station525.Any())
+                //        //    {
+                //        //        var value = pack.Station525.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_Station525).Name;
+                //        //    }
+                //        //}
+                //        //else if (ps.PropertyType == typeof(ProtocolData<WCS_Station90>[]))
+                //        //{
+                //        //    if (pack.Station90.Any())
+                //        //    {
+                //        //        var value = pack.Station90.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_Station90).Name;
+                //        //    }
+                //        //}
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Station91>[]))
+                //        {
+                //            if (pack.Station91.Any())
+                //            {
+                //                var value = pack.Station91.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Station91).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss520>[]))
+                //        {
+                //            if (pack.Truss520.Any())
+                //            {
+                //                var value = pack.Truss520.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Truss520).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss521>[]))
+                //        {
+                //            if (pack.Truss521.Any())
+                //            {
+                //                var value = pack.Truss521.Select(x => x.Data).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Truss521).Name;
+                //            }
+                //        }
+                //        else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss523>[]))
+                //        {
+                //            if (pack.Truss523.Any())
+                //            {
+                //                var value = pack.Truss523.Select(x => new QuestDb_Truss523()
+                //                {
+                //                    Alarm = Convert.ToString(x.Data.Alarm),
+                //                    Code = x.Data.Code,
+                //                    Frame = x.Data.Frame.ToLocalTime()
+                //                }).ToList();
+                //                var sql = db.Insertable(value).ToSqlString();
+                //                db.Ado.ExecuteCommand(sql);
+                //                typeName = typeof(WCS_Truss523).Name;
+                //            }
+                //        }
+                //        //else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss530>[]))
+                //        //{
+                //        //    if (pack.Truss530.Any())
+                //        //    {
+                //        //        var value = pack.Truss530.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_Truss530).Name;
+                //        //    }
+                //        //}
+                //        //else if (ps.PropertyType == typeof(ProtocolData<WCS_Truss531>[]))
+                //        //{
+                //        //    if (pack.Truss531.Any())
+                //        //    {
+                //        //        var value = pack.Truss531.Select(x => x.Data).ToList();
+                //        //        var sql = db.Insertable(value).ToSqlString();
+                //        //        db.Ado.ExecuteCommand(sql);
+                //        //        typeName = typeof(WCS_Truss531).Name;
+                //        //    }
+                //        //}
+                //    }
+                //    catch (Exception e)
+                //    {
+                //        World.Log($"错误内容:{e.Message}");
+                //    }
+                //    finally
+                //    {
+                //        sw10.Stop();
+                //        World.Log($"执行耗时:{typeName}:{sw10.ElapsedMilliseconds}");
+                //    }
+                //});
+
+
+
+
+
+
+
+
+                sw.Stop();
+                World.Log($"数据处理耗时:{sw.ElapsedMilliseconds}");
+            }
+            catch (Exception e)
+            {
+                World.Log($"错误内容:{e.Message}");
+            }
+        }
+
+        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(",N'", ",'")
+                .Replace("\0", "")
+                .Replace("(N'", "('") + "\r";
+        }
+
+        public override List<object> GetObjects()
+        {
+            return new List<object>();
+        }
+    }
+}

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

@@ -0,0 +1,43 @@
+<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.140" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\ZTGT-FJZX\ServiceCenter\ServiceCenter.csproj" />
+    <ProjectReference Include="..\..\ZTGT-FJZX\业务工程\分拣库\WCS.Entity.Protocol\WCS.Entity.Protocol.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>

+ 533 - 0
WCS.WorkEngineering/WorkStart.cs

@@ -0,0 +1,533 @@
+using PlcSiemens.Core.Extension;
+using ServiceCenter;
+using ServiceCenter.Logs;
+using ServiceCenter.Redis;
+using ServiceCenter.SqlSugars;
+using System.Diagnostics;
+using WCS.Core;
+using WCS.Entity.Protocol;
+using WCS.Entity.Protocol.BCR;
+using WCS.Entity.Protocol.DataStructure;
+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 初始化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<IRGV523>(0, 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, 522, 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>(){ 1741, 1736 }), //库三南
+            };
+
+            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<ISRM523>(0, 523, $"10.30.37.{ip}");
+
+                //增加巷道
+                var tunnel = new Device($"TY{i + 1}");
+                tunnel.AddFlag(DeviceFlags.巷道);
+            }
+
+            #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基本数据库
+
+                            break;
+
+                        case "WCSDlog"://WCS日志数据库
+
+                            break;
+
+                        case "PLC"://PLC
+
+                            SqlSugarHelper.SetPLC(connectionString.Key);
+
+                            _db.CodeFirst.InitTables<WCS_SRM520>();
+                            _db.CodeFirst.InitTables<WCS_SRM521>();
+                            _db.CodeFirst.InitTables<QuestDb_SRM523>();
+                            _db.CodeFirst.InitTables<WCS_RGV520>();
+                            _db.CodeFirst.InitTables<WCS_RGV521>();
+                            _db.CodeFirst.InitTables<QuestDb_RGV523>();
+                            _db.CodeFirst.InitTables<WCS_BCR80>();
+                            _db.CodeFirst.InitTables<WCS_BCR81>();
+                            _db.CodeFirst.InitTables<WCS_Station520>();
+                            _db.CodeFirst.InitTables<WCS_Station521>();
+                            _db.CodeFirst.InitTables<WCS_Station523>();
+                            _db.CodeFirst.InitTables<WCS_Station524>();
+                            _db.CodeFirst.InitTables<WCS_Station91>();
+                            _db.CodeFirst.InitTables<WCS_Truss520>();
+                            _db.CodeFirst.InitTables<WCS_Truss521>();
+                            _db.CodeFirst.InitTables<QuestDb_Truss523>();
+                            _db.CodeFirst.InitTables<WCS_Robot520>();
+                            _db.CodeFirst.InitTables<WCS_Robot521>();
+                            _db.CodeFirst.InitTables<QuestDb_Robot522>();
+                            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; }
+    }
+}

+ 19 - 0
WCS.WorkEngineering/Worlds/DataWorld.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WCS.WorkEngineering.Worlds
+{
+    [Description("数据采集世界")]
+    public class DataWorld : MainWorld
+    {
+        /// <summary>
+        ///  世界执行周期间隔
+        ///  单位:毫秒
+        /// </summary>
+        protected override int Interval => 500;
+    }
+}

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

@@ -0,0 +1,135 @@
+using ServiceCenter.Logs;
+using System.Collections.Concurrent;
+using System.ComponentModel;
+using System.Diagnostics;
+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 => 300;
+
+        /// <summary>
+        ///  更新前执行,重写改方法后请自行添加执行内容
+        ///  执行内容:清空日志队列
+        /// </summary>
+        protected override void BeforeUpdate(List<WorkTimes> list)
+        {
+            // 清空日志队列,确保日志队列中只会有当前周期日志
+            Logs.Clear();
+        }
+
+        /// <summary>
+        /// 更新后执行,重写改方法后请自行添加执行内容
+        /// 执行内容:清空日志队列
+        /// </summary>
+        protected override void AfterUpdate(List<WorkTimes> list)
+        {
+            //LogHub.WorldPublish(Logs, this.GetType().Name);
+            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;
+        }
+    }
+}

+ 61 - 0
合金库数据采集.sln

@@ -0,0 +1,61 @@
+
+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
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlSugar", "..\..\..\..\Code\SqlSugar\Src\Asp.NetCore2\SqlSugar\SqlSugar.csproj", "{D81E04A6-920F-4F37-9455-8C2CE26BEFF6}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceCenter", "..\ZTGT-FJZX\ServiceCenter\ServiceCenter.csproj", "{A603B4B8-05A0-4D22-9B41-F5171361BC73}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WCS.Core", "..\ZTGT-FJZX\WCS.Core\WCS.Core.csproj", "{ECAE9926-E222-4562-9DF4-254617D01E51}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WCS.Entity", "..\ZTGT-FJZX\WCS.Entity\WCS.Entity.csproj", "{068A6B12-E558-4536-B695-2C4EBBF29D45}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WCS.Entity.Protocol", "..\ZTGT-FJZX\业务工程\分拣库\WCS.Entity.Protocol\WCS.Entity.Protocol.csproj", "{71C89A1F-8FA0-4A96-ADD3-B8E09737817D}"
+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
+		{D81E04A6-920F-4F37-9455-8C2CE26BEFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D81E04A6-920F-4F37-9455-8C2CE26BEFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D81E04A6-920F-4F37-9455-8C2CE26BEFF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D81E04A6-920F-4F37-9455-8C2CE26BEFF6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A603B4B8-05A0-4D22-9B41-F5171361BC73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A603B4B8-05A0-4D22-9B41-F5171361BC73}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A603B4B8-05A0-4D22-9B41-F5171361BC73}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A603B4B8-05A0-4D22-9B41-F5171361BC73}.Release|Any CPU.Build.0 = Release|Any CPU
+		{ECAE9926-E222-4562-9DF4-254617D01E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{ECAE9926-E222-4562-9DF4-254617D01E51}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{ECAE9926-E222-4562-9DF4-254617D01E51}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{ECAE9926-E222-4562-9DF4-254617D01E51}.Release|Any CPU.Build.0 = Release|Any CPU
+		{068A6B12-E558-4536-B695-2C4EBBF29D45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{068A6B12-E558-4536-B695-2C4EBBF29D45}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{068A6B12-E558-4536-B695-2C4EBBF29D45}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{068A6B12-E558-4536-B695-2C4EBBF29D45}.Release|Any CPU.Build.0 = Release|Any CPU
+		{71C89A1F-8FA0-4A96-ADD3-B8E09737817D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{71C89A1F-8FA0-4A96-ADD3-B8E09737817D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{71C89A1F-8FA0-4A96-ADD3-B8E09737817D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{71C89A1F-8FA0-4A96-ADD3-B8E09737817D}.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