Files
WCTDataMiner/docs/architecture/数据处理流程.md
Scottxjw 430b38b361 feat(aggregation): 按 Threshold 分桶并优化 Qfod 聚合
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-14 09:24:38 +08:00

220 lines
7.3 KiB
Markdown
Raw 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.
# 数据处理流程
> 所属模块:[架构概览](./概览.md)
---
## 1. 完整流程
```text
log 文件
→ parse
→ local.db
→ aggregate
→ release.db
→ export
→ WCT-ChargingParameterDatabase.csv
```
---
## 2. 阶段一parse → local.db
```mermaid
sequenceDiagram
participant CLI as CLI
participant PS as ParseService
participant SS as ScenarioService
participant FNP as FileNameParser
participant QP as QfodParser
participant PP as PlossParser
participant DB as local.db
CLI->>PS: ParseFileAsync(filePath)
PS->>FNP: 解析文件名
FNP-->>PS: ScenarioInfo
PS->>SS: GetOrCreateScenarioAsync
SS->>DB: 维度 upsert + 场景 upsert
SS-->>PS: TestScenario
PS->>PS: 逐行读取文件
loop 每行
alt Qfod 行
PS->>QP: Parse(line)
QP-->>PS: QfodRecord
else Ploss 行
PS->>PP: Parse(line)
PP-->>PS: PlossRecord
end
end
PS->>DB: BatchInsert QfodRecords
PS->>DB: BatchInsert PlossRecords
PS->>DB: UpdateScenarioStats
PS-->>CLI: ParseReport
```
**关键点:**
- 文件名格式: `TxPanel-TxHardware-TxSoftware-RxType-Purpose-Date-Seq.log`
- 支持两行 Ploss 格式header 行 + 数据行)
- 事务批量写入,读取 `Parser:BatchSize` 配置分批
- 解析错误记录到 Serilog不阻断流程
---
## 3. 阶段二aggregate → release.db
```mermaid
sequenceDiagram
participant CLI as CLI
participant AS as AggregationService
participant LDB as local.db
participant RDB as release.db
CLI->>AS: AggregateAsync()
AS->>LDB: 读取所有 PlossRecord + 关联维度
AS->>AS: 按 (车厂, 车型, 手机厂商, 型号) 分组
AS->>AS: 按 Threshold(Field9) 分桶计算 9 个功率列
AS->>AS: 计算 Q值/Q基值/P-Q系数
AS->>RDB: DELETE + INSERT (事务)
AS-->>CLI: AggregationReport
```
**聚合规则:**
| 字段 | 来源 | 计算方式 |
|------|------|----------|
| 车厂 (CarFactory) | 配置映射 `Aggregation:TxPanelMappings` | 找不到映射时 fallback 为 TxPanel.Name |
| 车型 (CarModel) | 配置映射 `Aggregation:CarModelMappings` | fallback 为 TxHardware.Version |
| 手机厂商 (PhoneBrand) | RxType.Name 拆分 | 取第一个分隔符前部分 |
| 型号 (PhoneModel) | RxType.Name 拆分 | 取第一个分隔符后部分 |
| 350mW2250mW9列 | PlossRecord.Field7 | 按 Threshold(Field9) 分桶(记录阈值=档位),取 Field7 平均值 |
| Q值 (QValue) | QfodRecord.CurrentQ | 平均值 |
| Q基值 (QBaseValue) | QfodRecord.RawQ | 平均值 |
| P-Q值系数 (PqCoefficient) | PlossRecord.DeltaP | 平均值 |
| 谐振频率 (ResonanceFrequency) | 暂无 | 允许为空 |
> 各列的详细计算规则(含公式、功率匹配与空值逻辑)见 [§5 输出 CSV 各列计算详解](#5-输出-csv-各列计算详解)。
**替代配置:**
配置文件 `appsettings.json` 中可添加映射:
```json
{
"Aggregation": {
"TxPanelMappings": {
"single-mold": "奇瑞",
"dual-rapid": "智己"
},
"CarModelMappings": {
"L6": "L6",
"CM3": "CM3"
}
}
}
```
---
## 4. 阶段三export → CSV
```mermaid
sequenceDiagram
participant CLI as CLI
participant ES as ExportService
participant RDB as release.db
participant FS as Filesystem
CLI->>ES: ExportChargingParametersToCsvAsync(outputDir)
ES->>RDB: 读取 ChargingParameters (排序)
ES->>FS: 写入 WCT-ChargingParameterDatabase.csv
ES-->>CLI: 文件路径
```
**导出规格:**
- 固定文件名: `WCT-ChargingParameterDatabase.csv`
- 固定 17 列 UTF-8 BOM
- RFC 4180 转义
- 数字使用 invariant culture
---
## 5. 输出 CSV 各列计算详解
> 最终 `WCT-ChargingParameterDatabase.csv` 的每一列都在 aggregate 阶段由 `AggregationService`
> 计算写入 release.dbexport 阶段仅按 (车厂, 车型, 手机厂商, 型号) 排序后原样写出。
> 所有数值列均按同一个分组键聚合release.db 中每条记录对应 CSV 中的一行。
### 5.1 分组键(一行 = 一个分组)
| CSV 列 | 计算方式 |
|--------|----------|
| 车厂 | `Aggregation:TxPanelMappings` 映射 TxPanel.Name未配置映射时 fallback 为 TxPanel.Name |
| 车型 | `Aggregation:CarModelMappings` 映射 TxHardware.Version未配置映射时 fallback 为 TxHardware.Version |
| 手机厂商 | RxType.Name 按第一个分隔符(`/``_``:`)拆分的**前半部分** |
| 型号 | 分隔符的**后半部分** |
- RxType.Name 无分隔符时:手机厂商固定为 `Unknown`,型号取整个 RxType.Name。
- **分组基于 Ploss 记录**:只有 Qfod 记录、没有任何 Ploss 记录的场景不会产生发布记录,
因而不会出现在 CSV 中。
### 5.2 功率列350mW / 500mW / 750mW / 1000mW / 1250mW / 1500mW / 1750mW / 2000mW / 2250mW
- **语义**:列头即"允许的最大损耗限值"(安全红线)档位,对应需求文档 Table 4
0~5W→350、5~10W→500、10~15W→750、15~30W→1000、30~50W→1250
- **来源字段**:列值取 Ploss 记录的 `Field7`(实际损耗)。
- **分桶规则**:一条记录按其 `Field9`Threshold的值落入对应档位列
`Field9 == power` 的记录,其 `Field7` 计入 `power` 列的平均值;`Field9` 不在列头集合中的记录不进任何列。
- **聚合**:取分组内落入该档位的 `Field7` 平均值(有效值取平均,全部为空则留空)。
- **字段编号说明**:需求文档称阈值为逻辑编号 (8),但当前解析数据中阈值实际落在 `field_9`
`field_8` 为负数、非阈值)。解析器按原始位置逐字段存储,字段语义以实体注释与实测数据为准。
### 5.3 Q值 / Q基值
- **来源字段**Qfod 记录的 `CurrentQ` / `RawQ`
- **分场景预聚合**:每个测试场景先对 Qfod 记录求 `ΣCurrentQ``ΣRawQ``Count`(数据库端完成)。
- **组内加权累加**:分组内每一条 Ploss 记录,按其所属场景把该场景的预聚合值累加一次。
同一场景出现 k 条 Ploss 记录时,其 Sum 与 Count 同倍放大,商保持不变,
等效于将该场景全部 Qfod 明细拉平后求平均。
- **结果**Q值 = ΣCurrentQ / ΣCountQ基值 = ΣRawQ / ΣCount。
- **空值**:分组内没有任何 Qfod 记录时,两列均留空。
### 5.4 P-Q值系数
- **来源字段**Ploss 两行格式第一行的 `delta_p`(旧单行格式无此字段,为 null
- **聚合**:分组内所有 Ploss 记录 `DeltaP` 的平均值(忽略空值)。
- **空值**:分组内 `DeltaP` 全部为空时留空。
### 5.5 谐振频率
- 当前版本固定为 `null`**未实现计算**,导出时该列恒为空。
### 5.6 数值输出格式
- 数字使用 invariant culture 输出 double 全精度(如 `2531.5737704918033`),不四舍五入。
- CSV 为 UTF-8 带 BOMRFC 4180 转义。
---
## 6. 数据库隔离
```text
local.db release.db
───────── ──────────
tx_panel charging_parameter
tx_hardware (独立实体,无外键)
tx_software
rx_type
test_scenario
qfod_record
ploss_record
无关联 ←→ 无关联
```
- 代码中不存在 local entity 到 release entity 的导航属性
- 数据库中没有跨库外键
- export 不读 local.dbaggregate 不写 local.db