编辑 | blame | 历史 | 原始文档

换盘任务批次指令与双流向设计

概述

对换盘任务(ChangePallet)的取货/放货指令格式进行升级,支持批次编号和总数指令,并根据 RobotSourceAddressLineCode 区分两种流向。

背景

当前换盘任务实现(上一轮迭代)仅处理了 HandlePutFinishedStateAsync 中的假电芯补充逻辑,指令格式为简单的 Pickbattery,{地址}Pickbattery,5,{start}-{end}。现需要:

  1. 所有换盘取货/放货指令统一为批次格式 {Command},{位置},{start}-{end}
  2. 每批前发送总数指令 PickTotalNum,{N} / PutTotalNum,{N}
  3. 根据源地址线体编码区分两种完全不同的操作流向

指令格式

批次指令

指令类型 格式 示例
取货总数 PickTotalNum,{N} PickTotalNum,11
放货总数 PutTotalNum,{N} PutTotalNum,11
取货批次 Pickbattery,{位置},{start}-{end} Pickbattery,3,1-4
放货批次 Putbattery,{位置},{start}-{end} Putbattery,6,5-8
取货单个 Pickbattery,{位置},{n}-0 Pickbattery,3,11-0
放货单个 Putbattery,{位置},{n}-0 Putbattery,6,11-0
  • PickTotalNum/PutTotalNum 仅换盘任务发送,每批取/放之前都发
  • N = 真实电芯数量(即 task.RobotTaskTotalNum),机器人固件用此值判断托盘上正常电芯总数,两种流向均发送相同的 N 值
  • 每批最多4个,不满4个按实际数发,剩1个时 end=0

批次编号计算

BuildBatchRange(currentIndex, remaining)(start, end):
- remaining >= 4(currentIndex, currentIndex + 3)
- remaining > 1(currentIndex, currentIndex + remaining - 1)
- remaining == 1(currentIndex, 0)

示例(targetNormalCount=11):第1批 1-4,第2批 5-8,第3批 9-11
示例(targetNormalCount=9):第1批 1-4,第2批 5-8,第3批 9-0(单个,end=0)

两种流向

流向A:补假电芯到目标托盘

条件: RobotSourceAddressLineCode == "11001" || "11010"

场景: 目标托盘有 N 个正常电芯(N < 48),需从5号位假电芯托盘取假电芯补满48个。

阶段流转:

[取假电芯] Pickbattery,5,{positionIndex}        ← 从5号位取,编号用平面点位表PositionIndex
    ↓
[放假电芯] Putbattery,{目标地址},{N+1}-{N+4}    ← 放到目标托盘,编号从正常数+1递增
    ↓
重复直到补满48个

假电芯数量 = 48 - task.RobotTaskTotalNum

流向B:取正常电芯 + 回收假电芯

条件: RobotSourceAddressLineCode != "11001" && != "11010"

场景: 源托盘原本满48个(正常电芯 + 假电芯混合),先取走正常电芯放到目标托盘,再把源托盘上剩余的假电芯取出放回5号位。

阶段流转:

Phase 1 - 取正常电芯:
[取正常] Pickbattery,{源地址},{1}-{4}            ← 编号从1递增
    ↓
[放正常] Putbattery,{目标地址},{1}-{4}           ← 编号从1递增
    ↓
重复直到 N 个正常电芯全部取完

Phase 2 - 回收假电芯:
[取假电芯] Pickbattery,{源地址},{N+1}-{N+4}     ← 编号从正常数+1继续递增
    ↓
[放假电芯] Putbattery,5,{positionIndex}          ← 放回5号位,编号用平面点位表PositionIndex
    ↓
重复直到假电芯全部回收(48-N 个)

状态管理

RobotSocketState 新增字段

/// <summary>
/// 当前批次起始编号(用于递增计算取货/放货编号)
/// </summary>
public int CurrentBatchIndex { get; set; } = 1;

/// <summary>
/// 换盘任务当前阶段
/// </summary>
/// <remarks>
/// 0: 未开始
/// 1: 取正常电芯 / 取假电芯(流向A)
/// 2: 放正常电芯 / 放假电芯(流向A)
/// 3: 取假电芯(流向B Phase2)
/// 4: 放假电芯到5号位(流向B Phase2)
/// </remarks>
public int ChangePalletPhase { get; set; }

CurrentBatchIndex 生命周期

流向A:
- Phase 0→1:CurrentBatchIndex = 1(初始化,用于 PositionIndex 查询起点)
- Phase 1→2:不重置(放货编号从 targetNormalCount + 1 开始,由 Orchestrator 计算)
- Phase 2→1:CurrentBatchIndex += 本批数量(递增,下一批继续)
- 完成→0:CurrentBatchIndex = 1(重置)

流向B:
- Phase 0→1:CurrentBatchIndex = 1(取正常电芯从1开始)
- Phase 1→2:不重置(放货编号与取货编号一致)
- Phase 2→1:CurrentBatchIndex += 本批数量(递增)
- Phase 2→3(正常电芯取完,即 state.RobotTaskTotalNum >= targetNormalCount 时 putfinished 完成后触发):CurrentBatchIndex = targetNormalCount + 1(假电芯从正常数+1开始)
- Phase 3→4:不重置(放假电芯用 PositionIndex,由 Orchestrator 计算)
- Phase 4→3:CurrentBatchIndex += 本批数量(递增)
- 完成→0:CurrentBatchIndex = 1(重置)

阶段流转状态机

流向A:
Phase 0 → Phase 1(取假电芯from 5号位)→ Phase 2(放假电芯to 目标)→ Phase 1 → ... → Phase 0(完成)

流向B:
Phase 0 → Phase 1(取正常from 源)→ Phase 2(放正常to 目标)→ Phase 1 → ... → Phase 3(取假电芯from 源)→ Phase 4(放假电芯to 5号位)→ Phase 3 → ... → Phase 0(完成)

代码改动范围

文件 改动
RobotSocketState.cs +CurrentBatchIndex, +ChangePalletPhase
RobotTaskProcessor.cs +BuildBatchRange(), +SendPickWithBatchAsync(), +SendPutWithBatchAsync(), 修改现有假电芯方法
RobotWorkflowOrchestrator.cs 重写 ChangePallet 分支(HandlePutFinishedStateAsync + HandlePickFinishedStateAsync)
RobotPrefixCommandHandler.cs HandlePutFinishedAsync 中区分换盘阶段:假电芯放货不调用 ChangePalletAsync API,不递增 RobotTaskTotalNum;HandlePickFinishedAsync 中假电芯取货不调用拆盘 API
RobotSimpleCommandHandler.cs allpickfinished/allputfinished 中增加 ChangePalletPhase 守卫:仅当 Phase==0(所有阶段完成)时才触发入库和删除任务,中间阶段不处理
IFakeBatteryPositionService.cs +MarkAsAvailable(List<int> positions) 方法
FakeBatteryPositionService.cs +MarkAsAvailable 实现
IFakeBatteryPositionRepository.cs +MarkAsAvailable(List<int> positions) 方法
FakeBatteryPositionRepository.cs +MarkAsAvailable 实现(将指定点位 IsUsed 设为 false)

命令处理器交互

RobotPrefixCommandHandler 变更

pickfinished 处理:
- 当 ChangePalletPhase == 3(流向B取假电芯)时,不调用拆盘 API,仅更新状态
- 其他阶段保持现有逻辑

putfinished 处理:
- 判断流向:通过 state.CurrentTask.RobotSourceAddressLineCode 判断是流向A还是流向B
- 当 ChangePalletPhase == 2 且流向B(放正常电芯)时,正常调用 ChangePalletAsync API,state.RobotTaskTotalNum += positions.Length
- 当 ChangePalletPhase == 4(流向B放假电芯到5号位)时,不调用 API,不递增 RobotTaskTotalNum,调用 MarkAsAvailable(positions) 释放点位
- 当 ChangePalletPhase == 2 且流向A(放假电芯到目标)时,不调用 API,不递增 RobotTaskTotalNum

RobotSimpleCommandHandler 变更

allpickfinished / allputfinished:
- 增加守卫条件:仅当 state.ChangePalletPhase == 0(所有阶段完成)时,才执行入库回传和任务删除
- 中间阶段收到 allpickfinished/allputfinished 时,仅更新 state.CurrentAction,不触发入库逻辑
- 任务完成时额外重置:state.ChangePalletPhase = 0, state.CurrentBatchIndex = 1, state.IsInFakeBatteryMode = false

边界条件

  • task.RobotTaskTotalNum == 48:直接走原有逻辑,不进入批次模式
  • task.RobotTaskTotalNum == 0:流向A 补满48个假电芯;流向B 跳过 Phase 1/2,直接进入 Phase 3/4 回收48个假电芯
  • 假电芯平面点位不足:记录错误日志,中止当前批次
  • 假电芯点位碎片化:GetNextAvailable 要求同行连续,如果请求4个找不到,依次尝试3、2、1个
  • 批次编号溢出(超过48):不应发生,但需防御性检查