wanshenmean
12 小时以前 c5c3e4e538a11f2a6391e6b2613375b1f92eb20a
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
314
315
316
317
318
319
using MapsterMapper;
using Microsoft.Extensions.Logging;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.Service;
 
namespace WIDESEAWCS_Tasks
{
    /// <summary>
    /// 输送线调度处理器 - 处理输送线的各种业务请求
    /// </summary>
    /// <remarks>
    /// 核心职责:
    /// 1. 处理输送线的心跳(保持连接)
    /// 2. 处理入库请求(PLC 请求入库任务)
    /// 3. 处理入库下一地址(任务执行中的地址更新)
    /// 4. 处理入库完成
    /// 5. 处理出库请求
    /// 6. 处理出库下一地址
    /// 7. 处理出库完成
    ///
    /// 该类是输送线业务逻辑的核心,根据 PLC 的请求类型调用相应的处理方法。
    /// </remarks>
    public class ConveyorLineDispatchHandler
    {
        /// <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>
        private readonly ILogger _logger;
 
        /// <summary>
        /// 输送线任务过滤器
        /// </summary>
        /// <remarks>
        /// 用于查询待处理和执行中的任务。
        /// </remarks>
        private readonly ConveyorLineTaskFilter _taskFilter;
 
        /// <summary>
        /// 目标地址选择器
        /// </summary>
        /// <remarks>
        /// 用于处理拘束机/插拔钉机等设备的上下层请求。
        /// </remarks>
        private readonly ConveyorLineTargetAddressSelector _targetAddressSelector;
 
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="taskService">任务服务</param>
        /// <param name="taskExecuteDetailService">任务执行明细服务</param>
        /// <param name="routerService">路由服务</param>
        /// <param name="mapper">对象映射器</param>
        /// <param name="logger">日志记录器</param>
        public ConveyorLineDispatchHandler(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, ILogger logger)
        {
            _taskService = taskService;
            _taskExecuteDetailService = taskExecuteDetailService;
            _routerService = routerService;
            _mapper = mapper;
            _logger = logger;
 
            // 初始化任务过滤器和目标地址选择器
            _taskFilter = new ConveyorLineTaskFilter(taskService, _logger);
            _targetAddressSelector = new ConveyorLineTargetAddressSelector(_logger);
        }
 
        /// <summary>
        /// 处理输送线心跳
        /// </summary>
        /// <remarks>
        /// 当收到 PLC 的心跳信号时调用。
        /// 清除任务号,表示当前没有执行任务。
        /// 这是为了保持与 PLC 的连接活跃。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void HeartBeat(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            // 清除任务号,表示当前空闲
            conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, 0, childDeviceCode);
            QuartzLogHelper.LogDebug(_logger, "HeartBeat:子设备 {ChildDeviceCode} 心跳", $"HeartBeat:子设备 {childDeviceCode} 心跳", conveyorLine.DeviceCode, childDeviceCode);
        }
 
        /// <summary>
        /// 处理输送线入库请求
        /// </summary>
        /// <remarks>
        /// 当 PLC 请求入库任务时调用。
        /// 流程:
        /// 1. 向 WMS 请求新任务
        /// 2. 查询待处理任务
        /// 3. 下发任务到 PLC
        /// 4. 更新任务状态
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void RequestInbound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            QuartzLogHelper.LogInfo(_logger, "RequestInbound:子设备 {ChildDeviceCode} 请求入库", $"请求入库,子设备: {childDeviceCode}", conveyorLine.DeviceCode, childDeviceCode);
 
            // 向 WMS 请求新任务(基于条码)
            if (_taskFilter.RequestWmsTask(command.Barcode, childDeviceCode))
            {
                // WMS 返回成功,查询待处理任务
                Dt_Task? task = _taskFilter.QueryPendingTask(conveyorLine.DeviceCode, childDeviceCode);
                if (task != null)
                {
                    // 将任务映射为 PLC 命令
                    ConveyorLineTaskCommandNew taskCommand = _mapper.Map<ConveyorLineTaskCommandNew>(task);
 
                    // 继承 WCS_ACK 标志
                    taskCommand.WCS_ACK = command.WCS_ACK;
 
                    // 发送命令到 PLC
                    conveyorLine.SendCommand(taskCommand, childDeviceCode);
 
                    // 更新任务状态到下一阶段
                    _taskService.UpdateTaskStatusToNext(task);
 
                    QuartzLogHelper.LogInfo(_logger, "RequestInbound:入库任务已下发,任务号: {TaskNum},子设备: {ChildDeviceCode}", $"入库任务已下发,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode, task.TaskNum, childDeviceCode);
                }
            }
        }
 
        /// <summary>
        /// 处理输送线入库下一地址请求
        /// </summary>
        /// <remarks>
        /// 当入库任务执行到某个中间站点时调用。
        /// 根据下一地址判断是否需要与拘束机/插拔钉机等设备交互。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void RequestInNextAddress(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            // 查询正在执行的任务
            Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
            if (task == null)
            {
                QuartzLogHelper.LogDebug(_logger, "RequestInNextAddress:任务 {TaskNo} 不存在", $"RequestInNextAddress:任务 {command.TaskNo} 不存在", conveyorLine.DeviceCode, command.TaskNo);
                return;
            }
 
            QuartzLogHelper.LogInfo(_logger, "RequestInNextAddress:入库下一地址,任务号: {TaskNum},子设备: {ChildDeviceCode}", $"RequestInNextAddress:入库下一地址,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode, task.TaskNum, childDeviceCode);
 
            // 如果不是空托盘任务,处理目标地址(与拘束机/插拔钉机交互)
            if (task.TaskType != (int)TaskOutboundTypeEnum.OutEmpty)
            {
                _targetAddressSelector.HandleInboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
            }
 
            // 更新任务当前位置
            _ = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress);
 
            // 设置 WCS_STB 标志,表示 WCS 已处理
            conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
        }
 
        /// <summary>
        /// 处理输送线入库完成
        /// </summary>
        /// <remarks>
        /// 当入库任务完成时调用。
        /// 更新任务状态并回复 PLC。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void ConveyorLineInFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            // 查询正在执行的任务
            Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
            if (task != null)
            {
 
                // 更新任务状态到下一阶段(通常是完成)
                if (_taskService.UpdateTaskStatusToNext(task).Status)
                {
                    // 回复 ACK 确认
                    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                    QuartzLogHelper.LogInfo(_logger, "ConveyorLineInFinish:入库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", $"入库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode, task.TaskNum, childDeviceCode);
                }
 
 
            }
        }
 
        /// <summary>
        /// 处理输送线出库请求
        /// </summary>
        /// <remarks>
        /// 当 PLC 请求出库任务时调用。
        /// 流程:
        /// 1. 查询待处理任务
        /// 2. 下发任务到 PLC
        /// 3. 更新任务状态
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void RequestOutbound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            // 查询待处理任务
            Dt_Task? task = _taskFilter.QueryPendingTask(conveyorLine.DeviceCode, childDeviceCode);
            if (task != null)
            {
                // 设置任务号
                conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, task.TaskNum, childDeviceCode);
 
                // 设置托盘条码
                conveyorLine.SetValue(ConveyorLineDBNameNew.Barcode, task.PalletCode, childDeviceCode);
 
                // 设置目标地址
                conveyorLine.SetValue(ConveyorLineDBNameNew.Target, task.NextAddress, childDeviceCode);
 
                // 回复 ACK 确认
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
 
                // 更新任务状态
                _taskService.UpdateTaskStatusToNext(task);
 
                QuartzLogHelper.LogInfo(_logger, "RequestOutbound:出库任务已下发,任务号: {TaskNum},子设备: {ChildDeviceCode}", $"出库任务已下发,任务号: {task.TaskNum}", conveyorLine.DeviceCode, task.TaskNum, childDeviceCode);
            }
        }
 
        /// <summary>
        /// 处理输送线出库下一地址请求
        /// </summary>
        /// <remarks>
        /// 当出库任务执行到某个中间站点时调用。
        /// 根据下一地址判断是否需要与拘束机/插拔钉机等设备交互。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void RequestOutNextAddress(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            // 查询正在执行的任务
            Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
            if (task == null)
            {
                QuartzLogHelper.LogDebug(_logger, "RequestOutNextAddress:任务 {TaskNo} 不存在", $"RequestOutNextAddress:任务 {command.TaskNo} 不存在", conveyorLine.DeviceCode, command.TaskNo);
                return;
            }
 
            QuartzLogHelper.LogInfo(_logger, "RequestOutNextAddress:出库下一地址,任务号: {TaskNum},子设备: {ChildDeviceCode}", $"RequestOutNextAddress:出库下一地址,任务号: {task.TaskNum},子设备: {childDeviceCode}", conveyorLine.DeviceCode, task.TaskNum, childDeviceCode);
 
            // 如果不是空托盘任务,处理目标地址
            if (task.TaskType != (int)TaskOutboundTypeEnum.OutEmpty)
            {
                _targetAddressSelector.HandleOutboundNextAddress(conveyorLine, task.NextAddress, childDeviceCode);
            }
 
            // 更新任务当前位置
            _ = _taskService.UpdatePosition(task.TaskNum, task.CurrentAddress);
 
            // 回复 ACK 确认
            conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
        }
 
        /// <summary>
        /// 处理输送线出库完成
        /// </summary>
        /// <remarks>
        /// 当出库任务完成时调用。
        /// 更新任务状态并回复 PLC。
        /// </remarks>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="command">PLC 命令数据</param>
        /// <param name="childDeviceCode">子设备编码</param>
        public void ConveyorLineOutFinish(CommonConveyorLine conveyorLine, ConveyorLineTaskCommandNew command, string childDeviceCode)
        {
            // 查询正在执行的任务
            Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
            if (task != null)
            {
                // 更新任务状态到下一阶段(通常是完成)
                WebResponseContent content = _taskService.UpdateTaskStatusToNext(task);
 
                if (content.Status)
                {
                    // 回复 ACK 确认
                    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                }
 
                QuartzLogHelper.LogInfo(_logger, "ConveyorLineOutFinish:出库完成,完成结果:{Status},任务号: {TaskNum},子设备: {ChildDeviceCode},错误消息: {Message}", $"出库完成,完成结果:{content.Status},任务号: {task.TaskNum},错误消息:{content.Message}", conveyorLine.DeviceCode, content.Status, task.TaskNum, childDeviceCode, content.Message);
            }
        }
    }
}