673 lines
20 KiB
Markdown
673 lines
20 KiB
Markdown
# OPERATION 复合步骤实现计划
|
||||
|
|
|
|||
|
|
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|||
|
|
|
|||
|
|
**Goal:** 将 LOAD/EXECUTE/UNLOAD 三元组合并为 OPERATION 步骤,24 步 → 12 步
|
|||
|
|
|
|||
|
|
**Architecture:** 新增 OPERATION step_type,Generator 根据 job.RawStatus 分派 Load/MachineWait/Unload 候选;事件循环中 Load/MachineDone 不再调 AdvanceStep,改为 SetJobProcessing/SetJobWaitingUnload 原地改状态;Unload 完成后才 AdvanceStep 推进
|
|||
|
|
|
|||
|
|
**Tech Stack:** Go 1.25, ent ORM, PostgreSQL
|
|||
|
|
|
|||
|
|
**Spec:** `docs/superpowers/specs/2026-05-16-operation-step-design.md`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Chunk 1: 常量与 Schema 数据
|
|||
|
|
|
|||
|
|
### Task 1: 更新 step_type 常量
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `constants/constants.go:88-102`
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 新增 OPERATION,删除 LOAD/MACHINING/WASH/DEBURR/RUST_WASH/INSPECTION**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// constants.go StepType 枚举
|
|||
|
|
StepType_Operation StepType = "OPERATION"
|
|||
|
|
StepType_BufferStage StepType = "BUFFER_STAGE"
|
|||
|
|
StepType_Decision StepType = "DECISION"
|
|||
|
|
StepType_FinalScan StepType = "FINAL_SCAN"
|
|||
|
|
StepType_Inspection StepType = "INSPECTION" // 保留:SAMPLING 用
|
|||
|
|
StepType_Judge StepType = "JUDGE"
|
|||
|
|
StepType_LaserMark StepType = "LASER_MARK"
|
|||
|
|
StepType_Unload StepType = "UNLOAD"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
注意:`StepType_Inspection` 保留——OP110 SAMPLING 的 resourceType 通过 `EquipmentTypeCode_Sampling` 区分,但 step_type 仍可为 OPERATION。`loadActionFor("SAMPLING")` 返回 `ActionLoadSampling`。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 更新 `ValidStepTypes()` 集合**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
func ValidStepTypes() []string {
|
|||
|
|
return []string{
|
|||
|
|
string(StepType_Operation),
|
|||
|
|
string(StepType_BufferStage),
|
|||
|
|
string(StepType_Decision),
|
|||
|
|
string(StepType_FinalScan),
|
|||
|
|
string(StepType_Judge),
|
|||
|
|
string(StepType_LaserMark),
|
|||
|
|
string(StepType_Unload),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
移除 `StepType_Load`、`StepType_Machining`、`StepType_Wash`、`StepType_Deburr`、`StepType_RustWash`。
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: 编译验证**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go build ./...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
预期:constants 包编译通过;references to deleted constants 的编译错误由后续 task 修复。
|
|||
|
|
|
|||
|
|
- [ ] **Step 4: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add constants/constants.go
|
|||
|
|
git commit -m "refactor: 新增 StepType_Operation,删除 LOAD/MACHINING/WASH/DEBURR/RUST_WASH"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Task 2: 重写 recipe step 数据
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `schema/tools/data.go` (recipe step 部分)
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 删除旧 24 步,插入新 12 步**
|
|||
|
|
|
|||
|
|
定位 `schema/tools/data.go` 中 recipe step 的 bulk insert 代码,替换为:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
steps := []struct {
|
|||
|
|
stepID, stepName, stepType, resourceType, nextStepDefault string
|
|||
|
|
stepIndex int
|
|||
|
|
nextStepBranches map[string]any
|
|||
|
|
processingParams map[string]any
|
|||
|
|
}{
|
|||
|
|
{1, "OP10", "CNC加工", "OPERATION", string(constants.EquipmentTypeCode_CNC), "OP20"},
|
|||
|
|
{2, "OP20", "高压清洗", "OPERATION", string(constants.EquipmentTypeCode_WashingMachine), "OP30"},
|
|||
|
|
{3, "OP30", "内窥镜检测", "OPERATION", string(constants.EquipmentTypeCode_Inspection), "OP40"},
|
|||
|
|
{4, "OP40", "检测判断", "JUDGE", "", "OP50",
|
|||
|
|
map[string]any{"MEASURE_NG": "OP120"},
|
|||
|
|
map[string]any{"decisionType": "negate_bool", "setKey": "MEASURE_NG", "inputKey": "inspectionPass"},
|
|||
|
|
},
|
|||
|
|
{5, "OP50", "热能去毛刺", "OPERATION", string(constants.EquipmentTypeCode_Deburr), "OP60"},
|
|||
|
|
{6, "OP60", "除锈清洗", "OPERATION", string(constants.EquipmentTypeCode_CleaningLine), "OP70"},
|
|||
|
|
{7, "OP70", "终检扫码", "FINAL_SCAN", "", "OP80"},
|
|||
|
|
{8, "OP80", "打标判断", "DECISION", "", "OP90",
|
|||
|
|
map[string]any{"SKIP_MARK": "OP120", "NEED_MARKING": "OP90"}, // NEED_MARKING 为 truthy → OP90, else → OP120
|
|||
|
|
nil, // decisionType 由 FINAL_SCAN 步骤的 scan_mismatch 结果驱动
|
|||
|
|
},
|
|||
|
|
{9, "OP90", "激光打标", "LASER_MARK", "", "OP100"},
|
|||
|
|
{10, "OP100", "抽检判断", "DECISION", "", "OP120",
|
|||
|
|
map[string]any{"NEED_SAMPLING": "OP110"},
|
|||
|
|
map[string]any{"decisionType": "noop"}, // 规则由前置步骤 context 决定
|
|||
|
|
},
|
|||
|
|
{11, "OP110", "抽检", "OPERATION", string(constants.EquipmentTypeCode_Sampling), "OP120"},
|
|||
|
|
{12, "OP120", "下料接驳台", "UNLOAD", "", ""},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**分支逻辑说明:**
|
|||
|
|
- OP40 (JUDGE): `inspectionPass=false` → nextStepBranches 匹配 `MEASURE_NG` → OP120(跳过后续,直接下料)。`inspectionPass=true` → nextStepDefault → OP50。
|
|||
|
|
- OP80 (DECISION): `NEED_MARKING` 由 OP70 FINAL_SCAN 的 scan_mismatch 判定设置。存在 → OP90,不存在(skip)→ OP120。
|
|||
|
|
- OP100 (DECISION): `NEED_SAMPLING` 由外部判定设置。存在 → OP110,不存在 → OP120。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 重新生成 ent 代码**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd schema && go generate
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: 清空 job 表(dev 环境无旧数据兼容)**
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
DELETE FROM job;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 4: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add schema/tools/data.go schema/ ent/
|
|||
|
|
git commit -m "refactor: recipe 步骤改为 12 步 OPERATION 模型"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Chunk 2: DBState 新增方法
|
|||
|
|
|
|||
|
|
### Task 3: 新增 SetJobProcessing / SetJobWaitingUnload
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `internal/eventloop/dbstate.go`
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 添加 SetJobProcessing**
|
|||
|
|
|
|||
|
|
在 `DBState` 上新增方法——设置 job 为 PROCESSING 状态,记录位置:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// SetJobProcessing 标记工件已上料到设备,开始加工
|
|||
|
|
func (d *DBState) SetJobProcessing(ctx context.Context, jobID, equipmentID, slotNo int) error {
|
|||
|
|
_, err := d.client.Job.UpdateOneID(jobID).
|
|||
|
|
SetStatus(constants.JobStatus_Processing).
|
|||
|
|
SetPositionType(constants.PositionType_OnEquipment).
|
|||
|
|
SetPositionRefId(fmt.Sprintf("%d:%d", equipmentID, slotNo)).
|
|||
|
|
AddVersion(1).
|
|||
|
|
Save(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("set job %d processing: %w", jobID, err)
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 添加 SetJobWaitingUnload**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// SetJobWaitingUnload 标记设备加工完成,等待机器人卸料
|
|||
|
|
func (d *DBState) SetJobWaitingUnload(ctx context.Context, jobID int) error {
|
|||
|
|
_, err := d.client.Job.UpdateOneID(jobID).
|
|||
|
|
SetStatus(constants.JobStatus_WaitingUnload).
|
|||
|
|
AddVersion(1).
|
|||
|
|
Save(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("set job %d waiting unload: %w", jobID, err)
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add internal/eventloop/dbstate.go
|
|||
|
|
git commit -m "feat: 新增 SetJobProcessing / SetJobWaitingUnload"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Chunk 3: 调度器
|
|||
|
|
|
|||
|
|
### Task 4: JobView 新增 RawStatus,buildJobViews 填充
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `internal/scheduler/types.go:14-21`
|
|||
|
|
- Modify: `internal/eventloop/scheduler_bridge.go:buildJobViews`
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: JobView 新增字段**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type JobView struct {
|
|||
|
|
JobID int
|
|||
|
|
OrderID int
|
|||
|
|
ProductID int
|
|||
|
|
CurrentStep StepView
|
|||
|
|
State JobViewState
|
|||
|
|
RawStatus string // "ON_BUFFER" / "PROCESSING" / "WAITING_UNLOAD"
|
|||
|
|
Context map[string]any
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: buildJobViews 填充 RawStatus**
|
|||
|
|
|
|||
|
|
在 `scheduler_bridge.go` 的 `buildJobViews` 中,构建 JobView 时填充:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
views = append(views, scheduler.JobView{
|
|||
|
|
// ...existing...
|
|||
|
|
State: state,
|
|||
|
|
RawStatus: snap.Status, // 新增
|
|||
|
|
Context: snap.Context,
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add internal/scheduler/types.go internal/eventloop/scheduler_bridge.go
|
|||
|
|
git commit -m "feat: JobView 新增 RawStatus,buildJobViews 填充"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Task 5: Generator 新增 OPERATION case
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `internal/scheduler/generator.go:66-86`
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 删除旧的 LOAD/UNLOAD/MACHINING/WASH/... case,新增 OPERATION**
|
|||
|
|
|
|||
|
|
替换 `generateFromStep` 中的 switch 分支:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
case constants.StepType_Operation:
|
|||
|
|
switch job.RawStatus {
|
|||
|
|
case string(constants.JobStatus_OnBuffer):
|
|||
|
|
candidates = append(candidates, CandidateTask{
|
|||
|
|
JobID: job.JobID,
|
|||
|
|
OrderID: job.OrderID,
|
|||
|
|
StepIndex: step.Index,
|
|||
|
|
Action: g.loadActionFor(step.ResourceType),
|
|||
|
|
TargetID: 0, // trySchedule 中 findIdleMachine 分配
|
|||
|
|
Priority: PriorityNormal,
|
|||
|
|
})
|
|||
|
|
case string(constants.JobStatus_Processing):
|
|||
|
|
candidates = append(candidates, CandidateTask{
|
|||
|
|
JobID: job.JobID,
|
|||
|
|
OrderID: job.OrderID,
|
|||
|
|
StepIndex: step.Index,
|
|||
|
|
Action: ActionLoadCNC, // 占位
|
|||
|
|
TargetID: step.TargetID,
|
|||
|
|
Priority: PriorityNormal,
|
|||
|
|
Meta: map[string]any{"machineWait": true},
|
|||
|
|
})
|
|||
|
|
case string(constants.JobStatus_WaitingUnload):
|
|||
|
|
candidates = append(candidates, CandidateTask{
|
|||
|
|
JobID: job.JobID,
|
|||
|
|
OrderID: job.OrderID,
|
|||
|
|
StepIndex: step.Index,
|
|||
|
|
Action: g.unloadActionFor(step.ResourceType),
|
|||
|
|
TargetID: step.TargetID,
|
|||
|
|
Priority: PriorityHigh,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
删除旧 case:`StepType_Load`、`StepType_Unload`、`StepType_Machining`/`StepType_Wash`/`StepType_Deburr`/`StepType_RustWash`。
|
|||
|
|
|
|||
|
|
保留其他 case:`StepType_BufferStage`、`StepType_FinalScan`、`StepType_Decision`/`StepType_Judge`、`StepType_LaserMark`。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 编译验证**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go build ./internal/scheduler/...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add internal/scheduler/generator.go
|
|||
|
|
git commit -m "feat: Generator 新增 OPERATION case,按 RawStatus 分派"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Task 6: findExchangePair 更新
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `internal/eventloop/scheduler_bridge.go:332-353` (findExchangePair)
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 将 `StepType_Load` 改为 `StepType_Operation && ON_BUFFER`**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
func (l *ProductionEventLoop) findExchangePair(unloadSnap *RuntimeSnapshot, excludeLoadJobs ...map[int]bool) int {
|
|||
|
|
if unloadSnap == nil {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
if unloadSnap.ResourceType != string(constants.EquipmentTypeCode_CNC) &&
|
|||
|
|
unloadSnap.ResourceType != string(constants.EquipmentTypeCode_WashingMachine) &&
|
|||
|
|
unloadSnap.ResourceType != string(constants.EquipmentTypeCode_CleaningLine) {
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
var excluded map[int]bool
|
|||
|
|
if len(excludeLoadJobs) > 0 {
|
|||
|
|
excluded = excludeLoadJobs[0]
|
|||
|
|
}
|
|||
|
|
for _, bufSnap := range l.jobRuntimes {
|
|||
|
|
if bufSnap.StepType == constants.StepType_Operation &&
|
|||
|
|
bufSnap.Status == string(constants.JobStatus_OnBuffer) &&
|
|||
|
|
bufSnap.PositionType == string(constants.PositionType_OnBuffer) &&
|
|||
|
|
bufSnap.ResourceType == unloadSnap.ResourceType {
|
|||
|
|
if excluded != nil && excluded[bufSnap.JobID] {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
return bufSnap.JobID
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add internal/eventloop/scheduler_bridge.go
|
|||
|
|
git commit -m "refactor: findExchangePair 改为匹配 StepType_Operation + ON_BUFFER"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Chunk 4: 事件循环 — Load/MachineDone 分支
|
|||
|
|
|
|||
|
|
### Task 7: dispatchWorker Load 完成后分流
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `internal/eventloop/worker_dispatch.go:100-160` (dispatchWorker Load 完成部分)
|
|||
|
|
- Modify: `internal/eventloop/loop.go:130-152` (handleWorkerResult Load 完成部分)
|
|||
|
|
- Modify: `internal/eventloop/worker_dispatch.go:313-350` (handleMultiActionResult load_to_machine case)
|
|||
|
|
|
|||
|
|
在三个位置,Load 完成后检测 stepType:
|
|||
|
|
- `dispatchWorker`: Load 成功后 `l.registry` 查槽位 → 如果是 OPERATION → `l.db.SetJobProcessing`
|
|||
|
|
- `handleWorkerResult`: 成功路径中 `act.IsLoad()` → 如果 stepType==OPERATION → 只改状态不调 AdvanceStep
|
|||
|
|
- `handleMultiActionResult`: `load_to_machine` case → 同样分流
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: dispatchWorker 中 Load 成功后的槽位同步 + 状态更新**
|
|||
|
|
|
|||
|
|
`dispatchWorker` 中 Load 成功后已经有从 Station 查槽位的逻辑。在此逻辑之后,检测 stepType:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// dispatchWorker 中,Load 成功后:
|
|||
|
|
if err == nil && l.registry != nil && ct.TargetID > 0 && ct.Action.IsLoad() {
|
|||
|
|
// ...existing slot query...
|
|||
|
|
if assignedSlot > 0 {
|
|||
|
|
l.db.SetEquipmentSlot(ctx, ct.TargetID, assignedSlot, Empty, Occupied, ct.JobID)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 新增:OPERATION 步骤不调 AdvanceStep
|
|||
|
|
snap := l.jobRuntimes[ct.JobID]
|
|||
|
|
if snap != nil && snap.StepType == constants.StepType_Operation && ct.Action.IsLoad() {
|
|||
|
|
l.db.SetJobProcessing(ctx, ct.JobID, ct.TargetID, assignedSlot)
|
|||
|
|
// 不调 AdvanceStep
|
|||
|
|
} else {
|
|||
|
|
// 原有 AdvanceStep 逻辑
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
但注意:`dispatchWorker` 只是派遣,不更新 job 状态。状态更新在 `handleWorkerResult` 中。所以关键在于 `handleWorkerResult` 中 Load 成功的分支。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: handleWorkerResult 中 Load 成功分流**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// handleWorkerResult: success 路径中 Load 分支
|
|||
|
|
case act.IsLoad():
|
|||
|
|
if l.isOperationStep(jobID) {
|
|||
|
|
// OPERATION: 不调 AdvanceStep,只改状态
|
|||
|
|
slot := intFromPayload(msg.Payload, "assignedSlot")
|
|||
|
|
if slot < 1 { slot = 1 }
|
|||
|
|
l.db.SetJobProcessing(ctx, jobID, targetID, slot)
|
|||
|
|
} else {
|
|||
|
|
// 原有逻辑:AdvanceStep
|
|||
|
|
newPosType = constants.PositionType_OnEquipment
|
|||
|
|
// ...
|
|||
|
|
l.db.AdvanceStep(ctx, jobID, ...)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
需要在 `ProductionEventLoop` 上加一个辅助方法:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
func (l *ProductionEventLoop) isOperationStep(jobID int) bool {
|
|||
|
|
snap := l.jobRuntimes[jobID]
|
|||
|
|
return snap != nil && snap.StepType == constants.StepType_Operation
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: handleMultiActionResult load_to_machine 分流**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
case "load_to_machine":
|
|||
|
|
if snap != nil && snap.StepType == constants.StepType_Operation {
|
|||
|
|
l.db.SetJobProcessing(ctx, jobID, targetID, assignedSlot)
|
|||
|
|
} else {
|
|||
|
|
// 原有 AdvanceStep 逻辑
|
|||
|
|
l.db.AdvanceStep(ctx, jobID, nil, OnEquipment, fmt.Sprintf("%d:%d", targetID, assignedSlot))
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 4: 编译验证**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go build ./internal/eventloop/...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 5: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add internal/eventloop/
|
|||
|
|
git commit -m "feat: Load 完成后 OPERATION 步骤调用 SetJobProcessing"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Task 8: MachineDone 分流
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `internal/eventloop/candidate_handlers.go:34-44` (handleMachineWaitCandidate)
|
|||
|
|
- Modify: `internal/eventloop/loop.go:327-341` (handleMachineDone 步骤推进部分)
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: handleMachineWaitCandidate OPERATION 分支**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
func (l *ProductionEventLoop) handleMachineWaitCandidate(ctx context.Context, ct scheduler.CandidateTask) {
|
|||
|
|
// 检测设备不变
|
|||
|
|
if l.inspectionMachines[ct.TargetID] {
|
|||
|
|
l.entClient.Job.UpdateOneID(ct.JobID).SetStatus(WaitingUnload).Save(ctx)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// 检查 slot Done
|
|||
|
|
if ct.TargetID > 0 {
|
|||
|
|
slot, _ := l.entClient.EquipmentSlot.Query().
|
|||
|
|
Where(equipmentslot.EquipmentIdEQ(ct.TargetID),
|
|||
|
|
equipmentslot.CurrentJobIdEQ(ct.JobID)).First(ctx)
|
|||
|
|
if slot == nil || slot.Status != constants.SlotStatus_Done {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 分流
|
|||
|
|
if l.isOperationStep(ct.JobID) {
|
|||
|
|
l.db.SetJobWaitingUnload(ctx, ct.JobID)
|
|||
|
|
} else {
|
|||
|
|
finished, err := l.db.AdvanceStep(ctx, ct.JobID, nil, "", "")
|
|||
|
|
if err != nil {
|
|||
|
|
slog.Error(...)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
if finished {
|
|||
|
|
l.checkOrderCompletion(ctx, ct.JobID)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
l.SendScheduleTick()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: handleMachineDone 中推进步骤分流**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// handleMachineDone 中 doneJobs 处理循环:
|
|||
|
|
for _, jobID := range doneJobs {
|
|||
|
|
job, _ := l.entClient.Job.Get(ctx, jobID)
|
|||
|
|
if job != nil && job.Status == constants.JobStatus_Processing {
|
|||
|
|
if l.isOperationStep(jobID) {
|
|||
|
|
l.db.SetJobWaitingUnload(ctx, jobID)
|
|||
|
|
} else {
|
|||
|
|
l.db.AdvanceStep(ctx, jobID, nil, "", "")
|
|||
|
|
l.entClient.Job.UpdateOneID(jobID).SetStatus(WaitingUnload).Save(ctx)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if l.jobOps != nil {
|
|||
|
|
l.jobOps.WakeJob(jobID)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add internal/eventloop/
|
|||
|
|
git commit -m "feat: MachineDone 时 OPERATION 步骤调用 SetJobWaitingUnload"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Chunk 5: 事件循环 — InspectionResult / Unload
|
|||
|
|
|
|||
|
|
### Task 9: handleInspectionDone OPERATION 分流
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `internal/eventloop/loop.go:377-413`
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 分流 pass 路径**
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
func (l *ProductionEventLoop) handleInspectionDone(ctx context.Context, machineID int, pass bool) {
|
|||
|
|
// ... slot/job 查询不变 ...
|
|||
|
|
|
|||
|
|
if pass {
|
|||
|
|
if l.isOperationStep(jobID) {
|
|||
|
|
l.db.CompleteStep(ctx, jobID, job.CurrentStepIndex,
|
|||
|
|
map[string]any{"inspectionPass": true})
|
|||
|
|
l.db.SetJobWaitingUnload(ctx, jobID)
|
|||
|
|
} else {
|
|||
|
|
finished, err := l.db.AdvanceStep(ctx, jobID,
|
|||
|
|
map[string]any{"inspectionPass": true}, "", "")
|
|||
|
|
if err != nil { ... }
|
|||
|
|
if finished { l.checkOrderCompletion(ctx, jobID) }
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 不合格路径不变
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
注意:pass=true 时需要用 `CompleteStep` 先记录 `inspectionPass` 上下文(不推进 stepIndex),因为 `SetJobWaitingUnload` 不更新 context。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add internal/eventloop/loop.go
|
|||
|
|
git commit -m "feat: handleInspectionDone OPERATION 步骤 pass→SetJobWaitingUnload"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Task 10: Unload 完成后保持 AdvanceStep
|
|||
|
|
|
|||
|
|
Unload 是 OPERATION 的出口,`handleWorkerResult` 和 `handleMultiActionResult` 中 Unload 成功的路径继续调用 `AdvanceStep`,不需要修改。确认现有代码正确即可。
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 验证 handleWorkerResult 中 Unload 路径不变**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
grep -n "IsUnload\|ActionExchange\|ActionUnloadDock" internal/eventloop/worker_dispatch.go
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
确认这些路径的 `AdvanceStep` 调用不受影响。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 验证 handleMultiActionResult 中 unload_to_buffer/exchange_full/unload_to_dock 路径不变**
|
|||
|
|
|
|||
|
|
同上。
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: Commit (no-op 确认)**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git commit --allow-empty -m "chore: 确认 Unload 完成后 AdvanceStep 路径不受 OPERATION 影响"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Chunk 6: 清洗机批量编排适配
|
|||
|
|
|
|||
|
|
### Task 11: Washer batch 中 Load/MachineDone 分流
|
|||
|
|
|
|||
|
|
**Files:**
|
|||
|
|
- Modify: `internal/eventloop/washer_orchestrator.go`
|
|||
|
|
- Modify: `internal/eventloop/worker_dispatch.go:handleMultiActionResult`
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 确认 buildWasherLoadActions 不变**
|
|||
|
|
|
|||
|
|
清洗机 Load 编排逻辑不变——仍然一次机器人行程上两个工件。变化在 Load 完成后的状态更新(已在 Task 7 handleMultiActionResult 中处理)。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 确认 buildWasherUnloadActions 不变**
|
|||
|
|
|
|||
|
|
清洗机 Unload 编排不变。Unload 完成后 AdvanceStep(已在 Task 10 确认不受影响)。
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git commit --allow-empty -m "chore: 确认清洗机批量编排兼容 OPERATION"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Chunk 7: 编译、测试、清理
|
|||
|
|
|
|||
|
|
### Task 12: 全量编译和修复编译错误
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 全量编译**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go build ./...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
预期:按 chunk 1-6 顺序实施后,此时应无编译错误。如有,逐个修复(主要是遗漏的常量引用)。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 运行 eventloop 测试**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go test ./internal/eventloop/ -count=1 -v
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: 运行 scheduler 测试**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go test ./internal/scheduler/ -count=1 -v
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 4: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add -A
|
|||
|
|
git commit -m "chore: 修复遗漏的编译错误,测试通过"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Task 13: Mock 运行联调
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 运行 mockrun 验证端到端流程**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go run cmd/mockrun/main.go
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
观察:
|
|||
|
|
- OPERATION 步骤 Load → PROCESSING → MachineDone → WAITING_UNLOAD → Unload → AdvanceStep
|
|||
|
|
- 检测步骤 InspectionResult 路径
|
|||
|
|
- 清洗机批量编排
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 验证 Exchange 配对**
|
|||
|
|
|
|||
|
|
观察 mockrun 日志中 CNC 换料是否正常。
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: Commit 任何 mockrun 发现的问题修复**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Task 14: 清理死代码
|
|||
|
|
|
|||
|
|
- [ ] **Step 1: 搜索残留的已删除常量引用**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
rg "StepType_Load|StepType_Machining|StepType_Wash|StepType_Deburr|StepType_RustWash" --type go
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
确认全部清理干净或只在测试 mock 中有意保留。
|
|||
|
|
|
|||
|
|
- [ ] **Step 2: 删除未使用的 import 和函数**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
go vet ./...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- [ ] **Step 3: Commit**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git add -A
|
|||
|
|
git commit -m "chore: 清理死代码和未使用 import"
|
|||
|
|
```
|