For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development 或 superpowers:executing-plans 逐 Task 执行。步骤用 checkbox(
- [ ])追踪。 父依据:需求文档/01-架构与合并方案.md(合并形态权威)、需求文档/13-统一硬件访问层接口定义.md(HAL 接口)、00-需求总览.md§4/§5(D1/D2/D7)。 里程碑:M1 合并跑通(见00-需求总览.md§7、需求文档/09/12)。
Goal: 把 ivf_tl_operate_2.0(前台 WPF)与 ivf_tl_control_2.0(采集服务)合并为单进程:operate 登录成功后托管 control 的 StartRun() 为后台线程;删除 control 独立登录窗 Window1,全程序单登录;落地统一硬件访问层(HAL)单例,让 control 采集 / operate 调试 / 自动对焦三方改为向 HAL 借用串口与相机句柄,去掉各自直接 new ComBin/SerialPort/Camera,达成"调试取图 vs 后台采集"切换不报端口占用、不死锁的最小验证(01 §3 必过)。
Architecture: 单进程托管(D1)。主进程 = operate(App.xaml StartupUri=MainWindow.xaml)。control 各子工程(Com / CameraHelper / SerialHelper / Controller / Services / Entity / Util)作为类库并入合并解决方案,被托管入口 = ivf_tl_Control.StartMain.StartRun()(后台线程),不再有 control 的 UI/登录窗。三方硬件访问统一经新建的 IvfTl.Hardware 程序集(HAL 单例,接口签名见 13 文档 §3),HAL 内部用接口把旧 ComBin/Channel/Camera 包起来——代码隔离原则(01 §5):不重写老协议逻辑,只把它们封进 HAL 实现。
Tech Stack: C# / WPF / .NET(operate/control 目标 net8.0-windows,autofocustool 已 .NET 8);MVCAPI.dll(MVC2000 相机 P/Invoke);SerialPort 5E 串口协议。新增程序集 IvfTl.Hardware(HAL 接口 + 实现)。
本地环境现实(已实测,见 项目文档/开发计划/2026-06-17-改造执行框架与自动对焦数据层.md):本机 dotnet 6,而 operate/control 项目目标 net8.0-windows;无下位机、无相机、无运行中的微服务/中间件;根目录非 git 仓库。→ 本计划的代码无法本地构建或运行。
因此本计划每个 Task 的"构建/运行验证"一律改为:代码完成 → 登记 项目文档/进度/待验证清单.md 对应 V 项(依赖 net8 工具链 / 真机),到 M7 集中测。本计划不写任何假装能本地 dotnet build / 运行的命令。能在本机完成并核对的只有:源码改动、接口/类型签名一致性、调用点替换是否齐全(用 grep/codegraph 核对残留 new ComBin/new Camera/new SerialPort)。
HAL 接口骨架来源:13-统一硬件访问层接口定义.md §3 已给出只签名的接口(IHardwareAccessLayer / ISerialChannel / ICamera / IHouseHandle / IHouseGate / IHardwareLease / ICameraGate + HouseDeviceInfo / HardwareUser / DoorState)。本计划负责:① 把这套接口落成 .cs 文件;② 写出以旧代码为包装的实现;③ 改三方调用点。接口签名以 13 文档为准,本计划不重新发明签名。
代码隔离原则(01 §5,全程遵守):
ComBin/Channel/Camera/HouseBin 的老协议逻辑;HAL 实现内部调用旧类,不改旧类内部。Window1 的注释块、调试页旧 ClosePort 等),合并稳定后再清理;本计划仅"停用 + 隔离"。ivf_tl_control_2.0 整目录退役(删除)= 合并收尾动作,不在本计划执行。解锁判据(01 §5.5,全满足才删):① 本 M1 真机验证通过(V-011~V-016 等) ② operate 现以 3 条 ProjectReference 引用的 ivf_tl_Control/IvfTl.Hardware/IvfTl.AutoFocus 物理并入合并解决方案 ③ 消解 operate/control 同名程序集(ivf_tl_Entity/ivf_tl_Services)冲突 ④ 改完引用编译 0 error。届时另起清理 Task。StartRun 里 MessageBox + SendMessage 点 Yes 的 hack、明文账号密码)。Git 说明:根目录非 git 仓库,"Commit"步骤无法执行。每个 Task 末尾检查点用"保存并核对文件 + grep 残留"代替。
| 事实 | 位置(精确) |
|---|---|
| operate 启动入口:StartupUri=MainWindow.xaml | ivf_tl_operate_2.0/ivf_tl_Operate/App.xaml(OnStartup 仅做 Mutex+异常注册,App.xaml.cs:30-43) |
| operate 登录 + 主页加载点(StartRun 要挂这里) | ivf_tl_operate_2.0/ivf_tl_Operate/MainWindow.xaml.cs:44-59(MainWindow_Loaded:new LoginWindow(this).ShowDialog() → 成功后 LoadPage(mainPageView)) |
| operate 登录窗(保留,全程序唯一登录) | ivf_tl_operate_2.0/ivf_tl_Operate/Windows/LoginWindow.xaml.cs:31(LoginWindow(Window w)) |
| control 被托管入口 | ivf_tl_control_2.0/ivf_tl_Control/StartMain.cs:34 StartRun() → InitTL(66) → InitHouse(183) → AppData.StartAsync().Wait()(52) |
| control 登录窗(删除目标)+ 其内 StartRun 调用 | ivf_tl_control_2.0/ivf_tl_ControlTest/Window1.xaml.cs:25 Window1();Window1_Loaded1:51 内 Login() + new StartMain().StartRun()(77-78);Button_Click:174 也调 StartRun |
| StartRun 内的 MessageBox+SendMessage hack | StartMain.cs:97-141(InitTL:模块数≠11 时弹 MessageBox.Show(...),3 秒后 FindWindow/EnumChildWindows/SendMessage(BM_CLICK) 自动点 Yes) |
| 两个 AppData 单例命名冲突 | control ivf_tl_Control.AppData(AppData.cs:36 Lazy 单例,Login()/StartAsync()/LogService);operate ivf_tl_Operate.AppData(独立单例) |
| operate 调试硬件 new 点 | ivf_tl_operate_2.0/ivf_tl_Operate/ViewModel/HouseDebugPageViewModel.cs:35 ComBin comBin、:36 Camera camera、:213 new ComBin(houseSn, housePort)、:230 new Camera(CCDId, w, h, exposure)(类型 ivf_tl_Entity.ComEntitys.ComBin / ivf_tl_Entity.CameraEntitys.Camera) |
| control 采集硬件 new 点 | ivf_tl_control_2.0/ivf_tl_Com/HouseBin.cs:159 new ComBin、:450 / :844 new Camera;SerialBin.cs:78 new Camera、:190 / :364 new ComBin;ivf_tl_SerialHelper/Channel.cs:128 new SerialPort() |
| 自动对焦蓝本(HAL 接口以它为蓝本) | autofocustool/Serial/Protocol.cs、Serial/SerialMotor.cs、Serial/HouseMotor.cs、Camera/Camera.cs(M1 不接入对焦业务,仅留接口位) |
注:M1 只做"合并 + HAL 地基 + 调试/采集两方接入 + 最小互斥验证"。自动对焦三方接入(
HardwareUser.AutoFocus)属 M2,本计划只在 HAL 接口/枚举里留位,不写对焦借用逻辑。
项目文档/进度/待验证清单.md)| V 项 | 待验证内容 | 依赖 | 出处 |
|---|---|---|---|
| V-011 | 单进程启动:operate 前台 + control StartRun() 后台线程跑通,无 control 登录窗、无第二个 UI 窗口;control 初始化日志正常输出 |
net8 工具链 + 真机(下位机 11 模块) | M1-01 / M1-02(01 §6) |
| V-012 | 全程序单登录:只弹 operate LoginWindow,登录态同时满足 operate 与 control(control 的 AppData.Login 用同一账号成功) |
net8 + 真机/可达网关 | M1-02 |
| V-013 | 硬件互斥最小验证(01 §3 M1 必过):调试页"取一帧图"与 control 后台采集切换,不报端口占用、不死锁;前台借用时该舱采集暂停、归还后恢复采集节拍 | net8 + 真机 | M1-03(13 §⑤ V6) |
| V-014 | HAL ScanDevices 设备发现稳定:相机 index/COM 口开机漂移 + CCDSN→index 配对正确(禁止写死) |
真机多次重启 | M1-03(13 §⑤ V7) |
| V-015 | StartRun 去 hack 后行为:模块数≠11 的"继续运行"判定改为静默/日志后,初始化分支仍正确(不再依赖自动点 Yes) | 真机(模块数异常场景) | M1-01 |
| V-016 | 统一相机锁 ICameraGate:合并后 control 采集 GrabRgb 与 operate 调试 SetExposure/GrabStable 并发不段错误(替代三套各自 static 锁) |
net8 + 真机 | M1-03(13 §⑤ V5) |
字节序/写 EEPROM/读超时等(13 §⑤ V1/V2/V3/V8)属 M2 对焦链验证,M1 不展开,但 HAL 实现里照 13 接口注释预留属性(
MoveReadTimeoutMs等),登记时标注"M2 验"。
出口验收:合并解决方案中,operate 登录成功后在后台线程调用 ivf_tl_Control.StartMain.StartRun(),control 采集逻辑启动且不再弹任何 control 窗口;StartRun 内的弹窗自动点 Yes hack 改为静默/日志。→ 登记 V-011、V-015。
[ ] 步骤 1 — 把 control 子工程并入合并解决方案(作为类库)。
改什么:在合并解决方案里引用 control 的类库工程 ivf_tl_Control(含 StartMain/AppData)、ivf_tl_Com、ivf_tl_CameraHelper、ivf_tl_SerialHelper、ivf_tl_Controller、ivf_tl_ServicesImpl、ivf_tl_Entity(control 侧)、ivf_tl_UtilHelper。不引用 ivf_tl_ControlTest(启动壳,含 Window1,见 M1-02)。
为何:01 §2 落地步骤 1——control 各子工程作为类库并入;ControlTest 是独立 UI 壳,合并后不需要。
隔离:仅加引用,不动 control 各类库内部代码。
⚠ 命名冲突:control 有 ivf_tl_Control.AppData(AppData.cs:36),operate 有 ivf_tl_Operate.AppData,命名空间不同不会编译冲突,但语义是两个独立单例(01 §2 步骤 3)。M1 阶段保持两个单例并存(control 的归 control 后台用,operate 的归前台用),不强行合并为统一上下文——合并上下文是更大的重构,留待后续;本步只确认两者命名空间隔离、无类型名直接碰撞。
登记:构建依赖 net8 → 记 V-011("合并解决方案可编译"作为 V-011 前置)。
[ ] 步骤 2 — 去掉 StartRun() 内的 MessageBox + SendMessage 自动点 Yes hack,改静默/日志。
文件:ivf_tl_control_2.0/ivf_tl_Control/StartMain.cs:97-141(InitTL 内 modelCount != 11 分支)。
改什么:把 MessageBox.Show(...) + Task.Delay(3000) + FindWindow/EnumChildWindows/GetClassName/GetWindowText/SendMessage(BM_CLICK) 这段"弹窗再用 user32 自动点 Yes"的 hack 替换为:直接 AppData.LogService.TLLog($"检测到 {modelCount} 个模块(期望 11),按配置策略继续/中止", LogEnum.RunRecord),并按既有 errora="结束" 语义保留"是否继续"的可配置开关(默认继续,配置项控制)。FindWindow/SendMessage 等 [DllImport] 声明(StartMain.cs:321-332)可暂留(死代码先不删,01 §5),仅停止调用。
为何:合并后是单进程无人值守前台,不能弹模态框;原 hack 是"自己弹框再用 Win32 替用户点 Yes"的临时做法(01 §5 要求单独记录此 hack)。改静默后初始化在后台线程跑,不阻塞 UI。
隔离:只改这一分支的交互方式,不动 InitTL 其余的 SerialBin.UpdataCamera()/Start()、UpdateTLInfoController 等采集初始化逻辑。
登记:模块数异常场景需真机验证 → V-015。
[ ] 步骤 3 — 在 operate 登录成功后用后台线程调用 StartRun()。
文件:ivf_tl_operate_2.0/ivf_tl_Operate/MainWindow.xaml.cs:44-59(MainWindow_Loaded)。
改什么:在 new LoginWindow(this).ShowDialog() 返回 true(登录成功)、LoadPage(mainPageView) 之后,新增后台启动:
System.Threading.Tasks.Task.Run(() =>
{
var startMain = new ivf_tl_Control.StartMain(); // 已并入的 control 类库
string err = startMain.StartRun(); // 阻塞跑 InitTL→InitHouse→StartAsync
if (!string.IsNullOrEmpty(err))
ivf_tl_Services.Log4netHelper.WriteLog($"control 后台启动失败:{err}");
});
为何:01 §2 步骤 2——operate 启动、登录成功后调用 StartMain.StartRun()。StartRun 内部本就阻塞(AppData.StartAsync().Wait(),StartMain.cs:52),必须放后台线程,避免卡 UI 线程。复刻原 Window1_Loaded1(Window1.xaml.cs:60-87)里"Task.Run 内调 StartRun + 失败记日志"的形态,但去掉 Environment.Exit(0)(合并后 control 启动失败不应杀整个前台进程,仅记录/降级,配合 R5 自愈策略——M1 先记日志,自愈属后续)。
隔离:仅在 operate 既有登录成功回调里追加一段后台启动;不改 LoginWindow、不改 control StartRun 主体(步骤 2 已单独处理 hack)。
关键 hack 记录:control 端 AppData.Login(account,password) 与 operate 登录是两套账号体系——M1-02 处理"单登录"如何驱动 control 的 Login(见 M1-02 步骤 2)。
登记:后台线程跑通 + control 初始化日志 → V-011。
[ ] 检查点 M1-01:grep 确认 operate MainWindow_Loaded 已含 StartMain().StartRun() 后台调用;StartMain.cs:97-141 已无 MessageBox.Show/SendMessage 调用(声明可留)。保存并核对文件。构建/运行 → 待 net8+真机(V-011/V-015)。
出口验收:合并程序启动只出现 operate LoginWindow,无 Window1;登录态同时满足前台 operate 与后台 control(control AppData.Login 用同一账号登录成功)。→ 登记 V-012。
[ ] 步骤 1 — 排除 ivf_tl_ControlTest(Window1 所在工程)出合并启动路径。
文件:ivf_tl_control_2.0/ivf_tl_ControlTest/Window1.xaml、Window1.xaml.cs(启动壳)。
改什么:合并解决方案不把 ivf_tl_ControlTest 作为启动项,且 operate 不引用该工程(M1-01 步骤 1 已约定不引用)。Window1 的 App.config 启动逻辑由 operate 启动流程接管。Window1.xaml.cs 文件本身先不删(死代码先不删,01 §5),仅从合并产物的启动路径剔除。
为何:01 §2——删除 control 的 Window1 登录窗与独立 UI,全程序单一登录入口(operate LoginWindow)。Window1 是 control 唯一的窗口入口(codegraph:StartRun 仅 2 处调用均在 Window1.xaml.cs),剔除它即去掉 control 全部独立 UI。
隔离:不改 Window1 内部;只在解决方案/引用层面剔除。
登记:启动无 Window1 → V-011/V-012(真机/运行验)。
[ ] 步骤 2 — 把 Window1 的登录/初始化逻辑并入 operate 登录后的初始化流程。
文件:来源 ivf_tl_control_2.0/ivf_tl_ControlTest/Window1.xaml.cs:51-87(Window1_Loaded1);落点 ivf_tl_operate_2.0/ivf_tl_Operate/MainWindow.xaml.cs(M1-01 步骤 3 新增的后台块内)。
改什么:把 Window1_Loaded1 里 control 启动所必需的前置——
① ivf_tl_Control.AppData.Instance.Login(account, password)(control 端登录,Window1.xaml.cs:68);
② PathHelper.pan = cacheDisk / AppData.Instance.LogService.Pan = cacheDisk(缓存盘,:74-75);
—— 搬进 operate 后台启动块,在调用 StartRun() 之前执行。account/password 取自 operate 登录成功后的凭据(与 operate LoginWindow 同一账号,见 01 §2 步骤 4:control 原读 App.config 账号的逻辑并入 operate 登录后流程)。cacheDisk 仍可从 control 侧 config 读(M1 不做配置统一,那是 M5)。
为何:01 §2 步骤 4——control 原 Window1 的登录/初始化(读 App.config 账号)逻辑并入 operate 登录后的初始化流程,实现"单登录驱动两端"。
⚠ 关键差异记录:operate 与 control 是两套账号/登录服务。M1 的"单登录"指 UI 上只有一个登录窗;底层 control 仍需用账号调它自己的 AppData.Login。若两端账号体系不一致(V-012 要验),M1 先用 operate 登录得到的账号同时喂给 control Login;若 control 端登录失败,按 M1-01 步骤 3 的"记日志不退进程"处理。不去掉 Window1 里 Environment.Exit(0) 的等价物——合并后不杀进程。
隔离:搬逻辑、不改 AppData.Login 内部实现。
登记:单登录 + 两端登录态 → V-012。
[ ] 检查点 M1-02:grep 确认合并启动路径无 Window1/ivf_tl_ControlTest 引用;operate 后台启动块已含 control AppData.Instance.Login(...) + PathHelper.pan 赋值,且在 StartRun() 之前。保存并核对。运行验 → V-012。
出口验收:新建 IvfTl.Hardware 程序集落地 13 文档 §3 接口 + 以旧代码为包装的实现;HAL 单例在 operate 启动时初始化一次;operate 调试(HouseDebugPageViewModel)与 control 采集(HouseBin/SerialBin)改为向 HAL 借用 ISerialChannel/ICamera,删除各自 new ComBin/new Camera/new SerialPort;调试取图 vs 采集切换不冲突。→ 登记 V-013/V-014/V-016。
[ ] 步骤 1 — 新建 IvfTl.Hardware 程序集,落地 13 文档 §3 接口(只签名)。
文件(新建):IvfTl.Hardware/IHardwareAccessLayer.cs、ISerialChannel.cs、ICamera.cs、Concurrency.cs(含 IHouseGate/IHardwareLease/ICameraGate/HardwareUser)、Models.cs(HouseDeviceInfo/IHouseHandle/DoorState)。命名空间 IvfTl.Hardware(13 §3 建议)。
改什么:逐字落地 13 文档 §3.1–3.4 的接口签名(IHardwareAccessLayer.ScanDevices/GetSerial/GetCamera/GetHouse/ShutdownAll;ISerialChannel 的 Open/Close/SendWait/ShakeHandsWait/各电机 Wait/温压门/IO/LED + MoveReadTimeoutMs/QueryReadTimeoutMs/MotorSettleMs;ICamera 的 Init/SetOpMode/SetExposure/SetGain/GrabRgb/GetFrameBuffer/GrabStable/StartPreview/StopPreview;并发组三接口 + 枚举)。
为何:13 文档已是权威接口骨架,HAL 必须以它为契约,三方才能统一借用。
隔离:纯新增接口文件,不依赖任何旧工程;签名以 13 文档为准,不改签名。
登记:编译依赖 net8 → 随 V-011 前置。
[ ] 步骤 2 — 写 HAL 实现:用接口把旧 Channel/ComBin/Camera 包起来(不改旧逻辑)。
文件(新建):IvfTl.Hardware/Impl/HardwareAccessLayer.cs(单例,内部 portName→ISerialChannel、cameraIndex→ICamera 字典)、SerialChannelImpl.cs、CameraImpl.cs、CameraGateImpl.cs、HouseGateImpl.cs。
改什么(代码隔离的核心):
SerialChannelImpl 内部持有并调用 control 旧串口栈(ivf_tl_SerialHelper/Channel.cs 的 SerialPort + ivf_tl_Com/SerialBin/ComBin 的同步 XxxWait 方法),把 ISerialChannel.HorizontalMoveToWait/TemperatureWait/... 转发到旧 ComBin 的对应方法。不重写协议/收帧逻辑(保留 Commander/Analysiser 那套)。CameraImpl 内部持有 旧 Camera(ivf_tl_CameraHelper/Camera.cs),ICamera.Init/SetExposure/GrabRgb/... 转发到旧 Camera.InitCamera/SetPartOfCapInfo/GetRgbData/Usb2Start...;GrabStable 用旧抓帧 + 13 §3.5 的"丢残留帧 + 到位延时 + 重试"语义包装。CameraGateImpl 提供全进程唯一相机锁(Invoke),所有 CameraImpl 的 native 调用经它串行化——替代 control/operate/af 三套各自的 static locker/Locker(13 §②/§3.5:三把不互斥的锁合并为一把)。HardwareAccessLayer 单例:GetSerial(portName) 惰性建唯一 SerialChannelImpl 并缓存(重复调用返回同一实例 → 杜绝同口重复 SerialPort.Open());GetCamera(index) 同理;ScanDevices 复刻 af DeviceScanner.ScanAll(或 control SerialBin.UpdataCamera 的枚举)做 index/COM/CCDSN 配对。
为何:01 §3/§5 + 13 §②——合并后三套硬件代码争用同一物理资源,HAL 必须"每 COM 口/每相机唯一持有者 + 跨调用者统一锁 + 按舱借用"。用接口把老代码包进实现,是代码隔离原则的直接落地。
隔离:HAL 实现调用旧类,不修改旧类内部;旧类暂保留其自身的 static 锁字段(死代码先不删),但所有经 HAL 的 native 调用一律走 ICameraGate。
登记:统一锁并发安全 → V-016;设备发现稳定 → V-014。[ ] 步骤 3 — HAL 单例在 operate 启动时初始化一次。
文件:ivf_tl_operate_2.0/ivf_tl_Operate/MainWindow.xaml.cs(M1-01 步骤 3 后台启动块内,StartRun() 之前)或 operate App.OnStartup(App.xaml.cs:30)。
改什么:在 control StartRun() 之前 HardwareAccessLayer.Instance.ScanDevices() 完成设备发现,确保后台采集与前台调试拿到的是同一组句柄。
为何:13 §3.1——"合并后由宿主(operate 主进程)在启动时初始化一次";必须早于 control 采集启动,否则采集线程仍会自己枚举/打开。
隔离:仅新增一行初始化调用。
登记:随 V-011/V-014。
[ ] 步骤 4 — control 采集改为向 HAL 借用,删除采集端直接 new。
文件:ivf_tl_control_2.0/ivf_tl_Com/HouseBin.cs:159(new ComBin)、:450/:844(new Camera);ivf_tl_Com/SerialBin.cs:78(new Camera)、:190/:364(new ComBin)。
改什么:把这些 new 替换为从 HAL 取:HardwareAccessLayer.Instance.GetSerial(port) / GetCamera(index);采集每舱节拍按 13 §④ 用 using var lease = houseGate.Acquire(HardwareUser.ControlCapture, timeoutMs),拿不到(前台正在调试)则跳过本轮该舱,下一节拍重试(低优先级让路);响应 PauseCapture/ResumeCapture。原采集流程(温压门/电机/补排气/按曝光抓帧)改调 lease.Serial.* / lease.Camera.*。
为何:01 §3 + 13 §④——control 采集是后台节拍、优先级低于前台调试/对焦;这是 V-013 互斥验证的采集侧。
隔离:替换 new 与抓取方式,不改采集业务节拍/算法;旧 ComBin/Camera 类保留(被 HAL 包装)。
⚠ 范围控制:SerialBin.UpdataCamera/Start(开机枚举,SerialBin.cs:78/190 在 StartRun→InitTL 阶段)若已被 HAL ScanDevices 取代,则改为复用 HAL 发现结果;若时序上 HAL 尚未就绪,M1 可保留枚举但枚举后不再独立持有句柄,统一交还 HAL。此处时序以步骤 3 的初始化顺序为准。
登记:采集侧借用 + 互斥让路 → V-013。
[ ] 步骤 5 — operate 调试改为向 HAL 借用,删除调试端直接 new。
文件:ivf_tl_operate_2.0/ivf_tl_Operate/ViewModel/HouseDebugPageViewModel.cs:35-36(字段 ComBin comBin/Camera camera)、:213(new ComBin(currentHouse.houseSn, currentHouse.housePort))、:230(new Camera(CurrentCCDId, ccdWidth, ccdHeight, ccdExposure))。
改什么:按 13 §④ operate 调试接入——打开调试页:var house = HAL.GetHouse(houseSn),using var lease = houseGate.Acquire(HardwareUser.OperateDebug)(HAL 自动暂停 control 对该舱采集);调试动作改调 lease.Serial.HorizontalMoveToWait/...、lease.Camera.SetExposure/GrabStable/StartPreview(替代现状 VM 自建 comBin/camera 与 camera.SetPartOfCapInfo);关闭调试页 lease.Dispose() 归还,HAL 恢复采集——删掉调试页自己的 ClosePort/UnInit(统一关闭路径,杜绝句柄泄漏;旧关闭方法体先留作死代码不删,仅不再调用)。
为何:13 §④ + 01 §3——调试是前台高优先级借用,借用即暂停该舱采集;这是 V-013 互斥验证的调试侧,也是 01 §3 "调试取一帧图 vs 后台采集切换不冲突"的最小验证主场景。
隔离:替换硬件获取与调用入口,不重写调试页 UI 绑定逻辑;曝光入口 SetExposure(VM)改为转 lease.Camera.SetExposure。
登记:调试取图 vs 采集切换不冲突、暂停/恢复时序 → V-013(01 §3 M1 必过)。
[ ] 检查点 M1-03:grep 全仓确认 new ComBin / new Camera / new SerialPort 在 HouseBin.cs/SerialBin.cs/HouseDebugPageViewModel.cs 内仅剩 HAL 实现工程里的包装调用,业务侧已替换为 HardwareAccessLayer.Instance.Get* / lease.*。确认调试页旧 ClosePort/UnInit 不再被调用。保存并核对。互斥最小验证 → 待 net8+真机(V-013/V-014/V-016)。
合并后启动/硬件/登录三类故障,按以下顺序定位(先看日志,再二分隔离)。
A. 合并后启动失败(进程起不来 / 后台 control 不动)
Log4netHelper 日志 + control AppData.LogService.TLLog(RunRecord/RunException):定位卡在 InitTL(相机/串口枚举)还是 InitHouse 还是 StartAsync。StartRun() 后台块——若 operate 前台正常起,说明问题在 control 托管侧(继续看 control 日志);若前台仍崩,问题在 operate 合并引用/HAL 初始化。AppData 是否被误用(确认前台用 ivf_tl_Operate.AppData、后台用 ivf_tl_Control.AppData)。B. 硬件争用(端口占用 / 相机句柄崩 / 死锁)
new ComBin/new SerialPort(检查点 M1-03);确认 GetSerial(port) 对同口返回同一实例(HAL 字典命中)。ICameraGate.Invoke,旧三套 static 锁未被绕过(V-016)。IHouseGate.Acquire 的超时与"采集拿不到即跳过本轮"是否生效(采集不得无限等前台);调试 lease.Dispose() 是否在所有路径(含异常)都归还——用 using 保证。C. 登录流程断(登录后黑屏 / control 未登录 / 重复登录)
LoginWindow、Window1 已剔除(M1-02 步骤 1)。AppData.Login 返回值日志(M1-02 步骤 2)——账号是否正确透传、网关是否可达。MainWindow_Loaded 里 LoadPage(mainPageView) 仍在 StartRun 后台块之前/并行执行,未被后台阻塞(StartRun 在独立 Task.Run,不应卡 UI)。MainWindow.xaml.cs:44-59、HouseDebugPageViewModel.cs:35-36/213/230;control StartMain.cs:34/66/97-141、Window1.xaml.cs:25/51-87/77、HouseBin.cs:159/450/844、SerialBin.cs:78/190/364、Channel.cs:128、AppData.cs:36。StartMain.StartRun/InitTL/InitHouse、Window1.Window1_Loaded1、AppData.Instance/Login/StartAsync、ComBin、Camera、MainWindow_Loaded、LoginWindow(Window)、HAL 接口名(13 §3 原文)。Channel/ComBin/Camera,不改旧逻辑;死代码先不删;hack 单独记录。