wanshenmean
6 天以前 fe2a1e74780259605cd230e6f9c629c3dd7fdf15
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
using Microsoft.Extensions.Logging;
using WIDESEA_Core;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_Tasks.Workflow.Abstractions;
 
namespace WIDESEAWCS_Tasks.Workflow
{
    /// <summary>
    /// 机器人任务编排器 - 负责 RobotJob 中的状态机流转和执行步骤编排
    /// </summary>
    /// <remarks>
    /// 迁移原 RobotJob 状态机流转支持,降低 Job 层的复杂度。
    ///
    /// 核心职责:
    /// 1. 根据机器人当前状态和任务状态,决定下一步动作
    /// 2. 处理取货完成后的放货指令下发
    /// 3. 处理放货完成后的取货指令下发(组盘场景)
    ///
    /// 状态机流转规则:
    /// - 条件:RobotRunMode == 2(自动模式)且 RobotControlMode == 1(客户端控制)且 OperStatus != "Running"
    /// - 取货完成 -> 放货:PickFinished + RobotArmObject == 1 + RobotPickFinish -> 发送 Putbattery
    /// - 放货完成 -> 取货:PutFinished + Homed + RobotArmObject == 0 -> 发送 Pickbattery(组盘/换盘场景)
    /// </remarks>
    public class RobotWorkflowOrchestrator : IRobotWorkflowOrchestrator
    {
        /// <summary>
        /// 机械手状态管理器
        /// </summary>
        /// <remarks>
        /// 用于读取和更新机器人的状态。
        /// </remarks>
        private readonly RobotStateManager _stateManager;
 
        /// <summary>
        /// 机械手客户端管理器
        /// </summary>
        /// <remarks>
        /// 用于向机器人客户端发送指令。
        /// </remarks>
        private readonly RobotClientManager _clientManager;
 
        /// <summary>
        /// 任务处理器
        /// </summary>
        /// <remarks>
        /// 用于执行任务相关的业务逻辑,如发送取货指令。
        /// </remarks>
        private readonly RobotTaskProcessor _taskProcessor;
 
        /// <summary>
        /// 机器人任务服务
        /// </summary>
        /// <remarks>
        /// 用于更新任务状态。
        /// </remarks>
        private readonly IRobotTaskService _robotTaskService;
 
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger _logger;
 
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="stateManager">状态管理器</param>
        /// <param name="clientManager">客户端管理器</param>
        /// <param name="taskProcessor">任务处理器</param>
        /// <param name="robotTaskService">任务服务</param>
        /// <param name="logger">日志记录器</param>
        public RobotWorkflowOrchestrator(
            RobotStateManager stateManager,
            RobotClientManager clientManager,
            RobotTaskProcessor taskProcessor,
            IRobotTaskService robotTaskService,
            ILogger logger)
        {
            _stateManager = stateManager;
            _clientManager = clientManager;
            _taskProcessor = taskProcessor;
            _robotTaskService = robotTaskService;
            _logger = logger;
        }
 
        /// <summary>
        /// 执行任务编排流程
        /// </summary>
        /// <remarks>
        /// 根据机器人当前状态和任务状态,决定是否下发指令。
        ///
        /// 执行条件:
        /// 1. 机器人处于自动模式(RobotRunMode == 2)
        /// 2. 机器人处于客户端控制模式(RobotControlMode == 1)
        /// 3. 机器人不在运行中(OperStatus != "Running")
        ///
        /// 流转逻辑:
        /// - 取货完成且手臂有货 -> 发送放货指令(Putbattery)
        /// - 放货完成且手臂无货 -> 发送取货指令(Pickbattery)
        /// </remarks>
        /// <param name="latestState">机器人最新状态</param>
        /// <param name="task">待执行的机器人任务</param>
        /// <param name="ipAddress">机器人 IP 地址</param>
        public async Task ExecuteAsync(RobotSocketState latestState, Dt_RobotTask task, string ipAddress)
        {
            // 按原方案分支判断,确保逻辑一致
            // 检查是否满足自动控制条件:
            // 1. 运行模式为自动(2)
            // 2. 控制模式为客户端控制(1)
            // 3. 运行状态是 Running
            if (latestState.RobotRunMode == 2 /*&& latestState.RobotControlMode == 1*/ && latestState.OperStatus == "Running" && latestState.Homed == "Homed")
            {
                // ========== 取货完成后的放货处理 ==========
                // 条件:
                // - 当前动作是 PickFinished 或 AllPickFinished(取货完成)
                // - 手臂上有物料(RobotArmObject == 1)
                // - 任务状态为 RobotPickFinish(已记录取货完成)
                if ((latestState.CurrentAction == "PickFinished" || latestState.CurrentAction == "AllPickFinished")
                    && latestState.RobotArmObject == 0
                    && task.RobotTaskState == TaskRobotStatusEnum.RobotPickFinish.GetHashCode())
                {
                    _logger.LogInformation("ExecuteAsync:满足放货条件,开始处理取货完成,任务号: {TaskNum}", task.RobotTaskNum);
                    QuartzLogger.Info($"ExecuteAsync:满足放货条件,开始处理取货完成", latestState.RobotCrane?.DeviceName ?? ipAddress);
                    // 发送放货指令
                    await HandlePickFinishedStateAsync(task, ipAddress);
                }
 
                // ========== 初始化或者放货完成后的取货处理 ==========
                // 条件:
                // - 当前动作是 PutFinished、AllPutFinished 或 null(放货完成)
                // - 运行状态为 Homed(已归位)
                // - 手臂上无物料(RobotArmObject == 0)
                // - 任务状态为 RobotPutFinish 或不是 RobotExecuting
                else if ((latestState.CurrentAction == "PutFinished" || latestState.CurrentAction == "AllPutFinished" || latestState.CurrentAction.IsNullOrEmpty())
                    && latestState.RobotArmObject == 0
                    && (task.RobotTaskState == TaskRobotStatusEnum.RobotPutFinish.GetHashCode()
                    || task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()))
                {
                    _logger.LogInformation("ExecuteAsync:满足取货条件,开始处理放货完成,任务号: {TaskNum}", task.RobotTaskNum);
                    QuartzLogger.Info($"ExecuteAsync:满足取货条件,开始处理放货完成", latestState.RobotCrane?.DeviceName ?? ipAddress);
                    // 发送取货指令
                    await HandlePutFinishedStateAsync(task, ipAddress);
                }
            }
        }
 
        /// <summary>
        /// 处理取货完成后的放货指令
        /// </summary>
        /// <remarks>
        /// 当取货完成后,向机器人发送放货指令(Putbattery)。
        /// 机器人收到指令后会将货物放置到目标地址。
        ///
        /// 指令格式:Putbattery,{目标地址}
        /// 例如:Putbattery,B01 表示将货物放置到 B01 位置
        /// </remarks>
        /// <param name="task">当前任务</param>
        /// <param name="ipAddress">机器人 IP 地址</param>
        private async Task HandlePickFinishedStateAsync(Dt_RobotTask task, string ipAddress)
        {
            // 构建放货指令,格式:Putbattery,{目标地址}
            string taskString = $"Putbattery,{task.RobotTargetAddress}";
 
            // 通过客户端管理器发送指令到机器人
            bool result = await _clientManager.SendToClientAsync(ipAddress, taskString);
 
            if (result)
            {
                // 发送成功,记录 Info 日志
                _logger.LogInformation("HandlePickFinishedStateAsync:下发放货指令成功,指令: {TaskString},任务号: {TaskNum}", taskString, task.RobotTaskNum);
                QuartzLogger.Info($"下发放货指令成功,指令: {taskString}", task.RobotRoadway);
 
                // 更新任务状态为"机器人执行中"
                task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
 
                // 获取最新状态并更新任务关联
                var stateToUpdate = _stateManager.GetState(ipAddress);
                if (stateToUpdate != null)
                {
                    stateToUpdate.CurrentTask = task;
 
                    // 安全更新状态到 Redis
                    if (_stateManager.TryUpdateStateSafely(ipAddress, stateToUpdate))
                    {
                        // 状态更新成功后,更新任务记录
                        await _robotTaskService.UpdateRobotTaskAsync(task);
                    }
                }
            }
            else
            {
                // 发送失败,记录 Error 日志
                _logger.LogError("HandlePickFinishedStateAsync:下发放货指令失败,指令: {TaskString},任务号: {TaskNum}", taskString, task.RobotTaskNum);
                QuartzLogger.Error($"下发放货指令失败,指令: {taskString}", task.RobotRoadway);
            }
        }
 
        /// <summary>
        /// 处理放货完成后的取货指令
        /// </summary>
        /// <remarks>
        /// 当放货完成后,根据任务类型决定下一步:
        ///
        /// 1. 如果不是拆盘也不是组盘(普通任务):
        ///    - 直接发送取货指令
        ///
        /// 2. 如果是组盘或换盘任务:
        ///    - 生成新的托盘条码
        ///    - 将条码添加到状态中
        ///    - 发送取货指令
        ///
        /// 组盘任务的条码用于标识新生成的托盘,
        /// 后续放货时会用到这些条码信息。
        /// </remarks>
        /// <param name="task">当前任务</param>
        /// <param name="ipAddress">机器人 IP 地址</param>
        private async Task HandlePutFinishedStateAsync(Dt_RobotTask task, string ipAddress)
        {
            // 获取最新状态
            var stateForUpdate = _stateManager.GetState(ipAddress);
            if (stateForUpdate == null)
            {
                _logger.LogWarning("HandlePutFinishedStateAsync:获取状态失败,IP: {IpAddress}", ipAddress);
                QuartzLogger.Warn($"HandlePutFinishedStateAsync:获取状态失败,IP: {ipAddress}", ipAddress);
                return;
            }
 
            // 如果状态中还没有设置任务类型标志,根据任务类型设置
            if (!stateForUpdate.IsSplitPallet && !stateForUpdate.IsGroupPallet)
            {
                // 判断任务类型并设置相应的标志
                stateForUpdate.IsSplitPallet = task.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode();
                stateForUpdate.IsGroupPallet = task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode()
                    || task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode();
            }
 
            // 如果是组盘任务(包括换盘)
            if (task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode())
            {
                // 生成托盘条码前缀
                const string prefix = "TRAY";
 
                // 生成两个托盘条码(用于组盘操作)
                string trayBarcode1 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix);
                string trayBarcode2 = RobotBarcodeGenerator.GenerateTrayBarcode(prefix);
 
                // 如果条码生成成功
                if (!string.IsNullOrEmpty(trayBarcode1) && !string.IsNullOrEmpty(trayBarcode2))
                {
                    if(stateForUpdate.CellBarcode.Contains(trayBarcode1)|| stateForUpdate.CellBarcode.Contains(trayBarcode2))
                    {
                        _logger.LogError("HandlePutFinishedStateAsync:生成的托盘条码已存在,可能存在重复,任务号: {TaskNum}", task.RobotTaskNum);
                        QuartzLogger.Error($"生成的托盘条码已存在,可能存在重复", stateForUpdate.RobotCrane.DeviceName);
 
                        // 条码重复,记录错误日志并停止后续操作(后续放货时会用到这些条码信息,供后续放货时使用,调试后可能会取消此逻辑)
                        return;
                    }
                    else
                    {
                        _logger.LogInformation("HandlePutFinishedStateAsync:生成的托盘条码唯一,继续执行,任务号: {TaskNum}", task.RobotTaskNum);
                        QuartzLogger.Info($"生成的托盘条码唯一,继续执行", stateForUpdate.RobotCrane.DeviceName);
                        // 将条码添加到状态中,供后续放货时使用
                        stateForUpdate.CellBarcode.Add(trayBarcode1);
                        stateForUpdate.CellBarcode.Add(trayBarcode2);
                    }
 
 
                    // 记录日志:生成托盘条码成功
                    _logger.LogInformation("HandlePutFinishedStateAsync:生成托盘条码成功: {Barcode1}+{Barcode2},任务号: {TaskNum}", trayBarcode1, trayBarcode2, task.RobotTaskNum);
                    QuartzLogger.Info($"生成托盘条码成功: {trayBarcode1}+{trayBarcode2}", stateForUpdate.RobotCrane.DeviceName);
 
                    // 发送取货指令
                    await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
                }
                else
                {
                    // 条码生成失败,记录错误日志
                    _logger.LogError("HandlePutFinishedStateAsync:生成托盘条码失败,任务号: {TaskNum}", task.RobotTaskNum);
                    QuartzLogger.Error($"生成托盘条码失败", stateForUpdate.RobotCrane.DeviceName);
                }
            }
            else
            {
                // 非组盘任务,直接发送取货指令
                await _taskProcessor.SendSocketRobotPickAsync(task, stateForUpdate);
            }
        }
    }
}