Просмотр исходного кода

docs(d2-02-t3): 自动对焦重构实现计划——5阶段(DB+Java/control对焦/标定+调试页/砍预览窗/真机三门)

基于需求文档(2026-06-25)拆解,顺序依赖分5阶段,每阶段自成可编译+测试闭环:
- Phase1 数据层+Java: 范围列迁移+base同步+DAO/Mapper硬编码INSERT同步+well/update补MQTT+C#手映射(完整bite-sized)
- Phase2 control采集对焦: FocusRangeResolver纯逻辑+4单测(TDD)+path B注入范围+本地排层权威(完整bite-sized)
- Phase3-5 标定协作+调试页重构/front-manament砍对焦预览窗/真机三门(精确文件清单+任务骨架,逐阶段开工前详化)

执行约定: 逐阶段"详化→执行→再详化下阶段",后续阶段计划反映前阶段真实代码。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie 2 дней назад
Родитель
Сommit
fa6686f07b

+ 496 - 0
项目文档/开发计划/2026-06-25-D2-02-第三阶段-自动对焦重构-实现计划.md

@@ -0,0 +1,496 @@
+# D2-02 第三阶段 · 自动对焦重构 实现计划
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 把对焦从"人工标定清晰层 + 服务器评分选层"改成"调试页定 per-well 运动范围 + 采集时在范围内本地四步自动对焦",跨 DB/Java/control/operate/front-manament 五层。
+
+**Architecture:** 数据层加 per-well 半幅列(中心复用现有水平位/清晰位列);Java 同步读写 + 补 well/update 的 MQTT 通知;control 用公用方法读范围注入 `CalibrationEngine`、path B 实时对焦、本地 `PhotoLayerCalculator` 排层为准;operate 调试页重构成"自动标定 + 逐孔微调"并经借用闸门借 control 硬件;front-manament 砍掉靠服务器评分的"对焦预览"窗。
+
+**Tech Stack:** C# net6.0-windows(operate/control)+ net8.0(control 测试)+ xUnit;Java 11 + MyBatis-Plus(aivof-tl-control);MySQL 8(aivfo_tl_setting)+ control 本地 SQLite(SqlSugar)。
+
+**权威需求文档:** `项目文档/需求文档/specs/2026-06-25-D2-02-第三阶段-自动对焦重构-需求文档.md`(决策 D1–D13、影响面、禁区均在此)。
+
+**分阶段执行约定(大改造,逐阶段详化):** 本计划 Phase 1/2 给完整 bite-sized TDD 任务;Phase 3/4/5 给精确文件清单 + 任务分解 + 验证方式作为路线图。**每个 Phase 开工前,基于前一 Phase 落地后的真实代码把该 Phase 的任务再详化一遍**(避免现在凭空写死后续 C# 代码)。每个 Phase 自成"可编译 + 测试通过"的闭环。
+
+**通用纪律:** TDD(纯逻辑先写失败测试)、频繁提交、改文件后跑 `codegraph sync`、真机/连内网必须 Release 编译(Debug 的 `#if DEBUG` 会覆写服务器地址)、operate.exe 在跑会锁 DLL 先关再编。
+
+---
+
+## 文件结构地图(5 Phase 总览)
+
+### Phase 1 — 数据层 + Java(地基:范围能存/读/同步)
+- Create: `sql/migrations/2026-06-25-autofocus-range.sql` — 加 4+ 范围列的增量迁移
+- Modify: `sql/aivfo_tl_setting.sql` — base 脚本同步加列(全新部署一次到位)
+- Modify: `aivof-tl-control/.../entity/dao/house/HouseWellSetting.java` — 加 2 半幅字段
+- Modify: `aivof-tl-control/.../entity/dao/TlSetting.java` — 加设备级范围字段 + buildDefault 默认值
+- Modify: `aivof-tl-control/.../mapper/.../HouseWellSettingMapper.xml` — saveOrUpdateData INSERT 列同步(硬编码)
+- Modify: `aivof-tl-control/.../mapper/.../TlSettingMapper.xml` — saveOrUpdateData INSERT 列同步
+- Modify: `aivof-tl-control/.../entity/param/housewellsetting/HouseWellSettingUpdate.java` — 入参 DTO 加字段
+- Modify: `aivof-tl-control/.../manage/impl/tl/TimeLapseSettingManageImpl.java` — updateHouseWellSettings 加分发分支 + **补 sendUpdateSettingEvent**
+- Modify: control C# 端 `house_well_setting.cs` / `tl_setting.cs` DBEntity + `ConvertHelper.cs` 手映射段 + operate 对应段
+
+### Phase 2 — control 采集对焦(path B 用 per-well 范围 + 本地排层权威)
+- Create: `ivf_tl_operate_2.0/control/IvfTl.AutoFocus/Layout/FocusRangeResolver.cs` — 范围就近优先解析(纯逻辑,仿 PhotoLayerCalculator)
+- Create: `ivf_tl_operate_2.0/control/IvfTl.AutoFocus.Tests/FocusRangeResolverTests.cs` — 范围解析单测
+- Create: control 公用方法 `ReadWellFocusRange(wellSn)`(读 DB 范围→FocusRangeRawConfig→Resolve→注入参数),放 HouseBin 或独立 helper
+- Modify: `ivf_tl_operate_2.0/control/ivf_tl_Com/HouseBin.cs:1574` path B — 注入 per-well 范围到 CalibrationEngine
+- Modify: HouseBin path B 拍层段 — 确认本地 PhotoLayerCalculator 为权威、结果上传 DB
+
+### Phase 3 — control 标定协作 + operate 调试页重构
+- Create: control `/debug/calibrate/*` 端点 + `CalibrationCoordinator`(逐孔跑引擎 + 进度/画面回传)
+- Create: operate `CalibrationClient`(封装协作端点 + 轮询进度)+ 单测
+- Modify: operate `HouseDebugPageViewModel` / `HouseDebugPageView` — 16 孔总览 + 逐孔微调 + 保留手动
+- Modify: 调试页保存范围走 well/update(参照现有 SaveManualLayerTune)
+
+### Phase 4 — front-manament 砍对焦预览窗
+- Modify: `aivfo-front-manament-2.0/.../DetailView.xaml(.cs)` — 去掉"对焦预览"按钮 + Auto_Click
+- Delete/Modify: `AutoFocusWindow.xaml(.cs)` + `GetAutofocusPicturesApi` 消费链
+- 验证: 看胚胎切焦平面(0 层=焦面 + 层对称)仍正常
+
+### Phase 5 — 真机三门验证 + 启用安全门
+- 验证: 74000 伪峰(范围排除)/ 真胚胎峰比阈值 / EEPROM 4 个手写
+- Modify: 置 `tl_setting.local_autofocus_enabled=1` 启用
+
+---
+
+## Phase 1 — 数据层 + Java
+
+> 验证方式:Java 无单测惯例,靠"编译 0 错 + 真机/测试库 DESC 看列 + 存一条读回来对得上"。SQL 先在测试库跑通。加列**漏一处静默丢数据**,故 INSERT 列同步是重点。
+
+### Task 1.1:写范围列增量迁移 SQL
+
+**Files:**
+- Create: `sql/migrations/2026-06-25-autofocus-range.sql`
+
+- [ ] **Step 1: 写迁移脚本**
+
+```sql
+-- =============================================================================
+-- 自动对焦 per-well 运动范围迁移(依据 需求文档 2026-06-25 §七)
+-- 库:aivfo_tl_setting(MySQL 8,InnoDB/utf8mb4)
+-- 范围 = 中心 ± 半幅:中心复用现有 horizontal_motor_position / eeprom_clear_position,
+--        本次仅加"半幅"列 + 设备级默认/曝光范围。
+-- ⚠ MySQL 8 ADD COLUMN 不支持 IF NOT EXISTS:旧库重跑前先 DESC 判断列是否已存在。
+-- 全新部署不需要本文件(base 脚本 sql/aivfo_tl_setting.sql 已含,见 Task 1.2)。
+-- =============================================================================
+
+-- house_well_setting:per-well 半幅(可空=继承设备级默认)
+ALTER TABLE `house_well_setting`
+  ADD COLUMN `horizontal_focus_range` int NULL DEFAULT NULL COMMENT 'well级水平搜索半幅(围绕horizontal_motor_position;空=继承tl_setting.focus_h_range_default)',
+  ADD COLUMN `vertical_focus_range` int NULL DEFAULT NULL COMMENT 'well级垂直搜索半幅(围绕eeprom_clear_position;空=继承tl_setting.focus_v_range_default)';
+
+-- tl_setting:设备级范围默认 + 对焦曝光二分上下限
+ALTER TABLE `tl_setting`
+  ADD COLUMN `focus_h_range_default` int NULL DEFAULT NULL COMMENT '水平半幅设备级默认(well级留空时用)',
+  ADD COLUMN `focus_v_range_default` int NULL DEFAULT NULL COMMENT '垂直半幅设备级默认',
+  ADD COLUMN `focus_exposure_min` int NOT NULL DEFAULT 10 COMMENT '对焦曝光二分下限(沿用引擎现值)',
+  ADD COLUMN `focus_exposure_max` int NOT NULL DEFAULT 800 COMMENT '对焦曝光二分上限(沿用引擎现值)';
+```
+
+- [ ] **Step 2: 在测试库跑通**
+
+Run: 用 `项目文档/开发环境/` 里的测试库连接,执行该 SQL;再 `DESC house_well_setting;` / `DESC tl_setting;` 确认 6 列已加。
+Expected: 6 列存在,类型/默认值如上。
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add sql/migrations/2026-06-25-autofocus-range.sql
+git commit -m "feat(d2-02-t3): 加自动对焦per-well范围列迁移(半幅+设备级默认+曝光范围)"
+```
+
+### Task 1.2:base 脚本同步加列
+
+**Files:**
+- Modify: `sql/aivfo_tl_setting.sql`(`house_well_setting` 与 `tl_setting` 的 CREATE TABLE)
+
+- [ ] **Step 1: 在 base CREATE TABLE 里补同样 6 列**
+
+在 `sql/aivfo_tl_setting.sql` 的 `house_well_setting` 建表中,紧挨现有 `focus_layer_count` 列后加:
+```sql
+  `horizontal_focus_range` int NULL DEFAULT NULL COMMENT 'well级水平搜索半幅',
+  `vertical_focus_range` int NULL DEFAULT NULL COMMENT 'well级垂直搜索半幅',
+```
+在 `tl_setting` 建表中,紧挨现有 `local_autofocus_enabled` 列后加:
+```sql
+  `focus_h_range_default` int NULL DEFAULT NULL COMMENT '水平半幅设备级默认',
+  `focus_v_range_default` int NULL DEFAULT NULL COMMENT '垂直半幅设备级默认',
+  `focus_exposure_min` int NOT NULL DEFAULT 10 COMMENT '对焦曝光二分下限',
+  `focus_exposure_max` int NOT NULL DEFAULT 800 COMMENT '对焦曝光二分上限',
+```
+
+- [ ] **Step 2: 全新建库验证**
+
+Run: 在空测试库跑 `sql/init-database.sh`(或直接 source base 脚本),`DESC` 两表确认列齐(验证全新部署一次到位、与增量脚本一致)。
+Expected: 两表均含新列,与 Task 1.1 结果一致。
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add sql/aivfo_tl_setting.sql
+git commit -m "feat(d2-02-t3): base建库脚本同步范围列(全新部署一次到位)"
+```
+
+### Task 1.3:Java DAO 实体加字段
+
+**Files:**
+- Modify: `aivof-tl-control/aivfo-tl-control-entity/src/main/java/com/aivfo/tl/control/entity/dao/house/HouseWellSetting.java`
+- Modify: `aivof-tl-control/aivfo-tl-control-entity/src/main/java/com/aivfo/tl/control/entity/dao/TlSetting.java`
+
+- [ ] **Step 1: HouseWellSetting 加 2 字段**
+
+参照现有 `focusLayerSpacingPulse`/`focusLayerCount` 的 `@TableField` 写法,加:
+```java
+@TableField(value = "horizontal_focus_range")
+private Integer horizontalFocusRange;
+
+@TableField(value = "vertical_focus_range")
+private Integer verticalFocusRange;
+```
+
+- [ ] **Step 2: TlSetting 加 4 字段 + buildDefault 默认值**
+
+参照现有 focus 列写法加 4 个 `@TableField`(`focusHRangeDefault`/`focusVRangeDefault`/`focusExposureMin`/`focusExposureMax`,Integer);在 `buildDefault()`(现有 focus 默认值段)补 `setFocusExposureMin(10)` / `setFocusExposureMax(800)`(半幅默认留 null=不强制)。
+
+- [ ] **Step 3: 编译验证**
+
+Run: `mvn -q -pl aivfo-tl-control-entity -am compile`(先 `mvn -DskipTests install` 装 aivfo-framework)
+Expected: BUILD SUCCESS。
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add aivof-tl-control/aivfo-tl-control-entity/
+git commit -m "feat(d2-02-t3): Java DAO加范围字段(HouseWellSetting 2列/TlSetting 4列+默认值)"
+```
+
+### Task 1.4:Mapper saveOrUpdateData INSERT 列同步(漏加=静默丢数据)
+
+**Files:**
+- Modify: `aivof-tl-control/.../mapper/.../HouseWellSettingMapper.xml`(`saveOrUpdateData`)
+- Modify: `aivof-tl-control/.../mapper/.../TlSettingMapper.xml`(`saveOrUpdateData`)
+
+- [ ] **Step 1: HouseWellSettingMapper.saveOrUpdateData 两段都加**
+
+列名清单段加 `horizontal_focus_range, vertical_focus_range`;`<foreach>` values 段加 `#{item.horizontalFocusRange}, #{item.verticalFocusRange}`。评估 `on duplicate key update` 段是否需加(若 tl 端推送要覆盖该列则加,否则保持与现有 focus 列同模式)。
+
+- [ ] **Step 2: TlSettingMapper.saveOrUpdateData 两段都加**
+
+列名段加 `focus_h_range_default, focus_v_range_default, focus_exposure_min, focus_exposure_max`;values 段加对应 `#{tlSetting.xxx}`。
+
+- [ ] **Step 3: 编译 + 存读回环验证**
+
+Run: `mvn -q -pl aivfo-tl-control-... -am compile`;启动后跑一次 tl 同步 saveOrUpdateData,DB 查这几列**非 NULL**(确认 INSERT 没漏列)。
+Expected: 列值正确写入(漏列会是 NULL)。
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add aivof-tl-control/.../HouseWellSettingMapper.xml aivof-tl-control/.../TlSettingMapper.xml
+git commit -m "feat(d2-02-t3): Mapper saveOrUpdateData同步范围列(INSERT列名+values两段,防静默丢数据)"
+```
+
+### Task 1.5:well/update 加范围分发 + 补 MQTT 通知
+
+**Files:**
+- Modify: `aivof-tl-control/.../entity/param/housewellsetting/HouseWellSettingUpdate.java`
+- Modify: `aivof-tl-control/.../manage/impl/tl/TimeLapseSettingManageImpl.java:265-294`(updateHouseWellSettings)
+- Modify: `aivof-tl-control/.../service/house/HouseWellSettingService.java` + `...ServiceImpl.java`(加更新方法,仿 updateHorizontalMotorPosition)
+
+- [ ] **Step 1: DTO 加字段**
+
+`HouseWellSettingUpdate` 加 `Integer horizontalFocusRange; Integer verticalFocusRange;`(参照 focusLayerSpacingPulse)。
+
+- [ ] **Step 2: Service 加更新方法**
+
+仿 `updateHorizontalMotorPosition`(impl `HouseWellSettingServiceImpl.java:146` 的 LambdaUpdateWrapper 模式)加 `updateFocusRange(tlSn, houseSn, wellSn, hRange, vRange)`,`null` 值用 `.set(...)` 仅在非空时设(留空=清空继承设备级,对齐 updateManualLayerTune 语义)。
+
+- [ ] **Step 3: updateHouseWellSettings 加分发分支 + 补 MQTT**
+
+在 `updateHouseWellSettings`(:265-294)按字段加:携带半幅字段时调 `updateFocusRange`。**在方法末尾(:293 后)加 `this.sendUpdateSettingEvent(tlSn);`** —— 这是确认的硬缺口,well/update 全程没发 MQTT,补上后 control 才即时重拉。
+
+- [ ] **Step 4: 编译 + MQTT 验证**
+
+Run: 编译;改一次 well 范围 → 观察 Java 发 `SEND_Tl_SETTING_UPDATE` ping → control 收到 `MqttEnum.Update` 重拉 `/info`(日志可见)。
+Expected: control 即时重拉,本地 SQLite 范围列更新。
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add aivof-tl-control/
+git commit -m "feat(d2-02-t3): well/update支持范围字段+补sendUpdateSettingEvent(修复well更新不发MQTT缺口)"
+```
+
+### Task 1.6:control/operate C# DBEntity + ConvertHelper 手映射同步
+
+**Files:**
+- Modify: `ivf_tl_operate_2.0/control/ivf_tl_Entity/DTO1/house_well_setting.cs` + `tl_setting.cs`(DBEntity 加字段)
+- Modify: control 端 `ConvertHelper.cs`(HouseWellSetting/TLSetting 转换段,逐字段手映射)
+- Modify: operate 端对应 DBEntity/转换段(若 operate 也有同名实体)
+
+- [ ] **Step 1: DBEntity 加字段**
+
+`house_well_setting.cs` 加 `public int? horizontal_focus_range {get;set;}` / `vertical_focus_range`;`tl_setting.cs` 加 4 个范围字段(与 Java/SQL 同名同义)。
+
+- [ ] **Step 2: ConvertHelper 手映射补字段**
+
+control 端 ConvertHelper 的 ConvertToTLSetting / ConvertToTLSettingDB / HouseWellSetting 转换各段,逐字段补上新列映射(**漏段静默丢字段**,逐段核对)。
+
+- [ ] **Step 3: 编译验证**
+
+Run: `dotnet build ivf_tl_operate_2.0/control/control/ivf_tl_Control.sln -c Release`(operate.exe 在跑先关)
+Expected: 0 错。
+
+- [ ] **Step 4: Commit + codegraph sync**
+
+```bash
+git add ivf_tl_operate_2.0/control/
+git commit -m "feat(d2-02-t3): control C# DBEntity+ConvertHelper同步范围列(逐字段手映射防丢)"
+codegraph sync
+```
+
+---
+
+## Phase 2 — control 采集对焦(path B 用 per-well 范围 + 本地排层权威)
+
+> 开工前重新核对 Phase 1 落地后的实际列名/实体。范围解析做成纯逻辑类,完整 TDD(仿 PhotoLayerCalculator/PhotoLayerCalculatorTests)。
+
+### Task 2.1:FocusRangeResolver 纯逻辑 + 单测(TDD)
+
+**Files:**
+- Create: `ivf_tl_operate_2.0/control/IvfTl.AutoFocus/Layout/FocusRangeResolver.cs`
+- Test: `ivf_tl_operate_2.0/control/IvfTl.AutoFocus.Tests/FocusRangeResolverTests.cs`
+
+- [ ] **Step 1: 写失败测试**
+
+仿 `PhotoLayerCalculatorTests` 风格(xUnit + 中文用例名 + 推导注释):
+```csharp
+using Xunit;
+using IvfTl.AutoFocus.Layout;
+
+namespace IvfTl.AutoFocus.Tests
+{
+    public class FocusRangeResolverTests
+    {
+        private static FocusRangeRawConfig Raw(
+            int? wellH = null, int? wellV = null,
+            int? devH = null, int? devV = null,
+            int expMin = 10, int expMax = 800,
+            int hCenter = 71000, int vCenter = 88000) =>
+            new FocusRangeRawConfig
+            {
+                WellHRange = wellH, WellVRange = wellV,
+                DeviceHRange = devH, DeviceVRange = devV,
+                ExposureMin = expMin, ExposureMax = expMax,
+                HorizontalCenter = hCenter, VerticalCenter = vCenter,
+            };
+
+        // well 级非空 → 覆盖设备级
+        [Fact]
+        public void 半幅_well级覆盖设备级()
+        {
+            var r = FocusRangeResolver.Resolve(Raw(wellH: 1500, wellV: 4000, devH: 2000, devV: 6000));
+            Assert.Equal(1500, r.HHalf);
+            Assert.Equal(4000, r.VHalf);
+        }
+
+        // well 级空 → 用设备级默认
+        [Fact]
+        public void 半幅_well空则继承设备级()
+        {
+            var r = FocusRangeResolver.Resolve(Raw(wellH: null, wellV: null, devH: 2000, devV: 6000));
+            Assert.Equal(2000, r.HHalf);
+            Assert.Equal(6000, r.VHalf);
+        }
+
+        // 两级皆空 → 用引擎现有硬编码默认(H=2000,V=6000),不抛(范围缺失不致命,回退默认窗口)
+        [Fact]
+        public void 半幅_两级皆空则回退引擎默认()
+        {
+            var r = FocusRangeResolver.Resolve(Raw(wellH: null, wellV: null, devH: null, devV: null));
+            Assert.Equal(FocusRangeResolver.DefaultHHalf, r.HHalf);
+            Assert.Equal(FocusRangeResolver.DefaultVHalf, r.VHalf);
+        }
+
+        // 曝光上下限透传
+        [Fact]
+        public void 曝光范围_透传设备级()
+        {
+            var r = FocusRangeResolver.Resolve(Raw(expMin: 15, expMax: 600));
+            Assert.Equal(15, r.ExpLo);
+            Assert.Equal(600, r.ExpHi);
+        }
+    }
+}
+```
+
+- [ ] **Step 2: 跑测试确认失败**
+
+Run: `dotnet test ivf_tl_operate_2.0/control/IvfTl.AutoFocus.Tests/ --filter FocusRangeResolverTests`
+Expected: 编译失败(FocusRangeResolver/FocusRangeRawConfig 未定义)。
+
+- [ ] **Step 3: 写最小实现**
+
+```csharp
+namespace IvfTl.AutoFocus.Layout
+{
+    /// <summary>对焦范围就近优先解析:well级半幅 &gt; 设备级默认 &gt; 引擎硬编码默认。纯逻辑,仿 PhotoLayerCalculator。</summary>
+    public class FocusRangeRawConfig
+    {
+        public int? WellHRange, WellVRange, DeviceHRange, DeviceVRange;
+        public int ExposureMin, ExposureMax;
+        public int HorizontalCenter, VerticalCenter;
+    }
+
+    public class FocusRangeConfig
+    {
+        public int HHalf, VHalf, ExpLo, ExpHi, HCenter, VCenter;
+    }
+
+    public static class FocusRangeResolver
+    {
+        // 与 CalibrationEngine 现有硬编码默认对齐(HFineRange=2000、FineZHalf=6000)。
+        public const int DefaultHHalf = 2000;
+        public const int DefaultVHalf = 6000;
+
+        public static FocusRangeConfig Resolve(FocusRangeRawConfig raw)
+        {
+            if (raw == null) throw new System.ArgumentNullException(nameof(raw));
+            return new FocusRangeConfig
+            {
+                HHalf = raw.WellHRange ?? raw.DeviceHRange ?? DefaultHHalf,
+                VHalf = raw.WellVRange ?? raw.DeviceVRange ?? DefaultVHalf,
+                ExpLo = raw.ExposureMin,
+                ExpHi = raw.ExposureMax,
+                HCenter = raw.HorizontalCenter,
+                VCenter = raw.VerticalCenter,
+            };
+        }
+    }
+}
+```
+
+- [ ] **Step 4: 跑测试确认通过**
+
+Run: `dotnet test ivf_tl_operate_2.0/control/IvfTl.AutoFocus.Tests/ --filter FocusRangeResolverTests`
+Expected: 4 PASS。
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add ivf_tl_operate_2.0/control/IvfTl.AutoFocus/Layout/FocusRangeResolver.cs ivf_tl_operate_2.0/control/IvfTl.AutoFocus.Tests/FocusRangeResolverTests.cs
+git commit -m "feat(d2-02-t3): FocusRangeResolver范围就近优先解析+4单测(TDD,仿PhotoLayerCalculator)"
+```
+
+### Task 2.2:control 公用方法 ReadWellFocusRange + path B 注入引擎
+
+**Files:**
+- Modify: `ivf_tl_operate_2.0/control/ivf_tl_Com/HouseBin.cs`(path B :1574 段 + 新增 ReadWellFocusRange)
+
+- [ ] **Step 1: 写 ReadWellFocusRange 公用方法**
+
+从 `_wellSettings`(well 级 horizontal_focus_range/vertical_focus_range + 中心 horizontal_motor_position/eeprom_clear_position)+ `TLSetting`(设备级默认 + 曝光 min/max)拼 `FocusRangeRawConfig` → `FocusRangeResolver.Resolve` → 返回 `FocusRangeConfig`。**采集 path B 与 Phase 3 标定协作两处共用此方法**(防"改一处忘另一处")。
+
+- [ ] **Step 2: path B `new CalibrationEngine` 处注入范围**
+
+在 HouseBin.cs:1574 把 `new CalibrationEngine(...){ Log=... }` 改为注入:
+```csharp
+var range = ReadWellFocusRange(well);
+var engine = new CalibrationEngine(lease.Serial, lease.Camera)
+{
+    Log = msg => HouseLogEvent?.Invoke(...),
+    HFineRange = range.HHalf,
+    ZCoarseCenter = range.VCenter,   // 中心=该well清晰位(eeprom_clear_position),不再用引擎固定90000
+    ZCoarseHalf = range.VHalf,
+    ExpLo = range.ExpLo, ExpHi = range.ExpHi,
+};
+// CalibrateWell(well, 水平中心=range.HCenter, 垂直中心=range.VCenter)
+```
+保留逐孔 `IsStopClearest||FirstClearest` 中断检查;FocusZ 与 `vertical_motor_pulse_max` 同脉冲基准。
+
+- [ ] **Step 3: Release 编译验证**
+
+Run: `dotnet build ivf_tl_operate_2.0/control/control/ivf_tl_Control.sln -c Release`
+Expected: 0 错。
+
+- [ ] **Step 4: Commit + sync**
+
+```bash
+git add ivf_tl_operate_2.0/control/ivf_tl_Com/HouseBin.cs
+git commit -m "feat(d2-02-t3): path B读per-well范围注入CalibrationEngine(中心=清晰位/曝光设备级,公用ReadWellFocusRange)"
+codegraph sync
+```
+
+### Task 2.3:确认本地 PhotoLayerCalculator 为拍层权威(决策 D12)
+
+**Files:**
+- Modify: `ivf_tl_operate_2.0/control/ivf_tl_Com/HouseBin.cs`(拍层段 :1838/2026)
+
+- [ ] **Step 1: 核对拍层路径**
+
+确认 path B 算出 FocusZ 后,拍 N 层位置走本地 `PhotoLayerCalculator.ComputeLayerPositions`(已在 :1838/2026),结果上传 DB(参照现有上传链);服务器 `calCCDPosition` 退兜底不主用。**此处开工前需重新读实际代码确认现状**(L2 半本地半云端割裂,可能要清理 StartCCD 的 /ccd/position 主调用)。
+
+- [ ] **Step 2: 真机端到端冒烟**
+
+Run: 真机放皿触发 → 范围内对焦 → 本地排层拍 N 层(守电机安全区间 水平[0,220000]/垂直[0,125000])。
+Expected: 出图、层数对、最清晰焦面落 0 层。
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add ivf_tl_operate_2.0/control/ivf_tl_Com/HouseBin.cs
+git commit -m "refactor(d2-02-t3): 拍N层以本地PhotoLayerCalculator为权威,calCCDPosition退兜底"
+```
+
+---
+
+## Phase 3 — control 标定协作 + operate 调试页重构(开工前详化)
+
+> 此 Phase 涉及新端点 + UI 重构,开工前基于 Phase 1/2 真实代码 + 现有 `DebugSessionClient`/`MjpegStreamClient`/`OneClickCalibrate` 详化任务。任务骨架:
+
+- **Task 3.1**:control 加 `CalibrationCoordinator`(借 DebugSession lease → 逐孔 `CalibrateWell`(注入范围,复用 Task 2.2 的 ReadWellFocusRange)→ 收集结果 + 进度 + OnFrame 推流)。单测:协作任务状态机(待标/标定中/合格/伪峰/中止)。
+- **Task 3.2**:control `ControlHttpServer` 挂 `/debug/calibrate/{start,progress,recalibrate,adjust,stop}` 端点(复用 DebugSession 借用/心跳/release)。单测:端点入参解析 + sid 校验。
+- **Task 3.3**:operate `CalibrationClient`(封装端点 + 轮询 progress + 画面用 MjpegStreamClient)。单测:协议拼装 + 进度解析(仿 DebugSessionClient 单测,链入 control 测试工程)。
+- **Task 3.4**:operate `HouseDebugPageViewModel`/View 重构——16 孔 3×6 总览 + 进页面自动开标 + 逐孔微调(重标/调中心/调半幅/手动电机光源)。保留手动功能走现有 `/debug/command`。
+- **Task 3.5**:调试页保存范围(中心走现有水平位/清晰位更新、半幅走新 well/update 字段),本地预览为准、HTTP 持久化,参照现有 `SaveManualLayerTune`。
+- **验证**:编译 0 错;调试页单测;真机 16 孔标定出图 + 逐孔微调 + 范围存库 → control 重拉。
+
+---
+
+## Phase 4 — front-manament 砍对焦预览窗(开工前详化)
+
+> 依据需求 §8.2 决策 D13。任务骨架:
+
+- **Task 4.1**:去掉 `DetailView.xaml` 的"对焦预览"按钮(语言键 0250)+ `DetailView.xaml.cs:1461 Auto_Click`。
+- **Task 4.2**:删/停 `AutoFocusWindow.xaml(.cs)` + `GetAutofocusPicturesApi`(`PicProvider.cs:395`) 消费链 + `AutoFocusPictureEntity`(`highestScore`/`imageScore`)。
+- **Task 4.3**:核对看胚胎切焦平面(`DetailView.xaml.cs:2159` 等)仍按"0 层=焦面 + 层对称 ±N"工作,不受影响。
+- **验证**:`dotnet build aivfo-front-manament-2.0/ivf_tl_Manage.sln -c Release` 0 错;手点看胚胎切焦平面正常、无对焦预览入口残留报错。
+
+---
+
+## Phase 5 — 真机三门验证 + 启用安全门(开工前详化)
+
+> 依据需求 §九。这是验证 Phase,不写新代码(除最后置位)。
+
+- **Task 5.1**:74000 伪峰——开 DebugSave 存图,验 per-well 垂直范围把远处伪峰排除。
+- **Task 5.2**:真胚胎峰比阈值——真机活体胚胎上确认 `focus_peak_ratio_threshold`(默认 1.2)松紧。
+- **Task 5.3**:EEPROM 4 个手写命令真机生效(进/排气阀时间、扫描间隔、well 水平位);确认对焦不写 EEPROM。
+- **Task 5.4**:电机安全区间不撞机(范围钳位生效,水平[0,220000]/垂直[0,125000])。
+- **Task 5.5**:三门通过后置 `tl_setting.local_autofocus_enabled=1` 启用升级版自动对焦;端到端复跑(放皿/定时触发→范围内对焦→本地排层拍→焦面复用到下次)。
+- **Task 5.6**:use-after-free 压测(标定中反复 release/超时回收同舱)。
+
+---
+
+## 自检(计划 vs 需求文档)
+
+**1. 需求覆盖:**
+- §五 调试页重构 → Phase 3(Task 3.1–3.5)✓
+- §六 采集对焦改造(范围注入/path B/本地排层)→ Phase 2(Task 2.1–2.3)✓
+- §七 数据层(半幅列/存储链)→ Phase 1(Task 1.1–1.6)✓
+- §八 影响面:Java 补 MQTT+加列同步 → Task 1.4/1.5;front-manament 砍对焦预览窗 → Phase 4;禁区(不碰 getTlDataTransmissionSetting/不砍 calCCDPosition) → Task 1.4 注 + Task 2.3 ✓
+- §九 真机三门 → Phase 5 ✓
+- 决策 D1(启用)→Task 5.5;D11(只存范围)→Phase 1 列设计;D12(本地排层)→Task 2.3;D13(砍预览窗)→Phase 4 ✓
+
+**2. 占位符扫描:** Phase 1/2 任务含实际 SQL/C# 代码与命令;Phase 3/4/5 标"开工前详化"为明确的分阶段策略(非 TODO),每个 Task 有具体文件+动作+验证。无 "TBD/handle edge cases" 类空泛词。
+
+**3. 类型一致性:** `FocusRangeResolver.Resolve`→`FocusRangeConfig`(HHalf/VHalf/ExpLo/ExpHi/HCenter/VCenter) 在 Task 2.1 定义、Task 2.2 注入使用,命名一致;`ReadWellFocusRange` 在 Task 2.2 定义、Phase 3 复用,一致。列名 `horizontal_focus_range`/`vertical_focus_range`/`focus_h_range_default`/`focus_v_range_default`/`focus_exposure_min`/`focus_exposure_max` 跨 SQL/Java/C# 全程同名。