| | |
| | | #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; |
| | |
| | | |
| | | 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); |
| | | } |
| | | } |
| | | } |