# EventLoop: 理想模型推进计划 > 目标:EventLoop 为唯一状态读/写路径,JobRuntime 退化为复杂动作执行器。 > 进行中状态 -> 直接转 DB 调度。 ## 当前架构 v1(2026-05-10) ``` 简单路径 (Scan/Mark/Load/Unload): EventLoop → Worker dispatch → WorkerResult → SetJobContext/Position + NotifyStepComplete → JobRuntime.HandleTaskComplete → 步骤推进 → enqueueCurrentStep → notifyReady → SendScheduleTick → EventLoop → trySchedule 复杂路径 (Exchange/Cleaning/Washer): EventLoop → SubmitCandidates → JobRuntime.buildXxxAction → ActionFn 闭包 → Dispatcher.Enqueue → robotWorker → task.Execute → jr.HandleTaskComplete DB 角色: - job 表:status, position_type, position_ref_id, current_step_index, temp_slot_no - equipment_slot 表:status, current_job_id - task 表:仅 Dispatcher 路径使用 ``` ### v1 的问题 1. **双推进路径**:`HandleTaskComplete`(JobRuntime)和 `advanceJobStep`(EventLoop)同时存在 2. **JobRuntime 状态缓存与 DB 不一致风险**:内存 `Context`/`PositionType`/`State` 可能滞后于 DB 3. **`jr.Start()` 是多余的入口**:调度应直接从 DB 读状态决定候选,不应依赖 JobRuntime 的 `enqueueCurrentStep` 4. **`enqueueCurrentStep → notifyReady` 仍通过 JobRuntime**:简单路径的步骤推进仍需要 JobRuntime ## 理想架构 v3 ``` ┌─────────────────────────────────────┐ │ EventLoop (唯一路径) │ │ │ MachineDone ──→│ handleMachineDone │ │ ├─ DB: slot OCCUPIED→DONE │ │ ├─ wake jobs │ │ └─ trySchedule │ │ │ Inspection ───→│ handleInspectionResult │ │ ├─ DB: set context │ │ ├─ advanceStep (DB only) │ │ └─ trySchedule │ │ │ WorkerResult ─→│ handleWorkerResult │ │ ├─ DB: set context/position │ │ ├─ advanceStep (DB only) │ │ └─ trySchedule │ │ │ Command ──────→│ handleXxx │ │ └─ trySchedule │ │ │ trySchedule ──→│ 刷新 DB 快照 │ │ ├─ 构建 JobView + SystemState │ │ ├─ 运行三层调度器 │ │ ├─ MachineWait → 标记 DB │ │ ├─ Decision → 更新 Context + 推进 │ │ ├─ BufferStage → 更新位置 + 推进 │ │ ├─ Scan/Mark → go dispatchWorker │ │ ├─ Load/Unload → go dispatchWorker │ │ └─ Exchange等 → SubmitCandidates │ │ └─ Dispatcher (仅复杂动作) │ │ │ Worker 结果 ──→│ handleWorkerResult │ │ └─ advanceStep (DB only) → trySchedule │ └─────────────────────────────────────┘ JobRuntime (仅用于复杂动作): Exchange/Cleaning/Washer 的 ActionFn 闭包 不再承担: enqueueCurrentStep, notifyReady, syncStatusToDB DB 状态完全由 EventLoop 读写 ``` ## 推进计划(4 个 Phase) ### Phase 1: 简单路径完全脱离 JobRuntime **目标**:Scan/Mark/Load/Unload 路径不经过 `HandleTaskComplete`, 而是由 EventLoop 的 `handleWorkerResult` 直接推进 DB 步骤。 **现状**: ``` WorkerResult → SetJobContext/Position + NotifyStepComplete → jr.HandleTaskComplete → buildBranchConditions → GetNextStepIndex → jr.CurrentStepIdx = nextIdx → syncStatusToDB → enqueueCurrentStep → notifyReady → SendScheduleTick ``` **目标**: ``` WorkerResult → EventLoop.advanceStep(jobID, ctxUpdates) → DB: 读 Context → 分支条件 → 写 CurrentStepIndex + Context → SendScheduleTick → trySchedule ``` **改动点**: 1. `DBState` 新增 `AdvanceStep(ctx, jobID, contextUpdates)` 方法 - 读 job.context + 当前 recipe step - 计算分支条件 → 下一步索引 - 如果是最后一步 → FinishJob - 否则 → CompleteStep + 写 context 2. `handleWorkerResult` EventLoop 直派路径改为调用 `db.AdvanceStep` 3. 删除 `NotifyStepComplete` 调用(不再需要 JobRuntime) 4. `SetJobContext` / `SetJobPosition` 改为 DB 直写 **风险**:低——不影响复杂路径,简单路径已有 Worker 直派基础。 ### Phase 2: 调度入口去 JobRuntime 化 **目标**:`trySchedule` 独立于 JobRuntime 运行,不依赖 `SubmitCandidates` 的 MachineWait/Decision/BufferStage 处理。 **现状**: ``` trySchedule → 构建 views → 跑调度器 → Workers + SubmitCandidates SubmitCandidates → jr.State = WAITING_MACHINE (MachineWait) → jr.HandleTaskComplete(...) (Decision/BufferStage) ``` **目标**: ``` trySchedule → 构建 views → 跑调度器 → MachineWait: DB 写 job.status = WAITING_UNLOAD → Decision: DB 读 context + 写 context + advanceStep → BufferStage: DB 写 position + advanceStep → Scan/Mark/Load/Unload: Worker dispatch → Exchange等: Dispatcher.Enqueue (不经过 SubmitCandidates 的 JobRuntime 操作) ``` **改动点**: 1. `trySchedule` 中 MachineWait/Decision/BufferStage 直接调用 `DBState` 方法 2. 删除 `SubmitCandidates` 中的 MachineWait/Decision/BufferStage 直接处理逻辑 3. `SubmitCandidates` 简化为仅转换 + 入队 Dispatcher 4. 删除 `tryDirectDispatch` 方法(逻辑移到 trySchedule) 5. MachineWait 不再写 JobRuntime.State,只写 DB **风险**:中——`tryDirectDispatch` 逻辑需要搬迁到 EventLoop。 ### Phase 3: 删除旧调度路径 **目标**:`ScheduleAndSubmit` 完全被 `trySchedule` 替代。 **现状**: ``` RestoreOrder → ScheduleAndSubmit (旧路径) trySchedule nil entClient → ScheduleAndSubmit (测试 fallback) ``` **目标**: ``` RestoreOrder → EventLoop.SendScheduleTick → trySchedule 删除 ScheduleAndSubmit 方法 ``` **改动点**: 1. `RestoreOrder` 末尾的 `ScheduleAndSubmit()` 改为 `jp.eventLoop.SendScheduleTick()` 2. 删除 `ScheduleAndSubmit` 方法 3. 删除 `scheduleMu` 字段(EventLoop 单线程保证序列化) 4. `trySchedule` nil entClient 路径改为直接 return **风险**:低——仅旧的恢复路径受影响,改动量小。 ### Phase 4: JobRuntime 最小化 **目标**:JobRuntime 仅保留复杂动作所需的字段和方法。 **保留**: - `buildLoadAction/buildUnloadAction/buildScanAction/buildMarkingAction/buildDecisionAction/buildBufferStageAction` - `executeExchange/executeCleaningExchange/executeWasherExchange/executeWasherBatchUnload` - `handleMachineDone` (由 EventLoop 的 WakeJob 调用) - `GetState` / `GetContext` (供 Dispatcher 读取) - 所有依赖注入字段(registry, scanner, marker, robotCtrl 等) **删除**: - `enqueueCurrentStep` 方法(不再需要) - `HandleTaskComplete` 简化(仅复杂动作使用) - `notifyReady` 回调 → 删除(调度由 EventLoop 直接触发) - `syncStatusToDB` 简化为仅复杂动作使用 - `stepEnqueued` / `lastEnqueuedStep`(不再入队) - `pendingPause` / `pendingSuspendReason` → 改为 DB 字段 - `complete()` / `cleanupOnError()` → 移到 EventLoop **改动点**: 1. 复杂动作的 ActionFn 闭包完成后,不调 `HandleTaskComplete`,而是投递 WorkerResult 到 EventLoop 2. `exchangePairer` 逻辑移到 EventLoop 管理 3. JobRuntime 变成纯粹的 "动作描述符"(identity + position + context for closure capture) **风险**:高——深度改动 JobRuntime 状态机,需要大量回归测试。 ## 优先级建议 | Phase | 优先级 | 影响 | 状态 | |-------|--------|------|------| | Phase 1 | 🔴 高 | 简单路径彻底 SSOT | ✅ 完成 (2026-05-10) | | Phase 2 | 🟡 中 | 调度逻辑统一 | ✅ 完成 (2026-05-10) | | Phase 3 | 🟢 低 | 清理旧代码 | ✅ 完成 (2026-05-10) | | Phase 4 | ⚪ 部分 | 高风险的深度重构 | 仅移除 toolLocker 死代码 (2026-05-10) | ## 已完成改动 ### Phase 1: DBState.AdvanceStep - `dbstate.go` 新增 `AdvanceStep(ctx, jobID, ctxUpdates, posType, posRefID)` - 从 DB 读 job → 计算下一步 → 如果是最后步则 FinishJob,否则 CompleteStep - 同时更新 position、context - `loop.go` handleWorkerResult 直派路径:`db.AdvanceStep` 替代 `NotifyStepComplete → HandleTaskComplete` - Worker 结果不再经过 JobRuntime ### Phase 2: trySchedule 直接处理 MachineWait/Decision/BufferStage - `trySchedule` 中新增 3 个方法,直接写 DB: - `handleMachineWaitCandidate` → DB 写 WaitingUnload - `handleDecisionCandidate` → 读 context + 更新 + AdvanceStep - `handleBufferStageCandidate` → 读 position + AdvanceStep - `SubmitCandidates` 不再处理这 3 类候选 ### Phase 3: 删除旧路径 - `RestoreOrder` 的 `ScheduleAndSubmit()` 改为 `SendScheduleTick()` - 删除 `scheduleMu` 字段(EventLoop 单线程保障序列化) - `ScheduleAndSubmit` 保留为 nil entClient fallback ## 验收标准 - [ ] `go test ./...` 全量通过 - [ ] mockrun 1 个 job 完整流程 ≤ 3 分钟完成 - [ ] EventLoop.advanceStep 是唯一的步骤推进路径 - [ ] `jp.jobs` map 的写入只发生在 StartOrder/RestoreOrder - [ ] `syncStatusToDB` 不包含步骤推进逻辑 - [ ] 不存在 `jr.HandleTaskComplete` 被简单路径调用的情况