Files
bj_power/bj_power_mes/docs/superpowers/specs/2026-05-12-washer-batch-load-unload-design.md
T

167 lines
7.2 KiB
Markdown
Raw Normal View History

# 高压清洗机批量上料/下料/换料设计
## 背景
高压清洗机(设备ID=5WASHER_HP)是2槽位 batch 设备。当前实现是"单次 Load + StartupWasher"模式,每次只上1个工件就启动清洗。
新需求:**灵活策略** — 有2个待清洗工件就上满2个再启动,只有1个就上1个启动。批量完成后逐槽位判断卸载/换料,有 Exchange 或 Load 就追加 StartupWasher。
## 核心决策
1. **启动策略**:灵活 — 有2个待清洗就上满2个再启动,只有1个就上1个启动
2. **完成信号**:保持 batch 机制,一个信号 = 所有在加工槽位同时完成
3. **上料方式**:连续上料,中间无需等待
4. **换料策略**:逐槽位判断 — 有排队就 Exchange,没有就只卸载
5. **启动判断**actions 中包含 Exchange 或 Load → 追加 StartupWasher;纯 Unload 不启动
## 方案选择
**方案 A:调度器层面批量编排**(已确认)
`trySchedule` dispatch 阶段,当发现 WASHER_HP 相关候选时,查询槽位状态和配对情况,编排 `[]RobotAction` 数组一次性 dispatch。
理由:
- 符合现有架构(调度器驱动,EventLoop 串行处理)
- 不修改 Station 接口或 MockPLC 核心逻辑
- `handleMachineDone` 保持只改状态不编排
## 设计细节
### 1. RobotAction 数组
**`HardwareWorker.Execute` 接口变更**
`Execute(ctx, RobotAction)` 改为 `Execute(ctx, []RobotAction)`
Worker 串行执行数组中的每个 action,任一失败则中止后续 action,返回结果。
**`RobotWorker` 适配**
```go
func (w *RobotWorker) Execute(ctx context.Context, actions []action.RobotAction) error {
for _, act := range actions {
if err := w.executeSingle(ctx, act); err != nil {
return err
}
}
return nil
}
```
### 2. StartupWasher 作为 RobotAction
新增 `StartupWasher` 类型,与 Load/Unload/Exchange 同级。Worker 执行时调用 PLC 启动信号。
```go
// action.RobotAction 中 Kind 字段新增值
case "startup_washer":
// 调用 robotCtrl.StartupWasher(ctx)
```
### 3. 上料编排
**`trySchedule` dispatch 阶段新增逻辑**
当候选任务包含 WASHER_HP Load 时:
1. 查询 WASHER_HP 槽位状态(空槽位数量)
2. 检查是否有第二个待清洗工件(`jobRuntimes``StepType_Load + ResourceType=WASHER_HP + OnBuffer`
3. 编排 `[]RobotAction`
| 条件 | actions |
|------|---------|
| 1个空槽位 + 1个候选 | Load + StartupWasher |
| 2个空槽位 + 2个候选 | Load1 + Load2 + StartupWasher |
| 2个空槽位 + 1个候选 | Load + StartupWasher |
### 4. 卸载/换料编排
**`trySchedule` dispatch 阶段新增逻辑**
当 dispatch 一个 WASHER_HP Unload 或 Exchange 时:
1. 检查 `batchMachines[machineID]` 是否为 true
2. 是 → 查询同设备另一个槽位状态
- 另一个槽位有 Done 工件 + 有配对 → 追加 Exchange
- 另一个槽位有 Done 工件 + 无配对 → 追加 Unload
- 另一个槽位空 + 有待上料工件 → 追加 Load
- 另一个槽位空 + 无待上料工件 → 不追加
3. 否 → 正常单 action
**完整场景表**
| 场景 | actions | 启动? |
|------|---------|--------|
| 2个 Done0个配对 | Unload + Unload | 否 |
| 2个 Done1个配对 | Exchange + Unload + StartupWasher | 是 |
| 2个 Done2个配对 | Exchange + Exchange + StartupWasher | 是 |
| 1个 Done0个配对 | Unload | 否 |
| 1个 Done1个配对 | Exchange + Load + StartupWasher | 是 |
**启动判断规则**actions 中包含 Exchange 或 Load → 追加 StartupWasher。纯 Unload 不追加。
### 5. handleWorkerResult 适配
Worker 返回结果后,`handleWorkerResult` 需要处理多 action 的结果:
- 每个 action 的状态变更(位置更新、步骤推进)按顺序处理
- Exchange 成功 → Done 工件放暂存台 + 新工件上料到槽位
- Unload 成功 → Done 工件放暂存台 + 释放槽位
- Load 成功 → 新工件上料到槽位
- StartupWasher 成功 → 无状态变更(仅 PLC 信号)
- 任一 action 失败 → 中止后续,已成功的 action 状态保留不变(不回滚),失败的 Job 标记为 ERROR
**EquipmentSlot 一致性**:每个 action 成功后立即更新 `equipment_slot` 表状态(通过 DBState),确保 DB SSOT 与实际状态一致。后续 action 失败时,已更新的槽位状态反映真实物理状态(工件已在槽位中或已离开)。
### 6. 时序保证
**不存在"等第二个工件"的中间态**。编排时已确定 Load 数量:
- 1个候选 → `Load + StartupWasher`(上1个就启动,不等第二个)
- 2个候选 → `Load1 + Load2 + StartupWasher`(上满2个再启动)
Worker 串行执行,Load1 完成后立即执行 Load2,中间无等待。不存在"上了1个但还没启动,等第二个候选出现"的状态。
### 7. 卸载优先级
`trySchedule` 发现 WASHER_HP 有 Done 槽位时,**立即处理**,不等待更多 Done 槽位出现。1个 Done 就 dispatch 对应的 Unload/Exchange2个 Done 就一起编排。batch 信号保证所有 Occupied 槽位同时变为 Done,不会出现"先1个 Done,稍后另1个 Done"的情况。
### 8. MarkAllOccupiedDone 对单槽位的正确性
`MarkAllOccupiedDone()` 将所有 Occupied 槽位标记为 Done。当只有1个 Occupied 槽位时,只标记那1个,空槽位不受影响。这与"1个 Done"场景一致,无需特殊处理。
### 9. MockPLC 适配
当前 MockPLC 的 `washerDone` atomic 标志是"首个上料触发计时"。新逻辑下:
- `StartupWasher` 作为独立 RobotAction,调用 `robotCtrl.StartupWasher(ctx)` 触发 PLC 启动信号
- MockPLC 收到启动信号后开始 4s 计时,到期后触发 WasherDone
- MockPLC 核心逻辑不变:`triggerMachineDone``case 5``washerDone` atomic 防重复触发逻辑保留
- `executeLoadToMachine``machineID == 5` 时调用 `StartupWasher` 的逻辑移除,改为由 `[]RobotAction` 编排决定何时调用
### 10. batch_unload 移除
当前 `candidateToRobotAction` 中有 `batch_unload` 逻辑(检查 `FindAllDoneJobsOnMachine` > 1 时批量卸载)。新逻辑下:
- 卸载/换料由 `trySchedule` dispatch 阶段逐槽位编排
- `batch_unload` 逻辑移除
- `executeBatchUnload` 方法保留(用于 Worker 串行执行多个 Unload action
## 改动文件清单
| 文件 | 改动 |
|------|------|
| `internal/eventloop/worker.go` | `Execute` 接口改为接收 `[]RobotAction` |
| `internal/processor/robot_worker.go` | 适配新接口,串行执行 action 数组 |
| `internal/eventloop/loop.go` | `trySchedule` dispatch 阶段新增 WASHER_HP 槽位查询 + 批量编排逻辑 |
| `internal/eventloop/loop.go` | `handleWorkerResult` 适配多 action 结果处理 |
| `internal/processor/robot_worker.go` | 移除 `executeLoadToMachine``machineID==5``StartupWasher` 调用 |
| `internal/robot/washer.go` | 新增 `StartupWasher` RobotAction 执行逻辑 |
| `internal/action/` | RobotAction Kind 新增 `"startup_washer"` |
## 不改动的部分
- `handleMachineDone` — 只改状态,不编排
- `WasherStation` — 槽位管理逻辑不变
- MockPLC 核心逻辑 — 不变
- 调度器 Generator/Filter/Policy — 候选任务生成逻辑不变