using MapsterMapper;
using Microsoft.Extensions.Logging;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.Service;
namespace WIDESEAWCS_Tasks
{
///
/// 输送线调度处理器 - 处理输送线的各种业务请求
///
///
/// 核心职责:
/// 1. 处理输送线的心跳(保持连接)
/// 2. 处理入库请求(PLC 请求入库任务)
/// 3. 处理入库下一地址(任务执行中的地址更新)
/// 4. 处理入库完成
/// 5. 处理出库请求
/// 6. 处理出库下一地址
/// 7. 处理出库完成
///
/// 该类是输送线业务逻辑的核心,根据 PLC 的请求类型调用相应的处理方法。
///
public class ConveyorLineDispatchHandler
{
///
/// 任务服务
///
private readonly ITaskService _taskService;
///
/// 任务执行明细服务
///
private readonly ITaskExecuteDetailService _taskExecuteDetailService;
///
/// 路由服务
///
private readonly IRouterService _routerService;
///
/// 对象映射器
///
private readonly IMapper _mapper;
///
/// 日志记录器
///
private readonly ILogger _logger;
///
/// 输送线任务过滤器
///
///
/// 用于查询待处理和执行中的任务。
///
private readonly ConveyorLineTaskFilter _taskFilter;
///
/// 目标地址选择器
///
///
/// 用于处理拘束机/插拔钉机等设备的上下层请求。
///
private readonly ConveyorLineTargetAddressSelector _targetAddressSelector;
///
/// 构造函数
///
/// 任务服务
/// 任务执行明细服务
/// 路由服务
/// 对象映射器
/// 日志记录器
public ConveyorLineDispatchHandler(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, ILogger logger)
{
_taskService = taskService;
_taskExecuteDetailService = taskExecuteDetailService;
_routerService = routerService;
_mapper = mapper;
_logger = logger;
// 初始化任务过滤器和目标地址选择器
_taskFilter = new ConveyorLineTaskFilter(taskService, _logger);
_targetAddressSelector = new ConveyorLineTargetAddressSelector(_logger);
}
///
/// 处理输送线心跳
///
///
/// 当收到 PLC 的心跳信号时调用。
/// 清除任务号,表示当前没有执行任务。
/// 这是为了保持与 PLC 的连接活跃。
///
/// 输送线设备对象
/// PLC 命令数据
/// 子设备编码
public void HeartBeat(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
// 清除任务号,表示当前空闲
conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, 0, childDeviceCode);
_logger.LogDebug("HeartBeat:子设备 {ChildDeviceCode} 心跳", childDeviceCode);
QuartzLogger.Debug($"HeartBeat:子设备 {childDeviceCode} 心跳", conveyorLine.DeviceCode);
}
///
/// 处理输送线入库请求
///
///
/// 当 PLC 请求入库任务时调用。
/// 流程:
/// 1. 向 WMS 请求新任务
/// 2. 查询待处理任务
/// 3. 下发任务到 PLC
/// 4. 更新任务状态
///
/// 输送线设备对象
/// PLC 命令数据
/// 子设备编码
public void RequestInbound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
_logger.LogInformation("RequestInbound:子设备 {ChildDeviceCode} 请求入库", childDeviceCode);
QuartzLogger.Info($"请求入库,子设备: {childDeviceCode}", conveyorLine.DeviceCode);
// 向 WMS 请求新任务(基于条码)
if (_taskFilter.RequestWmsTask(command.Barcode, childDeviceCode))
{
// WMS 返回成功,查询待处理任务
Dt_Task? task = _taskFilter.QueryPendingTask(conveyorLine.DeviceCode, childDeviceCode);
if (task != null)
{
// 将任务映射为 PLC 命令
ConveyorLineTaskCommandNew taskCommand = _mapper.Map(task);
// 继承 WCS_ACK 标志
taskCommand.WCS_ACK = command.WCS_ACK;
// 发送命令到 PLC
conveyorLine.SendCommand(taskCommand, childDeviceCode);
// 更新任务状态到下一阶段
_taskService.UpdateTaskStatusToNext(task);
_logger.LogInformation("RequestInbound:入库任务已下发,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
QuartzLogger.Info($"入库任务已下发,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
}
}
}
///
/// 处理输送线入库下一地址请求
///
///
/// 当入库任务执行到某个中间站点时调用。
/// 根据下一地址判断是否需要与拘束机/插拔钉机等设备交互。
///
/// 输送线设备对象
/// PLC 命令数据
/// 子设备编码
public void RequestInNextAddress(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
// 查询正在执行的任务
Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
if (task == null)
{
_logger.LogDebug("RequestInNextAddress:任务 {TaskNo} 不存在", command.TaskNo);
QuartzLogger.Debug($"RequestInNextAddress:任务 {command.TaskNo} 不存在", conveyorLine.DeviceCode);
return;
}
_logger.LogInformation("RequestInNextAddress:入库下一地址,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
QuartzLogger.Info($"RequestInNextAddress:入库下一地址,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
// 如果不是空托盘任务,处理目标地址(与拘束机/插拔钉机交互)
if (task.TaskType != (int)TaskOutboundTypeEnum.OutEmpty)
{
_targetAddressSelector.HandleInboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
}
// 更新任务当前位置
_ = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress);
// 设置 WCS_STB 标志,表示 WCS 已处理
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
}
///
/// 处理输送线入库完成
///
///
/// 当入库任务完成时调用。
/// 更新任务状态并回复 PLC。
///
/// 输送线设备对象
/// PLC 命令数据
/// 子设备编码
public void ConveyorLineInFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
// 查询正在执行的任务
Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
if (task != null)
{
// 更新任务状态到下一阶段(通常是完成)
if (_taskService.UpdateTaskStatusToNext(task).Status)
{
}
// 回复 ACK 确认
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
_logger.LogInformation("ConveyorLineInFinish:入库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
QuartzLogger.Info($"入库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode);
}
}
///
/// 处理输送线出库请求
///
///
/// 当 PLC 请求出库任务时调用。
/// 流程:
/// 1. 查询待处理任务
/// 2. 下发任务到 PLC
/// 3. 更新任务状态
///
/// 输送线设备对象
/// PLC 命令数据
/// 子设备编码
public void RequestOutbound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
// 查询待处理任务
Dt_Task? task = _taskFilter.QueryPendingTask(conveyorLine.DeviceCode, childDeviceCode);
if (task != null)
{
// 设置任务号
conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, task.TaskNum, childDeviceCode);
// 设置托盘条码
conveyorLine.SetValue(ConveyorLineDBNameNew.Barcode, task.PalletCode, childDeviceCode);
// 设置目标地址
conveyorLine.SetValue(ConveyorLineDBNameNew.Target, task.NextAddress, childDeviceCode);
// 回复 ACK 确认
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
// 更新任务状态
_taskService.UpdateTaskStatusToNext(task);
_logger.LogInformation("RequestOutbound:出库任务已下发,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
QuartzLogger.Info($"出库任务已下发,任务号: {task.TaskNum}", conveyorLine.DeviceCode);
}
}
///
/// 处理输送线出库下一地址请求
///
///
/// 当出库任务执行到某个中间站点时调用。
/// 根据下一地址判断是否需要与拘束机/插拔钉机等设备交互。
///
/// 输送线设备对象
/// PLC 命令数据
/// 子设备编码
public void RequestOutNextAddress(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
// 查询正在执行的任务
Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
if (task == null)
{
_logger.LogDebug("RequestOutNextAddress:任务 {TaskNo} 不存在", command.TaskNo);
QuartzLogger.Debug($"RequestOutNextAddress:任务 {command.TaskNo} 不存在", conveyorLine.DeviceCode);
return;
}
_logger.LogInformation("RequestOutNextAddress:出库下一地址,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
QuartzLogger.Info($"RequestOutNextAddress:出库下一地址,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
// 如果不是空托盘任务,处理目标地址
if (task.TaskType != (int)TaskOutboundTypeEnum.OutEmpty)
{
_targetAddressSelector.HandleOutboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
}
// 更新任务当前位置
_ = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress);
// 回复 ACK 确认
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
}
///
/// 处理输送线出库完成
///
///
/// 当出库任务完成时调用。
/// 更新任务状态并回复 PLC。
///
/// 输送线设备对象
/// PLC 命令数据
/// 子设备编码
public void ConveyorLineOutFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
{
// 查询正在执行的任务
Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
if (task != null)
{
// 更新任务状态到下一阶段(通常是完成)
WebResponseContent content = _taskService.UpdateTaskStatusToNext(task);
// 回复 ACK 确认
conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
_logger.LogInformation("ConveyorLineOutFinish:出库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
QuartzLogger.Info($"出库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode);
}
}
}
}