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
using MapsterMapper;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Quartz;
using SqlSugar;
using System.Text.Json;
using WIDESEA_Core;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.Service;
 
namespace WIDESEAWCS_Tasks
{
    /// <summary>
    /// 输送线任务作业(Quartz Job)- 核心执行逻辑
    /// </summary>
    /// <remarks>
    /// Quartz 定时任务,负责处理输送线的任务调度。
    /// 使用 [DisallowConcurrentExecution] 禁止并发执行,确保同一设备的任务串行处理。
    ///
    /// 核心职责:
    /// 1. 获取输送线的所有子设备位置
    /// 2. 并行处理每个子设备的消息
    /// 3. 根据任务状态调用相应的调度方法
    /// 4. 处理入库和出库两大类任务
    ///
    /// 该 Job 通过 Parallel.For 并行处理多个子设备,提高处理效率。
    /// </remarks>
    [DisallowConcurrentExecution]
    public class CommonConveyorLineNewJob : IJob
    {
        /// <summary>
        /// 任务服务
        /// </summary>
        private readonly ITaskService _taskService;
 
        /// <summary>
        /// 任务执行明细服务
        /// </summary>
        private readonly ITaskExecuteDetailService _taskExecuteDetailService;
 
        /// <summary>
        /// 路由服务
        /// </summary>
        private readonly IRouterService _routerService;
 
        /// <summary>
        /// 对象映射器
        /// </summary>
        private readonly IMapper _mapper;
 
        /// <summary>
        /// 输送线调度处理器
        /// </summary>
        /// <remarks>
        /// 封装了输送线业务逻辑的处理方法。
        /// </remarks>
        private ConveyorLineDispatchHandler _conveyorLineDispatch;
 
        /// <summary>
        /// HTTP 客户端帮助类
        /// </summary>
        /// <remarks>
        /// 用于调用 WMS 系统的 HTTP 接口。
        /// </remarks>
        private readonly HttpClientHelper _httpClientHelper;
 
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger<CommonConveyorLineNewJob> _logger;
 
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="taskService">任务服务</param>
        /// <param name="taskExecuteDetailService">任务执行明细服务</param>
        /// <param name="routerService">路由服务</param>
        /// <param name="mapper">对象映射器</param>
        /// <param name="httpClientHelper">HTTP 客户端帮助类</param>
        /// <param name="logger">日志记录器</param>
        public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, HttpClientHelper httpClientHelper, ILogger<CommonConveyorLineNewJob> logger)
        {
            _taskService = taskService;
            _taskExecuteDetailService = taskExecuteDetailService;
            _routerService = routerService;
            _mapper = mapper;
            _httpClientHelper = httpClientHelper;
            _logger = logger;
 
            // 初始化调度处理器
            _conveyorLineDispatch = new ConveyorLineDispatchHandler(_taskService, _taskExecuteDetailService, _routerService, _mapper, _logger);
        }
 
        /// <summary>
        /// Quartz Job 的执行入口
        /// </summary>
        /// <remarks>
        /// 执行流程:
        /// 1. 从 JobDataMap 获取输送线设备信息
        /// 2. 获取该输送线的所有子设备位置列表
        /// 3. 并行处理每个子设备的消息
        /// 4. 检查托盘位置(特定配置的位置)
        /// 5. 根据任务状态分发到相应的处理方法
        ///
        /// 并行处理提高了对多站台输送线的处理效率。
        /// </remarks>
        /// <param name="context">Quartz 作业执行上下文</param>
        public Task Execute(IJobExecutionContext context)
        {
            try
            {
                // 从 JobDataMap 获取输送线设备参数
                CommonConveyorLine conveyorLine = (CommonConveyorLine)context.JobDetail.JobDataMap.Get("JobParams");
                if (conveyorLine != null)
                {
                    // 获取该输送线下的所有子设备位置编码
                    List<string> childDeviceCodes = _routerService.QueryAllPositions(conveyorLine.DeviceCode);
                    if (childDeviceCodes == null || childDeviceCodes.Count == 0)
                    {
                        // 没有子设备,直接返回
                        _logger.LogInformation("输送线 {DeviceCode} 没有子设备", conveyorLine.DeviceCode);
                        QuartzLogger.Info($"输送线 {conveyorLine.DeviceCode} 没有子设备", conveyorLine.DeviceCode);
                        return Task.CompletedTask;
                    }
 
                    // 创建并行选项,限制最大并发数
                    var parallelOptions = new ParallelOptions
                    {
                        // 限制并发数:子设备数量和 CPU 核心数*2 的较小值
                        MaxDegreeOfParallelism = Math.Min(childDeviceCodes.Count, Environment.ProcessorCount * 2),
                    };
 
                    _logger.LogDebug("Execute:开始并行处理输送线 {DeviceCode},子设备数量: {Count}", conveyorLine.DeviceCode, childDeviceCodes.Count);
                    QuartzLogger.Debug($"开始并行处理输送线,子设备数量: {childDeviceCodes.Count}", conveyorLine.DeviceCode);
 
                    // 并行处理每个子设备
                    Parallel.For(0, childDeviceCodes.Count, parallelOptions, i =>
                    {
                        string childDeviceCode = childDeviceCodes[i];
                        var correlationId = Guid.NewGuid().ToString("N");
                        try
                        {
                            // 读取该位置的 PLC 命令数据
                            ConveyorLineTaskCommandNew command = conveyorLine.ReadCustomer<ConveyorLineTaskCommandNew>(childDeviceCode);
 
                            // 如果命令为空,跳过
                            if (command == null)
                            {
                                _logger.LogDebug("Execute:子设备 {ChildDeviceCode} 命令为空,跳过", childDeviceCode);
                                QuartzLogger.Debug($"子设备 {childDeviceCode} 命令为空,跳过", conveyorLine.DeviceCode);
                                return;
                            }
 
                            // 如果 WCS_ACK 为 1,先清除(表示处理过上一次请求)
                            if (command.WCS_ACK == 1)
                                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, 0, childDeviceCode);
 
                            // ========== 检查特定位置是否有托盘 ==========
                            // 从配置中读取需要检查托盘的位置列表
                            var checkPalletPositions = App.Configuration.GetSection("CheckPalletPositions")
                                .Get<List<CheckPalletPosition>>() ?? new List<CheckPalletPosition>();
 
                            // 如果当前设备在检查列表中
                            if (checkPalletPositions.Any(x => x.Code == childDeviceCode))
                            {
                                // 检查输送线状态(是否有托盘)
                                if (command.CV_State.ObjToBool())
                                {
                                    // 检查该位置是否已有任务
                                    var existingTask = _taskService.Repository.QueryFirst(x => x.TargetAddress == childDeviceCode);
                                    if (existingTask.IsNullOrEmpty())
                                    {
                                        // 没有任务,向 WMS 请求出库托盘任务
                                        var position = checkPalletPositions.FirstOrDefault(x => x.Code == childDeviceCode);
                                        _logger.LogInformation("Execute:检查托盘位置 {ChildDeviceCode},请求WMS出库托盘任务", childDeviceCode);
                                        QuartzLogger.Info($"检查托盘位置 {childDeviceCode},请求WMS出库托盘任务", conveyorLine.DeviceCode);
 
                                        var responseResult = _httpClientHelper.Post<WebResponseContent>("GetOutBoundTrayTaskAsync", new CreateTaskDto()
                                        {
                                            WarehouseId = position.WarehouseId,
                                            TargetAddress = childDeviceCode
                                        }.Serialize());
 
                                        // 如果请求成功,接收 WMS 返回的任务
                                        if (responseResult.IsSuccess && responseResult.Data.Status)
                                        {
                                            var wmsTask = JsonSerializer.Deserialize<List<WMSTaskDTO>>(responseResult.Data.Data.Serialize());
                                            if (wmsTask != null)
                                                _taskService.ReceiveWMSTask(wmsTask);
                                        }
                                    }
                                }
                            }
 
                            // ========== 检查 PLC_STB 标志 ==========
                            // 只有当 PLC_STB 为 1 时才处理任务
                            if (command.PLC_STB != 1)
                            {
                                _logger.LogDebug("Execute:子设备 {ChildDeviceCode} PLC_STB 不为1,跳过", childDeviceCode);
                                QuartzLogger.Debug($"子设备 {childDeviceCode} PLC_STB 不为1,跳过", conveyorLine.DeviceCode);
                                return;
                            }
 
                            // ========== 处理无托盘条码的情况 ==========
                            // 无托盘条码时,请求出库任务
                            if (command.Barcode.IsNullOrEmpty() || command.Barcode.Replace("\0", "") == "")
                            {
                                _logger.LogDebug("Execute:子设备 {ChildDeviceCode} 无托盘条码,请求出库任务", childDeviceCode);
                                QuartzLogger.Debug($"子设备 {childDeviceCode} 无托盘条码,请求出库任务", conveyorLine.DeviceCode);
                                _conveyorLineDispatch.RequestOutbound(conveyorLine, command, childDeviceCode);
                                return;
                            }
 
                            // ========== 处理已有任务号的情况 ==========
                            if (command.TaskNo > 0)
                            {
                                // 查询正在执行的任务
                                Dt_Task task = _taskService.QueryExecutingConveyorLineTask(command.TaskNo, childDeviceCode);
                                if (!task.IsNullOrEmpty())
                                {
                                    _logger.LogInformation("Execute:子设备 {ChildDeviceCode} 处理任务 {TaskNum},状态: {Status}", childDeviceCode, task.TaskNum, task.TaskStatus);
                                    QuartzLogger.Info($"处理任务 {task.TaskNum},状态: {task.TaskStatus}", conveyorLine.DeviceCode);
                                    // 处理任务状态(根据状态分发到不同方法)
                                    ProcessTaskState(conveyorLine, command, task, childDeviceCode);
                                    return;
                                }
                            }
                        }
                        catch (Exception innerEx)
                        {
                            // 记录异常,但不影响其他子设备的处理
                            _logger.LogError(innerEx, "Execute:子设备 {ChildDeviceCode} 处理异常,CorrelationId: {CorrelationId}", childDeviceCode, correlationId);
                            QuartzLogger.Error($"子设备处理异常: {innerEx.Message}", conveyorLine.DeviceCode, innerEx);
                        }
                    });
                }
            }
            catch (Exception ex)
            {
                // 记录整体异常
                _logger.LogError(ex, "Execute:输送线 {DeviceCode} 执行异常", ex.Message);
                QuartzLogger.Error($"输送线执行异常: {ex.Message}", "CommonConveyorLineNewJob", ex);
            }
            return Task.CompletedTask;
        }
 
        /// <summary>
        /// 处理任务状态
        /// </summary>
        /// <remarks>
        /// 根据任务的当前状态,调用相应的调度方法:
        /// - InExecuting: 入库执行中 -> 调用 RequestInNextAddress
        /// - OutExecuting: 出库执行中 -> 根据是否到达目标地址调用对应方法
        /// - InFinish: 入库完成 -> 调用 ConveyorLineInFinish
        /// - OutFinish: 出库完成 -> 调用 ConveyorLineOutFinish
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="task">任务对象</param>
        /// <param name="childDeviceCode">子设备编码</param>
        private void ProcessTaskState(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, Dt_Task task, string childDeviceCode)
        {
            // 定义任务状态常量
            const int InExecuting = (int)TaskInStatusEnum.Line_InExecuting;     // 入库执行中
            const int OutExecuting = (int)TaskOutStatusEnum.Line_OutExecuting; // 出库执行中
            const int InFinish = (int)TaskInStatusEnum.InFinish;                 // 入库完成
            const int OutFinish = (int)TaskOutStatusEnum.OutFinish;             // 出库完成
 
            // 获取当前任务状态
            int state = task.TaskStatus;
 
            // 判断当前子设备是否为目标地址
            bool isTargetAddress = task.TargetAddress == childDeviceCode;
 
            // 根据状态分发处理
            switch (state)
            {
                case InExecuting:
                    // 入库执行中,调用下一地址处理
                    _conveyorLineDispatch.RequestInNextAddress(conveyorLine, command, childDeviceCode);
                    break;
 
                case OutExecuting:
                    // 出库执行中
                    if (isTargetAddress)
                        // 到达目标地址,调用出库完成
                        _conveyorLineDispatch.ConveyorLineOutFinish(conveyorLine, command, childDeviceCode);
                    else
                        // 未到达目标地址,调用出库下一地址处理
                        _conveyorLineDispatch.RequestOutNextAddress(conveyorLine, command, childDeviceCode);
                    break;
 
                case InFinish:
                    // 入库完成
                    _conveyorLineDispatch.ConveyorLineInFinish(conveyorLine, command, childDeviceCode);
                    break;
 
                case OutFinish:
                    // 出库完成
                    _conveyorLineDispatch.ConveyorLineOutFinish(conveyorLine, command, childDeviceCode);
                    break;
            }
        }
    }
}