using ServiceCenter.Extensions; using ServiceCenter.Logs; using ServiceCenter.SqlSugars; using System.ComponentModel; using WCS.Core; using WCS.Entity; using WCS.Entity.Protocol.BCR; using WCS.Entity.Protocol.RGV; using WCS.Entity.Protocol.Robot; 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; namespace WCS.WorkEngineering.Systems { /// /// RGV交互系统 /// [BelongTo(typeof(RgvWorld))] [Description("RGV交互系统")] public class RGVSystems : DeviceSystem> { protected override bool ParallelDo => true; protected override bool SaveLogsToFile => true; /// /// 取货点设备集合 /// private readonly Dictionary>> _pickUpDevices = new(); public RGVSystems() { //获取所有的巷道集合 var rgvList = Device.All.Where(v => v.HasFlag(DeviceFlags.RGV)); foreach (var rgv in rgvList) { _pickUpDevices.Add(rgv.Code, rgv.Sources.Where(v => v.HasFlag(DeviceFlags.输送机)).Select(v => new Device(v, this.World)).ToList()); } } public override void Do(Device obj) { try { if (obj.Data.VoucherNo != obj.Data2.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{obj.Data.VoucherNo}-DB521:{obj.Data2.VoucherNo}", LogLevelEnum.High); if (obj.Data2.WorkMode != RGVWorkMode.Automatic) return; if (obj.Data.RES1 == 1) return; //wcs任务完成确认信号未清除 if (obj.Data2.Status.HasFlag(RGVStatus.Taskfinishi)) { switch (obj.Data2.CmdType) { case RGVCmdType.PickGoods: //单独取货任务完成,默认只有空托盘才会下发单独取货任务 //开始申请读码信息 var bcrCode = obj.Data3.GetBCRCode(); var taskNumber = 0; SqlSugarHelper.Do(_db => { var db = _db.Default; var dev = Device.All.First(x => x.Code == obj.Data2.DestPosition.ToString()); if (dev.HasFlag(DeviceFlags.桁架码垛位)) { //开始绑定任务,并下发新的任务信息到小车 var palletizingInfo = db.Queryable().Single(x => x.Id == obj.Data2.TaskNumber); if (palletizingInfo == null) return; palletizingInfo.PalleCode = bcrCode; db.Updateable(palletizingInfo).ExecuteCommand(); taskNumber = palletizingInfo.Id; } else if (dev.HasFlag(DeviceFlags.环形库码垛工位)) { //开始处理对应的搬运任务信息 var task = db.Queryable().First(x => x.Type == TaskType.Delivery && x.ID == obj.Data2.TaskNumber && x.AddrTo == obj.Data2.DestPosition.ToString()); if (task == null) throw new KnownException($"未找到对应的任务{obj.Data2.TaskNumber}", LogLevelEnum.Mid); task.BarCode = bcrCode; db.Updateable(task).ExecuteCommand(); task.AddWCS_TASK_DTL(db, obj.Entity.Code, obj.Data2.DestPosition.ToString(), $"环形库码垛位{obj.Data2.DestPosition}搬运任务绑定条码信息{bcrCode}"); taskNumber = task.ID; } }); if (taskNumber == 0) return; //清空起始点信息 var staDev = new Device(Device.All.FirstOrDefault(x => x.Code == obj.Data2.DestPosition.ToString())!, World); staDev.Data.TaskNumber = 0; staDev.Data.GoodsStart = 0; staDev.Data.GoodsEnd = 0; obj.Data2.TaskNumber = taskNumber; obj.Data.RES1 = 1; break; case RGVCmdType.PutGoods: try { WCS_TaskInfo finishiTask = null; var startPosition = Device.All.Where(x => x.Code == obj.Data2.StartPosition.ToString()) .Select(x => new Device(x, World)) .FirstOrDefault(); var destPosition = Device.All.FirstOrDefault(x => x.Code == obj.Data2.DestPosition.ToString()); var isPalletizing = destPosition!.HasFlag(DeviceFlags.桁架码垛位, DeviceFlags.环形库码垛工位); short countQty = 0; short shortCode = 0; SqlSugarHelper.Do(_db => { var db = _db.Default; if (isPalletizing) { if (destPosition.HasFlag(DeviceFlags.桁架码垛位)) { var palletizingInfo = db.Queryable() .First(x => x.Id == obj.Data.TaskNumber); countQty = palletizingInfo.CountQty.ToShort(); shortCode = palletizingInfo.ShortCode; } else if (destPosition.HasFlag(DeviceFlags.环形库码垛工位)) { var deliveryTask = db.Queryable() .First(x => x.ID == obj.Data.TaskNumber); countQty = deliveryTask.FullQty; shortCode = deliveryTask.PalletType; deliveryTask.Status = TaskStatus.RgvCompleted; deliveryTask.EditTime = DateTime.Now; deliveryTask.LastInteractionPoint = obj.Entity.Code; db.Updateable(deliveryTask).ExecuteCommand(); deliveryTask.AddWCS_TASK_DTL(db, deliveryTask.AddrTo, $"RGV任务执行结束"); } } }); if (startPosition.Data.TaskNumber == obj.Data.TaskNumber) //初始化起始点信息 { startPosition.Data.TaskNumber = 0; startPosition.Data.GoodsEnd = 0; } //目标地址是码垛工位 if (isPalletizing) { if (destPosition.HasFlag(DeviceFlags.桁架码垛位)) { var dest = new Device(destPosition!, World); dest.Data.MaxQuantity = countQty; dest.Data.Quantity = 0; dest.Data.Type = shortCode; dest.Data.VoucherNo++; } else if (destPosition.HasFlag(DeviceFlags.环形库码垛工位)) { var dest = new Device(destPosition!, World); dest.Data.MaxQuantity = countQty; dest.Data.Type = shortCode; dest.Data.VoucherNo++; } } } catch (Exception e) { throw new KnownException($"处理小车放货完成是出现错误:{e.Message}-{e.StackTrace}", LogLevelEnum.High); } obj.Data.RES1 = 1; break; case RGVCmdType.Move: obj.Data.RES1 = 1; break; case RGVCmdType.ChangePutGoods: break; case RGVCmdType.ChangePickGoods: break; case RGVCmdType.PickPutGoods: var statDev = Device.All.FirstOrDefault(x => x.Code == obj.Data.StartPosition.ToString()); if (statDev.HasFlag(DeviceFlags.二次码垛RGV取货口)) { var dev = new Device(statDev, World); WCS_Palletizing pall = new WCS_Palletizing(); //获取码垛信息 SqlSugarHelper.Do(_db => { var db = _db.Default; pall = db.Queryable().Includes(x => x.Layers, x => x.Rows, x => x.Locs) .First(x => x.Id == obj.Data2.TaskNumber) ?? throw new KnownException($"未找到对应任务{obj.Data2.TaskNumber}", LogLevelEnum.High); }); var pallLoc = pall.Layers.SelectMany(x => x.Rows).SelectMany(x => x.Locs).Select(x => new { XYNo = x.XYNo.ToShort(), x.Finish }).ToList(); foreach (var loc in pallLoc) { dev.Data3.GetType().GetProperty($"IsGoods{loc.XYNo - 1}").SetValue(obj.Data3, loc.Finish); } dev.Data3.MaxQuantity = pall.CountQty.ToShort(); dev.Data3.Quantity = pallLoc.Count(x => x.Finish).ToShort(); dev.Data3.Type = pall.ShortCode; dev.Data3.VoucherNo++; } else { var destDev = new Device(Device.All.FirstOrDefault(x => x.Code == obj.Data.DestPosition.ToString())!, World); destDev.Data.TaskNumber = 0; destDev.Data.GoodsStart = 0; destDev.Data.GoodsEnd = 0; } obj.Data.RES1 = 1; break; default: throw new ArgumentOutOfRangeException(); } return; } if (obj.Data2.SystemStatus != RGVSystemStatus.空闲) return; if (obj.Data2.Status.HasFlag(RGVStatus.RES1)) //离开非安全区域 { obj.Data.TaskNumber = obj.Data.TaskNumber; obj.Data.CmdType = RGVCmdType.Move; obj.Data.DestPosition = obj.Entity.Code switch { "RGV1" => 1668, "RGV2" => 1683, "RGV3" => 1698, "RGV4" => 1713, "RGV5" => 1728, "RGV6" => 1743, _ => throw new ArgumentOutOfRangeException() }; obj.Data.VoucherNo++; return; } if (obj.Data2.CmdType == RGVCmdType.PickGoods && !obj.Data2.Status.HasFlag(RGVStatus.Taskfinishi)) { if (obj.Data2.Status.HasFlag(RGVStatus.PH_Status)) { obj.Data.TaskNumber = obj.Data.TaskNumber; obj.Data.CmdType = RGVCmdType.PutGoods; obj.Data.StartPosition = obj.Data2.StartPosition; obj.Data.DestPosition = obj.Data2.DestPosition; obj.Data.VoucherNo++; return; } } var pickUpDevices = _pickUpDevices.FirstOrDefault(x => x.Key == obj.Entity.Code).Value; //有货且需要搬运货物的站台 var devs = pickUpDevices.Where(v => v.Data3.Status.HasFlag(StationStatus.PH_Status) && v.Data.TaskNumber != 0) .Where(v => v.Entity.Code.ToShort() != v.Data.GoodsEnd && v.Data.GoodsEnd != 0) .ToList(); //筛选出目标站台无货的站台 var putDev = obj.Entity.Targets.Where(x => x.HasFlag(DeviceFlags.输送机)) .Select(x => new Device(x, World)) .Where(x => !x.Data3.Status.HasFlag(StationStatus.PH_Status)) .Select(x => x.Entity.Code.ToShort()); var devList = devs.OrderBy(x => x.Entity.Code).Where(x => putDev.Contains(x.Data.GoodsEnd)); foreach (var dev in devList) { //区分任务是拆盘机到码垛工位,还是码垛工位到拆盘机 if (dev.Entity.HasFlag(DeviceFlags.拆盘机)) { obj.Data.TaskNumber = dev.Data.TaskNumber; obj.Data.CmdType = RGVCmdType.PickGoods; obj.Data.StartPosition = dev.Entity.Code.ToShort(); obj.Data.DestPosition = dev.Data.GoodsEnd; obj.Data.VoucherNo++; return; } if (dev.Entity.HasFlag(DeviceFlags.二次码垛RGV取货口)) { obj.Data.TaskNumber = dev.Data.TaskNumber; obj.Data.CmdType = RGVCmdType.PickPutGoods; obj.Data.StartPosition = dev.Entity.Code.ToShort(); obj.Data.DestPosition = dev.Data.GoodsEnd; obj.Data.VoucherNo++; return; } //非拆盘机起始任务 //站台中的任务号 WCS_TaskInfo task = null; SqlSugarHelper.Do(_db => { var db = _db.Default; var taskInfo = db.Queryable().First(p => p.ID == dev.Data.TaskNumber && p.Status == TaskStatus.ConveyorExecution); if (taskInfo == null) return; taskInfo.Status = TaskStatus.RgvExecution; taskInfo.AddrNext = obj.Entity.Code; taskInfo.EditWho = "WCS"; taskInfo.EditTime = DateTime.Now; ; db.Updateable(taskInfo).ExecuteCommand(); taskInfo.AddWCS_TASK_DTL(db, dev.Entity.Code, obj.Entity.Code, $"任务分配至{obj.Entity.Code}"); task = taskInfo; }); if (task == null) continue; obj.Data.TaskNumber = task.ID; obj.Data.CmdType = RGVCmdType.PickPutGoods; obj.Data.StartPosition = dev.Entity.Code.ToShort(); obj.Data.DestPosition = dev.Data.GoodsEnd; obj.Data.VoucherNo++; return; } } catch (Exception ex) { if (ex.Message == "Destination array was not long enough. Check the destination index, length, and the array's lower bounds") { World.Log($"未知异常:{ex.StackTrace}"); } else if (ex.Message == "Number was less than the array's lower bound in the first dimension") { World.Log($"未知异常:{ex.StackTrace}"); } else throw new KnownException(ex.Message, LogLevelEnum.Mid); } } public override bool Select(Device dev) { return dev.Code is "RGV1" or "RGV2" or "RGV3" or "RGV4" or "RGV5" or "RGV6"; //return dev.HasFlag(Extensions.DeviceFlags.RGV); } } }