using Newtonsoft.Json; using ServiceCenter.Extensions; using ServiceCenter.Logs; using ServiceCenter.Redis; using ServiceCenter.SqlSugars; using SqlSugar; using System.ComponentModel; using WCS.Core; using WCS.Entity; using WCS.Entity.Protocol.Station; using WCS.Entity.Protocol.Truss; using WCS.WorkEngineering.Extensions; using WCS.WorkEngineering.Worlds; using DeviceFlags = WCS.WorkEngineering.Extensions.DeviceFlags; using TaskStatus = WCS.Entity.TaskStatus; using WCS_CacheLine = WCS.Entity.WCS_CacheLine; using WCS_CacheLineLoc = WCS.Entity.WCS_CacheLineLoc; namespace WCS.WorkEngineering.Systems.分拣支线 { /// /// 桁架分流点 /// [BelongTo(typeof(SortingBranchWorld))] [Description("桁架分流点")] public class 桁架分流点 : DeviceSystem> { protected override bool ParallelDo => true; public override void Do(Device obj) { if (obj.Data.VoucherNo != obj.Data2.VoucherNo) { World.Log($"凭证号不一致,DB520:{obj.Data.VoucherNo}-DB521:{obj.Data2.VoucherNo}"); return; } if (obj.Data3.Status.HasFlag(StationStatus.Run)) { World.Log("设备运行中"); return; } if (!obj.Data3.Status.HasFlag(StationStatus.OT_Status)) { World.Log("站台货物信息与实际占用不一致"); return; } ; if (obj.Data2.Request != 1) { World.Log("无请求"); return; } WCS_TaskInfo taskInfo = null; SqlSugarHelper.Do(_db => { var db = _db.Default; var task = db.Queryable() .First(v => v.ID == obj.Data2.TaskNumber && v.Status < TaskStatus.FinishOfShunt) ?? throw new KnownException($"未找到对应的WCS任务{obj.Data2.TaskNumber}", LogLevelEnum.Mid); if (Allot(db, task, obj)) taskInfo = task; }); if (taskInfo == null) return; obj.Data.TaskNumber = obj.Data2.TaskNumber; obj.Data.TaskNumber = obj.Data2.TaskNumber; obj.Data.GoodsStart = obj.Entity.Code.ToShort(); obj.Data.GoodsStart = obj.Entity.Code.ToShort(); obj.Data.GoodsEnd = taskInfo.AddrNext.ToShort(); obj.Data.GoodsEnd = taskInfo.AddrNext.ToShort(); obj.Data.SetVoucherNo(); World.Log($"执行记录:任务号[{obj.Data2.TaskNumber}][{obj.Data.VoucherNo}]"); } public override bool Select(Device dev) { return dev.HasFlag(Extensions.DeviceFlags.桁架分流点); } /// /// 计算去向 /// /// /// /// /// public bool Allot(SqlSugarScopeProvider db, WCS_TaskInfo taskInfo, Device obj) { var nowTime = DateTime.Now; taskInfo.Device = obj.Entity.Code switch { "455" => "Truss1", "655" => "Truss1", "855" => "Truss2", "1055" => "Truss2", "1255" => "Truss3", "1455" => "Truss3", _ => "Error" }; //获取当前货物类型可以到达的桁架缓存放行点信息 var cacheLineDevList = obj.Entity.Targets.Where(x => x.HasFlag(DeviceFlags.桁架缓存放行点)); //当前地址可以到达的所有桁架缓存放行点信息 switch (taskInfo.GoodsType) { case 9: //是异常工字轮 cacheLineDevList = taskInfo.BarCode.Contains("Error") ? cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架09异常缓存放行点)) : cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架09缓存放行点)); break; case 18: cacheLineDevList = cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架18缓存放行点)); break; } var value = RedisHub.Default.Get("TrussTunnelForbidden"); if (!value.Contains("[")) { World.Log($"没有桁架巷道禁用配置信息,请检查", LogLevelEnum.High); return false; } var trussTunnelForbidden = JsonConvert.DeserializeObject>(value); //排除禁用位置 cacheLineDevList = cacheLineDevList.Where(x => !trussTunnelForbidden.Contains(x.Code)); var cacheLineCodes = cacheLineDevList.Select(x => x.Code.ToShort()); var cacheLineList = db.Queryable().Includes(x => x.Locations).ToList(); #region 跟据缓存信息寻找可以到达的缓存点 //找到当前任务可用的缓存线信息 var cacheLine = cacheLineList.Where(x => x.Locations.Any(l => l is { InStock: false, IsEmpty: false })) .FirstOrDefault(x => cacheLineCodes.Contains(x.LocationNo) && x.MatCodeList.Contains(taskInfo.MatCode) && !x.InStock); //这个任务可以直接去一条线体,不需要新建缓存信息 //找到这条线体中序号最小的一条位信息 非空置且无货 var cacheLoc = cacheLine?.Locations.Where(x => x is { InStock: false, IsEmpty: false }).MinBy(x => x.XYNo); if (cacheLoc != null) { cacheLoc = db.Queryable().Single(x => x.Id == cacheLoc.Id); cacheLoc.InStock = true; cacheLoc.TaskId = taskInfo.ID; cacheLoc.EditTime = nowTime; db.UpdateableRowLock(cacheLoc).UpdateColumns(x => new { x.InStock, x.TaskId, x.EditTime }) .ExecuteCommand(); //WCS任务相关信息 taskInfo.Status = TaskStatus.FinishOfShunt; taskInfo.AddrNext = cacheLine.LocationNo.ToString(); taskInfo.EditWho = "WCS"; taskInfo.EditTime = nowTime; db.UpdateableRowLock(taskInfo).UpdateColumns(x => new { x.Status, x.AddrNext, x.EditWho, x.EndTime }) .ExecuteCommand(); taskInfo.AddWCS_TASK_DTL(db, obj.Entity.Code, taskInfo.AddrNext, $"完成分库计算,目标地址:{cacheLine.LocationNo}"); return true; } #endregion 跟据缓存信息寻找可以到达的缓存点 #region 初始化一个信息的缓存信息 //找到一个可以进行缓存的线体号 var devCode = cacheLineDevList.Select(x => x.Code.ToShort()).OrderByDescending(x => x) .FirstOrDefault(x => !cacheLineList.Select(s => s.LocationNo).Contains(x)); if (devCode == 0) { World.Log($"无可用线体:{taskInfo.ID}", LogLevelEnum.Mid); //如果当前工字轮是18,不会触发当前逻辑,因为所有在桁架码垛的18工字轮仅一种规格 var endTime = DateTime.Now.AddMinutes(-30); //找一个当前时间最早且已超过30分钟未码垛的码垛信息半托入库 //未结束且包含当前物料编号的垛形,按时间排序,创建时间早的优先分配,当前任务的仓库号必须要等于码垛信息绑定的仓库号 var palletizing = db.Queryable() .Includes(x => x.Layers, r => r.Rows, l => l.Locs) .Where(x => !x.Finish && x.WarehouseCode == taskInfo.WarehouseCode && x.Layers.Any(l => l.Rows.Any(r => r.Finish))) .Where(x => x.EditTime < endTime).ToList() .Where(x => x.Layers.SelectMany(l => l.Rows).GroupBy(r => r.MatCodeList).Count() > 1) .MinBy(x => x.AddTime); cacheLineList = db.Queryable() .Where(x => x.WarehouseCode == taskInfo.WarehouseCode && x.IsTruss).ToList(); if (palletizing == null || cacheLineList.Any()) return false; { var dev = Device.All.Where(x => x.Code == palletizing.PalletizingStation) .Select(x => new Device(x, World)).FirstOrDefault(); if (dev != null && !dev.Data.CmdType.HasFlag(TrussCmdType.Two) && !dev.Data.CmdType.HasFlag(TrussCmdType.End1)) { //如果找到的最早的一个托盘超六小时了,就直接强制结盘 dev.Data.CmdType = palletizing.AddTime < DateTime.Now.AddHours(-6) ? TrussCmdType.End1 : TrussCmdType.Two; } } return false; } var result = false; //未结束且包含当前物料编号的垛形,按时间排序,创建时间早的优先分配,当前任务的仓库号必须要等于码垛信息绑定的仓库号 var palletizingList = db.Queryable() .Includes(x => x.Layers, r => r.Rows, l => l.Locs) .Where(x => (!x.Finish /*|| (x.Finish && x.isItHalf)*/) && x.MatCodeList.Contains(taskInfo.MatCode) && x.WarehouseCode == taskInfo.WarehouseCode) .OrderBy(x => x.AddTime) .ToList(); foreach (var palletizing in palletizingList) { //当前任务已经分配结束,进入下一次迭代 if (result) continue; //未结束且包含当前物料编号的层,按层号排序,层号小的优先分配 var layer = palletizing.Layers.Where(x => x is { IsEmpty: false, Finish: false }) .Where(x => x.MatCodeList.Contains(taskInfo.MatCode)).MinBy(x => x.LayerNo); //未找到可用层,进入下一次迭代 if (layer == null) continue; // 当前任务已经分配结束,进入下一次迭代 //未结束,未预分配缓存线信息且包含当前物料编号的行,行号小的优先分配 var rows = layer.Rows.Where(x => x is { IsEmpty: false, Finish: false }) .Where(x => x.CacheLineId == 0) .Where(x => x.MatCodeList.Contains(taskInfo.MatCode)) .ToList(); //未找到可用行,进入下一次迭代 if (!rows.Any()) continue; var palletizingRow = rows.MinBy(x => x.RowNo); //开始初始化缓存位信息 cacheLine = new WCS_CacheLine() { LocationNo = devCode, AddTime = nowTime, EditTime = nowTime, PalletizingRowId = palletizingRow.Id, InStock = false, Put = false, IsTruss = false, MatCodeList = palletizingRow.MatCodeList, Quantity = palletizingRow.QtyMaxCount, WarehouseCode = taskInfo.WarehouseCode }; if (palletizing.isItHalf) //当前任务被分配到了二次码垛托盘上了。 { var twoDevCode = obj.Entity.Code switch { "455" => "1606", "655" => "1616", "855" => "1626", "1055" => "1636", "1255" => "1646", "1455" => "1656", _ => throw new ArgumentOutOfRangeException() }; //找到一个对应的空置码垛位 var twoDevs = Device.All.Where(x => twoDevCode == x.Code) .Select(x => new Device(x, World)) .Where(x => !x.Data3.Status.HasFlag(StationStatus.PH_Status)).Select(x => x.Entity.Code); //找到一个对应的空置码垛位 var twoDev = new Device(Device.All.First(x => x.Code == twoDevCode), World); if (twoDev.Data3.Status.HasFlag(StationStatus.PH_Status)) { World.Log("无可用的二次码垛工位"); continue; } if (db.Queryable().Any(x => !x.Finish && twoDevs.Contains(x.PalletizingStation))) { World.Log($"有正目标地址是{JsonConvert.SerializeObject(twoDevs)}的未结束二次码垛任务,无可用的二次码垛工位"); continue; } palletizing.EditTime = nowTime; palletizing.Finish = false; palletizing.PalletizingStation = twoDevs.FirstOrDefault(); palletizing.TaskAddNext = devCode.ToString(); db.UpdateableRowLock(palletizing).UpdateColumns(x => new { x.EditTime, x.Finish, x.PalletizingStation, x.TaskAddNext }).ExecuteCommand(); } if (palletizing.TaskAddNext == null) { palletizing.TaskAddNext = devCode.ToString(); db.Updateable(palletizing).UpdateColumns(x => new { x.TaskAddNext }).ExecuteCommand(); } var res = db.InsertableRowLock(cacheLine).ExecuteReturnEntity(); palletizingRow = db.Queryable().Includes(x => x.Locs) .Single(x => x.Id == palletizingRow.Id); palletizingRow.Locs = palletizingRow.Locs.OrderBy(x => x.XYNo).ToList(); palletizingRow.CacheLineId = res.Id; palletizingRow.EditTime = nowTime; var createCacheLoc = palletizingRow.Locs.Select((t, i) => new WCS_CacheLineLoc() { XYNo = t.XYNo, InStock = i == 0, IsEmpty = t.IsEmpty, MatCode = t.MatCode, TaskId = i == 0 ? taskInfo.ID : 0, CacheLineId = res.Id, AddTime = nowTime, SideNum = t.SideNum, EditTime = nowTime, TimeOut = t.TimeOut }).ToList(); taskInfo.Status = TaskStatus.FinishOfShunt; taskInfo.AddrNext = devCode.ToString(); taskInfo.EditWho = "WCS"; taskInfo.EditTime = nowTime; db.UpdateableRowLock(taskInfo).UpdateColumns(x => new { x.Status, x.AddrNext, x.EditWho, x.EditTime }) .ExecuteCommand(); taskInfo.AddWCS_TASK_DTL(db, obj.Entity.Code, taskInfo.AddrNext, $"完成分库计算,目标地址:{cacheLine.LocationNo}"); db.UpdateableRowLock(palletizingRow).UpdateColumns(x => new { x.CacheLineId, x.EditTime }) .ExecuteCommand(); db.InsertableRowLock(createCacheLoc).ExecuteCommand(); result = true; } if (!result) { taskInfo.InitStackStructure(db, World); } return result; #endregion 初始化一个信息的缓存信息 } } }