续接载体之一。每完成一步或暂停,在末尾追加一段:
## 日期 时间 · 一句话标题,下列「改动/核实/踩坑/下一步」。只追加,绝不覆盖。 新任务(2026-06-22 启动)。旧任务(M0-M8 合并改造)交接卡随旧文档清空,本卡为新任务重起。
项目文档/ 下旧文档(00总纲/需求文档01-14/开发计划归档/历史报告),为新任务腾空。需求文档/control-逻辑与配置全景.md。
需求文档/specs/2026-06-22-operate-control-双进程拆分-design.md(9章)。开发计划/2026-06-22-阶段1-control独立进程骨架.md。ivf_tl_ControlTest(csproj=ivf_tl_ControlMain)= 合并前 control 独立 exe,但 Window1 启动骨架已被 operate 复刻吸收、MainWindow 是纯测试、StopWindow 是死窗,命名混乱(三命名空间)。结论:不改造,阶段3 退役删,新 control 启动器基于干净的 ivf_tl_Control 类库新建。00-需求总览.md(新总纲);全景移入需求文档/、阶段1计划移入开发计划/;续接三件套(进度状态.yaml/工作计划表.md/本卡/待验证清单/进度数据.js)按新任务重写;CLAUDE.md 引用改到新体系。application-local.properties 里 jna.lib-path/video 路径写死成 D:\WorkSpace 开发机路径→改本机 + 重编译,DLL 加载成功、10030 通);起了本机微服务集群(gateway 等 6 服务,oplog 原在跑)。#if DEBUG 把地址覆盖到 207 外网,真机验证须用 Release。临时文件/ 基准 ↔ 现有合并版)、不依赖 git,完整复核合并改动与文档完整性。SerialChannelImpl 返回桩值):排气阀时间写(:130 return false)/读(:137 return -1)、缓冲瓶灯光写EEPROM(:143 return false)、调试页存图(CameraImpl:148 丢宽高转 SaveBmpPic 未验证)。另有写EEPROM"成功=true"不可靠等待验证项。需求文档/操作端逻辑与配置全景.md(operate 全景:启动时序/业务模块逐功能/8个 operate↔control 直连点/HTTP 接口全表/凭据加密/配置全键/§八 合并降级登记/后台线程/上线前清理项/拆分结论)。待验证清单.md 新增"合并遗留·operate 侧"区(M-01~M-07)。CLAUDE.md/00-需求总览.md/工作计划表.md/进度状态.yaml 里"M0-M8 已全部结项"为"代码完成、真机验收整体未做、有降级遗留";三处文档地图挂上 operate 全景。AppData.cs:91-111 #if DEBUG 覆写到 test-gateway 外网)——真机/排障必须用 Release。临时文件/)、合并版 operate、front、108 数据库,全面核对 项目文档/ 全部文档的完整性/全面性/滞后性/合理性,只认源码+数据库,不看 git 历史。comm -23 空)、对焦核心算法逐字保留(仅命名空间改 + 硬件面换 HAL)、同名程序集已改名(ivf_tl_Control_Entity/Services)。log.operation_log 19 列含 CLAUDE.md 列出的全部关键列、aivfo_tl_setting 配置表(tl_setting/house/house_well_setting + 对焦扩列)齐。开发计划/2026-06-22-阶段1-control独立进程骨架.md:ControlHost Program.cs 的 Log4netHelper 命名空间错——control 子树是 IvfTl.Control.Services(程序集 ivf_tl_Control_Services),计划照搬了 operate 端的 ivf_tl_Services 会 CS0234。已在 using 区加 using IvfTl.Control.Services; + 块内 11 处改裸 Log4netHelper;operate 侧 Step3 块保持 ivf_tl_Services(本就对)并加"勿混"提示;行号 67-134→67-132(3 处)。8表→9表 + 需预置不建表→8表预置、house_autofocus_calibration 由 DBService CodeFirst.InitTables 自动建;② 背景指南 §六.1 192.168.0.207 外网→#if DEBUG 顺序覆盖后最终生效 test-gateway.aivfo.com:36000、§六.5 同步订正建表表述;③ HTML 总图 §12+§17 两处 SQLite"不建表"同步订正。67-134=0、8 表,DependFile=0、不自动建表/需预置不建表=0;ControlHost 块无错误命名空间、operate 块保留正确命名空间。bufferBottlerPressureMin+10(源码无 +10);完整性小缺口:OperateHwTest(218 行控制台测试 Exe,不在主 sln)、IvfTl.AutoFocus.Tests(游离工程)均无文档登记;front oplog-config.json 实在 ivf_tl_Manage/ 非"front 根";start-all.sh 头注释路径写"临时文件/"实在"开发环境/"。CLAUDE.md 的问题并修正。逐条用源码/108 数据库核对(非 git)。ivf_tl_Manage/App.config 确有 kfkaIP=192.168.0.108/kfkaPort=9092;App.xaml.cs InitOperationLog()(project=front、topic=tl-oplog)确实起 Kafka 产消息端;operate oplog-config.json 模块(界面点击/HTTP/串口/相机/舱室设置/对焦调试/对焦设置/缓冲瓶调试/皿管理/胚胎操作)与 CLAUDE.md §5.1 一字不差;front config 仅 界面点击/HTTP。→ §5.2.3"operate/front…kfkaIP"正确。项目背景与上手指南.md 为开机先读第1篇/文档地图首条;② §二末环境权威源由 环境与账号清单.md(本机 Docker 备用)改指 服务器测试环境.md(108 当前部署)+连接配置清单;③ §六 新增 C#/.NET 编译段(net6.0-windows、三 sln、dotnet build、真机必 Release、MSB3021);④ §四 开发环境补 服务器测试环境.md;⑤ §5.3 operation_log 列 15→实测 19 列(补 parent_id/level/host/create_time);⑥ §二 标为"续接顺序唯一权威,背景指南/00总纲应对齐"。kfkaIP/kfkaPort 行;订正"front 无 Kafka/oplog 接入"为"front 有 oplog 接入"(附 App.config/App.xaml.cs 证据);§一 Kafka 行"业务里怎么连"由"operate"改"operate/front"。连接配置清单 §三 旧结论"front 无 Kafka/oplog"与源码相反——典型的文档滞后于代码;已订正。系统业务流程详图.html 背景段未点明"三项目"具体是哪三个、也没在背景段提示"上一个任务是半成品"。核实:§1.1 只讲 operate+control"两个独立进程",AutoFocusTool 要翻到 §7.1 才出现;§0 程序表列 operate/control/front,易被误读成"三项目=operate+control+front"(实则 front 从未合并)。系统业务流程详图.html:
临时文件/ 下。aivfo_tl_setting=18、log=2(非服务器测试环境.md 旧写的 17/1——两条 migration 已补执行);operation_log 19 列与 CLAUDE.md 一致。bufferBottlerPressureMin+10(源码无 +10)」记反了:实测 BufferBottleBin.cs:179 确有 if (BufferBottlePressure < (bufferBottlerPressureMin + 10)) BufferBottleAeration(...),源码有 +10,control 全景 §四 prose 正确,那条"勘误"作废(所幸列为未改、未误改全景)。服务器测试环境.md §三:表数 17→18、1→2;两条 schema migration 由「未执行」改「已补执行」并消除与连接配置清单的矛盾。操作端逻辑与配置全景.md:§十 补登 OperateHwTest(真机硬件测试 CLI,非交付件);§8.2 补 CameraImpl.SetGain→-1 桩。项目背景与上手指南.md §一:厘清三项目合并来源=operate+control+AutoFocusTool,明确 front 不在合并内。control-逻辑与配置全景.md:§一 补 StartTask:497 父线程;§二「无条件启动」改为受 runHouses 门控;§3.2 补 StartAutoFocus:1443+本地对焦安全门;§3.5 标 AirSwapNewFun:1039 死代码;§3.6 补 CapturePausedByGate:319;§7.3 补对焦库源自 AutoFocusTool + 登记 IvfTl.AutoFocus.Tests 游离单测工程。开发计划/阶段1:Task7 运行前置扩为三条源码实锤清单——新增 ControlHost 必须自带 App.config(否则 AppData 构造 NPE,比 DependFile 更靠前)+ 10 个 DependFile 文件具体名 + calibration.json 不必预置;统一复刻行号 78-130→67-132。OperateHwTest/IvfTl.AutoFocus.Tests 是已知文档缺口(本卡「全量核对」段已记),本轮补登;② 阶段1 真机最大坑非 DependFile(csproj 已传递拷贝 10 文件),而是 ControlHost 缺自带 App.config 会让 StartRun 第一步 NPE——计划原仅在部署备注模糊提"App.config",已升级为明确前置。/goal 要求按计划 TDD 一次性做完阶段1,真机已连,Claude 自主完成全部测试验证(不找用户配合点击/起停进程)。建 feature 分支 feature/control-independent-process,子代理驱动 Task1-6,Claude 亲自驱动 Task7 真机。ivf_tl_ControlHost(WinExe/net6.0-windows,引用 ivf_tl_Control+ServicesImpl)+ 单测工程;HostArgs(参数解析)/StatusDto(/ping /status 返回体)/ControlHttpServer(127.0.0.1 HttpListener)。6 xUnit 单测红→绿全通过,ControlHost 编译 0 错误。spec 审查逐文件核对一致(app.manifest 根标签笔误已按计划提示修正)。Program.cs 完整启动序——Mutex 单实例 + HTTP 先起 + 复刻 operate 启动序。坐实 control 端命名空间:Log4netHelper=IvfTl.Control.Services(程序集 ivf_tl_Control_Services)、AppData/StartMain=ivf_tl_Control、PathHelper=ivf_tl_UtilHelper(经 Controller/Services 传递可达);逐字给定代码一次编译过、零成员臆造。ControlProcessLauncher(探活/ping→不在则 Process.Start -Verb RunAs 拉起→轮询就绪)+ MainWindow 内嵌 StartRun 段(旧 63-132)替换为 EnsureRunning 调用 + App.config 加 controlPort/controlExePath。Release 编译 0 错误(Debug 被残留进程锁,改用 Release——亦真机连本地中间件正确配置)。ConsentPromptBehaviorAdmin=0=管理员静默提权,故 Claude 可 Start-Process -Verb RunAs 自主拉起 requireAdministrator 的 control/operate、自主提权 taskkill,全程无需用户点 UAC/起停进程。网关 10010=200、6+java 微服务在线、7 COM 口实测全 FREE(僵尸 20268 不占硬件)。App.config(operate 业务键+连接键,构建转 ivf_tl_ControlHost.dll.config——.dll.config 是 .NET6 正确约定,operate 亦然);Release 构建,DependFile(ccd 7 DLL/DB/newccd)经 ivf_tl_Control 传递拷贝全落地。/ping+/status 返回 {"ok":true,"pid":21976,...}✓;③ control 日志见 6 相机序列号 + 7 COM 口全 True——独立进程真机硬件获取✓;④ 第二个 control 撞 Mutex 自退、进程数恒 1(单实例)✓;⑤ control 结构性独立于 operate 运行(无 operate attached 仍驱动)=核心"续命"目标✓;⑥ 卡住的 control 可提权 taskkill 杀掉(非僵尸)✓。600075e):Program.cs 原在主线程直跑 StartRun,其内部 serialBin 握手/StartAsync().Wait() 阻塞会占住主线程使 _exitEvent.Wait()(阶段2 /shutdown)永不可达;改为 Task.Run 后台跑、主线程驻留——HTTP 即刻可达,对齐 operate 原 Task.Run 形态。StartRun 永久卡在 serialBin.Start() 串口握手,started 恒 false,无法到"数据入库"闭环。根因=合并遗留缺陷,非双进程改造:Program 先 HAL.ScanDevices()(HardwareAccessLayer.cs:185 已对每口握手一次),随后 serialBin.Start() 对同一 HAL 借用 ComBin 二次握手;ComBin.ShakeHandsWait(ComBin.cs:314)taskAutoResetEvent.WaitOne() 无超时,第二次握手命令入队后发送线程不处理(今日 0 个 HouseComRecord 日志)→ 死锁。两次干净 COM 口重启稳定复现(非偶发/非僵尸残留)。旧合并版 operate(僵尸 PID 20268,自 06-21 23:55 卡死、提权 taskkill/wmic 均拒绝访问、需重启清)卡死在同一 serialBin.Start()——即此缺陷之活证。.dll.config 而非 .exe.config 才是 .NET6 apphost 读取名。③ 串口握手 hang 是合并真机验收欠账(M 区同源:HAL/SerialChannelImpl 链路),阶段1"零改 control 业务"不含修复。待验证清单 D1-07/D1-08)。选项:(A) 开专项任务修 HAL 借用 ComBin 双重握手(可解锁全闭环,但属 control 业务逻辑改动、有真机电机风险,需用户拍板破"零改"约束);(B) 维持现状,串口死锁与 M-01~M-07 一并纳入"合并真机验收"专项。建议先重启清掉僵尸 20268 再做任一选项的最终闭环复测。HAL.ScanDevices()(HardwareAccessLayer.cs:178-219)对每口握手后 finally: ch.Close()(:217)。SerialChannelImpl.Close()(:50-58)里 _com.IsStop = true。ComBin 发送线程 while(true){ if(IsStop) return; ...}(ComBin.cs:126)→ IsStop=true 令发送线程永久退出,且 OpenPort() 原不复活它。serialBin.Start()(经 InitTL)借用同一缓存 ComBin(SerialBin.cs:195 HAL.GetSerial(0,port).RawComBin)、直接 OpenPort() 重开(返回True)再 ShakeHandsWait 入队握手 → 发送线程已死无人出队 → ShakeHandsWait(ComBin.cs:314)taskAutoResetEvent.WaitOne()(无超时)永久死锁。IsStopWhile=true(默认),若模块不响应 ScanDevices 自己会无限卡、serialBin 的"打开端口"日志根本不出现;但日志出现→ScanDevices 握手全成功、硬件通信正常→死锁纯是"Close 杀死发送线程、重开不复活"。旧合并 operate 僵尸 20268 卡死同处即此因(operate MainWindow 也先 ScanDevices 再 serialBin)。fix(control) 提交):ComBin.OpenPort() 重开端口成功且发送线程已停(IsStop)时,复位 IsStop=false 并 StartSendCommandThread() 复活(SendCommandLock 防并发双起)。不改命令内容/时序/电机逻辑,仅复活被误杀的工作线程。覆盖 serialBin 直接用 RawComBin.OpenPort() 的借用路径。同步惠及 operate(同路径)。/status started 恒 false、RunRecord 停在"打开端口…True"、0 个 HouseComRecord。/status = {"ok":true,"pid":22252,"tlSn":"NEO-1-20230411","started":true};RunRecord 越过握手→仓室信息(houseSn 枚举)→E方数据→【CamNum:6】设备基础数据;HouseComRecord 恢复产出,真实串口收发:[COM4]Temperature Rec:5E 86 02 09 0F D6、[COM11]DoorStatus Rec:5E 90 06、[COM3]BufferBottlePressure/Aeration(缓冲瓶换气执行)、house6 空闲监测开始——温度/压力/门/换气自控环在真机运行;control→gateway 10010(×2)+ MQTT 1883 ESTABLISHED。DBServiceImpl.StartDbService.InitTables 每启动报 SQLite Error 1: AUTOINCREMENT only allowed on INTEGER PRIMARY KEY(某表 CodeFirst 建表失败)+ DBUpdateTLInfo 本地写失败;control 仍可运行(读本地培养记录成功),影响限该表本地持久化。建议专项修。ServiceDishAndBalanceData 接口返回失败=无皿时服务器无培养记录、回退本地,正常非缺陷。临时文件/LauncherTest(<Compile Include> 直接链入 operate 真实 ivf_tl_Operate/Helpers/ControlProcessLauncher.cs,绕过被僵尸锁的 operate UI 单实例),非提权运行(其内部 Process.Start UseShellExecute=true 触发静默提权拉起 requireAdministrator 的 control):
IsControlAlive()=False → EnsureRunning 内 已拉起 control.exe→轮询→control 就绪 → 返回 True、IsControlAlive()=True。IsControlAlive()=True → "control 已在运行,直接连接" → 返回 True、PID 不变、进程数恒 1(未拉第二个)。EnsureRunning 已全路径实跑。aivfo_tl_setting:
house_collect 20:18 后新增 22 行,抽样:tl_sn=NEO-1-20230411, house_sn=2/4/6/7/8/9, temperature=37.16/37.46℃(真实箱体温), temperature_lower_cover/upper_cover/lower_glass 多点温, house_door=0, air_swap=0, create_by=admin_admin, create_time=20:29。alarm_data 新增 270 行,抽样:tl_sn=NEO-1-20230411, house_sn=11, alarm_type_id=44, title=PRESS alarm(control 自主检测缓冲瓶低压并上报)。OperationLogger 埋点都在 operate 侧(ivf_tl_Operate + operate 的 ivf_tl_Services/HttpHelper + operate 的 ivf_tl_Entity 的 Camera/ComBin),control 子树零埋点。control 硬件操作的 oplog 收口本就挂在 operate 的另一套 ComBin/Camera 栈(=阶段3 ComBin 两套栈去重 G1-2),故给 ControlHost 单加 InitOperationLog 也不产 oplog。归阶段3 处理。control 诊断走 RunRecord/HouseComRecord 文件日志,业务数据入库走 MQTT,闭环不受影响。App.xaml.cs Mutex 名在 OPERATE_E2E=1 时切 ivf_tl_Operate_e2e(绕僵尸占用的 Mutex);② MainWindow_Loaded 在 OPERATE_E2E=1 时跳登录窗、用 App.config 凭据走与 LoginWindow.StartDish_Click 完全相同的真实 AppDataInit(真服务器登录)。构建 operate Release + 把 control Release 部署到 operate 输出目录 control\ 子目录(真实部署布局)。bat 包装设 env + 提权启动真 operate.exe。this.Close() 退出)→ MainWindow_Loaded → ControlProcessLauncher.EnsureRunning → 10s 后拉起 control(PID 22156,21:46:05);control /status={"ok":true,"pid":22156,"tlSn":"NEO-1-20230411","started":true} 驱动机器。杀真 operate 20104(模拟用户关 operate)→ control 22156 仍在跑、/status 仍 started:true = "operate 关了 control 续命"用真 WPF 外壳实证(D1-01/D1-04 升级"真外壳实跑")。git diff 两文件 vs 已提交=空),operate Release 重编 0 错误。ControlHttpServer 扩展:GET /status 返回完整 MonitorSnapshot(rich);POST /shutdown(token)+ /serial/pause|/serial/resume(houseSn),含 POST body JSON 解析。MonitorSnapshot/HouseMonitorRow 补 §6 三块(只读):WorkingType/ValveState/CapturePausedByGate;GetMonitorSnapshot 填充。Program:BuildRichStatus + HandleShutdown(口令 tl13579→SafeShutdown:HAL.ShutdownAll+_exitEvent.Set)+ HandleSerialPause/Resume(HAL.GetHouseGate.PauseCapture/ResumeCapture,接已有 HouseBin 让路:CapturePausedByGate+采集主循环 if(IsDebug||CapturePausedByGate) 不发串口)。/serial/pause houseSn=2→舱2 CapturePausedByGate=true 且 COM4 停发(让路,不驱动电机)→ resume 恢复;/shutdown 错口令 403、对口令 tl13579 安全停机(进程退出 + 7 COM 口全释放)。6 单测仍过。Helpers/ControlClient.cs:跨进程 HTTP 客户端(/status 取快照、/ping、/shutdown、/serial/pause|resume),全 try 兜底。拆分后 operate 进程内无 control 实时 AppData,监控/调试必须经此。ServiceMonitorViewModel.Refresh:数据源由同进程 ControlAppData.Instance.GetMonitorSnapshot() 改为 ControlClient.GetStatusSnapshot()(HTTP);映射三块;加 ShutdownControlCommand(二次确认+Microsoft.VisualBasic.InputBox 口令→/shutdown)。ServiceMonitorView.xaml:舱行加三列(活动/排气阀/借用让路)+ 底部受护栏停止按钮。operate Release 编译 0 错误。GetStatusSnapshot 往返反序列化 10 舱(真温 37℃+三块);SerialPause(2)→快照 House2 Paused=true→SerialResume,跨进程借串口让路实证。gate.Acquire(OperateDebug) 借同进程 HAL 同一物理句柄驱动(HouseDebugPageViewModel:256,_halLease.Serial/Camera)。拆分后 operate 进程 HAL 是空单例、拿不到 control 持有的串口/相机——要在拆分下让调试页驱动,需 control 暴露串口/相机命令代理(逐操作:电机移动/读写EEPROM/LED/相机预览…一个大代理面,当前设计文档未细化)或端口让渡。这是独立子任务,且驱动电机受安全红线(§178 需用户在场)。本阶段交付让路契约(/serial/pause|resume,已验)作基础;调试页完整驱动 = 需设计(命令代理)+ 受监督真机验。ivf_tl_ControlTest(csproj=ivf_tl_ControlMain,命名空间 TLTest)是合并前 control 独立 exe 的脏壳(Window1 启动骨架已被 operate/ControlHost 复刻、MainWindow 纯测试、StopWindow 死窗)。新 ControlHost 已跑通真机闭环,确认后退役:dotnet sln control/ivf_tl_Control.sln remove + git rm -r 删目录。control sln 重编 0 错误、operate 编译 0 错误(operate 仅 SettingPageView:392 Process.GetProcessesByName("ivf_tl_ControlMain") 字符串引用旧进程名=死代码,无类型依赖)。踩坑:首次重编报 22 个 MSB3021 DLL 锁错——实为我之前启动的测试 control 21592 锁着 ControlHost 输出,与删 ControlTest 无关;/shutdown 停掉 21592(顺带再验受护栏停机:口令通过→进程退出→7 COM 口释放)后重编 0 错误。开发环境/双进程部署指南.md——两产物(operate 全目录 + control 放 operate 的 control/ 子目录)、control 必带 dll.config+DependFile、本地 HTTP 契约(/ping /status /shutdown /serial 端口 38080)、装机自检清单、已知遗留。部署布局已由 operate WPF 真外壳 E2E 实证(operate 即从 control/ivf_tl_ControlHost.exe 拉起)。ivf_tl_Entity/ComEntitys/ComBin,带 OperationLogger)与 control(ivf_tl_SerialHelper/ComBin,真驱动)两套串口栈。拆分后 operate 已不驱动串口→operate 那套栈成运行期死代码;control 操作不进 operation_log(oplog 埋点在 operate 栈,=D1-10)。去重=动串口代码的有风险重构,属收尾不阻塞功能,列延后专项(需设计 oplog 收口归属 + 受控验证),不做无监督风险重构。HouseAutofocusCalibrationDB.id 是 [IsPrimaryKey,IsIdentity] long,SqlSugar 把 long→SQLite BIGINT,而 SQLite AUTOINCREMENT 只允许 INTEGER PRIMARY KEY → CodeFirst.InitTables 报 "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY"、calib 表建不出;② 打包 DependFile/DB/aivfoTL.db 的 schema 落后于实体——tl_setting 缺 localAutofocusEnabled、house_well_setting 等多表缺列 → DBUpdateTLInfo/UpdateHouseWellSetting 等查询报 no such column。id long→int(→SQLite INTEGER;id 仅自增主键,业务从不按值读,查询按 tl_sn/house_sn/well_sn/scene);② DBService.StartDbService 对 control 本地写的 9 个实体(TLSetting/House/HouseWellSetting/Dish/Balance/Embryo/HouseWellPhoto/Picture/HouseAutofocusCalibration)逐个 CodeFirst.InitTables 自愈 schema(additive 补缺失列、各自 try/catch 隔离)。临时文件/相关参数.html(用户自测的下位机控制参考值,仅供参考,实际安全范围比文档更宽):水平钳位 0,220000、垂直 Z 钳位 0,125000。除这两轴运动范围外,其余下位机控制无任何风险。待验证清单.md(红线)、specs/...双进程拆分-design.md(§8 计划头 + §9.5 真机门控)、开发计划/...阶段1...md(关键现实约束 + Task7 头)、项目背景与上手指南.md(开工方式)、工作计划表.md(执行方式)、系统业务流程详图.html(§13 降级登记 + §14 路线图 lead + §14.1 阶段1说明 + 开工 li)。SerialChannelImpl 返桩,最适合 TDD(字节编码纯逻辑)。临时文件/ivf_tl_operate_2.0/ivf_tl_Entity/ComEntitys/Commander.cs)有 3 条 E方命令真机已用,合并时 control 端 ivf_tl_SerialHelper/Util/Commander.cs 未搬运:CreateWriteEEPROOpenVentTimeCommand(写排气阀时间,地址 00 03 08)、CreateReadEEPROMVentNum(读排气阀时间)、CreateWriteEEPROMLightNum(写灯光亮度,地址 00 05 34)。故 SerialChannelImpl.WriteOpenVentTimeWait/ReadOpenVentTimeWait/WriteLightBrightnessWait 只能返桩(false/-1),调试页写排气阀时间/读排气阀时间/缓冲瓶写灯光未真正下发下位机。ivf_tl_operate_2.0/control/ivf_tl_SerialHelper.Tests(net6.0-windows xUnit,ProjectReference SerialHelper,已 dotnet sln add 入 control sln)+ CommanderEepromTests.cs 4 测试,断言 3 个 builder 的精确字节(期望=合并前 operate 黄金真值,末位 CreateORC 累加和校验我手算 0x87/0x8C/0x05)+ 1 条交叉校验(写排气阀 vs 已通过的写进气阀仅地址低字节 0x08/0x0c 不同)。dotnet test → CS1061 方法不存在 = RED(feature missing)。Commander.cs 补 3 个 builder(逐字抄合并前 operate);dotnet test → 4 单测全绿(手算校验位全对)。Enums.cs 补 ReadEEPROOpenVentTime/WriteEEPROOpenVentTime/WriteLightNum;ComBin.cs 补 WriteEEPROOpenVentTimeWait/ReadEEPROMVentWait/WriteEEPROMLightNumWait(镜像既有 intake/light 的 commandType→Create*→Enqueue→WaitOne 模式);SerialChannelImpl.cs 三方法去桩调真实 _com.*;operate 消费方 HouseDebugPageViewModel.WriteOpenVentTime/RedVentTime、BufferDebugViewModel.writeL 的过时注释/兜底消息("control 端暂缺写E方命令")更新为"已补、真下发"。control sln(IvfTl.Hardware)+ operate Release 双编译 0 错误。临时文件/EepromVerify(console,ProjectReference IvfTl.Hardware,经真实 SerialChannelImpl 端到端,非走单测桩),对真机做非破坏性回环:读 V→写 V+1→读确认→写回 V→读确认。control 未跑时端口空闲、harness 直接 Open+ShakeHands(复刻 HAL.ScanDevices 模式)。
临时文件/ 已 gitignore(harness 不入库);codegraph sync 已跑。#if DEBUG(其 AppData.cs:108 覆写 BaseUrl 到 test-gateway 外网);现部署 App.config outInter=0(不触发 :87 外网覆写)+ urlIp=http://127.0.0.1/urlPort=10010→ 最终 BaseUrl=内网网关。已由阶段1 operate WPF 真外壳 Release E2E 真服务器登录成功 + control→gateway 10010 ESTABLISHED 坐实。现场换站点仅改 urlIp。ivf_tl_Entity/ComEntitys/ComBin.cs:871)与 control(ivf_tl_SerialHelper/ComBin.cs:895)两版基线都硬编码 CreateReadEEPROMvertMtStartPulse(1)(只读 well-1 零点)。builder 支持 case 1-16 但 Wait 方法两版都传 1 → 这是 operate 原始设计就有的限制,合并没改。改成按 well 是行为变更(影响 autofocus 的 Z 焦面零点、涉垂直电机焦面,正是需守安全区间的轴),需领域确认"well 级零点是否真实存在于 EEPROM" + 谨慎对焦真机验证,不盲改。已订正待验证清单 M-06 表述。Write*Wait 阻塞收回复即 true,不校验真实成功(基线 operate 同样无条件 true,非合并回归)。本轮 M-01~03 真机验证时印证了帧错位现象:CustomProtocolLength 对 0x12 写设回包长 6、对 0x11 读设 10,写后紧接读会因残留字节帧错位污染(无排空时偶现垃圾值/null,加间隔即消失)。改为校验写回包状态字 = 改动活动写路径、有风险(误判会破坏正常写),需真机逐字节定位 0x12 实际回包格式再动,列后续受控专项。CameraImpl.SavePic(name,w,h) 忽略 w/h 转发 _camera.SaveBmpPic(name),底层从相机自身 SourceBuffer 取帧(自带尺寸)→丢弃入参 w/h 无害;但落盘格式/旋转/位深 vs operate 基准 MVCAPI.SavePic(buf,w,h,name) 的等价性需一次受控抓帧核对。当前相机正被 control 用于活体培养成像,无监督抓帧可能干扰在运行的成像,不宜无监督进行,留受控时段验。CustomProtocolLength,发现 control ivf_tl_SerialHelper/Util/Commander.cs 对 0x12 写E方回包帧长设成 6,而合并前 operate 基线 ivf_tl_Entity/ComEntitys/Commander.cs(真机写过 EEPROM)=12 → 真实合并回归,正是上轮 M-01/02/03 验证时"写后读垃圾值"现象(此前用"丢弃一帧+700ms间隔"workaround 掩盖、并误记为"非回归")的根因。Protocol.ReplyLength 也写 6 但它根本没有 0x12 写命令(抄来的未验证默认);control=6。逻辑上 operate 真机写过 EEPROM(若 12 错、硬件只回 6 则每次写都卡等 12 字节,不可能出货)→ 强指向 12。但有分歧必须实测,不猜。临时文件/FrameLenProbe,裸 SerialPort 9600 8N1,DataReceived 事件收包,整窗累加计数;非破坏=写回刚读到的原值):
BytesToRead 轮询/阻塞 Read 均抓 0 字节,改用与可用成帧 Channel 一致的 DataReceived 事件收包才有数据;② CreateORC 覆写数组末位占位字节而非追加,我起初给握手/读帧多塞 1 个 0x00 致帧长字段[3]与实际不符、下位机判非法不回复——去掉多余字节后正常(握手5字节[3]=05、读9字节[3]=09、写12字节[3]=0C)。5E 92 09 0C C8 00 00 00 00 CA 00 97、舱8=12字节 5E 92 08 0C 5A 00 00 00 00 5B 00 B9(回包自带长度字段[3]=0x0C=12 自证;状态字[10]=0x00、ORC[11])。校准全对:0x11读=10、握手=6、0x10读门=7(舱8/舱9一致,[3]=0x07 自证)。磁盘上 control 帧长表已含 0x08=9/0x10=7(codegraph 旧快照曾显示缺,实为索引滞后),实测确认其正确,唯 0x12 错。ivf_tl_operate_2.0/control/ivf_tl_SerialHelper.Tests/CustomProtocolLengthTests.cs(18测:0x12=12/0x10=7/0x08=9 三焦点 + 整表14项逐项对齐 operate 基线 Theory + 未知码回退6)。先 RED——0x12 实得6,2处失败([Fact]写E方 + Theory 行 cmd=18即0x12);改 Commander.cs:76 custom.lenght = 12 后 22 单测全绿(含既有4个 M-01/02/03)。临时文件/FrameFixVerify,经真实 SerialChannelImpl,关键=【不用】丢帧/长间隔 workaround,紧凑"写原值→立即读"):舱9(V0=200)12轮、舱8(V0=90)12轮,24/24 全干净(读回精确=写入值,无 -1/垃圾);旧码此处必污染。回归 临时文件/EepromVerify M-01/02/03 三条仍 PASS。control sln(IvfTl.Hardware)+ operate Release 双编译 0 错。receivedBuffer[lenght-2] 状态字检查落到正确位置[10]。Write*Wait 仍"收回复即 return",未把状态字作 bool 成功传回调用方(=M-05"成功语义传播",基线 operate 同样无条件 true,非合并回归)。临时文件/ 已 gitignore(3 个 harness 不入库);codegraph sync 已跑。文档已同步(待验证清单 M-05 + §八 + 进度状态.yaml + 进度数据.js + 本卡)。ivf_tl_SerialHelper/Util/Commander.cs vs operate 黄金基线 临时文件/.../ivf_tl_Entity/ComEntitys/Commander.cs——30 个静态命令帧逐字节完全一致;方法名清单一致;全部参数化 builder 的 command.Add(0x..) 地址字节直方图一致。结论:串口命令层仅 CustomProtocolLength 0x12 一处回归(已修),无其它静默错字节。临时文件/FrameLenProbe ⑤ 段,纯 0x11 读,无电机):按 autofocustool 权威地址表(well1=0x08 步进4,well16=0x44)逐 well 读 Z 焦点零点——
ReadWellFocusZeroWait(well) 忽略 well 恒读 well-1,autofocus 对非 well-1 用了偏 600~900 脉冲的错误 Z 起点。临时文件/,不入库。SerialChannelImpl.ReadWellFocusZeroWait(well) 忽略 well,底层 ComBin.ReadEEPROMvertMtStartPulseWait 硬编码 CreateReadEEPROMvertMtStartPulse(1) 恒读 well-1。builder 早支持 case 1-16(地址 0x08+4*(well-1),与 autofocustool 权威表一致)。调用方 HouseBin:1549/HouseDebugPageViewModel:1224 已传 well,只是被底层忽略。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)。ComBin.ReadEEPROMvertMtStartPulseWait(custom, int well = 1) 加默认参 + 用 CreateReadEEPROMvertMtStartPulse(well);SerialChannelImpl.ReadWellFocusZeroWait 传真 well。SerialBin 两处舱级读不传 well→默认1→行为不变。临时文件/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 单测全绿。临时文件/。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)一致。SaveBmpPic 用整块 Marshal.Copy(非逐行),仅当 width*3 非4字节对齐才会错行;相机 width=1600(MVC2000),1600*3=4800 四字节对齐 → 无错行。MVCAPI.SavePic/Save2(Project2.dll #7)的逐字节一致既不可能(不同编码器永不产生字节相同的 JPEG/BMP)也无必要(调试页存图=人工目检产物,不入培养数据管线——真胚胎照走 HouseBin.SaveImage→Save2)。语义等价(尺寸/位深/朝向)已代码坐实且匹配权威 af。camera.SavePic(name,ccdWidth,ccdHeight) 调用核对。文档:待验证清单 M-04 ☑ + §8.1 表 + 表头注。/goal「按工作计划把活先干完,再出昨日建议」。M 区已全闭合,工作计划剩三延后专项(D3-04 两栈去重含 D1-10 oplog / D2-02 命令代理 / 整机自启复测)。关键现实核查:当前无 control 在跑、无活体培养(唯一存活=3天前僵尸 operate 20268,不驱动硬件)→ 自由启停 control 真机验证不中断任何生物过程。故先攻价值最高且可自主安全闭环的 D1-10(control 审计埋点,正是昨日建议第3条·医疗合规级,纯新增不碰电机)。把 D1-10 审计埋点从 D3-04 解耦:本轮只做新增埋点,删 operate 死栈(有风险删除)仍属 D3-04。..\..\..\Aivfo.OperationLog(net6.0,兼容)ProjectReference。ControlHost/Program.cs:InitControlOperationLog(Project=control,Kafka 取 App.config kfkaIP/kfkaPort=192.168.0.108:9092,topic=tl-oplog,ConfigFilePath=exe 同目录 oplog-config.json 热加载)+ 生命周期审计(进程启动即刻产·账号无关 / 采集启动成功·带 tlSn / 安全停机)+ SafeShutdown 先 Log 再 OperationLogger.Shutdown() flush。ivf_tl_CameraHelper/Camera.cs:Init/UnInit 边界埋点(逐字镜像 operate 死栈已验 P3b,module=相机;字段仅用确定存在的 index/width/height/exposure/result,不臆造 NumBer)。ivf_tl_Com/HouseBin.cs:换气 AirSwapOldFun / 补气 AerationNew 业务动作埋点(成功+失败两路,每会话一行、远离串口字节热循环,带 houseSn/tlSn/elapsed,module=换气/补气)。⚠ 踩坑:首次换气编辑的 old_string 贪婪吞下了下个方法签名 private void AirSwapNewFun() 致花括号失衡(204 错 CS0106/CS1022),补回签名即修复。IvfTl.Hardware/Impl/SerialChannelImpl.cs:5 个 EEPROM 写(写排气阀/进气阀/灯光/扫描间隔/水平well位置)埋点,日志置 _ioLock 外(绝不持串口 IO 锁记日志),module=写EEPROM。ivf_tl_ControlHost/oplog-config.json(模块 生命周期/相机/换气/补气/写EEPROM 默认全开,csproj <None CopyToOutputDirectory=PreserveNewest> 随 exe 部署)。log.operation_log 按 project 分组——operate=883、control=0(管道通但 control 零埋点=功能缺失,RED 原因正确)。project=control=70 行:生命周期3(进程启动/采集启动成功 output={"started":true}/安全停机)+ 相机68(结构化 input={"index":5,"width":1600,"height":1200,"exposure":400} output={"result":0})。/shutdown 口令 tl13579→200{ok:true}、安全停机行 flush 落库、/ping 不可达=干净退出+7 COM 释放。oplog-error 日志仅"配置已热加载"无错。HouseBin:670 注释「舱有培养记录才换气/拍照/对焦」——当前无皿空闲态不跑换气循环;写EEPROM 需调试页接通(D2-02)。放皿/调试接通即产行。临时文件/。MainWindow_Loaded:66 登录后 Task.Run 跑一次 EnsureRunning,之后不再探活),control 自己崩了无人重拉,尤其 operate 关闭后 control 独立崩溃 = 直接培养中断。经 brainstorming/规划,用户拍三决策:① 独立 watchdog 进程;② DPAPI 缓存首次凭据;③ 受护栏 /shutdown 写停机标记区分"崩溃 vs 故意停"。并明确要求"能人工干净卸载"。control/ivf_tl_Watchdog.Core(net6.0-windows 类库):WatchdogArgs(CLI 解析)/RelaunchDecision.ShouldRelaunch(纯决策)/WatchdogPaths(数据目录+标志文件)/CredentialStore(DPAPI 加密往返,字段 Base64+'|' 连接避分隔冲突)。control/ivf_tl_Watchdog(WinExe/requireAdmin/无窗口):单实例 Mutex Global\ivf_tl_watchdog_singleton + 命名停止事件 Global\ivf_tl_watchdog_stop + 探活循环(每 N 秒 /ping)+ 崩溃重拉(读 DPAPI 凭据 Process.Start 提权拉起)+ 退避(30s 冷却、连 5 次失败拉长 300s+告警)+ CLI(--pause/--resume/--stop/--install/--uninstall,自启走 reg.exe HKCU\Run)。control exe 同目录解析。control/ivf_tl_Watchdog.Tests(xUnit,入 control sln)。Program.cs:RunStartupSequence Login 成功 → CredentialStore.Save(DPAPI)+ WatchdogPaths.SetStopped(false)(故意启动清标记);SafeShutdown → WatchdogPaths.SetStopped(true)(受护栏停机写标记)。ControlHost 引 Watchdog.Core。C:\ProgramData\aivfo\watchdog\creds.dat=246 字节 DPAPI 密文,提权+非提权探针均能解密读回 admin/123456/C,明文不泄露。
② 崩溃重拉(核心):control 跑(pidA)→看门狗"control 存活"→提权 taskkill 杀 control→看门狗"control 探活失败→已重拉 control.exe"→新 pidB、/status started:true 续命。
③ 故意停不重拉:/shutdown 口令 tl13579→control 写 control.stopped→看门狗连续日志"为故意停机(受护栏 /shutdown),不重拉",control 保持停止。
④ 暂停让路:--pause(建 watchdog.pause)→杀 control→看门狗不重拉→--resume(删标志)→重拉恢复(新 pid)。
⑤ 卸载干净:--install --port=38080→自启项写入→--uninstall→自启项删空 + 常驻看门狗收停止信号优雅退出(进程数 0)。exe+dll、漏 ivf_tl_Watchdog.deps.json + runtimes/win/lib/net6.0/System.Security.Cryptography.ProtectedData.dll → 看门狗运行期加载根目录的非 Windows 占位 DPAPI stub → PlatformNotSupportedException: DPAPI is not supported → CredentialStore.Load() 吞异常返 null → 日志"无缓存凭据"无法重拉。补全 deps.json + runtimes 后立即正常。结论:看门狗部署必拷 bin 全目录(含 deps.json + runtimes/);两 exe 各有自己的 deps.json 可同目录共存。靠加诊断日志(裸 Unprotect 记异常)定位,非猜。%ProgramData%\aivfo)已清;临时 CredProbe harness 在 gitignore 临时文件/。ivf_tl_Entity/ComEntitys/*(ComBin/Channel/Commander/SerialBin…)+ CameraEntitys/* 被调试页 4 个 ViewModel(HouseDebugPageView.xaml.cs/BufferDebugViewModel/DebugCalibrationAdapter/HouseDebugPageViewModel)编译期引用。拆分后这套栈运行期死(operate HAL 空单例),但类型仍被调试页引用→ 不先把调试页改走 control 命令代理(D2-02),删栈必致 operate 编译失败。故 D3-04 必须在 D2-02 之后(任务依赖已登记)。HouseDebugPageViewModel(1592 行)经 _halLease.Serial/Camera(gate.Acquire(OperateDebug) 借用的与采集端同一物理句柄)驱动:① Z/水平电机 move/reset/forward/backward(红线两轴,十几处调用);② EEPROM 读写(M1-B2);③ LED 开关;④ StartPreview(hostControl,left,top,w,h) 贴 operate 窗口句柄的实时 USB 相机预览——拆分后相机 USB 句柄在 control 进程,无法跨进程把实时画面渲染进 operate 窗口,需重新设计帧传输(HTTP MJPEG 流 / 命名管道 / 共享内存),是独立硬子问题。完整 D2-02 = control 暴露 ~20+ 操作的命令代理 + operate 4 个 ViewModel 全改走 HTTP + 跨进程相机预览重构 + 红线电机真机受测。属多会话级设计+实现,设计文档自身标注"未细化的大改面"(spec §160/§178);非"本会话无监督蛮干"的任务——应先出设计(命令代理协议 + 相机预览传输方案)再实现。临时文件/、跑完即弃——没有入库资产能防这些修复被改回去;单测层只锁纯逻辑(帧长表等),锁不住真机端到端行为。需求文档/specs/2026-06-23-HIL硬件在环回归套件-design.md + 实现计划 开发计划/2026-06-23-HIL硬件在环回归套件实现计划.md。ivf_tl_operate_2.0/control/IvfTl.Hardware.HilTests(net6.0-windows,加入 control sln,ProjectReference IvfTl.Hardware,经真实 SerialChannelImpl 端到端):
Infrastructure/HardwareRigFixture.cs:扫 COM 口(跳 COM1/2)逐口 Open+ShakeHands 收集响应真舱(ChamberInfo{Port,HouseSn}),collection fixture 全套件构造一次;FirstChamberWithWells() 排除舱11。Infrastructure/HilCollection.cs + AssemblyInfo.cs:[CollectionDefinition("HIL")] + [assembly: CollectionBehavior(DisableTestParallelization=true)](串行,防多测试抢同一 COM 口)。FocusZeroHilTests(M-06):按 well 读焦点零点,断言去重>1 + 值∈[0,125000];FrameLengthHilTests(M-05):连读12轮断言全 sane;EepromWriteHilTests(M-01/02/03):写回环 [SkippableFact] 默认 Skip,仅 HIL_ALLOW_WRITE=1 才跑、写后立即恢复原值。README.md:用途/怎么跑/Skip 语义/安全口径。FirstChamberWithWells()(恒舱9),开写开关跑时M-03 灯光在舱9无项被动态 Skip→该命令永远得不到守护。改 RoundTrip 逐舱(排除11)找到能读到该项的舱再验(对齐原 EepromVerify 取证逻辑)——重跑后灯光自动落舱8 真验证。dotnet test → FocusZero/FrameLength 2 过(舱9 焦点零点 80200/79800/79300… 去重>1 与 raw 抓包诊断吻合;连读 12/12 干净),写测试 2 Skip。HIL_ALLOW_WRITE=1 dotnet test → 4/4 全过(排气阀舱9 200→201→200;灯光逐舱落舱8 500→501→500,与原 harness 证据一致),值已恢复原值。App.config 与 control ControlHost/App.config 约 25 键逐字重复,control 那份注释明写「必须与 operate 手动同步」——换服务器/调参要改两处、漏一处两进程行为打架。核查厘清:① operate 的 AppData 不读那 12 个换气/CCD 键(合并单进程时代死副本,只 control 读)→ 该删;② 真正"两进程都读、漂移出 bug"的是连接组 7 键(urlIp/urlPort/mqttIp/mqttPort/kfkaIP/kfkaPort/outInter);③ operate 已有半成品「统一配置」UI(UnifiedConfigViewModel+AppConfigHelper,M5 治理)但只写 operate 自己 config = 缺口所在。<appSettings file=> 只读合并,写由 operate 收口)(选 file= 不选 configSource:前者支持 .. 上级路径,control 在子目录要指父目录);凭据组本轮不并入(避免动 control C#)。设计 需求文档/specs/2026-06-23-配置收敛-operate-control连接组单一数据源-design.md + 计划 开发计划/2026-06-23-配置收敛-实现计划.md。分支 feature/config-consolidation。<appSettings file="..\probe-shared.config"> 真机实跑读到父目录键 probeKey=HELLO、exit=0 → 方案一命脉坐实。顺带确认 .NET6 产物名 .dll.config、外部文件与 inline 合并共存。Helpers/SharedConfigStore.cs(XDocument 读写 appSettings 片段,Read(path,key)/Write(path,key,value))+ operate 首个单测工程 ivf_tl_Operate.Tests;red→green 6 绿(含 null/XML 转义,审查后补)。SharedKeys(7 键 OrdinalIgnoreCase)+ SharedConfigPath(BaseDirectory\tl-shared.config);共享键落 SharedConfigStore、非共享键沿用 OpenExeConfiguration。读取侧零改。ivf_tl_Operate/tl-shared.config(7 键,108 现值)+ csproj Content/PreserveNewest 拷到输出根 + App.config 接 file="tl-shared.config" 删 7 共享键 + 删 12 换气/CCD 死键(删前逐键 grep 坐实 operate 树零消费);保留 11 operate 独有键。顺带更正 AppConfigHelper 过时分层注释。ControlHost/App.config 接 file="..\tl-shared.config" 删 7 共享键,保留 22 个 control 独有/业务键(换气/CCD 全留——control 真读)。C#/csproj 零改动。control\ 子目录;tl-shared.config 在 operate 根、control\ 下无副本。/status ServerUrl=http://127.0.0.1:10010/(由共享文件 urlIp+urlPort 拼成;control 子目录无此文件 → 只能经 ..\tl-shared.config 读到)。/status ServerUrl 变 19999;改回 10010 重启 → 恢复。对称坐实 control 实际读的就是这一份。/shutdown(JSON body token=tl13579)干净退出释放 COM。f547ac9 / plan c9f1c70 / Task2 ca30cb2+8f3d096 / Task3 d687a3f / Task4 c3866440+199db15 / Task5 9235e10。d687a3f/c386644/9235e10/1020e22)早已在 main、该分支不存在 → 配置收敛已落地 main,"待并 main"系笔误,本轮已在断点纠正。/debug/command(op枚举)分发(非每操作一端点)+ MJPEG 预览 + 红线电机钳位放 control 端(水平[0,220000]/垂直[0,125000]越界拒绝不下发)。是 D3-04(删 operate 死串口栈)的前置。需求文档/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。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。SweepExpired(LastSeen+ttl<now 自动 Dispose 归还)。追加 2 测试(超时回收后 IsCapturePaused=false + Heartbeat 失效;心跳续约保活)。关键踩坑见下。commit b7e8e40。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。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 绿。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错+测试绿。b4da856/e3fa590/ae80804/be88acf + 批B 968ad59/b7e8e40/cdc4976/5922797。91afb03):opus 审查揪出 Critical——相对运动(Forward/Backward)钳位 base 用会话跟踪位 CurrentVer/Hor(初值-1→fallback 0),真机电机在 120000 发 Forward 10000 时按 base=0 算 target=10000 放行,实际走到 130000 越 125000 红线。修为:钳位前回读真实物理位 ReadVerticalPositionWait/ReadHorizontalPositionWait 作 base + 成功后回读刷新(自愈漂移);顺带 motorDelay 透传 / Reset 加 ok 守卫 / Dispose 记日志。新增非零起点越界回归测试 red→green。7c43933 + b342374):Task8 ControlHttpServer 加可选参 DebugSessionManager + /debug/acquire|command|release|heartbeat 路由(状态码 acquire 200/409、heartbeat 200/410、command 按 code 410/400/200)+ ReadBody;Task9 Program.cs 装配 debugMgr(HardwareAccessLayer.GetHouseGate+DateTime.UtcNow+ttl10s)+ _exitEvent new 后起 TTL 看门狗(每3s SweepExpired)。Debug/Release 0 错,27 单测全绿。b4da856/e3fa590/ae80804/be88acf + 批B 968ad59/b7e8e40/cdc4976/5922797 + C-1 91afb03 + 批C 7c43933/b342374。aivfo-auth.user 本零用户(全库唯一用户表,dump 只建表)。按用户要求插 admin:密码=md5(AUTH_SALT+123456+AUTH_SALT)=582de128a6d0050981bf90059ceeb2d9(CryptoUtils.encode 盐固定),deleted='2017-01-01 00:00:00'(BaseEntity @TableLogic 未删哨兵)。直连 /api/gateway/auth/login 返 success+token——用户没问题。无 mysql 客户端,用 JDBC(maven-repo mysql-connector 8.0.28+JDK11) 连 192.168.0.108:3306 root/root 直插(临时文件/DbSql.java)。bin/Release/net6.0-windows/ 跑,dll.config 是 <appSettings file="..\tl-shared.config">、urlIp/urlPort 配置收敛后只在共享文件;..\tl-shared.config(=bin/Release/tl-shared.config)不存在→urlIp 读空→BaseUrl 拼坏→登录发错地址。真实部署 control 在 operate 的 control\ 子目录、..\ 指 operate 根,所以没问题。修:把 ivf_tl_Operate/tl-shared.config copy 到 bin/Release/。/debug 通真串口。MainThread/StartSendCommandThread 是 while(true){try/catch},异常捕获记录、循环继续、别舱无感)。SerialBin.cs:252/舱号或CCDSN重复)→errorlist.Add→InitTL 只要 errorList.Any() 就整体 return(StartMain.cs:89)→好舱也起不来。完全坏死/没接的舱则静默跳过(口打不开/握手失败 goto CC)。模块数≠11 已修成可继续。需求文档/specs/2026-06-23-舱室故障隔离与双端故障提示-design.md:启动期按舱容错 + 双端故障提示硬约束(operate监控页+front 都明确显示哪个舱/什么故障/时间,复用现有 alarm_data+MQTT 管道)。临时文件/ 下留 JDBC 工具 + 冒烟 ps1。100e5f8,工作区干净)= ①收尾完成。本轮做用户已定 a+c 的 ②:把加固专项 spec 拆成可执行开发计划。control/ivf_tl_Com/SerialBin.cs(StartMain using ivf_tl_Com),非 operate 死栈 ivf_tl_Entity/ComEntitys/SerialBin.cs(那套 D3-04 待删)。control 版 errorlist 仅 6 处来源:相机重复SN(:121)/相机读异常catch(:135)/舱号重复(:212)/CCDSN缺失(:252)/CCDSN重复(:260)/扫口catch(:318)。注:control 版"串口打开失败/握手失败"是静默 goto CC不进 errorlist(与 operate 版不同)。StartMain.InitTL(StartMain.cs:78-94)两处"errorList.Any() 即 return 空"=一刀切中止;InitHouse(:160-259)是一个大 try-catch,任一 HouseBinN 构造抛异常→return false→所有舱起不来。HttpService.AlarmApi(HttpService.cs:54)/AlarmApi1(:85)→POST /api/tl/control/alarm/reportCloudAlarm(现有 MqttAlarm 已走 AlarmApi1);快照=AppData.GetMonitorSnapshot(AppData.cs:222)→MonitorSnapshot(operate ControlClient 轮询读);运行期告警出口=HouseBin_HouseStateEvent→ReportAlarmController。开发计划/2026-06-23-舱室故障隔离与双端故障提示-实现计划.md(分支 feature/house-fault-isolation):
AppData.StartupFaults/ReportStartupFaults,二者 Task6 定义——先做 Task6 再回填 Task4 两处调用。feature/house-fault-isolation(从 feature/d2-02-debug-command-proxy 切——因单测工程 IvfTl.ControlHost.Tests 在 D2-02 分支、main 落后 16 commit,从 main 切会丢测试工程)。control/ivf_tl_Entity/(命名空间 IvfTl.Control.Entity.InitEntitys),非计划写的 IvfTl.Control.Entity/ 目录名。HouseFault.cs 建在 control/ivf_tl_Entity/InitEntitys/。SerialBinController.ReportAlarmController(tlSn,houseSn,houseState,comState,photoState,wellSn,airSwapState) → Java /reportAlarm → 报警责任链(温度/气压/舱门/串口HousePort/舱态/相机Photo/排气 handler)→ 写 alarm 表 → front报警列表 + operate顶部"系统异常(N)" 都读这表 + 短信/电话通知(报警链内置 getNotifier→模板)+ muteAlarm静音 + 恢复自动 stopAlarm 消警。HousePortAlarmHandler/PhotoStateAlarmHandler 均 if(SKIP==state)return;,SKIP=-1)。HouseBin.ComBin_ComStateEvent→HouseStateEvent→ReportAlarmController)。reportCloudAlarm:实读 AlarmManageImpl.reportCloudAlarm 只 sendGroupMessage 发 IM 群消息(钉钉),不入alarm表/不进列表/不能消警——形不成闭环;且新建 HOUSE_* alarmTypeKey 要 Java 字典登记=多余跨端依赖。全删。用户明确"群消息不要了""短信电话需要的"——正好对齐(短信电话=reportAlarm闭环内置,保留并复用)。ReportAlarmController:相机类故障(CcdSnMissing/CcdSnDuplicate/Camera*)→photoState=1;串口/编号/Init类→comState=1;其余维度-1跳过(不误清别的告警)。spec §4.2 + 计划 Task6 + 自查已同步改。HouseFault+HouseFaultType枚举(control/ivf_tl_Entity/InitEntitys/HouseFault.cs)——2单测绿。commit f7d1d75。StartupFaultPolicy(ivf_tl_Control,BadHouseSns/RunnableHouses/IsFatal,纯函数)——真值表6单测绿。761a996。abbf490。c2686f7。4b60fd9。AppData缺using IvfTl.Control.Entity.InitEntitys已补。StartupFaultPolicy.RunnableHouses算可跑舱;坏舱标Isolated存AppData.StartupFaults;IsFatal(零可跑)才中止。a9045c9。Build(sn,Action)/Start(sn,Action)逐舱try-catch,单舱构造异常登记InitException坏舱+跳过、不拖垮其余;CamNum/StartTask全加!=null判空。上报挪到StartRun(InitHouse之后)报含构造期故障的完整清单一次。最新commit。dotnet test IvfTl.ControlHost.Tests = 37 绿 0 失败(原27 + HouseFault2 + Policy6 + Snapshot2);dotnet build ivf_tl_Control.sln -c Debug 0 错。决策逻辑纯单测覆盖;启动核心改动(SerialBin/InitTL/InitHouse)行为靠 Task7 真机验。control\ 子目录跑,..\tl-shared.config才解析)→ /status 看 snapshot.Faults空 + started + houses齐 + 无回归。可自主跑。/shutdown(token tl13579)干净停(无看门狗);编 Release(先停旧实例解 DLL 锁)→ 提权起新 control pid10360(ivf_tl_ControlHost/bin/Release/net6.0-windows/,参数 --account=admin --password=123456 --cacheDisk=C --port=38080,..\tl-shared.config 在 bin/Release)。无活体培养(空闲监测),重启安全。/status = started:true、新增 Faults:[](空)、好舱 2/4/6/7/8/9 照常连接≈37℃、ServerUrl 127.0.0.1:10010。改造零回归。SerialBinController.ReportAlarmController(tlSn,houseSn,houseState,comState,photoState,wellSn,airSwapState) → /reportAlarm → 报警责任链(HousePort/Photo/温度/气压/舱门/排气 handler)→ aivfo_tl_setting.alarm 表 → front 报警列表 + operate"系统异常(N)" + 短信/电话(AlarmSchedule 每 1min 扫在报→getPersonList→aivfo-service 阿里云短信)+ muteAlarm 静音 + 恢复 stopAlarm 消警。AlarmDTO 接口 0正常/1异常/-1跳过;HousePort/Photo handler 均 if(SKIP==state)return(SKIP=-1)。reportCloudAlarm 实测只 monitorRequest.sendGroupMessage 发 IM 群消息、不入 alarm 表——已全弃用(用户也明确"群消息不要,短信电话要")。/reportAlarm 舱2 photoState=1(=ReportStartupFaults 对相机坏舱发的)→ alarm 表落 id=6 house_sn=2 PHOTO_STATE_ALARM type=0(在报),sms_map 已填。证实被排除舱(配置舱1-10均在 house 表)能正常落库——原"排除舱不在house表"担心不成立。alarm_personnel 原空→插测试联系人(name=测试-Claude验证, phone=18223210384, sms=1, state=1)→ 触发新告警(舱4 photoState=1,continueTime=0 命中 determineNotifyWay==0 发短信窗口)→ 约 1min 后 alarm_send_info 落 2 条(舱2/舱4,alarm_model=0 短信,alarm_respond=发送成功!=SendResultEnums.SUCCESS)。短信走阿里云(凭据硬编码在 AliConstant:signName=艾伟孚科技,appKey=LTAI5t...)。系统侧已成功派发;是否真到手机由阿里云决定(模板审批/余额),用户自查 18223210384。按用户要求未等阿里云回执。alarm_send_info 测试记录、测试联系人全部删除核零。新 control pid10360 保留运行(健康、无回归;本特性分支构建,未并 main)。/status 的 Faults 字段(HouseFaultRow);本步做 H-08 的 operate 那半——监控页加红色"舱故障"区展示它。front 报警列表那半 H-05/06 已通。MonitorSnapshot.Faults(HouseSn/FaultType/Reason/Stage/At/Isolated)已透出、ControlClient.GetStatusSnapshot() 已能整体带回,所以本步纯 operate 侧加展示,不碰 control。FaultType 透出的是英文枚举名(f.Type.ToString(),如 CcdSnMissing),需 operate 翻中文;Reason/Stage 已中文;At 是 UtcNow(展示要转本地);HouseSn=-1=相机/串口级未定位。ViewModel/ServiceMonitorFaultMapper.cs(新增):纯静态映射,不依赖 WPF/control,签名只收基础类型 → FaultTypeZh(8 枚举名→中文 + 未知码原样 + 空→"未知故障")/HouseText/AtText(UTC→本地 MM-dd HH:mm:ss)/IsolatedText。解耦便于独立验证。ViewModel/ServiceMonitorViewModel.cs:加 ObservableCollection<HouseFaultRowVm> Faults + 汇总属性 HasFaults/FaultSummaryText/FaultSummaryBrush + 显隐 FaultListVisibility/NoFaultVisibility(直接给 Visibility,不依赖未注册的布尔转换器——项目里 BoolToVisibilityConverter 根本没注册,首版误用已纠正);Refresh() 调 BuildFaultRows(snap.Faults) 填充;新增 HouseFaultRowVm 展示类。View/ServiceMonitorView.xaml:在"舱室状态"与"受护栏停止"区之间插红色"舱故障"区——标题带数量(红/绿)、无故障显绿条"舱室均正常"、有故障逐条红底(#FBE9E7+红框)显示 舱号/类型/原因/阶段/时间/隔离态。镜像现有 Houses 区样式。dotnet build ivf_tl_Operate.csproj -c Release 0 错(WPF 的 XAML 编进 BAML = 绑定路径/属性/语法全部对得上 ViewModel)。僵尸 operate 20268 未锁输出 DLL。/status → Faults 字段存在=True、当前数量 0(基线无故障)、Houses 10、ControlHosted True。证明字段透出 + operate 反序列化目标字段名对得上;无故障时 operate 显绿"舱室均正常"。临时文件/FaultMapperTest(Compile Include operate 真实 ServiceMonitorFaultMapper.cs,零外部依赖)→ 全 8 枚举名中文化正确 + 未知码原样兜底 + 舱号边界(2/11/-1/0)+ UTC 01:30→本地 09:30(东八区+8 正确)+ 隔离布尔,全部通过。"有故障"核心路径有了真实运行验证。临时文件/。dcf7a52 "docs(flow): 完成业务流程图制作+可复用规范模板",包含 3 份文档(流程图制作规范/流程图交付清单/进度状态更新)。<Image>。分支 feature/d2-02-mjpeg-preview(从 main 切)。需求文档/specs/2026-06-24-D2-02-第二阶段-MJPEG实时预览-design.md + 开发计划/2026-06-24-D2-02-第二阶段-MJPEG实时预览-实现计划.md。ivf_tl_operate_2.0/control/ivf_tl_ControlHost/):Debug/MjpegStreamWriter.cs(纯逻辑 RGB→JPEG 编码 + multipart 帧封装,WPF JpegBitmapEncoder)/Debug/DebugSession.cs(+StreamBroken 字段)/Debug/DebugSessionManager.cs(+TryGet 只读,不动既有6方法)/ControlHttpServer.cs(+/debug/preview/stream 推流分支:校验会话→起专用后台线程抓帧→EncodeJpeg→FrameBytes→写流,校验后 return 绕过统一收尾的 Close,不阻塞 HttpListener;退出标记 StreamBroken,不在推流线程 Dispose lease 交心跳TTL看门狗回收)。ivf_tl_operate_2.0/ivf_tl_Operate/):Debug/MjpegFrameParser.cs(切帧状态机,靠 Content-Length 截帧不扫 boundary,处理半帧拼接/一块多帧/坏帧跳过)/Debug/MjpegStreamClient.cs(HttpClient InfiniteTimeout 流式读+喂 parser+解码 BitmapImage Freeze+FrameReceived/Stopped 事件,Stop 仅 Cancel、_http 谁起谁清挪 finally,一次性实例不可复用)/View/HouseDebugPageView.xaml(预览区 Border 内加 <Image x:Name=_previewImage>)/View/HouseDebugPageView.xaml.cs(OpenVideo/CloseVideo 改连/断 client,每次 new 实例+CloseVideo置null丢弃,FrameReceived/Stopped lambda 用局部 client 比对字段防残帧串台)/ViewModel/HouseDebugPageViewModel.cs(+CurrentSessionId 属性)/Helpers/ControlClient.cs(BaseUrl 提 public)。/shutdown(token tl13579)优雅停机(关硬件+释放7COM口+退出),再编 0 错。② 僵尸 operate 20268 仍在(开机 6/21,提权杀不掉),但不锁输出 DLL、不挡编译,符合记忆"需重启清"。/debug/acquire 拿 sid 赋 vm.CurrentSessionId,否则预览点不开(阶段边界非bug)。② 真机出图看画面。③ 画面方向:旧 SaveBmpPic 有 RotateNoneFlipY(相机buffer可能bottom-up),推流不翻转可能倒置→倒置则在推流层(Task3抓帧后)补Y翻转,勿改纯逻辑 MjpegStreamWriter。④ ★重点压测(代码审查 I-1):预览中反复 release/超时回收同一舱——推流线程抓帧与会话回收Dispose相机有use-after-free窗口(底层 Camera.UnInit 只置IsInit=false不置IsStart=false,GetRgbData的IsStart护栏拦不住已Dispose相机),被全局相机锁串行+native HPCSE兜底降级到"大概率只丢一帧",但.NET6 HPCSE不保证可恢复,真机务必压确认不偶发崩溃;若崩,最小修=底层Camera.UnInit顺带置IsStart=false(单独评估)。⑤ 相机锁冲突实测:A舱预览+B舱采集拍照同时观察影响。⑥ 关预览画面无残帧(已代码根治当值实例闸,真机确认)。