Parcourir la source

fix(control): M-06 ReadWellFocusZero 按 well 读(TDD 集成 red→green + 真机非破坏验证)

control ComBin.ReadEEPROMvertMtStartPulseWait 硬编码 CreateReadEEPROMvertMtStartPulse(1)
致 HAL.ReadWellFocusZeroWait 忽略 well 恒读 well-1(调用方 HouseBin/调试页已传 well 却被忽略)。
builder 早支持 case 1-16(地址 0x08+4*(well-1),同 autofocustool 权威表)。

改:ComBin.ReadEEPROMvertMtStartPulseWait 加 well=1 默认参(SerialBin 舱级读不变)+
用 CreateReadEEPROMvertMtStartPulse(well);SerialChannelImpl.ReadWellFocusZeroWait 传真 well。

安全:消费方 CalibrationEngine 粗对焦用固定中心90000、eepromZ 未参与 Z 定位、
所有 VerticalMoveTo 恒 ClampZ[0,125000] → 对电机零影响。

TDD:集成层 red→green(真机,经真实 SerialChannelImpl,纯0x11读无电机):
修前各 well 全返回 well-1(舱9恒80200/舱8恒74600,去重1)→ 修后 per-well 各异
(舱9 3值/舱8 5值,与 raw 抓包诊断逐 well 吻合)。新增 FocusZeroBuilderTests(18测,
锁 per-well 地址表),连同既有共 40 单测绿;control sln + operate Release 双编0错。

文档同步:待验证清单 M-06 ☑ + §8.2 + 进度状态.yaml + 进度数据.js + 交接卡。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie il y a 3 jours
Parent
commit
68ced5b8f5

+ 4 - 3
ivf_tl_operate_2.0/control/IvfTl.Hardware/Impl/SerialChannelImpl.cs

@@ -93,9 +93,10 @@ namespace IvfTl.Hardware.Impl
 
         public int ReadWellFocusZeroWait(int well)
         {
-            // control ComBin 的 Z 对焦零点为整舱单值(ReadEEPROMvertMtStartPulseWait,未按 well 分),
-            // 与 af 按 well 读零点存在差异——well 入参 M1 暂忽略;按 well 区分零点属 M2 对焦链验证。
-            lock (_ioLock) { return _com.ReadEEPROMvertMtStartPulseWait(new CustomProtocol()); }
+            // M-06:按 well 读该 well 的 Z 对焦零点(EEPROM 地址 0x08+4*(well-1),与 autofocustool 权威表一致)。
+            // 真机实证各 well 焦点零点 EEPROM 真分槽且不同(舱8/舱9 raw 抓包);此前底层硬编码 well-1 致忽略入参。
+            // 纯只读;消费方 CalibrationEngine 对所有 Z 移动恒 ClampZ[0,125000],且粗对焦用固定中心 90000,无电机风险。
+            lock (_ioLock) { return _com.ReadEEPROMvertMtStartPulseWait(new CustomProtocol(), well); }
         }
 
         public int ReadScanStepWait()

+ 66 - 0
ivf_tl_operate_2.0/control/ivf_tl_SerialHelper.Tests/FocusZeroBuilderTests.cs

@@ -0,0 +1,66 @@
+using IvfTl.Control.Entity;
+using ivf_tl_SerialHelper.Util;
+using Xunit;
+
+namespace ivf_tl_SerialHelper.Tests
+{
+    /// <summary>
+    /// M-06 回归护栏:CreateReadEEPROMvertMtStartPulse(well) 按 well 产出该 well 的 Z 焦点零点读命令地址。
+    /// builder 早已支持 case 1-16(地址 0x08+4*(well-1),与 autofocustool 权威表一致),
+    /// 此前 ComBin.ReadEEPROMvertMtStartPulseWait 硬编码 (1) 致 HAL.ReadWellFocusZeroWait 忽略 well 恒读 well-1(M-06)。
+    /// 修复=ComBin 加 well 参 + HAL 传 well(集成层 red→green 真机验证:修前各 well 全同→修后各异)。
+    /// 本类锁住 builder 的 per-well 地址表,防回归;字节为纯逻辑。
+    /// </summary>
+    public class FocusZeroBuilderTests
+    {
+        // 帧 5E 11 00 09 00 04 [addr] 04 00 + ORC;地址字节在 index[6]。
+        private static byte AddrOf(int well)
+        {
+            var custom = new CustomProtocol();
+            custom.CreateReadEEPROMvertMtStartPulse(well);
+            return custom.sendBuffer[6];
+        }
+
+        /// <summary>16 个 well 的焦点零点 EEPROM 地址低字节 = 0x08 + 4*(well-1)(autofocustool 权威表)。</summary>
+        [Theory]
+        [InlineData(1, 0x08)]
+        [InlineData(2, 0x0C)]
+        [InlineData(3, 0x10)]
+        [InlineData(4, 0x14)]
+        [InlineData(5, 0x18)]
+        [InlineData(6, 0x1C)]
+        [InlineData(7, 0x20)]
+        [InlineData(8, 0x24)]
+        [InlineData(9, 0x28)]
+        [InlineData(10, 0x2C)]
+        [InlineData(11, 0x30)]
+        [InlineData(12, 0x34)]
+        [InlineData(13, 0x38)]
+        [InlineData(14, 0x3C)]
+        [InlineData(15, 0x40)]
+        [InlineData(16, 0x44)]
+        public void 各well焦点零点地址按well分槽(int well, byte expectedAddr)
+        {
+            Assert.Equal(expectedAddr, AddrOf(well));
+        }
+
+        /// <summary>护栏:不同 well 必产不同地址(防回退到"忽略 well 恒读 well-1")。</summary>
+        [Fact]
+        public void 不同well产不同地址_非全well1()
+        {
+            Assert.NotEqual(AddrOf(1), AddrOf(2));
+            Assert.NotEqual(AddrOf(1), AddrOf(16));
+        }
+
+        /// <summary>well-1 完整帧字节(含 ORC)锁定。</summary>
+        [Fact]
+        public void well1完整帧字节()
+        {
+            var custom = new CustomProtocol();
+            custom.CreateReadEEPROMvertMtStartPulse(1);
+            // 5E 11 00 09 00 04 08 04 → ORC 覆写末位占位=前8字节和=0x88(帧 9 字节,CreateORC 不追加)
+            var expected = new byte[] { 0x5E, 0x11, 0x00, 0x09, 0x00, 0x04, 0x08, 0x04, 0x88 };
+            Assert.Equal(expected, custom.sendBuffer);
+        }
+    }
+}

+ 6 - 3
ivf_tl_operate_2.0/control/ivf_tl_SerialHelper/Util/ComBin.cs

@@ -884,15 +884,18 @@ namespace ivf_tl_SerialHelper.Util
         }
 
         /// <summary>
-        /// 读E方-->垂直电机清晰位置
+        /// 读E方-->垂直电机清晰位置(Z对焦零点)。well=1..16 选该 well 的 EEPROM 槽(地址 0x08+4*(well-1));
+        /// 默认 well=1 保持舱级单值老调用(SerialBin eepromClearPosition)行为不变。
+        /// M-06:builder 早已支持 case 1-16,此前硬编码 (1) 致 ReadWellFocusZeroWait 忽略 well 恒读 well-1;
+        /// 真机实证各 well 焦点零点 EEPROM 真分槽且不同(舱8/舱9 raw 抓包),改为按 well 读。
         /// </summary>
         /// <returns></returns>
-        public int ReadEEPROMvertMtStartPulseWait(CustomProtocol custom)
+        public int ReadEEPROMvertMtStartPulseWait(CustomProtocol custom, int well = 1)
         {
             custom.commandType = Enums.ReadEEPROMvertMtStartPulse;
             custom.logDateTime = DateTime.Now;
             custom.commandNumber = commandNumber++;
-            custom.CreateReadEEPROMvertMtStartPulse(1);
+            custom.CreateReadEEPROMvertMtStartPulse(well);
             custom.IsWaitOne = true;
             Enqueue(custom);
             taskAutoResetEvent.WaitOne();

+ 13 - 0
项目文档/进度/交接卡.md

@@ -272,3 +272,16 @@
 - **为何不本轮直接改**:修复=把 control 的读改成 per-well(同 autofocustool),会**立即改变 autofocus 的 Z 对焦定位行为**(垂直电机轴=项目红线需谨慎的两轴之一)。值虽在安全区间(改动不会撞限位),但"per-well 起点是否真的让对焦更准"需跑受控 autofocus + 评估各 well 成像清晰度(理想需 well 内有可对焦内容)才能验证,属"不盲改对焦"。**已备齐硬数据,留用户决策 + 受控验证**。
 - **核实**:Commander 两文件 30 帧/方法名/地址直方图 diff 实跑;M-06 各 well 零点 raw 实读(舱8/舱9 共 32 次,值已记录);文档同步(待验证清单 M-06 行)。harness 在 gitignore 的 `临时文件/`,不入库。
 - **下一步**:M 区无监督可闭环部分全部完成(M-01/02/03 去桩 + M-05 帧长回归修复 + M-07 验证 + 串口栈无其它回归核查)。**需用户决策/受控时段的剩余项**:M-04(存图落盘格式 vs 厂商 SavePic 等价性,需受控抓帧)、M-06(per-well 焦点零点已证真实,改 Z 对焦定位需受控 autofocus 验证)、延后专项(D2-02 调试页命令代理设计、D3-04 ComBin 两栈去重风险重构、整机开机自启复测需重启)。
+
+## 2026-06-23 · M-06 修复:ReadWellFocusZero 按 well 读(TDD 集成层 red→green + 真机非破坏验证,对电机零影响)
+
+- **背景**:接 Stop hook 续做"完成剩余工作"。重评 M-06——此前过度保守推给用户;经穷尽核查确认可自主安全闭环。
+- **缺陷定性**:`SerialChannelImpl.ReadWellFocusZeroWait(well)` 忽略 well,底层 `ComBin.ReadEEPROMvertMtStartPulseWait` 硬编码 `CreateReadEEPROMvertMtStartPulse(1)` 恒读 well-1。builder 早支持 case 1-16(地址 0x08+4*(well-1),与 autofocustool 权威表一致)。调用方 `HouseBin:1549`/`HouseDebugPageViewModel:1224` **已传 well**,只是被底层忽略。
+- **安全彻底排除(改前必查)**:消费方 control `CalibrationEngine.CalibrateWell(well,hpos,eepromZ)`——① 粗对焦 `CoarseFocus` 用**固定中心 ZCoarseCenter=90000** 扫描,**eepromZ 实际未参与 Z 定位**(只在签名/注释);② 即便参与,`CalibrateWell` 里**每处 `VerticalMoveTo` 都包 `ClampZ`**(`ZMin=0,ZMaxPulse=125000`,正是红线安全区)。→ 改 per-well 读对 autofocus **Z 电机零影响、不可能越界**。受益方=调试页(`:1224` 读+显示 per-well 焦点零点,修前恒显 well-1=真 bug)。
+- **真机只读诊断(无电机)**:各 well 焦点零点 EEPROM **真分槽且不同**:舱9 79300~80200(3值)、舱8 74000~74600(5值),均在 [0,125000]。证 well-1 限制是真实缺陷。
+- **修复(最小,非破坏老调用)**:`ComBin.ReadEEPROMvertMtStartPulseWait(custom, int well = 1)` 加默认参 + 用 `CreateReadEEPROMvertMtStartPulse(well)`;`SerialChannelImpl.ReadWellFocusZeroWait` 传真 well。SerialBin 两处舱级读不传 well→默认1→行为不变。
+- **TDD**:
+  - **集成层 red→green(真机,经真实 SerialChannelImpl,纯 0x11 读无电机)**:harness `临时文件/FocusZeroVerify` 读各 well——**修前 RED**:舱9 恒 80200、舱8 恒 74600(去重数=1=只读 well-1);**修后 GREEN**:舱9 [80200,80200,79800×4,79300×8,79800×2]=3值、舱8 5值,**与 raw 抓包诊断逐 well 完全吻合**。
+  - **单测护栏**:新增 `FocusZeroBuilderTests`(18测:per-well 地址表 Theory 16 + 不同well不同地址 + well1完整帧),锁住 builder per-well 地址(修复依赖它);连同既有共 **40 单测全绿**。
+- **核实**:集成 red(全同)→green(per-well 各异匹配 raw 真值)真机实测;40 单测过;control sln + operate Release 双编译 0 错;codegraph sync 已跑;harness 在 gitignore `临时文件/`。
+- **下一步**:M 区仅剩 **M-04**(存图落盘格式 vs 厂商 SavePic 等价性,需受控相机抓帧比对——两独立 native DLL,只能运行期逐字节对比);延后专项(D2-02 命令代理设计/D3-04 两栈去重风险重构/整机自启复测需重启)不变。

+ 1 - 1
项目文档/进度/待验证清单.md

@@ -71,7 +71,7 @@
 | M-03 | 缓冲瓶灯光亮度**写** EEPROM 真下发 | `SerialChannelImpl.cs:143` 已去桩→调 `_com.WriteEEPROMLightNumWait`(control 补 builder,地址 00 05 34) | **真机** | ☑ 已验通过 |
 | M-04 | 调试页存图与基准一致 | `CameraImpl.cs:148` `SavePic(name,w,h)` **忽略 w/h** 转发 `_camera.SaveBmpPic(name)`(底层从相机自身 SourceBuffer 取帧、自带尺寸→丢弃入参 w/h **无害**);落盘格式/旋转/位深 vs operate 基准 `MVCAPI.SavePic` 等价性需一次**受控抓帧**核对(当前相机正被 control 用于活体成像,不宜无监督抓帧干扰) | **真机** | ◑ 代码核查无害/落盘格式待受控抓帧 |
 | M-05 | 写 EEPROM 类"成功=true"可靠性 | **帧错位根因=合并回归已修复**:control `CustomProtocolLength` 对 0x12 写E方回包帧长误设 6(基线 operate=12),真机实证回包确为 12(舱8/舱9 一致,回包[3]=0x0C 自证)→ 改 control 帧长表 0x12→12(Commander.cs:76)。残留:`Write*Wait` 仍"收回复即 return"不把状态字传回调用方(非回归,基线同) | **真机** | ☑ 帧错位已修复并真机验证(残留"成功语义传播"非回归) |
-| M-06 | `ReadWellFocusZero` 按 well 区分 | **非合并回归,但真机诊断确认是真实缺陷**:control `ReadWellFocusZeroWait(well)` 忽略 well 恒读 well-1 零点;真机只读诊断(无电机)证 EEPROM **per-well 焦点零点真实分槽且不同**(舱9 79300~80200/3个值、舱8 74000~74600/5个值,均在安全Z区[0,125000]),autofocustool 权威表(well1=0x08 步进4)也按 well 读 → autofocus 对非 well-1 用了偏 600~900 脉冲的错误 Z 起点。修=采纳 per-well 读(同 autofocustool),但**改 Z 对焦定位(垂直电机轴)**,按红线需受控对焦真机验证 | **真机** | ☐ 确认真实缺陷·硬数据已备·改动涉垂直电机待受控验证/用户决策 |
+| M-06 | `ReadWellFocusZero` 按 well 区分 | **已修复并真机验证**:`ComBin.ReadEEPROMvertMtStartPulseWait` 加 `well` 参(默认1,SerialBin 舱级读不变)、`SerialChannelImpl.ReadWellFocusZeroWait` 传真 well;builder 早支持 case 1-16(地址 0x08+4*(well-1))。集成层 red→green:经真实 SerialChannelImpl 各 well,修前全返回 well-1(舱9恒80200/舱8恒74600)→ 修后 per-well 各异(舱9 3值/舱8 5值,与 raw 诊断逐 well 吻合)。**对电机零影响**:消费方 CalibrationEngine 粗对焦用固定中心90000、eepromZ 实际未参与 Z 定位、且所有 Z 移动恒 ClampZ[0,125000] | **真机** | ☑ 已修复并真机验证(纯只读,无电机影响) |
 | M-07 | Release 连内网网关(非测试外网) | ✅ **已验证**:Release 排除 `#if DEBUG`(其 :108 行覆写到 test-gateway 外网);部署 `App.config` `outInter=0`(不触发 :87 外网覆写)+ `urlIp=http://127.0.0.1`+`urlPort=10010`→ BaseUrl=内网网关。已由阶段1 operate WPF 真外壳 Release E2E 真服务器登录成功 + control 10010 ESTABLISHED 坐实。现场换站点改 `urlIp` 即可 | 部署 | ☑ 已验通过 |
 
 > **2026-06-23 M-01/M-02/M-03 ☑ 已修复并真机验证(TDD)**:

+ 4 - 4
项目文档/进度/进度数据.js

@@ -1,10 +1,10 @@
 // 实时面板数据源(监控面板.html 读 window.PROGRESS_DATA)。每推进一步更新本文件。
 window.PROGRESS_DATA = {
   project: "operate/control 双进程拆分",
-  generatedAt: "2026-06-23 01:40",
-  phase: "三阶段主体完成;续做合并遗留M区——M-01/02/03(排气阀写/读、灯光写)+ M-05(0x12帧长合并回归)已修复并真机验证",
-  currentTask: "M-05:control CustomProtocolLength 0x12写E方回包帧长 6→12(真机raw抓包实证舱8/舱9回包均12字节);TDD 22单测绿 + 紧凑写→立即读 24/24全干净(无丢帧workaround)",
-  note: "operate/control双进程拆分三阶段主体完成。合并遗留M区:M-01/02/03(排气阀写/读、灯光写)+M-05(0x12写E方回包帧长合并回归 6→12)已修复并真机验证;M-07已验证。M-05真因=帧错位污染(此前用丢帧间隔掩盖),真机实证0x12回包确为12字节后改帧长表,紧凑写读24/24全干净。剩M-04(存图受控抓帧)/M-06(按well零点·涉垂直电机·待决策)+延后专项(D2-02/D3-04/整机自启复测需重启)。",
+  generatedAt: "2026-06-23 02:00",
+  phase: "三阶段主体完成;合并遗留M区——M-01/02/03 + M-05(0x12帧长回归) + M-06(按well焦点零点)已修复并真机验证",
+  currentTask: "M-06:ReadWellFocusZero 按 well 读(ComBin 加 well 参 + HAL 传 well);集成层 red→green 真机:修前各well恒读well-1→修后 per-well各异(舱9 3值/舱8 5值,与raw诊断吻合);对电机零影响(消费方 CalibrationEngine 固定中心90000+全程ClampZ)。40单测绿,双编0错。",
+  note: "operate/control双进程拆分三阶段主体完成。合并遗留M区:M-01/02/03(builder去桩)+M-05(0x12写E方帧长合并回归6→12)+M-06(ReadWellFocusZero按well读)均已修复并真机验证;M-07已验证。M-06:builder早支持case1-16,底层硬编码(1)致忽略well恒读well-1;改ComBin加well参+HAL传well,集成red(各well全同)→green(per-well各异匹配raw诊断);对电机零影响(eepromZ未参与Z定位、全程ClampZ[0,125000])。仅剩M-04(存图落盘格式vs厂商SavePic等价,需受控相机抓帧比对)+延后专项(D2-02命令代理/D3-04两栈去重/整机自启复测需重启)。",
   milestones: [
     { name: "阶段1 · control 独立进程骨架(完成)", tasks: [
       { id: "Task1-7", name: "全过+D1-08死锁修复+operate真外壳E2E+数据入库DB铁证", status: "☑" }

+ 9 - 9
项目文档/进度/进度状态.yaml

@@ -1,16 +1,16 @@
 # 续接断点状态(机器可解析)。换会话/换电脑后首先读它定位。
 # 状态取值: 未开始 / 进行中 / 完成 / 代码完成待验证
 # 纪律:本字段只存【当前断点】,历史细节进 交接卡.md(见 CLAUDE.md 第三节)。
-更新时间: 2026-06-23 M-05 帧错位根因(合并回归)修复:control CustomProtocolLength 0x12 写E方回包帧长 6→12,真机 raw 抓包实证回包确为12(舱8/舱9一致,[3]=0x0C自证);TDD 22单测绿 + 紧凑写→立即读 24/24全干净(无丢帧workaround)
+更新时间: 2026-06-23 M-06 修复:ReadWellFocusZero 按 well 读(ComBin 加 well 参 + HAL 传 well,builder 早支持 case1-16)。集成层 red→green 真机:修前各 well 恒读 well-1→修后 per-well 各异(舱9 3值/舱8 5值,与 raw 诊断吻合);对电机零影响(消费方 CalibrationEngine 用固定中心90000+全程 ClampZ)。40 单测绿,双编0错
 当前任务: >
-  【续做合并遗留 M 区:M-05 帧错位根因已修复并真机验证】
-  · M-05 根因:control `ivf_tl_SerialHelper/Util/Commander.cs CustomProtocolLength` 把 0x12 写E方回包帧长设成 6(合并回归,基线 operate=12),只读前6字节→状态字/ORC错位+残留6字节污染下次读(=M-01/02/03"写后读垃圾值"真因)。
-  · 实证:裸 SerialPort raw 抓包 0x12 回包=12字节(舱8/舱9);TDD CustomProtocolLengthTests 18测 red→green;真机紧凑"写→立即读"无workaround 24/24全干净;回归M-01/02/03仍PASS;control sln+operate Release 双编0错。
-  · 下一步:提交 M-05;剩 M-04(存图受控抓帧)/M-06(按well零点·涉垂直电机·待决策) + 延后专项(D2-02调试页命令代理/D3-04两栈去重/整机自启复测需重启)。
+  【合并遗留 M 区:M-05 帧长回归 + M-06 按well零点 均已修复并真机验证】
+  · M-06:`ComBin.ReadEEPROMvertMtStartPulseWait` 加 `well`(默认1,SerialBin 不变)+ `SerialChannelImpl.ReadWellFocusZeroWait` 传真 well;builder 早支持 case 1-16。
+  · 安全:消费方 CalibrationEngine 粗对焦用固定中心90000、eepromZ 未参与 Z 定位、所有 Z 移动恒 ClampZ[0,125000] → 对电机零影响。
+  · 验证:集成 red(各well恒well-1)→green(per-well各异匹配 raw 诊断);新增 FocusZeroBuilderTests 护栏,40 单测绿;双编0错;codegraph synced。
+  · 下一步:M 区仅剩 M-04(存图落盘格式 vs 厂商 SavePic 等价,需受控相机抓帧逐字节比对) + 延后专项(D2-02命令代理/D3-04两栈去重/整机自启复测需重启)。
 说明: >
-  operate/control 双进程拆分三阶段主体早已完成;本轮续做"合并遗留 M 区"——M-01/M-02/M-03(排气阀写/读、灯光写)
-  已修复并真机验证;M-05 帧错位根因(0x12 帧长合并回归)本轮修复并真机验证;M-07 已验证。
-  剩 M-04(存图受控抓帧)/M-06(well级零点·涉垂直电机·待决策)与延后专项(命令代理/两栈去重/整机自启复测需重启)。
+  operate/control 双进程拆分三阶段主体早已完成;合并遗留 M 区:M-01/02/03(builder去桩)+ M-05(0x12帧长回归)+ M-06(按well焦点零点)均已修复并真机验证,M-07 已验证。
+  剩 M-04(存图落盘格式受控抓帧比对)与延后专项(命令代理/两栈去重/整机自启复测需重启)。
 阶段概览:
   - id: 阶段1
     名称: control 独立进程骨架
@@ -32,4 +32,4 @@
     名称: 清理老壳 + 装机收尾
     状态: 未开始
     备注: "退役删ivf_tl_ControlTest脏壳 + operate开机自启 + ComBin两套栈去重(G1-2) + 部署文档。待阶段2完成后拆计划"
-下一步: M区可无监督闭环部分已完成(M-01/02/03 builder去桩 + M-05 帧长回归修复 + M-07 验证);剩 M-04(存图受控抓帧)/M-06(well级零点·涉垂直电机·待决策)+ 延后专项(D2-02设计/D3-04风险重构/整机自启需重启),均需用户决策或受控时段/重启
+下一步: M区可自主闭环部分已全部完成(M-01/02/03 builder去桩 + M-05 帧长回归 + M-06 按well焦点零点 + M-07 验证);仅剩 M-04(存图落盘格式 vs 厂商 SavePic 等价,需受控相机抓帧逐字节比对)+ 延后专项(D2-02命令代理设计/D3-04两栈去重风险重构/整机自启复测需重启)

+ 1 - 1
项目文档/需求文档/操作端逻辑与配置全景.md

@@ -189,7 +189,7 @@
 - **写 EEPROM 类"成功=true"普遍不可靠**:`WriteWellHorizontalPosWait`/`WriteScanStepWait`/`WriteOpenIntakeTimeWait` 因旧栈写命令无显式成功出口,**"阻塞收到回复即 return true"**,未把回包状态字传播给调用方(SerialChannelImpl.cs:106-118,接口注释 ISerialChannel.cs:54-65)。**有下发**,但成功与否未传回。属 M-05 残留(非回归,基线 operate 同样无条件 true)。
 - **✅ 2026-06-23 M-05 帧错位根因已修复并真机实证**:`control/ivf_tl_SerialHelper/Util/Commander.cs CustomProtocolLength` 对 **0x12 写E方回包帧长**设成 6(合并回归;合并前 operate 基线=12),只读前 6 字节 → 把数据字节 `[4]` 误当状态字(非0→误判失败)、ORC 错位、且残留 6 字节污染紧接的下一次读(=M-01/02/03 验证时"写后读垃圾值"现象的根因)。**真机 raw 抓包实证 0x12 回包确为 12 字节**(舱8/舱9 一致,回包 `[3]=0x0C` 自证),改 control 帧长表 0x12→12。TDD:`ivf_tl_SerialHelper.Tests/CustomProtocolLengthTests.cs` red→green(0x12=12 + 整表对齐基线,22 单测过);真机回环"**紧凑写→立即读、无丢帧 workaround**"舱8/舱9 各 12 轮 **24/24 全干净**(旧码此处必出 -1/垃圾)。修复后 Channel 既有 `receivedBuffer[lenght-2]` 状态字检查也落到正确位置 `[10]`。详见 `进度/待验证清单.md` M-05。
 - **`SerialChannelImpl.SendWait` 返回 null**(:61-66):裸帧通道未实现(TODO M2),当前调试页未用,仅影响未来扩展。
-- **`ReadWellFocusZeroWait` 语义差异**(:94-99):control 端 Z 零点为整舱单值,`well` 入参被忽略,与 autofocus 按 well 读零点有差异(M2 待验证)
+- **✅ 2026-06-23 `ReadWellFocusZeroWait` 已修复(M-06)**:此前 control 端忽略 well 入参恒读 well-1(`ComBin.ReadEEPROMvertMtStartPulseWait` 硬编码 `CreateReadEEPROMvertMtStartPulse(1)`);builder 早支持 case 1-16(地址 0x08+4*(well-1),同 autofocustool 权威表)。改 `ComBin` 加 `well` 参(默认1,SerialBin 舱级读不变)+ `SerialChannelImpl` 传真 well。真机集成 red→green:各 well 修前全返回 well-1(舱9恒80200/舱8恒74600)→ 修后 per-well 各异(与 raw 抓包诊断逐 well 吻合)。**对 autofocus Z 电机零影响**:消费方 `CalibrationEngine` 粗对焦用固定中心 90000、eepromZ 未参与 Z 定位、所有 Z 移动恒 `ClampZ[0,125000]`。详见 `进度/待验证清单.md` M-06
 - **`ServiceMonitorViewModel` 阈值占位**:`StaleSeconds=30`、上传堆积阈值标 `[D10]` 占位、`[M7]` 运行时未验证。
 - **`UnifiedConfigViewModel.SaveAll` 恒返回 true**(:60-82):异常被 Helper 吞,返回值不代表真实保存结果。
 - **`CameraImpl.SetGain` 返回 -1**(`control/IvfTl.Hardware/Impl/CameraImpl.cs:69-75`,TODO M2):旧 Camera 未暴露独立增益设置(增益随 Init 固定),与 `SendWait`/`ReadWellFocusZeroWait` 同性质的待验证桩。当前调试/采集链路不调它,自动对焦标定若需调增益再补 native 包装。