wanshenmean
11 小时以前 2f7c7a0621ee2e84c47ccd054889a71e8ce4fdd0
feat(机械手任务): 实现换盘任务多阶段处理逻辑

重构换盘任务处理流程,将原有单阶段实现拆分为多阶段处理:
1. 新增 Phase 1-5 状态定义,区分正常电芯和假电芯处理阶段
2. 实现流向A/B的差异化处理逻辑
3. 添加假电芯批次取货指令下发功能
4. 完善状态机切换和任务计数管理

新增文档说明换盘任务完整流程和阶段切换逻辑
已添加1个文件
已重命名1个文件
已修改8个文件
1004 ■■■■ 文件已修改
Code/.omc/state/mission-state.json 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/subagent-tracking.json 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/docs/换盘任务流程图.md 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/上位系统对接/高温2常温1及机械手设备账号信息表(1).xlsx 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/机械手协议/~$交互流程表(1).xlsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/mission-state.json
@@ -1,5 +1,5 @@
{
  "updatedAt": "2026-04-18T07:46:50.058Z",
  "updatedAt": "2026-04-18T08:52:24.581Z",
  "missions": [
    {
      "id": "session:9007b9ea-1eb6-4d24-8fe7-2c3a949eac88:none",
@@ -1212,6 +1212,62 @@
          "sourceKey": "session-stop:aa71986c72a8dd1f4"
        }
      ]
    },
    {
      "id": "session:78e67b30-83ce-4757-a175-68c3442c4534:none",
      "source": "session",
      "name": "none",
      "objective": "Session mission",
      "createdAt": "2026-04-18T08:34:11.529Z",
      "updatedAt": "2026-04-18T08:52:24.581Z",
      "status": "done",
      "workerCount": 1,
      "taskCounts": {
        "total": 1,
        "pending": 0,
        "blocked": 0,
        "inProgress": 0,
        "completed": 1,
        "failed": 0
      },
      "agents": [
        {
          "name": "general-purpose:a6a0c97",
          "role": "general-purpose",
          "ownership": "a6a0c97facebc27a6",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-18T08:52:24.581Z"
        }
      ],
      "timeline": [
        {
          "id": "session-start:a6a0c97facebc27a6:2026-04-18T08:34:11.529Z",
          "at": "2026-04-18T08:34:11.529Z",
          "kind": "update",
          "agent": "general-purpose:a6a0c97",
          "detail": "started general-purpose:a6a0c97",
          "sourceKey": "session-start:a6a0c97facebc27a6"
        },
        {
          "id": "session-stop:acc34e8d2cd052b69:2026-04-18T08:42:23.674Z",
          "at": "2026-04-18T08:42:23.674Z",
          "kind": "completion",
          "agent": "general-purpose:a6a0c97",
          "detail": "completed",
          "sourceKey": "session-stop:acc34e8d2cd052b69"
        },
        {
          "id": "session-stop:a6a0c97facebc27a6:2026-04-18T08:52:24.581Z",
          "at": "2026-04-18T08:52:24.581Z",
          "kind": "completion",
          "agent": "general-purpose:a6a0c97",
          "detail": "completed",
          "sourceKey": "session-stop:a6a0c97facebc27a6"
        }
      ]
    }
  ]
}
Code/.omc/state/subagent-tracking.json
@@ -758,10 +758,19 @@
      "status": "completed",
      "completed_at": "2026-04-18T07:46:15.279Z",
      "duration_ms": 58126
    },
    {
      "agent_id": "a6a0c97facebc27a6",
      "agent_type": "general-purpose",
      "started_at": "2026-04-18T08:34:11.529Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-18T08:52:24.581Z",
      "duration_ms": 1093052
    }
  ],
  "total_spawned": 82,
  "total_completed": 82,
  "total_spawned": 83,
  "total_completed": 83,
  "total_failed": 0,
  "last_updated": "2026-04-18T07:46:50.164Z"
  "last_updated": "2026-04-18T14:04:59.113Z"
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotSocketState.cs
@@ -197,10 +197,11 @@
        /// <remarks>
        /// é˜¶æ®µå®šä¹‰ï¼š
        /// 0: æœªå¼€å§‹
        /// 1: å–正常电芯(流向B) / å–假电芯(流向A)
        /// 2: æ”¾æ­£å¸¸ç”µèŠ¯ï¼ˆæµå‘B) / æ”¾å‡ç”µèŠ¯ï¼ˆæµå‘A)
        /// 3: å–假电芯(流向B Phase2)
        /// 4: æ”¾å‡ç”µèŠ¯åˆ°5号位(流向B Phase2)
        /// 1: å–正常电芯(两流向相同)
        /// 2: æ”¾æ­£å¸¸ç”µèŠ¯åˆ°ç›®æ ‡æ‰˜ç›˜ï¼ˆä¸¤æµå‘ç›¸åŒï¼‰
        /// 3: æµå‘A:正常取完→空托盘回库→取假电芯从5号位 / æµå‘B:正常取完→取假电芯从源地址
        /// 4: æµå‘A:放假电芯到目标托盘 / æµå‘B:放假电芯到5号位
        /// 5: æµå‘A:假放完→allputfinished入库HCSC1 / æµå‘B:假取完→空托盘回库HCSC1+组盘入库GWSC1
        /// </remarks>
        public int ChangePalletPhase { get; set; }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
@@ -344,6 +344,53 @@
        }
        /// <summary>
        /// ä¸‹å‘假电芯取货指令(带批次格式和总数)
        /// </summary>
        /// <remarks>
        /// å‘送顺序:
        /// 1. PickTotalNum,{N} -- çœŸå®žç”µèŠ¯æ€»æ•°
        /// 2. Pickbattery,5,{start}-{end} -- æ‰¹æ¬¡å–货指令(固定从5号位取)
        ///
        /// ä¸‹å‘成功后更新任务状态为"机器人执行中"。
        /// </remarks>
        /// <param name="task">要下发的任务对象</param>
        /// <param name="state">机器人当前状态</param>
        /// <param name="batchStart">批次起始编号</param>
        /// <param name="batchEnd">批次结束编号</param>
        public async Task SendFakeBatteryPickWithBatchAsync(Dt_RobotTask task, RobotSocketState state, int batchStart, int batchEnd)
        {
            // å…ˆå‘送总数指令
            string totalNumCmd = $"PickTotalNum,{task.RobotTaskTotalNum}";
            await _socketClientGateway.SendToClientAsync(state.IPAddress, totalNumCmd);
            // å†å‘送批次取货指令(假电芯固定从5号位取)
            string range = batchEnd == 0 ? $"{batchStart}-0" : $"{batchStart}-{batchEnd}";
            string taskString = $"Pickbattery,5,{range}";
            bool result = await _socketClientGateway.SendToClientAsync(state.IPAddress, taskString);
            if (result)
            {
                _logger.LogInformation("下发假电芯批次取货指令成功,指令: {TaskString},批次: {Range},设备: {DeviceName}",
                    taskString, range, state.RobotCrane?.DeviceName);
                QuartzLogger.Info($"下发假电芯批次取货指令成功,指令: {taskString},批次: {range}", state.RobotCrane?.DeviceName);
                task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
                state.CurrentTask = task;
                if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
                {
                    await _robotTaskService.UpdateRobotTaskAsync(task);
                }
            }
            else
            {
                _logger.LogError("下发假电芯批次取货指令失败,指令: {TaskString},设备: {DeviceName}", taskString, state.RobotCrane?.DeviceName);
                QuartzLogger.Error($"下发假电芯批次取货指令失败,指令: {taskString}", state.RobotCrane?.DeviceName);
            }
        }
        /// <summary>
        /// ä¸‹å‘放货指令(带批次格式和总数)
        /// </summary>
        /// <remarks>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotPrefixCommandHandler.cs
@@ -270,28 +270,47 @@
                    // æ¢ç›˜ä»»åŠ¡ï¼šæ ¹æ®é˜¶æ®µåŒºåˆ†å¤„ç†
                    if (state.ChangePalletPhase == 2)
                    {
                        // Phase 2:放正常电芯到目标托盘完成,递增计数
                        state.RobotTaskTotalNum += positions.Length;
                        if (task != null)
                            task.RobotTaskTotalNum -= positions.Length;
                        if (isFlowA)
                        {
                            // æµå‘A Phase2:放假电芯到目标托盘,不调用 API,不递增计数
                            // ä»…更新状态
                            // æµå‘A:不调用 API,仅递增计数
                        }
                        else
                        {
                            // æµå‘B Phase2:放正常电芯,递增计数
                            state.RobotTaskTotalNum += positions.Length;
                            if (task != null)
                                task.RobotTaskTotalNum -= positions.Length;
                            // æž„建库存 DTO å¹¶è°ƒç”¨ ChangePalletAsync API
                            // æµå‘B:调用换盘 API
                            var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
                            var result = _taskProcessor.PostGroupPalletAsync(nameof(ConfigKey.ChangePalletAsync), stockDTO);
                            putSuccess = result.Data.Status && result.IsSuccess;
                        }
                        // åˆ‡å›ž Phase 1,继续取正常电芯
                        state.ChangePalletPhase = 1;
                    }
                    else if (state.ChangePalletPhase == 4)
                    {
                        // æµå‘B Phase4:放假电芯到5号位,不调用 API,不递增计数,释放点位
                        _fakeBatteryPositionService.MarkAsAvailable(positions.ToList());
                        if (isFlowA)
                        {
                            // æµå‘A:放假电芯到目标托盘,递增计数,调用换盘 API
                            state.RobotTaskTotalNum += positions.Length;
                            if (task != null)
                                task.RobotTaskTotalNum -= positions.Length;
                            var stockDTO = RobotTaskProcessor.BuildStockDTO(state, positions);
                            var result = _taskProcessor.PostGroupPalletAsync(nameof(ConfigKey.ChangePalletAsync), stockDTO);
                            putSuccess = result.Data.Status && result.IsSuccess;
                        }
                        else
                        {
                            // æµå‘B:放假电芯到5号位,释放点位
                            _fakeBatteryPositionService.MarkAsAvailable(positions.ToList());
                        }
                        // åˆ‡å›ž Phase 3,继续取假电芯
                        state.ChangePalletPhase = 3;
                    }
                    else
                    {
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
@@ -143,35 +143,72 @@
                        // åˆ¤æ–­ä»»åŠ¡ç±»åž‹
                        var robotTaskType = (RobotTaskTypeEnum)currentTask.RobotTaskType;
                        // æ¢ç›˜ä»»åŠ¡ï¼šä»…å½“æ‰€æœ‰é˜¶æ®µå®Œæˆæ—¶æ‰å¤„ç†å…¥åº“
                        // æ¢ç›˜ä»»åŠ¡ï¼šæ ¹æ®é˜¶æ®µåŒºåˆ†å¤„ç†
                        if (robotTaskType == RobotTaskTypeEnum.ChangePallet)
                        {
                            if (state.ChangePalletPhase == 0)
                            // è°ƒç”¨æ‰¹é‡æ‹†ç›˜ç¡®è®¤æŽ¥å£
                            var sourcePallet = state.CurrentTask.RobotSourceAddressPalletCode;
                            var confirmResult = _taskProcessor.PostSplitPalletConfirmAsync(sourcePallet, state.RobotCrane?.DeviceName);
                            if (!confirmResult.IsSuccess)
                            {
                                // è°ƒç”¨æ‰¹é‡æ‹†ç›˜ç¡®è®¤æŽ¥å£ï¼ˆæ¢ç›˜å–完阶段)
                                var sourcePallet = state.CurrentTask.RobotSourceAddressPalletCode;
                                var confirmResult = _taskProcessor.PostSplitPalletConfirmAsync(sourcePallet, state.RobotCrane?.DeviceName);
                                if (!confirmResult.IsSuccess)
                                {
                                    QuartzLogger.Error($"批量拆盘确认失败: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
                                }
                                // æ‰€æœ‰é˜¶æ®µå®Œæˆï¼Œå¤„理入库
                                if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
                                {
                                    // å…¥åº“成功,删除任务记录
                                    _taskProcessor.DeleteTask(currentTask.RobotTaskId);
                                    await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                                    QuartzLogger.Info($"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                                    // é‡ç½®æ‰¹æ¬¡çŠ¶æ€
                                    state.ChangePalletPhase = 0;
                                    state.CurrentBatchIndex = 1;
                                    state.IsInFakeBatteryMode = false;
                                    return true;
                                }
                                QuartzLogger.Error($"批量拆盘确认失败: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            // ä¸­é—´é˜¶æ®µä¸å¤„理,仅更新状态
                            if (state.ChangePalletPhase == 5)
                            {
                                // FlowB æœ€ç»ˆé˜¶æ®µï¼šå‡ç”µèŠ¯å–å®Œï¼Œæºç©ºæ‰˜ç›˜å›žåº“ HCSC1
                                if (!await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
                                {
                                    return false;
                                }
                                if (_taskProcessor.DeleteTask(currentTask.RobotTaskId) != true)
                                {
                                    QuartzLogger.Error($"allpickfinished:删除任务记录失败,任务号: {currentTask.RobotTaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
                                    return false;
                                }
                                await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                                QuartzLogger.Info($"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                                state.CurrentTask = null;
                                state.RobotTaskTotalNum = 0;
                                state.CellBarcode = new List<string>();
                                state.ChangePalletPhase = 0;
                                state.CurrentBatchIndex = 1;
                                state.IsInFakeBatteryMode = false;
                                return true;
                            }
                            else if (state.ChangePalletPhase != 0)
                            {
                                // FlowA ä¸­é—´é˜¶æ®µï¼šæ­£å¸¸ç”µèŠ¯å–å®Œï¼Œæºç©ºæ‰˜ç›˜å›žåº“ GWSC1
                                // ä¸åˆ é™¤ä»»åŠ¡ï¼Œä¸é‡ç½®çŠ¶æ€ï¼Œç»§ç»­ Phase 3-4 å‡ç”µèŠ¯æµç¨‹
                                if (!await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
                                {
                                    return false;
                                }
                                return true;
                            }
                            // Phase == 0: éžæ‰¹æ¬¡æ¨¡å¼ï¼ˆç›®æ ‡æ€»æ•°==48)
                            if (!await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
                            {
                                return false;
                            }
                            if (_taskProcessor.DeleteTask(currentTask.RobotTaskId) != true)
                            {
                                QuartzLogger.Error($"allpickfinished:删除任务记录失败,任务号: {currentTask.RobotTaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                            QuartzLogger.Info($"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                            state.ChangePalletPhase = 0;
                            state.CurrentBatchIndex = 1;
                            state.IsInFakeBatteryMode = false;
                            return true;
                        }
@@ -184,16 +221,24 @@
                            if (!confirmResult.IsSuccess)
                            {
                                QuartzLogger.Error($"批量拆盘确认失败: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
                            if (!await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: true))
                            {
                                // å…¥åº“成功,删除任务记录
                                _taskProcessor.DeleteTask(currentTask.RobotTaskId);
                                await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                                QuartzLogger.Info($"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                                return true;
                                return false;
                            }
                            // å…¥åº“成功,删除任务记录
                            if (_taskProcessor.DeleteTask(currentTask.RobotTaskId) != true)
                            {
                                QuartzLogger.Error($"allpickfinished:删除任务记录失败,任务号: {currentTask.RobotTaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                            QuartzLogger.Info($"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                            return true;
                        }
                        return false;
                    }
@@ -214,41 +259,77 @@
                        // åˆ¤æ–­ä»»åŠ¡ç±»åž‹
                        var robotTaskType = (RobotTaskTypeEnum)currentTask.RobotTaskType;
                        // æ¢ç›˜ä»»åŠ¡ï¼šä»…å½“æ‰€æœ‰é˜¶æ®µå®Œæˆæ—¶æ‰å¤„ç†å…¥åº“
                        // æ¢ç›˜ä»»åŠ¡ï¼šæ ¹æ®é˜¶æ®µåŒºåˆ†å¤„ç†
                        if (robotTaskType == RobotTaskTypeEnum.ChangePallet)
                        {
                            if (state.ChangePalletPhase == 0)
                            // è°ƒç”¨æ‰¹é‡ç»„盘确认接口
                            var targetPallet = state.CurrentTask.RobotTargetAddressPalletCode;
                            var confirmResult = _taskProcessor.PostGroupPalletConfirmAsync(targetPallet, state.RobotCrane?.DeviceName);
                            if (!confirmResult.IsSuccess)
                            {
                                // è°ƒç”¨æ‰¹é‡ç»„盘确认接口(换盘放完阶段)
                                var targetPallet = state.CurrentTask.RobotTargetAddressPalletCode;
                                var confirmResult = _taskProcessor.PostGroupPalletConfirmAsync(targetPallet, state.RobotCrane?.DeviceName);
                                if (!confirmResult.IsSuccess)
                                {
                                    QuartzLogger.Error($"批量组盘确认失败: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
                                }
                                // æ‰€æœ‰é˜¶æ®µå®Œæˆï¼Œå¤„理入库
                                if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
                                {
                                    // å…¥åº“成功,删除任务记录
                                    _taskProcessor.DeleteTask(currentTask.RobotTaskId);
                                    // æ¸…理状态,为下一个任务做准备
                                    state.CurrentTask = null;           // æ¸…除当前任务
                                    state.RobotTaskTotalNum = 0;        // é‡ç½®ä»»åŠ¡è®¡æ•°
                                    state.CellBarcode = new List<string>();  // æ¸…空条码列表
                                    await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                                    QuartzLogger.Info($"发送消息:【Group,diskFinished】", state.RobotCrane.DeviceName);
                                    // é‡ç½®æ‰¹æ¬¡çŠ¶æ€
                                    state.ChangePalletPhase = 0;
                                    state.CurrentBatchIndex = 1;
                                    state.IsInFakeBatteryMode = false;
                                    return true;
                                }
                                QuartzLogger.Error($"批量组盘确认失败: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            // ä¸­é—´é˜¶æ®µä¸å¤„理,仅更新状态
                            if (state.ChangePalletPhase == 5)
                            {
                                // FlowA æœ€ç»ˆé˜¶æ®µï¼šå‡ç”µèŠ¯æ”¾å®Œï¼Œç›®æ ‡æ‰˜ç›˜æ»¡48入库 HCSC1
                                if (!await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
                                {
                                    return false;
                                }
                                if (_taskProcessor.DeleteTask(currentTask.RobotTaskId) != true)
                                {
                                    QuartzLogger.Error($"allputfinished:删除任务记录失败,任务号: {currentTask.RobotTaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
                                    return false;
                                }
                                state.CurrentTask = null;
                                state.RobotTaskTotalNum = 0;
                                state.CellBarcode = new List<string>();
                                await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                                QuartzLogger.Info($"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                                state.ChangePalletPhase = 0;
                                state.CurrentBatchIndex = 1;
                                state.IsInFakeBatteryMode = false;
                                return true;
                            }
                            else if (state.ChangePalletPhase != 0)
                            {
                                // FlowB ä¸­é—´é˜¶æ®µï¼šæ­£å¸¸ç”µèŠ¯æ”¾å®Œï¼Œæœ‰è´§æ‰˜ç›˜ç»„ç›˜å…¥åº“ GWSC1
                                // ä¸åˆ é™¤ä»»åŠ¡ï¼Œä¸é‡ç½®çŠ¶æ€ï¼Œç»§ç»­ Phase 3-4 å‡ç”µèŠ¯æµç¨‹
                                if (!await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
                                {
                                    return false;
                                }
                                return true;
                            }
                            // Phase == 0: éžæ‰¹æ¬¡æ¨¡å¼ï¼ˆç›®æ ‡æ€»æ•°==48)
                            if (!await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
                            {
                                return false;
                            }
                            if (_taskProcessor.DeleteTask(currentTask.RobotTaskId) != true)
                            {
                                QuartzLogger.Error($"allputfinished:删除任务记录失败,任务号: {currentTask.RobotTaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            state.CurrentTask = null;
                            state.RobotTaskTotalNum = 0;
                            state.CellBarcode = new List<string>();
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Swap,diskFinished");
                            QuartzLogger.Info($"发送消息:【Swap,diskFinished】", state.RobotCrane.DeviceName);
                            state.ChangePalletPhase = 0;
                            state.CurrentBatchIndex = 1;
                            state.IsInFakeBatteryMode = false;
                            return true;
                        }
@@ -256,25 +337,36 @@
                        if (robotTaskType == RobotTaskTypeEnum.GroupPallet)
                        {
                            // è°ƒç”¨æ‰¹é‡ç»„盘确认接口
                            //var targetPallet = state.CurrentTask.RobotTargetAddressPalletCode;
                            //_taskProcessor.PostGroupPalletConfirmAsync(targetPallet, state.RobotCrane?.DeviceName);
                            var targetPallet = state.CurrentTask.RobotTargetAddressPalletCode;
                            var confirmResult = _taskProcessor.PostGroupPalletConfirmAsync(targetPallet, state.RobotCrane?.DeviceName);
                            if (!confirmResult.IsSuccess)
                            {
                                QuartzLogger.Error($"批量组盘确认失败: {confirmResult.ErrorMessage}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            // å¤„理入库任务回传
                            // useSourceAddress: false è¡¨ç¤ºä½¿ç”¨ç›®æ ‡åœ°å€ï¼ˆç»„盘场景)
                            if (await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
                            if (!await _taskProcessor.HandleInboundTaskAsync(state, useSourceAddress: false))
                            {
                                // å…¥åº“成功,删除任务记录
                                _taskProcessor.DeleteTask(currentTask.RobotTaskId);
                                // æ¸…理状态,为下一个任务做准备
                                state.CurrentTask = null;           // æ¸…除当前任务
                                state.RobotTaskTotalNum = 0;        // é‡ç½®ä»»åŠ¡è®¡æ•°
                                state.CellBarcode = new List<string>();  // æ¸…空条码列表
                                await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                                QuartzLogger.Info($"发送消息:【Group,diskFinished】", state.RobotCrane.DeviceName);
                                return true;
                                return false;
                            }
                            // å…¥åº“成功,删除任务记录
                            if (_taskProcessor.DeleteTask(currentTask.RobotTaskId) != true)
                            {
                                QuartzLogger.Error($"allputfinished:删除任务记录失败,任务号: {currentTask.RobotTaskNum}", state.RobotCrane?.DeviceName ?? "Unknown");
                                return false;
                            }
                            // æ¸…理状态,为下一个任务做准备
                            state.CurrentTask = null;           // æ¸…除当前任务
                            state.RobotTaskTotalNum = 0;        // é‡ç½®ä»»åŠ¡è®¡æ•°
                            state.CellBarcode = new List<string>();  // æ¸…空条码列表
                            await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                            QuartzLogger.Info($"发送消息:【Group,diskFinished】", state.RobotCrane.DeviceName);
                            return true;
                        }
                        return false;
                    }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
@@ -166,7 +166,7 @@
            var state = _stateManager.GetState(ipAddress);
            // æ¢ç›˜ä»»åŠ¡ä½¿ç”¨æ‰¹æ¬¡æ ¼å¼
            // æ¢ç›˜ä»»åŠ¡æ‰¹æ¬¡æ¨¡å¼
            if (task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode())
            {
                int targetNormalCount = task.RobotTaskTotalNum;
@@ -174,78 +174,94 @@
                bool isFlowA = task.RobotSourceAddressLineCode is "11001" or "11010";
                // æµå‘A Phase 2:放假电芯到目标托盘
                if (isFlowA && state?.ChangePalletPhase == 2)
                // ==================== Phase 2: æ”¾æ­£å¸¸ç”µèŠ¯åˆ°ç›®æ ‡æ‰˜ç›˜ï¼ˆä¸¤æµå‘ç›¸åŒï¼‰====================
                // PickFinished åˆ°è¾¾ï¼šPhase 1 çš„ Pick å‘½ä»¤å®Œæˆï¼ŒçŽ°åœ¨ä¸‹å‘ Put å‘½ä»¤æ”¾æ­£å¸¸ç”µèН
                if (state?.ChangePalletPhase == 2)
                {
                    int remaining = 48 - currentCompletedCount;
                    if (remaining <= 0) return;
                    int remainingNormal = targetNormalCount - currentCompletedCount;
                    if (remainingNormal <= 0)
                    {
                        // æ­£å¸¸ç”µèŠ¯å…¨éƒ¨æ”¾å®Œï¼Œç­‰å¾… HandlePutFinishedAsync åˆ‡æ¢åˆ° Phase 3
                        return;
                    }
                    int batchStart = targetNormalCount + 1 + (state.CurrentBatchIndex - 1);
                    int putCount = Math.Min(4, remaining);
                    int batchStart = (currentCompletedCount / 4) * 4 + 1;
                    int putCount = Math.Min(4, remainingNormal);
                    var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount);
                    await _taskProcessor.SendPutWithBatchAsync(task, state, task.RobotTargetAddress, start, end);
                    // Phase ä¿æŒä¸º 2,等 HandlePutFinishedAsync å¤„理完放货计数后再切回 Phase 1
                    _stateManager.TryUpdateStateSafely(ipAddress, state);
                    return;
                }
                // æµå‘B Phase 4:放假电芯到5号位
                if (!isFlowA && state?.ChangePalletPhase == 4)
                // ==================== Phase 4: æ”¾å‡ç”µèŠ¯ï¼ˆä¸¤æµå‘åˆ†å‰ï¼‰====================
                // PickFinished åˆ°è¾¾ï¼šPhase 3 çš„ Pick å‘½ä»¤å®Œæˆï¼ŒçŽ°åœ¨ä¸‹å‘ Put å‘½ä»¤æ”¾å‡ç”µèН
                if (state?.ChangePalletPhase == 4)
                {
                    int fakeCount = 48 - targetNormalCount;
                    int completedFake = Math.Max(0, currentCompletedCount - targetNormalCount);
                    int remainingFake = fakeCount - completedFake;
                    if (remainingFake <= 0) return;
                    var positions = _taskProcessor.GetNextAvailableFakeBatteryPositions(Math.Min(4, remainingFake));
                    if (positions.Count == 0)
                    if (remainingFake <= 0)
                    {
                        _logger.LogError("HandlePickFinishedStateAsync:无可用假电芯点位,任务号: {TaskNum}", task.RobotTaskNum);
                        // å‡ç”µèŠ¯å…¨éƒ¨æ”¾å®Œï¼Œç­‰å¾… allputfinished è§¦å‘ Phase 5 å…¥åº“
                        return;
                    }
                    int start = positions.Min();
                    int end = positions.Max();
                    await _taskProcessor.SendPutWithBatchAsync(task, state, "5", start, end);
                    return;
                }
                // æµå‘B Phase 2:放正常电芯到目标托盘
                if (!isFlowA && state?.ChangePalletPhase == 2)
                {
                    int remainingNormal = targetNormalCount - currentCompletedCount;
                    if (remainingNormal <= 0) return;
                    int batchStart = ((currentCompletedCount - 1) / 4) * 4 + 1;
                    int putCount = Math.Min(4, remainingNormal);
                    var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount);
                    await _taskProcessor.SendPutWithBatchAsync(task, state, task.RobotTargetAddress, start, end);
                    return;
                }
                // é»˜è®¤ï¼šä½¿ç”¨åŽŸæœ‰æ ¼å¼
                taskString = $"Putbattery,{task.RobotTargetAddress}";
            }
            else
            {
                // éžæ¢ç›˜ä»»åŠ¡ï¼šä½¿ç”¨åŽŸæœ‰æ ¼å¼
                if (state != null && state.IsGroupPallet && task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode())
                {
                    // ç»„盘任务:放货需判断是否NG,如果NG则放到NG口
                    if (state.IsScanNG)
                    if (isFlowA)
                    {
                        taskString = $"Putbattery,4";
                        // æµå‘A:放假电芯到目标托盘
                        int batchStart = targetNormalCount + 1 + (state.CurrentBatchIndex - 1);
                        int putCount = Math.Min(4, remainingFake);
                        var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount);
                        await _taskProcessor.SendPutWithBatchAsync(task, state, task.RobotTargetAddress, start, end);
                        state.CurrentBatchIndex += putCount;
                        // Phase ä¿æŒä¸º 4,等 HandlePutFinishedAsync å¤„理完后再切回 Phase 3
                        _stateManager.TryUpdateStateSafely(ipAddress, state);
                    }
                    else
                    {
                        taskString = $"Putbattery,{task.RobotTargetAddress}";
                        // æµå‘B:放假电芯到5号位
                        var positions = _taskProcessor.GetNextAvailableFakeBatteryPositions(Math.Min(4, remainingFake));
                        if (positions.Count == 0)
                        {
                            _logger.LogError("HandlePickFinishedStateAsync:无可用假电芯点位,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        int start = positions.Min();
                        int end = positions.Max();
                        await _taskProcessor.SendPutWithBatchAsync(task, state, "5", start, end);
                        // Phase ä¿æŒä¸º 4,等 HandlePutFinishedAsync å¤„理完后再切回 Phase 3
                        _stateManager.TryUpdateStateSafely(ipAddress, state);
                    }
                    return;
                }
                // éžæ‰¹æ¬¡æ¨¡å¼æˆ–其他阶段不下发指令
                return;
            }
            // éžæ¢ç›˜ä»»åŠ¡ï¼šä½¿ç”¨åŽŸæœ‰æ ¼å¼
            if (state != null && state.IsGroupPallet && task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode())
            {
                if (state.IsScanNG)
                {
                    taskString = $"Putbattery,4";
                }
                else
                {
                    taskString = $"Putbattery,{task.RobotTargetAddress}";
                }
            }
            else
                taskString = $"Putbattery,{task.RobotTargetAddress}";
            bool result = await _clientManager.SendToClientAsync(ipAddress, taskString);
@@ -376,7 +392,9 @@
            else if (task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode())
            {
                const int targetTotal = 48;
                // æ¢ç›˜ä»»åŠ¡è¿›å…¥æ‰¹æ¬¡æ¨¡å¼ï¼Œåˆ†é˜¶æ®µå¤„ç†æ­£å¸¸ç”µèŠ¯å’Œå‡ç”µèŠ¯ ï¼ˆæœºå™¨äººä»»åŠ¡æ€»æ•°ï¼‰
                int targetNormalCount = task.RobotTaskTotalNum;
                // å½“前已完成数量(取货完成的数量),初始为状态中的 RobotTaskTotalNum,后续根据取货完成的数量动态更新
                int currentCompletedCount = stateForUpdate.RobotTaskTotalNum;
                // åˆ¤æ–­æµå‘(null-safe)
@@ -396,159 +414,82 @@
                    stateForUpdate.CurrentBatchIndex = 1;
                    _logger.LogInformation("HandlePutFinishedStateAsync:换盘任务进入批次模式,任务号: {TaskNum},流向: {Flow}",
                        task.RobotTaskNum, isFlowA ? "A" : "B");
                    _stateManager.TryUpdateStateSafely(ipAddress, stateForUpdate);
                }
                // ==================== æµå‘A:补假电芯到目标托盘 ====================
                if (isFlowA)
                // ==================== Phase 1: å–正常电芯(两流向相同)====================
                if (stateForUpdate.ChangePalletPhase == 1)
                {
                    // Phase 1: å–假电芯(从5号位,使用 PositionIndex)
                    if (stateForUpdate.ChangePalletPhase == 1)
                    int remainingNormal = targetNormalCount - currentCompletedCount;
                    if (remainingNormal <= 0)
                    {
                        int remaining = targetTotal - currentCompletedCount;
                        if (remaining <= 0)
                        {
                            stateForUpdate.ChangePalletPhase = 0;
                            stateForUpdate.CurrentBatchIndex = 1;
                            stateForUpdate.IsInFakeBatteryMode = false;
                            _logger.LogInformation("HandlePutFinishedStateAsync:流向A完成,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        int pickCount = Math.Min(4, remaining);
                        var positions = _taskProcessor.GetNextAvailableFakeBatteryPositions(pickCount);
                        if (positions.Count == 0)
                        {
                            _logger.LogError("HandlePutFinishedStateAsync:无可用假电芯点位,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        await _taskProcessor.SendSocketRobotFakeBatteryPickAsync(task, stateForUpdate, positions);
                        stateForUpdate.ChangePalletPhase = 2;
                        // æ­£å¸¸ç”µèŠ¯å–å®Œï¼Œåˆ‡æ¢åˆ° Phase 3 å–假电芯
                        stateForUpdate.ChangePalletPhase = 3;
                        stateForUpdate.CurrentBatchIndex = 1; // å‡ç”µèŠ¯æ‰¹æ¬¡ä»Žå¤´å¼€å§‹
                        _logger.LogInformation("HandlePutFinishedStateAsync:正常电芯取完,切换到Phase 3取假电芯,任务号: {TaskNum}", task.RobotTaskNum);
                        _stateManager.TryUpdateStateSafely(ipAddress, stateForUpdate);
                        return;
                    }
                    // Phase 2: æ”¾å‡ç”µèŠ¯åˆ°ç›®æ ‡æ‰˜ç›˜ï¼ˆä»Ž targetNormalCount+1 å¼€å§‹é€’增)
                    else if (stateForUpdate.ChangePalletPhase == 2)
                    {
                        int remaining = targetTotal - currentCompletedCount;
                        if (remaining <= 0)
                        {
                            stateForUpdate.ChangePalletPhase = 0;
                            stateForUpdate.CurrentBatchIndex = 1;
                            stateForUpdate.IsInFakeBatteryMode = false;
                            _logger.LogInformation("HandlePutFinishedStateAsync:流向A完成,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        // è®¡ç®—放货批次编号:从 targetNormalCount + 1 å¼€å§‹
                        int batchStart = targetNormalCount + 1 + (stateForUpdate.CurrentBatchIndex - 1);
                        int putCount = Math.Min(4, remaining);
                        var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount);
                    int pickCount = Math.Min(4, remainingNormal);
                    var (start, end) = _taskProcessor.BuildBatchRange(stateForUpdate.CurrentBatchIndex, pickCount);
                        await _taskProcessor.SendPutWithBatchAsync(task, stateForUpdate, task.RobotTargetAddress, start, end);
                    await _taskProcessor.SendPickWithBatchAsync(task, stateForUpdate, task.RobotSourceAddress, start, end);
                        stateForUpdate.CurrentBatchIndex += putCount;
                        stateForUpdate.ChangePalletPhase = 1;
                    }
                    stateForUpdate.CurrentBatchIndex += pickCount;
                    // å‘完 Pick åŽåˆ‡æ¢åˆ° Phase=2,等 PickFinished è§¦å‘ HandlePickFinishedStateAsync ä¸‹å‘放货指令
                    stateForUpdate.ChangePalletPhase = 2;
                    _stateManager.TryUpdateStateSafely(ipAddress, stateForUpdate);
                    return;
                }
                // ==================== æµå‘B:取正常电芯 + å›žæ”¶å‡ç”µèН ====================
                else
                // ==================== Phase 3: å¤„理假电芯(流向A/B åˆ†å‰ï¼‰ï¼Œè‡ªå·±å¾ªçޝ ====================
                if (stateForUpdate.ChangePalletPhase == 3)
                {
                    // Phase 1: å–正常电芯(从源地址,从1开始递增)
                    if (stateForUpdate.ChangePalletPhase == 1)
                    int fakeCount = targetTotal - targetNormalCount;
                    int completedFake = Math.Max(0, currentCompletedCount - targetNormalCount);
                    int remainingFake = fakeCount - completedFake;
                    if (remainingFake <= 0)
                    {
                        int remainingNormal = targetNormalCount - currentCompletedCount;
                        if (remainingNormal <= 0)
                        {
                            // æ­£å¸¸ç”µèŠ¯å–å®Œï¼Œåˆ‡æ¢åˆ° Phase 3
                            stateForUpdate.ChangePalletPhase = 3;
                            stateForUpdate.CurrentBatchIndex = targetNormalCount + 1;
                            _logger.LogInformation("HandlePutFinishedStateAsync:正常电芯全部取完,进入Phase 3回收假电芯,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        int pickCount = Math.Min(4, remainingNormal);
                        var (start, end) = _taskProcessor.BuildBatchRange(stateForUpdate.CurrentBatchIndex, pickCount);
                        await _taskProcessor.SendPickWithBatchAsync(task, stateForUpdate, task.RobotSourceAddress, start, end);
                        stateForUpdate.CurrentBatchIndex += pickCount;
                        stateForUpdate.ChangePalletPhase = 2;
                        // å‡ç”µèŠ¯å…¨éƒ¨å¤„ç†å®Œï¼Œåˆ‡æ¢åˆ° Phase 5 ç­‰å¾…入库
                        stateForUpdate.ChangePalletPhase = 5;
                        _logger.LogInformation("HandlePutFinishedStateAsync:假电芯处理完毕,切换到Phase 5等待入库,任务号: {TaskNum}", task.RobotTaskNum);
                        _stateManager.TryUpdateStateSafely(ipAddress, stateForUpdate);
                        return;
                    }
                    // Phase 2: æ”¾æ­£å¸¸ç”µèŠ¯åˆ°ç›®æ ‡æ‰˜ç›˜ï¼ˆæ”¾è´§ç¼–å·ä¸Žå–è´§ç¼–å·ä¸€è‡´ï¼‰
                    else if (stateForUpdate.ChangePalletPhase == 2)
                    if (isFlowA)
                    {
                        int remainingNormal = targetNormalCount - currentCompletedCount;
                        if (remainingNormal <= 0)
                        {
                            // æ­£å¸¸ç”µèŠ¯æ”¾å®Œï¼Œåˆ‡æ¢åˆ° Phase 3
                            stateForUpdate.ChangePalletPhase = 3;
                            stateForUpdate.CurrentBatchIndex = targetNormalCount + 1;
                            _logger.LogInformation("HandlePutFinishedStateAsync:正常电芯全部放完,进入Phase 3回收假电芯,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        // è®¡ç®—本批放货编号:基于 currentCompletedCount æŽ¨å¯¼æ‰¹æ¬¡èµ·å§‹
                        int batchStart = ((currentCompletedCount - 1) / 4) * 4 + 1;
                        int putCount = Math.Min(4, remainingNormal);
                        var (start, end) = _taskProcessor.BuildBatchRange(batchStart, putCount);
                        await _taskProcessor.SendPutWithBatchAsync(task, stateForUpdate, task.RobotTargetAddress, start, end);
                        stateForUpdate.ChangePalletPhase = 1;
                    }
                    // Phase 3: å–假电芯(从源地址,从 targetNormalCount+1 å¼€å§‹é€’增)
                    else if (stateForUpdate.ChangePalletPhase == 3)
                    {
                        int fakeCount = targetTotal - targetNormalCount;
                        int completedFake = Math.Max(0, currentCompletedCount - targetNormalCount);
                        int remainingFake = fakeCount - completedFake;
                        if (remainingFake <= 0)
                        {
                            stateForUpdate.ChangePalletPhase = 0;
                            stateForUpdate.CurrentBatchIndex = 1;
                            stateForUpdate.IsInFakeBatteryMode = false;
                            _logger.LogInformation("HandlePutFinishedStateAsync:流向B完成,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        int pickCount = Math.Min(4, remainingFake);
                        var (start, end) = _taskProcessor.BuildBatchRange(stateForUpdate.CurrentBatchIndex, pickCount);
                        await _taskProcessor.SendPickWithBatchAsync(task, stateForUpdate, task.RobotSourceAddress, start, end);
                        stateForUpdate.CurrentBatchIndex += pickCount;
                        stateForUpdate.ChangePalletPhase = 4;
                    }
                    // Phase 4: æ”¾å‡ç”µèŠ¯åˆ°5号位(使用 PositionIndex)
                    else if (stateForUpdate.ChangePalletPhase == 4)
                    {
                        int fakeCount = targetTotal - targetNormalCount;
                        int completedFake = Math.Max(0, currentCompletedCount - targetNormalCount);
                        int remainingFake = fakeCount - completedFake;
                        if (remainingFake <= 0)
                        {
                            stateForUpdate.ChangePalletPhase = 0;
                            stateForUpdate.CurrentBatchIndex = 1;
                            stateForUpdate.IsInFakeBatteryMode = false;
                            _logger.LogInformation("HandlePutFinishedStateAsync:流向B完成,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        // æµå‘A:从5号位取假电芯
                        var positions = _taskProcessor.GetNextAvailableFakeBatteryPositions(Math.Min(4, remainingFake));
                        if (positions.Count == 0)
                        {
                            _logger.LogError("HandlePutFinishedStateAsync:无可用假电芯点位,任务号: {TaskNum}", task.RobotTaskNum);
                            return;
                        }
                        int start = positions.Min();
                        int end = positions.Max();
                        await _taskProcessor.SendPutWithBatchAsync(task, stateForUpdate, "5", start, end);
                        stateForUpdate.ChangePalletPhase = 3;
                        await _taskProcessor.SendSocketRobotFakeBatteryPickAsync(task, stateForUpdate, positions);
                    }
                    else
                    {
                        // æµå‘B:从源地址取假电芯
                        int pickCount = Math.Min(4, remainingFake);
                        var (start, end) = _taskProcessor.BuildBatchRange(stateForUpdate.CurrentBatchIndex, pickCount);
                        await _taskProcessor.SendPickWithBatchAsync(task, stateForUpdate, task.RobotSourceAddress, start, end);
                        stateForUpdate.CurrentBatchIndex += pickCount;
                    }
                    // å‘完 Pick åŽåˆ‡æ¢åˆ° Phase=4,等 PickFinished è§¦å‘ HandlePickFinishedStateAsync ä¸‹å‘放货指令
                    stateForUpdate.ChangePalletPhase = 4;
                    _stateManager.TryUpdateStateSafely(ipAddress, stateForUpdate);
                    return;
                }
                // ==================== Phase 5: å®Œæˆå…¥åº“(allputfinished / allpickfinished è§¦å‘)====================
                if (stateForUpdate.ChangePalletPhase == 5)
                {
                    // Phase 5 ç”± allpickfinished/allputfinished è§¦å‘入库,本方法不再下发指令
                    return;
                }
            }
            else
Code/WCS/WIDESEAWCS_Server/docs/»»ÅÌÈÎÎñÁ÷³Ìͼ.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,291 @@
# æ¢ç›˜ä»»åŠ¡å®Œæ•´æµç¨‹åˆ†æž
## ä»»åŠ¡å‚æ•°
| å‚æ•° | å€¼ |
|------|-----|
| æµå‘ | **Flow A** (isFlowA = true, æºåœ°å€ LineCode = "11001") |
| æºåœ°å€ | 11001 |
| ç›®æ ‡åœ°å€ | 2101 |
| æ­£å¸¸ç”µèŠ¯æ•°é‡ | **33** |
| å‡ç”µèŠ¯æ•°é‡ | 48 - 33 = **15** |
| ç›®æ ‡æ€»æ•° | 48 |
---
## é˜¶æ®µçŠ¶æ€å®šä¹‰
| Phase | å«ä¹‰ |
|-------|------|
| 0 | æœªå¼€å§‹ |
| 1 | å–正常电芯 |
| 2 | æ”¾æ­£å¸¸ç”µèН |
| 3 | å–假电芯(Flow A: ä»Ž5号位取) |
| 4 | æ”¾å‡ç”µèŠ¯ï¼ˆFlow A: æ”¾åˆ°ç›®æ ‡æ‰˜ç›˜ï¼‰ |
| 5 | ç­‰å¾…入库完成 |
---
## å®Œæ•´ä»£ç æ‰§è¡Œæµç¨‹ï¼ˆ33 æ­£å¸¸ + 15 å‡ = 48 æ€»è®¡ï¼‰
### åˆå§‹çŠ¶æ€
```
ChangePalletPhase = 0
currentCompletedCount = 0
targetNormalCount = 33
```
---
### ç¬¬1轮:Pick 1-4 æ­£å¸¸ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 1 | HandlePutFinishedStateAsync | 1 | 0 | 33 - 0 = **33** | PickBatch(1, 4) â†’ Phase=2 |
| 2 | (机械手执行取货) | - | - | - | - |
| 3 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished, task状态=RobotPickFinish |
| 4 | HandlePickFinishedStateAsync | 2 | 0 | 33 - 0 = **33** | PutBatch(1, 4) â†’ Phase=1 |
| 5 | (机械手执行放货) | - | - | - | - |
| 6 | HandlePutFinishedAsync | - | **4** | - | currentCompletedCount+=4, task.RobotTaskTotalNum-=4 |
---
### ç¬¬2轮:Pick 5-8 æ­£å¸¸ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 7 | HandlePutFinishedStateAsync | 1 | 4 | 33 - 4 = **29** | PickBatch(5, 8) â†’ Phase=2 |
| 8 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 9 | HandlePickFinishedStateAsync | 2 | 4 | 33 - 4 = **29** | PutBatch(1, 4) â†’ Phase=1 |
| 10 | HandlePutFinishedAsync | - | **8** | - | currentCompletedCount+=4 |
---
### ç¬¬3轮:Pick 9-12 æ­£å¸¸ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 11 | HandlePutFinishedStateAsync | 1 | 8 | 33 - 8 = **25** | PickBatch(9, 12) â†’ Phase=2 |
| 12 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 13 | HandlePickFinishedStateAsync | 2 | 8 | 33 - 8 = **25** | PutBatch(1, 4) â†’ Phase=1 |
| 14 | HandlePutFinishedAsync | - | **12** | - | currentCompletedCount+=4 |
---
### ç¬¬4轮:Pick 13-16 æ­£å¸¸ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 15 | HandlePutFinishedStateAsync | 1 | 12 | 33 - 12 = **21** | PickBatch(13, 16) â†’ Phase=2 |
| 16 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 17 | HandlePickFinishedStateAsync | 2 | 12 | 33 - 12 = **21** | PutBatch(1, 4) â†’ Phase=1 |
| 18 | HandlePutFinishedAsync | - | **16** | - | currentCompletedCount+=4 |
---
### ç¬¬5轮:Pick 17-20 æ­£å¸¸ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 19 | HandlePutFinishedStateAsync | 1 | 16 | 33 - 16 = **17** | PickBatch(17, 20) â†’ Phase=2 |
| 20 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 21 | HandlePickFinishedStateAsync | 2 | 16 | 33 - 16 = **17** | PutBatch(1, 4) â†’ Phase=1 |
| 22 | HandlePutFinishedAsync | - | **20** | - | currentCompletedCount+=4 |
---
### ç¬¬6轮:Pick 21-24 æ­£å¸¸ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 23 | HandlePutFinishedStateAsync | 1 | 20 | 33 - 20 = **13** | PickBatch(21, 24) â†’ Phase=2 |
| 24 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 25 | HandlePickFinishedStateAsync | 2 | 20 | 33 - 20 = **13** | PutBatch(1, 4) â†’ Phase=1 |
| 26 | HandlePutFinishedAsync | - | **24** | - | currentCompletedCount+=4 |
---
### ç¬¬7轮:Pick 25-28 æ­£å¸¸ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 27 | HandlePutFinishedStateAsync | 1 | 24 | 33 - 24 = **9** | PickBatch(25, 28) â†’ Phase=2 |
| 28 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 29 | HandlePickFinishedStateAsync | 2 | 24 | 33 - 24 = **9** | PutBatch(1, 4) â†’ Phase=1 |
| 30 | HandlePutFinishedAsync | - | **28** | - | currentCompletedCount+=4 |
---
### ç¬¬8轮:Pick 29-32 æ­£å¸¸ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 31 | HandlePutFinishedStateAsync | 1 | 28 | 33 - 28 = **5** | PickBatch(29, 32) â†’ Phase=2 |
| 32 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 33 | HandlePickFinishedStateAsync | 2 | 28 | 33 - 28 = **5** | PutBatch(1, 4) â†’ Phase=1 |
| 34 | HandlePutFinishedAsync | - | **32** | - | currentCompletedCount+=4 |
---
### ç¬¬9轮:Pick 33 æ­£å¸¸ç”µèŠ¯ï¼ˆæœ€åŽ1个)
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 35 | HandlePutFinishedStateAsync | 1 | 32 | 33 - 32 = **1** | PickBatch(33, 33) â†’ Phase=2 |
| 36 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 37 | HandlePickFinishedStateAsync | 2 | 32 | 33 - 32 = **1** | PutBatch(33, 33) â†’ Phase=1 |
| 38 | HandlePutFinishedAsync | - | **33** | - | currentCompletedCount+=1 |
---
### ç¬¬10轮:正常电芯全部完成 â†’ åˆ‡æ¢ Phase 3
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingNormal | æ“ä½œ |
|------|------|-------|---------------------|----------------|------|
| 39 | HandlePutFinishedStateAsync | 1 | **33** | 33 - 33 = **0** â†’ â‰¤0 | **切换 Phase=3**, Pick fake from 5号位 |
> **Flow A ç‰¹æ€§**:正常电芯取完后,源托盘(11001)已空。此时机械手携带最后一批正常电芯放到目标托盘后,源托盘自动空出回库。代码中不需要显式处理这个回库动作——由输送线自动完成。
---
### ç¬¬11轮:Pick 1-4 å‡ç”µèŠ¯ï¼ˆä»Ž5号位)
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingFake | æ“ä½œ |
|------|------|-------|---------------------|--------------|------|
| 40 | HandlePutFinishedStateAsync | 3 | 33 | 15 - 0 = **15** | Pick fake from 5号位(1,4) â†’ Phase=4 |
| 41 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 42 | HandlePickFinishedStateAsync | 4 | 33 | 15 - 0 = **15** | Put fake to target(BatchStart=34, 4个) â†’ Phase=3 |
| 43 | HandlePutFinishedAsync | - | **37** | - | currentCompletedCount+=4 |
---
### ç¬¬12轮:Pick 5-8 å‡ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingFake | æ“ä½œ |
|------|------|-------|---------------------|--------------|------|
| 44 | HandlePutFinishedStateAsync | 3 | 37 | 15 - 4 = **11** | Pick fake from 5号位(5,8) â†’ Phase=4 |
| 45 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 46 | HandlePickFinishedStateAsync | 4 | 37 | 15 - 4 = **11** | Put fake to target(BatchStart=38, 4个) â†’ Phase=3 |
| 47 | HandlePutFinishedAsync | - | **41** | - | currentCompletedCount+=4 |
---
### ç¬¬13轮:Pick 9-12 å‡ç”µèН
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingFake | æ“ä½œ |
|------|------|-------|---------------------|--------------|------|
| 48 | HandlePutFinishedStateAsync | 3 | 41 | 15 - 8 = **7** | Pick fake from 5号位(9,12) â†’ Phase=4 |
| 49 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 50 | HandlePickFinishedStateAsync | 4 | 41 | 15 - 8 = **7** | Put fake to target(BatchStart=42, 4个) â†’ Phase=3 |
| 51 | HandlePutFinishedAsync | - | **45** | - | currentCompletedCount+=4 |
---
### ç¬¬14轮:Pick 13-15 å‡ç”µèŠ¯ï¼ˆæœ€åŽ3个)
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingFake | æ“ä½œ |
|------|------|-------|---------------------|--------------|------|
| 52 | HandlePutFinishedStateAsync | 3 | 45 | 15 - 12 = **3** | Pick fake from 5号位(13,15) â†’ Phase=4 |
| 53 | HandlePickFinishedAsync | - | - | - | CurrentAction=PickFinished |
| 54 | HandlePickFinishedStateAsync | 4 | 45 | 15 - 12 = **3** | Put fake to target(BatchStart=46, 3个) â†’ Phase=3 |
| 55 | HandlePutFinishedAsync | - | **48** | - | currentCompletedCount+=3 |
---
### ç¬¬15轮:假电芯全部完成 â†’ åˆ‡æ¢ Phase 5
| æ­¥éª¤ | æ–¹æ³• | Phase | currentCompletedCount | remainingFake | æ“ä½œ |
|------|------|-------|---------------------|--------------|------|
| 56 | HandlePutFinishedStateAsync | 3 | **48** | 15 - 15 = **0** â†’ â‰¤0 | **切换 Phase=5**, ç­‰å¾…入库 |
---
### ç¬¬16轮:全部放货完成 â†’ å…¥åº“
| æ­¥éª¤ | æ–¹æ³• | Phase | æ“ä½œ |
|------|------|-------|------|
| 57 | HandlePutFinishedAsync | - | PutFinished â†’ allputfinished |
| 58 | ExecuteAsync | - | æ£€æµ‹ allputfinished â†’ RobotSimpleCommandHandler å¤„理 |
| 59 | RobotSimpleCommandHandler | **5** | Phase=5 â†’ è°ƒç”¨ HandleInboundTaskAsync(useSourceAddress=false) å…¥åº“到 **HCSC1** |
| 60 | - | - | åˆ é™¤ä»»åŠ¡è®°å½•ï¼Œé‡ç½®çŠ¶æ€ï¼Œå‘é€ "Group,diskFinished" |
---
## æ•°æ®æµæ±‡æ€»
### currentCompletedCount å˜åŒ–
```
0 â†’ 4 â†’ 8 â†’ 12 â†’ 16 â†’ 20 â†’ 24 â†’ 28 â†’ 32 â†’ 33 â†’ 37 â†’ 41 â†’ 45 â†’ 48
 |    |    |    |    |    |    |    |    |    |    |    |    |    |
 æ­£å¸¸ç”µèŠ¯æ‰¹æ¬¡                  æ­£å¸¸å®Œæˆ   å‡ç”µèŠ¯æ‰¹æ¬¡                      æ€»å®Œæˆ
 (每批4个)                     +0        (每批4个)                    +3
```
### Phase åˆ‡æ¢å›¾
```
Phase=0 â”€â”€(初始化)──► Phase=1 â”€â”€â–º Phase=2 â”€â”€â–º Phase=1 â”€â”€â–º ... â”€â”€â–º Phase=1
                       (Pick 1-4)   (Put 1-4)    (Pick 5-8)         (Pick 33)
                                                    â†“
                                              Phase=2 â”€â”€â–º ... â”€â”€â–º Phase=2
                                              (Put 5-8)              (Put 33)
                                                    â†“
                               â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
                               â”‚        Phase=1  (remainingNormal=0)   â”‚
                               â”‚              â†“                      â”‚
                               â”‚        Phase=3 (取假电芯)             â”‚
                               â”‚     Pick fake â†’ Phase=4 â†’ Put fake    â”‚
                               â”‚              â†º                        â”‚
                               â”‚        ç›´åˆ° remainingFake=0          â”‚
                               â”‚              â†“                      â”‚
                               â”‚        Phase=5 (入库)               â”‚
                               â””──────────────────────────────────────┘
```
### å…³é”®æ•°æ®å¯¹ç…§è¡¨
| è½®æ¬¡ | Phase | æ“ä½œ | currentCompletedCount | remainingNormal | remainingFake | å‘送指令 |
|------|-------|------|---------------------|----------------|--------------|---------|
| 1 | 1→2 | Pick 1-4 | 0 | 33 | - | PickBatch(1,4) |
| 1 | 2→1 | Put 1-4 | 4 | 33 | - | PutBatch(1,4) |
| 2 | 1→2 | Pick 5-8 | 4 | 29 | - | PickBatch(5,8) |
| 2 | 2→1 | Put 5-8 | 8 | 29 | - | PutBatch(1,4) |
| 3 | 1→2 | Pick 9-12 | 8 | 25 | - | PickBatch(9,12) |
| 3 | 2→1 | Put 9-12 | 12 | 25 | - | PutBatch(1,4) |
| 4 | 1→2 | Pick 13-16 | 12 | 21 | - | PickBatch(13,16) |
| 4 | 2→1 | Put 13-16 | 16 | 21 | - | PutBatch(1,4) |
| 5 | 1→2 | Pick 17-20 | 16 | 17 | - | PickBatch(17,20) |
| 5 | 2→1 | Put 17-20 | 20 | 17 | - | PutBatch(1,4) |
| 6 | 1→2 | Pick 21-24 | 20 | 13 | - | PickBatch(21,24) |
| 6 | 2→1 | Put 21-24 | 24 | 13 | - | PutBatch(1,4) |
| 7 | 1→2 | Pick 25-28 | 24 | 9 | - | PickBatch(25,28) |
| 7 | 2→1 | Put 25-28 | 28 | 9 | - | PutBatch(1,4) |
| 8 | 1→2 | Pick 29-32 | 28 | 5 | - | PickBatch(29,32) |
| 8 | 2→1 | Put 29-32 | 32 | 5 | - | PutBatch(1,4) |
| 9 | 1→2 | Pick 33 | 32 | 1 | - | PickBatch(33,33) |
| 9 | 2→1 | Put 33 | 33 | 0 | - | PutBatch(33,33) |
| 10 | 1→**3** | **切换** | 33 | **0** | 15 | Pick fake(5号位 1-4) |
| 11 | 3→4 | Pick fake 1-4 | 33 | 0 | 15 | PickBattery,5,1-4 |
| 11 | 4→3 | Put fake 34-37 | 37 | 0 | 11 | PutBatch(34,37) |
| 12 | 3→4 | Pick fake 5-8 | 37 | 0 | 11 | PickBattery,5,5-8 |
| 12 | 4→3 | Put fake 38-41 | 41 | 0 | 7 | PutBatch(38,41) |
| 13 | 3→4 | Pick fake 9-12 | 41 | 0 | 7 | PickBattery,5,9-12 |
| 13 | 4→3 | Put fake 42-45 | 45 | 0 | 3 | PutBatch(42,45) |
| 14 | 3→4 | Pick fake 13-15 | 45 | 0 | 3 | PickBattery,5,13-15 |
| 14 | 4→3 | Put fake 46-48 | 48 | 0 | 0 | PutBatch(46,48) |
| 15 | 3→**5** | **切换** | 48 | 0 | **0** | ç­‰å¾…入库 |
| 16 | 5 | **入库** | - | - | - | allputfinished â†’ HandleInboundTaskAsync â†’ HCSC1 |
---
## ä»£ç èŒè´£å¯¹ç…§
| æ–¹æ³• | ä½•时调用 | èŒè´£ |
|------|---------|------|
| HandlePutFinishedStateAsync | `ExecuteAsync` æ£€æµ‹ PutFinished | å‘送 **Pick** æŒ‡ä»¤ï¼Œæ›´æ–° Phase |
| HandlePickFinishedStateAsync | `ExecuteAsync` æ£€æµ‹ PickFinished | å‘送 **Put** æŒ‡ä»¤ |
| HandlePickFinishedAsync | prefix command `pickfinished` | æ›´æ–°çŠ¶æ€ CurrentAction |
| HandlePutFinishedAsync | prefix command `putfinished` | æ›´æ–° currentCompletedCount è®¡æ•° |
| RobotSimpleCommandHandler | å‘½ä»¤ `allpickfinished` / `allputfinished` | è§¦å‘入库,清理状态 |
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/ÉÏλϵͳ¶Ô½Ó/¸ßÎÂ2³£ÎÂ1¼°»úеÊÖÉ豸Õ˺ÅÐÅÏ¢±í(1).xlsx
Binary files differ
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/»úеÊÖЭÒé/~$½»»¥Á÷³Ì±í(1).xlsx
Binary files differ