220 lines
7.3 KiB
Markdown
220 lines
7.3 KiB
Markdown
# 数据处理流程
|
||
|
||
> 所属模块:[架构概览](./概览.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 拆分 | 取第一个分隔符后部分 |
|
||
| 350mW~2250mW(9列) | 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.db,export 阶段仅按 (车厂, 车型, 手机厂商, 型号) 排序后原样写出。
|
||
> 所有数值列均按同一个分组键聚合;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 / ΣCount;Q基值 = Σ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 带 BOM,RFC 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.db,aggregate 不写 local.db |