Kaynağa Gözat

docs(d2-02-t3): 自动对焦重构需求文档(修订版)——纠正旧spec+补全影响面

逐轮与用户确认后重写的权威需求文档,取代旧spec(commit 8036bcb,已随文档清理删除)。
关键纠正/新增(对照旧spec):
- D11 调试页只定/存范围,标定结果采集时才产生才存(scene=1);不存固定FocusZ
- D12 拍照N层位置以本地PhotoLayerCalculator为准+算完上传DB,服务器calCCDPosition退兜底
- D13 砍front-manament靠服务器评分的"对焦预览"窗;看胚胎切焦平面守"0层=焦面+层对称"
- §1.4 正视"合并改了一半的乱"L1-L6(死代码/双轨/半本地半云端/命名误导)+清理落点
- §八 全链路影响面:Java补well/update漏发MQTT+加列硬编码同步点;抠图SELECT禁区;calCCDPosition不砍

依据现/老两套operate+control+Java+管理端+aivfo_tl_setting库实读,关键处给file:line。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie 2 gün önce
ebeveyn
işleme
975a7dfcec

+ 265 - 0
项目文档/需求文档/specs/2026-06-25-D2-02-第三阶段-自动对焦重构-需求文档.md

@@ -0,0 +1,265 @@
+# D2-02 第三阶段 · 自动对焦重构 需求文档
+
+> 版本:2026-06-25(取代旧 spec commit 8036bcb,旧 spec 已随文档清理归档/删除)。
+> 性质:跨 operate / control / Java 微服务 / front-manament(C#管理端) / DB 五层的对焦重构。
+> 编写依据:现/老两套源码(`ivf_tl_operate_2.0` 现项目 + `临时文件/ivf_tl_operate_2.0`、`临时文件/ivf_tl_control_2.0` 老代码)+ Java 微服务 + `aivfo-front-manament-2.0` + `aivfo_tl_setting` 库实读,关键处给 file:line。
+> 状态:需求与方向已逐条与用户确认;待用户审本文档 → 转实现计划。
+
+---
+
+## 一、为什么做(背景)
+
+### 1.1 培养箱拍照的两个前提
+培养箱每舱 16 个 well,要给每个 well 自动拍延时照片。**拍清楚**需两件事对:相机对准孔中心(**水平电机**位)、镜头在正确清晰高度(**垂直电机** FocusZ)。因每台机器装配公差不同、每个孔也不同,必须实测标定。
+
+### 1.2 旧方式(要废)
+1. 工程师**手动**一个个调舱:手动移电机、看画面、记下清晰层 + 定好每个 well 水平位。
+2. 采集时在固定垂直清晰位**上下拍 N 层**。
+3. N 层图全部上传服务器,**服务器做清晰度评分**排序,选最清晰层当 0 层(赤道面)。
+4. 用户在管理端看胚胎时以该层为最清晰、可上下切焦平面。
+
+**痛点**:人工标定工作量大、难度高;对焦依赖事后云端评分,链路长。
+
+> 老链路依据:老 control `临时文件/ivf_tl_control_2.0/ivf_tl_Com/HouseBin.cs`——`StartAutoFocus`(:1351 取对焦起点)→`GetClearest`(:1456 上下拍N层)→每层 `UploadImageEvent`(:2359 传服务器评分)→`StartCCD`(:1385 `/ccd/position` 取最清晰层)。清晰层"服务器算、服务器存、设备取"。老调试页 `临时文件/ivf_tl_operate_2.0/.../HouseDebugPageViewModel.cs`**只有手动电机/光源/EEPROM 读写 + 清晰层抓图,无一键标定、无评分**。
+
+### 1.3 新方式(本次做)
+1. **调试页全自动标定为主、手动微调为辅**:进调试页 16 孔自动对焦跑一遍,哪孔不准就地微调;目的是**给每个 well 调出并存下"对焦电机运动范围"**。
+2. **采集升级版自动对焦**:触发后在范围内**实时跑四步算法**找当次最清晰焦面(取代"静态清晰位"),再上下拍 N 层。
+3. **服务器不再做清晰度评分**:最清晰层在"对焦那一刻"就定了,N 层不再上传评分选层。
+
+### 1.4 现状:这是个"合并改了一半的乱摊子"(设计必须正视)
+现 `ivf_tl_operate_2.0` = 老 operate/control + AutoFocusTool 对焦算法**合并改造**而来,但老的"服务器评分"链没拆干净,形成多处新老并存。核对老代码后确认的乱点(设计须一并理清):
+
+| # | 乱点 | 依据 |
+|---|---|---|
+| L1 | **对焦取数双轨**:本地 `CalibrationEngine` 算 FocusZ(新),但老 `GetAutoFocusServiceEvent`/`GetAutoFocusDBEvent` + `/autofocus/position` 服务器链作为死代码全保留 | 现 control HouseBin.cs:1480/1546 |
+| L2 | **拍照层位置半本地半云端割裂**:FocusZ 本地算了,但拍照 N 层位置**既有本地 `PhotoLayerCalculator` 又走服务器 `calCCDPosition`(/ccd/position)** 两套 | 现 HouseBin.cs:1838/2026 vs :1647 |
+| L3 | **本地对焦默认没真跑**:被安全门 `tl_setting.local_autofocus_enabled`(默认0) 锁着,走降级分支 | 现 HouseBin.cs:1499-1540 |
+| L4 | **拍层参数命名双轨**:新 `focus_layer_count/spacing_pulse/move_down_layer` 已用,老 `House.autoFocusNumber/verticalMotorSpacePulse` 旧列未清 | PhotoLayerCalculator.cs:36/94 |
+| L5 | **命名误导**:`Autofocus()` 里 `int score=SaveImage()` 的 `score` 其实是存图结果(1/0/-1)非分数;老 `AutoFocusPic` 叫对焦其实只抓图 | 现 HouseBin.cs:2619 |
+| L6 | **清晰层数据散三处**:服务器 `/ccd/position`、机旁 `calibration.json`、本地表 `house_autofocus_calibration`(scene0/1) | 见 §七 |
+
+---
+
+## 二、目标与范围
+
+### 2.1 三大目标(一起做)
+- **A 调试页重构(operate,大改)**:从"纯手动逐项调"改成"进去自动标 16 孔 → 哪不准人工微调"。3 列×6 行竖屏总览。手动电机/光源保留作兜底。**产出 = 每 well 的对焦运动范围(落库)。**
+- **B 采集升级版自动对焦(control)**:启用本地四步算法在范围内实时定焦面,取代静态清晰位 + 事后评分。
+- **C 每 well 运动范围(DB)**:水平=参考位±X、垂直=清晰位±半幅,存配置;采集在范围内对焦——既提速又防撞机。
+
+### 2.2 数据归属(两个"存"在不同环节,勿混)
+- **调试页(人工)只定/存"范围"**(中心 + 半幅),**不存固定 FocusZ**。原因:胚胎在长、焦面在变,存死一个 FocusZ 无意义。
+- **标定结果(当次 FocusZ/水平/曝光)是"采集时"才产生、才存**(scene=1),复用到下次触发;拍照绕它上下拍 N 层。
+
+### 2.3 不做(YAGNI / 边界)
+- 不动对焦触发时机(放皿/每日定时/关舱门/MQTT 手动)与采集主循环状态机——只换"触发后怎么算焦面"。
+- 不删 path A 静态降级(保留作 Java 不可达/未标定兜底)。
+- 不碰 `getTlDataTransmissionSetting`(喂抠图/数据传输的 SELECT,见 §八禁区)。
+- 不碰 `calCCDPosition/calAutofocusStart` 几何换算本体(在用;退为兜底,不砍)。
+- 对焦清晰位**不回写 EEPROM**(历来只读参考);EEPROM 手写只保留老代码 4 个(进/排气阀时间、扫描间隔、well 水平位)。
+- 缓冲瓶舱 11 无相机无标定,仅保留手动功能。
+
+---
+
+## 三、整体数据流闭环
+
+**① 调试阶段(operate 调试页,人工,定范围)**
+1. 进调试页选舱 → 向 control 借硬件 → 16 孔逐个跑四步自动对焦(control 执行,画面/进度回传 operate 3列×6行总览)。
+2. 工程师看哪孔不准/范围不合适 → 就地微调那孔(调水平参考位、垂直清晰位、水平/垂直半幅;可重标验证;手动电机/光源兜底)。
+3. 满意 → 每 well **范围参数**(水平中心+半幅、垂直中心+半幅)+ 设备级(曝光上下限等)**上传保存到服务器 DB(以本地算/预览为准,参照现 operate 既有上传链)**。
+4. 调试页**只存范围,不存最终焦面 Z**。
+
+**② 采集阶段(control,自动,用范围)**
+5. 触发(放皿/每日定时/关舱门/MQTT 手动)→ `StartAutoFocus` path B。
+6. 每 well **读 DB 范围**(中心=现有水平位/清晰位列,半幅=新列)→ 在 [中心±半幅] 区间内跑四步算法找**当次**最清晰 FocusZ + 居中水平位 + 曝光。
+7. 存当次标定(scene=1)→ FocusZ 写 `_autoFocusPhoto` → **本地 `PhotoLayerCalculator` 绕 FocusZ 算 N 层位置(以本地为准)→ 保存数据库** → 水平定点拍、垂直上下拍 N 层。
+8. 焦面复用到下次触发,期间每轮只拍照不重对。
+9. 服务器不再对 N 层评分选层;管理端看胚胎仍按"0 层=焦面、层对称"切(§八)。
+
+---
+
+## 四、架构与硬件借用(衔接命脉)
+
+### 4.1 总览
+```
+┌─ operate 调试页(大改) ─────┐        ┌─ control ───────────────────────────────┐
+│ 新版 ViewModel/View        │  HTTP  │ ControlHttpServer + DebugSessionManager  │
+│ · 16孔总览(3×6)            │ ─指令─▶│  · 借用闸门 IHouseGate(按舱独占)          │
+│ · 逐孔微调/重标            │ ◀进度─ │  · 标定协作(逐孔跑 CalibrationEngine)     │
+│ · 手动电机/光源(走command) │ ◀画面─ │  · CalibrationEngine(已有,改造:注入范围)  │
+│ · CalibrationClient(新)    │  MJPEG │  HouseBin.StartAutoFocus path B(改造)     │
+└──────────┬─────────────────┘        │   └ 同一个 CalibrationEngine(采集入口)    │
+           │保存范围 HTTP               │  PhotoLayerCalculator(本地排N层,权威)     │
+           ▼                           └───────────┬──────────────────────────────┘
+   Java aivof-tl-control                           │读范围参数
+   · well/update +范围字段 +补MQTT通知               ▼
+           │                            ┌───────────────────────────┐
+           ▼                            │ 服务器 MySQL aivfo_tl_setting│
+   服务器 MySQL ◀──同步(MQTT ping)──── │ house_well_setting(+半幅列)  │
+           ▲                            │ tl_setting(+设备级范围列)    │
+           └──control启动/重拉─本地SQLite缓存────────────────────────┘
+```
+
+### 4.2 硬件借用机制(已建好,勿破坏)
+- **按舱借用闸门 `IHouseGate`**(`control/IvfTl.Hardware/Concurrency.cs`):同舱同刻仅一个使用者持 lease。三种使用者 `ControlCapture`(后台采集) / `OperateDebug`(调试) / `AutoFocus`(采集对焦)。
+- **优先级**:前台(`OperateDebug`/`AutoFocus`) > 后台(`ControlCapture`)。前台一借,HAL 暂停该舱采集(`PauseCapture`→`OnPauseCapture`),归还恢复(`ResumeCapture`)。**调试与对焦互斥**(同舱不可同时占电机/相机)。
+- **会话后端 `DebugSessionManager`**(`control/ivf_tl_ControlHost/Debug/`):会话表 + acquire/heartbeat/release + `SweepExpired` 超时回收兜底 + `Execute` 命令分发(电机带 `MotorClamp` 红线钳位、EEPROM 写、光源/阀)。
+- **operate 端 `DebugSessionClient`**(已建,有单测):acquire/command/release/心跳定时器/失效回调。**生命周期勿破坏。**
+- **新增**:标定协作(逐孔跑引擎 + 进度/画面回传)挂在 control,复用上述借用机制;operate 新增 `CalibrationClient` 封装协作端点 + 轮询进度,画面用已有 `MjpegStreamClient`。
+
+> ⚠ 设计约束:调试页标定全程持 `OperateDebug` lease(control 采集该舱暂停);采集对焦持 `AutoFocus` lease。二者互斥天然防打架。标定结束/中止/超时务必 release,采集才恢复。
+
+---
+
+## 五、调试页重构(operate,大改)
+
+> 重构唯一导航入口 `SettingPageView.xaml.cs`,不波及别页面;`BufferDebugViewModel` 独立无共享基类。现状已从"自己 new ComBin/Camera"改成"HAL 借用",命令名整体换皮(如 `OpenLEDWait`→`OpenLedWait`、`VerticalMotorAbsoluteWait`→`VerticalMoveToWait`),排查时新老名要对照(L5 乱点)。
+
+- **主区 16 孔总览**:竖屏 3 列×6 行,每格 = 实时画面 + 状态(待标/标定中黄/合格绿/伪峰橙)+ 关键值(FocusZ/峰比/居中偏移)。
+- **进页面自动开标**:选舱 → 借硬件 → 全 16 孔自动标定,画面/进度填总览。
+- **逐孔微调**:点某孔 → 重标本孔 / 调水平参考位 / 调垂直清晰位 / 改水平半幅 / 改垂直半幅 / 手动电机·光源·曝光。UI 形态实现期定。
+- **手动功能保留**(兜底/校准):水平/垂直电机正反转·复位·到位、光源、曝光 → 走现有 `/debug/command`(`DebugSessionManager.Execute` 已支持)。
+- **保存范围参数**:每孔中心(复用现有水平位/清晰位列)+ 半幅(新列)→ 走现有 well/update 链上传服务器(**本地算/预览为准,参照现 `SaveManualLayerTune→WellUpdateApi` 那套**)。
+- **EEPROM 手写 4 个保留**:进/排气阀时间、垂直扫描间隔、well 水平位 → command。对焦清晰位不写 EEPROM。
+- **替换净增物**:现有半成品 `OneClickCalibrate`(一键标定写 calibration.json/scene=0) 整体并入新的标定协作;现有 M2-07 手调拍摄层(本地算+预览+HTTP 持久化)是范本,照其模式做范围保存。
+
+---
+
+## 六、采集对焦改造(control,升级版)
+
+### 6.1 改动边界(极小)
+触发链、采集主循环、`GetClearest` 外壳、`StartAutoFocus` 返回契约(填 `_autoFocusPhoto`≥1 条→true)全不动。只改 path B「算 FocusZ」段:
+```
+现状: new CalibrationEngine(lease.Serial, lease.Camera){ Log=... }   // 范围吃硬编码默认;中心读 EEPROM
+改造: var range = ReadWellFocusRange(wellSn)   // 公用方法: well级半幅→设备级默认→就近优先
+      new CalibrationEngine(lease.Serial, lease.Camera){
+          Log=..., ZCoarseCenter=清晰位中心, ZCoarseHalf=range.V半幅,
+          HFineRange=range.H半幅, ExpLo/ExpHi=设备级曝光范围, ... }       // 注入 per-well 范围
+      → CalibrateWell(well, 水平中心, 垂直中心) → 填 _autoFocusPhoto(verticalMotorPosition=FocusZ)
+      → 本地 PhotoLayerCalculator 排 N 层 → 保存DB(以本地为准)
+```
+
+### 6.2 公用方法防漏(关键)
+两个调用方(调试页标定协作 + 采集 path B)**共用同一个"读范围→注入引擎"的公用方法**,杜绝"改一处忘另一处"(现状隐性漏点:path B `new CalibrationEngine` 处只设 Log、未设范围字段,吃默认值)。
+
+### 6.3 契约约束(保下游无感)
+- `_autoFocusPhoto` 每孔一条 `{wellSn, verticalMotorPosition=FocusZ}`,下游 AllEmbryoAutofocus/SingleEmbryoAutofocus/PhotoLayerCalculator/拍照无感。
+- FocusZ 必须与 `vertical_motor_pulse_max` 同脉冲基准(HouseBin.cs:1995 有校验,量纲错会误删孔)。
+- path B 逐孔循环保留 `IsStopClearest||FirstClearest` 中断检查(开门急停/重触发抢占)。
+
+### 6.4 拍照层位置:以本地为准(决策,对应乱点 L2)
+- 改造后**本地 `PhotoLayerCalculator.ComputeLayerPositions(FocusZ, cfg, pulseMax)` 为算 N 层的权威**(与本地对焦闭环一致),算完上传保存到 DB。
+- 服务器 `calCCDPosition`(/ccd/position) 几何换算**退为兜底/老路,不再主用、但不砍**(path A 及历史兼容仍需)。
+- 层公式(沿用现有):对焦起点(第0层)=`FocusZ − focus_layer_down×focus_layer_spacing_pulse`;第 i 层=起点 + i×spacing(i=0..count-1)。**须保证最清晰焦面映射到管理端认的"0 层"且层对称**(§八兼容线)。
+
+---
+
+## 七、数据层设计(基于现有 aivfo_tl_setting,最小新增)
+
+### 7.1 复用现有列(不动;范围的"中心"复用,半幅新增)
+| 用途 | 现有列 | 表 |
+|---|---|---|
+| 水平参考位(区间中心,可微调) | `horizontal_motor_position` | house_well_setting |
+| 垂直清晰位(区间中心,可微调) | `eeprom_clear_position` | house_well_setting |
+| 垂直全局硬上限(撞机保护) | `vertical_motor_pulse_max` | tl_setting |
+| 拍 N 层:层间距/层数/下移 | `focus_layer_spacing_pulse`/`focus_layer_count`(well级覆盖)、`focus_layer_down`、`move_down_layer` | tl_setting + house_well_setting |
+| 峰比阈值 / 安全门 | `focus_peak_ratio_threshold` / `local_autofocus_enabled` | tl_setting |
+| 标定结果镜像(scene 0/1) | `house_autofocus_calibration` 整表已有(focus_z/exposure/horizontal_pulse/peak_ratio/circle_found/center_offset_pct/scene) | 已有 |
+
+### 7.2 新增列(最小集)
+**`house_well_setting`(per-well,可空=继承设备级默认):**
+- `horizontal_focus_range` int DEFAULT NULL —— 水平搜索半幅 X(围绕 `horizontal_motor_position`;空=继承设备级)
+- `vertical_focus_range` int DEFAULT NULL —— 垂直搜索半幅(围绕 `eeprom_clear_position`;空=继承设备级)
+
+**`tl_setting`(设备级默认/统一):**
+- `focus_exposure_min` int —— 对焦曝光二分下限(默认沿用引擎现值 10)
+- `focus_exposure_max` int —— 对焦曝光二分上限(默认 800)
+- `focus_h_range_default` int —— 水平半幅设备级默认(well 级留空时用)
+- `focus_v_range_default` int —— 垂直半幅设备级默认
+- 对焦扫描步距(粗/精):实现期评估——优先复用现有 `focus_layer_spacing_pulse`,确需独立再加(倾向不加)。
+
+> 区间表达:水平 = `horizontal_motor_position ± horizontal_focus_range`;垂直 = `eeprom_clear_position ± vertical_focus_range`,并与 `vertical_motor_pulse_max` 取交集做硬钳位。
+
+### 7.3 存储链(按"以前的方式",不臆造)
+- **范围参数** → 服务器 MySQL(权威,`house_well_setting`/`tl_setting`)↔ control 本地 SQLite `aivfoTL.db`(缓存,启动/收 MQTT 重拉)。operate 无本地库、只发 HTTP。
+- **标定结果** → control 本地 SQLite `house_autofocus_calibration` + 机旁 `calibration.json`,沿用现有 scene 机制(调试页基准 scene=0 / 采集日常 scene=1)。服务器虽建同名表但 Java 全仓 0 引用——不上服务器(机旁本地为准;代价:本地库重建则需重标,范围因在服务器不丢,用户已接受)。
+
+---
+
+## 八、影响面与配合改动(全链路,重点)
+
+### 8.1 Java 微服务(aivof-tl-control)
+1. **`house_well_setting` 加 2 半幅列读写**:`HouseWellSetting` DAO 加字段;`HouseWellSettingMapper.xml` 的 `saveOrUpdateData` **INSERT 列清单硬编码**(:96-118 两段都加;漏加=静默丢数据)+ 评估 `on duplicate key update`;`HouseWellSettingUpdate` DTO 加字段 + `TimeLapseSettingManageImpl.updateHouseWellSettings` 加分发分支。
+2. **`tl_setting` 加设备级范围列**:`TlSetting` DAO + `buildDefault` 默认值;`TlSettingMapper.xml` `saveOrUpdateData` INSERT 列硬编码同步;`TlCommonUpdateDTO`/`TlSystemSettingUpdateDTO` + 对应 VO 加字段。
+3. **补 MQTT 通知(确认的硬缺口)**:`updateHouseWellSettings`(well/update 主链,:265-294)**全程没发 MQTT**(focusPoint/horizontalMotorPosition/manualLayerTune 三分支都只写库不通知)。需在末尾补 `this.sendUpdateSettingEvent(tlSn)`,control 才即时重拉生效(机制现成,tl/house 级方法已有)。
+4. **读 SELECT**:范围列加到 `selectAutofocusParam`/`selectHouseFocusSetting`,**绝不进 `getTlDataTransmissionSetting`**(禁区)。
+
+### 8.2 front-manament(C# 管理端)
+该端是 PC 管理/监控后台(设备/报警/胚胎详情/权限),非操作端。两处受影响:
+1. **"对焦预览"窗 `AutoFocusWindow` —— 砍掉**(决策)。它靠服务器评分 `highestScore`/`imageScore` 给最清晰图高亮(`AutoFocusWindow.xaml.cs:152-155`),服务器评分一砍它就失去意义。连带入口(`DetailView` 的"对焦预览"按钮 + `GetAutofocusPicturesApi`/`/getAutofocusPictures` 消费链)一并清理。
+2. **看胚胎切焦平面(`DetailView`)—— 不破,但有兼容硬约束**。它按"**0 层=焦面、上下层对称 ±N**"切层(`(totalLayer-1)/2`),不读评分。**新方式必须保证最清晰焦面仍落 `pictureLayer==0`、层仍对称**;若范围/层参数搞出上下不对称会错位。→ 与后端钉死此约定。
+3. **对焦参数配置 UI**:该端虽有 `AutoFocusHouseInfo`/`AutoFocusWellInfo` 模型,但承载 UI 是死代码(`HouseSettingViewModel.Init()` 早 `return`)。**加范围列对它不强制**:是否让管理端也能远程配 per-well 范围 = 产品决策,本次默认**不动该端配置**(范围只在设备操作端调)。`updateClearestNumber`("更新最清晰层数量"评分语义) 一并视为废弃。
+
+### 8.3 现状清理(对应 §1.4 乱点)
+- L1 对焦取数死代码:`GetAutoFocusServiceEvent`/`GetAutoFocusDBEvent` + `/autofocus/position` 链——标注隔离/不主用(最终去留实现期定)。
+- L2 拍层割裂:本地 `PhotoLayerCalculator` 定为权威(§6.4),服务器 `calCCDPosition` 退兜底。
+- L3 安全门:`local_autofocus_enabled` 验证通过后置 1 启用;path A 静态降级保留兜底。
+- L4 拍层参数双轨:以新列为准,老 `autoFocusNumber/verticalMotorSpacePulse` 标废。
+- L5 命名误导:`score`(存图结果)、`AutoFocusPic`(只抓图) 注释纠正,避免误读。
+- L6 清晰层三处来源:明确机旁本地(scene0/1)为采集真相源,服务器表不用,调试页只存范围。
+
+### 8.4 禁区(碰了会坏别功能)
+- `getTlDataTransmissionSetting` 的 SELECT(`HouseWellSettingMapper.xml:32-60` 选 left/bottom_offset)→ Feign → data-transmission `CacheMange` 喂抠图裁剪。**对焦列绝不加进这条 SELECT。**
+- `calCCDPosition`/`calAutofocusStart` 在用(生成拍照位置写 house_photograph_setting),**不砍**,只退主用。
+- `DebugSessionClient` 借用生命周期、`MjpegStreamClient` 防残帧语义——勿破坏。
+
+---
+
+## 九、真机三门(决策 D1:这次一起验+修,真正启用)
+1. **74000 伪峰**:per-well 垂直区间收窄粗对焦窗口,远处伪峰被范围排除(治本);真机开 DebugSave 存图验证。
+2. **真胚胎峰比阈值**:用 `tl_setting.focus_peak_ratio_threshold`(已有可调列,默认 1.2),真机活体胚胎上确认松紧。
+3. **EEPROM 回写**:对焦不写;只验调试页 4 个手动写命令真机生效。
+- 三门验证通过后 `local_autofocus_enabled` 置 1,升级版自动对焦正式启用。
+
+---
+
+## 十、测试策略(分层)
+- **纯逻辑单测(TDD)**:范围解析(well级覆盖→设备级默认→就近优先,仿 `PhotoLayerCalculator.Resolve`)、标定协作任务状态机、`CalibrationClient` 协议拼装/进度解析、范围→引擎注入映射、`PhotoLayerCalculator` 排层。
+- **编译验证**:control + operate Release 双编译 0 错;Java 编译;front-manament 砍窗后编译。
+- **真机验证(Claude 自主跑,电机守安全区间 水平[0,220000]/垂直[0,125000])**:
+  1. 调试页 16 孔标定出图 + 逐孔微调 + 范围调整。
+  2. 范围存库 → control 重拉(验 well/update 补的 MQTT 生效)→ 采集用新范围对焦。
+  3. 三门:74000 伪峰(DebugSave 验范围排除)/ 真胚胎峰比 / EEPROM 4 个手写生效。
+  4. 电机安全区间不撞机(范围钳位生效)。
+  5. 端到端:放皿/定时触发 → 范围内对焦 → 本地排 N 层拍 → 焦面复用到下次触发。
+  6. 管理端看胚胎切焦平面仍正常(0 层=焦面、层对称未破);对焦预览窗已砍无残留报错。
+  7. use-after-free 压测(标定中反复 release/超时回收同舱)。
+
+---
+
+## 十一、关键决策登记
+| # | 决策 |
+|---|---|
+| D1 | 三个真机门一起验+修,真正启用升级版自动对焦 |
+| D2 | 调试页进去就 16 孔全自动重标(不选孔);哪孔不准就地微调;UI 竖屏 3列×6行 |
+| D3 | 标定引擎统一在 control 执行,operate 发指令 + 收画面/进度(借用闸门现成) |
+| D4 | 旧"拍N层→服务器评分选赤道面"已废,新算法对焦时直接定最清晰层,不恢复 |
+| D5 | 范围粒度:水平半幅、垂直半幅 = per-well;曝光范围/扫描步进/层数 = 设备级 |
+| D6 | 存储沿用"服务器 DB 权威 + control 本地 SQLite 缓存";范围上服务器、标定结果机旁本地 |
+| D7 | 手动电机/光源保留(兜底/校准) |
+| D8 | 触发时机不动(放皿/定时/关门/手动),只换"触发后怎么算焦面" |
+| D9 | EEPROM 回写保留老代码 4 个手写;对焦清晰位不写 EEPROM |
+| D10 | 区间用"中心±半幅"存(中心复用现有水平位/清晰位列,半幅各加 1 列),不用绝对上下限 |
+| D11 | **调试页只定/存范围,不存固定 FocusZ;标定结果采集时产生才存(scene=1)** |
+| D12 | **拍照 N 层位置以本地 `PhotoLayerCalculator` 为准、算完上传 DB;服务器 `calCCDPosition` 退兜底(参照现 operate 上传链)** |
+| D13 | **front-manament"对焦预览"评分高亮窗砍掉;看胚胎切焦平面须守"0层=焦面+层对称"兼容线** |
+
+---
+
+## 十二、自检
+- §1.3 三目标 A/B/C → §五(调试页)、§六(采集升级)、§七(范围存储) 全覆盖。✓
+- 现状乱点 L1-L6 → §1.4 登记,§六/§八 给清理落点。✓
+- 用户两点硬嘱咐:存储参考以前方式 → §七基于实读表结构 + 两层链;影响面 → §八全链路(Java/管理端/禁区/清理)。✓
+- 受影响处配合改:Java 补 MQTT + 加列同步、管理端砍对焦预览窗 + 守 0 层对称 → §八明确。✓
+- 无 TBD:§7.2 扫描步距标"实现期评估倾向复用"为明确实现指令;管理端范围配置标"本次不动"为明确边界。✓