فهرست منبع

docs(progress): D2-02 第一阶段批B(DebugSessionManager)落地回写——断点/交接卡/计划表/面板

huangjie 2 روز پیش
والد
کامیت
d1cb3fa4d5

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

@@ -401,3 +401,18 @@
 - **产出**:spec `需求文档/specs/2026-06-23-D2-02-调试页命令代理-design.md`(14节,已提交 d5afcba) + 实现计划 `开发计划/2026-06-23-阶段2-D2-02-调试页命令代理实现计划.md`。计划按 Scope Check 拆 3 子阶段:①control后端(会话/分发/钳位/超时回收,纯 xUnit 单测 + curl 真机,**bite-sized 完整 Task0-10**)②MJPEG预览(提纲)③operate接入+真机V-012(提纲,解锁D3-04)。分支 `feature/d2-02-debug-command-proxy`。
 - **核实**:codegraph 实读改面(HouseDebugPageViewModel 全操作面/HouseGateImpl 借用闸门/CameraImpl.StartPreview=Usb2Start 贴HWND 跨进程做不到→改帧传输/ControlHttpServer switch路由/Program装配/ISerialChannel 54方法签名);spec 自查(无悬空TBD)+ 用户 review 通过;计划自查(对照 spec 覆盖、类型一致 DebugCommandResult/code串/Execute签名贯穿)。**代码一行未写**——本轮只到设计+计划。
 - **下一步**:执行第一阶段 control 后端(建议子代理驱动逐 Task 实现,真机 curl 冒烟由 Claude 自主跑)。spec+计划(本分支)待并 main。
+
+---
+
+## 2026-06-23 · D2-02 第一阶段(control后端)批B 完成:DebugSessionManager 会话/超时/分发/红线钳位(TDD)
+
+- **背景**:接批A(单测工程 IvfTl.ControlHost.Tests + 数据类 DebugSession/DebugCommandResult + 红线钳位 MotorClamp + 测试替身 Fakes/FakeHardware.cs)继续做安全核心。批B = 计划 Task4-7,全程严格 TDD(每 Task 先写失败测试→跑红→实现→跑绿),逐 Task 单独 commit。
+- **改动(新增源码 1 个 + 测试 3 处)**:
+  1. **Task4** 新建 `ivf_tl_ControlHost/Debug/DebugSessionManager.cs`:注入 `Func<int,IHouseGate>`(按舱取闸门)+ `Func<DateTime>`(注入时钟,可纯单测)+ ttlMs + log。ConcurrentDictionary 会话表。`Acquire`(借闸门→gate.Acquire(OperateDebug)→失败返 BUSY、新建 sessionId)/`Heartbeat`(刷 LastSeen,失效返 SESSION_EXPIRED)/`Release`(TryRemove+Dispose,未知会话幂等返 Ok)。新测试 `DebugSessionManagerTests.cs` 4 绿。commit 968ad59。
+  2. **Task5** 安全地基超时回收:`SweepExpired`(LastSeen+ttl<now 自动 Dispose 归还)。追加 2 测试(超时回收后 IsCapturePaused=false + Heartbeat 失效;心跳续约保活)。**关键踩坑见下**。commit b7e8e40。
+  3. **Task6** 命令分发 `Execute(sid, op, JObject args)`:会话校验(失效 SESSION_EXPIRED)+ switch 分发读数/握手/阀/LED(ReadTemp/ReadPressure/ReadDoor/ShakeHands/OpenLed/.../HouseVent),未知 op 经占位 ExecuteMotorOrEeprom 返 BAD_OP;catch 异常返 HARDWARE_ERROR。文件顶加 `using Newtonsoft.Json.Linq;`。新测试 `DebugExecuteTests.cs` 4 绿。commit cdc4976。
+  4. **Task7【红线重点】** 替换 ExecuteMotorOrEeprom 为真实电机/EEPROM 分发:VerticalMoveTo/HorizontalMoveTo 越界(MotorClamp.IsVerticalInRange 垂直[0,125000]/IsHorizontalInRange 水平[0,220000])**返 OUT_OF_RANGE 且不下发串口**;Forward/Backward 用 CurrentVer/CurrentHor 算相对目标后同样钳位;Reset 归零;WriteScanStep/WriteOpenIntakeTime/WriteOpenVentTime/WriteWellHorizontalPos 直发。追加 4 测试(在范围下发/越界拒绝不下发/相对越界/EEPROM写)8 绿。commit 5922797。
+- **踩坑(Task5)**:批A 的 `FakeLease.Dispose()` 只置 `Disposed=true`、**不触发 gate.ResumeCapture**,导致 `Sweep_Reclaims_After_Ttl_And_Resumes` 断言 `IsCapturePaused==false` 失败(实跑 red:Actual=True)。按计划改 FakeLease 持 `FakeGate` 引用、`Dispose(){Disposed=true;_gate.ResumeCapture();}`,并把 FakeGate.Acquire 内 `new FakeLease(_serial,u)`→`new FakeLease(this,_serial,u)`。这对齐真实 HardwareLeaseImpl.Dispose 会调 gate 恢复采集。改后 6 绿。
+- **核实**:每 Task 都贴了真实 red→green 输出。全量 `dotnet test IvfTl.ControlHost.Tests` = **25 绿 0 失败**(Smoke1 + MotorClamp 10 + SessionManager 6 + Execute 8)。codegraph sync = Already up to date(监视器实时同步)。蓝本零偏离:核对 FakeSerial.Calls 记录格式(VMoveTo({p})/WriteScanStep({p})/Temp/OpenLed)、ISerialChannel 方法签名(WriteOpenIntakeTimeWait 第二参 bool 用默认)、批A 数据类字段(CurrentHor/CurrentVer 默认 -1)全对得上,照抄即编译0错+测试绿。
+- **8 个 commit(本分支)**:批A b4da856/e3fa590/ae80804/be88acf + 批B 968ad59/b7e8e40/cdc4976/5922797。
+- **下一步**:批C = Task8 ControlHttpServer 加 /debug 路由(acquire/heartbeat/release/command 转 DebugSessionManager) + Task9 Program.cs 装配 DebugSessionManager(按舱取 gate 委托)+ 起 SweepExpired 看门狗定时器;再 Task10 真机 curl 冒烟(Claude 自主跑)。本分支待并 main。

+ 1 - 1
项目文档/进度/工作计划表.md

@@ -26,7 +26,7 @@
 | 阶段 | 内容 | 状态 | 出口验收 |
 |------|------|------|----------|
 | **阶段1** | control 独立进程骨架 | 🟢 代码完成·真机闭环打通(待并 main) | control 独立 exe 能起✓、HTTP探活/读状态✓、续命✓、单实例✓、硬件获取✓、**真机自控环运行✓**;阻塞闭环的 D1-08 串口握手死锁已修复 |
-| **阶段2** | 监控补全 + 调试借串口 + 受护栏停止 | 🟢 监控/受护栏停止/借串口让路 已实现+真机验;**D2-02 调试页完整借串口:spec + 第一阶段实现计划已出(2026-06-23),代码待执行** | 监控页跨进程 /status 显示完整✓;受护栏 /shutdown 安全停✓;/serial 让路✓;**D2-02(会话式借用+通用command分发+MJPEG预览+红线钳位+超时自动回收)spec `2026-06-23-D2-02-调试页命令代理-design.md` + 计划 `2026-06-23-阶段2-D2-02-...实现计划.md`(拆3阶段,①control后端 bite-sized 完整) 已出,分支 feature/d2-02-debug-command-proxy,待执行** |
+| **阶段2** | 监控补全 + 调试借串口 + 受护栏停止 | 🟢 监控/受护栏停止/借串口让路 已实现+真机验;**D2-02 调试页完整借串口:第一阶段 control 后端 批A+批B 已落地(2026-06-23,TDD 25单测绿),待批C(HTTP路由+装配)+真机冒烟** | 监控页跨进程 /status 显示完整✓;受护栏 /shutdown 安全停✓;/serial 让路✓;**D2-02(会话式借用+通用command分发+MJPEG预览+红线钳位+超时自动回收)spec+计划已出;第一阶段 control 后端:批A(单测工程/数据类 DebugSession+DebugCommandResult/红线钳位 MotorClamp/Fakes 测试替身)✓ + 批B(DebugSessionManager:acquire/release/heartbeat/幂等+超时自动回收+Execute 命令分发含电机红线钳位/EEPROM写)✓ 全 TDD 25 单测绿,8 commit 在 feature/d2-02-debug-command-proxy;剩 批C=Task8 ControlHttpServer /debug 路由+Task9 Program 装配 SweepExpired 看门狗,再 Task10 真机 curl 冒烟,待并 main** |
 | **阶段3** | 清理老壳 + 装机收尾 | 🟢 退役删ControlTest+部署文档+开机自启 已做;**D1-10 control oplog审计埋点已迁移+真机验证**;**D3-05 control崩溃看门狗已实现+真机验证(2026-06-23)**;**HIL硬件在环回归套件已入库+真机验证(2026-06-23)**;**配置收敛 已完成+真机验证(2026-06-23)**;删operate死栈延后 | 退役删 ivf_tl_ControlTest✓;双进程部署指南✓;开机自启✓;**D1-10 oplog审计迁移到control活栈✓**;**D3-05 看门狗(崩溃重拉/DPAPI凭据/可暂停停止卸载)✓**;**HIL套件 IvfTl.Hardware.HilTests(守护M-05帧长/M-06按well焦点/M-01-03 EEPROM写,门控Skip+默认零写入)✓**;**配置收敛(operate↔control连接组7键单一数据源tl-shared.config经<appSettings file=>合并读+operate删12换气CCD死键,真机改一处对称生效)✓**;ComBin删operate死栈(D3-04,被D2-02阻塞)仍延后 |
 
 ---

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

@@ -1,10 +1,10 @@
 // 实时面板数据源(监控面板.html 读 window.PROGRESS_DATA)。每推进一步更新本文件。
 window.PROGRESS_DATA = {
   project: "operate/control 双进程拆分",
-  generatedAt: "2026-06-23 20:30",
-  phase: "三阶段主体完成;M区全闭合;D1-10 oplog审计迁移;D3-05 看门狗;HIL硬件在环回归套件;配置收敛(连接组单一数据源+删死键)真机验证",
-  currentTask: "配置收敛(operate↔control 连接组单一数据源 + operate死键清理,=昨日建议「配置收敛」):7个共享连接键(urlIp/urlPort/mqttIp/mqttPort/kfkaIP/kfkaPort/outInter)收敛为唯一数据源tl-shared.config,operate/control各自App.config经<appSettings file=>只读合并→两进程读取C#零改动、换机只改一处;写入由operate AppConfigHelper经新SharedConfigStore(XDocument直写)收口;operate删12个换气/CCD死副本键。子代理驱动6任务+每任务两阶段审查。真机:control经..\\tl-shared.config读到ServerUrl=127.0.0.1:10010、改urlPort 10010↔19999重启对称生效、回归operate6/SerialHelper40/HIL2过2跳/双编译0错。",
-  note: "痛点:双进程拆分后operate与control的App.config约25键逐字重复、control那份注释明写'必须手动同步'=漂移源头。核查厘清:operate的AppData不读那12个换气/CCD键(合并时代死副本只control读)→删;真正两进程都读会漂移的是连接组7键。机制=方案一(共享片段+<appSettings file=..>只读合并,选file=不选configSource因前者支持..上级路径;写由operate收口),control只加一行XML、C#/csproj零改动。Task1临时harness真机坐实file=+..子目录读父目录(probeKey=HELLO/exit0)。Task2 SharedConfigStore+operate首个单测工程6绿。Task3 AppConfigHelper.Save按SharedKeys分流。Task4建tl-shared.config(108值)+csproj拷输出根+operate App.config接file=删7共享键+grep坐实后删12死键。Task5 control接..\\tl-shared.config删7共享键(换气/CCD全留)。Task6真机:部署布局下control经..读到共享连接配置、改一处重启对称生效(19999↔10010)、回归全绿。operate真外壳受僵尸20268 Mutex门控未跑通(读取机制与control同源据其判定成立)。凭据组本轮不并入(独立小决策)。分支feature/config-consolidation待并main。",
+  generatedAt: "2026-06-23 21:30",
+  phase: "三阶段主体完成;M区全闭合;配置收敛真机验证;D2-02 调试页命令代理 第一阶段 control 后端 批A+批B 落地(TDD 25单测绿)",
+  currentTask: "D2-02 调试页跨进程命令代理 · 第一阶段(control后端):批A(单测工程 IvfTl.ControlHost.Tests + 数据类 DebugSession/DebugCommandResult + 红线电机钳位 MotorClamp + 测试替身 Fakes)+ 批B(DebugSessionManager:acquire/release/heartbeat/幂等 + 超时自动回收 SweepExpired + Execute 命令分发 读数/握手/阀/LED/电机红线钳位/EEPROM写)。全程严格 TDD red→green,逐 Task 单独 commit,全量 25 单测绿(Smoke1+MotorClamp10+SessionManager6+Execute8)。8 commit 在 feature/d2-02-debug-command-proxy。剩 批C(Task8 ControlHttpServer /debug 路由 + Task9 Program 装配 SweepExpired 看门狗)+ Task10 真机 curl 冒烟。",
+  note: "安全地基(spec §5):DebugSessionManager 注入 Func<int,IHouseGate>+Func<DateTime> 时钟可纯单测;会话表 ConcurrentDictionary;绝不指望 operate 主动还,SweepExpired 超时(TTL)自动 Dispose 归还兜底,心跳续约只罚失联不罚操作时长。红线电机钳位放 control:VerticalMoveTo/HorizontalMoveTo 及 Forward/Backward 相对目标越界(垂直[0,125000]/水平[0,220000])返 OUT_OF_RANGE 且不下发串口。Execute 失效 sessionId 一律拒(SESSION_EXPIRED 防抢串口)。Task5 踩坑:批A FakeLease.Dispose 不触发 gate.ResumeCapture 致超时回收测试红,改 FakeLease 持 gate 引用 Dispose 时恢复采集(对齐真实 HardwareLeaseImpl)。8 commit:批A b4da856/e3fa590/ae80804/be88acf + 批B 968ad59/b7e8e40/cdc4976/5922797。分支 feature/d2-02-debug-command-proxy 待并 main。",
   milestones: [
     { name: "阶段1 · control 独立进程骨架(完成)", tasks: [
       { id: "Task1-7", name: "全过+D1-08死锁修复+operate真外壳E2E+数据入库DB铁证", status: "☑" }
@@ -12,7 +12,7 @@ window.PROGRESS_DATA = {
     { name: "阶段2 · 监控补全+借串口+受护栏停止", tasks: [
       { id: "D2-01", name: "监控页跨进程/status三块(harness验)", status: "☑" },
       { id: "D2-03", name: "受护栏停止/shutdown(口令+安全停机释放硬件)", status: "☑" },
-      { id: "D2-02", name: "借串口/serial让路(验);调试页完整驱动待命令代理设计", status: "◑" }
+      { id: "D2-02", name: "调试页命令代理:第一阶段control后端 批A+批B(单测工程/数据类/红线钳位MotorClamp/Fakes + DebugSessionManager 会话/超时回收/Execute分发含电机钳位,TDD 25绿);剩批C HTTP路由+装配+真机冒烟", status: "◑" }
     ]},
     { name: "阶段3 · 清理老壳+装机收尾", tasks: [
       { id: "D3-01", name: "退役删ivf_tl_ControlTest(两编译0错)", status: "☑" },

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

@@ -1,16 +1,13 @@
 # 续接断点状态(机器可解析)。换会话/换电脑后首先读它定位。
 # 状态取值: 未开始 / 进行中 / 完成 / 代码完成待验证
 # 纪律:本字段只存【当前断点】,历史细节进 交接卡.md(见 CLAUDE.md 第三节)。
-更新时间: 2026-06-23 D2-02(调试页跨进程命令代理)已出 spec + 第一阶段实现计划(bite-sized TDD),代码未开工。配置收敛此前已并入 main(那批 config 提交已在 main,旧断点"待并 main"系笔误、已纠正)。当前无 control 在跑、无活体培养(僵尸 operate 20268 仍在但不占舱口、需重启清)。
+更新时间: 2026-06-23 D2-02 第一阶段(control后端)批A+批B 已落地:单测工程+数据类(DebugSession/DebugCommandResult)+红线钳位 MotorClamp + 测试替身 + DebugSessionManager(借用/归还/心跳/超时自动回收/命令分发含电机红线钳位)全 TDD,全量 25 单测绿,8 个 commit 在 feature/d2-02-debug-command-proxy。当前无 control 在跑、无活体培养(僵尸 operate 20268 仍在但不占舱口、需重启清)。
 当前任务: >
-  【D2-02 调试页命令代理 · 设计+计划已出,待执行】(分支 feature/d2-02-debug-command-proxy)
-  · spec:需求文档/specs/2026-06-23-D2-02-调试页命令代理-design.md(已提交 d5afcba)
-    = 会话式借用(sessionId)+通用/debug/command(op枚举)分发+MJPEG预览;安全地基=租约+心跳+control端超时自动回收
-    (扛 operate 崩溃/丢消息/长时调试);红线电机钳位放 control;借用边界沿用现状(点初始化借、卸载/返回还);是 D3-04 前置。
-  · 计划:开发计划/2026-06-23-阶段2-D2-02-调试页命令代理实现计划.md
-    拆 3 阶段:①control后端(会话/分发/钳位/超时,纯单测+curl,bite-sized 完整 Task0-10)②MJPEG预览(提纲)③operate接入+真机V-012(提纲)。
-  · 下一步:执行第一阶段(control 后端),建议子代理驱动逐 Task 实现;真机冒烟 curl 由 Claude 自主跑。
-    本分支(spec+计划)待并 main。
+  【D2-02 调试页命令代理 · 第一阶段批A+批B 完成,待做批C(Task8-9)+真机冒烟(Task10)】(分支 feature/d2-02-debug-command-proxy)
+  · 已完成:批A(Task0-3 单测工程/数据类/MotorClamp 钳位/Fakes)+ 批B(Task4-7 DebugSessionManager:
+    acquire/release/heartbeat/幂等 + 超时自动回收(改 FakeLease 持 gate)+ Execute 分发(读数/阀/LED/电机红线钳位/EEPROM写)),全 TDD red→green,全量 25 绿。
+  · 下一步:批C = Task8 ControlHttpServer /debug 路由(acquire/heartbeat/release/command) + Task9 Program 装配 DebugSessionManager
+    + SweepExpired 看门狗定时器;然后 Task10 真机 curl 冒烟(Claude 自主跑)。本分支待并 main。
 说明: >
   operate/control 双进程拆分三阶段主体早已完成;合并遗留 M 区 M-01~M-07 本轮全部闭合
   (M-01/02/03 builder去桩、M-04 存图代码定论、M-05 0x12帧长回归、M-06 按well焦点零点、M-07 网关)。