林豪 左 2 years ago
parent
commit
3980783b04
39 changed files with 1959 additions and 72 deletions
  1. 1 1
      分拣库/WCS.Entity.Protocol/Station/IStation523.cs
  2. 1 1
      分拣库/WCS.Entity.Protocol/Station/WCS_Station523.cs
  3. 4 0
      分拣库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj
  4. 179 1
      分拣库/WCS.WorkEngineering/Extensions/DeviceExtension.cs
  5. 116 0
      分拣库/WCS.WorkEngineering/Extensions/TaskExtension.cs
  6. 29 0
      分拣库/WCS.WorkEngineering/Systems/MainLineScanCode.cs
  7. 290 0
      分拣库/WCS.WorkEngineering/Systems/SrmSystems.cs
  8. 4 0
      分拣库/WCS.WorkEngineering/WCS.WorkEngineering.csproj
  9. 43 0
      分拣库/WCS.WorkEngineering/WebApi/Controllers/AgvController.cs
  10. 108 9
      分拣库/WCS.WorkEngineering/Worlds/MainWorld.cs
  11. 12 0
      分拣库/WCS.WorkEngineering/Worlds/SortingMainLineWorld.cs
  12. 1 1
      合金库/WCS.Entity.Protocol/Station/IStation523.cs
  13. 2 0
      合金库/WCS.Entity.Protocol/Station/IStation91.cs
  14. 1 1
      合金库/WCS.Entity.Protocol/Station/WCS_Station523.cs
  15. 1 1
      合金库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj
  16. 179 2
      合金库/WCS.WorkEngineering/Extensions/DeviceExtension.cs
  17. 1 1
      合金库/WCS.WorkEngineering/WCS.WorkEngineering.csproj
  18. 108 9
      合金库/WCS.WorkEngineering/Worlds/MainWorld.cs
  19. 1 1
      成品库/WCS.Entity.Protocol/Station/IStation523.cs
  20. 2 0
      成品库/WCS.Entity.Protocol/Station/IStation91.cs
  21. 1 1
      成品库/WCS.Entity.Protocol/Station/WCS_Station523.cs
  22. 1 1
      成品库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj
  23. 179 2
      成品库/WCS.WorkEngineering/Extensions/DeviceExtension.cs
  24. 1 1
      成品库/WCS.WorkEngineering/WCS.WorkEngineering.csproj
  25. 108 9
      成品库/WCS.WorkEngineering/Worlds/MainWorld.cs
  26. 1 1
      时效库/WCS.Entity.Protocol/Station/IStation523.cs
  27. 2 0
      时效库/WCS.Entity.Protocol/Station/IStation91.cs
  28. 1 1
      时效库/WCS.Entity.Protocol/Station/WCS_Station523.cs
  29. 1 1
      时效库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj
  30. 179 2
      时效库/WCS.WorkEngineering/Extensions/DeviceExtension.cs
  31. 1 1
      时效库/WCS.WorkEngineering/WCS.WorkEngineering.csproj
  32. 108 9
      时效库/WCS.WorkEngineering/Worlds/MainWorld.cs
  33. 1 1
      盘条库/WCS.Entity.Protocol/Station/IStation523.cs
  34. 2 0
      盘条库/WCS.Entity.Protocol/Station/IStation91.cs
  35. 1 1
      盘条库/WCS.Entity.Protocol/Station/WCS_Station523.cs
  36. 1 1
      盘条库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj
  37. 179 2
      盘条库/WCS.WorkEngineering/Extensions/DeviceExtension.cs
  38. 1 1
      盘条库/WCS.WorkEngineering/WCS.WorkEngineering.csproj
  39. 108 9
      盘条库/WCS.WorkEngineering/Worlds/MainWorld.cs

+ 1 - 1
分拣库/WCS.Entity.Protocol/Station/IStation523.cs

@@ -5,7 +5,7 @@ namespace WCS.Entity.Protocol.Station
     /// <summary>
     /// 输送机状态读取协议
     /// </summary>
-    public interface IStation522 : IProtocol
+    public interface IStation523 : IProtocol
     {
         /// <summary>
         /// 警报

+ 1 - 1
分拣库/WCS.Entity.Protocol/Station/WCS_Station523.cs

@@ -8,7 +8,7 @@ namespace WCS.Entity.Protocol.Station
     /// </summary>
     [SugarTable(nameof(WCS_Station523), "站台状态读取记录表")]
     [DataContract]
-    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation522
+    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation523
     {
         /// <summary>
         /// 警报

+ 4 - 0
分拣库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj

@@ -7,7 +7,11 @@
   </PropertyGroup>
 
   <ItemGroup>
+<<<<<<< Updated upstream
     <PackageReference Include="WCS.Entity" Version="1.0.0.9" />
+=======
+    <PackageReference Include="WCS.Entity" Version="1.0.1.4" />
+>>>>>>> Stashed changes
   </ItemGroup>
 
 </Project>

+ 179 - 1
分拣库/WCS.WorkEngineering/Extensions/DeviceExtension.cs

@@ -8,9 +8,187 @@ namespace WCS.WorkEngineering.Extensions
     /// </summary>
     public static class DeviceExtension
     {
+        /// <summary>
+        ///  是否是输送线
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
         public static bool IsConv(this Device source)
         {
-            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation522));
+            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation523));
         }
+
+        /// <summary>
+        ///  是否是巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static bool IsTunnel(this WCS_DeviceInfo source)
+        {
+            return source.Code.Contains("TY");
+        }
+
+        /// <summary>
+        ///  获取下一个地址
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetNextAdd(this WCS_DeviceInfo source, string addto)
+        {
+            var path = source.Paths.First(p => p.EndCode == addto).Path;
+            return path.Split('-')[0];
+        }
+
+        /// <summary>
+        ///  获取到达目标地址需要的路径信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetAddtoPath(this WCS_DeviceInfo source, string addto)
+        {
+            return source.Paths.First(p => p.EndCode == addto).Path;
+        }
+<<<<<<< Updated upstream
+=======
+
+        #region WCS_DEVICE扩展数据
+
+        /// <summary>
+        ///  设备标识字典
+        /// </summary>
+        private static ConcurrentDictionary<string, object> DeviceValues = new ConcurrentDictionary<string, object>();
+
+        /// <summary>
+        /// 添加设备标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        public static void AddFlag(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            df = df | flag;
+            source.Set("DeviceFlag", df);
+        }
+
+        /// <summary>
+        ///  当前设备是否包含标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        /// <returns></returns>
+        public static bool Is(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            return (df & flag) == flag;
+        }
+
+        /// <summary>
+        ///  为设备写入标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <param name="value">写入值</param>
+        public static void Set<T>(this Device source, string key, T value)
+        {
+            DeviceValues[source.Code + key] = value;
+        }
+
+        /// <summary>
+        ///  获取设备标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <returns></returns>
+        public static T Get<T>(this Device source, string key)
+        {
+            if (!DeviceValues.ContainsKey(source.Code + key))
+                return default(T);
+            return (T)DeviceValues[source.Code + key];
+        }
+
+        /// <summary>
+        ///  获取short类型的设备号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static short Code(this Device source)
+        {
+            return short.Parse(source.Code);
+        }
+
+        /// <summary>
+        ///  获取巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道Code</returns>
+        public static string Tunnel(this Device source)
+        {
+            return source.Get<string>("Tunnel");
+        }
+
+        /// <summary>
+        ///  获取巷道号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道号</returns>
+        public static int TunnelNum(this Device source)
+        {
+            return int.Parse(source.Tunnel().Last().ToString());
+        }
+
+        /// <summary>
+        ///  获取设备楼层信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static int Floor(this Device source)
+        {
+            return source.Get<int>("Floor");
+        }
+
+        /// <summary>
+        ///  获取堆垛机
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device SRM(this Device source)
+        {
+            return source.Get<Device>("SRM");
+        }
+
+        /// <summary>
+        ///  获取RGV
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device RGV(this Device source)
+        {
+            return source.Get<Device>("RGV");
+        }
+
+        //public static void AddFlag(DF flag, params string[] devices)
+        //{
+        //    var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(v => devices.Contains(v.CODE)).ToArray();
+        //    Parallel.ForEach(arr, v =>
+        //    {
+        //        v.AddFlag(flag);
+        //    });
+        //}
+
+        #endregion WCS_DEVICE扩展数据
+    }
+
+    /// <summary>
+    /// 设备配置
+    /// </summary>
+    [Flags]
+    public enum DF
+    {
+        无 = 0,
+>>>>>>> Stashed changes
     }
 }

+ 116 - 0
分拣库/WCS.WorkEngineering/Extensions/TaskExtension.cs

@@ -0,0 +1,116 @@
+using ServiceCenter.SqlSugars;
+using WCS.Core;
+using WCS.Entity;
+using WCS.WorkEngineering.Worlds.Logs;
+
+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, SqlSugarHelper db, string curPoint, string desc)
+        {
+            db.Default.Insertable(new WCS_TaskDtl
+            {
+                ParentTaskCode = task.ID,
+                CurPoint = curPoint,
+                NextPoint = "",
+                Desc = desc,
+                AddWho = "WCS"
+            }).AddQueue();
+        }
+
+        /// <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, SqlSugarHelper db, string curPoint, string NextPoint, string desc)
+        {
+            db.Default.Insertable(new WCS_TaskDtl
+            {
+                ParentTaskCode = task.ID,
+                CurPoint = curPoint,
+                NextPoint = NextPoint,
+                Desc = desc,
+                AddWho = "WCS"
+            }).AddQueue();
+        }
+
+        /// <summary>
+        /// 完成或取消任务
+        /// </summary>
+        /// <param name="taskInfo"></param>
+        /// <param name="db"></param>
+        public static void CompleteOrCancelTasks(this WCS_TaskInfo taskInfo, SqlSugarHelper db, World world)
+        {
+            if (taskInfo.Status is not Entity.TaskStatus.Finish and not Entity.TaskStatus.Cancel)
+            {
+                world.Log("任务未完成或取消,无法执行WCS_TaskInfo与WCS_TaskOld同步动作", LogLevelEnum.Mid, ErrorTypeEnum.Kown, LogUpLoadEnum.NotUpLoad);
+            }
+            else // 任务完成或取消,进行相关同步动作
+            {
+                WCS_TaskOld taskOld = db.Default.Queryable<WCS_TaskOld>().Where(v => v.WcsTaskID == taskInfo.ID).OrderByDescending(v => v.AddTime).First();
+                if (taskOld is null)
+                {
+                    world.Log($"WCS_TaskOld表中不存在任务:{taskInfo.ID},无法执行WCS_TaskInfo与WCS_TaskOld同步动作", LogLevelEnum.Mid, ErrorTypeEnum.Kown, LogUpLoadEnum.UpLoadWMS);
+                }
+                else
+                {
+                    //状态
+                    taskOld.Status = taskInfo.Status;
+                    taskOld.Uploaded = taskInfo.Uploaded;
+                    //任务号
+                    taskOld.WmsTask = taskInfo.WmsTask;
+                    taskOld.AgvTaskID = taskInfo.AgvTaskID;
+                    taskOld.TaskGroupKey = taskInfo.TaskGroupKey;
+                    taskOld.Priority = taskInfo.Priority;
+                    //时间与用户
+                    taskOld.AddTime = taskInfo.AddTime;
+                    taskOld.AddWho = taskInfo.AddWho;
+                    taskOld.StartTime = taskInfo.StartTime;
+                    taskOld.EedTime = taskInfo.EedTime;
+                    taskOld.EditWho = taskInfo.EditWho;
+                    taskOld.EditTime = taskInfo.EditTime;
+                    //条码与地址
+                    taskOld.BarCode = taskInfo.BarCode;
+                    taskOld.MaterialCode = taskInfo.MaterialCode;
+                    taskOld.AddrFrom = taskInfo.AddrFrom;
+                    taskOld.AddrTo = taskInfo.AddrTo;
+                    taskOld.LastInteractionPoint = taskInfo.LastInteractionPoint;
+                    taskOld.AddrNext = taskInfo.AddrNext;
+                    taskOld.Tunnel = taskInfo.Tunnel;
+                    taskOld.Floor = taskInfo.Floor;
+                    taskOld.Device = taskInfo.Device;
+                    taskOld.SrmStation = taskInfo.SrmStation;
+                    //码垛信息
+                    taskOld.DocID = taskInfo.DocID;
+                    taskOld.PalletType = taskInfo.PalletType;
+                    taskOld.ProdLine = taskInfo.ProdLine;
+                    taskOld.FullQty = taskInfo.FullQty;
+                    //外形信息
+                    taskOld.Height = taskInfo.Height;
+                    taskOld.Length = taskInfo.Length;
+                    //手动处理备注
+                    taskOld.ManualRemarks = taskInfo.ManualRemarks;
+
+                    //更新任务历史表,删除任务当前表
+                    db.Default.Updateable(taskOld).AddQueue();
+                    db.Default.Deleteable(taskInfo).AddQueue();
+                }
+            }
+        }
+    }
+}

+ 29 - 0
分拣库/WCS.WorkEngineering/Systems/MainLineScanCode.cs

@@ -0,0 +1,29 @@
+using System.ComponentModel;
+using WCS.Core;
+using WCS.Entity.Protocol.Station;
+using WCS.WorkEngineering.Worlds;
+
+namespace WCS.WorkEngineering.Systems
+{
+    /// <summary>
+    ///  主线扫码
+    /// </summary>
+    [BelongTo(typeof(SortingMainLineWorld))]
+    [Description("主线扫码")]
+    public class MainLineScanCode : DeviceSystem<Device<IStation520, IStation521, IStation523>>
+    {
+        protected override bool ParallelDo => throw new NotImplementedException();
+
+        protected override bool SaveLogsToFile => throw new NotImplementedException();
+
+        public override void Do(Device<IStation520, IStation521, IStation523> obj)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override bool Select(Device dev)
+        {
+            return new int[] { 1701, 2101, 1717, 2117 }.Contains(int.Parse(dev.Code));
+        }
+    }
+}

+ 290 - 0
分拣库/WCS.WorkEngineering/Systems/SrmSystems.cs

@@ -0,0 +1,290 @@
+using ServiceCenter;
+using ServiceCenter.Extensions;
+using ServiceCenter.SqlSugars;
+using System.ComponentModel;
+using WCS.Core;
+using WCS.Entity;
+using WCS.Entity.Protocol.SRM;
+using WCS.Entity.Protocol.Station;
+using WCS.WorkEngineering.Extensions;
+using WCS.WorkEngineering.WebApi.Controllers;
+using WCS.WorkEngineering.Worlds;
+using WCS.WorkEngineering.Worlds.Logs;
+using KnownException = WCS.WorkEngineering.Worlds.Logs.KnownException;
+using TaskStatus = WCS.Entity.TaskStatus;
+
+namespace WCS.WorkEngineering.Systems
+{
+    /// <summary>
+    /// 堆垛机系统
+    /// </summary>
+    [BelongTo(typeof(MainWorld))]
+    [Description("堆垛机系统")]
+    public class SrmSystems : DeviceSystem<SRM>
+    {
+        /// <summary>
+        /// 取货点设备集合
+        /// </summary>
+        private Dictionary<string, List<Station>> PickUpDevices = new Dictionary<string, List<Station>>();
+
+        /// <summary>
+        /// 放货设备
+        /// </summary>
+        private Dictionary<string, List<Station>> PutDevices = new Dictionary<string, List<Station>>();
+
+        public SrmSystems()
+        {
+            //获取所有的巷道集合
+            var devices = ServiceHub.deviceInfos.Where(x => x.IsTunnel());
+
+            //开始分配
+            foreach (var item in devices)
+            {
+                //取货设备
+                var deviceCode = item.FormerRoutes.Select(v => v.DeviceCode).ToList();
+                PickUpDevices.Add(item.NextRoutes.First().DeviceCode, World.Devices.Where(v => deviceCode.Contains(v.Code)).Select(v => new Station(v)).ToList());
+                //放货设备
+                deviceCode = ServiceHub.deviceInfos.First(x => item.NextRoutes.First().DeviceCode == x.Code).NextRoutes.Select(v => v.NextCode).ToList();
+                PutDevices.Add(item.NextRoutes.First().DeviceCode, World.Devices.Where(v => deviceCode.Contains(v.Code)).Select(v => new Station(v)).ToList());
+            }
+        }
+
+        protected override bool ParallelDo => true;
+
+        protected override bool SaveLogsToFile => true;
+
+        public override void Do(SRM obj)
+        {
+            //判断堆垛机是否报警
+            if (obj.Data2.Status.HasFlag(SrmStatus.Alarm)) throw new KnownException(obj.Data3.Alarm.ToString(), LogLevelEnum.High);
+
+            //判断DB520 完成任务确认清除信号 是否为1
+            if (obj.Data.OkAck == 1) throw new KnownException("任务完成确认信号未清除", LogLevelEnum.Mid);
+
+            //判断完成任务号是否大于0
+            if (obj.Data2.TaskFinishiId > 0)
+            {
+                var result = SqlSugarHelper.Do(db =>
+                  {
+                      //根据DB521任务号获取对应任务
+                      var task = db.Default.Queryable<WCS_TaskInfo>().First(v => v.ID == obj.Data2.TaskFinishiId) ?? throw new KnownException($"未找到任务{obj.Data2.TaskFinishiId}", LogLevelEnum.High);
+                      if (task.Status != Entity.TaskStatus.StackerExecution)
+                          throw new KnownException($"任务{task.ID}状态是{task.Status.Description()}.堆垛机完成任务需要对应任务状态处于堆垛机执行中", LogLevelEnum.High);
+                      //根据任务类型做不同的处理
+                      switch (task.Type)
+                      {
+                          case TaskType.EnterDepot:
+                              //完成任务
+                              task.Status = Entity.TaskStatus.Finish;
+                              task.EedTime = DateTime.Now;
+                              task.AddWCS_TASK_DTL(db, task.AddrTo, "入库任务结束");
+                              break;
+
+                          case TaskType.OutDepot:
+                              task.Status = Entity.TaskStatus.ConveyorExecution;
+                              task.AddWCS_TASK_DTL(db, task.SrmStation, "出库任务到达放货站台");
+                              break;
+
+                          case TaskType.TransferDepot:
+                              task.Status = Entity.TaskStatus.Finish;
+                              task.EedTime = DateTime.Now;
+                              task.AddWCS_TASK_DTL(db, task.AddrTo, "移库任务结束");
+                              break;
+                      }
+
+                      db.Default.Updateable(task).AddQueue();
+                      task.CompleteOrCancelTasks(db, World);
+                      db.Default.SaveQueues();
+                  });
+                if (!result) throw new KnownException("数据库提交事务错误", LogLevelEnum.High);
+                // 写入信号
+                obj.Data.OkAck = 1;
+            }
+
+            //堆垛机是否可以下发任务
+            if (obj.Data2.VoucherNo != obj.Data.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{obj.Data.VoucherNo},DB521:{obj.Data2.VoucherNo}", LogLevelEnum.Mid);
+            if (obj.Data2.AutoStatus != SrmAutoStatus.Automatic) throw new KnownException($"堆垛机处于{obj.Data2.AutoStatus}模式", LogLevelEnum.Low);
+            if (obj.Data2.RunStatus != SrmRunStatus.Idle) throw new KnownException($"堆垛机处于{obj.Data2.RunStatus}状态", LogLevelEnum.High);
+
+            //默认没有移库任务
+            bool isTransfer = false;
+
+            //出入库优先级任务 1:无优先 2:入库 3:出库
+            int enterOrOut = 1;
+
+            //再检查是否有等待执行的货物
+            SqlSugarHelper.Do(db =>
+            {
+                //获取当前堆垛机的所有未完成任务
+                var tasks = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Status < Entity.TaskStatus.Finish && (v.Device == obj.Entity.Code));
+                //任务集合是否有处于堆垛机执行状态的任务
+                if (tasks.Any(v => v.Status == Entity.TaskStatus.StackerExecution)) throw new KnownException($"有任务处于堆垛机执行状态", LogLevelEnum.High);
+
+                //判断是否存在调整优先级任务
+                if (!tasks.Any(v => v.Type != TaskType.TransferDepot && v.Status < Entity.TaskStatus.StackerExecution && v.Priority > 0))
+                {
+                    //不存在调整优先级任务,判断是否存在移库任务
+                    isTransfer = tasks.Any(v => v.Type == TaskType.TransferDepot && v.Status == Entity.TaskStatus.NewBuild);
+                }
+                else //存在调整优先级任务
+                {
+                    //获取出库任务中新建状态最大优先级
+                    var outPriorityNewBuild = tasks.Where(v => v.Type == TaskType.OutDepot && v.Status == Entity.TaskStatus.NewBuild).Max(v => v.Priority);
+                    //获取入库任务中最大优先级
+                    var enterPriority = tasks.Where(v => v.Type == TaskType.EnterDepot && v.Status < Entity.TaskStatus.StackerExecution).Max(v => v.Priority);
+                    //出入库最大优先级相加大于零
+                    if (outPriorityNewBuild + enterPriority > 0)
+                    {
+                        //出入库优先级任务 1:无优先 2:入库 3:出库
+                        enterOrOut = enterPriority > outPriorityNewBuild ? 2 : 3;
+                    }
+                }
+            });
+
+            #region 移库
+
+            if (isTransfer)
+            {
+                WCS_TaskInfo taskInfo = null;
+                var result = SqlSugarHelper.Do(db =>
+                   {
+                       //获取一条当前堆垛机优先级最高的新建移库任务
+                       var task = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Device == obj.Entity.Code && v.Type == TaskType.TransferDepot && v.Status == Entity.TaskStatus.NewBuild)
+                                                                      .OrderByDescending(v => v.Priority)
+                                                                      .First() ?? throw new KnownException("未找到移库任务", LogLevelEnum.High);
+                       //任务状态改为堆垛机执行中
+                       task.Status = Entity.TaskStatus.StackerExecution;
+                       task.StartTime = DateTime.Now;
+                       task.AddWCS_TASK_DTL(db, task.AddrFrom, task.Device, $"堆垛机{obj.Entity.Code}开始执行任务");
+                       db.Default.Updateable(task).AddQueue();
+                       db.Default.SaveQueues();
+                       taskInfo = task;
+                   });
+                if (taskInfo == null || !result) throw new KnownException("数据更新错误", LogLevelEnum.High);
+                var addrFrom = taskInfo.AddrFrom.Split("-");
+                var addrTo = taskInfo.AddrTo.Split("-");
+                //下发任务
+                obj.Data.TaskNumber = taskInfo.ID;
+                obj.Data.RowPos1 = addrFrom[0].ToShort();
+                obj.Data.TravelPos1 = addrFrom[1].ToShort();
+                obj.Data.LiftPos1 = addrFrom[2].ToShort();
+                obj.Data.RowPos2 = addrTo[0].ToShort();
+                obj.Data.TravelPos2 = addrTo[1].ToShort();
+                obj.Data.LiftPos2 = addrTo[2].ToShort();
+                obj.Data.VoucherNo++;
+            }
+
+            #endregion 移库
+
+            #region 出入库
+
+            //上一个周期是不是出库任务 第一次获取返回结果会是false
+            var lastIsOut = obj.Entity.Get<bool>("LastIsOut");
+            obj.Entity.Set("LastIsOut", !lastIsOut);
+
+            //入库任务优先 或 上一个周期是出库任务并且出库任务无优先
+            if (enterOrOut == 2 || (lastIsOut && enterOrOut == 1)) //入库任务
+            {
+                //判断本次优先执行楼层,并设置下次执行时优先楼层
+                var floor = obj.Entity.Get<int>("FloorIn");
+                floor = floor % 2 + 1;
+                obj.Entity.Set("FloorIn", floor);
+
+                //获取当前堆垛机所有的取货站台
+                var arrIn = PickUpDevices.First(v => v.Key == obj.Entity.Code).Value;
+                if (!arrIn.Any()) throw new KnownException($"堆垛机{obj.Entity.Code}无取货路径点", LogLevelEnum.High);
+
+                //获取有货的设备
+                arrIn = arrIn.Where(v => v.Data2.TaskNumber > 0 && v.Data3.Status.HasFlag(StatusEunm.PH_Status) && !v.Data3.Status.HasFlag(StatusEunm.Run)).ToList();
+                if (!arrIn.Any()) throw new KnownException($"[{obj.Entity.Code}]等待入库任务输送到位", LogLevelEnum.Mid);
+                WCS_TaskInfo taskInfo = null;
+                Station station = null;
+                var result = SqlSugarHelper.Do(db =>
+                {
+                    //根据有货设备的任务号获取所有类型为入库状态为输送机执行中的任务
+                    var tasks = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Type == TaskType.EnterDepot && v.Status == TaskStatus.ConveyorExecution && arrIn.Select(p => p.Data2.TaskNumber).Contains(v.ID));
+                    //按条件先后排序获取一条排序后第一条结果1.优先级2.所在楼层与本次优先执行楼层 TODO:待验证排序结果
+                    var task = tasks.OrderByDescending(v => v.Priority).OrderByDescending(v => v.Floor == floor ? 1 : 0).First() ?? throw new KnownException($"{obj.Entity.Code}未找到入库任务", LogLevelEnum.High);
+                    //获取任务所有设备
+                    station = arrIn.First(v => v.Data2.TaskNumber == task.ID);
+                    //获取当前货物巷道
+                    var loc = WmsApi.GetLocalIn(task.ID, task.Tunnel, station.Entity.Code);
+
+                    task.Status = TaskStatus.StackerExecution;
+                    task.AddrTo = $"{loc.Row}-{loc.Colomn}-{loc.Layer}";
+                    task.LastInteractionPoint = station.Entity.Code;
+                    task.EditWho = "WCS";
+                    task.AddWCS_TASK_DTL(db, station.Entity.Code, task.AddrTo, "任务下发堆垛机执行");
+                    db.Default.Updateable(task).AddQueue();
+                    db.Default.SaveQueues();
+                });
+
+                if (taskInfo == null || !result) throw new KnownException("数据更新错误", LogLevelEnum.High);
+                var addrTo = taskInfo.AddrTo.Split("-");
+                //下发任务
+                obj.Data.TaskNumber = taskInfo.ID;
+                obj.Data.RowPos1 = station.Entity.Code.ToShort();
+                obj.Data.RowPos2 = addrTo[0].ToShort();
+                obj.Data.TravelPos2 = addrTo[1].ToShort();
+                obj.Data.LiftPos2 = addrTo[2].ToShort();
+                obj.Data.VoucherNo++;
+            }
+            else if (enterOrOut == 3 || !lastIsOut) //出库任务
+            {
+                //判断本次优先执行楼层,并设置下次执行时优先楼层
+                var floor = obj.Entity.Get<int>("FloorOut");
+                floor = floor % 2 + 1;
+                obj.Entity.Set("FloorOut", floor);
+
+                //获取当前堆垛机所有的取货站台
+                var arrOut = PickUpDevices.First(v => v.Key == obj.Entity.Code).Value;
+                if (!arrOut.Any()) throw new KnownException($"堆垛机{obj.Entity.Code}无放货路径点", LogLevelEnum.High);
+
+                //获取可以放货的设备集合
+                arrOut = arrOut.Where(v => !v.Data3.Status.HasFlag(StatusEunm.PH_Status) //无光电
+                                        && !v.Data3.Status.HasFlag(StatusEunm.Run) //未运行
+                                        && !v.Data3.Status.HasFlag(StatusEunm.OT_Status) //无任务
+                                        && !v.Data3.Status.HasFlag(StatusEunm.UnassignedTask) //未分配任务
+                                        && v.Data3.Status.HasFlag(StatusEunm.Auto)).ToList(); //自动
+                if (!arrOut.Any()) throw new KnownException($"[{obj.Entity.Code}]等待出库任务输送到位", LogLevelEnum.Mid);
+                WCS_TaskInfo taskInfo = null;
+                string[] addrFrom = null;
+
+                var result = SqlSugarHelper.Do(db =>
+                {
+                    var allOutCode = arrOut.Select(v => v.Entity.Code).ToList();
+                    //按条件先后排序获取一条排序后第一条结果1.优先级2.所在楼层与本次优先执行楼层 TODO:待验证排序结果
+                    var task = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Type == TaskType.OutDepot && v.Status == TaskStatus.WaitingToExecute)
+                                                                   .Where(v => allOutCode.Contains(v.SrmStation))
+                                                                   .OrderByDescending(v => v.Priority)
+                                                                   .OrderByDescending(v => v.Floor == floor ? 1 : 0)
+                                                                   .First() ?? throw new KnownException($"{obj.Entity.Code}未找到出库任务", LogLevelEnum.High);
+
+                    addrFrom = task.AddrFrom.Split("-");
+                    task.Status = TaskStatus.StackerExecution;
+                    task.LastInteractionPoint = task.Device;
+                    task.EditWho = "WCS";
+                    task.AddWCS_TASK_DTL(db, task.Device, task.SrmStation, "任务下发堆垛机执行");
+                    db.Default.Updateable(task).AddQueue();
+                    taskInfo = task;
+                    db.Default.SaveQueues();
+                });
+
+                obj.Data.TaskNumber = taskInfo.ID;
+                obj.Data.RowPos1 = addrFrom[0].ToShort();
+                obj.Data.TravelPos1 = addrFrom[1].ToShort();
+                obj.Data.LiftPos1 = addrFrom[2].ToShort();
+                obj.Data.RowPos2 = taskInfo.SrmStation.ToShort();
+                obj.Data.TravelPos2 = taskInfo.AddrNext.ToShort();
+                obj.Data.VoucherNo++;
+            }
+
+            #endregion 出入库
+        }
+
+        public override bool Select(Device dev)
+        {
+            return dev.Code.Contains("SRM");
+        }
+    }
+}

+ 4 - 0
分拣库/WCS.WorkEngineering/WCS.WorkEngineering.csproj

@@ -8,7 +8,11 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
+<<<<<<< Updated upstream
     <PackageReference Include="ServiceCenter" Version="1.0.0.12" />
+=======
+    <PackageReference Include="ServiceCenter" Version="1.0.1.8" />
+>>>>>>> Stashed changes
   </ItemGroup>
 
   <ItemGroup>

+ 43 - 0
分拣库/WCS.WorkEngineering/WebApi/Controllers/AgvController.cs

@@ -0,0 +1,43 @@
+using Microsoft.AspNetCore.Mvc;
+using WCS.WorkEngineering.WebApi.Models.AGV;
+
+namespace WCS.WorkEngineering.WebApi.Controllers
+{
+    /// <summary>
+    /// AGV相关接口控制器
+    /// </summary>
+    [ApiController]
+    [Route("[controller]/[action]")]
+    public class AgvController : ControllerBase
+    {
+        /// <summary>
+        /// AGV执行回调
+        /// </summary>
+        /// <param name="reqDto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        public AgvCallbackResponse AgvCallback(AgvCallbackRequest reqDto)
+        {
+            switch (reqDto.method)
+            {
+                case "申请":
+                    break;
+                case "申请放货":
+                    break;
+
+                case "申请取货":
+                    break;
+
+                case "取放完成":
+                    break;
+
+                default:
+                    break;
+            }
+
+            var res = new AgvCallbackResponse() { code = "0", message = "成功" };
+
+            return res;
+        }
+    }
+}

+ 108 - 9
分拣库/WCS.WorkEngineering/Worlds/MainWorld.cs

@@ -1,16 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Collections.Concurrent;
+using System.ComponentModel;
 using WCS.Core;
+using WCS.WorkEngineering.Worlds.Logs;
+using KnownException = WCS.WorkEngineering.Worlds.Logs.KnownException;
+using LogInfo = WCS.WorkEngineering.Worlds.Logs.LogInfo;
 
 namespace WCS.WorkEngineering.Worlds
 {
     /// <summary>
     /// 主世界,所有的系统(交互点)默认在该世界下执行。
-    /// 如有系统需独立,请自行增加
+    /// 如有系统需独立,请自行增加对应世界
+    /// 新增世界应当继承此世界,而不是直接继承World
     /// </summary>
+    [Description("主世界")]
     public class MainWorld : World
     {
         /// <summary>
@@ -18,15 +20,112 @@ namespace WCS.WorkEngineering.Worlds
         /// </summary>
         public MainWorld()
         {
-            Interval = 300;
         }
 
         /// <summary>
-        /// 更新后执行
+        ///  日志队列
+        /// </summary>
+        protected ConcurrentQueue<KeyLog> Logs = new ConcurrentQueue<KeyLog>();
+
+        /// <summary>
+        ///  世界执行周期间隔
+        ///  单位:毫秒
+        /// </summary>
+        protected override int Interval => 300;
+
+        /// <summary>
+        ///  更新前执行,重写改方法后请自行添加执行内容
+        ///  执行内容:清空日志队列
+        /// </summary>
+        protected override void BeforeUpdate()
+        {
+            // 清空日志队列,确保日志队列中只会有当前周期日志
+            Logs.Clear();
+        }
+
+        /// <summary>
+        /// 更新后执行,重写改方法后请自行添加执行内容
+        /// 执行内容:清空日志队列
         /// </summary>
         protected override void AfterUpdate()
         {
-            //var objs = GetSystem<CollectConvDataSys>().Objects;
+            #region 日志处理
+
+            //TODO:暂时仅将日志记录至文本文件
+            if (Logs.Count() > 0)
+            {
+                var time = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:FF")}";
+                var fst = Logs.First();
+                var dir = $"Logs\\{DateTime.Now.ToString("yyyy-MM-dd")}\\{fst.Channel.World}\\{fst.Channel.System}\\";
+                if (!Directory.Exists(dir))
+                    Directory.CreateDirectory(dir);
+
+                foreach (var log in Logs)
+                {
+                    var msg = $"--------------------{log.Time}[{Thread.CurrentThread.ManagedThreadId}]--------------------\n{log}\n";
+                    File.AppendAllText(Path.Combine(dir, $"{log.Log.Message.Split(":")[0]}.txt"), msg);
+                }
+            }
+
+            #endregion 日志处理
+        }
+
+        /// <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.NotUpLoad, 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)
+        {
+            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 };
+            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());
         }
     }
 }

+ 12 - 0
分拣库/WCS.WorkEngineering/Worlds/SortingMainLineWorld.cs

@@ -0,0 +1,12 @@
+namespace WCS.WorkEngineering.Worlds
+{
+    /// <summary>
+    /// 分拣主线
+    /// </summary>
+    public class SortingMainLineWorld : MainWorld
+    {
+        public SortingMainLineWorld()
+        {
+        }
+    }
+}

+ 1 - 1
合金库/WCS.Entity.Protocol/Station/IStation523.cs

@@ -5,7 +5,7 @@ namespace WCS.Entity.Protocol.Station
     /// <summary>
     /// 输送机状态读取协议
     /// </summary>
-    public interface IStation522 : IProtocol
+    public interface IStation523 : IProtocol
     {
         /// <summary>
         /// 警报

+ 2 - 0
合金库/WCS.Entity.Protocol/Station/IStation91.cs

@@ -27,5 +27,7 @@
         /// </summary>
         [IgnoreChanging]
         float Width { get; set; }
+
+
     }
 }

+ 1 - 1
合金库/WCS.Entity.Protocol/Station/WCS_Station523.cs

@@ -8,7 +8,7 @@ namespace WCS.Entity.Protocol.Station
     /// </summary>
     [SugarTable(nameof(WCS_Station523), "站台状态读取记录表")]
     [DataContract]
-    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation522
+    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation523
     {
         /// <summary>
         /// 警报

+ 1 - 1
合金库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="WCS.Entity" Version="1.0.0.9" />
+    <PackageReference Include="WCS.Entity" Version="1.0.1.4" />
   </ItemGroup>
 
 </Project>

+ 179 - 2
合金库/WCS.WorkEngineering/Extensions/DeviceExtension.cs

@@ -1,4 +1,6 @@
-using WCS.Core;
+using System.Collections.Concurrent;
+using WCS.Core;
+using WCS.Entity;
 using WCS.Entity.Protocol.Station;
 
 namespace WCS.WorkEngineering.Extensions
@@ -8,9 +10,184 @@ namespace WCS.WorkEngineering.Extensions
     /// </summary>
     public static class DeviceExtension
     {
+        /// <summary>
+        ///  是否是输送线
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
         public static bool IsConv(this Device source)
         {
-            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation522));
+            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation523));
         }
+
+        /// <summary>
+        ///  是否是巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static bool IsTunnel(this WCS_DeviceInfo source)
+        {
+            return source.Code.Contains("TY");
+        }
+
+        /// <summary>
+        ///  获取下一个地址
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetNextAdd(this WCS_DeviceInfo source, string addto)
+        {
+            var path = source.Paths.First(p => p.EndCode == addto).Path;
+            return path.Split('-')[0];
+        }
+
+        /// <summary>
+        ///  获取到达目标地址需要的路径信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetAddtoPath(this WCS_DeviceInfo source, string addto)
+        {
+            return source.Paths.First(p => p.EndCode == addto).Path;
+        }
+
+        #region WCS_DEVICE扩展数据
+
+        /// <summary>
+        ///  设备标识字典
+        /// </summary>
+        private static ConcurrentDictionary<string, object> DeviceValues = new ConcurrentDictionary<string, object>();
+
+        /// <summary>
+        /// 添加设备标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        public static void AddFlag(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            df = df | flag;
+            source.Set("DeviceFlag", df);
+        }
+
+        /// <summary>
+        ///  当前设备是否包含标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        /// <returns></returns>
+        public static bool Is(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            return (df & flag) == flag;
+        }
+
+        /// <summary>
+        ///  为设备写入标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <param name="value">写入值</param>
+        public static void Set<T>(this Device source, string key, T value)
+        {
+            DeviceValues[source.Code + key] = value;
+        }
+
+        /// <summary>
+        ///  获取设备标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <returns></returns>
+        public static T Get<T>(this Device source, string key)
+        {
+            if (!DeviceValues.ContainsKey(source.Code + key))
+                return default(T);
+            return (T)DeviceValues[source.Code + key];
+        }
+
+        /// <summary>
+        ///  获取short类型的设备号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static short Code(this Device source)
+        {
+            return short.Parse(source.Code);
+        }
+
+        /// <summary>
+        ///  获取巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道Code</returns>
+        public static string Tunnel(this Device source)
+        {
+            return source.Get<string>("Tunnel");
+        }
+
+        /// <summary>
+        ///  获取巷道号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道号</returns>
+        public static int TunnelNum(this Device source)
+        {
+            return int.Parse(source.Tunnel().Last().ToString());
+        }
+
+        /// <summary>
+        ///  获取设备楼层信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static int Floor(this Device source)
+        {
+            return source.Get<int>("Floor");
+        }
+
+        /// <summary>
+        ///  获取堆垛机
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device SRM(this Device source)
+        {
+            return source.Get<Device>("SRM");
+        }
+
+        /// <summary>
+        ///  获取RGV
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device RGV(this Device source)
+        {
+            return source.Get<Device>("RGV");
+        }
+
+        //public static void AddFlag(DF flag, params string[] devices)
+        //{
+        //    var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(v => devices.Contains(v.CODE)).ToArray();
+        //    Parallel.ForEach(arr, v =>
+        //    {
+        //        v.AddFlag(flag);
+        //    });
+        //}
+
+        #endregion WCS_DEVICE扩展数据
+    }
+
+    /// <summary>
+    /// 设备配置
+    /// </summary>
+    [Flags]
+    public enum DF
+    {
+        无 = 0,
     }
 }

+ 1 - 1
合金库/WCS.WorkEngineering/WCS.WorkEngineering.csproj

@@ -8,7 +8,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
-    <PackageReference Include="ServiceCenter" Version="1.0.0.12" />
+    <PackageReference Include="ServiceCenter" Version="1.0.1.8" />
   </ItemGroup>
 
   <ItemGroup>

+ 108 - 9
合金库/WCS.WorkEngineering/Worlds/MainWorld.cs

@@ -1,16 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Collections.Concurrent;
+using System.ComponentModel;
 using WCS.Core;
+using WCS.WorkEngineering.Worlds.Logs;
+using KnownException = WCS.WorkEngineering.Worlds.Logs.KnownException;
+using LogInfo = WCS.WorkEngineering.Worlds.Logs.LogInfo;
 
 namespace WCS.WorkEngineering.Worlds
 {
     /// <summary>
     /// 主世界,所有的系统(交互点)默认在该世界下执行。
-    /// 如有系统需独立,请自行增加
+    /// 如有系统需独立,请自行增加对应世界
+    /// 新增世界应当继承此世界,而不是直接继承World
     /// </summary>
+    [Description("主世界")]
     public class MainWorld : World
     {
         /// <summary>
@@ -18,15 +20,112 @@ namespace WCS.WorkEngineering.Worlds
         /// </summary>
         public MainWorld()
         {
-            Interval = 300;
         }
 
         /// <summary>
-        /// 更新后执行
+        ///  日志队列
+        /// </summary>
+        protected ConcurrentQueue<KeyLog> Logs = new ConcurrentQueue<KeyLog>();
+
+        /// <summary>
+        ///  世界执行周期间隔
+        ///  单位:毫秒
+        /// </summary>
+        protected override int Interval => 300;
+
+        /// <summary>
+        ///  更新前执行,重写改方法后请自行添加执行内容
+        ///  执行内容:清空日志队列
+        /// </summary>
+        protected override void BeforeUpdate()
+        {
+            // 清空日志队列,确保日志队列中只会有当前周期日志
+            Logs.Clear();
+        }
+
+        /// <summary>
+        /// 更新后执行,重写改方法后请自行添加执行内容
+        /// 执行内容:清空日志队列
         /// </summary>
         protected override void AfterUpdate()
         {
-            //var objs = GetSystem<CollectConvDataSys>().Objects;
+            #region 日志处理
+
+            //TODO:暂时仅将日志记录至文本文件
+            if (Logs.Count() > 0)
+            {
+                var time = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:FF")}";
+                var fst = Logs.First();
+                var dir = $"Logs\\{DateTime.Now.ToString("yyyy-MM-dd")}\\{fst.Channel.World}\\{fst.Channel.System}\\";
+                if (!Directory.Exists(dir))
+                    Directory.CreateDirectory(dir);
+
+                foreach (var log in Logs)
+                {
+                    var msg = $"--------------------{log.Time}[{Thread.CurrentThread.ManagedThreadId}]--------------------\n{log}\n";
+                    File.AppendAllText(Path.Combine(dir, $"{log.Log.Message.Split(":")[0]}.txt"), msg);
+                }
+            }
+
+            #endregion 日志处理
+        }
+
+        /// <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.NotUpLoad, 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)
+        {
+            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 };
+            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());
         }
     }
 }

+ 1 - 1
成品库/WCS.Entity.Protocol/Station/IStation523.cs

@@ -5,7 +5,7 @@ namespace WCS.Entity.Protocol.Station
     /// <summary>
     /// 输送机状态读取协议
     /// </summary>
-    public interface IStation522 : IProtocol
+    public interface IStation523 : IProtocol
     {
         /// <summary>
         /// 警报

+ 2 - 0
成品库/WCS.Entity.Protocol/Station/IStation91.cs

@@ -27,5 +27,7 @@
         /// </summary>
         [IgnoreChanging]
         float Width { get; set; }
+
+
     }
 }

+ 1 - 1
成品库/WCS.Entity.Protocol/Station/WCS_Station523.cs

@@ -8,7 +8,7 @@ namespace WCS.Entity.Protocol.Station
     /// </summary>
     [SugarTable(nameof(WCS_Station523), "站台状态读取记录表")]
     [DataContract]
-    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation522
+    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation523
     {
         /// <summary>
         /// 警报

+ 1 - 1
成品库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="WCS.Entity" Version="1.0.0.9" />
+    <PackageReference Include="WCS.Entity" Version="1.0.1.4" />
   </ItemGroup>
 
 </Project>

+ 179 - 2
成品库/WCS.WorkEngineering/Extensions/DeviceExtension.cs

@@ -1,4 +1,6 @@
-using WCS.Core;
+using System.Collections.Concurrent;
+using WCS.Core;
+using WCS.Entity;
 using WCS.Entity.Protocol.Station;
 
 namespace WCS.WorkEngineering.Extensions
@@ -8,9 +10,184 @@ namespace WCS.WorkEngineering.Extensions
     /// </summary>
     public static class DeviceExtension
     {
+        /// <summary>
+        ///  是否是输送线
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
         public static bool IsConv(this Device source)
         {
-            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation522));
+            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation523));
         }
+
+        /// <summary>
+        ///  是否是巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static bool IsTunnel(this WCS_DeviceInfo source)
+        {
+            return source.Code.Contains("TY");
+        }
+
+        /// <summary>
+        ///  获取下一个地址
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetNextAdd(this WCS_DeviceInfo source, string addto)
+        {
+            var path = source.Paths.First(p => p.EndCode == addto).Path;
+            return path.Split('-')[0];
+        }
+
+        /// <summary>
+        ///  获取到达目标地址需要的路径信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetAddtoPath(this WCS_DeviceInfo source, string addto)
+        {
+            return source.Paths.First(p => p.EndCode == addto).Path;
+        }
+
+        #region WCS_DEVICE扩展数据
+
+        /// <summary>
+        ///  设备标识字典
+        /// </summary>
+        private static ConcurrentDictionary<string, object> DeviceValues = new ConcurrentDictionary<string, object>();
+
+        /// <summary>
+        /// 添加设备标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        public static void AddFlag(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            df = df | flag;
+            source.Set("DeviceFlag", df);
+        }
+
+        /// <summary>
+        ///  当前设备是否包含标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        /// <returns></returns>
+        public static bool Is(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            return (df & flag) == flag;
+        }
+
+        /// <summary>
+        ///  为设备写入标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <param name="value">写入值</param>
+        public static void Set<T>(this Device source, string key, T value)
+        {
+            DeviceValues[source.Code + key] = value;
+        }
+
+        /// <summary>
+        ///  获取设备标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <returns></returns>
+        public static T Get<T>(this Device source, string key)
+        {
+            if (!DeviceValues.ContainsKey(source.Code + key))
+                return default(T);
+            return (T)DeviceValues[source.Code + key];
+        }
+
+        /// <summary>
+        ///  获取short类型的设备号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static short Code(this Device source)
+        {
+            return short.Parse(source.Code);
+        }
+
+        /// <summary>
+        ///  获取巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道Code</returns>
+        public static string Tunnel(this Device source)
+        {
+            return source.Get<string>("Tunnel");
+        }
+
+        /// <summary>
+        ///  获取巷道号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道号</returns>
+        public static int TunnelNum(this Device source)
+        {
+            return int.Parse(source.Tunnel().Last().ToString());
+        }
+
+        /// <summary>
+        ///  获取设备楼层信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static int Floor(this Device source)
+        {
+            return source.Get<int>("Floor");
+        }
+
+        /// <summary>
+        ///  获取堆垛机
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device SRM(this Device source)
+        {
+            return source.Get<Device>("SRM");
+        }
+
+        /// <summary>
+        ///  获取RGV
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device RGV(this Device source)
+        {
+            return source.Get<Device>("RGV");
+        }
+
+        //public static void AddFlag(DF flag, params string[] devices)
+        //{
+        //    var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(v => devices.Contains(v.CODE)).ToArray();
+        //    Parallel.ForEach(arr, v =>
+        //    {
+        //        v.AddFlag(flag);
+        //    });
+        //}
+
+        #endregion WCS_DEVICE扩展数据
+    }
+
+    /// <summary>
+    /// 设备配置
+    /// </summary>
+    [Flags]
+    public enum DF
+    {
+        无 = 0,
     }
 }

+ 1 - 1
成品库/WCS.WorkEngineering/WCS.WorkEngineering.csproj

@@ -8,7 +8,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
-    <PackageReference Include="ServiceCenter" Version="1.0.0.12" />
+    <PackageReference Include="ServiceCenter" Version="1.0.1.8" />
   </ItemGroup>
 
   <ItemGroup>

+ 108 - 9
成品库/WCS.WorkEngineering/Worlds/MainWorld.cs

@@ -1,16 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Collections.Concurrent;
+using System.ComponentModel;
 using WCS.Core;
+using WCS.WorkEngineering.Worlds.Logs;
+using KnownException = WCS.WorkEngineering.Worlds.Logs.KnownException;
+using LogInfo = WCS.WorkEngineering.Worlds.Logs.LogInfo;
 
 namespace WCS.WorkEngineering.Worlds
 {
     /// <summary>
     /// 主世界,所有的系统(交互点)默认在该世界下执行。
-    /// 如有系统需独立,请自行增加
+    /// 如有系统需独立,请自行增加对应世界
+    /// 新增世界应当继承此世界,而不是直接继承World
     /// </summary>
+    [Description("主世界")]
     public class MainWorld : World
     {
         /// <summary>
@@ -18,15 +20,112 @@ namespace WCS.WorkEngineering.Worlds
         /// </summary>
         public MainWorld()
         {
-            Interval = 300;
         }
 
         /// <summary>
-        /// 更新后执行
+        ///  日志队列
+        /// </summary>
+        protected ConcurrentQueue<KeyLog> Logs = new ConcurrentQueue<KeyLog>();
+
+        /// <summary>
+        ///  世界执行周期间隔
+        ///  单位:毫秒
+        /// </summary>
+        protected override int Interval => 300;
+
+        /// <summary>
+        ///  更新前执行,重写改方法后请自行添加执行内容
+        ///  执行内容:清空日志队列
+        /// </summary>
+        protected override void BeforeUpdate()
+        {
+            // 清空日志队列,确保日志队列中只会有当前周期日志
+            Logs.Clear();
+        }
+
+        /// <summary>
+        /// 更新后执行,重写改方法后请自行添加执行内容
+        /// 执行内容:清空日志队列
         /// </summary>
         protected override void AfterUpdate()
         {
-            //var objs = GetSystem<CollectConvDataSys>().Objects;
+            #region 日志处理
+
+            //TODO:暂时仅将日志记录至文本文件
+            if (Logs.Count() > 0)
+            {
+                var time = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:FF")}";
+                var fst = Logs.First();
+                var dir = $"Logs\\{DateTime.Now.ToString("yyyy-MM-dd")}\\{fst.Channel.World}\\{fst.Channel.System}\\";
+                if (!Directory.Exists(dir))
+                    Directory.CreateDirectory(dir);
+
+                foreach (var log in Logs)
+                {
+                    var msg = $"--------------------{log.Time}[{Thread.CurrentThread.ManagedThreadId}]--------------------\n{log}\n";
+                    File.AppendAllText(Path.Combine(dir, $"{log.Log.Message.Split(":")[0]}.txt"), msg);
+                }
+            }
+
+            #endregion 日志处理
+        }
+
+        /// <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.NotUpLoad, 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)
+        {
+            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 };
+            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());
         }
     }
 }

+ 1 - 1
时效库/WCS.Entity.Protocol/Station/IStation523.cs

@@ -5,7 +5,7 @@ namespace WCS.Entity.Protocol.Station
     /// <summary>
     /// 输送机状态读取协议
     /// </summary>
-    public interface IStation522 : IProtocol
+    public interface IStation523 : IProtocol
     {
         /// <summary>
         /// 警报

+ 2 - 0
时效库/WCS.Entity.Protocol/Station/IStation91.cs

@@ -27,5 +27,7 @@
         /// </summary>
         [IgnoreChanging]
         float Width { get; set; }
+
+
     }
 }

+ 1 - 1
时效库/WCS.Entity.Protocol/Station/WCS_Station523.cs

@@ -8,7 +8,7 @@ namespace WCS.Entity.Protocol.Station
     /// </summary>
     [SugarTable(nameof(WCS_Station523), "站台状态读取记录表")]
     [DataContract]
-    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation522
+    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation523
     {
         /// <summary>
         /// 警报

+ 1 - 1
时效库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="WCS.Entity" Version="1.0.0.9" />
+    <PackageReference Include="WCS.Entity" Version="1.0.1.4" />
   </ItemGroup>
 
 </Project>

+ 179 - 2
时效库/WCS.WorkEngineering/Extensions/DeviceExtension.cs

@@ -1,4 +1,6 @@
-using WCS.Core;
+using System.Collections.Concurrent;
+using WCS.Core;
+using WCS.Entity;
 using WCS.Entity.Protocol.Station;
 
 namespace WCS.WorkEngineering.Extensions
@@ -8,9 +10,184 @@ namespace WCS.WorkEngineering.Extensions
     /// </summary>
     public static class DeviceExtension
     {
+        /// <summary>
+        ///  是否是输送线
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
         public static bool IsConv(this Device source)
         {
-            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation522));
+            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation523));
         }
+
+        /// <summary>
+        ///  是否是巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static bool IsTunnel(this WCS_DeviceInfo source)
+        {
+            return source.Code.Contains("TY");
+        }
+
+        /// <summary>
+        ///  获取下一个地址
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetNextAdd(this WCS_DeviceInfo source, string addto)
+        {
+            var path = source.Paths.First(p => p.EndCode == addto).Path;
+            return path.Split('-')[0];
+        }
+
+        /// <summary>
+        ///  获取到达目标地址需要的路径信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetAddtoPath(this WCS_DeviceInfo source, string addto)
+        {
+            return source.Paths.First(p => p.EndCode == addto).Path;
+        }
+
+        #region WCS_DEVICE扩展数据
+
+        /// <summary>
+        ///  设备标识字典
+        /// </summary>
+        private static ConcurrentDictionary<string, object> DeviceValues = new ConcurrentDictionary<string, object>();
+
+        /// <summary>
+        /// 添加设备标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        public static void AddFlag(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            df = df | flag;
+            source.Set("DeviceFlag", df);
+        }
+
+        /// <summary>
+        ///  当前设备是否包含标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        /// <returns></returns>
+        public static bool Is(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            return (df & flag) == flag;
+        }
+
+        /// <summary>
+        ///  为设备写入标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <param name="value">写入值</param>
+        public static void Set<T>(this Device source, string key, T value)
+        {
+            DeviceValues[source.Code + key] = value;
+        }
+
+        /// <summary>
+        ///  获取设备标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <returns></returns>
+        public static T Get<T>(this Device source, string key)
+        {
+            if (!DeviceValues.ContainsKey(source.Code + key))
+                return default(T);
+            return (T)DeviceValues[source.Code + key];
+        }
+
+        /// <summary>
+        ///  获取short类型的设备号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static short Code(this Device source)
+        {
+            return short.Parse(source.Code);
+        }
+
+        /// <summary>
+        ///  获取巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道Code</returns>
+        public static string Tunnel(this Device source)
+        {
+            return source.Get<string>("Tunnel");
+        }
+
+        /// <summary>
+        ///  获取巷道号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道号</returns>
+        public static int TunnelNum(this Device source)
+        {
+            return int.Parse(source.Tunnel().Last().ToString());
+        }
+
+        /// <summary>
+        ///  获取设备楼层信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static int Floor(this Device source)
+        {
+            return source.Get<int>("Floor");
+        }
+
+        /// <summary>
+        ///  获取堆垛机
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device SRM(this Device source)
+        {
+            return source.Get<Device>("SRM");
+        }
+
+        /// <summary>
+        ///  获取RGV
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device RGV(this Device source)
+        {
+            return source.Get<Device>("RGV");
+        }
+
+        //public static void AddFlag(DF flag, params string[] devices)
+        //{
+        //    var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(v => devices.Contains(v.CODE)).ToArray();
+        //    Parallel.ForEach(arr, v =>
+        //    {
+        //        v.AddFlag(flag);
+        //    });
+        //}
+
+        #endregion WCS_DEVICE扩展数据
+    }
+
+    /// <summary>
+    /// 设备配置
+    /// </summary>
+    [Flags]
+    public enum DF
+    {
+        无 = 0,
     }
 }

+ 1 - 1
时效库/WCS.WorkEngineering/WCS.WorkEngineering.csproj

@@ -8,7 +8,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
-    <PackageReference Include="ServiceCenter" Version="1.0.0.12" />
+    <PackageReference Include="ServiceCenter" Version="1.0.1.8" />
   </ItemGroup>
 
   <ItemGroup>

+ 108 - 9
时效库/WCS.WorkEngineering/Worlds/MainWorld.cs

@@ -1,16 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Collections.Concurrent;
+using System.ComponentModel;
 using WCS.Core;
+using WCS.WorkEngineering.Worlds.Logs;
+using KnownException = WCS.WorkEngineering.Worlds.Logs.KnownException;
+using LogInfo = WCS.WorkEngineering.Worlds.Logs.LogInfo;
 
 namespace WCS.WorkEngineering.Worlds
 {
     /// <summary>
     /// 主世界,所有的系统(交互点)默认在该世界下执行。
-    /// 如有系统需独立,请自行增加
+    /// 如有系统需独立,请自行增加对应世界
+    /// 新增世界应当继承此世界,而不是直接继承World
     /// </summary>
+    [Description("主世界")]
     public class MainWorld : World
     {
         /// <summary>
@@ -18,15 +20,112 @@ namespace WCS.WorkEngineering.Worlds
         /// </summary>
         public MainWorld()
         {
-            Interval = 300;
         }
 
         /// <summary>
-        /// 更新后执行
+        ///  日志队列
+        /// </summary>
+        protected ConcurrentQueue<KeyLog> Logs = new ConcurrentQueue<KeyLog>();
+
+        /// <summary>
+        ///  世界执行周期间隔
+        ///  单位:毫秒
+        /// </summary>
+        protected override int Interval => 300;
+
+        /// <summary>
+        ///  更新前执行,重写改方法后请自行添加执行内容
+        ///  执行内容:清空日志队列
+        /// </summary>
+        protected override void BeforeUpdate()
+        {
+            // 清空日志队列,确保日志队列中只会有当前周期日志
+            Logs.Clear();
+        }
+
+        /// <summary>
+        /// 更新后执行,重写改方法后请自行添加执行内容
+        /// 执行内容:清空日志队列
         /// </summary>
         protected override void AfterUpdate()
         {
-            //var objs = GetSystem<CollectConvDataSys>().Objects;
+            #region 日志处理
+
+            //TODO:暂时仅将日志记录至文本文件
+            if (Logs.Count() > 0)
+            {
+                var time = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:FF")}";
+                var fst = Logs.First();
+                var dir = $"Logs\\{DateTime.Now.ToString("yyyy-MM-dd")}\\{fst.Channel.World}\\{fst.Channel.System}\\";
+                if (!Directory.Exists(dir))
+                    Directory.CreateDirectory(dir);
+
+                foreach (var log in Logs)
+                {
+                    var msg = $"--------------------{log.Time}[{Thread.CurrentThread.ManagedThreadId}]--------------------\n{log}\n";
+                    File.AppendAllText(Path.Combine(dir, $"{log.Log.Message.Split(":")[0]}.txt"), msg);
+                }
+            }
+
+            #endregion 日志处理
+        }
+
+        /// <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.NotUpLoad, 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)
+        {
+            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 };
+            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());
         }
     }
 }

+ 1 - 1
盘条库/WCS.Entity.Protocol/Station/IStation523.cs

@@ -5,7 +5,7 @@ namespace WCS.Entity.Protocol.Station
     /// <summary>
     /// 输送机状态读取协议
     /// </summary>
-    public interface IStation522 : IProtocol
+    public interface IStation523 : IProtocol
     {
         /// <summary>
         /// 警报

+ 2 - 0
盘条库/WCS.Entity.Protocol/Station/IStation91.cs

@@ -27,5 +27,7 @@
         /// </summary>
         [IgnoreChanging]
         float Width { get; set; }
+
+
     }
 }

+ 1 - 1
盘条库/WCS.Entity.Protocol/Station/WCS_Station523.cs

@@ -8,7 +8,7 @@ namespace WCS.Entity.Protocol.Station
     /// </summary>
     [SugarTable(nameof(WCS_Station523), "站台状态读取记录表")]
     [DataContract]
-    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation522
+    public class WCS_Station523 : WCS_PROTOCOLDATA, IStation523
     {
         /// <summary>
         /// 警报

+ 1 - 1
盘条库/WCS.Entity.Protocol/WCS.Entity.Protocol.csproj

@@ -7,7 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="WCS.Entity" Version="1.0.0.9" />
+    <PackageReference Include="WCS.Entity" Version="1.0.1.4" />
   </ItemGroup>
 
 </Project>

+ 179 - 2
盘条库/WCS.WorkEngineering/Extensions/DeviceExtension.cs

@@ -1,4 +1,6 @@
-using WCS.Core;
+using System.Collections.Concurrent;
+using WCS.Core;
+using WCS.Entity;
 using WCS.Entity.Protocol.Station;
 
 namespace WCS.WorkEngineering.Extensions
@@ -8,9 +10,184 @@ namespace WCS.WorkEngineering.Extensions
     /// </summary>
     public static class DeviceExtension
     {
+        /// <summary>
+        ///  是否是输送线
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
         public static bool IsConv(this Device source)
         {
-            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation522));
+            return source.HasProtocol(typeof(IStation521)) || source.HasProtocol(typeof(IStation520)) || source.HasProtocol(typeof(IStation523));
         }
+
+        /// <summary>
+        ///  是否是巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static bool IsTunnel(this WCS_DeviceInfo source)
+        {
+            return source.Code.Contains("TY");
+        }
+
+        /// <summary>
+        ///  获取下一个地址
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetNextAdd(this WCS_DeviceInfo source, string addto)
+        {
+            var path = source.Paths.First(p => p.EndCode == addto).Path;
+            return path.Split('-')[0];
+        }
+
+        /// <summary>
+        ///  获取到达目标地址需要的路径信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="addto">目标地址</param>
+        /// <returns></returns>
+        public static string GetAddtoPath(this WCS_DeviceInfo source, string addto)
+        {
+            return source.Paths.First(p => p.EndCode == addto).Path;
+        }
+
+        #region WCS_DEVICE扩展数据
+
+        /// <summary>
+        ///  设备标识字典
+        /// </summary>
+        private static ConcurrentDictionary<string, object> DeviceValues = new ConcurrentDictionary<string, object>();
+
+        /// <summary>
+        /// 添加设备标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        public static void AddFlag(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            df = df | flag;
+            source.Set("DeviceFlag", df);
+        }
+
+        /// <summary>
+        ///  当前设备是否包含标识
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <param name="flag">标识</param>
+        /// <returns></returns>
+        public static bool Is(this Device source, DF flag)
+        {
+            var df = source.Get<DF>("DeviceFlag");
+            return (df & flag) == flag;
+        }
+
+        /// <summary>
+        ///  为设备写入标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <param name="value">写入值</param>
+        public static void Set<T>(this Device source, string key, T value)
+        {
+            DeviceValues[source.Code + key] = value;
+        }
+
+        /// <summary>
+        ///  获取设备标识
+        /// </summary>
+        /// <typeparam name="T">写入值类型</typeparam>
+        /// <param name="source">设备信息</param>
+        /// <param name="key">标识类型</param>
+        /// <returns></returns>
+        public static T Get<T>(this Device source, string key)
+        {
+            if (!DeviceValues.ContainsKey(source.Code + key))
+                return default(T);
+            return (T)DeviceValues[source.Code + key];
+        }
+
+        /// <summary>
+        ///  获取short类型的设备号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static short Code(this Device source)
+        {
+            return short.Parse(source.Code);
+        }
+
+        /// <summary>
+        ///  获取巷道
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道Code</returns>
+        public static string Tunnel(this Device source)
+        {
+            return source.Get<string>("Tunnel");
+        }
+
+        /// <summary>
+        ///  获取巷道号
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns>返回巷道号</returns>
+        public static int TunnelNum(this Device source)
+        {
+            return int.Parse(source.Tunnel().Last().ToString());
+        }
+
+        /// <summary>
+        ///  获取设备楼层信息
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static int Floor(this Device source)
+        {
+            return source.Get<int>("Floor");
+        }
+
+        /// <summary>
+        ///  获取堆垛机
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device SRM(this Device source)
+        {
+            return source.Get<Device>("SRM");
+        }
+
+        /// <summary>
+        ///  获取RGV
+        /// </summary>
+        /// <param name="source">设备信息</param>
+        /// <returns></returns>
+        public static Device RGV(this Device source)
+        {
+            return source.Get<Device>("RGV");
+        }
+
+        //public static void AddFlag(DF flag, params string[] devices)
+        //{
+        //    var arr = LogicHandler.AllObjects.OfType<WCS_DEVICE>().Where(v => devices.Contains(v.CODE)).ToArray();
+        //    Parallel.ForEach(arr, v =>
+        //    {
+        //        v.AddFlag(flag);
+        //    });
+        //}
+
+        #endregion WCS_DEVICE扩展数据
+    }
+
+    /// <summary>
+    /// 设备配置
+    /// </summary>
+    [Flags]
+    public enum DF
+    {
+        无 = 0,
     }
 }

+ 1 - 1
盘条库/WCS.WorkEngineering/WCS.WorkEngineering.csproj

@@ -8,7 +8,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.5" />
-    <PackageReference Include="ServiceCenter" Version="1.0.0.12" />
+    <PackageReference Include="ServiceCenter" Version="1.0.1.8" />
   </ItemGroup>
 
   <ItemGroup>

+ 108 - 9
盘条库/WCS.WorkEngineering/Worlds/MainWorld.cs

@@ -1,16 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Collections.Concurrent;
+using System.ComponentModel;
 using WCS.Core;
+using WCS.WorkEngineering.Worlds.Logs;
+using KnownException = WCS.WorkEngineering.Worlds.Logs.KnownException;
+using LogInfo = WCS.WorkEngineering.Worlds.Logs.LogInfo;
 
 namespace WCS.WorkEngineering.Worlds
 {
     /// <summary>
     /// 主世界,所有的系统(交互点)默认在该世界下执行。
-    /// 如有系统需独立,请自行增加
+    /// 如有系统需独立,请自行增加对应世界
+    /// 新增世界应当继承此世界,而不是直接继承World
     /// </summary>
+    [Description("主世界")]
     public class MainWorld : World
     {
         /// <summary>
@@ -18,15 +20,112 @@ namespace WCS.WorkEngineering.Worlds
         /// </summary>
         public MainWorld()
         {
-            Interval = 300;
         }
 
         /// <summary>
-        /// 更新后执行
+        ///  日志队列
+        /// </summary>
+        protected ConcurrentQueue<KeyLog> Logs = new ConcurrentQueue<KeyLog>();
+
+        /// <summary>
+        ///  世界执行周期间隔
+        ///  单位:毫秒
+        /// </summary>
+        protected override int Interval => 300;
+
+        /// <summary>
+        ///  更新前执行,重写改方法后请自行添加执行内容
+        ///  执行内容:清空日志队列
+        /// </summary>
+        protected override void BeforeUpdate()
+        {
+            // 清空日志队列,确保日志队列中只会有当前周期日志
+            Logs.Clear();
+        }
+
+        /// <summary>
+        /// 更新后执行,重写改方法后请自行添加执行内容
+        /// 执行内容:清空日志队列
         /// </summary>
         protected override void AfterUpdate()
         {
-            //var objs = GetSystem<CollectConvDataSys>().Objects;
+            #region 日志处理
+
+            //TODO:暂时仅将日志记录至文本文件
+            if (Logs.Count() > 0)
+            {
+                var time = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:FF")}";
+                var fst = Logs.First();
+                var dir = $"Logs\\{DateTime.Now.ToString("yyyy-MM-dd")}\\{fst.Channel.World}\\{fst.Channel.System}\\";
+                if (!Directory.Exists(dir))
+                    Directory.CreateDirectory(dir);
+
+                foreach (var log in Logs)
+                {
+                    var msg = $"--------------------{log.Time}[{Thread.CurrentThread.ManagedThreadId}]--------------------\n{log}\n";
+                    File.AppendAllText(Path.Combine(dir, $"{log.Log.Message.Split(":")[0]}.txt"), msg);
+                }
+            }
+
+            #endregion 日志处理
+        }
+
+        /// <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.NotUpLoad, 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)
+        {
+            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 };
+            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());
         }
     }
 }