using DBHelper; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using WCS.Core; using WCS.Entity; using WCS.Entity.Protocol; using WCS.Entity.Protocol.SRM; using WCS.Service.Entity; using WCS.Service.Extensions; using WCS.Service.Handlers; using WCS.Service.Helpers; using WCS.Service.Log; namespace WCS.Service.Works.SRM { [WorkTitle(typeof(SRMHandler), "堆垛机")] internal class SRMWork : DeviceWork { protected override void Do(SRMDevice obj) { obj.EX(srmDevice => { var deviceCode = srmDevice.Entity.CODE; //先检查堆垛机是否报警 if (srmDevice.Data3.SCAlarm != 0) { if (srmDevice.Entity.WakeupOn(5000)) WMS.DevInfo(srmDevice.Entity.CODE, srmDevice.Data3.SCAlarm.ToString()); InfoLog.INFO_SRMALARM($"{srmDevice.Entity.CODE}-{srmDevice.Data3.SCAlarm}"); return; } if (srmDevice.Data.FinishedACK_1 == 1 || srmDevice.Data.FinishedACK_2 == 1) throw new WarnException($"堆垛机完成任务WCS反馈信号未清除"); if (srmDevice.Data2.VoucherNo_1 != srmDevice.Data.VoucherNo_1 || srmDevice.Data2.VoucherNo_2 != srmDevice.Data.VoucherNo_2) { DB.Do(db => { if (srmDevice.Data2.VoucherNo_1 != srmDevice.Data.VoucherNo_1) { if (db.Default.Set().Find(srmDevice.Data.TaskID_1)!.STATUS == TaskStatus.新建) { srmDevice.Data.VoucherNo_1 = srmDevice.Data2.VoucherNo_1; } } if (srmDevice.Data2.VoucherNo_2 != srmDevice.Data.VoucherNo_2) { if (db.Default.Set().Find(srmDevice.Data.TaskID_2)!.STATUS == TaskStatus.新建) { srmDevice.Data.VoucherNo_2 = srmDevice.Data2.VoucherNo_2; } } }); throw new WarnException($"等待执行{srmDevice.Data.TaskID_1}-{srmDevice.Data.TaskID_2}"); } //处理堆垛机已完成的任务 if (srmDevice.Data2.FinishedTask_1 != 0 || srmDevice.Data2.FinishedTask_2 != 0) { InfoLog.INFO_SRMINFO($"开始完成任务:[{srmDevice.Entity.CODE}]-{srmDevice.Data2.FinishedTask_1}-{srmDevice.Data2.FinishedTask_2}"); srmDevice.FinishedTaskHandle(); InfoLog.INFO_SRMINFO($"完成任务处理结束:[{srmDevice.Entity.CODE}]-{srmDevice.Data2.FinishedTask_1}-{srmDevice.Data2.FinishedTask_2}"); return; } if (srmDevice.Data2.SRMMode != SCMode.远程) return; if (srmDevice.Data2.SRMStatus != SCRunStatus.空闲) return; ////检查标记好的出库任务,并将出库任务下达至堆垛机 //DB.Do(db => //{ // //找到两个任务 // var taksLsit = db.Default.Set().Where(v => // v.ID == srmDevice.Data.TaskID_1 || // v.ID == srmDevice.Data.TaskID_2 && v.STATUS == TaskStatus.堆垛机执行).ToList(); // var itemList = taksLsit.Select(v => v.Create()).OrderBy(v => v.Col).ToArray(); // if (!taksLsit.Any()) return; // for (var i = 0; i < itemList.Length; i++) // { // var item = itemList[i]; // item.SRMSTATION = taksLsit.FirstOrDefault(v => v.ID==item.ID)!.SRMSTATION; // var fork = srmDevice.GetFork(item, i); // switch (fork) // { // // 列数较小的放一工位 // case SrmFork.货叉1: // obj.WriteTask1(item, (short)itemList.Length); // break; // //列数较大的放二工位 // case SrmFork.货叉2: // obj.WriteTask2(item, (short)itemList.Length); // break; // default: // throw new ArgumentOutOfRangeException(); // } // } //}); var isTransfer = new List(); //是否有移库任务 WCS_TASK enterPriority = new(), outPriority = new(); //出入库优先级任务 //再检查是否有等待执行的货物 DB.Do(db => { var task = db.Default.Set().Where(v => v.DEVICE == srmDevice.Entity.CODE).FirstOrDefault(v => v.STATUS == TaskStatus.堆垛机执行); if (task != null) throw new WarnException($"[{deviceCode}]有正在执行的任务:[{task.ID}]"); //属于当前堆垛机未执行的移库任务 isTransfer = db.Default.Set().AsNoTracking().Where(v => v.DEVICE == srmDevice.Entity.CODE && v.TYPE == TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行).ToList(); //判断是否存在调整优先级任务,存在初始化isTransfer值 让本次执行优先任务 if (db.Default.Set().AsNoTracking().Any(v => v.DEVICE == srmDevice.Entity.CODE && v.TYPE != TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行 && v.Priority > 0)) isTransfer = new List(); enterPriority = db.Default.Set().Where(v => v.DEVICE == srmDevice.Entity.CODE && v.TYPE == TaskType.入库 && v.STATUS < TaskStatus.堆垛机执行) .OrderByDescending(v => v.Priority).FirstOrDefault(); outPriority = db.Default.Set().Where(v => v.DEVICE == srmDevice.Entity.CODE && v.TYPE == TaskType.出库 && v.STATUS < TaskStatus.堆垛机执行) .OrderByDescending(v => v.Priority).FirstOrDefault(); }); //最后一个是否是出库任务 var LastIsOut = srmDevice.Entity.Get("LastIsOut"); srmDevice.Entity.Set("LastIsOut", !LastIsOut); if (isTransfer.Count > 0) //防止因为无当前堆垛机移库任务导致无法执行其他类型任务 { #region 移库 DB.Do(db => { //获取当前堆垛机未执行的任务的组ID var taskGroupKey = db.Default.Set().Where(v => v.DEVICE == srmDevice.Entity.CODE && v.TYPE == TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行).OrderBy(p => p.CREATETIME).FirstOrDefault()!.TaskGroupKey; //通过任务的组ID找到本组的所有任务 var tasks = db.Default.Set().Where(v => v.TaskGroupKey == taskGroupKey); var taskList = tasks.Select(v => v.Create()); foreach (var item in taskList) { } //双工位 货架列单数为一工位取放点 货架列双数为二工位取放点 foreach (var task in tasks) { var addrFrom = task.ADDRFROM.Split("-"); var addrTo = task.ADDRTO.Split("-"); var oldTaskSTATUS = task.STATUS; task.STARTTIME = DateTime.Now; task.UPDATETIME = DateTime.Now; task.STATUS = WCS.Entity.TaskStatus.堆垛机执行; task.DEVICE = srmDevice.Entity.CODE; db.Default.SaveChanges(); Uploader.Upload(db); task.CreateStatusLog(db, $"状态由[{oldTaskSTATUS}]变更为[{task.STATUS}]", this.GetType()); if (addrFrom[2].ToShort().OddNumberOrEven()) { srmDevice.Data.TaskID_1 = task.ID; srmDevice.Data.SLine_1 = addrFrom[0].ToShort(); srmDevice.Data.SCol_1 = addrFrom[1].ToShort(); srmDevice.Data.SLayer_1 = addrFrom[2].ToShort(); srmDevice.Data.ELine_1 = addrTo[0].ToShort(); srmDevice.Data.ECol_1 = addrTo[1].ToShort(); srmDevice.Data.ELayer_1 = addrTo[2].ToShort(); srmDevice.Data.VoucherNo_1++; } else { srmDevice.Data.TaskID_2 = task.ID; srmDevice.Data.SLine_2 = addrFrom[0].ToShort(); srmDevice.Data.SCol_2 = addrFrom[1].ToShort(); srmDevice.Data.SLayer_2 = addrFrom[2].ToShort(); srmDevice.Data.ELine_2 = addrTo[0].ToShort(); srmDevice.Data.ECol_2 = addrTo[1].ToShort(); srmDevice.Data.ELayer_2 = addrTo[2].ToShort(); srmDevice.Data.VoucherNo_2++; } } }); #endregion 移库 } else if (LastIsOut) { #region 入库 var floor = srmDevice.Entity.Get("LastInFloor"); floor = floor % 2 + 1; srmDevice.Entity.Set("LastInFloor", floor); if (enterPriority != null && outPriority != null && outPriority.Priority > enterPriority.Priority) return; var arrIn = srmDevice.GetPickPoint() .Where(v => Device.Where(d => d.IsConv()).Select(d => d.Device()).Any(d => d.Data.Goodsend == v.Entity.Code())) //有正在前往取货点的任务 .ToList(); if (!arrIn.Any()) return; //当前堆垛机无入库任务 //入库口设备信息 找一个有任务有光电且不在运行状态位的取货点 如果找不到代表任务还在输送途中 var station = arrIn.OrderBy(v => v.Data2.Tasknum > 0 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位) ? 0 : 1) .ThenBy(v => v.UpdateTime) .FirstOrDefault() ?? throw new WarnException($"[{deviceCode}]等待入库任务输送到位"); //根据上述筛选条件筛选出来的入库任务 找到对应的设备组 var item = Device.Where(v => v.DEVICEGROUP.Any(p => p.MEMBER.CODE == station.Entity.CODE)).Single(); //对数据进行排序,根据CAD图纸规划,取货点两个设备中设备号较大的一个一定对应到堆垛机的一工位货叉 //因此按照降序进行排序,可以保证数组中第一个一定是放到堆垛机一工位的数据 var devise = item.DEVICEGROUP.Select(v => v.MEMBER) .Select(v => v.Create()) .OrderByDescending(v => v.Entity.CODE) .ToArray(); var finishTaskList = new List>(); //成功分配货位的任务 //取巷道 DB.Do(db => { //检测有效任务数与实际任务是是否相等 var validDev = devise.Where(v => v.Data2.Tasknum > 10000 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位)); if (!validDev.Any()) throw new DoException("无有效入库任务"); var tasknum = db.Default.Set().FirstOrDefault(v => v.ID == validDev.First().Data2.Tasknum); var taskList = db.Default.Set().Count(v => v.TaskGroupKey == tasknum.TaskGroupKey && v.TYPE == TaskType.入库); if (validDev.Count() != taskList) throw new WarnException($"任务数量不匹配,设备-{validDev.Count()},WCS-{taskList}"); foreach (var dev in validDev) { var task = db.Default.Set().FirstOrDefault(v => v.STATUS < TaskStatus.堆垛机执行 && v.ID == dev.Data2.Tasknum) ?? throw new WarnException($"设备有光电有任务且不在运行状态,但WCS找不到任务{dev.Data2.Tasknum}"); var oldTask = task.STATUS; var tunnel = dev.Entity.ROUTES.First().NEXT.CODE; I_WCS_GetWareCellResponse loc; //判定当前设备对应的堆垛机工位 if (dev.Entity.CODE == devise[0].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉1); //一工位 else if (dev.Entity.CODE == devise[1].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉2); //2工位 else throw new WarnException($"设备{dev.Entity.CODE}无法对应至堆垛机的任一一个工位"); task.UPDATETIME = DateTime.Now; task.STATUS = TaskStatus.堆垛机执行; task.ADDRTO = $"{loc.Row}-{loc.Colomn}-{loc.Layer}"; task.DEVICE = srmDevice.Entity.CODE; task.TUNNEL = tunnel; task.CreateStatusLog(db, $"状态由{oldTask}变更至{task.STATUS}", this.GetType()); finishTaskList.Add(new FinishTaskList(task.ID, dev)); } db.Default.SaveChanges(); }); DB.Do(db => { foreach (var finish in finishTaskList) { var task = db.Default.Set().Find(finish.FinishCode); var addTo = task.ADDRTO.Split("-"); if (finish.Station.Entity.CODE == devise[0].Entity.CODE) //一工位 { InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{srmDevice.Entity.CODE}]1工位-开始:[{srmDevice.Data.TaskID_1}][{srmDevice.Data.SLine_1}][{srmDevice.Data.ELine_1}][{srmDevice.Data.ECol_1}][{srmDevice.Data.ELayer_1}][{srmDevice.Data.VoucherNo_1}]--[{finishTaskList.Count.ToShort()}]"); srmDevice.Data.TaskID_1 = task.ID; srmDevice.Data.SLine_1 = finish.Station.Entity.CODE.ToShort(); srmDevice.Data.SCol_1 = 0; srmDevice.Data.SLayer_1 = 0; srmDevice.Data.ELine_1 = addTo[0].ToShort(); srmDevice.Data.ECol_1 = addTo[1].ToShort(); srmDevice.Data.ELayer_1 = addTo[2].ToShort(); srmDevice.Data.RES1_1 = finishTaskList.Count.ToShort(); srmDevice.Data.VoucherNo_1++; InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{srmDevice.Entity.CODE}]1工位-结束:[{srmDevice.Data.TaskID_1}][{srmDevice.Data.SLine_1}][{srmDevice.Data.ELine_1}][{srmDevice.Data.ECol_1}][{srmDevice.Data.ELayer_1}][{srmDevice.Data.VoucherNo_1}]--[{finishTaskList.Count.ToShort()}]"); } else if (finish.Station.Entity.CODE == devise[1].Entity.CODE) { InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{srmDevice.Entity.CODE}]2工位-开始:[{srmDevice.Data.TaskID_2}][{srmDevice.Data.SLine_2}][{srmDevice.Data.ELine_2}][{srmDevice.Data.ECol_2}][{srmDevice.Data.ELayer_2}][{srmDevice.Data.VoucherNo_2}]--[{finishTaskList.Count.ToShort()}]"); srmDevice.Data.TaskID_2 = task.ID; srmDevice.Data.SLine_2 = finish.Station.Entity.CODE.ToShort(); srmDevice.Data.SCol_2 = 0; srmDevice.Data.SLayer_2 = 0; srmDevice.Data.ELine_2 = addTo[0].ToShort(); srmDevice.Data.ECol_2 = addTo[1].ToShort(); srmDevice.Data.ELayer_2 = addTo[2].ToShort(); srmDevice.Data.RES1_2 = finishTaskList.Count.ToShort(); srmDevice.Data.VoucherNo_2++; InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{srmDevice.Entity.CODE}]2工位-结束:[{srmDevice.Data.TaskID_2}][{srmDevice.Data.SLine_2}][{srmDevice.Data.ELine_2}][{srmDevice.Data.ECol_2}][{srmDevice.Data.ELayer_2}][{srmDevice.Data.VoucherNo_2}]--[{finishTaskList.Count.ToShort()}]"); } } }); #endregion 入库 } else { #region 出库 srmDevice.CheckOutTask(); var floor = srmDevice.Entity.Get("LastOutFloor"); floor = floor % 2 + 1; srmDevice.Entity.Set("LastOutFloor", floor); if (enterPriority != null && outPriority != null && enterPriority.Priority > outPriority.Priority) return; //获取当前堆垛机所有的放货点 var list = srmDevice.GetDeliveryPoint(); list = list.Where(v => { //true:满足条件 false:不满足条件 //返回结果为无货的设备 默认无货 var res = true; //放货点是否有货 if (v.Data.VoucherNo != v.Data2.VoucherNo) res = false; else if (v.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false; else if (v.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false; else if (v.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false; else if (v.Data2.Tasknum > 10000) res = false; else if (v.Entity.Is(DF.SRM涂布放货)) //涂布分货,应为电控不会等到RGV取货点无货时,再让货物离开无动力站台,因此需要判断一次旋转台的状态 { var devise = new List(); switch (v.Entity.CODE) { case "1283" or "1284": devise = Device.Where(b => b.CODE is "1281" or "1282").Select(b => b.Create()).ToList(); break; case "1290" or "1291" or "1292" or "1293": devise = Device.Where(b => b.CODE is "1288" or "1289").Select(b => b.Create()).ToList(); break; case "1299" or "1300" or "1301" or "1302": devise = Device.Where(b => b.CODE is "1297" or "1298").Select(b => b.Create()).ToList(); break; case "1308" or "1309" or "1310" or "1311": devise = Device.Where(b => b.CODE is "1306" or "1307").Select(b => b.Create()).ToList(); break; } if (!devise.Any()) return res; foreach (var stationDevice in devise) { //放货点是否有货 if (stationDevice.Data.VoucherNo != stationDevice.Data2.VoucherNo) res = false; else if (stationDevice.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false; else if (stationDevice.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false; else if (stationDevice.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false; else if (stationDevice.Data2.Tasknum > 10000) res = false; } } return res; }).ToList(); //没有可用货位 if (list.Count == 0) return; //堆垛机设备 var srm = srmDevice.Entity.CODE; DB.Do(db => { //堆垛机当前是否有正在执行的任务 if (db.Default.Set().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行)) throw new WarnException($"[{deviceCode}]有正在执行的出库任务"); //找出等待执行的出库任务 var waitTask = db.Default.Set().Where(v => v.STATUS == TaskStatus.新建) .Where(v => v.DEVICE == srm) .Where(v => v.TYPE == TaskType.出库) .Where(v => !db.Default.Set().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行)).ToList(); //同时对结果进行排序,分组 var maximum = ProtocolProxy.YGWMS150Redis.Get("SaleTaskGroupCount").ToInt(); //所有 有优先级任务的出货口 var priorityADDRTO = db.Default.Set().Where(v => v.Priority > 0 && v.STATUS == TaskStatus.新建 && v.DEVICE != srmDevice.Entity.CODE).ToList() .GroupBy(v => v.ADDRTO).Select(v => new { v.Key, List = v.Select(p => p.DEVICE).Distinct().ToList() }) .Where(v => v.List.Where(p => { var dev = Device.Find(p).Create(); return dev.Data2.SRMMode == SCMode.远程 && dev.Data2.SRMStatus == SCRunStatus.空闲; }).Any()) .Select(v => v.Key) .ToList(); var cTaskList = db.Default.Set().AsNoTracking() .Where(d => d.TYPE == TaskType.出库) .Where(d => d.STATUS > TaskStatus.新建) .Where(d => d.STATUS < TaskStatus.已完成).ToList(); var outDepotList = waitTask.Where(v => cTaskList.Where(d => d.ADDRTO == v.ADDRTO && d.FLOOR == v.FLOOR).GroupBy(d => d.TaskGroupKey).Count() < maximum) .OrderByDescending(v => v.Priority) .ThenBy(v => v.ADDRTO == "G1340" ? 0 : 1) .ThenBy(v => v.FLOOR == floor ? 0 : 1) .ThenBy(v => cTaskList.Where(d => d.ADDRTO == v.ADDRTO && d.FLOOR == v.FLOOR).GroupBy(d => d.TaskGroupKey).Count()) .ThenBy(v => v.CREATETIME) .GroupBy(v => v.ADDRTO) .FirstOrDefault(v => !priorityADDRTO.Contains(v.Key))! .Select(v => v).ToList(); //获取两个个可执行任务,此时这两个任务的目标地址是一致的 var tasks = outDepotList.GetOutTask(); #region 校验两个产品是否为同规格 if (tasks.Length == 2) { var length = tasks.OrderByDescending(v => v.Length).ToArray(); //较大的长度减去较小的长度,差大于两百表示为不同规格产品 if (length[0].Length - length[1].Length > 200) { tasks = tasks.Take(1).ToArray(); } } #endregion 校验两个产品是否为同规格 var finishTaskList = new List>(); for (var i = 0; i < tasks.Length; i++) { var item = tasks[i]; var task = db.Default.Set().Find(item.ID); var oldTaskSTATUS = task.STATUS; task.STARTTIME = DateTime.Now; task.UPDATETIME = DateTime.Now; task.STATUS = WCS.Entity.TaskStatus.堆垛机执行; task.DEVICE = srmDevice.Entity.CODE; task.TaskGroupKey = tasks.Length switch { 1 => $"{tasks[0].ID}_0", 2 => $"{tasks[0].ID}_{tasks[1].ID}", _ => throw new WarnException($"可用任务数异常{tasks.Length}"), }; var fork = srmDevice.GetFork(item, i); //获取站台及下一个地址 task.GetSrmStationAndaddNext(fork); var msg = ""; msg = fork == SrmFork.货叉1 ? $"状态由[{oldTaskSTATUS}]变更为[{task.STATUS}][{srmDevice.Data.SLine_1}-{srmDevice.Data.SCol_1}-{srmDevice.Data.SLayer_1}][{srmDevice.Data.ELine_1}][{srmDevice.Data.VoucherNo_1}]" : $"状态由[{oldTaskSTATUS}]变更为[{task.STATUS}][{srmDevice.Data.SLine_2}-{srmDevice.Data.SCol_2}-{srmDevice.Data.SLayer_2}][{srmDevice.Data.ELine_2}][{srmDevice.Data.VoucherNo_2}]"; task.CreateStatusLog(db, msg, this.GetType()); item.SRMSTATION = task.SRMSTATION; finishTaskList.Add(new FinishTaskList(fork, item)); } db.Default.SaveChanges(); //此处只做标记,表示当前事务已经提交 foreach (var finish in finishTaskList) { switch (finish.FinishCode) { // 列数较小的放一工位 case SrmFork.货叉1: obj.WriteTask1(finish.Station, (short)tasks.Length); break; //列数较大的放二工位 case SrmFork.货叉2: obj.WriteTask2(finish.Station, (short)tasks.Length); break; default: throw new ArgumentOutOfRangeException(); } } }); #endregion 出库 } }); } protected override bool SelectDevice(WCS_DEVICE dev) { return dev.Is(DF.SRM); } } }