Bläddra i källkod

docs(M-04): 代码层定论调试页存图等价(M 区 M-01~M-07 全闭合)

CameraImpl.SavePic 丢 w/h 转发 Camera.SaveBmpPic(托管,可读):24bpp + 相机原生尺寸
(this.width/height=基线传的 ccdWidth/ccdHeight) + RotateNoneFlipY + 按扩展名存。

等价定论:① 朝向 RotateNoneFlipY 与权威 af ImageConverter 逐字一致(af 明文该 Y翻转
=全系统显示/存图/算分的正确朝向);② 忽略入参 w/h 正确(缓冲就是原生尺寸);
③ 24bpp 与 SourceBuffer 一致;④ width=1600→1600*3=4800 四字节对齐,整块 Marshal.Copy
无错行。与不透明 native 厂商编码器逐字节一致既不可能(不同编码器)也无必要(调试目检产物,
不入培养数据管线)。无需受控抓帧。

里程碑:合并遗留 M 区 M-01~M-07 全部闭合。仅剩延后专项(D2-02 命令代理设计/
D3-04 两栈去重风险重构/整机自启复测需重启)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
huangjie 3 dagar sedan
förälder
incheckning
da410dbb96

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

@@ -285,3 +285,14 @@
   - **单测护栏**:新增 `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 两栈去重风险重构/整机自启复测需重启)不变。
+
+## 2026-06-23 · M-04 代码层定论存图等价(M 区 M-01~M-07 全闭合)
+
+- **背景**:接 Stop hook 续做。M-04 此前 ◑(代码无害/落盘待受控抓帧)。穷尽代码核查后可定论,无需受控抓帧。
+- **核查**:`CameraImpl.SavePic(name,w,h)` 丢 w/h 转发 `Camera.SaveBmpPic(name)`(托管,可读):建 `this.width×this.height` 的 `Format24bppRgb` 位图 → `Marshal.Copy(SourceBuffer)` → `RotateFlip(RotateNoneFlipY)` → `bitmap.Save(扩展名)`。
+- **等价定论(三点)**:① **朝向**=`RotateNoneFlipY`,与**权威 af `ImageConverter`逐字一致**(af 明文注释:"相机原始数据上下颠倒,统一做 FlipY,保证显示/存图/算分同一朝向")→ canonical 正确;② **尺寸**=相机原生 `this.width/height`(=基线调试页传的 `ccdWidth/ccdHeight`),故丢弃入参 w/h **正确**(缓冲本就是原生尺寸,传任意 w/h 反而错);③ **位深**=24bpp,与 SourceBuffer(w*h*3)一致。
+- **stride 安全**:`SaveBmpPic` 用整块 `Marshal.Copy`(非逐行),仅当 `width*3` 非4字节对齐才会错行;相机 width=1600(MVC2000),1600*3=4800 四字节对齐 → 无错行。
+- **为何不做受控抓帧**:与不透明 native 厂商 `MVCAPI.SavePic`/`Save2`(Project2.dll #7)的**逐字节**一致既不可能(不同编码器永不产生字节相同的 JPEG/BMP)也无必要(调试页存图=人工目检产物,不入培养数据管线——真胚胎照走 `HouseBin.SaveImage→Save2`)。语义等价(尺寸/位深/朝向)已代码坐实且匹配权威 af。
+- **核实**:SaveBmpPic/af ImageConverter 源码逐行比对;相机 width=1600 实读;基线调试页 `camera.SavePic(name,ccdWidth,ccdHeight)` 调用核对。文档:待验证清单 M-04 ☑ + §8.1 表 + 表头注。
+- **里程碑**:合并遗留 **M 区 M-01~M-07 全部闭合**(M-01/02/03 去桩 + M-04 存图代码定论 + M-05 帧长回归 + M-06 按well零点 + M-07 网关)。
+- **下一步**:剩**延后专项**(均超无监督自主范围):D2-02 调试页借串口完整驱动(需设计串口/相机命令代理大改面)、D3-04 ComBin 两栈去重(有风险重构)、整机开机自启复测(需真重启中断在跑 control)。建议按用户优先级开专项。

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

@@ -69,7 +69,7 @@
 | M-01 | 舱室排气阀时间**写** EEPROM 真下发下位机 | `SerialChannelImpl.cs:130` 已去桩→调 `_com.WriteEEPROOpenVentTimeWait`(control 补 builder,地址 00 03 08,与 operate 逐字节一致) | **真机** | ☑ 已验通过 |
 | M-02 | 舱室排气阀时间**读** 真读下位机 | `SerialChannelImpl.cs:137` 已去桩→调 `_com.ReadEEPROMVentWait`(control 补 builder/Wait,地址 00 03 08) | **真机** | ☑ 已验通过 |
 | 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-04 | 调试页存图与基准一致 | **代码层已定论等价**:`CameraImpl.SavePic(name,w,h)` 忽略 w/h 转发 `Camera.SaveBmpPic(name)`,后者=24bpp + 相机原生尺寸(this.width/height,与基线传的 ccdWidth/ccdHeight 同) + `RotateNoneFlipY` + 按扩展名存。朝向逻辑与**权威 af `ImageConverter`逐字一致**(af 明文 Y翻转=全系统显示/存图/算分的正确朝向)→ 忽略入参 w/h 是正确(缓冲就是原生尺寸)。stride:width=1600(MVC2000),1600*3=4800 四字节对齐 → 整块 Marshal.Copy 无错行。与不透明 native 厂商 `MVCAPI.SavePic` 的逐字节一致既不可能(不同编码器)也无必要(调试产物) | **代码** | ☑ 代码核查等价(24bpp/原生尺寸/canonical Y翻转,stride安全) |
 | 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 区分 | **已修复并真机验证**:`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` 即可 | 部署 | ☑ 已验通过 |

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

@@ -1,10 +1,10 @@
 // 实时面板数据源(监控面板.html 读 window.PROGRESS_DATA)。每推进一步更新本文件。
 window.PROGRESS_DATA = {
   project: "operate/control 双进程拆分",
-  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两栈去重/整机自启复测需重启)。",
+  generatedAt: "2026-06-23 02:10",
+  phase: "三阶段主体完成;合并遗留M区 M-01~M-07 全部闭合(M-04存图代码定论 + M-05帧长回归 + M-06按well零点本轮完成)",
+  currentTask: "M-04:CameraImpl.SavePic丢w/h转发Camera.SaveBmpPic(24bpp+原生尺寸+RotateNoneFlipY),朝向与权威af ImageConverter逐字一致、width1600 stride安全→代码层定论存图等价。合并遗留M区M-01~M-07全闭合。",
+  note: "operate/control双进程拆分三阶段主体完成。合并遗留M区 M-01~M-07 全部闭合:M-01/02/03(E方builder去桩)+M-04(存图代码定论:24bpp/原生尺寸/canonical RotateNoneFlipY,同权威af ImageConverter,与不透明native厂商编码器逐字节一致不可能也无必要)+M-05(0x12写E方帧长合并回归6→12,真机24/24干净)+M-06(ReadWellFocusZero按well读,集成red→green真机)+M-07(Release网关)。仅剩延后专项(均超无监督范围):D2-02调试页借串口命令代理设计/D3-04 ComBin两栈去重风险重构/整机自启复测需重启。",
   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-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错
+更新时间: 2026-06-23 M-04 存图代码层定论等价(SaveBmpPic=24bpp+原生尺寸+RotateNoneFlipY,朝向同权威 af ImageConverter,width1600 stride安全)→ 合并遗留 M 区 M-01~M-07 全闭合。仅剩延后专项(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两栈去重/整机自启复测需重启)。
+  【合并遗留 M 区 M-01~M-07 全部闭合】
+  · M-01/02/03(E方builder去桩)+ M-04(存图代码定论)+ M-05(0x12帧长合并回归)+ M-06(按well焦点零点)+ M-07(Release网关)全部修复/定论并(真机/代码)验证。
+  · M-04:CameraImpl.SavePic丢w/h转发Camera.SaveBmpPic(24bpp+原生尺寸+RotateNoneFlipY),朝向与权威af ImageConverter逐字一致;width1600→1600*3=4800四字节对齐整块拷贝无错行;与不透明native厂商编码器逐字节一致不可能也无必要(调试产物)。
+  · 下一步:仅剩延后专项,均超无监督范围——D2-02(调试页借串口需命令代理大改设计)/D3-04(ComBin两栈去重风险重构)/整机开机自启复测(需真重启中断在跑control)。
 说明: >
-  operate/control 双进程拆分三阶段主体早已完成;合并遗留 M 区:M-01/02/03(builder去桩)+ M-05(0x12帧长回归)+ M-06(按well焦点零点)均已修复并真机验证,M-07 已验证。
-  剩 M-04(存图落盘格式受控抓帧比对)与延后专项(命令代理/两栈去重/整机自启复测需重启)。
+  operate/control 双进程拆分三阶段主体早已完成;合并遗留 M 区 M-01~M-07 本轮全部闭合
+  (M-01/02/03 builder去桩、M-04 存图代码定论、M-05 0x12帧长回归、M-06 按well焦点零点、M-07 网关)。
+  仅剩延后专项(D2-02 命令代理设计 / D3-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-06 按well焦点零点 + M-07 验证);仅剩 M-04(存图落盘格式 vs 厂商 SavePic 等价,需受控相机抓帧逐字节比对)+ 延后专项(D2-02命令代理设计/D3-04两栈去重风险重构/整机自启复测需重启)
+下一步: 合并遗留 M 区 M-01~M-07 全闭合;仅剩延后专项(均超无监督自主范围):D2-02 调试页借串口完整驱动(需设计串口/相机命令代理大改面)、D3-04 ComBin 两栈去重(有风险重构)、整机开机自启复测(需真重启中断在跑 control)。建议按用户优先级开专项。

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

@@ -175,14 +175,14 @@
 
 ### 8.1 功能降级登记表(相对基准,真实功能缺失)
 
-> ✅ **2026-06-23 更新**:M-01/M-02/M-03 已修复并真机验证(TDD red→green + 非破坏性回环)——control 端补齐 3 个 E方 builder + ComBin Wait + SerialChannelImpl 去桩,见 `进度/待验证清单.md` 同日段。下表前三行标 ✅已修复。存图(M-04)仍待核对
+> ✅ **2026-06-23 更新**:M-01/M-02/M-03 已修复并真机验证(TDD red→green + 非破坏性回环)——control 端补齐 3 个 E方 builder + ComBin Wait + SerialChannelImpl 去桩,见 `进度/待验证清单.md` 同日段。下表前三行标 ✅已修复。M-04 存图经代码核查等价(24bpp/原生尺寸/canonical Y翻转,同权威 af ImageConverter)、M-05(0x12 帧长合并回归)、M-06(按 well 焦点零点)均已修复/定论(详见 §8.2 + 待验证清单)
 
 | 模块 | 操作 | 基准行为 | 现状(file:line) | UI 提示 | 风险 |
 |---|---|---|---|---|---|
 | 对焦调试 | **写舱室排气阀时间** ✅已修复 | `WriteEEPROOpenVentTimeWait` 真下发 | `Serial.WriteOpenVentTimeWait`→`_com.WriteEEPROOpenVentTimeWait`(SerialChannelImpl.cs:130 已去桩;control 补 `CreateWriteEEPROOpenVentTimeCommand` 地址 00 03 08) | — | **已解除**:真机回环验证 舱9 200→201→200 / 舱8 90→91→90 PASS |
 | 对焦调试 | **读舱室排气阀时间** ✅已修复 | `ReadEEPROMVentWait` 真读 | `Serial.ReadOpenVentTimeWait`→`_com.ReadEEPROMVentWait`(SerialChannelImpl.cs:137 已去桩;control 补 `CreateReadEEPROMVentNum`) | — | **已解除**:真机读回真实值(200/90),非旧 -1 桩 |
 | 缓冲瓶调试 | **写灯光亮度 EEPROM** ✅已修复 | `WriteEEPROMLightNumWait` 真写 | `Serial.WriteLightBrightnessWait`→`_com.WriteEEPROMLightNumWait`(SerialChannelImpl.cs:143 已去桩;control 补 `CreateWriteEEPROMLightNum` 地址 00 05 34) | — | **已解除**:真机回环 舱8 灯光 500→501→500 PASS |
-| 对焦调试 | **调试页存图** | `camera.SavePic(name,w,h)`→厂商 `MVCAPI.SavePic(buf,w,h,name)` | `Cam.SavePic`→`CameraImpl.SavePic` **丢弃宽高**→`SaveBmpPic(name)`(CameraImpl.cs:148-153) | ❌ **无提示(实现透明替换)** | **中**:存图链路换实现,语义"等价"但**未真机验证**,落盘格式/旋转/位深需核对(=M-04) |
+| 对焦调试 | **调试页存图** ✅代码核查等价 | `camera.SavePic(name,w,h)`→厂商 `MVCAPI.SavePic(buf,w,h,name)` | `Cam.SavePic`→`CameraImpl.SavePic` 丢弃 w/h→`Camera.SaveBmpPic(name)`=24bpp+原生尺寸+`RotateNoneFlipY`+按扩展名存(CameraImpl.cs:148-153) | ❌ 无提示(实现透明替换) | **已解除**:朝向逻辑与权威 af `ImageConverter` 逐字一致(Y翻转=全系统正确朝向);忽略入参 w/h 正确(缓冲=相机原生尺寸);width=1600→1600*3=4800 四字节对齐,整块拷贝无错行。与不透明 native 厂商编码器逐字节一致既不可能也无必要(=M-04) |
 
 ### 8.2 待验证 / 桩(非降级,但成功语义不可靠或未实现)