using MapsterMapper;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Quartz;
using SqlSugar;
using System.Text;
using System.Text.Json;
using WIDESEA_Core;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.Service;
namespace WIDESEAWCS_Tasks
{
///
/// 输送线任务作业(Quartz Job)- 核心执行逻辑
///
///
/// Quartz 定时任务,负责处理输送线的任务调度。
/// 使用 [DisallowConcurrentExecution] 禁止并发执行,确保同一设备的任务串行处理。
///
/// 核心职责:
/// 1. 获取输送线的所有子设备位置
/// 2. 并行处理每个子设备的消息
/// 3. 根据任务状态调用相应的调度方法
/// 4. 处理入库和出库两大类任务
///
/// 该 Job 通过 Parallel.For 并行处理多个子设备,提高处理效率。
///
[DisallowConcurrentExecution]
public class CommonConveyorLineNewJob : IJob
{
///
/// 任务服务
///
private readonly ITaskService _taskService;
///
/// 任务执行明细服务
///
private readonly ITaskExecuteDetailService _taskExecuteDetailService;
///
/// 路由服务
///
private readonly IRouterService _routerService;
///
/// 对象映射器
///
private readonly IMapper _mapper;
///
/// 输送线调度处理器
///
///
/// 封装了输送线业务逻辑的处理方法。
///
private ConveyorLineDispatchHandler _conveyorLineDispatch;
///
/// HTTP 客户端帮助类
///
///
/// 用于调用 WMS 系统的 HTTP 接口。
///
private readonly HttpClientHelper _httpClientHelper;
///
/// 日志记录器
///
private readonly ILogger _logger;
///
/// 构造函数
///
/// 任务服务
/// 任务执行明细服务
/// 路由服务
/// 对象映射器
/// HTTP 客户端帮助类
/// 日志记录器
public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, HttpClientHelper httpClientHelper, ILogger logger)
{
_taskService = taskService;
_taskExecuteDetailService = taskExecuteDetailService;
_routerService = routerService;
_mapper = mapper;
_httpClientHelper = httpClientHelper;
_logger = logger;
// 初始化调度处理器
_conveyorLineDispatch = new ConveyorLineDispatchHandler(_taskService, _taskExecuteDetailService, _routerService, _mapper, _logger);
}
///
/// Quartz Job 的执行入口
///
///
/// 执行流程:
/// 1. 从 JobDataMap 获取输送线设备信息
/// 2. 获取该输送线的所有子设备位置列表
/// 3. 并行处理每个子设备的消息
/// 4. 检查托盘位置(特定配置的位置)
/// 5. 根据任务状态分发到相应的处理方法
///
/// 并行处理提高了对多站台输送线的处理效率。
///
/// Quartz 作业执行上下文
public Task Execute(IJobExecutionContext context)
{
try
{
// 从 JobDataMap 获取输送线设备参数
CommonConveyorLine conveyorLine = (CommonConveyorLine)context.JobDetail.JobDataMap.Get("JobParams");
if (conveyorLine != null)
{
// 获取该输送线下的所有子设备位置编码
List childDeviceCodes = _routerService.QueryAllPositions(conveyorLine.DeviceCode);
if (childDeviceCodes == null || childDeviceCodes.Count == 0)
{
// 没有子设备,直接返回
_logger.LogInformation("输送线 {DeviceCode} 没有子设备", conveyorLine.DeviceCode);
QuartzLogger.Info($"输送线 {conveyorLine.DeviceCode} 没有子设备", conveyorLine.DeviceCode);
return Task.CompletedTask;
}
// 创建并行选项,限制最大并发数
//var parallelOptions = new ParallelOptions
//{
// // 限制并发数:子设备数量和 CPU 核心数*2 的较小值
// MaxDegreeOfParallelism = Math.Min(childDeviceCodes.Count, Environment.ProcessorCount * 2),
//};
_logger.LogDebug("Execute:开始并行处理输送线 {DeviceCode},子设备数量: {Count}", conveyorLine.DeviceCode, childDeviceCodes.Count);
QuartzLogger.Debug($"开始并行处理输送线,子设备数量: {childDeviceCodes.Count}", conveyorLine.DeviceCode);
// 并行处理每个子设备
//Parallel.For(0, childDeviceCodes.Count, parallelOptions, i =>
foreach (var childDeviceCode in childDeviceCodes)
{
//string childDeviceCode = childDeviceCodes[i];
var correlationId = Guid.NewGuid().ToString("N");
try
{
// 读取该位置的 PLC 命令数据
ConveyorLineTaskCommandNew command = conveyorLine.ReadCustomer(childDeviceCode);
// 如果命令为空,跳过
if (command == null)
{
_logger.LogDebug("Execute:子设备 {ChildDeviceCode} 命令为空,跳过", childDeviceCode);
QuartzLogger.Debug($"子设备 {childDeviceCode} 命令为空,跳过", conveyorLine.DeviceCode);
continue;
}
// 如果 WCS_ACK 为 1,先清除(表示处理过上一次请求)
if (command.WCS_ACK == 1)
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 0, childDeviceCode);
// ========== 检查特定位置是否有托盘 ==========
// 从配置中读取需要检查托盘的位置列表
var checkPalletPositions = App.Configuration.GetSection("CheckPalletPositions")
.Get>() ?? new List();
// 如果当前设备在检查列表中
if (checkPalletPositions.Any(x => x.Code == childDeviceCode))
{
// 检查输送线状态(是否有托盘)
if (command.CV_State == 2)
{
// 检查该位置是否已有任务
var existingTask = _taskService.Db.Queryable().First(x => x.TargetAddress == childDeviceCode);
if (existingTask.IsNullOrEmpty())
{
// 没有任务,向 WMS 请求出库托盘任务
var position = checkPalletPositions.FirstOrDefault(x => x.Code == childDeviceCode);
_logger.LogInformation("Execute:检查托盘位置 {ChildDeviceCode},请求WMS出库托盘任务", childDeviceCode);
QuartzLogger.Info($"检查托盘位置 {childDeviceCode},请求WMS出库托盘任务", conveyorLine.DeviceCode);
var responseResult = _httpClientHelper.Post("GetOutBoundTrayTaskAsync", new CreateTaskDto()
{
WarehouseId = position.WarehouseId,
TargetAddress = childDeviceCode
}.Serialize());
// 如果请求成功,接收 WMS 返回的任务
if (responseResult.IsSuccess && responseResult.Data.Status)
{
var wmsTask = JsonConvert.DeserializeObject(responseResult.Data.Data.ToString());
List taskDTOs = new List { wmsTask };
if (wmsTask != null)
_taskService.ReceiveWMSTask(taskDTOs);
}
}
}
}
// ========== 检查 PLC_STB 标志 ==========
// 只有当 PLC_STB 为 1 时才处理任务
if (command.PLC_STB != 1)
{
return Task.CompletedTask;
}
// ========== 处理无托盘条码的情况 ==========
// 无托盘条码时,请求出库任务
if (command.Barcode.IsNullOrEmpty() || command.Barcode.Replace("\0", "") == "")
{
_logger.LogDebug("Execute:子设备 {ChildDeviceCode} 无托盘条码,请求出库任务", childDeviceCode);
QuartzLogger.Debug($"子设备 {childDeviceCode} 无托盘条码,请求出库任务", conveyorLine.DeviceCode);
_conveyorLineDispatch.RequestOutbound(conveyorLine, command, childDeviceCode);
return Task.CompletedTask;
}
// ========== 处理已有任务号的情况 ==========
if (command.TaskNo > 0 && !command.Barcode.IsNullOrEmpty())
{
// 查询正在执行的任务
Dt_Task task = _taskService.QueryExecutingConveyorLineTask(command.TaskNo, childDeviceCode);
if (!task.IsNullOrEmpty())
{
_logger.LogInformation("Execute:子设备 {ChildDeviceCode} 处理任务 {TaskNum},状态: {Status}", childDeviceCode, task.TaskNum, task.TaskStatus);
QuartzLogger.Info($"处理任务 {task.TaskNum},状态: {task.TaskStatus}", conveyorLine.DeviceCode);
// 处理任务状态(根据状态分发到不同方法)
ProcessTaskState(conveyorLine, command, task, childDeviceCode);
return Task.CompletedTask;
}
}
}
catch (Exception innerEx)
{
// 记录异常,但不影响其他子设备的处理
_logger.LogError(innerEx, "Execute:子设备 {ChildDeviceCode} 处理异常,CorrelationId: {CorrelationId}", childDeviceCode, correlationId);
QuartzLogger.Error($"子设备处理异常: {innerEx.Message}", conveyorLine.DeviceCode, innerEx);
}
}
}
}
catch (Exception ex)
{
// 记录整体异常
_logger.LogError(ex, "Execute:输送线 {DeviceCode} 执行异常", ex.Message);
QuartzLogger.Error($"输送线执行异常: {ex.Message}", "CommonConveyorLineNewJob", ex);
}
return Task.CompletedTask;
}
///
/// 处理任务状态
///
///
/// 根据任务的当前状态,调用相应的调度方法:
/// - InExecuting: 入库执行中 -> 调用 RequestInNextAddress
/// - OutExecuting: 出库执行中 -> 根据是否到达目标地址调用对应方法
/// - InFinish: 入库完成 -> 调用 ConveyorLineInFinish
/// - OutFinish: 出库完成 -> 调用 ConveyorLineOutFinish
///
/// 输送线设备对象
/// PLC 命令数据
/// 任务对象
/// 子设备编码
private void ProcessTaskState(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, Dt_Task task, string childDeviceCode)
{
// 定义任务状态常量
const int InExecuting = (int)TaskInStatusEnum.Line_InExecuting; // 入库执行中
const int OutExecuting = (int)TaskOutStatusEnum.Line_OutExecuting; // 出库执行中
const int InFinish = (int)TaskInStatusEnum.InFinish; // 入库完成
const int OutFinish = (int)TaskOutStatusEnum.OutFinish; // 出库完成
// 获取当前任务状态
int state = task.TaskStatus;
// 判断当前子设备是否为目标地址
bool isTargetAddress = task.TargetAddress == childDeviceCode;
// 根据状态分发处理
switch (state)
{
case InExecuting:
// 入库执行中,调用下一地址处理
_conveyorLineDispatch.RequestInNextAddress(conveyorLine, command, childDeviceCode);
break;
case OutExecuting:
// 出库执行中
if (isTargetAddress)
// 到达目标地址,调用出库完成
_conveyorLineDispatch.ConveyorLineOutFinish(conveyorLine, command, childDeviceCode);
else
// 未到达目标地址,调用出库下一地址处理
_conveyorLineDispatch.RequestOutNextAddress(conveyorLine, command, childDeviceCode);
break;
case InFinish:
// 入库完成
_conveyorLineDispatch.ConveyorLineInFinish(conveyorLine, command, childDeviceCode);
break;
case OutFinish:
// 出库完成
_conveyorLineDispatch.ConveyorLineOutFinish(conveyorLine, command, childDeviceCode);
break;
}
}
}
}