wanshenmean
3 天以前 5e851678cc02257bbbd179446de36082430ca5bc
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
@@ -1,26 +1,9 @@
#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 命名空间:WIDESEAWCS_Tasks.ConveyorLineJob
 * 创建者:胡童庆
 * 创建时间:2024/8/2 16:13:36
 * 版本:V1.0.0
 * 描述:
 *
 * ----------------------------------------------------------------
 * 修改人:
 * 修改时间:
 * 版本:V1.0.1
 * 修改说明:
 *
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using AutoMapper;
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;
@@ -28,135 +11,315 @@
namespace WIDESEAWCS_Tasks
{
    /// <summary>
    /// 输送线调度处理器 - 处理输送线的各种业务请求
    /// </summary>
    /// <remarks>
    /// 核心职责:
    /// 1. 处理输送线的心跳(保持连接)
    /// 2. 处理入库请求(PLC 请求入库任务)
    /// 3. 处理入库下一地址(任务执行中的地址更新)
    /// 4. 处理入库完成
    /// 5. 处理出库请求
    /// 6. 处理出库下一地址
    /// 7. 处理出库完成
    ///
    /// 该类是输送线业务逻辑的核心,根据 PLC 的请求类型调用相应的处理方法。
    /// </remarks>
    public class ConveyorLineDispatchHandler
    {
        /// <summary>
        /// 任务服务
        /// </summary>
        private readonly ITaskService _taskService;
        /// <summary>
        /// 任务执行明细服务
        /// </summary>
        private readonly ITaskExecuteDetailService _taskExecuteDetailService;
        /// <summary>
        /// 路由服务
        /// </summary>
        private readonly IRouterService _routerService;
        /// <summary>
        /// 对象映射器
        /// </summary>
        private readonly IMapper _mapper;
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger _logger;
        /// <summary>
        /// 输送线任务过滤器
        /// </summary>
        /// <remarks>
        /// 用于查询待处理和执行中的任务。
        /// </remarks>
        private readonly ConveyorLineTaskFilter _taskFilter;
        /// <summary>
        /// 目标地址选择器
        /// </summary>
        /// <remarks>
        /// 用于处理拘束机/插拔钉机等设备的上下层请求。
        /// </remarks>
        private readonly ConveyorLineTargetAddressSelector _targetAddressSelector;
        public ConveyorLineDispatchHandler(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper)
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="taskService">任务服务</param>
        /// <param name="taskExecuteDetailService">任务执行明细服务</param>
        /// <param name="routerService">路由服务</param>
        /// <param name="mapper">对象映射器</param>
        /// <param name="logger">日志记录器</param>
        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);
            _targetAddressSelector = new ConveyorLineTargetAddressSelector();
            // 初始化任务过滤器和目标地址选择器
            _taskFilter = new ConveyorLineTaskFilter(taskService, _logger);
            _targetAddressSelector = new ConveyorLineTargetAddressSelector(_logger);
        }
        /// <summary>
        /// 心跳处理
        /// 处理输送线心跳
        /// </summary>
        /// <remarks>
        /// 当收到 PLC 的心跳信号时调用。
        /// 清除任务号,表示当前没有执行任务。
        /// 这是为了保持与 PLC 的连接活跃。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        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);
        }
        /// <summary>
        /// 输送线请求入库
        /// 处理输送线入库请求
        /// </summary>
        /// <remarks>
        /// 当 PLC 请求入库任务时调用。
        /// 流程:
        /// 1. 向 WMS 请求新任务
        /// 2. 查询待处理任务
        /// 3. 下发任务到 PLC
        /// 4. 更新任务状态
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        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<ConveyorLineTaskCommandNew>(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);
                }
            }
        }
        /// <summary>
        /// 输送线请求入库下一地址
        /// 处理输送线入库下一地址请求
        /// </summary>
        /// <remarks>
        /// 当入库任务执行到某个中间站点时调用。
        /// 根据下一地址判断是否需要与拘束机/插拔钉机等设备交互。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        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;
            }
            _targetAddressSelector.HandleInboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
            _logger.LogInformation("RequestInNextAddress:入库下一地址,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
            QuartzLogger.Info($"RequestInNextAddress:入库下一地址,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode);
            Dt_Task? newTask = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress);
            if (newTask != null)
            // 如果不是空托盘任务,处理目标地址(与拘束机/插拔钉机交互)
            if (task.TaskType != (int)TaskOutboundTypeEnum.OutEmpty)
            {
                if (_taskService.UpdateTaskStatusToNext(newTask).Status && newTask.TaskState == (int)TaskInStatusEnum.Line_InFinish)
                {
                    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_STB, 1, childDeviceCode);
                }
                _targetAddressSelector.HandleInboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
            }
            // 更新任务当前位置
            _ = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress);
            // 设置 WCS_STB 标志,表示 WCS 已处理
            conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
        }
        /// <summary>
        /// 输送线入库完成
        /// 处理输送线入库完成
        /// </summary>
        /// <remarks>
        /// 当入库任务完成时调用。
        /// 更新任务状态并回复 PLC。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void ConveyorLineInFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            // 查询正在执行的任务
            Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
            if (task != null)
            {
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
                WebResponseContent content = _taskService.UpdateTaskStatusToNext(task);
                Console.Out.WriteLine(content.Serialize());
                // 更新任务状态到下一阶段(通常是完成)
                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);
                }
            }
        }
        /// <summary>
        /// 输送线请求出信息
        /// 处理输送线出库请求
        /// </summary>
        /// <remarks>
        /// 当 PLC 请求出库任务时调用。
        /// 流程:
        /// 1. 查询待处理任务
        /// 2. 下发任务到 PLC
        /// 3. 更新任务状态
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        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.TargetAddress, childDeviceCode);
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, 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);
            }
        }
        /// <summary>
        /// 输送线请求出库下一地址
        /// 处理输送线出库下一地址请求
        /// </summary>
        /// <remarks>
        /// 当出库任务执行到某个中间站点时调用。
        /// 根据下一地址判断是否需要与拘束机/插拔钉机等设备交互。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        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;
            }
            _targetAddressSelector.HandleOutboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
            _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);
        }
        /// <summary>
        /// 输送线出库完成
        /// 处理输送线出库完成
        /// </summary>
        /// <remarks>
        /// 当出库任务完成时调用。
        /// 更新任务状态并回复 PLC。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void ConveyorLineOutFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            // 查询正在执行的任务
            Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
            if (task != null)
            {
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode);
                // 更新任务状态到下一阶段(通常是完成)
                WebResponseContent content = _taskService.UpdateTaskStatusToNext(task);
                Console.Out.WriteLine(content.Serialize());
                // 回复 ACK 确认
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                _logger.LogInformation("ConveyorLineOutFinish:出库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
                QuartzLogger.Info($"出库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode);
            }
        }
    }