using HH.WCS.Mobox3.AnGang.device; using HH.WCS.Mobox3.AnGang.dispatch; using HH.WCS.Mobox3.AnGang.models; using HH.WCS.Mobox3.AnGang.process; using Newtonsoft.Json; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using static HH.WCS.Mobox3.AnGang.api.ApiModel; using HH.WCS.Mobox3.AnGang.wms; using HH.WCS.Mobox3.AnGang.util; namespace HH.WCS.Mobox3.AnGang.core { internal class WCSCore { public static ReturnResult OperateAgvTaskStatus(AgvTaskState model) { var result = new ReturnResult(); try { if (model.state > 0) { // AGV 执行任务的逻辑处理 if (!AgvTaskProcessOk(model)) { // 执行不OK,说明没有找到任务 result.ResultCode = 1; result.ResultMsg = $"根据Model.No未找到对应的任务,{model.task_no}"; LogHelper.Info(result.ResultMsg, "API"); return result; } } result.ResultCode = 0; result.ResultMsg = "success"; LogHelper.Info(result.ResultMsg + $": state {model.state}", "API"); return result; } catch (Exception ex) { result.ResultCode = -1; result.ResultMsg = $"发生了异常:+{ex.Message}"; LogHelper.Info(result.ResultMsg, "Error"); return result; } } /// /// 执行 AGV 任务,查询不到任务返回 /// /// /// internal static bool AgvTaskProcessOk(AgvTaskState model) { var TN_Task = WCSHelper.GetTask(model.task_no); // 根据当前model编号查询任务 if (TN_Task == null) { return false; } if (model.state > 7) { //安全请求等 TaskProcess.OperateReq(model.task_no, model.state, model.forklift_no, model.ext_data); return true; } // AGV 任务 134562(7) 状态处理 switch (model.state) { case 1: // 执行 WCSHelper.Begin(TN_Task, model.forklift_no); // 已推送的任务的状态改成执行 break; case 3: // 开始取货 WCSHelper.UpdateStatus(TN_Task, "开始取货"); // 任务状态改成开始取货 break; case 4: // 取货完成 WCSHelper.UpdateStatus(TN_Task, "取货完成"); // 任务状态改成取货完成 if (TN_Task.S_TYPE == TaskName.产品入库 || TN_Task.S_TYPE == TaskName.产品部分回库) { var captureTask = Task.Run(() => { CapturePic(TN_Task); LogHelper.Info($"{TN_Task}:拍照Task结束"); }); } if (TN_Task.S_TYPE == TaskName.产品入库) { var setEndLocTask = Task.Run(() => { // 只要任务为产品入库(PDA),就需要重新指定终点(默认endLoc为"") SetEndLoc(TN_Task); LogHelper.Info("设置终点Task结束"); }); } if (TN_Task.S_TYPE == TaskName.产品部分回库) { var weightTask = Task.Run(() => { UpdateWeight(TN_Task); LogHelper.Info("称重Task结束"); }); } TaskProcess.OperateStatus(TN_Task, 4); // 起点容器货位解绑,解锁起点 break; case 5: // 开始卸货 WCSHelper.UpdateStatus(TN_Task, "开始卸货"); // 任务状态改成开始卸货 break; case 6: // 卸货完成 WCSHelper.UpdateStatus(TN_Task, "卸货完成"); // 任务状态改成卸货完成 TaskProcess.OperateStatus(TN_Task, 6); // 终点容器货位绑定,解锁终点 break; case 2: // 完成 WCSHelper.End(TN_Task); // 任务状态改成结束 break; case 7: // 异常 TaskProcess.OperateStatus(TN_Task, 7); // 异常处理 WCSHelper.Fail(TN_Task); // 任务状态改成错误 break; } WCSHelper.AddActionRecord(model.task_no, model.state, model.forklift_no, model.ext_data); //调用第三方接口(如果有)TaskProcess.ReportStatus,添加任务动作关系表 return true; } public static void CapturePic(TN_Task model) { var db = new SqlHelper().GetInstance(); try { var filepath = SnapManager.GetCapturePicturePath(); if (string.IsNullOrEmpty(filepath)) { LogHelper.Info("图片地址为空"); return; } if (db.Updateable() .SetColumns(d => d.S_IMG_URL == filepath) .Where(d => d.S_CNTR_CODE == model.S_CNTR_CODE).ExecuteCommand() <= 0) { LogHelper.Info($"图片URL '{filepath}' 写入数据库失败"); return; } } catch (Exception ex) { LogHelper.Info($"发生了错误:{ex.Message}"); } } public static void SetEndLoc(TN_Task tn_task) { var db = new SqlHelper().GetInstance(); var info = ""; try { var endLoc = db.Queryable() .First(a => a.S_CODE == tn_task.S_END_LOC); // 如果已经设置的终点货位,就不要再进入这个流程 if (endLoc != null) { LogHelper.Info("已经设置了终点货位:" + JsonConvert.SerializeObject(endLoc)); return; } var cgDetail = db.Queryable() .Where(d => d.S_CNTR_CODE == tn_task.S_CNTR_CODE).First(); if (cgDetail == null) { LogHelper.Info("设置终点货位失败:当前任务的托盘号在物料表中不存在"); return; } var data = GZRobot.CustomBuf(); if (data.Count == 0) { LogHelper.Info("设置终点货位失败:没有接受到来自国自AGV的重量信息"); //return; // TODO 正式运行有国自AGV的时候恢复 } else { var weight = float.Parse(data[0].parameter_varchar200_up); cgDetail.F_QTY = weight; tn_task.F_WEIGHT = weight; using (var tran = db.Ado.UseTran()) { if (db.Updateable(cgDetail).UpdateColumns(it => it.F_QTY).ExecuteCommand() <= 0) { tran.RollbackTran(); LogHelper.Info("修改物料表重量失败"); return; } if (db.Updateable(tn_task).UpdateColumns(it => it.F_WEIGHT).ExecuteCommand() <= 0) { tran.RollbackTran(); LogHelper.Info("修改任务表重量失败"); return; } tran.CommitTran(); } LogHelper.Info($"修改任务号{tn_task.S_CODE}物料重量为{weight}", "HosttoagvTask"); if (weight > 2000) { LogHelper.Info($"物料重量超过2t", "HosttoagvTask"); } } // 终点货架为空时,不判断,交给人工处理 if (tn_task.S_END_LOC == "0") { LogHelper.Info("终点货架为空,不处理"); return; } if (!int.TryParse(tn_task.S_END_LOC.Trim(), out int row)) { LogHelper.Info($"终点货架号 '{tn_task.S_END_LOC}' 无法转成数字"); return; } // 只当之前指定终点货架后,才尝试计算终点货位 if (cgDetail.F_QTY > 2000) { // 重量超过 2t endLoc = null; LogHelper.Info("重量超过2t,不允许入库"); return; } else if (cgDetail.F_QTY > 1500) { // 重量超过1.5t,需要选择1-2层货架 endLoc = db.Queryable() .First(a => a.N_ROW == row && a.N_LAYER <= 2 && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y"); } else if (cgDetail.F_QTY > 0) { // 重量未超过1.5t,在指定货架随便选择1个 endLoc = db.Queryable().Where(a => a.N_ROW == row && a.N_CURRENT_NUM == 0 && a.N_LOCK_STATE == 0 && a.S_LOCK_STATE == "无" && a.C_ENABLE == "Y") .OrderBy(a => a.N_LAYER > 2 ? 0 : 1).First(); // 优先选层级高的 } else { // 没有接收到重量,或重量出错 endLoc = null; LogHelper.Info($"错误的重量信息:{cgDetail.F_QTY}"); return; } // 如果没有符合条件的货位,置空,等待PDA重新确定 if (endLoc == null) { tn_task.S_END_AREA = Settings.Areas[1][0]; // HJQ tn_task.S_END_LOC = "0"; // 不需要再给GZ AGV传空值,一开始就没给具体货位,只给了Area //var request = new UpdateInteractInfo { // interaction_info_id = 3, // 更改终点信息 // info_status = "active", // return_value = "", //}; //GZRobot.UpdateInteractInfo(request); if (db.Updateable(tn_task).UpdateColumns(it => new { it.S_END_LOC }) .ExecuteCommand() > 0) { //LocationHelper.LockLoc(endLoc.S_CODE, 1);//终点入库锁 LogHelper.Info($"重新计算后没有合适货位,任务 {tn_task.S_CODE} 修改成功,修改终点货位为 0空"); } else { LogHelper.Info($"重新计算后没有合适货位,任务 {tn_task.S_CODE} 修改失败,修改终点货位为 0空"); } } else { // 找到合适的货位,推送 tn_task.S_END_LOC = endLoc.S_CODE; LocationHelper.LockLoc(ref endLoc, 1); // 终点入库锁 using (var tran = db.Ado.UseTran()) { if (db.Updateable(tn_task).UpdateColumns(it => new { it.S_END_LOC }) .ExecuteCommand() <= 0) { tran.RollbackTran(); LogHelper.Info($"计算货位成功,任务'{tn_task.S_CODE}'修改失败,终点货架为{endLoc.N_ROW},修改终点位置为 {endLoc.S_CODE}"); } if (db.Updateable(endLoc).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY }).ExecuteCommand() <= 0) { tran.RollbackTran(); info = $"更新终点货位锁状态失败:起点货位{endLoc.S_CODE}"; LogHelper.Info(info); } tran.CommitTran(); } } } catch (Exception ex) { LogHelper.Info($"发生了错误:{ex.Message}"); } } public static void UpdateWeight(TN_Task tn_task) { var db = new SqlHelper().GetInstance(); try { var cgDetail = db.Queryable() .Where(d => d.S_CNTR_CODE == tn_task.S_CNTR_CODE).First(); if (cgDetail == null) { LogHelper.Info("更新物料重量失败:当前任务的托盘号在物料表中不存在"); return; } var data = GZRobot.CustomBuf(); if (data.Count == 0) { LogHelper.Info("更新物料重量失败:没有接受到来自国自AGV的重量信息"); return; } var weight = float.Parse(data[0].parameter_varchar200_up); cgDetail.F_QTY = weight; if (db.Updateable(cgDetail).UpdateColumns(it => it.F_QTY).ExecuteCommand() <= 0) { LogHelper.Info("修改物料表重量失败"); return; } LogHelper.Info($"修改任务号{tn_task.S_CODE}物料重量为{weight}", "HosttoagvTask"); } catch (Exception ex) { LogHelper.Info($"发生了错误:{ex.Message}"); } } /// /// 任务分发,根据调度类型发给不同的调度系统 /// internal static void Dispatch() { //查询任务 //获取所有等待的任务 var list = WCSHelper.GetWaitingTaskList(); LogHelper.Info("等待任务信息" + JsonConvert.SerializeObject(list), "API"); if (list.Count > 0) { list.ForEach(task => { //使用自定义任务推送 TaskProcess.SendTask(task);//调度NDC或杭奥或国自设备 //TaskProcess.SendGZTask(task);///调度国自设备 }); } else { LogHelper.Info("暂无任务"); } } // DOC 4. 站台申请安全交互 RCS->WMS public static ReturnResult SafetyInteraction(SafetyInteractionInfo model) { var gzResult = new ReturnResult(); // 目前该项目没有站台安全交互的功能 return gzResult; } } }