Bläddra i källkod

docs(阶段1): 进度目录+阶段1计划复核回写收尾(App.config前置/DependFile清单/108库实测订正)

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

+ 16 - 8
项目文档/开发环境/服务器测试环境.md

@@ -54,21 +54,29 @@ ssh root@192.168.0.108        # 密码 aivfo2017
 | `aivfo-auth`(中划线) | 4 | aivfo-gateway 认证/网关 |
 | `aivfo_services` | 2 | aivfo-service 核心服务 |
 | `aivfo-tl`(中划线) | 4 | data-transmission 数据传输 |
-| `aivfo_tl_setting` | 17 | tl-control 控制配置 |
+| `aivfo_tl_setting` | 18 | tl-control 控制配置(含 autofocus-data-layer 迁移后的对焦表,故 18 而非基线 17) |
 | `aivfo_tl`(下划线) | 25 | business-manage 业务管理 |
-| `log` | 1 | 框架日志组件 |
+| `log` | 2 | 框架日志(`system_log`)+ 操作日志(`operation_log`,operation-log 迁移后) |
 | `quartz` | 11 | 定时任务组件 |
 
 > ⚠️ `aivfo_tl`(下划线,业务库) 与 `aivfo-tl`(中划线,数据传输库) 是两个不同的库。
 
-**⏳ 未执行的内容(按主文档保留手动,与中间件运行无关):**
-- `/opt/sql/migrations/2026-06-17-autofocus-data-layer.sql`(对 `aivfo_tl_setting`,对焦数据层)
-- `/opt/sql/migrations/2026-06-18-operation-log.sql`
-- `/opt/sql/精简测试数据.sql`(测试数据)
+**✅ 已补执行的迁移(2026-06-19 集群恢复时执行,108 实测已入库):**
+- `/opt/sql/migrations/2026-06-17-autofocus-data-layer.sql`(对 `aivfo_tl_setting` 加对焦数据层,tl-control 启动曾因缺列报错 → 执行后 `aivfo_tl_setting` 表数 17→18)
+- `/opt/sql/migrations/2026-06-18-operation-log.sql`(在 `log` 库建 `operation_log`,oplog 入库依赖它 → `log` 库表数 1→2)
 
-需要时手动执行,例如:
+> ⚠ 本节早期(2026-06-18)曾把上述两条标为「未执行」,与 `连接配置清单-换服务器必读.md` 顶部 2026-06-19 核验记录不一致;**以 108 实测为准:两条 schema 迁移均已执行**(核对命令见下)。
+
+**⏳ 仍未执行(可选,与中间件运行无关):**
+- `/opt/sql/精简测试数据.sql`(测试数据,按需手动执行)
+
+核对表数 / 复跑测试数据,例如:
 ```bash
-mysql -uroot -proot aivfo_tl_setting < /opt/sql/migrations/2026-06-17-autofocus-data-layer.sql
+# 核对已执行的迁移(实测应为 18 / 2)
+mysql -h192.168.0.108 -uroot -proot -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='aivfo_tl_setting'"   # 18
+mysql -h192.168.0.108 -uroot -proot -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='log'"                 # 2
+# 按需导入测试数据
+mysql -uroot -proot < /opt/sql/精简测试数据.sql
 ```
 
 ---

+ 111 - 43
项目文档/开发计划/2026-06-22-阶段1-control独立进程骨架.md

@@ -8,53 +8,56 @@
 
 **Tech Stack:** C# / .NET 6 (net6.0-windows) / WPF(operate)/ System.Net.HttpListener / Newtonsoft.Json / Mutex / 现有 `ivf_tl_Control` 类库 / xUnit(纯逻辑单测)/ MSBuild(`dotnet build`)。
 
-**关键现实约束:** 这是 WPF + 串口/相机硬件项目,进程拉起、采集启动、HttpListener 多数只能**编译验证 + 真机/运行期验证**;纯单测仅覆盖可隔离的纯逻辑(命令行参数解析、status JSON 序列化)。计划里凡标 **[真机]** 的步骤须用户在场,绝不无人值守驱动电机。
+**关键现实约束:** 这是 WPF + 串口/相机硬件项目,进程拉起、采集启动、HttpListener 多数只能**编译验证 + 真机/运行期验证**;纯单测仅覆盖可隔离的纯逻辑(命令行参数解析、status JSON 序列化)。计划里凡标 **\[真机]** 的步骤须用户在场,绝不无人值守驱动电机。
 
 **前置参考:** 现状基线 `项目文档/control-逻辑与配置全景.md`;架构设计 `项目文档/需求文档/specs/2026-06-22-operate-control-双进程拆分-design.md`。
 
----
+***
 
 ## 文件结构
 
-新建项目 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/`(干净的 control 独立启动器,**替代脏壳 ivf_tl_ControlTest,后者阶段3 删**):
+新建项目 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/`(干净的 control 独立启动器,**替代脏壳 ivf\_tl\_ControlTest,后者阶段3 删**):
 
-| 文件 | 职责 |
-|---|---|
-| `ivf_tl_ControlHost.csproj` | WinExe/net6.0-windows,requireAdministrator manifest,引用 `ivf_tl_Control` |
-| `app.manifest` | requireAdministrator(串口/相机/盘符) |
-| `Program.cs` | 进程入口:Mutex 单实例 → 解析参数 → 启动序(Login→盘→ScanDevices→StartRun)→ 起 HttpListener → 阻塞驻留 |
-| `HostArgs.cs` | **纯逻辑**:命令行参数解析(account/password/cacheDisk/port),可单测 |
-| `ControlHttpServer.cs` | HttpListener 封装:监听 127.0.0.1:port,路由 `/ping`、`/status`,JSON 响应 |
-| `StatusDto.cs` | `/status` 返回的 DTO + 从 `MonitorSnapshot` 的映射(阶段1 先映射现有快照字段) |
+| 文件                          | 职责                                                                               |
+| --------------------------- | -------------------------------------------------------------------------------- |
+| `ivf_tl_ControlHost.csproj` | WinExe/net6.0-windows,requireAdministrator manifest,引用 `ivf_tl_Control`          |
+| `app.manifest`              | requireAdministrator(串口/相机/盘符)                                                   |
+| `Program.cs`                | 进程入口:Mutex 单实例 → 解析参数 → 启动序(Login→盘→ScanDevices→StartRun)→ 起 HttpListener → 阻塞驻留 |
+| `HostArgs.cs`               | **纯逻辑**:命令行参数解析(account/password/cacheDisk/port),可单测                             |
+| `ControlHttpServer.cs`      | HttpListener 封装:监听 127.0.0.1:port,路由 `/ping`、`/status`,JSON 响应                   |
+| `StatusDto.cs`              | `/status` 返回的 DTO + 从 `MonitorSnapshot` 的映射(阶段1 先映射现有快照字段)                       |
 
 新建单测项目 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost.Tests/`:
-| 文件 | 职责 |
-|---|---|
+
+| 文件                                | 职责                             |
+| --------------------------------- | ------------------------------ |
 | `ivf_tl_ControlHost.Tests.csproj` | xUnit(仿 IvfTl.AutoFocus.Tests) |
-| `HostArgsTests.cs` | 参数解析单测 |
-| `StatusDtoTests.cs` | status JSON 映射/序列化单测 |
+| `HostArgsTests.cs`                | 参数解析单测                         |
+| `StatusDtoTests.cs`               | status JSON 映射/序列化单测           |
 
 operate 侧修改:
-| 文件 | 改动 |
-|---|---|
-| `ivf_tl_Operate/MainWindow.xaml.cs:67-132` | 删除内嵌 control 那段 `Task.Run`,改为调用新的 `ControlProcessLauncher.EnsureRunning(...)` |
-| `ivf_tl_Operate/Helpers/ControlProcessLauncher.cs`(新建) | 探 `/ping`→不在则 Process.Start 拉起 control.exe(传参)→轮询就绪 |
-| `ivf_tl_Operate/App.config:67` 后 | 加 `controlPort`、`controlExePath` 两个 key |
+
+| 文件                                                     | 改动                                                                            |
+| ------------------------------------------------------ | ----------------------------------------------------------------------------- |
+| `ivf_tl_Operate/MainWindow.xaml.cs:67-132`             | 删除内嵌 control 那段 `Task.Run`,改为调用新的 `ControlProcessLauncher.EnsureRunning(...)` |
+| `ivf_tl_Operate/Helpers/ControlProcessLauncher.cs`(新建) | 探 `/ping`→不在则 Process.Start 拉起 control.exe(传参)→轮询就绪                           |
+| `ivf_tl_Operate/App.config:67` 后                       | 加 `controlPort`、`controlExePath` 两个 key                                       |
 
 解决方案:`control/ivf_tl_Control.sln` 和 `ivf_tl_Operate.sln` 都加入新项目。
 
----
+***
 
 ## Task 1: 新建 control 启动器项目骨架(可编译空壳)
 
 **Files:**
+
 - Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/ivf_tl_ControlHost.csproj`
 - Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/app.manifest`
 - Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/Program.cs`
-
 - [ ] **Step 1: 创建 csproj**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/ivf_tl_ControlHost.csproj`:
+
 ```xml
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
@@ -74,11 +77,13 @@ operate 侧修改:
   </ItemGroup>
 </Project>
 ```
-> UseWPF=true 因为 control 类库链路里仍有 WPF 依赖(MessageBox/Dispatcher 等历史残留),与 ivf_tl_Control 一致避免编译缺引用。
+
+> UseWPF=true 因为 control 类库链路里仍有 WPF 依赖(MessageBox/Dispatcher 等历史残留),与 ivf\_tl\_Control 一致避免编译缺引用。
 
 - [ ] **Step 2: 创建 app.manifest(requireAdministrator)**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/app.manifest`:
+
 ```xml
 <?xml version="1.0" encoding="utf-8"?>
 <assembly manifest xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
@@ -95,6 +100,7 @@ operate 侧修改:
 - [ ] **Step 3: 创建最小 Program.cs(先空跑,验证能编译)**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/Program.cs`:
+
 ```csharp
 using System;
 
@@ -115,19 +121,23 @@ namespace IvfTl.ControlHost
 - [ ] **Step 4: 把项目加入解决方案**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
 "C:/Program Files/dotnet/dotnet.exe" sln ivf_tl_Control.sln add ivf_tl_ControlHost/ivf_tl_ControlHost.csproj
 ```
+
 Expected: `Project ... added to the solution.`(若 dotnet 不在该路径,用 `dotnet` 直接调)
 
 - [ ] **Step 5: 编译验证**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
 dotnet build ivf_tl_ControlHost/ivf_tl_ControlHost.csproj -c Debug 2>&1 | tail -15
 ```
+
 Expected: `Build succeeded` / `0 Error`
 
 - [ ] **Step 6: 提交**
@@ -142,18 +152,19 @@ git commit -m "feat(control): 新建 ivf_tl_ControlHost 独立启动器项目骨
 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
 ```
 
----
+***
 
 ## Task 2: 命令行参数解析(纯逻辑 + 单测)
 
 **Files:**
-- Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/HostArgs.cs`
+
+- Create: `ivf_tl_oper得复核西ate_2.0/control/ivf_tl_ControlHost/HostArgs.cs`
 - Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost.Tests/ivf_tl_ControlHost.Tests.csproj`
 - Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost.Tests/HostArgsTests.cs`
-
 - [ ] **Step 1: 创建单测项目 csproj(仿 IvfTl.AutoFocus.Tests)**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost.Tests/ivf_tl_ControlHost.Tests.csproj`:
+
 ```xml
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
@@ -171,11 +182,13 @@ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
   </ItemGroup>
 </Project>
 ```
+
 > 用 `<Compile Include>` 直接链入 HostArgs.cs 单文件做单测,**不引用整个 WinExe 项目**(net8 测试项目引 net6-windows WinExe 会有 TFM/WPF 冲突)。HostArgs.cs 须是纯 BCL、无 WPF 依赖。
 
 - [ ] **Step 2: 写失败测试**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost.Tests/HostArgsTests.cs`:
+
 ```csharp
 using IvfTl.ControlHost;
 using Xunit;
@@ -222,15 +235,18 @@ namespace IvfTl.ControlHost.Tests
 - [ ] **Step 3: 跑测试确认失败**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
 dotnet test ivf_tl_ControlHost.Tests/ivf_tl_ControlHost.Tests.csproj 2>&1 | tail -15
 ```
+
 Expected: 编译失败(HostArgs 不存在)。
 
 - [ ] **Step 4: 实现 HostArgs(纯 BCL)**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/HostArgs.cs`:
+
 ```csharp
 using System;
 using System.Collections.Generic;
@@ -277,10 +293,12 @@ namespace IvfTl.ControlHost
 - [ ] **Step 5: 跑测试确认通过**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
 dotnet test ivf_tl_ControlHost.Tests/ivf_tl_ControlHost.Tests.csproj 2>&1 | tail -15
 ```
+
 Expected: `Passed! - Failed: 0, Passed: 4`
 
 - [ ] **Step 6: 提交**
@@ -295,34 +313,40 @@ git commit -m "feat(control): HostArgs 命令行参数解析 + 单测
 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
 ```
 
----
+***
 
 ## Task 3: /status DTO 与映射(纯逻辑 + 单测)
 
 **Files:**
+
 - Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/StatusDto.cs`
 - Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost.Tests/StatusDtoTests.cs`
 - Modify: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost.Tests/ivf_tl_ControlHost.Tests.csproj`
 
-> 阶段1 只暴露"现有快照字段 + ok/pid/tlSn"(监控补全留阶段2)。`StatusDto` 不直接依赖 `MonitorSnapshot` 类型(那在 ivf_tl_Control 里、带硬件链),而是提供一个**从原始字段构造**的工厂,保证可纯单测。
+> 阶段1 只暴露"现有快照字段 + ok/pid/tlSn"(监控补全留阶段2)。`StatusDto` 不直接依赖 `MonitorSnapshot` 类型(那在 ivf\_tl\_Control 里、带硬件链),而是提供一个**从原始字段构造**的工厂,保证可纯单测。
 
 - [ ] **Step 1: 改测试 csproj 链入 StatusDto.cs**
 
 在 `ivf_tl_ControlHost.Tests.csproj` 的 `<ItemGroup>` 里 HostArgs 那行后加:
+
 ```xml
     <Compile Include="..\ivf_tl_ControlHost\StatusDto.cs" Link="StatusDto.cs" />
 ```
+
 并加 Newtonsoft 引用(单测要验 JSON):
+
 ```xml
   <ItemGroup>
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
   </ItemGroup>
 ```
+
 > 版本对齐主项目实际用的 Newtonsoft 版本;若主项目用别的版本,改成一致(执行时 `grep -r Newtonsoft.Json control/*/*.csproj` 确认)。
 
 - [ ] **Step 2: 写失败测试**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost.Tests/StatusDtoTests.cs`:
+
 ```csharp
 using IvfTl.ControlHost;
 using Newtonsoft.Json;
@@ -357,15 +381,18 @@ namespace IvfTl.ControlHost.Tests
 - [ ] **Step 3: 跑测试确认失败**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
 dotnet test ivf_tl_ControlHost.Tests/ivf_tl_ControlHost.Tests.csproj --filter StatusDtoTests 2>&1 | tail -10
 ```
+
 Expected: 编译失败(StatusDto 不存在)。
 
 - [ ] **Step 4: 实现 StatusDto(纯 BCL + Newtonsoft 特性)**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/StatusDto.cs`:
+
 ```csharp
 using Newtonsoft.Json;
 
@@ -392,10 +419,12 @@ namespace IvfTl.ControlHost
 - [ ] **Step 5: 跑测试确认通过**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
 dotnet test ivf_tl_ControlHost.Tests/ivf_tl_ControlHost.Tests.csproj 2>&1 | tail -10
 ```
+
 Expected: `Passed! - Failed: 0, Passed: 6`(累计 HostArgs4 + StatusDto2)
 
 - [ ] **Step 6: 提交**
@@ -410,11 +439,12 @@ git commit -m "feat(control): StatusDto + /ping 工厂 + 单测
 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
 ```
 
----
+***
 
 ## Task 4: 内嵌 HttpListener(/ping + /status)
 
 **Files:**
+
 - Create: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/ControlHttpServer.cs`
 
 > HttpListener 依赖网络/管理员,**不做纯单测**(运行期 + Task7 真机验证)。本 Task 只到"编译通过 + 类结构正确"。
@@ -422,6 +452,7 @@ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
 - [ ] **Step 1: 实现 ControlHttpServer**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/ControlHttpServer.cs`:
+
 ```csharp
 using System;
 using System.Net;
@@ -509,10 +540,12 @@ namespace IvfTl.ControlHost
 - [ ] **Step 2: 编译验证**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
 dotnet build ivf_tl_ControlHost/ivf_tl_ControlHost.csproj -c Debug 2>&1 | tail -15
 ```
+
 Expected: `Build succeeded` / `0 Error`
 
 - [ ] **Step 3: 提交**
@@ -527,18 +560,20 @@ git commit -m "feat(control): 内嵌 HttpListener 本地小服务(/ping /status)
 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
 ```
 
----
+***
 
 ## Task 5: Program.cs 完整启动序(Mutex→参数→启动序→HTTP→驻留)
 
 **Files:**
+
 - Modify: `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/Program.cs`
 
-> 复刻 operate `MainWindow.xaml.cs:78-130` 那段启动序(已核实):Login → 设 cacheDisk → HAL.ScanDevices → StartMain.StartRun。**顺序不能变**。无界面、无单测(真机/运行期验证)。
+> 复刻 operate `MainWindow.xaml.cs:67-132` 那段启动序(已核实):Login → 设 cacheDisk → HAL.ScanDevices → StartMain.StartRun。**顺序不能变**。无界面、无单测(真机/运行期验证)。
 
 - [ ] **Step 1: 实现完整 Program.cs**
 
 `ivf_tl_operate_2.0/control/ivf_tl_ControlHost/Program.cs`(整文件替换):
+
 ```csharp
 using System;
 using System.Diagnostics;
@@ -658,16 +693,19 @@ namespace IvfTl.ControlHost
     }
 }
 ```
+
 > 注:`AppData.Instance.TLSetting` 在 StartRun 后才被赋值(StartMain.cs:47);未启动时 try 兜底返回空,符合"started=false 时 tlSn 可空"。
 
 - [ ] **Step 2: 编译验证**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
 dotnet build ivf_tl_ControlHost/ivf_tl_ControlHost.csproj -c Debug 2>&1 | tail -20
 ```
-Expected: `Build succeeded`。若报缺命名空间(如 `ivf_tl_UtilHelper`/`IvfTl.Hardware`),按报错给 csproj 补对应 `ProjectReference`(执行时 `grep ProjectReference control/ivf_tl_Control/ivf_tl_Control.csproj` 对照补齐:ivf_tl_Com/Controller/Entity/ServicesImpl/Services;HAL 在 IvfTl.Hardware,UtilHelper 在 ivf_tl_UtilHelper)。
+
+Expected: `Build succeeded`。若报缺命名空间(如 `ivf_tl_UtilHelper`/`IvfTl.Hardware`),按报错给 csproj 补对应 `ProjectReference`(执行时 `grep ProjectReference control/ivf_tl_Control/ivf_tl_Control.csproj` 对照补齐:ivf\_tl\_Com/Controller/Entity/ServicesImpl/Services;HAL 在 IvfTl.Hardware,UtilHelper 在 ivf\_tl\_UtilHelper)。
 
 - [ ] **Step 3: 提交**
 
@@ -682,28 +720,31 @@ git commit -m "feat(control): Program.cs 完整启动序(Mutex+启动序+HTTP驻
 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
 ```
 
----
+***
 
 ## Task 6: operate 侧改为拉起独立 control
 
 **Files:**
+
 - Create: `ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/ControlProcessLauncher.cs`
 - Modify: `ivf_tl_operate_2.0/ivf_tl_Operate/MainWindow.xaml.cs:67-132`
 - Modify: `ivf_tl_operate_2.0/ivf_tl_Operate/App.config`(加 controlPort/controlExePath)
-
 - [ ] **Step 1: App.config 加两个 key**
 
 在 `ivf_tl_Operate/App.config` 的 `<add key="cacheDisk" value="C" />`(:67)后加:
+
 ```xml
 		<!-- 双进程:control 本地HTTP端口 + control.exe 路径(相对 operate 输出目录) -->
 		<add key="controlPort" value="38080" />
 		<add key="controlExePath" value="control\ivf_tl_ControlHost.exe" />
 ```
+
 > controlExePath 的实际部署布局在阶段3 装机时定;阶段1 先按"operate 输出目录下 control 子目录"占位,运行验证时按真实产出路径调。
 
 - [ ] **Step 2: 实现 ControlProcessLauncher**
 
 `ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/ControlProcessLauncher.cs`:
+
 ```csharp
 using System;
 using System.Configuration;
@@ -781,9 +822,10 @@ namespace ivf_tl_Operate.Helpers
 }
 ```
 
-- [ ] **Step 3: 替换 MainWindow.xaml.cs 内嵌段**
+- [ ] **Step 3: 替换 MainWindow\.xaml.cs 内嵌段**
 
 把 `MainWindow.xaml.cs` 第 67-132 行的整个 `System.Threading.Tasks.Task.Run(() => { ... });` 块(从 `// M1-01 步骤3` 注释起到该 Task.Run 闭合)替换为:
+
 ```csharp
             // 双进程改造:control 已剥离为独立进程 ivf_tl_ControlHost.exe。
             // operate 登录后只负责"确保 control 在跑"(探活→不在则拉起→轮询就绪),
@@ -811,16 +853,19 @@ namespace ivf_tl_Operate.Helpers
                 }
             });
 ```
+
 > 删掉的旧逻辑(进程内 Login/ScanDevices/StartRun)已迁入 ControlHost.Program。`using ivf_tl_Control;` 等若变成未使用,可留(不影响编译)或清理。
-> ⚠ 注意:本段在 operate 项目里,`Log4netHelper` 的命名空间就是 `ivf_tl_Services`(operate 端),**保持 `ivf_tl_Services.Log4netHelper` 正确,勿改成 control 端的 `IvfTl.Control.Services`**。与 Step 1 的 ControlHost.Program 是两个不同程序集的同名类——别混。
+> ⚠ 注意:本段在 operate 项目里,`Log4netHelper` 的命名空间就是 `ivf_tl_Services`(operate 端),**保持** **`ivf_tl_Services.Log4netHelper`** **正确,勿改成 control 端的** **`IvfTl.Control.Services`**。与 Step 1 的 ControlHost.Program 是两个不同程序集的同名类——别混。
 
 - [ ] **Step 4: 编译 operate**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
 dotnet build ivf_tl_Operate/ivf_tl_Operate.csproj -c Debug 2>&1 | tail -20
 ```
+
 Expected: `Build succeeded` / `0 Error`(若 operate.exe 正运行锁 DLL 报 MSB3021,先关 operate 再编,见 CLAUDE.md 第九节 Claude 自己执行)。
 
 - [ ] **Step 5: 提交**
@@ -836,26 +881,36 @@ App.config 加 controlPort/controlExePath。
 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
 ```
 
----
+***
 
-## Task 7: 端到端运行验证 [真机]
+## Task 7: 端到端运行验证 \[真机]
 
 > 须用户在场(管理员提权、连真机硬件)。绝不无人值守驱动电机。
 
 - [ ] **Step 1: 把 control 输出部署到 operate 输出目录的 control 子目录**
 
-Run(执行时按实际产出路径调整;control 依赖 DependFile/原生 DLL,需连同 ivf_tl_Control 的运行时依赖一起):
+Run(执行时按实际产出路径调整;control 依赖 DependFile/原生 DLL,需连同 ivf\_tl\_Control 的运行时依赖一起):
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
 dotnet build control/ivf_tl_ControlHost/ivf_tl_ControlHost.csproj -c Debug 2>&1 | tail -5
 # 将 control/ivf_tl_ControlHost/bin/Debug/net6.0-windows/ 整目录拷到
 #   ivf_tl_Operate/bin/Debug/net6.0-windows/control/ 下(含所有依赖 DLL + DependFile)
 ```
-> ⚠ control 需要 `DependFile`(SQLite/相机 DLL/calibration 路径)与 App.config(连接/换气参数)。部署时确保这些随 control.exe 到位——这是阶段1 运行验证的关键前置,缺则 StartRun 会失败。具体依赖清单见全景文档 §八。
+
+> ⚠ **运行前置(源码实锤,缺一项 StartRun 即失败)——阶段1 真机最易栽这三处:**
+>
+> 1. **ControlHost 必须自带 App.config(否则 AppData 构造 NPE,比 DependFile 更靠前的坑)。** 独立进程下入口程序集 = `ivf_tl_ControlHost.exe`,`ivf_tl_Control.AppData` 构造用 `.ToString()` 读 `ConfigurationManager.AppSettings`,**换气/CCD 类键(gbTime/csTime/VentNum/VentPre/VentWaitTimeB/VentWaitTimeD/AutoWaitTime/CCDAutoWaitTime/CCDError/CCDFailedNumber/CCDFailedWaitTime/QueuAir 等)缺键直接 NPE**(仅 url/kfka/mqtt 三组有 null 容错,见全景§8.1)。单进程合并时它读的是 operate 的 App.config,**拆独立进程后必须给 ControlHost 工程加一份 App.config**,内容 = operate `ivf_tl_Operate/App.config` 里那批 control 业务键 + 连接键(urlIp/urlPort/mqttIp/mqttPort/kfkaIP/kfkaPort)。部署时与 operate 那份保持同步。
+> 2. **DependFile 原生依赖(由 `ivf_tl_Control.csproj` 经 `CopyToOutputDirectory=Always` 拷贝,因 ControlHost 引用 ivf_tl_Control 而传递到 ControlHost 输出目录;务必核对真落地):**
+>    - `DependFile/ccd/`:`CellCultureDllMulti64.dll`、`CellCultureDllMulti64.lib`、`CellProcessorDll.dll`、`MVBayerDec.dll`、`MVCAPI.dll`、`MVParm.dll`、`opencv_world342.dll`
+>    - `DependFile/DB/aivfoTL.db`
+>    - `DependFile/newccd/`:`opencv_world3416.dll`、`Project2.dll`
+> 3. **calibration.json 不必预置**:它是对焦运行期产出(全景§7.3),`house_autofocus_calibration` 表由 DBService `CodeFirst.InitTables` 自动建,勿当缺文件 bug 排查。
 
 - [ ] **Step 2: 起本机微服务集群(control 登录 gateway 需要)**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0"
 bash 项目文档/开发环境/start-all.sh
@@ -865,54 +920,66 @@ bash 项目文档/开发环境/start-all.sh
 - [ ] **Step 2.5: 确认无残留 operate/control 进程**
 
 Run:
+
 ```bash
 powershell -NoProfile -Command 'Get-Process ivf_tl_Operate,ivf_tl_ControlHost -ErrorAction SilentlyContinue | Select-Object Id,ProcessName | Format-Table -AutoSize'
 ```
+
 Expected: 无残留(有则按 CLAUDE.md 第九节 Claude 自行 RunAs 清理)。
 
 - [ ] **Step 3: 启动 operate,登录,观察 control 被拉起**
 
 由 Claude 用 RunAs 静默启动 operate(Debug 含 207 外网坑→**必须用 Release**,见全景 §8.1;阶段1 验证若用 Debug 需临时确认连本机)。用户登录 admin/123456。
 验证点:
+
 - `ivf_tl_ControlHost.exe` 进程出现(`Get-Process ivf_tl_ControlHost`)。
 - 浏览器/curl 访问 `http://127.0.0.1:38080/status` 返回 JSON,`started` 最终变 true。
 - control 日志(`C:/TLData/ivf_tl_Control_logs`)出现"control 启动成功,常驻运行"。
-- **[真机]** 采集起来:control 日志有 7 舱握手、相机信息、COM 口 True(参考全景 §二启动时序)。
+- **\[真机]** 采集起来:control 日志有 7 舱握手、相机信息、COM 口 True(参考全景 §二启动时序)。
 
 Run(验证 /status):
+
 ```bash
 powershell -NoProfile -Command '(Invoke-WebRequest -Uri http://127.0.0.1:38080/status -UseBasicParsing).Content'
 ```
+
 Expected: `{"ok":true,"pid":...,"tlSn":"NEO-1-...","started":true}`
 
 - [ ] **Step 4: 验证 operate 关闭后 control 续命(核心目标)**
 
 关闭 operate UI(Claude RunAs 结束 operate 进程),然后:
+
 ```bash
 powershell -NoProfile -Command 'Get-Process ivf_tl_ControlHost -ErrorAction SilentlyContinue | Select-Object Id; (Invoke-WebRequest -Uri http://127.0.0.1:38080/status -UseBasicParsing).Content'
 ```
+
 Expected: control 进程**仍在**、`/status` 仍返回 `started:true` —— **证明 operate 关了 control 续命**。
 
 - [ ] **Step 5: 验证重开 operate 复用已有 control(不重复拉起)**
 
 重新启动 operate 登录。验证点:control 进程 **PID 不变**(没起第二个);operate 日志出现"control 已在运行,直接连接"。
+
 ```bash
 powershell -NoProfile -Command 'Get-Process ivf_tl_ControlHost | Select-Object Id,StartTime'
 ```
+
 Expected: 同一个 PID/StartTime(未重启)。
 
 - [ ] **Step 6: 验证单实例 Mutex(手动起第二个 control 应自退)**
 
 Run:
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/ivf_tl_Operate/bin/Debug/net6.0-windows/control"
 powershell -NoProfile -Command 'Start-Process ivf_tl_ControlHost.exe -ArgumentList "--account=admin","--password=123456" -Verb RunAs; Start-Sleep 3; (Get-Process ivf_tl_ControlHost | Measure-Object).Count'
 ```
+
 Expected: 进程数仍为 **1**(第二个发现 Mutex 已占→自退,日志"已有实例在运行,本进程退出")。
 
 - [ ] **Step 7: 回写文档 + 提交**
 
 更新:`进度状态.yaml`(断点)、`交接卡.md`(追加阶段1完成)、`工作计划表.md`(双进程阶段1 状态)、`进度数据.js`。
+
 ```bash
 cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0"
 codegraph sync
@@ -924,12 +991,13 @@ control 独立进程:能起/能连/operate关了续命/重开复用/单实例;
 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
 ```
 
----
+***
 
 ## 自检结果
 
 **Spec 覆盖(对照设计 §3.2/§5.1/§5.2/§5.3 + §8 阶段1):**
-- control 无界面常驻、引用 ivf_tl_Control 类库 → Task1/5 ✅
+
+- control 无界面常驻、引用 ivf\_tl\_Control 类库 → Task1/5 ✅
 - 账号命令行传入 → Task2(解析)+ Task6(传)✅
 - Mutex 单实例 → Task5 + Task7-S6 ✅
 - 本地 HTTP /ping+/status → Task3/4 ✅

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

@@ -75,3 +75,23 @@
   - §1(改造前 vs 改造后 之后)加 warn callout:**本次是接一个"半成品"**——上个三项目合并任务代码完成、但真机验收整体未做、有 operate 侧降级遗留(指向 §13 / 待验证清单 M-01~M-07)。
 - **核实**:三项目合并范围(operate+control+AutoFocusTool→operate、front 独立)此前已由原始↔合并比对坐实(交接卡 2026-06-22 复核段);AutoFocusTool 算法逐字保留、重构进 IvfTl.AutoFocus/IvfTl.Hardware 属实。
 - **下一步**:无强制后续。
+
+---
+
+## 2026-06-22 · 进度目录+阶段1计划复核 + 文档缺口补登 + 计划真机前置补正
+
+- **背景**:用户两轮要求——(A)以源码+108库复核全部项目文档(排进度目录);(B)再专项复核进度目录是否最新/正确、阶段1计划是否合理。**只认源码+数据库,不看 git**。
+- **核实(源码/108 实测坐实)**:
+  1. **进度目录最新且基本正确**:阶段1"未开工"属实(control/ 下无 ivf_tl_ControlHost);待验证清单 M-01~M-07 的 file:line 全部源码坐实;yaml/工作计划表/进度数据.js 三件套自洽。
+  2. **108 库实测**:7 库齐;`aivfo_tl_setting`=**18**、`log`=**2**(非服务器测试环境.md 旧写的 17/1——两条 migration 已补执行);`operation_log` 19 列与 CLAUDE.md 一致。
+  3. **阶段1 计划合理可执行**:对源码所有关键假设(类/方法/签名/返回值/命名空间/引用链/复刻段 67-132)实测全成立,无引用不存在类型的硬冲突。
+- **更正(本卡追加更正,前段不回改)**:
+  - ❗**本卡 2026-06-22「全量核对」段所记「§四 `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",已升级为明确前置。
+- **下一步**:阶段1 开工(建 feature 分支 → 子代理跑 Task1-6 → Task7 真机);开工时务必先给 ControlHost 工程加 App.config。

+ 6 - 4
项目文档/需求文档/control-逻辑与配置全景.md

@@ -23,6 +23,7 @@
 
 | 线程 | 来源 | 职责 | 节奏 |
 |---|---|---|---|
+| 每舱 **采集启动器** ×10 | `HouseBin.StartTask` (HouseBin.cs:497) | 采集端真正入口:Task.Run 内做握手/电机自检,**末尾依次派生** MainThread/RunAlarmTask/RunHistoryTask | 启动一次 |
 | 每舱 **采集主循环** ×10 | `HouseBin.MainThread` (HouseBin.cs:614) | 温压/补气/排气/换气/拍照/对焦时序 | while(true),靠串口往返自然节流 |
 | 每舱 运行监测 ×10 | `HouseBin.RunAlarmTask` (:798) | 10 分钟没心跳→报卡死 | 每 5 分钟 |
 | 每舱 历史上报 ×10 | `HouseBin.RunHistoryTask` (:833) | 推温压/门/换气/培养态 | 换气中 1s / 平时 `historyCurveInterval` 秒 |
@@ -47,7 +48,7 @@
    - `SerialBin.Start()` 扫 COM 口(跳过 COM1/2)握手 → 每舱 houseSn、读 EEPROM、按 CCDSN 配对相机 index(失败即中止)。**TLNum 从 11 号缓冲瓶模块读出**,组 `tlSn = NEO-1-{TLNum}`。
    - 期望 **11 个模块**,不符看 `ContinueOnModuleCountMismatch`(默认 true 继续)。
    - `UpdateTLInfoController(tLInitData)` 向服务器拉配置(**重试 3 次,全失败回退本地 SQLite `DbGetTLInfo`**——断网也能开机)。
-3. **`InitHouse`**(:51 / 详 :160)——建 1 个 `BufferBottleBin`(houseSn=11)+ 10 个 `HouseBin`,按 `runHouses`(握手成功的舱)逐舱 `StartTask()` 起采集。⚠ 第 5 舱和缓冲瓶在 `!MvcTest` 之外**无条件启动**(不对称点)。
+3. **`InitHouse`**(:51 / 详 :160)——建 1 个 `BufferBottleBin`(houseSn=11)+ 10 个 `HouseBin`,按 `runHouses`(握手成功的舱)逐舱 `StartTask()` 起采集。⚠ 第 5 舱(:248)和缓冲瓶(:250)被放在 `if(!MvcTest)` 块**之外**——即**不受 `MvcTest` 测试门控**,但仍各自受 `runHouses.Contains(5/11)` 守卫(并非真"无条件",是不对称点)。
 4. **`AppData.StartAsync().Wait()`**(:52 / 详 AppData.cs:332)——建 Kafka 3 分区(失败仅记日志不中止)→ StartMqtt → 起状态上报/历史/心跳线程 → 删旧日志。
 
 ---
@@ -66,7 +67,7 @@
 按各秒表自主触发:
 - **换气**:`AirSwapTimeStopwatch >= airSwapFrequency*60s` → `AirSwapFun()`(:696)。关门首次也触发(`StartAir`)。
 - **拍照**:`BmpSaveIntervalStopwatch >= photoIntervalPerRound*60s` → `StartCCD()`+`ccdThreadFun()`(:704)。
-- **对焦**(`FirstClearest==true` 时):等气稳(`isAir`)→ `StartAutoFocus()` → `GetClearest()` → 成功后换气+拍照(:712-748)。
+- **对焦**(`FirstClearest==true` 时):等气稳(`isAir`)→ `StartAutoFocus()`(:1443)→ `GetClearest()` → 成功后换气+拍照(:712-748)。⚠ `StartAutoFocus` 内含**本地对焦安全门降级**:`localAutofocusEnabled`(:1462)默认 0=降级(按 scene=0 基准/`eepromClearPosition` 拍,不实时对焦),=1 才走本地 `CalibrationEngine` 实时对焦。
 
 ### 3.3 平衡模式(:753-783)
 **只有换气,无拍照/对焦**。`FirstAir` 首次立即换、之后按周期换。排气走 `VentWaitTimeB`。
@@ -75,7 +76,7 @@
 水平电机复位 → 开 LED → **逐 well**(移水平电机到 `horizontalMotorPosition`)→ **逐层**(移垂直电机到该层 Z)→ `Photograph` 抓图+处理+上传 → 关 LED 归位。门开/欠压/抓图失败有打断与插补。抓图失败重试 `CCDFailedNumber` 次。
 
 ### 3.5 换气/补气动作内部
-- **换气 `AirSwapOldFun`**(:991,当前唯一启用):循环 `airSwapTime` 次,每次"开排气阀→持续 `ventilationDelay` 秒放气→关排气阀→欠压则补气",即**反复排空+回充稀释置换**成预混气。(新版 `AirSwapNewFun`/排队 `AirSwapQueueFun` 见 §四)
+- **换气 `AirSwapOldFun`**(:991,当前唯一启用):循环 `airSwapTime` 次,每次"开排气阀→持续 `ventilationDelay` 秒放气→关排气阀→欠压则补气",即**反复排空+回充稀释置换**成预混气。(新版 `AirSwapNewFun`(:1039)当前被 `AirSwapFun`(:975)注释屏蔽=**死代码分支**、排队 `AirSwapQueueFun` 见 §四)
 - **补气 `AerationNew`**(:1307):循环 `houseAerationNum` 次发补气命令,每次 `Sleep(aerationDelay)` 读压力,达 `pressureAlarmMin` 停。
 - **排气 `PaiQi`**(:1341):循环 `VentNum` 次,降到 `VentPre` 停。
 
@@ -87,7 +88,7 @@
 | `FirstClearest` (:277) | 本轮是否先对焦;`=house.autoFocus` 初始;关门/StartDish/HouseAutoFocus 置 true |
 | `IsStopClearest/CCD/AirSwap` (:287) | **开门**时全置 true,立即打断进行中的对焦/拍照/换气 |
 | `CCDState` (:369) | 拍照异常态,连续抓图失败 `CCDFailedNumber` 次置 1 报警+冷却 |
-| `CapturePausedByGate` / `IsDebug` | HAL 借用让路 / MQTT 调试让路(主循环顶部守卫) |
+| `CapturePausedByGate` (:319) / `IsDebug` | HAL 借用让路(订阅 `OnPauseCapture`,:456/481/488 收口)/ MQTT 调试让路(主循环顶部守卫 :632) |
 | `localAutofocusEnabled` (TLSetting) | 本地对焦安全门,**默认 0=降级**(按 scene=0 基准/eepromClearPosition 拍,不实时对焦) |
 
 ---
@@ -169,6 +170,7 @@
 
 ### 7.3 对焦标定双写
 真相源 = `calibration.json`(运行期产出);DB `house_autofocus_calibration` 是镜像。经 `CalibrationStore`(委托回调注入,避免 AutoFocus→control 反向依赖),两步各自容错、绝不崩对焦线程。
+> 来源:对焦库 `control/IvfTl.AutoFocus`(`CalibrationEngine`/`Sharpness`/`WellDetector` 等)**源自合并前的独立项目 `AutoFocusTool`**(三项目合并的第三个来源,见 `项目背景与上手指南.md §一`),合并时重构为 control 的对焦类库。配套单测工程 `control/IvfTl.AutoFocus.Tests`(xUnit,目标框架 net8.0)对焦算法纯逻辑测试——**未挂入 `ivf_tl_Control.sln` 主解决方案(游离工程)**,需单独 `dotnet test control/IvfTl.AutoFocus.Tests` 跑。
 
 ### 7.4 数据归属
 - **中央下发、本地缓存兜底**:配置(TLSetting/House/Well)、培养数据(Dish/Embryo/Balance)、电机位置。

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

@@ -189,6 +189,7 @@
 - **`ReadWellFocusZeroWait` 语义差异**(:94-99):control 端 Z 零点为整舱单值,`well` 入参被忽略,与 autofocus 按 well 读零点有差异(M2 待验证)。
 - **`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 包装。
 
 ### 8.3 合并新增能力(非降级,记录在案)
 - `Helpers/` 整目录新增:`AppConfigHelper`(配置容错)、`CryptoHelper`(DPAPI 加密)、`ClickTrailLogger`(全局点击层日志)。
@@ -225,6 +226,7 @@
 4. ⚠ **多处 `#if DEBUG` 本地绝对路径**(AppData.cs:243-247、App.xaml.cs:243-246):Logo/语言文件指向 `C:\PersonalSpace\work\...`,仅开发机有效。
 5. ⚠ **`urlIp=127.0.0.1`/`mqttIp=192.168.0.108`**(App.config,合并改值):需现场确认实际网关 IP(注释已标 `[M7] 现场核对`)。
 6. ⚠ **control 托管线程静默失败**:登录/StartRun/HAL 失败全部只记日志不退进程(MainWindow.xaml.cs:84/91/118/123/130)——control 后台没起来时 operate 前台仍可用,易被忽略(无显式告警)。
+7. ⚠ **`OperateHwTest` = 测试代码,非交付件**(`ivf_tl_operate_2.0/OperateHwTest/Program.cs`,与 `ivf_tl_Operate` 平级的控制台项目):`internal class Program`,真机硬件闭环测试 CLI,子命令 `scan/motor/http/camera`,复用 operate 真实 `ivf_tl_Entity.ComEntitys.ComBin` 与 `HttpHelper` 驱动真机;硬编码 admin/123456 与 7 舱→COM 口映射、电机脉冲安全钳位。**属测试/诊断工具**(性质同 control 侧 `ivf_tl_ControlTest` 老壳),交付打包不含、上线前确认随测试资料归档或排除,勿当产品功能。
 
 ---
 

+ 1 - 1
项目文档/项目背景与上手指南.md

@@ -18,7 +18,7 @@
 | **front** | `aivfo-front-manament-2.0/` | **管理端/医生工作站**:患者管理、胚胎打分、出报告 | 医生办公电脑 |
 | Java 微服务 | `aivfo-gateway/`、`aivof-tl-control/`、`aivfo-business-manage/`、`aivfo-data-transmission/`、`aivfo-oplog/` 等 | 网关/业务/数据传输/操作日志 | 服务器(本机开发时跑在 108 + 本机) |
 
-> 历史:operate / control / front 原本是三个独立项目,做过一次"三项目合并"(M0–M8)。合并把 control 的代码塞进了 operate 进程内托管运行。**合并代码完成,但真机验收整体未做、且有 operate 侧功能降级遗留**(见 `进度/待验证清单.md` M-01~M-07 与 `需求文档/操作端逻辑与配置全景.md` §八);旧文档在重建时清空归档。
+> 历史:做过一次"三项目合并"(M0–M8)。**被合并进 `ivf_tl_operate_2.0` 解决方案的三个原始项目是:operate(→`ivf_tl_Operate`)+ control(→`control/` 子树)+ AutoFocusTool 自动对焦工具(→`control/IvfTl.AutoFocus` 对焦库)**;合并把 control 的代码塞进了 operate 进程内托管运行,把 AutoFocusTool 重构成 control 的对焦类库。**注意:front 不在本次合并内**——它始终是独立的第四个桌面程序(`aivfo-front-manament-2.0/`),只是同属一个产品。**合并代码完成,但真机验收整体未做、且有 operate 侧功能降级遗留**(见 `进度/待验证清单.md` M-01~M-07 与 `需求文档/操作端逻辑与配置全景.md` §八);旧文档在重建时清空归档。
 
 ---