using CirculateTool.Attribute; using WcsMain.Business.CommonAction; using WcsMain.Common; using WcsMain.DataBase.Dao; using WcsMain.DataBase.TableEntity; using WcsMain.DataService; using WcsMain.Enum.Stacker; using WcsMain.EquipOperation.Convey; using WcsMain.EquipOperation.Entity.Stacker; using WcsMain.EquipOperation.Stacker; using WcsMain.ExtendMethod; namespace WcsMain.Business.CirculationTask.Stacker; /// /// 双货叉单深位执行堆垛机任务 ---- 卡特模式 /// [Circulation("双货叉单深位执行堆垛机任务")] public class ExeTaskDoubleFork( StackerOperation stackerOperation, AppWcsTaskDao wcsTaskDao, WCSTaskExecuteEvent wcsTaskEvent, ConveyOperation conveyOperation, DataBaseData dataBaseData) { /// /// 执行堆垛机任务 /// /// [Circulation("执行堆垛机任务")] public bool ExecuteStackerTask() { foreach (var stacker in CommonData.AppStackers.Open()) { var stackerUseStatus = stackerOperation.StackerCanUse(stacker.StackerId, out int plcId, out int spare1); if (stackerUseStatus == StackerUseStatusEnum.Free) { /* 空闲时正常执行任务 */ // 移库 bool exeMoveTask = ExecuteMoveTask(stacker.StackerId); if (exeMoveTask) continue; // 出库 bool exeOutTask = ExecuteOutTask(stacker.StackerId); if (exeOutTask) continue; // 入库 bool exeInTask = ExecuteInTask(stacker.StackerId); if (exeInTask) continue; // 拣选 //bool exePickTask = ExecutePickTask(stacker.StackerId); //if (exePickTask) return; } if (stackerUseStatus == StackerUseStatusEnum.waitTask) { /* 重复入库时执行任务 */ bool exeDoubleInTask = ExecuteDoubleInTask(stacker.StackerId, plcId, 1); if (exeDoubleInTask) continue; exeDoubleInTask = ExecuteDoubleInTask(stacker.StackerId, spare1, 2); if (exeDoubleInTask) continue; } } return true; } /// /// 执行堆垛机入库任务,若执行了任务则返回 true /// /// /// private bool ExecuteInTask(int? stackerId) { if (stackerId == default) return false; /* 检查入库站台是否允许取货 */ bool allowGetGoods = conveyOperation.AllowGetVehicle(stackerId.ToString()); if(!allowGetGoods) return false; // 入库站台不允许取货 /* 读取入库站台的条码 */ var codes = conveyOperation.ReadAreaConveyCode(stackerId.ToString()); if(codes == default || codes.Count != 2) return false; /* 构造任务数据 */ bool isWriteTask = false; // 指示是否写入任务 for (int i = 0; i < 2; i++) { /* 校验货叉开启 */ string forkStatus = CommonData.AppStackers.First(f => f.StackerId == stackerId).ForkStatus ?? ""; if (forkStatus.Length <= i || forkStatus[i] != '1') continue; // 获取数据失败或者货叉未开启 var code = codes[i]; if (string.IsNullOrEmpty(code)) continue; // 检查条码是否为空 //ConsoleLog.Warning($"{stackerId} 号堆垛机,入库位置:{i + 1} 返回:{code}"); if (code == "NoRead") // 检查条码是否为 NoRead { /* 检查卸货站台是否允许卸货 */ bool allowSetGoods = conveyOperation.AllowSetVehicle(stackerId.ToString()); if (!allowSetGoods) continue; // 出库站台不允许取货 /* 生成一个直接卸货出去的任务 */ int? plcId = dataBaseData.GetNewPlcTaskId(); if(plcId == default || plcId == 0) continue; StackerPlcTask errTask = StackerPlcTask.DefaultErrTask((int)plcId!, (int)stackerId!, i + 1); string WriteTaskErrText = stackerOperation.WriteTask(errTask, i + 1); if(string.IsNullOrEmpty(WriteTaskErrText)) // 写入成功 { ConsoleLog.Success($"{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务成功,{errTask}"); isWriteTask = true; } else { ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务失败,{errTask},异常信息:{WriteTaskErrText},"); } continue; } var wcsTasks = wcsTaskDao.Select(new AppWcsTask { VehicleNo = code, TaskStatus = (int)WcsTaskStatusEnum.create }); if(wcsTasks == default) // 查询任务失败 { ConsoleLog.Exception($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code} 查找任务失败,和数据库服务器连接中断"); continue; } if(wcsTasks.Count < 1) // 没有任务 { /* 检查卸货站台是否允许卸货 */ bool allowSetGoods = conveyOperation.AllowSetVehicle(stackerId.ToString()); if (!allowSetGoods) continue; // 出库站台不允许取货 /* 生成一个直接卸货出去的任务 */ ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code} 没有找到对应任务"); int? plcId = dataBaseData.GetNewPlcTaskId(); if (plcId == default || plcId == 0) continue; StackerPlcTask errTask = StackerPlcTask.DefaultErrTask((int)plcId!, (int)stackerId!, i + 1, code.ToPlcVehicleNo()); string WriteTaskErrText = stackerOperation.WriteTask(errTask, i + 1); if (string.IsNullOrEmpty(WriteTaskErrText)) // 写入成功 { ConsoleLog.Success($"{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务成功,{errTask}"); isWriteTask = true; } else { ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务失败,{errTask},异常信息:{WriteTaskErrText},"); } continue; } var wcsTask = wcsTasks[0]; // 找出第一个任务 /* 校验终点是否正确 */ var destinationLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Destination); if(destinationLocationInfo == default) // 任务终点错误,理论上此处不应该出现异常 { /* 检查卸货站台是否允许卸货 */ bool allowSetGoods = conveyOperation.AllowSetVehicle(stackerId.ToString()); if (!allowSetGoods) continue; // 出库站台不允许取货 /* 生成一个直接卸货出去的任务 */ ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code} 无法识别的入库库位"); int? plcId = dataBaseData.GetNewPlcTaskId(); if (plcId == default || plcId == 0) continue; StackerPlcTask errTask = StackerPlcTask.DefaultErrTask((int)plcId!, (int)stackerId!, i + 1, code.ToPlcVehicleNo()); string WriteTaskErrText = stackerOperation.WriteTask(errTask, i + 1); if (string.IsNullOrEmpty(WriteTaskErrText)) // 写入成功 { ConsoleLog.Success($"{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务成功,{errTask}"); wcsTaskEvent.ErrTaskEvent(wcsTask, "入库库位不正确"); isWriteTask = true; } else { ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务失败,{errTask},异常信息:{WriteTaskErrText},"); } continue; } /* 判断冲突货位是否有任务 */ var runningWcsTasks = wcsTaskDao.QueryTaskWithWcsLocation(destinationLocationInfo.InterveneLocation); if (runningWcsTasks == default) continue; // 查询失败 if(runningWcsTasks.Count > 0) continue; // 存在冲突点位,等待 /* 下发任务 */ StackerPlcTask stackerTask = wcsTask.ToStackerInTask((int)stackerId!, i + 1, destinationLocationInfo); string WriteStackerTaskErrText = stackerOperation.WriteTask(stackerTask, i + 1); if (string.IsNullOrEmpty(WriteStackerTaskErrText)) // 写入成功 { ConsoleLog.Success($"{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务成功,{stackerTask}"); wcsTaskEvent.StartTaskEvent(wcsTask); // 任务开始 isWriteTask = true; } else { ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务失败,{stackerTask},异常信息:{WriteStackerTaskErrText},"); } } if (isWriteTask) { string confirmResult = stackerOperation.WriteTaskConfirm(stackerId); // 写入任务确认 ConsoleLog.Info($"堆垛机:{stackerId} 入库任务写任务确认;信息:{(string.IsNullOrEmpty(confirmResult) ? "成功" : confirmResult)}"); Thread.Sleep(1000); } return isWriteTask; } /// /// 执行堆垛机出库任务,若执行了任务则返回 true /// /// /// public bool ExecuteOutTask(int? stackerId) { if (stackerId == default) return false; /* 检查出库站台是否可以卸货 */ bool allowSetGoods = conveyOperation.AllowSetVehicle(stackerId.ToString()); if (!allowSetGoods) return false; // 出库站台不允许取货 var wcsTasks = wcsTaskDao.SelectOutTaskWithStacker((int)stackerId!); if (wcsTasks == default) { ConsoleLog.Exception(string.Format("【异常】{0} 号堆垛机出库任务查询失败,与数据库连接异常", stackerId)); Thread.Sleep(5000); return false; } if (wcsTasks.Count == 0) return false; bool isWriteTask = false; // 指示是否写入任务 int writeTaskCount = 0; // 表示执行了多少条任务 for (int i = 0; i < wcsTasks.Count; i++) { if (writeTaskCount >= 2) break; // 最多执行两条 /* 校验货叉开启 */ string forkStatus = CommonData.AppStackers.First(f => f.StackerId == stackerId).ForkStatus ?? ""; if (forkStatus.Length <= i || forkStatus[i] != '1') { writeTaskCount ++; continue; // 获取数据失败或者货叉未开启 } var wcsTask = wcsTasks[i]; /* 校验起点是否正确 */ var originLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Origin); if (originLocationInfo == default) // 任务起点错误,理论上此处不应该出现异常 { ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,出库库位:{wcsTask.Origin} 条码:{wcsTask.VehicleNo} 无法识别的出库库位"); wcsTaskEvent.ErrTaskEvent(wcsTask, "出库的库位不正确"); continue; } /* 判断冲突货位是否有任务 */ var runningWcsTasks = wcsTaskDao.QueryTaskWithWcsLocation(originLocationInfo.InterveneLocation); if (runningWcsTasks == default) continue; // 查询失败 if (runningWcsTasks.Count > 0) continue; // 存在冲突点位,等待 /* 下发任务 */ StackerPlcTask stackerTask = wcsTask.ToStackerOutTask((int)stackerId!, i + 1, originLocationInfo); string WriteStackerTaskErrText = stackerOperation.WriteTask(stackerTask, i + 1); if (string.IsNullOrEmpty(WriteStackerTaskErrText)) // 写入成功 { ConsoleLog.Success($"{stackerId} 号堆垛机,出库位置:{i + 1} 条码:{wcsTask.VehicleNo},写入任务成功,{stackerTask}"); wcsTaskEvent.StartTaskEvent(wcsTask); // 任务开始 writeTaskCount++; isWriteTask = true; } else { ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,出库位置:{i + 1} 条码:{wcsTask.VehicleNo},写入任务失败,{stackerTask},异常信息:{WriteStackerTaskErrText},"); } } if (isWriteTask) { string confirmResult = stackerOperation.WriteTaskConfirm(stackerId); // 写入任务确认 ConsoleLog.Info($"堆垛机:{stackerId}出库任务写任务确认;信息:{(string.IsNullOrEmpty(confirmResult) ? "成功" : confirmResult)}"); Thread.Sleep(1000); } return isWriteTask; } /// /// 执行堆垛机拣选任务,若执行了任务则返回 true /// /// /// public bool ExecutePickTask(int? stackerId) { if (stackerId == default) return false; var wcsTasks = wcsTaskDao.SelectPickOutTaskWithStacker((int)stackerId!); if (wcsTasks == default) { ConsoleLog.Error($"【异常】{stackerId} 号堆垛机拣选任务查询失败,与数据库连接异常"); Thread.Sleep(5000); return false; } if (wcsTasks.Count == 0) return false; var wcsTask = wcsTasks[0]; // 取第一条任务 /* 校验出库站台是否可以卸货 */ // TODO /* 检验并返回起点终点信息 */ var originLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Origin); var destinationLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Destination); if (destinationLocationInfo == default || originLocationInfo == default) // 起点终点错误,直接标记错误 { // 已经在接口内做了检查,按道理此处不会出错。直接返回异常,任务直接取消 ConsoleLog.Warning($"【警告】任务号:{wcsTask.TaskId},Plc任务号:{wcsTask.PlcId} 无法执行,起点或者终点存在异常"); wcsTaskEvent.ErrTaskEvent(wcsTask, $"起点或者终点存在异常"); return false; } StackerPlcTask stackerTask = new() { StackerId = stackerId, PlcId = wcsTask.PlcId, TaskType = Convert.ToInt16(wcsTask.TaskType), GetStand = 0, InTunnelId = Convert.ToInt16(stackerId), OutTunnelId = Convert.ToInt16(stackerId), SetStand = 0, GetQueue = Convert.ToInt16(originLocationInfo.Queue), GetLine = Convert.ToInt16(originLocationInfo.Line), GetLayer = Convert.ToInt16(originLocationInfo.Layer), GetDeep = Convert.ToInt16(originLocationInfo.Depth), SetQueue = Convert.ToInt16(destinationLocationInfo.Queue), SetLine = Convert.ToInt16(destinationLocationInfo.Line), SetLayer = Convert.ToInt16(destinationLocationInfo.Layer), SetDeep = Convert.ToInt16(destinationLocationInfo.Depth), Size = Convert.ToInt16(wcsTask.VehicleSize), Weight = Convert.ToInt16(wcsTask.Weight), Code = wcsTask.PlcVehicleNo ?? 0, }; var writeStackerTaskErrText = stackerOperation.WriteTask(stackerTask); if (string.IsNullOrEmpty(writeStackerTaskErrText)) { ConsoleLog.Success($"堆垛机:{stackerId} 写入拣选任务成功,箱号:{wcsTask.VehicleNo},PlcId:{wcsTask.PlcId};{wcsTask.Origin} => {wcsTask.Destination}"); wcsTaskEvent.StartTaskEvent(wcsTask); return true; } else { ConsoleLog.Warning($"【警告】堆垛机:{stackerId} 写入拣选任务失败,箱号:{wcsTask.VehicleNo},PlcId:{wcsTask.PlcId};{wcsTask.Origin} => {wcsTask.Destination},异常信息:{writeStackerTaskErrText}"); wcsTaskEvent.ErrTaskEvent(wcsTask, $"写入任务失败,异常信息:{writeStackerTaskErrText}"); } return false; } /// /// 执行堆垛机移库任务,若执行了任务则返回 true /// /// /// public bool ExecuteMoveTask(int? stackerId) { if (stackerId == default) return false; var wcsTasks = wcsTaskDao.SelectMoveTaskWithStacker((int)stackerId!); if (wcsTasks == default) { ConsoleLog.Error($"【异常】{stackerId} 号堆垛机移库任务查询失败,与数据库连接异常"); Thread.Sleep(5000); return false; } if (wcsTasks.Count == 0) return false; bool isWriteTask = false; // 指示是否写入任务 int writeTaskCount = 0; // 表示执行了多少条任务 for (int i = 0; i < wcsTasks.Count; i++) { if (writeTaskCount >= 2) break; // 最多执行两条 /* 校验货叉开启 */ string forkStatus = CommonData.AppStackers.First(f => f.StackerId == stackerId).ForkStatus ?? ""; if (forkStatus.Length <= i || forkStatus[i] != '1') { writeTaskCount++; continue; // 获取数据失败或者货叉未开启 } var wcsTask = wcsTasks[i]; /* 校验起点是否正确 */ var originLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Origin); var destinationLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Origin); if (originLocationInfo == default || destinationLocationInfo == default) // 任务起点终点错误,理论上此处不应该出现异常 { ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,移库库位:{wcsTask.Origin} 条码:{wcsTask.VehicleNo} 无法识别的库位"); wcsTaskEvent.ErrTaskEvent(wcsTask, "移库的库位不正确"); continue; } /* 判断冲突货位是否有任务,起点终点都要判断 */ var runningWcsTasks = wcsTaskDao.QueryTaskWithWcsLocation(destinationLocationInfo.InterveneLocation, originLocationInfo.InterveneLocation); if (runningWcsTasks == default) continue; // 查询失败 if (runningWcsTasks.Count > 0) continue; // 存在冲突点位,等待 /* 下发任务 */ StackerPlcTask stackerTask = wcsTask.ToStackerMoveTask((int)stackerId!, originLocationInfo, destinationLocationInfo); string WriteStackerTaskErrText = stackerOperation.WriteTask(stackerTask, i + 1); if (string.IsNullOrEmpty(WriteStackerTaskErrText)) // 写入成功 { ConsoleLog.Success($"{stackerId} 号堆垛机,移库位置:{i + 1} 条码:{wcsTask.VehicleNo},写入任务成功,{stackerTask}"); wcsTaskEvent.StartTaskEvent(wcsTask); // 任务开始 writeTaskCount++; isWriteTask = true; } else { ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,移库位置:{i + 1} 条码:{wcsTask.VehicleNo},写入任务失败,{stackerTask},异常信息:{WriteStackerTaskErrText},"); } } if (isWriteTask) { string confirmResult = stackerOperation.WriteTaskConfirm(stackerId); // 写入任务确认 ConsoleLog.Info($"堆垛机:{stackerId}移库任务写任务确认;信息:{(string.IsNullOrEmpty(confirmResult) ? "成功" : confirmResult)}"); Thread.Sleep(1000); } return isWriteTask; } /// /// 执行重复入库新任务,若执行了返回true /// /// /// /// 货叉编号 /// public bool ExecuteDoubleInTask(int? stackerId, int plcId, int forkId) { if (stackerId == default || plcId == 0) return false; /* 查找这个PlcId对应的wms任务 */ List? doubleInTasks = wcsTaskDao.Select(new AppWcsTask { PlcId = plcId }); if (doubleInTasks == default) { ConsoleLog.Error($"【异常】{stackerId} 号堆垛机重复入库任务检验失败,与数据库连接异常"); Thread.Sleep(5000); return false; } if (doubleInTasks.Count == 0) return false; // 找不到对应的PlcId的任务 var doubleTask = doubleInTasks[0]; /* 判断这个任务是否出现卸货位置有货 */ if(doubleTask.TaskStatus != (int)WcsTaskStatusEnum.doubleIn) { return false; } // 没有重复入库不执行下面方法 /* 查找这个任务的新任务 */ List? newTasks = wcsTaskDao.Select(new AppWcsTask { TaskId = doubleTask.TaskId, TaskType = (int)WcsTaskTypeEnum.newTaskForDoubleIn, TaskStatus = (int)WcsTaskStatusEnum.create }); if (newTasks == default) { ConsoleLog.Error($"【异常】{stackerId} 号堆垛机重复入库任务新任务查询失败,与数据库连接异常"); Thread.Sleep(5000); return false; } if (newTasks.Count == 0) return false; var newTask = newTasks[0]; var destinationLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(newTask.Destination); if (destinationLocationInfo == default) // 终点错误,直接标记错误 { // 已经在接口内做了检查,按道理此处不会出错。直接返回异常,任务直接取消 ConsoleLog.Warning($"【警告】任务号:{newTask.TaskId},Plc任务号:{newTask.PlcId} 无法执行,新终点存在异常"); wcsTaskEvent.ErrTaskEvent(newTask, $"新终点存在异常"); return false; } /* 判断冲突货位是否有任务 */ var runningWcsTasks = wcsTaskDao.QueryTaskWithWcsLocation(destinationLocationInfo.InterveneLocation); if (runningWcsTasks == default) return false; // 查询失败 if (runningWcsTasks.Count > 0) return false; // 存在冲突点位,等待 /* 下发任务 */ StackerPlcTask stackerTask = newTask.ToStackerInTask((int)stackerId!, forkId, destinationLocationInfo); var writeStackerTaskErrText = stackerOperation.WriteTask(stackerTask); if (string.IsNullOrEmpty(writeStackerTaskErrText)) { ConsoleLog.Success($"堆垛机:{stackerId} 写入重复入库新任务成功,箱号:{newTask.VehicleNo},PlcId:{newTask.PlcId};新目的地:{newTask.Destination}"); wcsTaskEvent.StartTaskEvent(newTask); return true; } else { ConsoleLog.Warning($"【警告】堆垛机:{stackerId} 写入重复入库新任务失败,箱号:{newTask.VehicleNo},PlcId:{newTask.PlcId};新目的地:{newTask.Destination},异常信息:{writeStackerTaskErrText}"); wcsTaskEvent.ErrTaskEvent(newTask, $"写入任务失败,异常信息:{writeStackerTaskErrText}"); } return false; } }