wanshenmean
2026-03-27 bf2aa9dd56432a74940ca1bb08fb4d7eaee37045
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
using Microsoft.Extensions.Logging;
using Quartz;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.StackerCrane;
using WIDESEAWCS_Tasks.StackerCraneJob;
using WIDESEA_Core;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_QuartzJob.Service;
 
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 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;
                }
 
                // ========== 选择任务 ==========
                // 任务选择下沉到专用选择器
                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)
                {
                    // 发送成功,更新状态
                    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;
        }
 
        /// <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)
            {
                // 记录日志
                _logger.LogInformation("CommonStackerCrane_StackerCraneTaskCompletedEventHandler:任务完成,任务号: {TaskNum}", e.TaskNum);
                QuartzLogger.Info($"任务完成,任务号: {e.TaskNum}", stackerCrane.DeviceCode);
 
                // 更新任务状态为完成
                _taskService.StackCraneTaskCompleted(e.TaskNum);
 
                // 清除堆垛机的作业指令(设置为 2,表示空闲)
                stackerCrane.SetValue(StackerCraneDBName.WorkAction, 2);
            }
        }
 
        /// <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
            };
        }
    }
}