|
@@ -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级半幅 > 设备级默认 > 引擎硬编码默认。纯逻辑,仿 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# 全程同名。
|