feat(WCS): 完善 WIDESEAWCS_Tasks 模块日志系统
为 RobotJob、ConveyorLineNewJob、StackerCraneJob 三个模块的代码添加完整的日志记录,
同时使用 ILogger 和 QuartzLogger 两种日志记录器,确保日志的完整性和可追溯性。
主要变更:
【日志规范】
- 所有 _logger.LogInformation/Debug/Warning/Error 均同步添加 QuartzLogger.Info/Debug/Warn/Error
- Info:记录正常业务流程节点
- Debug:记录详细执行路径和查询结果
- Warn:记录需要注意的异常情况(如版本冲突、未找到资源)
- Error:记录操作失败的情况
【RobotJob 模块】
- RobotClientManager: 添加 TCP 客户端连接/断开/订阅的完整日志
- RobotStateManager: 添加状态创建、版本冲突的日志
- RobotTaskProcessor: 添加取货指令下发、入库任务处理的完整日志
- RobotWorkflowOrchestrator: 添加放货/取货条件判断、条码生成的日志
- RobotJob: 添加 TCP 事件订阅、客户端断开、任务执行的日志
- 修复 Dt_RobotTask.TaskNum 属性引用错误
【ConveyorLineNewJob 模块】
- CommonConveyorLineNewJob: 添加并行处理、托盘检查、任务分发的完整日志
- ConveyorLineDispatchHandler: 添加心跳、入库请求、下一地址、出库完成的完整日志
- ConveyorLineTaskFilter: 添加任务查询、向 WMS 请求任务的日志
- ConveyorLineTargetAddressSelector: 添加拘束机/插拔钉机交互的完整日志
【StackerCraneJob 模块】
- CommonStackerCraneJob: 添加任务选择、命令构建/发送、任务完成的完整日志
- StackerCraneTaskSelector: 添加任务筛选、站台可用性判断的完整日志
- StackerCraneCommandBuilder: 添加命令类型匹配、入库/出库/移库命令构建的完整日志
【代码修复】
- 修复 ILogger<T> 类型 invariance 问题,改用非泛型 ILogger 接口
- 修复 LogWarn 方法名错误(应为 LogWarning)
- 修复 StackerCraneTaskSelectorTests 测试文件构造函数参数问题
修改文件:13 个
代码行数:+411 / -58
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| | |
| | | using MapsterMapper; |
| | | using Microsoft.Extensions.Configuration; |
| | | using Microsoft.Extensions.Logging; |
| | | using Quartz; |
| | | using SqlSugar; |
| | | using System.Text.Json; |
| | |
| | | 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; |
| | |
| | | private readonly HttpClientHelper _httpClientHelper; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger<CommonConveyorLineNewJob> _logger; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="taskService">任务服务</param> |
| | |
| | | /// <param name="routerService">路由服务</param> |
| | | /// <param name="mapper">对象映射器</param> |
| | | /// <param name="httpClientHelper">HTTP 客户端帮助类</param> |
| | | public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, HttpClientHelper httpClientHelper) |
| | | /// <param name="logger">日志记录器</param> |
| | | public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, HttpClientHelper httpClientHelper, ILogger<CommonConveyorLineNewJob> logger) |
| | | { |
| | | _taskService = taskService; |
| | | _taskExecuteDetailService = taskExecuteDetailService; |
| | | _routerService = routerService; |
| | | _mapper = mapper; |
| | | _httpClientHelper = httpClientHelper; |
| | | _logger = logger; |
| | | |
| | | // 初始化调度处理器 |
| | | _conveyorLineDispatch = new ConveyorLineDispatchHandler(_taskService, _taskExecuteDetailService, _routerService, _mapper); |
| | | _conveyorLineDispatch = new ConveyorLineDispatchHandler(_taskService, _taskExecuteDetailService, _routerService, _mapper, _logger); |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | if (childDeviceCodes == null || childDeviceCodes.Count == 0) |
| | | { |
| | | // 没有子设备,直接返回 |
| | | Console.WriteLine($"输送线 {conveyorLine.DeviceCode} 没有子设备"); |
| | | _logger.LogInformation("输送线 {DeviceCode} 没有子设备", conveyorLine.DeviceCode); |
| | | QuartzLogger.Info($"输送线 {conveyorLine.DeviceCode} 没有子设备", conveyorLine.DeviceCode); |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | |
| | | // 限制并发数:子设备数量和 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 => |
| | |
| | | // 如果命令为空,跳过 |
| | | if (command == null) |
| | | { |
| | | _logger.LogDebug("Execute:子设备 {ChildDeviceCode} 命令为空,跳过", childDeviceCode); |
| | | QuartzLogger.Debug($"子设备 {childDeviceCode} 命令为空,跳过", conveyorLine.DeviceCode); |
| | | return; |
| | | } |
| | | |
| | |
| | | { |
| | | // 没有任务,向 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<WebResponseContent>("GetOutBoundTrayTaskAsync", new CreateTaskDto() |
| | | { |
| | | WarehouseId = position.WarehouseId, |
| | |
| | | |
| | | // ========== 检查 PLC_STB 标志 ========== |
| | | // 只有当 PLC_STB 为 1 时才处理任务 |
| | | if (command.PLC_STB != 1) return; |
| | | if (command.PLC_STB != 1) |
| | | { |
| | | _logger.LogDebug("Execute:子设备 {ChildDeviceCode} PLC_STB 不为1,跳过", childDeviceCode); |
| | | QuartzLogger.Debug($"子设备 {childDeviceCode} PLC_STB 不为1,跳过", conveyorLine.DeviceCode); |
| | | return; |
| | | } |
| | | |
| | | // ========== 处理无托盘条码的情况 ========== |
| | | // 无托盘条码时,请求出库任务 |
| | | if (command.Barcode.IsNullOrEmpty() || command.Barcode.Replace("\0", "") == "") |
| | | { |
| | | _logger.LogDebug("Execute:子设备 {ChildDeviceCode} 无托盘条码,请求出库任务", childDeviceCode); |
| | | QuartzLogger.Debug($"子设备 {childDeviceCode} 无托盘条码,请求出库任务", conveyorLine.DeviceCode); |
| | | _conveyorLineDispatch.RequestOutbound(conveyorLine, command, childDeviceCode); |
| | | return; |
| | | } |
| | |
| | | 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; |
| | |
| | | catch (Exception innerEx) |
| | | { |
| | | // 记录异常,但不影响其他子设备的处理 |
| | | Console.Error.WriteLine($"{DateTime.UtcNow:O} [{childDeviceCode}] CorrelationId={correlationId} {innerEx}"); |
| | | _logger.LogError(innerEx, "Execute:子设备 {ChildDeviceCode} 处理异常,CorrelationId: {CorrelationId}", childDeviceCode, correlationId); |
| | | QuartzLogger.Error($"子设备处理异常: {innerEx.Message}", conveyorLine.DeviceCode, innerEx); |
| | | } |
| | | }); |
| | | } |
| | |
| | | catch (Exception ex) |
| | | { |
| | | // 记录整体异常 |
| | | Console.Error.WriteLine(ex); |
| | | _logger.LogError(ex, "Execute:输送线 {DeviceCode} 执行异常", ex.Message); |
| | | QuartzLogger.Error($"输送线执行异常: {ex.Message}", "CommonConveyorLineNewJob", ex); |
| | | } |
| | | return Task.CompletedTask; |
| | | } |
| | |
| | | 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; |
| | |
| | | private readonly IMapper _mapper; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 输送线任务过滤器 |
| | | /// </summary> |
| | | /// <remarks> |
| | |
| | | /// <param name="taskExecuteDetailService">任务执行明细服务</param> |
| | | /// <param name="routerService">路由服务</param> |
| | | /// <param name="mapper">对象映射器</param> |
| | | public ConveyorLineDispatchHandler(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper) |
| | | /// <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> |
| | |
| | | { |
| | | // 清除任务号,表示当前空闲 |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, 0, childDeviceCode); |
| | | _logger.LogDebug("HeartBeat:子设备 {ChildDeviceCode} 心跳", childDeviceCode); |
| | | QuartzLogger.Debug($"HeartBeat:子设备 {childDeviceCode} 心跳", conveyorLine.DeviceCode); |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | /// <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)) |
| | | { |
| | |
| | | |
| | | // 更新任务状态到下一阶段 |
| | | _taskService.UpdateTaskStatusToNext(task); |
| | | |
| | | _logger.LogInformation("RequestInbound:入库任务已下发,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode); |
| | | QuartzLogger.Info($"入库任务已下发,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode); |
| | | } |
| | | } |
| | | } |
| | |
| | | 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) |
| | |
| | | |
| | | // 更新任务状态到下一阶段(通常是完成) |
| | | WebResponseContent content = _taskService.UpdateTaskStatusToNext(task); |
| | | Console.Out.WriteLine(content.Serialize()); |
| | | |
| | | _logger.LogInformation("ConveyorLineInFinish:入库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode); |
| | | QuartzLogger.Info($"入库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | // 更新任务状态 |
| | | _taskService.UpdateTaskStatusToNext(task); |
| | | |
| | | _logger.LogInformation("RequestOutbound:出库任务已下发,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode); |
| | | QuartzLogger.Info($"出库任务已下发,任务号: {task.TaskNum}", conveyorLine.DeviceCode); |
| | | } |
| | | } |
| | | |
| | |
| | | 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) |
| | |
| | | |
| | | // 更新任务状态到下一阶段(通常是完成) |
| | | WebResponseContent content = _taskService.UpdateTaskStatusToNext(task); |
| | | Console.Out.WriteLine(content.Serialize()); |
| | | |
| | | _logger.LogInformation("ConveyorLineOutFinish:出库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode); |
| | | QuartzLogger.Info($"出库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode); |
| | | } |
| | | } |
| | | } |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | | using WIDESEAWCS_QuartzJob; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | |
| | | private static readonly List<string> PinMachineCodes = new List<string> { "10190", "20100" }; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="logger">日志记录器</param> |
| | | public ConveyorLineTargetAddressSelector(ILogger logger) |
| | | { |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理入库场景的下一地址请求 |
| | | /// </summary> |
| | | /// <remarks> |
| | |
| | | /// <param name="childDeviceCode">当前子设备编码</param> |
| | | public void HandleInboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | { |
| | | _logger.LogDebug("HandleInboundNextAddress:入库下一地址,子设备: {ChildDeviceCode},目标地址: {NextAddress}", childDeviceCode, nextAddress); |
| | | QuartzLogger.Debug($"HandleInboundNextAddress:入库下一地址,子设备: {childDeviceCode},目标地址: {nextAddress}", conveyorLine.DeviceCode); |
| | | // 调用通用处理方法,isUpper = true 表示处理上层 |
| | | HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: true); |
| | | } |
| | |
| | | /// <param name="childDeviceCode">当前子设备编码</param> |
| | | public void HandleOutboundNextAddress(CommonConveyorLine conveyorLine, string nextAddress, string childDeviceCode) |
| | | { |
| | | _logger.LogDebug("HandleOutboundNextAddress:出库下一地址,子设备: {ChildDeviceCode},目标地址: {NextAddress}", childDeviceCode, nextAddress); |
| | | QuartzLogger.Debug($"HandleOutboundNextAddress:出库下一地址,子设备: {childDeviceCode},目标地址: {nextAddress}", conveyorLine.DeviceCode); |
| | | // 调用通用处理方法,isUpper = false 表示处理下层 |
| | | HandleDeviceRequest(conveyorLine, nextAddress, childDeviceCode, isUpper: false); |
| | | } |
| | |
| | | ConstraintMachine? constraint = devices.OfType<ConstraintMachine>().FirstOrDefault(d => d.DeviceName == ConstraintMachineName); |
| | | if (constraint == null) |
| | | { |
| | | _logger.LogDebug("HandleDeviceRequest:未找到拘束机设备"); |
| | | QuartzLogger.Debug("HandleDeviceRequest:未找到拘束机设备", conveyorLine.DeviceCode); |
| | | // 未找到拘束机设备,直接返回 |
| | | return; |
| | | } |
| | |
| | | { |
| | | constraint.SetValue(ConstraintMachineDBName.ConstraintTrayOutputReadyLower, outputReq ? 1 : 0); |
| | | } |
| | | }); |
| | | }, |
| | | "拘束机"); |
| | | } |
| | | else if (PinMachineCodes.Contains(nextAddress)) |
| | | { |
| | |
| | | PinMachine? pinMachine = devices.OfType<PinMachine>().FirstOrDefault(d => d.DeviceName == PinMachineName); |
| | | if (pinMachine == null) |
| | | { |
| | | _logger.LogDebug("HandleDeviceRequest:未找到插拔钉机设备"); |
| | | QuartzLogger.Debug("HandleDeviceRequest:未找到插拔钉机设备", conveyorLine.DeviceCode); |
| | | return; |
| | | } |
| | | |
| | |
| | | { |
| | | pinMachine.SetValue(PinMachineDBName.PlugPinTrayOutputReadyLower, outputReq ? 1 : 0); |
| | | } |
| | | }); |
| | | }, |
| | | "插拔钉机"); |
| | | } |
| | | } |
| | | |
| | |
| | | /// <param name="getMaterialRequest">获取物料请求状态的委托</param> |
| | | /// <param name="getOutputRequest">获取出料请求状态的委托</param> |
| | | /// <param name="setOutputReady">设置输出就绪标志的委托</param> |
| | | private static void ProcessDeviceRequest( |
| | | /// <param name="deviceType">设备类型描述</param> |
| | | private void ProcessDeviceRequest( |
| | | CommonConveyorLine conveyorLine, |
| | | string childDeviceCode, |
| | | Func<bool> getMaterialRequest, |
| | | Func<bool> getOutputRequest, |
| | | Action<bool> setOutputReady) |
| | | Action<bool> setOutputReady, |
| | | string deviceType) |
| | | { |
| | | // 获取物料请求状态 |
| | | bool materialReq = getMaterialRequest(); |
| | | |
| | | // 获取出料请求状态 |
| | | bool outputReq = getOutputRequest(); |
| | | |
| | | _logger.LogDebug("ProcessDeviceRequest:{DeviceType},子设备: {ChildDeviceCode},物料请求: {MaterialReq},出料请求: {OutputReq}", |
| | | deviceType, childDeviceCode, materialReq, outputReq); |
| | | QuartzLogger.Debug($"ProcessDeviceRequest:{deviceType},子设备: {childDeviceCode},物料请求: {materialReq},出料请求: {outputReq}", conveyorLine.DeviceCode); |
| | | |
| | | // 如果设备需要物料 |
| | | if (materialReq) |
| | |
| | | |
| | | // 回复 ACK 确认信号 |
| | | conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 1, childDeviceCode); |
| | | |
| | | _logger.LogInformation("ProcessDeviceRequest:{DeviceType} 需要物料,已设置目标地址和ACK", deviceType); |
| | | QuartzLogger.Info($"ProcessDeviceRequest:{deviceType} 需要物料,已设置目标地址和ACK", conveyorLine.DeviceCode); |
| | | } |
| | | else |
| | | { |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | |
| | |
| | | private readonly ITaskService _taskService; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="taskService">任务服务实例</param> |
| | | public ConveyorLineTaskFilter(ITaskService taskService) |
| | | /// <param name="logger">日志记录器</param> |
| | | public ConveyorLineTaskFilter(ITaskService taskService, ILogger logger) |
| | | { |
| | | _taskService = taskService; |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | /// <returns>待处理的任务对象,如果没有则返回 null</returns> |
| | | public Dt_Task? QueryPendingTask(string deviceCode, string childDeviceCode) |
| | | { |
| | | return _taskService.QueryConveyorLineTask(deviceCode, childDeviceCode); |
| | | var task = _taskService.QueryConveyorLineTask(deviceCode, childDeviceCode); |
| | | _logger.LogDebug("QueryPendingTask:设备 {DeviceCode},子设备 {ChildDeviceCode},查询结果: {TaskNum}", deviceCode, childDeviceCode, task?.TaskNum); |
| | | QuartzLogger.Debug($"QueryPendingTask:设备 {deviceCode},子设备 {childDeviceCode},查询结果: {task?.TaskNum}", deviceCode); |
| | | return task; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | /// <returns>执行中的任务对象,如果没有则返回 null</returns> |
| | | public Dt_Task? QueryExecutingTask(int taskNo, string childDeviceCode) |
| | | { |
| | | return _taskService.QueryExecutingConveyorLineTask(taskNo, childDeviceCode); |
| | | var task = _taskService.QueryExecutingConveyorLineTask(taskNo, childDeviceCode); |
| | | _logger.LogDebug("QueryExecutingTask:任务号 {TaskNo},子设备 {ChildDeviceCode},查询结果: {Found}", taskNo, childDeviceCode, task != null); |
| | | QuartzLogger.Debug($"QueryExecutingTask:任务号 {taskNo},子设备 {childDeviceCode},查询结果: {(task != null)}", childDeviceCode); |
| | | return task; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | /// <returns>请求是否成功</returns> |
| | | public bool RequestWmsTask(string barcode, string childDeviceCode) |
| | | { |
| | | return _taskService.RequestWMSTask(barcode, childDeviceCode).Status; |
| | | _logger.LogInformation("RequestWmsTask:向WMS请求任务,条码: {Barcode},子设备: {ChildDeviceCode}", barcode, childDeviceCode); |
| | | QuartzLogger.Info($"向WMS请求任务,条码: {barcode}", childDeviceCode); |
| | | var result = _taskService.RequestWMSTask(barcode, childDeviceCode); |
| | | return result.Status; |
| | | } |
| | | } |
| | | } |
| | |
| | | using System.Collections.Concurrent; |
| | | using System.Net.Sockets; |
| | | using Microsoft.Extensions.Logging; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | | using WIDESEAWCS_QuartzJob; |
| | | using WIDESEAWCS_Tasks.SocketServer; |
| | |
| | | /// 机械手状态管理器,用于读写设备状态 |
| | | /// </summary> |
| | | private readonly RobotStateManager _stateManager; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 跟踪已启动消息处理的客户端,避免重复启动 |
| | |
| | | /// </summary> |
| | | /// <param name="tcpSocket">TCP Socket 服务器实例</param> |
| | | /// <param name="stateManager">状态管理器实例</param> |
| | | public RobotClientManager(TcpSocketServer tcpSocket, RobotStateManager stateManager) |
| | | /// <param name="logger">日志记录器</param> |
| | | public RobotClientManager(TcpSocketServer tcpSocket, RobotStateManager stateManager, ILogger logger) |
| | | { |
| | | _tcpSocket = tcpSocket; |
| | | _stateManager = stateManager; |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | // 清理该客户端的 HandleClientAsync 启动标志 |
| | | // 以便下次重连时可以重新启动处理 |
| | | _handleClientStarted.TryRemove(ipAddress, out _); |
| | | _logger.LogDebug("客户端未连接,IP: {IpAddress}", ipAddress); |
| | | QuartzLogger.Debug($"客户端未连接,IP: {ipAddress}", robotCrane.DeviceName); |
| | | return false; |
| | | } |
| | | |
| | |
| | | { |
| | | // 绑定客户端断开连接的事件处理 |
| | | _tcpSocket.RobotReceived += OnRobotReceived; |
| | | // 记录日志(注意:日志内容为"客户端已断开连接",可能是遗留的占位文本) |
| | | QuartzLogger.Warn($"客户端已断开连接", robotCrane.DeviceName); |
| | | // 记录日志:事件订阅成功 |
| | | _logger.LogInformation("机械手TCP消息事件已订阅,设备: {DeviceName}", robotCrane.DeviceName); |
| | | QuartzLogger.Info($"机械手TCP消息事件已订阅", robotCrane.DeviceName); |
| | | } |
| | | |
| | | // 从 TCP 服务器的客户端字典中获取 TcpClient 对象 |
| | |
| | | { |
| | | // 移除启动标志,返回 false 表示客户端不可用 |
| | | _handleClientStarted.TryRemove(ipAddress, out _); |
| | | _logger.LogWarning("获取TcpClient失败,IP: {IpAddress}", ipAddress); |
| | | QuartzLogger.Warn($"获取TcpClient失败,IP: {ipAddress}", robotCrane.DeviceName); |
| | | return false; |
| | | } |
| | | |
| | |
| | | // 如果尚未启动,则启动消息处理循环 |
| | | if (!alreadyStarted) |
| | | { |
| | | // 记录日志 |
| | | // 记录日志:启动消息处理 |
| | | _logger.LogInformation("启动客户端消息处理,IP: {IpAddress}", ipAddress); |
| | | QuartzLogger.Info($"启动客户端消息处理", robotCrane.DeviceName); |
| | | |
| | | // 获取最新的状态对象 |
| | |
| | | if (t.IsFaulted) |
| | | { |
| | | // 记录错误日志 |
| | | QuartzLogger.Info($"监听客户端消息事件异常", robotCrane.DeviceName); |
| | | Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] HandleClientAsync error: {t.Exception?.GetBaseException().Message}"); |
| | | _logger.LogError(t.Exception, "监听客户端消息事件异常,IP: {IpAddress}", ipAddress); |
| | | QuartzLogger.Error($"监听客户端消息事件异常", robotCrane.DeviceName, t.Exception); |
| | | // 发生错误时,移除启动标志,允许下次重试 |
| | | _handleClientStarted.TryRemove(ipAddress, out _); |
| | | } |
| | |
| | | // 移除该客户端的 HandleClientAsync 启动标志 |
| | | _handleClientStarted.TryRemove(clientId, out _); |
| | | |
| | | // 记录日志:客户端断开连接 |
| | | _logger.LogInformation("客户端断开连接,IP: {ClientId}", clientId); |
| | | QuartzLogger.Info($"客户端断开连接", clientId); |
| | | |
| | | // 重置该客户端的状态信息 |
| | | _stateManager.TryUpdateStateSafely(clientId, state => |
| | | { |
| | |
| | | ILogger<RobotJob> logger) |
| | | { |
| | | // 初始化状态管理器,传入缓存服务 |
| | | _stateManager = new RobotStateManager(cache); |
| | | _stateManager = new RobotStateManager(cache, _logger); |
| | | _logger = logger; |
| | | |
| | | // 创建 Socket 网关,封装 TcpSocketServer 的访问 |
| | |
| | | ISocketClientGateway socketGateway = new SocketClientGateway(tcpSocket); |
| | | |
| | | // 初始化任务处理器 |
| | | _taskProcessor = new RobotTaskProcessor(socketGateway, _stateManager, robotTaskService, taskService, httpClientHelper); |
| | | _taskProcessor = new RobotTaskProcessor(socketGateway, _stateManager, robotTaskService, taskService, httpClientHelper, _logger); |
| | | |
| | | // 初始化客户端管理器 |
| | | _clientManager = new RobotClientManager(tcpSocket, _stateManager); |
| | | _clientManager = new RobotClientManager(tcpSocket, _stateManager, _logger); |
| | | |
| | | // 初始化命令处理器 |
| | | // 简单命令处理器:处理状态更新等简单命令 |
| | |
| | | _messageRouter = new RobotMessageHandler(socketGateway, _stateManager, cache, simpleCommandHandler, prefixCommandHandler, logger); |
| | | |
| | | // 初始化工作流编排器 |
| | | _workflowOrchestrator = new RobotWorkflowOrchestrator(_stateManager, _clientManager, _taskProcessor, robotTaskService); |
| | | _workflowOrchestrator = new RobotWorkflowOrchestrator(_stateManager, _clientManager, _taskProcessor, robotTaskService, _logger); |
| | | |
| | | // 订阅客户端断开连接事件 |
| | | _clientManager.OnClientDisconnected += OnClientDisconnected; |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using Newtonsoft.Json; |
| | | using WIDESEAWCS_Common; |
| | | using WIDESEAWCS_Core.Caches; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | | using WIDESEAWCS_QuartzJob; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | |
| | | private readonly ICacheService _cache; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="cache">缓存服务实例(通常为 HybridCacheService)</param> |
| | | public RobotStateManager(ICacheService cache) |
| | | /// <param name="logger">日志记录器</param> |
| | | public RobotStateManager(ICacheService cache, ILogger logger) |
| | | { |
| | | _cache = cache; |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | newState.Version = DateTime.UtcNow.Ticks; |
| | | // 直接添加到缓存 |
| | | _cache.AddObject(cacheKey, newState); |
| | | _logger.LogDebug("TryUpdateStateSafely:创建新状态,IP: {IpAddress}", ipAddress); |
| | | QuartzLogger.Debug($"创建新状态,IP: {ipAddress}", ipAddress); |
| | | return true; |
| | | } |
| | | |
| | |
| | | newState.Version = DateTime.UtcNow.Ticks; |
| | | |
| | | // 尝试安全更新,如果版本冲突则返回 false |
| | | return _cache.TrySafeUpdate( |
| | | bool success = _cache.TrySafeUpdate( |
| | | cacheKey, |
| | | newState, |
| | | expectedVersion, |
| | | s => s.Version |
| | | ); |
| | | |
| | | if (!success) |
| | | { |
| | | _logger.LogWarning("TryUpdateStateSafely:版本冲突,更新失败,IP: {IpAddress},期望版本: {ExpectedVersion}", ipAddress, expectedVersion); |
| | | QuartzLogger.Warn($"版本冲突,更新失败,IP: {ipAddress}", ipAddress); |
| | | } |
| | | |
| | | return success; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using Newtonsoft.Json; |
| | | using WIDESEA_Core; |
| | | using WIDESEAWCS_Common; |
| | |
| | | private readonly HttpClientHelper _httpClientHelper; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="socketClientGateway">Socket 网关</param> |
| | |
| | | /// <param name="robotTaskService">机器人任务服务</param> |
| | | /// <param name="taskService">通用任务服务</param> |
| | | /// <param name="httpClientHelper">HTTP 客户端帮助类</param> |
| | | /// <param name="logger">日志记录器</param> |
| | | public RobotTaskProcessor( |
| | | ISocketClientGateway socketClientGateway, |
| | | RobotStateManager stateManager, |
| | | IRobotTaskService robotTaskService, |
| | | ITaskService taskService, |
| | | HttpClientHelper httpClientHelper) |
| | | HttpClientHelper httpClientHelper, |
| | | ILogger logger) |
| | | { |
| | | _socketClientGateway = socketClientGateway; |
| | | _stateManager = stateManager; |
| | | _robotTaskService = robotTaskService; |
| | | _taskService = taskService; |
| | | _httpClientHelper = httpClientHelper; |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | |
| | | if (result) |
| | | { |
| | | // 发送成功,记录日志 |
| | | QuartzLogger.Error($"下发取货指令,指令: {taskString}", state.RobotCrane.DeviceName); |
| | | // 发送成功,记录 Info 日志 |
| | | _logger.LogInformation("下发取货指令成功,指令: {TaskString},设备: {DeviceName}", taskString, state.RobotCrane?.DeviceName); |
| | | QuartzLogger.Info($"下发取货指令成功,指令: {taskString}", state.RobotCrane?.DeviceName); |
| | | |
| | | // 更新任务状态为"机器人执行中" |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | |
| | | { |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // 发送失败,记录 Error 日志 |
| | | _logger.LogError("下发取货指令失败,指令: {TaskString},设备: {DeviceName}", taskString, state.RobotCrane?.DeviceName); |
| | | QuartzLogger.Error($"下发取货指令失败,指令: {taskString}", state.RobotCrane?.DeviceName); |
| | | } |
| | | } |
| | | |
| | |
| | | var currentTask = state.CurrentTask; |
| | | if (currentTask == null) |
| | | { |
| | | _logger.LogDebug("HandleInboundTaskAsync:当前任务为空"); |
| | | QuartzLogger.Debug($"HandleInboundTaskAsync:当前任务为空", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | { |
| | | case RobotTaskTypeEnum.GroupPallet: |
| | | // 组盘任务不使用源地址,直接返回 false |
| | | _logger.LogDebug("HandleInboundTaskAsync:组盘任务不使用源地址"); |
| | | QuartzLogger.Debug($"HandleInboundTaskAsync:组盘任务不使用源地址", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return false; |
| | | |
| | | case RobotTaskTypeEnum.ChangePallet: |
| | |
| | | |
| | | case RobotTaskTypeEnum.SplitPallet: |
| | | // 拆盘任务不使用目标地址 |
| | | _logger.LogDebug("HandleInboundTaskAsync:拆盘任务不使用目标地址"); |
| | | QuartzLogger.Debug($"HandleInboundTaskAsync:拆盘任务不使用目标地址", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return true; |
| | | } |
| | | } |
| | |
| | | TaskType = taskType // 任务类型(入库/空托盘入库) |
| | | }; |
| | | |
| | | // 记录日志:开始调用 WMS 创建入库任务 |
| | | _logger.LogInformation("HandleInboundTaskAsync:调用WMS创建入库任务,托盘码: {PalletCode},任务类型: {TaskType}", PalletCode, taskType); |
| | | QuartzLogger.Info($"调用WMS创建入库任务,托盘码: {PalletCode},任务类型: {taskType}", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | |
| | | // 调用 WMS 接口创建入库任务 |
| | | var result = _httpClientHelper.Post<WebResponseContent>(nameof(ConfigKey.CreateTaskInboundAsync), taskDto.ToJson()); |
| | | |
| | | // 如果调用失败或返回错误状态 |
| | | if (!result.Data.Status && result.IsSuccess) |
| | | { |
| | | _logger.LogError("HandleInboundTaskAsync:WMS返回错误状态,Status: {Status}", result.Data.Status); |
| | | QuartzLogger.Error($"HandleInboundTaskAsync:WMS返回错误状态", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | var content = _taskService.ReceiveWMSTask(new List<WMSTaskDTO> { taskDTO }); |
| | | if (!content.Status) |
| | | { |
| | | _logger.LogError("HandleInboundTaskAsync:接收WMS任务失败"); |
| | | QuartzLogger.Error($"HandleInboundTaskAsync:接收WMS任务失败", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return false; |
| | | } |
| | | |
| | |
| | | // 更新任务状态到下一阶段 |
| | | if (_taskService.UpdateTaskStatusToNext(taskInfo).Status) |
| | | { |
| | | _logger.LogInformation("HandleInboundTaskAsync:入库任务处理成功,任务号: {TaskNum}", taskInfo.TaskNum); |
| | | QuartzLogger.Info($"HandleInboundTaskAsync:入库任务处理成功,任务号: {taskInfo.TaskNum}", state.RobotCrane?.DeviceName ?? "Unknown"); |
| | | return true; |
| | | } |
| | | } |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using WIDESEA_Core; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | |
| | | private readonly IRobotTaskService _robotTaskService; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="stateManager">状态管理器</param> |
| | | /// <param name="clientManager">客户端管理器</param> |
| | | /// <param name="taskProcessor">任务处理器</param> |
| | | /// <param name="robotTaskService">任务服务</param> |
| | | /// <param name="logger">日志记录器</param> |
| | | public RobotWorkflowOrchestrator( |
| | | RobotStateManager stateManager, |
| | | RobotClientManager clientManager, |
| | | RobotTaskProcessor taskProcessor, |
| | | IRobotTaskService robotTaskService) |
| | | IRobotTaskService robotTaskService, |
| | | ILogger logger) |
| | | { |
| | | _stateManager = stateManager; |
| | | _clientManager = clientManager; |
| | | _taskProcessor = taskProcessor; |
| | | _robotTaskService = robotTaskService; |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | && latestState.RobotArmObject == 1 |
| | | && task.RobotTaskState == TaskRobotStatusEnum.RobotPickFinish.GetHashCode()) |
| | | { |
| | | _logger.LogInformation("ExecuteAsync:满足放货条件,开始处理取货完成,任务号: {TaskNum}", task.RobotTaskNum); |
| | | QuartzLogger.Info($"ExecuteAsync:满足放货条件,开始处理取货完成", latestState.RobotCrane?.DeviceName ?? ipAddress); |
| | | // 发送放货指令 |
| | | await HandlePickFinishedStateAsync(task, ipAddress); |
| | | } |
| | |
| | | && (task.RobotTaskState == TaskRobotStatusEnum.RobotPutFinish.GetHashCode() |
| | | || task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode())) |
| | | { |
| | | _logger.LogInformation("ExecuteAsync:满足取货条件,开始处理放货完成,任务号: {TaskNum}", task.RobotTaskNum); |
| | | QuartzLogger.Info($"ExecuteAsync:满足取货条件,开始处理放货完成", latestState.RobotCrane?.DeviceName ?? ipAddress); |
| | | // 发送取货指令 |
| | | await HandlePutFinishedStateAsync(task, ipAddress); |
| | | } |
| | |
| | | |
| | | if (result) |
| | | { |
| | | // 发送成功,记录日志 |
| | | QuartzLogger.Info($"下发放货指令,指�?: {taskString}", task.RobotRoadway); |
| | | // 发送成功,记录 Info 日志 |
| | | _logger.LogInformation("HandlePickFinishedStateAsync:下发放货指令成功,指令: {TaskString},任务号: {TaskNum}", taskString, task.RobotTaskNum); |
| | | QuartzLogger.Info($"下发放货指令成功,指令: {taskString}", task.RobotRoadway); |
| | | |
| | | // 更新任务状态为"机器人执行中" |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // 发送失败,记录 Error 日志 |
| | | _logger.LogError("HandlePickFinishedStateAsync:下发放货指令失败,指令: {TaskString},任务号: {TaskNum}", taskString, task.RobotTaskNum); |
| | | QuartzLogger.Error($"下发放货指令失败,指令: {taskString}", task.RobotRoadway); |
| | | } |
| | | } |
| | | |
| | |
| | | var stateForUpdate = _stateManager.GetState(ipAddress); |
| | | if (stateForUpdate == null) |
| | | { |
| | | _logger.LogWarning("HandlePutFinishedStateAsync:获取状态失败,IP: {IpAddress}", ipAddress); |
| | | QuartzLogger.Warn($"HandlePutFinishedStateAsync:获取状态失败,IP: {ipAddress}", ipAddress); |
| | | return; |
| | | } |
| | | |
| | |
| | | stateForUpdate.CellBarcode.Add(trayBarcode1); |
| | | stateForUpdate.CellBarcode.Add(trayBarcode2); |
| | | |
| | | // 记录日志 |
| | | QuartzLogger.Info($"ȡ�������о�ţ���о: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName); |
| | | // 记录日志:生成托盘条码成功 |
| | | _logger.LogInformation("HandlePutFinishedStateAsync:生成托盘条码成功: {Barcode1}+{Barcode2},任务号: {TaskNum}", trayBarcode1, trayBarcode2, task.RobotTaskNum); |
| | | QuartzLogger.Info($"生成托盘条码成功: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName); |
| | | |
| | | // 发送取货指令 |
| | | await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate); |
| | | } |
| | | else |
| | | { |
| | | // 条码生成失败,记录错误日志 |
| | | _logger.LogError("HandlePutFinishedStateAsync:生成托盘条码失败,任务号: {TaskNum}", task.RobotTaskNum); |
| | | QuartzLogger.Error($"生成托盘条码失败", stateForUpdate.RobotCrane.DeviceName); |
| | | } |
| | | } |
| | | else |
| | | { |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using Quartz; |
| | | using System; |
| | | using System.Diagnostics.CodeAnalysis; |
| | |
| | | using WIDESEAWCS_QuartzJob.StackerCrane; |
| | | using WIDESEAWCS_Tasks.StackerCraneJob; |
| | | using WIDESEA_Core; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | | using WIDESEAWCS_QuartzJob.Service; |
| | | |
| | | namespace WIDESEAWCS_Tasks |
| | |
| | | private readonly StackerCraneCommandBuilder _commandBuilder; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger<CommonStackerCraneJob> _logger; |
| | | |
| | | /// <summary> |
| | | /// 堆垛机设备编码 |
| | | /// </summary> |
| | | private string _deviceCode = string.Empty; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="taskService">任务服务</param> |
| | |
| | | /// <param name="taskRepository">任务仓储</param> |
| | | /// <param name="routerService">路由服务</param> |
| | | /// <param name="httpClientHelper">HTTP 客户端帮助类</param> |
| | | /// <param name="logger">日志记录器</param> |
| | | public CommonStackerCraneJob( |
| | | ITaskService taskService, |
| | | ITaskExecuteDetailService taskExecuteDetailService, |
| | | ITaskRepository taskRepository, |
| | | IRouterService routerService, |
| | | HttpClientHelper httpClientHelper) |
| | | HttpClientHelper httpClientHelper, |
| | | ILogger<CommonStackerCraneJob> logger) |
| | | { |
| | | _taskService = taskService; |
| | | _taskExecuteDetailService = taskExecuteDetailService; |
| | | _taskRepository = taskRepository; |
| | | _logger = logger; |
| | | |
| | | // 加载配置文件 |
| | | _config = LoadConfig(); |
| | | |
| | | // 初始化任务选择器 |
| | | _taskSelector = new StackerCraneTaskSelector(taskService, routerService, httpClientHelper); |
| | | _taskSelector = new StackerCraneTaskSelector(taskService, routerService, httpClientHelper, _logger); |
| | | |
| | | // 初始化命令构建器 |
| | | _commandBuilder = new StackerCraneCommandBuilder(taskService, routerService, _config); |
| | | _commandBuilder = new StackerCraneCommandBuilder(taskService, routerService, _config, _logger); |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | if (!flag || value is not IStackerCrane commonStackerCrane) |
| | | { |
| | | // 参数无效,直接返回 |
| | | _logger.LogWarning("Execute:参数无效"); |
| | | QuartzLogger.Warn("Execute:参数无效", "CommonStackerCraneJob"); |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | _deviceCode = commonStackerCrane.DeviceCode; |
| | | |
| | | // ========== 订阅任务完成事件(全局只订阅一次) ========== |
| | | if (!commonStackerCrane.IsEventSubscribed) |
| | | { |
| | | // 绑定任务完成事件处理方法 |
| | | commonStackerCrane.StackerCraneTaskCompletedEventHandler += CommonStackerCrane_StackerCraneTaskCompletedEventHandler; |
| | | _logger.LogInformation("Execute:订阅任务完成事件,设备: {DeviceCode}", _deviceCode); |
| | | QuartzLogger.Info($"订阅任务完成事件", _deviceCode); |
| | | } |
| | | |
| | | // ========== 检查堆垛机任务完成状态 ========== |
| | | commonStackerCrane.CheckStackerCraneTaskCompleted(); |
| | | _logger.LogDebug("Execute:检查任务完成状态,设备: {DeviceCode}", _deviceCode); |
| | | QuartzLogger.Debug($"检查任务完成状态,设备: {_deviceCode}", _deviceCode); |
| | | |
| | | // ========== 检查是否可以发送新任务 ========== |
| | | if (!commonStackerCrane.IsCanSendTask(commonStackerCrane.Communicator, commonStackerCrane.DeviceProDTOs, commonStackerCrane.DeviceProtocolDetailDTOs)) |
| | | { |
| | | // 堆垛机不可用(如正在执行上一任务),直接返回 |
| | | _logger.LogDebug("Execute:堆垛机不可用,设备: {DeviceCode}", _deviceCode); |
| | | QuartzLogger.Debug($"堆垛机不可用,设备: {_deviceCode}", _deviceCode); |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | |
| | | if (task == null) |
| | | { |
| | | // 没有可用任务 |
| | | _logger.LogDebug("Execute:没有可用任务,设备: {DeviceCode}", _deviceCode); |
| | | QuartzLogger.Debug($"没有可用任务,设备: {_deviceCode}", _deviceCode); |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | _logger.LogInformation("Execute:选择任务,设备: {DeviceCode},任务号: {TaskNum}", _deviceCode, task.TaskNum); |
| | | QuartzLogger.Info($"选择任务,任务号: {task.TaskNum}", _deviceCode); |
| | | |
| | | // ========== 构建命令 ========== |
| | | // 命令构建下沉到专用构建器 |
| | |
| | | if (stackerCraneTaskCommand == null) |
| | | { |
| | | // 命令构建失败 |
| | | _logger.LogWarning("Execute:命令构建失败,设备: {DeviceCode},任务号: {TaskNum}", _deviceCode, task.TaskNum); |
| | | QuartzLogger.Warn($"命令构建失败,任务号: {task.TaskNum}", _deviceCode); |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | |
| | | // 发送成功,更新状态 |
| | | commonStackerCrane.LastTaskType = task.TaskType; |
| | | _taskService.UpdateTaskStatusToNext(task.TaskNum); |
| | | |
| | | _logger.LogInformation("Execute:命令发送成功,设备: {DeviceCode},任务号: {TaskNum}", _deviceCode, task.TaskNum); |
| | | QuartzLogger.Info($"命令发送成功,任务号: {task.TaskNum}", _deviceCode); |
| | | } |
| | | else |
| | | { |
| | | _logger.LogError("Execute:命令发送失败,设备: {DeviceCode},任务号: {TaskNum}", _deviceCode, task.TaskNum); |
| | | QuartzLogger.Error($"命令发送失败", _deviceCode); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // 记录异常 |
| | | Console.WriteLine($"CommonStackerCraneJob Error: {ex.Message}"); |
| | | _logger.LogError(ex, "Execute:执行异常,设备: {DeviceCode}", _deviceCode); |
| | | QuartzLogger.Error($"执行异常: {ex.Message}", _deviceCode, ex); |
| | | } |
| | | |
| | | return Task.CompletedTask; |
| | |
| | | if (stackerCrane != null) |
| | | { |
| | | // 记录日志 |
| | | Console.Out.WriteLine("TaskCompleted" + e.TaskNum); |
| | | _logger.LogInformation("CommonStackerCrane_StackerCraneTaskCompletedEventHandler:任务完成,任务号: {TaskNum}", e.TaskNum); |
| | | QuartzLogger.Info($"任务完成,任务号: {e.TaskNum}", stackerCrane.DeviceCode); |
| | | |
| | | // 更新任务状态为完成 |
| | | _taskService.StackCraneTaskCompleted(e.TaskNum); |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using System; |
| | | using System.Diagnostics.CodeAnalysis; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob.Models; |
| | |
| | | private readonly StackerCraneCommandConfig _config; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="taskService">任务服务</param> |
| | | /// <param name="routerService">路由服务</param> |
| | | /// <param name="config">命令配置</param> |
| | | /// <param name="logger">日志记录器</param> |
| | | public StackerCraneCommandBuilder( |
| | | ITaskService taskService, |
| | | IRouterService routerService, |
| | | StackerCraneCommandConfig config) |
| | | StackerCraneCommandConfig config, |
| | | ILogger logger) |
| | | { |
| | | _taskService = taskService; |
| | | _routerService = routerService; |
| | | _config = config; |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | { |
| | | // 根据巷道获取命令类型 |
| | | string commandType = GetCommandType(task.Roadway); |
| | | |
| | | _logger.LogInformation("ConvertToStackerCraneTaskCommand:构建命令,任务号: {TaskNum},巷道: {Roadway},命令类型: {CommandType}", task.TaskNum, task.Roadway, commandType); |
| | | QuartzLogger.Info($"构建命令,任务号: {task.TaskNum},巷道: {task.Roadway},命令类型: {commandType}", task.Roadway); |
| | | |
| | | // 根据命令类型调用相应的构建方法 |
| | | return commandType switch |
| | |
| | | { |
| | | if (roadway.Contains(mapping.Key)) |
| | | { |
| | | _logger.LogDebug("GetCommandType:匹配巷道 {Roadway},命令类型: {CommandType}", roadway, mapping.Value); |
| | | QuartzLogger.Debug($"GetCommandType:匹配巷道 {roadway},命令类型: {mapping.Value}", roadway); |
| | | return mapping.Value; |
| | | } |
| | | } |
| | | |
| | | _logger.LogDebug("GetCommandType:巷道 {Roadway} 未匹配,使用默认命令类型: {DefaultType}", roadway, _config.DefaultCommandType); |
| | | QuartzLogger.Debug($"GetCommandType:巷道 {roadway} 未匹配,使用默认命令类型: {_config.DefaultCommandType}", roadway); |
| | | return _config.DefaultCommandType; |
| | | } |
| | | |
| | |
| | | // 获取任务类型分组 |
| | | TaskTypeGroup taskTypeGroup = task.TaskType.GetTaskTypeGroup(); |
| | | |
| | | _logger.LogDebug("BuildCommand:任务号: {TaskNum},任务类型分组: {TaskTypeGroup}", task.TaskNum, taskTypeGroup); |
| | | QuartzLogger.Debug($"BuildCommand:任务号: {task.TaskNum},任务类型分组: {taskTypeGroup}", task.Roadway); |
| | | |
| | | // 根据任务类型分发构建 |
| | | return taskTypeGroup switch |
| | | { |
| | |
| | | /// <returns>填充好的命令对象</returns> |
| | | private T? BuildInboundCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | _logger.LogInformation("BuildInboundCommand:构建入库命令,任务号: {TaskNum}", task.TaskNum); |
| | | QuartzLogger.Info($"BuildInboundCommand:构建入库命令,任务号: {task.TaskNum}", task.Roadway); |
| | | |
| | | // 确定任务类型(空托盘用特殊类型 100) |
| | | int taskType = 0; |
| | | if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty) |
| | |
| | | if (router == null) |
| | | { |
| | | // 未找到站台,更新异常信息 |
| | | _logger.LogError("BuildInboundCommand:未找到站台【{CurrentAddress}】信息,任务号: {TaskNum}", task.CurrentAddress, task.TaskNum); |
| | | QuartzLogger.Error($"BuildInboundCommand:未找到站台【{task.CurrentAddress}】信息", task.Roadway); |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.CurrentAddress}】信息,无法获取对应的堆垛机取货站台信息"); |
| | | return null; |
| | | } |
| | |
| | | // 解析目标地址(库位地址) |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _logger.LogError("BuildInboundCommand:入库任务终点地址解析失败,终点: {NextAddress},任务号: {TaskNum}", task.NextAddress, task.TaskNum); |
| | | QuartzLogger.Error($"BuildInboundCommand:入库任务终点地址解析失败,终点: {task.NextAddress}", task.Roadway); |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"入库任务终点错误,终点:【{task.NextAddress}】"); |
| | | return null; |
| | | } |
| | |
| | | SetCommandProperty(command, "EndRow", endRow); |
| | | SetCommandProperty(command, "EndColumn", endColumn); |
| | | SetCommandProperty(command, "EndLayer", endLayer); |
| | | |
| | | _logger.LogInformation("BuildInboundCommand:入库命令构建成功,起点: {StartRow}-{StartColumn}-{StartLayer},终点: {EndRow}-{EndColumn}-{EndLayer},任务号: {TaskNum}", |
| | | router.SrmRow, router.SrmColumn, router.SrmLayer, endRow, endColumn, endLayer, task.TaskNum); |
| | | QuartzLogger.Info($"BuildInboundCommand:入库命令构建成功,起点: {router.SrmRow}-{router.SrmColumn}-{router.SrmLayer},终点: {endRow}-{endColumn}-{endLayer}", task.Roadway); |
| | | |
| | | return command; |
| | | } |
| | |
| | | /// <returns>填充好的命令对象</returns> |
| | | private T? BuildOutboundCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | _logger.LogInformation("BuildOutboundCommand:构建出库命令,任务号: {TaskNum}", task.TaskNum); |
| | | QuartzLogger.Info($"BuildOutboundCommand:构建出库命令,任务号: {task.TaskNum}", task.Roadway); |
| | | |
| | | // 确定任务类型 |
| | | int taskType = 0; |
| | | if (task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty) |
| | |
| | | Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, taskType); |
| | | if (router == null) |
| | | { |
| | | _logger.LogError("BuildOutboundCommand:未找到站台【{TargetAddress}】信息,任务号: {TaskNum}", task.TargetAddress, task.TaskNum); |
| | | QuartzLogger.Error($"BuildOutboundCommand:未找到站台【{task.TargetAddress}】信息", task.Roadway); |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.TargetAddress}】信息,无法获取对应的堆垛机放货站台信息"); |
| | | return null; |
| | | } |
| | |
| | | // 解析起始地址(库位地址) |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _logger.LogError("BuildOutboundCommand:出库任务起点地址解析失败,起点: {CurrentAddress},任务号: {TaskNum}", task.CurrentAddress, task.TaskNum); |
| | | QuartzLogger.Error($"BuildOutboundCommand:出库任务起点地址解析失败,起点: {task.CurrentAddress}", task.Roadway); |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"出库任务起点错误,起点:【{task.CurrentAddress}】"); |
| | | return null; |
| | | } |
| | |
| | | SetCommandProperty(command, "StartRow", startRow); |
| | | SetCommandProperty(command, "StartColumn", startColumn); |
| | | SetCommandProperty(command, "StartLayer", startLayer); |
| | | |
| | | _logger.LogInformation("BuildOutboundCommand:出库命令构建成功,起点: {StartRow}-{StartColumn}-{StartLayer},终点: {EndRow}-{EndColumn}-{EndLayer},任务号: {TaskNum}", |
| | | startRow, startColumn, startLayer, router.SrmRow, router.SrmColumn, router.SrmLayer, task.TaskNum); |
| | | QuartzLogger.Info($"BuildOutboundCommand:出库命令构建成功,起点: {startRow}-{startColumn}-{startLayer},终点: {router.SrmRow}-{router.SrmColumn}-{router.SrmLayer}", task.Roadway); |
| | | |
| | | return command; |
| | | } |
| | |
| | | /// <returns>填充好的命令对象</returns> |
| | | private T? BuildRelocationCommand<T>(Dt_Task task, T command) where T : class |
| | | { |
| | | _logger.LogInformation("BuildRelocationCommand:构建移库命令,任务号: {TaskNum}", task.TaskNum); |
| | | QuartzLogger.Info($"BuildRelocationCommand:构建移库命令,任务号: {task.TaskNum}", task.Roadway); |
| | | |
| | | // 解析目标地址 |
| | | if (!TryParseAddress(task.NextAddress, out short endRow, out short endColumn, out short endLayer)) |
| | | { |
| | | _logger.LogError("BuildRelocationCommand:移库任务终点地址解析失败,终点: {NextAddress},任务号: {TaskNum}", task.NextAddress, task.TaskNum); |
| | | QuartzLogger.Error($"BuildRelocationCommand:移库任务终点地址解析失败,终点: {task.NextAddress}", task.Roadway); |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务终点错误,终点:【{task.NextAddress}】"); |
| | | return null; |
| | | } |
| | |
| | | // 解析起始地址 |
| | | if (!TryParseAddress(task.CurrentAddress, out short startRow, out short startColumn, out short startLayer)) |
| | | { |
| | | _logger.LogError("BuildRelocationCommand:移库任务起点地址解析失败,起点: {CurrentAddress},任务号: {TaskNum}", task.CurrentAddress, task.TaskNum); |
| | | QuartzLogger.Error($"BuildRelocationCommand:移库任务起点地址解析失败,起点: {task.CurrentAddress}", task.Roadway); |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"移库任务起点错误,起点:【{task.CurrentAddress}】"); |
| | | return null; |
| | | } |
| | |
| | | SetCommandProperty(command, "StartColumn", startColumn); |
| | | SetCommandProperty(command, "StartLayer", startLayer); |
| | | |
| | | _logger.LogInformation("BuildRelocationCommand:移库命令构建成功,起点: {StartRow}-{StartColumn}-{StartLayer},终点: {EndRow}-{EndColumn}-{EndLayer},任务号: {TaskNum}", |
| | | startRow, startColumn, startLayer, endRow, endColumn, endLayer, task.TaskNum); |
| | | QuartzLogger.Info($"BuildRelocationCommand:移库命令构建成功,起点: {startRow}-{startColumn}-{startLayer},终点: {endRow}-{endColumn}-{endLayer}", task.Roadway); |
| | | |
| | | return command; |
| | | } |
| | | |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using Newtonsoft.Json; |
| | | using System.Diagnostics.CodeAnalysis; |
| | | using WIDESEA_Core; |
| | | using WIDESEAWCS_Common.HttpEnum; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core; |
| | | using WIDESEAWCS_Core.LogHelper; |
| | | using WIDESEAWCS_ITaskInfoService; |
| | | using WIDESEAWCS_Model.Models; |
| | | using WIDESEAWCS_QuartzJob; |
| | |
| | | private readonly IRouterService _routerService; |
| | | |
| | | /// <summary> |
| | | /// 日志记录器 |
| | | /// </summary> |
| | | private readonly ILogger _logger; |
| | | |
| | | /// <summary> |
| | | /// 移库检查委托函数 |
| | | /// </summary> |
| | | /// <remarks> |
| | |
| | | /// <param name="taskService">任务服务</param> |
| | | /// <param name="routerService">路由服务</param> |
| | | /// <param name="httpClientHelper">HTTP 客户端帮助类</param> |
| | | public StackerCraneTaskSelector(ITaskService taskService, IRouterService routerService, HttpClientHelper httpClientHelper) |
| | | : this(taskService, routerService, taskNum => QueryTransferTask(httpClientHelper, taskNum)) |
| | | /// <param name="logger">日志记录器</param> |
| | | public StackerCraneTaskSelector(ITaskService taskService, IRouterService routerService, HttpClientHelper httpClientHelper, ILogger logger) |
| | | : this(taskService, routerService, taskNum => QueryTransferTask(httpClientHelper, taskNum), logger) |
| | | { |
| | | } |
| | | |
| | |
| | | /// <param name="taskService">任务服务</param> |
| | | /// <param name="routerService">路由服务</param> |
| | | /// <param name="transferCheck">移库检查函数</param> |
| | | public StackerCraneTaskSelector(ITaskService taskService, IRouterService routerService, Func<int, Dt_Task?> transferCheck) |
| | | /// <param name="logger">日志记录器</param> |
| | | public StackerCraneTaskSelector(ITaskService taskService, IRouterService routerService, Func<int, Dt_Task?> transferCheck, ILogger logger) |
| | | { |
| | | _taskService = taskService; |
| | | _routerService = routerService; |
| | | _transferCheck = transferCheck; |
| | | _logger = logger; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | public Dt_Task? SelectTask(IStackerCrane commonStackerCrane) |
| | | { |
| | | Dt_Task? candidateTask; |
| | | var deviceCode = commonStackerCrane.DeviceCode; |
| | | |
| | | _logger.LogInformation("SelectTask:开始选择任务,设备: {DeviceCode},上一任务类型: {LastTaskType}", deviceCode, commonStackerCrane.LastTaskType); |
| | | QuartzLogger.Info($"开始选择任务,设备: {deviceCode},上一任务类型: {commonStackerCrane.LastTaskType}", deviceCode); |
| | | |
| | | // 根据上一任务类型决定查询策略 |
| | | if (commonStackerCrane.LastTaskType == null) |
| | | { |
| | | // 没有上一任务类型,查询普通任务 |
| | | candidateTask = _taskService.QueryStackerCraneTask(commonStackerCrane.DeviceCode); |
| | | candidateTask = _taskService.QueryStackerCraneTask(deviceCode); |
| | | _logger.LogDebug("SelectTask:查询普通任务,设备: {DeviceCode},结果: {TaskNum}", deviceCode, candidateTask?.TaskNum); |
| | | QuartzLogger.Debug($"查询普通任务,设备: {deviceCode},结果: {candidateTask?.TaskNum}", deviceCode); |
| | | } |
| | | else if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup) |
| | | { |
| | | // 上一任务是出库,优先查入库任务 |
| | | candidateTask = _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | candidateTask = _taskService.QueryStackerCraneInTask(deviceCode); |
| | | // 如果没有入库任务,再查一下出库任务 |
| | | candidateTask ??= _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | candidateTask ??= _taskService.QueryStackerCraneOutTask(deviceCode); |
| | | _logger.LogDebug("SelectTask:出库后优先查入库,设备: {DeviceCode},结果: {TaskNum}", deviceCode, candidateTask?.TaskNum); |
| | | QuartzLogger.Debug($"出库后优先查入库,设备: {deviceCode},结果: {candidateTask?.TaskNum}", deviceCode); |
| | | } |
| | | else |
| | | { |
| | | // 上一任务是入库(非出库),优先查出库任务 |
| | | candidateTask = _taskService.QueryStackerCraneOutTask(commonStackerCrane.DeviceCode); |
| | | candidateTask = _taskService.QueryStackerCraneOutTask(deviceCode); |
| | | _logger.LogDebug("SelectTask:入库后优先查出库,设备: {DeviceCode},结果: {TaskNum}", deviceCode, candidateTask?.TaskNum); |
| | | QuartzLogger.Debug($"入库后优先查出库,设备: {deviceCode},结果: {candidateTask?.TaskNum}", deviceCode); |
| | | } |
| | | |
| | | // 如果没有候选任务,返回 null |
| | | if (candidateTask == null) |
| | | { |
| | | _logger.LogDebug("SelectTask:没有候选任务,设备: {DeviceCode}", deviceCode); |
| | | QuartzLogger.Debug($"没有候选任务,设备: {deviceCode}", deviceCode); |
| | | return null; |
| | | } |
| | | |
| | | // 如果不是出库任务,直接返回 |
| | | if (candidateTask.TaskType.GetTaskTypeGroup() != TaskTypeGroup.OutbondGroup) |
| | | { |
| | | _logger.LogInformation("SelectTask:选中非出库任务,设备: {DeviceCode},任务号: {TaskNum},任务类型: {TaskType}", deviceCode, candidateTask.TaskNum, candidateTask.TaskType); |
| | | QuartzLogger.Info($"选中非出库任务,任务号: {candidateTask.TaskNum},任务类型: {candidateTask.TaskType}", deviceCode); |
| | | return candidateTask; |
| | | } |
| | | |
| | |
| | | Dt_Task? selectedTask = TrySelectOutboundTask(candidateTask); |
| | | if (selectedTask != null) |
| | | { |
| | | _logger.LogInformation("SelectTask:选中出库任务,设备: {DeviceCode},任务号: {TaskNum}", deviceCode, selectedTask.TaskNum); |
| | | QuartzLogger.Info($"选中出库任务,任务号: {selectedTask.TaskNum}", deviceCode); |
| | | return selectedTask; |
| | | } |
| | | |
| | | // 查找其他可用的出库站台 |
| | | var otherOutStationCodes = _routerService |
| | | .QueryNextRoutes(commonStackerCrane.DeviceCode, candidateTask.NextAddress, candidateTask.TaskType) |
| | | .QueryNextRoutes(deviceCode, candidateTask.NextAddress, candidateTask.TaskType) |
| | | .Select(x => x.ChildPosi) |
| | | .ToList(); |
| | | |
| | | // 查询其他站台的出库任务 |
| | | var tasks = _taskService.QueryStackerCraneOutTasks(commonStackerCrane.DeviceCode, otherOutStationCodes); |
| | | var tasks = _taskService.QueryStackerCraneOutTasks(deviceCode, otherOutStationCodes); |
| | | foreach (var alternativeTask in tasks) |
| | | { |
| | | selectedTask = TrySelectOutboundTask(alternativeTask); |
| | | if (selectedTask != null) |
| | | { |
| | | _logger.LogInformation("SelectTask:选中备选出库任务,设备: {DeviceCode},任务号: {TaskNum}", deviceCode, selectedTask.TaskNum); |
| | | QuartzLogger.Info($"选中备选出库任务,任务号: {selectedTask.TaskNum}", deviceCode); |
| | | return selectedTask; |
| | | } |
| | | } |
| | | |
| | | // 没有可用出库任务,尝试返回入库任务 |
| | | return _taskService.QueryStackerCraneInTask(commonStackerCrane.DeviceCode); |
| | | var inboundTask = _taskService.QueryStackerCraneInTask(deviceCode); |
| | | _logger.LogInformation("SelectTask:返回入库任务,设备: {DeviceCode},任务号: {TaskNum}", deviceCode, inboundTask?.TaskNum); |
| | | QuartzLogger.Info($"返回入库任务,任务号: {inboundTask?.TaskNum}", deviceCode); |
| | | return inboundTask; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | if (router == null) |
| | | { |
| | | // 未找到站台路由信息 |
| | | _logger.LogWarning("IsOutTaskStationAvailable:未找到站台路由信息,站台: {NextAddress},任务号: {TaskNum}", task.NextAddress, task.TaskNum); |
| | | QuartzLogger.Warn($"IsOutTaskStationAvailable:未找到站台路由信息,站台: {task.NextAddress}", task.Roadway); |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到站台【{task.NextAddress}】信息,无法校验站台"); |
| | | return false; |
| | | } |
| | |
| | | if (device == null) |
| | | { |
| | | // 未找到设备 |
| | | _logger.LogWarning("IsOutTaskStationAvailable:未找到出库站台对应的通讯对象,站台: {ChildPosiDeviceCode},任务号: {TaskNum}", router.ChildPosiDeviceCode, task.TaskNum); |
| | | QuartzLogger.Warn($"IsOutTaskStationAvailable:未找到出库站台对应的通讯对象,站台: {router.ChildPosiDeviceCode}", task.Roadway); |
| | | _taskService.UpdateTaskExceptionMessage(task.TaskNum, $"未找到出库站台【{router.ChildPosiDeviceCode}】对应的通讯对象,无法判断出库站台是否被占用"); |
| | | return false; |
| | | } |
| | |
| | | CommonConveyorLine conveyorLine = (CommonConveyorLine)device; |
| | | |
| | | // 检查站台是否被占用 |
| | | return conveyorLine.IsOccupied(router.ChildPosi); |
| | | bool isOccupied = conveyorLine.IsOccupied(router.ChildPosi); |
| | | _logger.LogInformation("IsOutTaskStationAvailable:站台 {ChildPosi},是否被占用: {IsOccupied},任务号: {TaskNum}", router.ChildPosi, isOccupied, task.TaskNum); |
| | | QuartzLogger.Info($"IsOutTaskStationAvailable:站台 {router.ChildPosi},是否被占用: {isOccupied}", task.Roadway); |
| | | |
| | | return !isOccupied; |
| | | } |
| | | } |
| | | } |
| | |
| | | using Microsoft.Extensions.Logging; |
| | | using Moq; |
| | | using WIDESEAWCS_Common.TaskEnum; |
| | | using WIDESEAWCS_Core; |
| | |
| | | taskService.Setup(x => x.AddData(It.IsAny<Dt_Task>())).Returns(WebResponseContent.Instance.OK()); |
| | | |
| | | var transferCheckCalledCount = 0; |
| | | var mockLogger = new Mock<ILogger>(); |
| | | var selector = new StackerCraneTaskSelector( |
| | | taskService.Object, |
| | | routerService.Object, |
| | |
| | | transferCheckCalledCount++; |
| | | Assert.Equal(1001, taskNum); |
| | | return relocationTask; |
| | | }); |
| | | }, |
| | | mockLogger.Object); |
| | | |
| | | var selectedTask = selector.SelectTask(stackerCrane.Object); |
| | | |
| | |
| | | .Setup(x => x.QueryNextRoutes("SC01", "OUT-01", (int)TaskOutboundTypeEnum.Outbound)) |
| | | .Returns(new List<WIDESEAWCS_QuartzJob.Models.Dt_Router>()); |
| | | |
| | | var mockLogger = new Mock<ILogger>(); |
| | | var selector = new StackerCraneTaskSelector( |
| | | taskService.Object, |
| | | routerService.Object, |
| | | _ => newOutboundTask); |
| | | _ => newOutboundTask, |
| | | mockLogger.Object); |
| | | |
| | | _ = selector.SelectTask(stackerCrane.Object); |
| | | |