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
|
{
|
/// <summary>
|
/// 堆垛机任务作业(Quartz Job)- 核心调度逻辑
|
/// </summary>
|
/// <remarks>
|
/// Quartz 定时任务,负责堆垛机的任务调度。
|
/// 使用 [DisallowConcurrentExecution] 禁止并发执行,确保同一堆垛机的任务串行处理。
|
///
|
/// 核心职责:
|
/// 1. 从配置文件加载命令类型映射
|
/// 2. 检查堆垛机任务完成状态
|
/// 3. 选择合适的任务(委托给 StackerCraneTaskSelector)
|
/// 4. 构建命令对象(委托给 StackerCraneCommandBuilder)
|
/// 5. 发送命令到堆垛机
|
/// 6. 处理任务完成事件
|
///
|
/// 架构设计:
|
/// - StackerCraneTaskSelector:负责选择合适的任务
|
/// - StackerCraneCommandBuilder:负责将任务转换为命令对象
|
/// - CommonStackerCraneJob:负责调度流程控制
|
/// </remarks>
|
[DisallowConcurrentExecution]
|
public class CommonStackerCraneJob : IJob
|
{
|
/// <summary>
|
/// 任务服务
|
/// </summary>
|
private readonly ITaskService _taskService;
|
|
/// <summary>
|
/// 任务执行明细服务
|
/// </summary>
|
private readonly ITaskExecuteDetailService _taskExecuteDetailService;
|
|
/// <summary>
|
/// 任务仓储
|
/// </summary>
|
private readonly ITaskRepository _taskRepository;
|
|
/// <summary>
|
/// 堆垛机命令配置
|
/// </summary>
|
/// <remarks>
|
/// 包含巷道与命令类型的映射关系。
|
/// 从 JSON 文件加载。
|
/// </remarks>
|
private readonly StackerCraneCommandConfig _config;
|
|
/// <summary>
|
/// 堆垛机任务选择器
|
/// </summary>
|
/// <remarks>
|
/// 负责选择合适的任务进行执行。
|
/// </remarks>
|
private readonly StackerCraneTaskSelector _taskSelector;
|
|
/// <summary>
|
/// 堆垛机命令构建器
|
/// </summary>
|
/// <remarks>
|
/// 负责将任务转换为堆垛机可执行的命令对象。
|
/// </remarks>
|
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="taskExecuteDetailService">任务执行明细服务</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,
|
ILogger<CommonStackerCraneJob> 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);
|
}
|
|
/// <summary>
|
/// 加载配置文件(优先级:配置文件 > 默认配置)
|
/// </summary>
|
/// <remarks>
|
/// 从应用程序目录下的 StackerCraneJob/stackercrane-command-config.json 读取配置。
|
/// 如果文件不存在或解析失败,使用默认配置。
|
/// </remarks>
|
/// <returns>堆垛机命令配置</returns>
|
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<StackerCraneCommandConfig>(json) ?? new StackerCraneCommandConfig();
|
}
|
}
|
catch (Exception ex)
|
{
|
// 配置加载失败,使用默认配置
|
Console.WriteLine($"配置加载失败: {ex.Message},使用默认配置");
|
}
|
|
return new StackerCraneCommandConfig();
|
}
|
|
/// <summary>
|
/// Quartz Job 的执行入口
|
/// </summary>
|
/// <remarks>
|
/// 执行流程:
|
/// 1. 从 JobDataMap 获取堆垛机设备信息
|
/// 2. 订阅任务完成事件(仅第一次执行时)
|
/// 3. 检查堆垛机任务完成状态
|
/// 4. 检查是否可以发送任务
|
/// 5. 选择任务
|
/// 6. 构建命令
|
/// 7. 发送命令
|
/// 8. 更新任务状态和堆垛机状态
|
/// </remarks>
|
/// <param name="context">Quartz 作业执行上下文</param>
|
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;
|
}
|
|
/// <summary>
|
/// 堆垛机任务完成事件处理
|
/// </summary>
|
/// <remarks>
|
/// 当堆垛机报告任务完成时调用此方法。
|
/// 处理:
|
/// 1. 记录日志
|
/// 2. 更新任务状态为完成
|
/// 3. 清除堆垛机的作业指令
|
/// </remarks>
|
/// <param name="sender">事件发送者(堆垛机设备)</param>
|
/// <param name="e">事件参数,包含完成的任务号</param>
|
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);
|
}
|
}
|
}
|
|
/// <summary>
|
/// 发送堆垛机命令
|
/// </summary>
|
/// <remarks>
|
/// 根据命令类型调用相应的发送方法。
|
/// 支持标准命令和成型命令两种格式。
|
/// </remarks>
|
/// <param name="commonStackerCrane">堆垛机设备对象</param>
|
/// <param name="command">命令对象</param>
|
/// <returns>发送是否成功</returns>
|
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
|
};
|
}
|
}
|
}
|