using Microsoft.Extensions.Logging; using Quartz; using WIDESEA_Core; using WIDESEAWCS_Core; using WIDESEAWCS_Core.LogHelper; using WIDESEAWCS_ITaskInfoRepository; using WIDESEAWCS_ITaskInfoService; using WIDESEAWCS_Model.Models; using WIDESEAWCS_QuartzJob; using WIDESEAWCS_QuartzJob.Service; using WIDESEAWCS_QuartzJob.StackerCrane; using WIDESEAWCS_QuartzJob.StackerCrane.Enum; using WIDESEAWCS_Common.Constants; using WIDESEAWCS_Tasks.StackerCraneJob; namespace WIDESEAWCS_Tasks { /// /// 堆垛机任务作业(Quartz Job)- 核心调度逻辑 /// /// /// Quartz 定时任务,负责堆垛机的任务调度。 /// 使用 [DisallowConcurrentExecution] 禁止并发执行,确保同一堆垛机的任务串行处理。 /// /// 核心职责: /// 1. 从配置文件加载命令类型映射 /// 2. 检查堆垛机任务完成状态 /// 3. 选择合适的任务(委托给 StackerCraneTaskSelector) /// 4. 构建命令对象(委托给 StackerCraneCommandBuilder) /// 5. 发送命令到堆垛机 /// 6. 处理任务完成事件 /// /// 架构设计: /// - StackerCraneTaskSelector:负责选择合适的任务 /// - StackerCraneCommandBuilder:负责将任务转换为命令对象 /// - CommonStackerCraneJob:负责调度流程控制 /// [DisallowConcurrentExecution] public class CommonStackerCraneJob : IJob { /// /// 任务服务 /// private readonly ITaskService _taskService; /// /// 任务执行明细服务 /// private readonly ITaskExecuteDetailService _taskExecuteDetailService; /// /// 任务仓储 /// private readonly ITaskRepository _taskRepository; /// /// 堆垛机命令配置 /// /// /// 包含巷道与命令类型的映射关系。 /// 从 JSON 文件加载。 /// private readonly StackerCraneCommandConfig _config; /// /// 堆垛机任务选择器 /// /// /// 负责选择合适的任务进行执行。 /// private readonly StackerCraneTaskSelector _taskSelector; /// /// 堆垛机命令构建器 /// /// /// 负责将任务转换为堆垛机可执行的命令对象。 /// private readonly StackerCraneCommandBuilder _commandBuilder; /// /// 日志记录器 /// private readonly ILogger _logger; /// /// 堆垛机设备编码 /// private string _deviceCode = string.Empty; /// /// 构造函数 /// /// 任务服务 /// 任务执行明细服务 /// 任务仓储 /// 路由服务 /// HTTP 客户端帮助类 /// 日志记录器 public CommonStackerCraneJob( ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, ITaskRepository taskRepository, IRouterService routerService, HttpClientHelper httpClientHelper, ILogger logger) { _taskService = taskService; _taskExecuteDetailService = taskExecuteDetailService; _taskRepository = taskRepository; _logger = logger; // 加载配置文件 _config = LoadConfig(); // 初始化任务选择器 _taskSelector = new StackerCraneTaskSelector(taskService, routerService, httpClientHelper, _logger); // 初始化命令构建器 _commandBuilder = new StackerCraneCommandBuilder(taskService, routerService, _config, _logger); } /// /// 加载配置文件(优先级:配置文件 > 默认配置) /// /// /// 从应用程序目录下的 StackerCraneJob/stackercrane-command-config.json 读取配置。 /// 如果文件不存在或解析失败,使用默认配置。 /// /// 堆垛机命令配置 private static StackerCraneCommandConfig LoadConfig() { try { // 构造配置文件路径 string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "StackerCraneJob", "stackercrane-command-config.json"); if (File.Exists(configPath)) { // 读取并解析 JSON 配置 string json = File.ReadAllText(configPath); return System.Text.Json.JsonSerializer.Deserialize(json) ?? new StackerCraneCommandConfig(); } } catch (Exception ex) { // 配置加载失败,使用默认配置 Console.WriteLine($"配置加载失败: {ex.Message},使用默认配置"); } return new StackerCraneCommandConfig(); } /// /// Quartz Job 的执行入口 /// /// /// 执行流程: /// 1. 从 JobDataMap 获取堆垛机设备信息 /// 2. 订阅任务完成事件(仅第一次执行时) /// 3. 检查堆垛机任务完成状态 /// 4. 检查是否可以发送任务 /// 5. 选择任务 /// 6. 构建命令 /// 7. 发送命令 /// 8. 更新任务状态和堆垛机状态 /// /// Quartz 作业执行上下文 public Task Execute(IJobExecutionContext context) { try { // 从 JobDataMap 获取堆垛机设备参数 bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value); if (!flag || value is not CommonStackerCrane commonStackerCrane) { // 参数无效,直接返回 QuartzLogHelper.LogWarn(_logger, "Execute:参数无效", "Execute:参数无效", "CommonStackerCraneJob"); return Task.CompletedTask; } _deviceCode = commonStackerCrane.DeviceCode; // ========== 订阅任务完成事件(全局只订阅一次) ========== if (!commonStackerCrane.IsEventSubscribed) { // 绑定任务完成事件处理方法 commonStackerCrane.StackerCraneTaskCompletedEventHandler += CommonStackerCrane_StackerCraneTaskCompletedEventHandler; QuartzLogHelper.LogInfo(_logger, "Execute:订阅任务完成事件,设备: {DeviceCode}", "订阅任务完成事件", _deviceCode, _deviceCode); } // ========== 检查堆垛机任务完成状态 ========== commonStackerCrane.CheckStackerCraneTaskCompleted(); //_logger.LogDebug("Execute:检查任务完成状态,设备: {DeviceCode}", _deviceCode); //QuartzLogger.Debug($"检查任务完成状态,设备: {_deviceCode}", _deviceCode); // ========== 检查是否可以发送新任务 ========== //if (!commonStackerCrane.IsCanSendTask(commonStackerCrane.Communicator, commonStackerCrane.DeviceProDTOs, commonStackerCrane.DeviceProtocolDetailDTOs)) if (commonStackerCrane.StackerCraneStatusValue != StackerCraneStatus.Normal /*&& commonStackerCrane.StackerCraneAutoStatusValue != StackerCraneAutoStatus.Automatic && commonStackerCrane.StackerCraneWorkStatusValue != StackerCraneWorkStatus.Standby*/) { // 堆垛机不可用(如正在执行上一任务),直接返回 //_logger.LogDebug("Execute:堆垛机不可用,设备: {DeviceCode}", _deviceCode); //QuartzLogger.Debug($"堆垛机不可用,设备: {_deviceCode}", _deviceCode); return Task.CompletedTask; } // ========== 选择任务 ========== // 任务选择下沉到专用选择器 Dt_Task? task = _taskSelector.SelectTask(commonStackerCrane); 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); // ========== 构建命令 ========== // 命令构建下沉到专用构建器 object? stackerCraneTaskCommand = _commandBuilder.ConvertToStackerCraneTaskCommand(task); if (stackerCraneTaskCommand == null) { // 命令构建失败 QuartzLogHelper.LogWarn(_logger, "Execute:命令构建失败,设备: {DeviceCode},任务号: {TaskNum}", $"命令构建失败,任务号: {task.TaskNum}", _deviceCode, _deviceCode, task.TaskNum); return Task.CompletedTask; } // ========== 发送命令 ========== bool sendFlag = SendStackerCraneCommand(commonStackerCrane, stackerCraneTaskCommand); if (sendFlag) { Task.Delay(1000).Wait(); commonStackerCrane.SetValue(StackerCraneDBName.WorkAction, (short)StackerCraneWorkActionEnum.StartTask); // 发送成功,更新状态 commonStackerCrane.LastTaskType = task.TaskType; _taskService.UpdateTaskStatusToNext(task.TaskNum); QuartzLogHelper.LogInfo(_logger, "Execute:命令发送成功,设备: {DeviceCode},任务号: {TaskNum}", $"命令发送成功,任务号: {task.TaskNum}", _deviceCode, _deviceCode, task.TaskNum); } else { QuartzLogHelper.LogError(_logger, "Execute:命令发送失败,设备: {DeviceCode},任务号: {TaskNum}", $"命令发送失败", _deviceCode, _deviceCode, task.TaskNum); } } catch (Exception ex) { // 记录异常 QuartzLogHelper.LogError(_logger, ex, "Execute:执行异常,设备: {DeviceCode}", $"执行异常: {ex.Message}", _deviceCode, _deviceCode); } return Task.CompletedTask; } /// /// 堆垛机任务完成事件处理 /// /// /// 当堆垛机报告任务完成时调用此方法。 /// 处理: /// 1. 记录日志 /// 2. 更新任务状态为完成 /// 3. 清除堆垛机的作业指令 /// /// 事件发送者(堆垛机设备) /// 事件参数,包含完成的任务号 private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, StackerCraneTaskCompletedEventArgs e) { IStackerCrane? stackerCrane = sender as IStackerCrane; if (stackerCrane != null) { // 记录日志 QuartzLogHelper.LogInfo(_logger, "CommonStackerCrane_StackerCraneTaskCompletedEventHandler:任务完成,任务号: {TaskNum}", $"任务完成,任务号: {e.TaskNum}", stackerCrane.DeviceCode, e.TaskNum); // 更新任务状态为完成 if (_taskService.StackCraneTaskCompleted(e.TaskNum).Status) { // 清除堆垛机的作业指令(设置为空闲) stackerCrane.SetValue(StackerCraneDBName.WorkAction, (short)StackerCraneWorkActionEnum.TaskComplete); } } } /// /// 发送堆垛机命令 /// /// /// 根据命令类型调用相应的发送方法。 /// 支持标准命令和成型命令两种格式。 /// /// 堆垛机设备对象 /// 命令对象 /// 发送是否成功 private static bool SendStackerCraneCommand(IStackerCrane commonStackerCrane, object command) { return command switch { // 成型命令(如 HC 巷道) FormationStackerCraneTaskCommand formationCommand => commonStackerCrane.SendCommand(formationCommand), // 标准命令(如 GW、CW 巷道) StackerCraneTaskCommand standardCommand => commonStackerCrane.SendCommand(standardCommand), // 未知命令类型 _ => false }; } } }