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_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)
{
// 参数无效,直接返回
_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))
if (commonStackerCrane.StackerCraneStatusValue != StackerCraneStatus.Normal)
{
// 堆垛机不可用(如正在执行上一任务),直接返回
_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)
{
// 命令构建失败
_logger.LogWarning("Execute:命令构建失败,设备: {DeviceCode},任务号: {TaskNum}", _deviceCode, task.TaskNum);
QuartzLogger.Warn($"命令构建失败,任务号: {task.TaskNum}", _deviceCode);
return Task.CompletedTask;
}
// ========== 发送命令 ==========
bool sendFlag = SendStackerCraneCommand(commonStackerCrane, stackerCraneTaskCommand);
if (sendFlag)
{
Task.Delay(1000).Wait();
commonStackerCrane.SetValue(StackerCraneDBName.WorkAction, (short)1);
// 发送成功,更新状态
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)
{
// 记录异常
_logger.LogError(ex, "Execute:执行异常,设备: {DeviceCode}", _deviceCode);
QuartzLogger.Error($"执行异常: {ex.Message}", _deviceCode, ex);
}
return Task.CompletedTask;
}
///
/// 堆垛机任务完成事件处理
///
///
/// 当堆垛机报告任务完成时调用此方法。
/// 处理:
/// 1. 记录日志
/// 2. 更新任务状态为完成
/// 3. 清除堆垛机的作业指令
///
/// 事件发送者(堆垛机设备)
/// 事件参数,包含完成的任务号
private void CommonStackerCrane_StackerCraneTaskCompletedEventHandler(object? sender, StackerCraneTaskCompletedEventArgs e)
{
IStackerCrane? stackerCrane = sender as IStackerCrane;
if (stackerCrane != null)
{
// 记录日志
_logger.LogInformation("CommonStackerCrane_StackerCraneTaskCompletedEventHandler:任务完成,任务号: {TaskNum}", e.TaskNum);
QuartzLogger.Info($"任务完成,任务号: {e.TaskNum}", stackerCrane.DeviceCode);
// 更新任务状态为完成
if (_taskService.StackCraneTaskCompleted(e.TaskNum).Status)
{
// 清除堆垛机的作业指令(设置为 2,表示空闲)
stackerCrane.SetValue(StackerCraneDBName.WorkAction, 2);
}
}
}
///
/// 发送堆垛机命令
///
///
/// 根据命令类型调用相应的发送方法。
/// 支持标准命令和成型命令两种格式。
///
/// 堆垛机设备对象
/// 命令对象
/// 发送是否成功
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
};
}
}
}