wanshenmean
10 小时以前 853f7a71577bd8694c848985e1eb21c74d30eba9
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs
@@ -9,6 +9,7 @@
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.ConveyorLine.Enum;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Service;
@@ -62,7 +63,7 @@
        /// <param name="httpClientHelper">HTTP 客户端帮助类</param>
        /// <param name="logger">日志记录器</param>
        public StackerCraneTaskSelector(ITaskService taskService, IRouterService routerService, HttpClientHelper httpClientHelper, ILogger logger)
            : this(taskService, routerService, taskNum => QueryTransferTask(httpClientHelper, taskNum), logger)
            : this(taskService, routerService, taskNum => QueryTransferTask(httpClientHelper, taskNum, logger), logger)
        {
        }
@@ -100,15 +101,14 @@
            Dt_Task? candidateTask;
            var deviceCode = commonStackerCrane.DeviceCode;
            //_logger.LogInformation("SelectTask:开始选择任务,设备: {DeviceCode},上一任务类型: {LastTaskType}", deviceCode, commonStackerCrane.LastTaskType);
            //QuartzLogger.Info($"开始选择任务,设备: {deviceCode},上一任务类型: {commonStackerCrane.LastTaskType}", deviceCode);
            QuartzLogHelper.LogDebug(_logger, $"开始选择任务,设备: {deviceCode},上一任务类型: {commonStackerCrane.LastTaskType}",commonStackerCrane.DeviceName);
            // 根据上一任务类型决定查询策略
            if (commonStackerCrane.LastTaskType == null || commonStackerCrane.LastTaskType == TaskRelocationTypeEnum.Relocation.GetHashCode())
            {
                // 没有上一任务类型,查询普通任务
                candidateTask = _taskService.QueryStackerCraneTask(deviceCode);
                //QuartzLogHelper.LogDebug(_logger, "SelectTask:查询普通任务,设备: {DeviceCode},结果: {TaskNum}", $"查询普通任务,设备: {deviceCode},结果: {candidateTask?.TaskNum}", deviceCode, deviceCode, candidateTask?.TaskNum);
                QuartzLogHelper.LogDebug(_logger, $"查询普通任务,设备: {deviceCode},结果: {candidateTask?.TaskNum}", commonStackerCrane.DeviceName);
            }
            else if (commonStackerCrane.LastTaskType.GetValueOrDefault().GetTaskTypeGroup() == TaskTypeGroup.OutbondGroup)
            {
@@ -116,26 +116,26 @@
                candidateTask = _taskService.QueryStackerCraneInTask(deviceCode);
                // 如果没有入库任务,再查一下出库任务
                candidateTask ??= _taskService.QueryStackerCraneOutTask(deviceCode);
                //QuartzLogHelper.LogDebug(_logger, "SelectTask:出库后优先查入库,设备: {DeviceCode},结果: {TaskNum}", $"出库后优先查入库,设备: {deviceCode},结果: {candidateTask?.TaskNum}", deviceCode, deviceCode, candidateTask?.TaskNum);
                QuartzLogHelper.LogDebug(_logger, $"出库后优先查入库,设备: {deviceCode},结果: {candidateTask?.TaskNum}", commonStackerCrane.DeviceName);
            }
            else
            {
                // 上一任务是入库(非出库),优先查出库任务
                candidateTask = _taskService.QueryStackerCraneOutTask(deviceCode);
                //QuartzLogHelper.LogDebug(_logger, "SelectTask:入库后优先查出库,设备: {DeviceCode},结果: {TaskNum}", $"入库后优先查出库,设备: {deviceCode},结果: {candidateTask?.TaskNum}", deviceCode, deviceCode, candidateTask?.TaskNum);
                QuartzLogHelper.LogDebug(_logger, $"入库后优先查出库,设备: {deviceCode},结果: {candidateTask?.TaskNum}", commonStackerCrane.DeviceName);
            }
            // 如果没有候选任务,返回 null
            if (candidateTask == null)
            {
                //QuartzLogHelper.LogDebug(_logger, "SelectTask:没有候选任务,设备: {DeviceCode}", $"没有候选任务,设备: {deviceCode}", deviceCode, deviceCode);
                QuartzLogHelper.LogDebug(_logger, $"没有候选任务,设备: {deviceCode}", commonStackerCrane.DeviceName);
                return null;
            }
            // 如果不是出库任务,直接返回
            if (candidateTask.TaskType.GetTaskTypeGroup() != TaskTypeGroup.OutbondGroup)
            {
                QuartzLogHelper.LogInfo(_logger, "SelectTask:选中非出库任务,设备: {DeviceCode},任务号: {TaskNum},任务类型: {TaskType}", $"选中非出库任务,任务号: {candidateTask.TaskNum},任务类型: {candidateTask.TaskType}", deviceCode, deviceCode, candidateTask.TaskNum, candidateTask.TaskType);
                QuartzLogHelper.LogDebug(_logger, $"选中非出库任务,任务号: {candidateTask.TaskNum},任务类型: {candidateTask.TaskType}", commonStackerCrane.DeviceName);
                return candidateTask;
            }
@@ -143,10 +143,27 @@
            Dt_Task? selectedTask = TrySelectOutboundTask(candidateTask);
            if (selectedTask != null)
            {
                QuartzLogHelper.LogInfo(_logger, "SelectTask:选中出库任务,设备: {DeviceCode},任务号: {TaskNum}", $"选中出库任务,任务号: {selectedTask.TaskNum}", deviceCode, deviceCode, selectedTask.TaskNum);
                QuartzLogHelper.LogDebug(_logger, $"选中出库任务,任务号: {selectedTask.TaskNum}", commonStackerCrane.DeviceName);
                return selectedTask;
            }
            // ===== TargetAddress 不可用时,先尝试同 NextAddress 的其他任务 =====
            var sameStationTasks = _taskService
                .QueryStackerCraneOutTasks(deviceCode, new List<string> { candidateTask.NextAddress })
                .Where(x => x.TaskId != candidateTask.TaskId)
                .ToList();
            foreach (var sameStationTask in sameStationTasks)
            {
                selectedTask = TrySelectOutboundTask(sameStationTask);
                if (selectedTask != null)
                {
                    QuartzLogHelper.LogDebug(_logger, $"选中同站台备选出库任务,任务号: {selectedTask.TaskNum}", commonStackerCrane.DeviceName);
                    return selectedTask;
                }
            }
            // ===== 同 NextAddress 无可用任务,尝试不同 NextAddress 的任务 =====
            // 查找其他可用的出库站台
            var otherOutStationCodes = _routerService
                .QueryNextRoutes(deviceCode, candidateTask.NextAddress, candidateTask.TaskType)
@@ -160,14 +177,14 @@
                selectedTask = TrySelectOutboundTask(alternativeTask);
                if (selectedTask != null)
                {
                    QuartzLogHelper.LogInfo(_logger, "SelectTask:选中备选出库任务,设备: {DeviceCode},任务号: {TaskNum}", $"选中备选出库任务,任务号: {selectedTask.TaskNum}", deviceCode, deviceCode, selectedTask.TaskNum);
                    QuartzLogHelper.LogDebug(_logger, $"选中备选出库任务,任务号: {selectedTask.TaskNum}", commonStackerCrane.DeviceName);
                    return selectedTask;
                }
            }
            // 没有可用出库任务,尝试返回入库任务
            var inboundTask = _taskService.QueryStackerCraneInTask(deviceCode);
            QuartzLogHelper.LogInfo(_logger, "SelectTask:返回入库任务,设备: {DeviceCode},任务号: {TaskNum}", $"返回入库任务,任务号: {inboundTask?.TaskNum}", deviceCode, deviceCode, inboundTask?.TaskNum);
            QuartzLogHelper.LogDebug(_logger, $"返回入库任务,任务号: {inboundTask?.TaskNum}", commonStackerCrane.DeviceName);
            return inboundTask;
        }
@@ -185,7 +202,21 @@
        /// <returns>可选中的任务,或 null(站台不可用)</returns>
        private Dt_Task? TrySelectOutboundTask(Dt_Task outboundTask)
        {
            // 对于所有出库任务,必须先调用 WMS 判断是否需要移库
            // 先进行本地站台检查(PLC 读取,快速),避免不必要的 WMS HTTP 调用
            // 判断 TargetAddress 输送线站台是否空闲
            if (!IsTargetAddressConveyorStationAvailable(outboundTask))
            {
                return null;
            }
            // 判断 NextAddress 出库站台是否可用
            if (!IsOutTaskStationAvailable(outboundTask))
            {
                return null;
            }
            // 站台检查通过后,调用 WMS 判断是否需要移库
            var taskAfterTransferCheck = _transferCheck(outboundTask.TaskNum) ?? outboundTask;
            var taskGroup = taskAfterTransferCheck.TaskType.GetTaskTypeGroup();
@@ -207,8 +238,7 @@
                return taskAfterTransferCheck;
            }
            // 判断出库站台是否可用
            return IsOutTaskStationAvailable(taskAfterTransferCheck) ? taskAfterTransferCheck : null;
            return taskAfterTransferCheck;
        }
        /// <summary>
@@ -220,18 +250,24 @@
        /// <param name="httpClientHelper">HTTP 客户端帮助类</param>
        /// <param name="taskNum">任务号</param>
        /// <returns>如果需要移库返回移库任务,否则返回 null</returns>
        private static Dt_Task? QueryTransferTask(HttpClientHelper httpClientHelper, int taskNum)
        private static Dt_Task? QueryTransferTask(HttpClientHelper httpClientHelper, int taskNum, ILogger logger)
        {
            // 调用 WMS 的移库检查接口
            string configKey = nameof(ConfigKey.TransferCheck);
            string requestParam = taskNum.ToString();
            var response = httpClientHelper.Post<WebResponseContent>(
                nameof(ConfigKey.TransferCheck),
                taskNum.ToString());
                configKey,
                requestParam);
            // 检查响应是否成功
            if (response == null || !response.IsSuccess || response.Data == null || !response.Data.Status || response.Data.Data == null)
            {
                QuartzLogHelper.LogError(logger, $"调用WMS接口失败,接口:【{configKey}】,请求参数:【{requestParam}】,错误信息:【{(response?.Data?.Message ?? "无响应")}】", "StackerCraneTaskSelector");
                return null;
            }
            QuartzLogHelper.LogInfo(logger, $"调用WMS接口成功,接口:【{configKey}】,响应数据:【{response.Data.Data}】", "StackerCraneTaskSelector");
            // 解析返回的任务数据
            var taskJson = response.Data.Data.ToString();
@@ -314,5 +350,47 @@
            return isOccupied;
        }
        /// <summary>
        /// 判断 TargetAddress 输送线站台是否空闲
        /// </summary>
        /// <param name="task">出库任务</param>
        /// <returns>站台空闲(CV_State == 2)返回 true</returns>
        private bool IsTargetAddressConveyorStationAvailable([NotNull] Dt_Task task)
        {
            // 确定任务类型
            int taskType = task.TaskType == (int)TaskOutboundTypeEnum.OutEmpty
                ? StackerCraneConst.EmptyPalletTaskType
                : task.TaskType;
            // 通过路由查找 TargetAddress 对应的设备信息
            Dt_Router? router = _routerService.QueryNextRoute(task.Roadway, task.TargetAddress, taskType);
            if (router == null)
            {
                QuartzLogHelper.LogWarn(_logger, "IsTargetAddressConveyorStationAvailable:未找到 TargetAddress 路由信息,TargetAddress: {TargetAddress},任务号: {TaskNum}",
                    $"IsTargetAddressConveyorStationAvailable:未找到 TargetAddress 路由信息,TargetAddress: {task.TargetAddress}", task.Roadway, task.TargetAddress, task.TaskNum);
                return false;
            }
            // 查找输送线设备
            IDevice? device = Storage.Devices.FirstOrDefault(x => x.DeviceCode == router.ChildPosiDeviceCode);
            if (device == null)
            {
                QuartzLogHelper.LogWarn(_logger, "IsTargetAddressConveyorStationAvailable:未找到输送线设备,ChildPosiDeviceCode: {ChildPosiDeviceCode},任务号: {TaskNum}",
                    $"IsTargetAddressConveyorStationAvailable:未找到输送线设备,ChildPosiDeviceCode: {router.ChildPosiDeviceCode}", task.Roadway, router.ChildPosiDeviceCode, task.TaskNum);
                return false;
            }
            // 转换为输送线设备
            CommonConveyorLine conveyorLine = (CommonConveyorLine)device;
            // 读取 CV_State,CV_State == 2 表示空闲
            byte cvState = conveyorLine.GetValue<ConveyorLineDBNameNew, byte>(ConveyorLineDBNameNew.CV_State, task.TargetAddress);
            bool isAvailable = cvState == 2;
            QuartzLogHelper.LogInfo(_logger, "IsTargetAddressConveyorStationAvailable:TargetAddress: {TargetAddress},CV_State: {CV_State},是否空闲: {IsAvailable},任务号: {TaskNum}",
                $"IsTargetAddressConveyorStationAvailable:TargetAddress: {task.TargetAddress},CV_State: {cvState},是否空闲: {isAvailable}", task.Roadway, task.TargetAddress, cvState, isAvailable, task.TaskNum);
            return isAvailable;
        }
    }
}