|
|
@@ -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 ✅
|