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

7.3 KiB
Raw Blame History

数据处理流程

所属模块:架构概览


1. 完整流程

log 文件
  → parse
  → local.db
  → aggregate
  → release.db
  → export
  → WCT-ChargingParameterDatabase.csv

2. 阶段一parse → local.db

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

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 各列计算详解

替代配置:

配置文件 appsettings.json 中可添加映射:

{
  "Aggregation": {
    "TxPanelMappings": {
      "single-mold": "奇瑞",
      "dual-rapid": "智己"
    },
    "CarModelMappings": {
      "L6": "L6",
      "CM3": "CM3"
    }
  }
}

4. 阶段三export → CSV

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 05W→350、510W→500、1015W→750、1530W→1000、30~50W→1250
  • 来源字段:列值取 Ploss 记录的 Field7(实际损耗)。
  • 分桶规则:一条记录按其 Field9Threshold的值落入对应档位列 Field9 == power 的记录,其 Field7 计入 power 列的平均值;Field9 不在列头集合中的记录不进任何列。
  • 聚合:取分组内落入该档位的 Field7 平均值(有效值取平均,全部为空则留空)。
  • 字段编号说明:需求文档称阈值为逻辑编号 (8),但当前解析数据中阈值实际落在 field_9 field_8 为负数、非阈值)。解析器按原始位置逐字段存储,字段语义以实体注释与实测数据为准。

5.3 Q值 / Q基值

  • 来源字段Qfod 记录的 CurrentQ / RawQ
  • 分场景预聚合:每个测试场景先对 Qfod 记录求 ΣCurrentQΣRawQCount(数据库端完成)。
  • 组内加权累加:分组内每一条 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. 数据库隔离

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