SSPC-Tester/Docs/开发文档.md
2026-06-30 12:10:29 +08:00

577 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 固态功率控制器动态参数测试系统 — 开发文档
> 版本 v1.1 技术栈:**C# + WPF + .NET 8** 口径:**以当前仓库代码为准**
>
> 本文档描述当前仓库的真实结构、配置方式、核心流程和维护约定。
> 若文档与代码冲突,以代码实现为准,并应同步回写本文档。
---
## 0. 开发约束
对本项目进行新增、修改、排障时,默认遵守以下原则:
1. **分层隔离优先**UI 只依赖 ViewModel / Service / Logic不直接依赖具体设备驱动。
2. **接口先行**SSPC、电源、负载、DAQ 一律经 `SSPCTester.Devices.Interfaces` 暴露,真假驱动由配置决定。
3. **按设备粒度切换 Mock/Real**:当前不再是“一个全局 `MockMode` 管全部设备”,而是每类设备都有自己的 `UseMock`
4. **配置分层加载**:程序先读内置默认值,再读 `appsettings.json`,最后读 `%LOCALAPPDATA%\SSPCTester\settings.json`
5. **测试态独占**:进入测试后只展示当前项目,禁止切换其他项目,设备连接由测试态独占。
6. **文档随代码更新**:涉及接口、项目结构、运行方式、持久化格式变化时,要同步更新本文档。
---
## 1. 项目背景
### 1.1 被测对象
固态功率控制器SSPC用于智能配电场景。本项目按 **24 路** 输出通道建模,通过软件控制每路导通/关断,并测量:
- 基础通断状态
- 开启/关断曲线与时间参数
- 功率损耗与效率
- 保护功能演示结果
### 1.2 当前测试台硬件
| 部件 | 型号 | 作用 | 当前代码状态 |
|---|---|---|---|
| 工控机 | Windows x64 | 运行 WPF 主程序 | 已支持 |
| 被测件 SSPC | 24 路 | 控制 24 路导通/关断 | 已有 `SspcModbus` |
| 程控电源 | UDP5080-100 | 输入端电压/电流读取 | 已有真实驱动,控制命令保留接口 |
| 可编程负载 | IT8733P / IT8702P | 输出端电压/电流/功率读取 | 已有真实驱动 |
| 高速采集卡 | PCIe8586 | 曲线波形采集 | 已有 `Pcie8586Daq` |
| 示波器 | Tektronix MDO3024 | 线下校准基准 | 不纳入软件控制 |
### 1.3 当前实现事实
- SSPC 24 路通过 **3 个 Modbus 模块 × 每模块 8 路** 映射。
- 采集卡实现已经统一为 **PCIe8586**,不再使用旧文档中的 `PCIe9318` 命名。
- 真假设备切换已经演进为 **每类设备独立 `UseMock`**
- 主程序启动后会按配置自动连接 SSPC、电源、负载、DAQ。
- 根目录脚本 `run.ps1` / `publish.ps1` 已是当前标准运行入口。
---
## 2. 当前功能范围
主流程如下:
```text
新建/打开项目
-> 填写项目信息 / 勾选测试项
-> 进入测试态
-> 执行基础测试 / 曲线测试 / 功率损耗 / 保护功能
-> 生成 PDF / Word 报告
```
### 2.1 四大模块
| 模块 | 当前实现 | 状态 |
|---|---|---|
| 基础测试 | 24 路逐路开/关、读电压电流、判定稳态关断 | 已实现 |
| 开启与关断曲线 | 支持开通/关断波形采集、指标计算、独立曲线视窗、波形落盘 | 已实现 |
| 功率损耗 | 使用 UDP5080 读输入端IT87xx 读输出端功率/电压/电流 | 已实现 |
| 保护功能 | UI、数据结构和演示结果生成已完成真实联动未接 | 框架完成 |
### 2.2 指标口径
当前计算与判定主要围绕以下指标:
- `1.1` 开启时间:到达输入电压 90% 的时间
- `1.2` 关闭时间:下降到输入电压 10% 的时间
- `1.5` 上升/下降时间10%~90% 或 90%~10% 时间
- `1.9` 电压降:`Vin - Vout`
- `1.10 / 1.13` 功率损耗、效率
- `1.14+` 保护功能相关故障时间与演示状态
阈值定义和最终判断以 `Project.CurveThresholds` 与各页结果模型为准。
---
## 3. 技术栈
| 用途 | 当前选型 | 说明 |
|---|---|---|
| UI | WPF + .NET 8 | `net8.0-windows`x64 |
| MVVM | `CommunityToolkit.Mvvm` | `ObservableObject` / `RelayCommand` |
| 配置 | `Microsoft.Extensions.Configuration` | JSON + 用户设置覆盖 |
| DI / Host | `Microsoft.Extensions.Hosting` | 启动时装配所有服务 |
| Modbus | `NModbus` | SSPC 串口控制 |
| 串口 | `System.IO.Ports` | SSPC/部分设备底层通信 |
| 波形绘制 | `ScottPlot` / `ScottPlot.WPF` | 曲线页与报告插图 |
| 报告 | `QuestPDF` + `DocumentFormat.OpenXml` | PDF 与 Word 双实现 |
| Web 预览 | `Microsoft.Web.WebView2` | 报告页 PDF 预览 |
| 日志 | Serilog | Debug + 文件输出 |
---
## 4. 解决方案结构
### 4.1 分层
```text
UI (WPF / XAML / ViewModel)
-> Logic (测试流程 / 计算 / 报告 / 存储)
-> Devices.Interfaces (统一设备接口)
-> Devices.Drivers.Mock / Real (具体驱动)
```
依赖方向始终是 **上层只依赖下层抽象**
例如 UI 只拿到 `ISspc`,不直接感知背后是 `MockSspc` 还是 `SspcModbus`
### 4.2 当前解决方案
```text
SSPCTester.sln
├── SSPCTester.UI/ # WPF 主程序(启动项)
│ ├── Views/ # 页面与窗口
│ ├── ViewModels/ # MVVM 视图模型
│ ├── Controls/ # 自定义控件(含曲线控件)
│ ├── Resources/ # Tokens / Controls / Icons
│ ├── Converters/ # 状态与颜色转换
│ └── Services/ # 用户设置、应用配置等
├── SSPCTester.Logic/ # 业务层
│ ├── Testing/ # 测试模块与运行器
│ ├── Calculation/ # 曲线/功率计算纯函数
│ ├── Report/ # PDF/Word 报告实现
│ ├── Storage/ # 项目与波形持久化
│ └── Models/ # 项目、结果、阈值、枚举
├── SSPCTester.Devices/ # 设备抽象与驱动
│ ├── Interfaces/ # ISspc / IPowerSupply / ILoad / IDaq
│ ├── Drivers/Mock/ # Mock 驱动
│ ├── Drivers/Real/ # 真设备驱动
│ ├── Drivers/ConfigurableDevices.cs # 按设备切换 Mock/Real
│ ├── Protocol/ # 协议辅助
│ └── Config/ # 设备配置模型
├── SSPCTester.Tests/ # xUnit 测试
├── SSPCTester.Seed/ # 示例项目/种子数据生成
└── Docs/PCIe8586驱动接入资料/src/Pcie8586.Driver/
# 采集卡独立驱动类库,被 Devices/Tests 引用
```
### 4.3 关键脚本
- `run.ps1`:发布 UI、可选执行单测、可选灌入种子数据、最后启动发布产物
- `publish.ps1`:单独发布 WPF 程序
- `SSPCTester.Seed/WriteQualifiedProjects.ps1`:生成演示项目
---
## 5. 配置与驱动切换
### 5.1 配置加载顺序
程序启动时配置来源按优先级叠加:
1. 内置默认配置
2. `SSPCTester.UI/appsettings.json`
3. `%LOCALAPPDATA%\SSPCTester\settings.json`
用户在设置页修改的路径、报告格式等,最终写到用户目录,不回写仓库里的 `appsettings.json`
### 5.2 当前切换策略
当前采用 **每类设备独立切换**
- `Devices:Sspc:UseMock`
- `Devices:PowerSupply:UseMock`
- `Devices:Load:UseMock`
- `Devices:Daq:UseMock`
`Devices:MockMode` 仅保留兼容旧配置用途,不应再作为新逻辑入口。
### 5.3 当前 `appsettings.json` 示例
```json
{
"Devices": {
"MockMode": false,
"Sspc": {
"UseMock": false,
"AutoConnect": true,
"PortName": "COM1",
"BaudRate": 9600,
"ModuleAddresses": [1, 2, 3]
},
"PowerSupply": {
"UseMock": false,
"AutoConnect": true,
"Host": "192.168.6.5",
"Port": 5025
},
"Load": {
"UseMock": false,
"AutoConnect": true,
"Host": "192.168.200.100",
"Port": 30000
},
"Daq": {
"UseMock": false,
"AutoConnect": true,
"LogicalDeviceId": 0,
"ChannelCount": 4,
"PreTriggerMs": 10,
"PostTriggerMs": 40
}
},
"Storage": {
"ProjectsRoot": "%LOCALAPPDATA%\\SSPCTester\\Projects"
},
"Report": {
"SavePath": "%LOCALAPPDATA%\\SSPCTester\\Reports",
"Format": "Pdf"
}
}
```
---
## 6. 设备接口
所有设备接口均为异步接口,默认支持 `CancellationToken`
### 6.1 公共连接接口
```csharp
public interface IDeviceConnection
{
bool IsConnected { get; }
string DisplayName { get; }
Task<bool> ConnectAsync(CancellationToken ct = default);
Task DisconnectAsync();
event EventHandler<bool>? ConnectionChanged;
}
```
### 6.2 SSPC
```csharp
public interface ISspc : IDeviceConnection
{
int ChannelCount { get; }
Task TurnOnAsync(int physicalSlot, CancellationToken ct = default);
Task TurnOffAsync(int physicalSlot, CancellationToken ct = default);
Task FlashAsync(int physicalSlot, int milliseconds, CancellationToken ct = default);
Task<bool> GetChannelStateAsync(int physicalSlot, CancellationToken ct = default);
Task<ChannelMeasurement> ReadMeasurementAsync(int physicalSlot, CancellationToken ct = default);
Task<IReadOnlyList<ChannelMeasurement>> ReadMeasurementsAsync(CancellationToken ct = default);
}
```
### 6.3 电源
```csharp
public interface IPowerSupply : IDeviceConnection
{
Task SetVoltageAsync(double volts, CancellationToken ct = default);
Task SetCurrentLimitAsync(double amps, CancellationToken ct = default);
Task OutputAsync(bool on, CancellationToken ct = default);
Task<(double volts, double amps)> ReadAsync(CancellationToken ct = default);
Task<string> QueryIdentityAsync(CancellationToken ct = default);
}
```
### 6.4 负载
```csharp
public readonly record struct PowerReading(double Volts, double Amps, double Watts);
public interface ILoad : IDeviceConnection
{
Task SetModeAsync(LoadMode mode, CancellationToken ct = default);
Task SetValueAsync(double value, CancellationToken ct = default);
Task InputAsync(bool on, CancellationToken ct = default);
Task<(double volts, double amps)> ReadAsync(CancellationToken ct = default);
Task<PowerReading> ReadPowerAsync(CancellationToken ct = default);
Task<string> QueryIdentityAsync(CancellationToken ct = default);
}
```
### 6.5 DAQ
```csharp
public interface IDaq : IDeviceConnection
{
Task<IReadOnlyList<DaqDeviceDescriptor>> EnumerateDevicesAsync(CancellationToken ct = default);
Task<CurveData> CaptureAsync(int[] channels, int sampleRateHz, double durationSec, CancellationToken ct = default);
Task<double[]> ReadInstantAsync(int[] channels, CancellationToken ct = default);
Task<CurveData> CaptureAroundActionAsync(
DaqActionCaptureRequest request,
Func<CancellationToken, Task> action,
CancellationToken ct = default);
event EventHandler<CurveSample>? SampleReceived;
}
```
`CaptureAroundActionAsync()` 是当前曲线测试的核心接口:
先进入预采集,再在触发点附近执行开关动作,以得到统一对齐的波形。
---
## 7. 核心业务逻辑
### 7.1 测试模块统一入口
`Logic/Testing` 中已经落地:
```csharp
public interface ITestModule
{
string Name { get; }
Task RunAsync(TestContext ctx, IProgress<TestProgress> progress, CancellationToken ct);
}
```
`TestContext` 当前持有:
- `Project`
- `ISspc`
- `IPowerSupply`
- `ILoad`
- `IDaq`
`TestRunner` 依据 `Project.Selection` 串行执行模块。
### 7.2 浏览态 / 测试态
当前程序已实现两种应用状态:
- **浏览态**:左侧树显示全部项目,可自由切换
- **测试态**
- 只显示当前项目
- 顶部展示“测试进行中”状态条
- 可中止测试
- 设备连接视为当前项目独占
这一逻辑由 `MainViewModel.AppState` 控制。
### 7.3 基础测试
当前基础测试实现采用 `ISspc.ReadMeasurementAsync()` 直接读取每路电压/电流:
```text
TurnOnAsync(ch)
-> Delay
-> ReadMeasurementAsync(ch)
-> 记录 VoltageOn / CurrentOn
-> TurnOffAsync(ch)
-> WaitForStableOffAsync(ch)
-> 根据导通和关断稳态结果判定 Pass / Fail
```
说明:
- 不是旧文档中的 `Daq.ReadInstant()` 方案
- “关断是否稳定”通过 `BasicTestCriteria.WaitForStableOffAsync()` 判定
### 7.4 曲线测试
当前代码存在两套相关逻辑:
1. **Logic 模块批处理**`CurveTest.cs`
- 一次完成开通与关断两段采集
- 适用于测试模块整体执行
2. **UI 曲线页交互**`CurveTestViewModel`
- 已拆成两个独立按钮:
- `开启曲线测试`
- `关断曲线测试`
- 开/关曲线各自保存、各自绘图、各自维护视窗范围
当前页与弹窗的“起点 / 终点 / 默认”已经拆成两套独立状态:
- `RisingViewStartMs` / `RisingViewEndMs`
- `FallingViewStartMs` / `FallingViewEndMs`
这些值会写回 `CurveResultRow` 并持久化。
状态规则:
- 两半都没做:`Untested`
- 只完成一半:`Measured`
- 两半都完成后:才根据 4 个指标判 `Pass / Fail`
### 7.5 功率损耗
当前正式实现采用:
- `UDP5080` 读取输入端 `Vin / Iin`
- `IT87xx` 读取输出端 `Vout / Iout / Pout`
这是当前在用方案。
仓库中仍保留旧的 `PowerTestLegacy`DAQ 思路),但未作为正式模块注册。
### 7.6 保护功能
当前保护功能页和逻辑层是**演示框架**
- UI 已完成
- 结果模型已完成
- `ProtectTest` 目前生成演示数据
- 未接真实故障触发与真实曲线采集链路
### 7.7 报告
当前报告支持:
- PDF
- Word
- PDF + Word
报告特性:
- 基于项目与勾选项动态生成
- 曲线插图由 `CurveImageRenderer` 生成
- 报告文件名不是固定 `report.docx`,而是动态解析后输出
- 报告页通过 `WebView2` 预览 PDF
---
## 8. 数据存储
### 8.1 项目目录
每个项目一个目录,目录根路径由 `Storage:ProjectsRoot` 决定。
典型内容:
- `project.json`
- `curves/`
- 生成出的 PDF / Word 报告
### 8.2 项目对象
`Project` 当前持久化内容包括:
- DUT 信息
- 测试站点信息
- 测试项勾选
- 报告配置
- 基础测试、曲线测试、功率损耗、保护功能结果
- 曲线阈值
- 四页测试说明文本
### 8.3 波形存储格式
波形不是简单的 JSON而是
- `curves/*.bin`float32 二进制采样数据
- `curves/*.json`:元数据
元数据包含:
- 采样率
- 触发点位置
- 通道角色/名称/单位
- 标定参数
### 8.4 旧数据兼容
`CurveResultRow` 中保留了旧字段:
- `ViewStartMs`
- `ViewEndMs`
新逻辑优先使用:
- `RisingViewStartMs / RisingViewEndMs`
- `FallingViewStartMs / FallingViewEndMs`
缺失时会回退到旧字段,兼容老项目。
---
## 9. 运行与发布
### 9.1 本地运行
推荐命令:
```powershell
PowerShell -ExecutionPolicy Bypass -File .\run.ps1
```
该脚本默认行为:
1. `dotnet publish` UI 到 `publish\win-x64`
2. 可选执行测试
3. 可选执行种子数据生成
4. 直接启动 `publish\win-x64\SSPCTester.UI.exe`
### 9.2 常用参数
- `-Test`:执行 `SSPCTester.Tests`
- `-Seed`:执行 `SSPCTester.Seed`
- `-CleanSeed`:清空项目目录后重新灌入种子数据
- `-NoLaunch`:只发布不启动
### 9.3 发布
推荐命令:
```powershell
PowerShell -ExecutionPolicy Bypass -File .\publish.ps1
```
当前默认发布参数是:
- `Release`
- `win-x64`
- `SelfContained = true`
- `SingleFile = false`
说明:
- 当前脚本明确没有走 WPF 单文件发布默认方案
- 发布时会检查 `ACTS1000_64.dll` 是否存在
### 9.4 运行环境
源码构建至少需要:
- Windows x64
- .NET 8 SDK x64
- WebView2 Runtime
若只运行现成发布版,则至少需要满足发布产物依赖和本机驱动/DLL 条件。
---
## 10. 当前状态与后续工作
### 10.1 已完成
- WPF 主程序骨架、主题资源、页面导航
- 浏览态 / 测试态切换
- 项目新建、加载、保存
- 基础测试、曲线测试、功率损耗、保护功能页
- PDF / Word 报告生成
- 真/假设备可按设备粒度切换
- 实际 SSPC / 电源 / 负载 / PCIe8586 驱动接入代码
### 10.2 待统一 / 待完善
- 保护功能仍为演示逻辑,未接真实测试链路
- 功率损耗旧版 `PowerTestLegacy` 仍在仓库,后续应明确去留
- 曲线弹窗窗口已实现,但当前主页面尚未提供入口按钮
- 报告生成对新旧曲线视窗字段的使用口径后续建议统一
---
## 11. 风险与注意事项
1. **DAQ 仍是最高风险点**:厂商 DLL、设备 ID、采样参数、输入量程和触发时序都依赖现场。
2. **Modbus 地址和端口仍需现场核实**:虽然当前实现已支持 3 模块映射,但设备地址/串口号应视现场配置为准。
3. **电源与负载真实驱动已接入,但控制命令支持度需按实物验证**
4. **所有长流程应贯穿 `CancellationToken`**,中止后优先保证设备回到安全状态。
5. **示波器不纳入软件控制**,仅作为线下校准基准。
6. **任何涉及数据模型持久化格式的修改,都要考虑旧项目兼容**
---
## 12. 维护建议
1. 改接口前,先同步修改 `Devices.Interfaces`、实现类、DI 装配和本文档。
2. 改项目结构前,先检查 `run.ps1`、`publish.ps1`、`Seed`、测试项目是否受影响。
3. 改曲线页前,注意当前已经有“主页面 + 弹窗 + 报告图”的三处视窗相关逻辑。
4. 改报告前,确认 `ReportViewModel`、`Logic/Report`、`WebView2` 预览链路同时保持可用。
5. 若发现文档内容再次与代码偏离,应优先更新本文档,而不是继续在过时约束上开发。