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;
|
}
|
|
}
|
|
/// <summary>
|
/// 执行 AGV 任务,查询不到任务返回 <see langword="false"/>
|
/// </summary>
|
/// <param name="model"></param>
|
/// <returns></returns>
|
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<object>().GetInstance();
|
|
try {
|
var filepath = SnapManager.GetCapturePicturePath();
|
if (string.IsNullOrEmpty(filepath)) {
|
LogHelper.Info("图片地址为空");
|
return;
|
}
|
|
if (db.Updateable<TN_Loc_Container>()
|
.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<object>().GetInstance();
|
var info = "";
|
try {
|
var endLoc = db.Queryable<TN_Location>()
|
.First(a => a.S_CODE == tn_task.S_END_LOC);
|
|
// 如果已经设置的终点货位,就不要再进入这个流程
|
if (endLoc != null) {
|
LogHelper.Info("已经设置了终点货位:" + JsonConvert.SerializeObject(endLoc));
|
return;
|
}
|
|
var cgDetail = db.Queryable<TN_CG_Detail>()
|
.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<TN_CG_Detail>(cgDetail).UpdateColumns(it => it.F_QTY).ExecuteCommand() <= 0) {
|
tran.RollbackTran();
|
LogHelper.Info("修改物料表重量失败");
|
return;
|
}
|
|
if (db.Updateable<TN_Task>(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<TN_Location>()
|
.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<TN_Location>().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>(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>(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<TN_Location>(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<object>().GetInstance();
|
|
try {
|
|
var cgDetail = db.Queryable<TN_CG_Detail>()
|
.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<TN_CG_Detail>(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}");
|
}
|
|
}
|
|
/// <summary>
|
/// 任务分发,根据调度类型发给不同的调度系统
|
/// </summary>
|
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 gzResult SafetyInteraction(SafetyInteractionInfo model) {
|
var gzResult = new gzResult() {
|
code = 0,
|
msg = "success"
|
};
|
// 目前该项目没有站台安全交互的功能
|
return gzResult;
|
}
|
}
|
}
|