using AutoMapper;
|
using MailKit.Search;
|
using Microsoft.AspNetCore.Http;
|
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Logging;
|
using Newtonsoft.Json;
|
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
|
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
|
using System;
|
using System.Collections.Generic;
|
using System.Diagnostics;
|
using System.Linq;
|
using System.Net;
|
using System.Reflection.Metadata;
|
using System.Security.Policy;
|
using System.Text;
|
using System.Threading.Tasks;
|
using WIDESEA_Common.LocationEnum;
|
using WIDESEA_Common.Log;
|
using WIDESEA_Common.OtherEnum;
|
using WIDESEA_Common.StockEnum;
|
using WIDESEA_Common.TaskEnum;
|
using WIDESEA_Core;
|
using WIDESEA_Core.BaseRepository;
|
using WIDESEA_Core.BaseServices;
|
using WIDESEA_Core.Enums;
|
using WIDESEA_Core.Helper;
|
using WIDESEA_DTO.Inbound;
|
using WIDESEA_DTO.Stock;
|
using WIDESEA_DTO.Task;
|
using WIDESEA_IBasicService;
|
using WIDESEA_IStockService;
|
using WIDESEA_ITaskInfoService;
|
using WIDESEA_Model.Models;
|
using static WIDESEA_ITaskInfoService.ITaskService;
|
|
namespace WIDESEA_TaskInfoService
|
{
|
public partial class WMSCruJob : IHostedService, IDisposable
|
{
|
private readonly ILogger<WMSCruJob> _logger;
|
private Timer _timer;
|
private readonly ITaskService _taskService;
|
private readonly ILocationInfoService _locationInfoService;
|
private readonly IStockInfoService _stockInfoService;
|
private readonly IUnitOfWorkManage _unitOfWorkManage;
|
|
|
public WMSCruJob(ILogger<WMSCruJob> logger, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, ILocationInfoService locationInfoService,
|
ITaskService taskService)
|
{
|
_logger = logger;
|
_unitOfWorkManage = unitOfWorkManage;
|
_stockInfoService = stockInfoService;
|
_locationInfoService = locationInfoService;
|
_taskService=taskService;
|
}
|
private static readonly object _taskProcessLock = new object();
|
private const int TASK_PROCESS_TIMEOUT = 3000; // 3秒超时
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
{
|
_timer = new Timer(DoWorkWheel, null, TimeSpan.Zero, TimeSpan.FromSeconds(3));
|
return Task.CompletedTask;
|
}
|
|
public static List<string> InStationareaList = new List<string>() { "1113", "1114", "1115", "1116", "1117", "1118", "1119", "1120", "1121", "1122" };
|
|
private void DoWorkWheel(object state)
|
{
|
if (!Monitor.TryEnter(_taskProcessLock, TASK_PROCESS_TIMEOUT))
|
{
|
return;
|
}
|
|
try
|
{
|
// 定义状态常量,避免魔法数字
|
const int PLC_IN_FINISH = (int)InTaskStatusEnum.PLC_InFinish;
|
const int PLC_IN_InNew = (int)InTaskStatusEnum.InNew;
|
const int OUT_NEW = (int)OutTaskStatusEnum.OutNew;
|
const int SC_OUT_FINISH = (int)OutTaskStatusEnum.SC_OutFinish;
|
const int SC_IN_EXECUTING = (int)InTaskStatusEnum.SC_IntExecuting;
|
const int SC_OUT_EXECUTING = (int)OutTaskStatusEnum.SC_OutExecuting;
|
const int RELOCATION_EXECUTING = (int)RelocationTaskStatusEnum.RelocationExecuting;
|
const int PLC_OUT_EXECUTING = (int)OutTaskStatusEnum.PLC_OutExecuting;
|
|
// 获取当前任务数据
|
List<Dt_Task> allTasks = _taskService.Repository.QueryData();
|
|
// 获取巷道超过3个任务的路段
|
HashSet<string> excludedRoadways = allTasks
|
.Where(x => x.NumberSsuances > 3)
|
.Select(x => x.Roadway)
|
.Distinct()
|
.ToHashSet();
|
|
// 过滤需要处理的任务
|
List<Dt_Task> tasksToProcess = allTasks
|
.Where(x =>
|
(x.TaskStatus == PLC_IN_FINISH ||
|
x.TaskStatus == OUT_NEW ||
|
x.TaskStatus == SC_OUT_FINISH || (x.TaskStatus == PLC_IN_InNew && InStationareaList.Contains(x.SourceAddress))) &&
|
!excludedRoadways.Contains(x.Roadway))
|
.ToList();
|
|
// 按状态分组处理,减少重复查询
|
ProcessInAndNewTasks(tasksToProcess, allTasks, excludedRoadways); //下发堆垛机
|
ProcessOutFinishTasks(tasksToProcess, allTasks); //下发输送线任务
|
ProcessInPalltask(tasksToProcess, allTasks);
|
|
}
|
catch (Exception ex)
|
{
|
Console.WriteLine($"任务处理异常:{ex.Message}");
|
}
|
finally
|
{
|
// 必须释放锁,否则会导致死锁
|
Monitor.Exit(_taskProcessLock);
|
}
|
|
|
}
|
|
private void ProcessInAndNewTasks(List<Dt_Task> tasksToProcess, List<Dt_Task> allTasks, HashSet<string> excludedRoadways)
|
{
|
const int PLC_IN_FINISH = (int)InTaskStatusEnum.PLC_InFinish;
|
const int OUT_NEW = (int)OutTaskStatusEnum.OutNew;
|
const int SC_IN_EXECUTING = (int)InTaskStatusEnum.SC_IntExecuting;
|
const int SC_OUT_EXECUTING = (int)OutTaskStatusEnum.SC_OutExecuting;
|
const int RELOCATION_EXECUTING = (int)RelocationTaskStatusEnum.RelocationExecuting;
|
const int RELOCCATION_NEW = (int)RelocationTaskStatusEnum.RelocationNew;
|
|
var inAndNewTasks = tasksToProcess
|
.Where(x => x.TaskStatus == PLC_IN_FINISH || x.TaskStatus == OUT_NEW).OrderBy(x=>x.CreateDate)
|
.ToList();
|
if(inAndNewTasks.Count > 0)
|
{
|
foreach (var task in inAndNewTasks)
|
{
|
// 检查同一巷道是否有正在执行的任务
|
bool hasExecutingTask = allTasks.Any(x =>
|
x.TaskId != task.TaskId &&
|
x.Roadway == task.Roadway &&
|
(x.TaskStatus == SC_IN_EXECUTING ||
|
x.TaskStatus == SC_OUT_EXECUTING ||
|
x.TaskStatus == RELOCATION_EXECUTING));
|
|
if (hasExecutingTask)
|
continue;
|
|
if (task.TaskStatus == OUT_NEW)
|
{
|
bool SCNewTasks = allTasks.Any(x => x.TaskStatus != OUT_NEW && x.TargetAddress == task.TargetAddress);
|
if (SCNewTasks)
|
continue;
|
}
|
|
//判断是否需要移库
|
WebResponseContent webResponse = JudgmentIsRelocations(task);
|
if (!webResponse.Status) continue;
|
|
|
// 下发堆垛机任务
|
WCSginseng result = _taskService.SC_IssueTasks(
|
task.TaskId,
|
int.Parse(task.Roadway),
|
task.TaskNum,
|
task.PalletCode,
|
int.Parse(task.PalletType),
|
task.CurrentAddress,
|
task.NextAddress,
|
task.TargetAddress);
|
|
UpdateTaskStatus(task, result,
|
successStatus: task.TaskStatus == PLC_IN_FINISH ? SC_IN_EXECUTING : SC_OUT_EXECUTING, 1);
|
}
|
}
|
|
|
}
|
|
private void ProcessOutFinishTasks(List<Dt_Task> tasksToProcess, List<Dt_Task> allTasks)
|
{
|
const int SC_OUT_FINISH = (int)OutTaskStatusEnum.SC_OutFinish;
|
const int PLC_OUT_EXECUTING = (int)OutTaskStatusEnum.PLC_OutExecuting;
|
|
var outFinishTasks = tasksToProcess
|
.Where(x => x.TaskStatus == SC_OUT_FINISH)
|
.ToList();
|
if(outFinishTasks.Count > 0)
|
{
|
foreach (var task in outFinishTasks)
|
{
|
// 检查同一巷道是否有其他已完成的任务
|
bool hasOtherFinishTask = allTasks.Any(x =>
|
x.Roadway == task.Roadway &&
|
x.TaskStatus == SC_OUT_FINISH &&
|
x.TaskId != task.TaskId);
|
|
if (hasOtherFinishTask)
|
continue;
|
|
// 下发PLC任务
|
WCSginseng result = _taskService.PLC_IssueTasks(
|
task.TaskId,
|
int.Parse(task.Roadway),
|
task.TaskNum,
|
task.PalletCode,
|
int.Parse(task.PalletType),
|
task.CurrentAddress,
|
task.NextAddress,
|
"");
|
|
UpdateTaskStatus(task, result, PLC_OUT_EXECUTING, 2);
|
}
|
}
|
|
|
}
|
|
private void ProcessInPalltask(List<Dt_Task> tasksToProcess, List<Dt_Task> allTasks)
|
{
|
const int PLC_IN_InNew = (int)InTaskStatusEnum.InNew;
|
const int PLC_PLC_INEXECUTING = (int)InTaskStatusEnum.PLC_InExecuting;
|
var outFinishTasks = tasksToProcess
|
.Where(x => x.TaskStatus == PLC_IN_InNew && InStationareaList.Contains(x.SourceAddress))
|
.ToList();
|
if(outFinishTasks.Count > 0)
|
{
|
foreach (var task in outFinishTasks)
|
{
|
// 下发PLC任务
|
WCSginseng result = _taskService.PLC_IssueTasks(
|
task.TaskId,
|
int.Parse(task.Roadway),
|
task.TaskNum,
|
task.PalletCode,
|
int.Parse(task.PalletType),
|
task.CurrentAddress,
|
task.NextAddress,
|
"");
|
|
UpdateTaskStatus(task, result, PLC_PLC_INEXECUTING, 2);
|
}
|
}
|
}
|
|
private void UpdateTaskStatus(Dt_Task task, WCSginseng result, int successStatus,int statype)
|
{
|
if (result.IsSuccess)
|
{
|
|
task.TaskStatus = successStatus;
|
if(task.TaskStatus== (int)OutTaskStatusEnum.SC_OutExecuting)
|
{
|
string Result = MesOutTaskStatusEnum.Start.GetDescription();
|
//调取上游系统反馈开始任务
|
_taskService.OutStoreDocCallback(task.TaskNum,Result, "操作成功");
|
}else if(task.TaskStatus == (int)InTaskStatusEnum.PLC_InExecuting)
|
{
|
string Result = MesInTaskStatusEnum.Start.GetDescription();
|
//调取上游系统反馈开始任务
|
_taskService.InStoreDocCallback(task.TaskNum, Result, "操作成功",task.PalletCode,"");
|
}
|
}
|
else
|
{
|
task.Remark = result.Message;
|
task.NumberSsuances++;
|
}
|
|
_taskService.Repository.UpdateData(task);
|
}
|
|
|
public Task StopAsync(CancellationToken cancellationToken)
|
{
|
_timer?.Dispose();
|
return Task.CompletedTask;
|
}
|
|
public void Dispose()
|
{
|
throw new NotImplementedException();
|
}
|
|
/// <summary>
|
/// 判断巷道内移库
|
/// </summary>
|
/// <param name="TaskNum"></param>
|
/// <param name="SourceAddress"></param>
|
/// <returns></returns>
|
public WebResponseContent JudgmentIsRelocations(Dt_Task dt_Task)
|
{
|
const int PLC_IN_FINISH = (int)InTaskStatusEnum.PLC_InFinish;
|
const int SC_IN_EXECUTING = (int)InTaskStatusEnum.SC_IntExecuting;
|
const int SC_OUT_EXECUTING = (int)OutTaskStatusEnum.SC_OutExecuting;
|
WebResponseContent content = new WebResponseContent();
|
try
|
{
|
string Locationcodeadd = dt_Task.TaskType == (int)TaskTypeEnum.Inbound ? dt_Task.NextAddress: dt_Task.CurrentAddress ;
|
|
Dt_LocationInfo dt_Location = _locationInfoService.Repository.QueryData(x => x.LocationCode == Locationcodeadd).FirstOrDefault();
|
if (dt_Location.Depth == 1 || dt_Location.RoadwayNo == "5") return content.OK();
|
int locrow = judgmentRow(dt_Location.RoadwayNo, dt_Location.Row);
|
Dt_LocationInfo shallowLocation = _locationInfoService.Repository.QueryData(x => x.RoadwayNo == dt_Location.RoadwayNo && x.Row == locrow && x.Layer == dt_Location.Layer && x.Column == dt_Location.Column).FirstOrDefault();
|
if (shallowLocation.LocationStatus == (int)LocationStatusEnum.Free) return content.OK();
|
|
|
//判断是否有移库任务
|
var TransferTask = _taskService.Repository.QueryData(x=>x.TaskStatus== (int)RelocationTaskStatusEnum.RelocationNew && x.CurrentAddress== shallowLocation.LocationCode).FirstOrDefault();
|
if(TransferTask != null)
|
{
|
//进行下发给堆垛机任务
|
WCSginseng result = _taskService.SC_IssueTasks(
|
TransferTask.TaskId,
|
int.Parse(TransferTask.Roadway),
|
TransferTask.TaskNum,
|
TransferTask.PalletCode,
|
int.Parse(TransferTask.PalletType),
|
TransferTask.CurrentAddress,
|
TransferTask.NextAddress,
|
dt_Task.TargetAddress);
|
if (result.IsSuccess)
|
{
|
TransferTask.TaskStatus = (int)RelocationTaskStatusEnum.RelocationExecuting;
|
_taskService.UpdateData(TransferTask);
|
return content.OK();
|
}
|
else
|
{
|
UpdateTaskStatus(dt_Task, result,
|
successStatus: dt_Task.TaskStatus == PLC_IN_FINISH ? SC_IN_EXECUTING : SC_OUT_EXECUTING, 1);
|
|
WriteLog.GetLog("判断巷道内移库").Write($"下发堆垛机任务失败,原因:{result.Message}", $"生成移库任务:GenerateTransferTask");
|
return content.Error();
|
}
|
}
|
|
var CuttTransferTask = _taskService.Repository.QueryData(x => x.TaskStatus == (int)RelocationTaskStatusEnum.RelocationNew).FirstOrDefault();
|
//判断当前是否有移库任务
|
if (CuttTransferTask!=null) return content.Error();
|
|
|
if (shallowLocation.LocationStatus != (int)LocationStatusEnum.InStock) return content.Error();
|
|
//生成移库任务,进行下发
|
return content =GenerateTransferTask(shallowLocation.LocationCode);
|
}
|
catch (Exception ex)
|
{
|
WriteLog.GetLog("判断巷道内移库").Write($"判断移库失败,原因:{ex.Message}", $"判断巷道内移库方法:JudgmentIsRelocations");
|
return content.Error();
|
|
}
|
}
|
|
private int judgmentRow(string RoadwayNo,int locrow)
|
{
|
if (RoadwayNo == "1" || RoadwayNo == "3")
|
{
|
return locrow == 1 ? 2 : 3;
|
}
|
else if (RoadwayNo == "2" || RoadwayNo == "4")
|
{
|
return locrow == 5 ? 6 : 7;
|
}
|
else
|
{
|
return 0;
|
}
|
}
|
|
public WebResponseContent GenerateTransferTask(string shallowLocation)
|
{
|
WebResponseContent webResponse=new WebResponseContent();
|
try
|
{
|
Dt_LocationInfo OriginalLocation = _locationInfoService.Repository.QueryData(x => x.LocationCode == shallowLocation).FirstOrDefault();
|
Dt_StockInfo dt_StockInfo = _stockInfoService.Repository.QueryData(x => x.LocationCode == shallowLocation).FirstOrDefault();
|
Dt_LocationInfo newLocation = _locationInfoService.GetLocation(OriginalLocation.RoadwayNo, OriginalLocation.LocationType);
|
List<Dt_LocationInfo> dt_Locations = new List<Dt_LocationInfo>();
|
|
MES_parameter mES_Parame = _taskService.ApplicationChangeStorageLocation(dt_StockInfo.PalletCode, OriginalLocation.LocationCode, newLocation.LocationCode);
|
if (mES_Parame != null)
|
{
|
if (mES_Parame.Result == "Y")
|
{
|
//进行生成任务,下发任务至MES
|
Dt_Task task = new Dt_Task();
|
task.TaskNum = "0";
|
task.PalletCode = dt_StockInfo.PalletCode;
|
task.PalletType = dt_StockInfo.PalletType;
|
task.Roadway = OriginalLocation.RoadwayNo;
|
task.TaskType = (int)TaskTypeEnum.Relocation;
|
task.TaskStatus = (int)RelocationTaskStatusEnum.RelocationNew;
|
task.SourceAddress = OriginalLocation.LocationCode;
|
task.TargetAddress = newLocation.LocationCode;
|
task.CurrentAddress = OriginalLocation.LocationCode;
|
task.NextAddress = newLocation.LocationCode;
|
task.WarehouseId = OriginalLocation.WarehouseId;
|
task.OrderNo = "";
|
task.Grade = 1;
|
task.Creater = "MES";
|
task.CreateDate = DateTime.Now;
|
|
OriginalLocation.LocationStatus = (int)LocationStatusEnum.Lock;
|
newLocation.LocationStatus = (int)LocationStatusEnum.Lock;
|
dt_StockInfo.StockStatus = (int)StockStatusEmun.移库锁定;
|
|
dt_Locations.Add(OriginalLocation);
|
dt_Locations.Add(newLocation);
|
|
_unitOfWorkManage.BeginTran();
|
_taskService.AddData(task);
|
_locationInfoService.UpdateData(dt_Locations);
|
_stockInfoService.UpdateData(dt_StockInfo);
|
_unitOfWorkManage.CommitTran();
|
|
|
return webResponse.Error("已生成移库任务");
|
//进行下发给堆垛机任务
|
/*WCSginseng result = _taskService.SC_IssueTasks(
|
task.TaskId,
|
int.Parse(task.Roadway),
|
task.TaskNum,
|
task.PalletCode,
|
int.Parse(task.PalletType),
|
task.CurrentAddress,
|
task.NextAddress,
|
task.TargetAddress);
|
if (result.IsSuccess)
|
{
|
task.TaskStatus = (int)RelocationTaskStatusEnum.RelocationExecuting;
|
_taskService.UpdateData(task);
|
return webResponse.OK();
|
}
|
else
|
{
|
WriteLog.GetLog("判断巷道内移库").Write($"下发堆垛机任务失败,原因:{result.Message}", $"生成移库任务:GenerateTransferTask");
|
return webResponse.Error();
|
}*/
|
}
|
else
|
{
|
WriteLog.GetLog("判断巷道内移库").Write($"申请储位异动,MES不允许移动", $"生成移库任务:GenerateTransferTask");
|
return webResponse.Error();
|
}
|
}
|
else
|
{
|
WriteLog.GetLog("判断巷道内移库").Write($"申请储位异动,MES返回为空", $"生成移库任务:GenerateTransferTask");
|
return webResponse.Error();
|
}
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
WriteLog.GetLog("判断巷道内移库").Write($"生成移库任务失败:{ex.Message}", $"生成移库任务:GenerateTransferTask");
|
return webResponse.Error();
|
}
|
}
|
}
|
}
|