← 返回目录

JetWave 航行日志分析系统改进方案 v2.0

项 内容
版本 v2.0
日期 2026-06-01
状态 设计稿(待研发评审)
基准版本 TLA v1.0.2,故障码分级表 v1.0
依据文档 FAULT_CODE_GRADED_REFERENCE.md、LOG_ANALYSIS_IMPROVEMENT_PLAN_PROTOCOL.md

目录

  1. 现状诊断
  2. 核心问题清单
  3. 改进目标
  4. 数据模型重设计(Schema v2)
  5. 码表架构重设计(fault_catalog)
  6. 分析引擎重设计
  7. 三级分级落地方案
  8. 迁移策略
  9. 分阶段路线图
  10. 各领域视角补充
  11. 待研发确认项

1. 现状诊断

1.1 系统架构(已稳定,保留)

cron (15 min)
  └─ run.py → worker.run_incremental()
        ├─ db/pending.py        → SQL_PENDING(水位 + 版本过滤)
        ├─ oss_client.py        → OSS JSON 下载 + 本地缓存
        ├─ session_analyzer.py  → analyze_trajectory_row()
        │     └─ fault_engine.py → analyze_session()
        │           └─ esc_faults.py → 故障码掩码字典
        └─ db/repository.py     → upsert_analysis() + fault_detail

架构本身合理:增量水位、幂等 UPSERT、指纹跳过、版本触发重算,均设计正确,不动骨架,改填充。

1.2 当前 esc_faults.py 实际覆盖(仅 12 个码)

域 已覆盖 实际协议码数 覆盖率
ESC 6 24 25%
ECU 3 6 50%
BMS 4 22 18%

1.3 核心痛点量化

问题 影响
has_fault 过宽 BMS 0x10(低电 10%)触发 has_fault=1,大量「故障会话」是电量告警
severity 与协议无关 纯自研打分,无法与 App 红/黄/蓝弹窗分级对应
overvoltage_points 语义混乱 0x400000 在 motorstuck 固件分支实为异常转速,报表误导研发
缺板体失控字段 研发三条 OR 逻辑无法一键筛出
转速超范围未检测 |motorSpeed| >= 20000 是失控第三条,完全缺失
码表硬编码 新码入库须改 Python 代码、重新部署
BMS 详情归并 bms_fault_points 是所有 BMS 码之和,无法区分「低电告警」和「电芯失效」
日聚合缺失告警/失控统计 surf_log_version_daily_stat 无 session_board_out_of_control、session_warning 列

2. 核心问题清单

P0 — 影响数据正确性(立即修)

P1 — 影响报表可用性(1~2 周)

P2 — 长期可维护性(1 月内)


3. 改进目标

目标 指标
故障定级与 App 一致 has_protocol_fault 与 App 红色弹窗 backIdx=2 对齐
板体失控可复现 has_board_out_of_control=1 的会话即研发 failsafe 关注对象
报表误报率 原「故障会话」里 BMS 低电告警占比降至可区分
码表覆盖率 ESC≥90%、ECU=100%、BMS≥90%
更新无需部署 P2 完成后,新增故障码只改 YAML 并重算

4. 数据模型重设计(Schema v2)

4.1 主表新增列(迁移 SQL)

-- 迁移文件: sql/migrations/20260601_tla_v1_0_3_graded_fault.sql

ALTER TABLE surf_motion_trajectory_analysis

  -- P0: 板体失控
  ADD COLUMN `invalid_speed_points`      INT NOT NULL DEFAULT 0
    COMMENT '|motorSpeed|>=20000 的轨迹点数(固件异常转速阈值)'
    AFTER `stall_thr_motor_mismatch`,

  ADD COLUMN `has_board_out_of_control`  TINYINT(1) NOT NULL DEFAULT 0
    COMMENT '板体失控:堵转 OR ESC_0x400000 OR 超范围转速(研发三条 OR)'
    AFTER `invalid_speed_points`,

  -- P1: 三级分级
  ADD COLUMN `has_protocol_fault`        TINYINT(1) NOT NULL DEFAULT 0
    COMMENT '命中协议级故障(对应 App 红色弹窗 backIdx=2)'
    AFTER `has_board_out_of_control`,

  ADD COLUMN `has_warning`               TINYINT(1) NOT NULL DEFAULT 0
    COMMENT '命中告警/提醒(App 黄/蓝弹窗)但未达故障级'
    AFTER `has_protocol_fault`,

  ADD COLUMN `fault_severity_display`    VARCHAR(16) NOT NULL DEFAULT 'none'
    COMMENT '展示用最高级别: fault|warning|notice|none'
    AFTER `has_warning`,

  -- P1: 0x400000 中性字段(同时保留 overvoltage_points 向后兼容)
  ADD COLUMN `esc_event_0x400000_points` INT NOT NULL DEFAULT 0
    COMMENT 'escEventCode & 0x400000 点数(语义待研发确认)'
    AFTER `overvoltage_ratio`,

  -- P1: BMS 细分
  ADD COLUMN `bms_notice_points`         INT NOT NULL DEFAULT 0
    COMMENT 'BMS 提醒级事件点数(低电20%/单体低压偏低等)'
    AFTER `bms_fault_points`,

  ADD COLUMN `bms_warning_points`        INT NOT NULL DEFAULT 0
    COMMENT 'BMS 告警级事件点数(低电10%/高温限速等)'
    AFTER `bms_notice_points`,

  ADD COLUMN `bms_critical_points`       INT NOT NULL DEFAULT 0
    COMMENT 'BMS 故障级事件点数(压差过大/单体失效/过流等)'
    AFTER `bms_warning_points`;

-- 索引
ALTER TABLE surf_motion_trajectory_analysis
  ADD KEY `idx_board_out_of_control` (`has_board_out_of_control`),
  ADD KEY `idx_fault_severity`       (`fault_severity_display`),
  ADD KEY `idx_protocol_fault`       (`has_protocol_fault`);

4.2 故障明细表新增列

ALTER TABLE surf_motion_trajectory_fault_detail
  ADD COLUMN `severity_level` VARCHAR(8) NOT NULL DEFAULT 'unknown'
    COMMENT 'fault|warning|notice|unknown'
    AFTER `fault_code`,
  ADD COLUMN `board_out_of_control_related` TINYINT(1) NOT NULL DEFAULT 0
    COMMENT '是否参与板体失控合成'
    AFTER `severity_level`;

4.3 日聚合表新增列

ALTER TABLE surf_log_version_daily_stat
  ADD COLUMN `session_board_out_of_control` INT NOT NULL DEFAULT 0
    COMMENT '板体失控会话数' AFTER `session_overvoltage`,
  ADD COLUMN `session_warning`              INT NOT NULL DEFAULT 0
    COMMENT '纯告警(非故障)会话数' AFTER `session_board_out_of_control`,
  ADD COLUMN `session_protocol_fault`       INT NOT NULL DEFAULT 0
    COMMENT '协议故障会话数' AFTER `session_warning`,
  ADD COLUMN `invalid_speed_points_sum`     BIGINT NOT NULL DEFAULT 0
    COMMENT '超范围转速点数合计' AFTER `session_protocol_fault`;

4.4 字段语义重新定义

字段 v1.0.2 语义 v1.0.3 新语义
has_fault severity∈{high,critical} OR 任意码命中 has_protocol_fault OR has_board_out_of_control(收口,不再因 BMS 提醒触发)
has_anomaly D4A081 OR 轨迹空洞 不变
severity 自研打分 none/medium/high/critical 不变(保留,用于内部评分)
fault_severity_display 新增 对外展示级别,从码表 severity_level 取最高
overvoltage_points 0x400000 计数 保留不变(向后兼容),与 esc_event_0x400000_points 值相同
bms_fault_points 全 BMS 码之和 仅 BMS 故障级之和(breaking change,需 bump version)

5. 码表架构重设计(fault_catalog)

5.1 目标文件结构

trajectory-log-analyzer/tla/
  fault_catalog.yaml          ← 新增,人工维护码表
  esc_faults.py               ← 改为从 YAML 加载(P2);P0/P1 阶段先内联扩充
  fault_engine.py             ← 使用新码表字段
  session_analyzer.py         ← 新字段 + bump version

5.2 fault_catalog.yaml 规范(P2 目标)

# fault_catalog.yaml — JetWave 故障码表(由此生成 esc_faults.py 运行时字典)
# 维护者: 固件/协议组 + 数据组
# 每次更新后: analyzer_version bump + FORCE_REANALYZE=1

version: "1.0.3"
updated: "2026-06-01"

esc:
  # ── 故障级 (fault) ─────────────────────────────────────────────────────────
  - code: ESC_FAULT_MOTOR_LINE_CURRENT_OUTPUT_1
    mask: 0x00000001
    severity: fault
    display_zh: 电机线电流输出异常1
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_TOTAL_CURRENT_OUTPUT
    mask: 0x00000002
    severity: fault
    display_zh: 总电流输出异常
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_MOTOR_LINE_VOLTAGE
    mask: 0x00000004
    severity: fault
    display_zh: 电机线电压异常
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_ELECTROLYTIC_CAP_NTC_OUTPUT
    mask: 0x00000008
    severity: fault
    display_zh: 电解电容NTC异常
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_MOSFET_NTC_OUTPUT
    mask: 0x00000010
    severity: fault
    display_zh: MOS管NTC异常
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_MOTOR_LINE_LOOP_IMPEDANCE_SMALL
    mask: 0x00000080
    severity: fault
    display_zh: 电机线回路阻抗偏小
    app_text: 电调模块故障
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_MOTOR_LINE_LOOP_IMPEDANCE_LARGE
    mask: 0x00000100
    severity: fault
    display_zh: 电机线回路阻抗偏大
    app_text: 电调模块故障
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_MOTOR_LINE_VOLTAGE_DETECTION_1
    mask: 0x00000200
    severity: fault
    display_zh: 电机线电压检测电路异常1
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_MOTOR_LINE_VOLTAGE_DETECTION_2
    mask: 0x00000400
    severity: fault
    display_zh: 电机线电压检测电路异常2
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_MOTOR_LINE_CURRENT_OUTPUT_2
    mask: 0x00000800
    severity: fault
    display_zh: 电机线电流输出异常2
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_SW_HW_INCOMPATIBLE
    mask: 0x00002000
    severity: fault
    display_zh: 软硬件版本不兼容
    app_text: ~
    board_out_of_control: false
    confirmed: true
    notes: ECU 上报前通常已屏蔽

  - code: ESC_FAULT_BOOTLOADER_UNSUPPORTED
    mask: 0x00004000
    severity: fault
    display_zh: Bootloader不支持
    app_text: ~
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_OVERCURRENT_SHORT_CIRCUIT_PROTECTION
    mask: 0x00010000
    severity: fault
    display_zh: 过流/短路保护
    app_text: 电调过流异常
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_OVERTEMPERATURE_PROTECTION
    mask: 0x00040000
    severity: fault
    display_zh: 过温保护
    app_text: 电调过温异常
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_PWM_SIGNAL_LOST
    mask: 0x00080000
    severity: fault
    display_zh: PWM油门信号丢失
    app_text: ~
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_THROTTLE_SATURATION
    mask: 0x00200000
    severity: fault
    display_zh: 油门饱和
    app_text: ~
    board_out_of_control: false
    confirmed: true

  # ── 告警级 (warning) ────────────────────────────────────────────────────────
  - code: ESC_FAULT_BUS_VOLTAGE_SAMPLING
    mask: 0x00000040
    severity: fault          # selfcheck,归故障
    display_zh: 母线电压采样异常
    app_text: 电调模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_STALL_PROTECTION
    mask: 0x00020000
    severity: warning
    display_zh: 电机堵转保护
    app_text: 电机堵转,请检查螺旋桨
    board_out_of_control: true
    confirmed: true
    notes: running_error bit1;研发口径 P0

  - code: ESC_FAULT_CURRENT_HIGH
    mask: 0x00800000
    severity: warning
    display_zh: 电调电流高(限速保护中)
    app_text: 电调过流,限速保护中
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_TEMPERATURE_HIGH
    mask: 0x01000000
    severity: warning
    display_zh: 电调温度高(限速保护中)
    app_text: 电调温度高,限速保护中
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_PROPELLER_OUT_OF_WATER
    mask: 0x02000000
    severity: warning
    display_zh: 螺旋桨离水空转保护
    app_text: 螺旋桨未入水的空转保护
    board_out_of_control: false
    confirmed: true

  # ── 提醒级 (notice) ─────────────────────────────────────────────────────────
  - code: ESC_FAULT_BUS_VOLTAGE_OV_UV
    mask: 0x00000020
    severity: notice
    display_zh: 母线电压过压或欠压
    app_text: 电调输入电压异常
    board_out_of_control: false
    confirmed: true

  - code: ESC_FAULT_NO_LOAD
    mask: 0x00100000
    severity: notice
    display_zh: 无负载/电机空转
    app_text: 电机空转
    board_out_of_control: false
    confirmed: true

  # ── 待确认 ──────────────────────────────────────────────────────────────────
  - code: ESC_EVENT_0x400000
    mask: 0x00400000
    severity: warning          # 暂按告警;语义待研发确认
    display_zh: ESC事件0x400000(过压/固件误映射,待确认)
    app_text: ~
    board_out_of_control: true
    confirmed: false
    notes: |
      JetWave.xml 定义为过压保护;motorstuck 固件分支中 ECU 将 |speed|>=20000
      (running_error bit6) 错误映射到此位。建议展示时标注"待确认"。
      与 ECU ecuEventCode 0x40000000(ESC通信故障) 不同,注意区分。

ecu:
  - code: FAULT_BOARD_ATTITUDE
    mask: 0x04000000
    severity: fault
    display_zh: 板体姿态异常
    app_text: 板体姿态异常
    board_out_of_control: false
    confirmed: true

  - code: FAULT_FLASH_COMMUNICATION
    mask: 0x08000000
    severity: fault
    display_zh: FLASH/SD通信故障
    app_text: 主控模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: FAULT_WATER_PRESSURE_SENSOR_COMMUNICATION
    mask: 0x10000000
    severity: fault
    display_zh: 水压传感器通信异常
    app_text: 主控模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

  - code: FAULT_IMU_COMMUNICATION
    mask: 0x20000000
    severity: fault
    display_zh: IMU通信故障
    app_text: 主控模块故障,请联系售后
    board_out_of_control: false
    confirmed: true
    notes: 勿与 ESC escEventCode 0x02000000 混淆

  - code: FAULT_ESC_COMMUNICATION
    mask: 0x40000000
    severity: fault
    display_zh: 电调通信故障(12s无数据)
    app_text: 主控模块故障,请联系售后
    board_out_of_control: false
    confirmed: true
    notes: 勿与 ESC escEventCode 0x00400000 混淆

  - code: FAULT_BATTERY_COMMUNICATION
    mask: 0x80000000
    severity: fault
    display_zh: 电池通信故障
    app_text: 主控模块故障,请联系售后
    board_out_of_control: false
    confirmed: true

bms:
  # ── 提醒级 ──────────────────────────────────────────────────────────────────
  - code: BMS_LOW_SOC_WARNING_20
    mask: 0x00000020
    severity: notice
    display_zh: 电量低于20%
    app_text: 板体低电量20%
    confirmed: true

  - code: BMS_CELL_VOLTAGE_LOW
    mask: 0x00000400
    severity: notice
    display_zh: 电池单体电压偏低
    app_text: (仅埋点)
    confirmed: true

  - code: BMS_CELL_VOLTAGE_HIGH
    mask: 0x00000800
    severity: notice
    display_zh: 电池单体电压偏高
    app_text: ~
    confirmed: true

  - code: BMS_TOTAL_VOLTAGE_LOW
    mask: 0x00001000
    severity: notice
    display_zh: 电池总电压偏低
    app_text: (仅埋点)
    confirmed: true

  - code: BMS_TOTAL_VOLTAGE_HIGH
    mask: 0x00002000
    severity: notice
    display_zh: 电池总电压偏高
    app_text: ~
    confirmed: true

  # ── 告警级 ──────────────────────────────────────────────────────────────────
  - code: BMS_LOW_SOC_WARNING_10
    mask: 0x00000010
    severity: warning
    display_zh: 电量低于10%(严重低电)
    app_text: 板体严重低电量10%
    confirmed: true

  - code: BMS_LOW_SOH_WARNING
    mask: 0x00000040
    severity: warning
    display_zh: 电池健康度偏低
    app_text: 电池健康度低
    confirmed: true

  - code: BMS_CELL_TEMP_DIFFERENCE_WARNING
    mask: 0x00000080
    severity: warning
    display_zh: 电芯温差大
    app_text: 电芯温差大
    confirmed: true

  - code: BMS_CELL_VOLTAGE_DIFFERENCE_WARNING
    mask: 0x00000100
    severity: warning
    display_zh: 电芯压差大
    app_text: 电芯压差大
    confirmed: true

  - code: BMS_BATTERY_CURRENT_HIGH
    mask: 0x00000200
    severity: warning
    display_zh: 电池电流高(限速保护)
    app_text: 电池过流,限速保护中
    confirmed: true

  - code: BATTERY_TEMP_LOW
    mask: 0x00004000
    severity: warning
    display_zh: 电池温度偏低(保护中)
    app_text: 电池温度过低保护中
    confirmed: true

  - code: BATTERY_TEMP_HIGH
    mask: 0x00008000
    severity: warning
    display_zh: 电池温度偏高(限速)
    app_text: 电池温度高,限速保护中
    confirmed: true

  # ── 故障级 ──────────────────────────────────────────────────────────────────
  - code: BMS_CELL_VOLTAGE_DIFF_TOO_HIGH
    mask: 0x00400000
    severity: fault
    display_zh: 电池压差过大
    app_text: 电池压差过大,请联系售后
    confirmed: true

  - code: BMS_CELL_VOLTAGE_LOW_FAILURE
    mask: 0x00800000
    severity: fault
    display_zh: 单体低压失效
    app_text: 电池故障,请联系售后
    confirmed: true

  - code: BMS_CELL_VOLTAGE_HIGH_FAILURE
    mask: 0x01000000
    severity: fault
    display_zh: 单体过压失效
    app_text: 电池故障,请联系售后
    confirmed: true

  - code: BATTERY_CURRENT_TOO_HIGH
    mask: 0x02000000
    severity: fault
    display_zh: 电池电流过高
    app_text: 电池过流异常
    confirmed: true

  - code: BMS_CELL_VOLTAGE_TOO_LOW
    mask: 0x04000000
    severity: fault
    display_zh: 电池单体电压过低
    app_text: 电池故障,请联系售后
    confirmed: true

  - code: BMS_CELL_VOLTAGE_TOO_HIGH
    mask: 0x08000000
    severity: fault
    display_zh: 电池单体电压过高
    app_text: 电池故障,请联系售后
    confirmed: true

  - code: BMS_TOTAL_VOLTAGE_TOO_LOW
    mask: 0x10000000
    severity: fault
    display_zh: 电池总电压过低(低电关机)
    app_text: 板体低电量关机
    confirmed: true

  - code: BMS_TOTAL_VOLTAGE_TOO_HIGH
    mask: 0x20000000
    severity: fault
    display_zh: 电池总电压过高
    app_text: 电池故障,请联系售后
    confirmed: true
    notes: 勿与 ECU ecuEventCode 0x20000000 (IMU通信故障) 混淆

  - code: BATTERY_TEMP_TOO_LOW
    mask: 0x40000000
    severity: fault
    display_zh: 电池温度过低
    app_text: 电池温度过低保护中
    confirmed: true

  - code: BATTERY_TEMP_TOO_HIGH
    mask: 0x80000000
    severity: fault
    display_zh: 电池温度过高
    app_text: 电池高温异常
    confirmed: true

motor_speed_rules:
  - rule: out_of_range
    condition: "abs(motorSpeed) >= 20000"
    severity: warning
    board_out_of_control: true
    display_zh: 转速反馈超范围(固件异常转速阈值)
    confirmed: true
    notes: 固件 esc_manager.c 阈值一致;单位 0.1RPM,即>=2000RPM

  - rule: corrupt_d4a081
    condition: "(motorSpeed >> 8) == 0xD4A081"
    severity: anomaly
    board_out_of_control: false
    display_zh: 转速数据脏包(CAN通信异常)
    confirmed: true

6. 分析引擎重设计

6.1 esc_faults.py — P0/P1 内联扩充(YAML 未上线前)

# P0/P1 过渡版本,P2 完成后由 fault_catalog.yaml 生成

SEVERITY = {
    "fault":   "fault",
    "warning": "warning",
    "notice":  "notice",
    "anomaly": "anomaly",
}

ESC_FAULTS: dict[str, int] = {
    # ── 故障级 ──────────────────────────────────────────────────────────
    "ESC_FAULT_MOTOR_LINE_CURRENT_OUTPUT_1":            0x00000001,
    "ESC_FAULT_TOTAL_CURRENT_OUTPUT":                   0x00000002,
    "ESC_FAULT_MOTOR_LINE_VOLTAGE":                     0x00000004,
    "ESC_FAULT_ELECTROLYTIC_CAP_NTC_OUTPUT":            0x00000008,
    "ESC_FAULT_MOSFET_NTC_OUTPUT":                      0x00000010,
    "ESC_FAULT_BUS_VOLTAGE_SAMPLING":                   0x00000040,
    "ESC_FAULT_MOTOR_LINE_LOOP_IMPEDANCE_SMALL":        0x00000080,
    "ESC_FAULT_MOTOR_LINE_LOOP_IMPEDANCE_LARGE":        0x00000100,
    "ESC_FAULT_MOTOR_LINE_VOLTAGE_DETECTION_1":         0x00000200,
    "ESC_FAULT_MOTOR_LINE_VOLTAGE_DETECTION_2":         0x00000400,
    "ESC_FAULT_MOTOR_LINE_CURRENT_OUTPUT_2":            0x00000800,
    "ESC_FAULT_SW_HW_INCOMPATIBLE":                     0x00002000,
    "ESC_FAULT_BOOTLOADER_UNSUPPORTED":                 0x00004000,
    "ESC_FAULT_OVERCURRENT_SHORT_CIRCUIT_PROTECTION":   0x00010000,
    "ESC_FAULT_OVERTEMPERATURE_PROTECTION":             0x00040000,
    "ESC_FAULT_PWM_SIGNAL_LOST":                        0x00080000,
    "ESC_FAULT_THROTTLE_SATURATION":                    0x00200000,
    # ── 告警级 ──────────────────────────────────────────────────────────
    "ESC_FAULT_STALL_PROTECTION":                       0x00020000,
    "ESC_FAULT_CURRENT_HIGH":                           0x00800000,
    "ESC_FAULT_TEMPERATURE_HIGH":                       0x01000000,
    "ESC_FAULT_PROPELLER_OUT_OF_WATER":                 0x02000000,
    "ESC_EVENT_0x400000":                               0x00400000,  # 语义待确认
    # ── 提醒级 ──────────────────────────────────────────────────────────
    "ESC_FAULT_BUS_VOLTAGE_OV_UV":                      0x00000020,
    "ESC_FAULT_NO_LOAD":                                0x00100000,
}

ESC_SEVERITY: dict[str, str] = {
    "ESC_FAULT_MOTOR_LINE_CURRENT_OUTPUT_1":            "fault",
    "ESC_FAULT_TOTAL_CURRENT_OUTPUT":                   "fault",
    "ESC_FAULT_MOTOR_LINE_VOLTAGE":                     "fault",
    "ESC_FAULT_ELECTROLYTIC_CAP_NTC_OUTPUT":            "fault",
    "ESC_FAULT_MOSFET_NTC_OUTPUT":                      "fault",
    "ESC_FAULT_BUS_VOLTAGE_SAMPLING":                   "fault",
    "ESC_FAULT_MOTOR_LINE_LOOP_IMPEDANCE_SMALL":        "fault",
    "ESC_FAULT_MOTOR_LINE_LOOP_IMPEDANCE_LARGE":        "fault",
    "ESC_FAULT_MOTOR_LINE_VOLTAGE_DETECTION_1":         "fault",
    "ESC_FAULT_MOTOR_LINE_VOLTAGE_DETECTION_2":         "fault",
    "ESC_FAULT_MOTOR_LINE_CURRENT_OUTPUT_2":            "fault",
    "ESC_FAULT_SW_HW_INCOMPATIBLE":                     "fault",
    "ESC_FAULT_BOOTLOADER_UNSUPPORTED":                 "fault",
    "ESC_FAULT_OVERCURRENT_SHORT_CIRCUIT_PROTECTION":   "fault",
    "ESC_FAULT_OVERTEMPERATURE_PROTECTION":             "fault",
    "ESC_FAULT_PWM_SIGNAL_LOST":                        "fault",
    "ESC_FAULT_THROTTLE_SATURATION":                    "fault",
    "ESC_FAULT_STALL_PROTECTION":                       "warning",
    "ESC_FAULT_CURRENT_HIGH":                           "warning",
    "ESC_FAULT_TEMPERATURE_HIGH":                       "warning",
    "ESC_FAULT_PROPELLER_OUT_OF_WATER":                 "warning",
    "ESC_EVENT_0x400000":                               "warning",
    "ESC_FAULT_BUS_VOLTAGE_OV_UV":                      "notice",
    "ESC_FAULT_NO_LOAD":                                "notice",
}

ESC_BOARD_OUT_OF_CONTROL: set[str] = {
    "ESC_FAULT_STALL_PROTECTION",
    "ESC_EVENT_0x400000",
}

ECU_FAULTS: dict[str, int] = {
    "FAULT_BOARD_ATTITUDE":                             0x04000000,
    "FAULT_FLASH_COMMUNICATION":                        0x08000000,
    "FAULT_WATER_PRESSURE_SENSOR_COMMUNICATION":        0x10000000,
    "FAULT_IMU_COMMUNICATION":                          0x20000000,
    "FAULT_ESC_COMMUNICATION":                          0x40000000,
    "FAULT_BATTERY_COMMUNICATION":                      0x80000000,
}

ECU_SEVERITY: dict[str, str] = {k: "fault" for k in ECU_FAULTS}

BMS_FAULTS: dict[str, int] = {
    # 提醒级
    "BMS_LOW_SOC_WARNING_20":                           0x00000020,
    "BMS_CELL_VOLTAGE_LOW":                             0x00000400,
    "BMS_CELL_VOLTAGE_HIGH":                            0x00000800,
    "BMS_TOTAL_VOLTAGE_LOW":                            0x00001000,
    "BMS_TOTAL_VOLTAGE_HIGH":                           0x00002000,
    # 告警级
    "BMS_LOW_SOC_WARNING_10":                           0x00000010,
    "BMS_LOW_SOH_WARNING":                              0x00000040,
    "BMS_CELL_TEMP_DIFFERENCE_WARNING":                 0x00000080,
    "BMS_CELL_VOLTAGE_DIFFERENCE_WARNING":              0x00000100,
    "BMS_BATTERY_CURRENT_HIGH":                         0x00000200,
    "BATTERY_TEMP_LOW":                                 0x00004000,
    "BATTERY_TEMP_HIGH":                                0x00008000,
    # 故障级
    "BMS_CELL_VOLTAGE_DIFF_TOO_HIGH":                   0x00400000,
    "BMS_CELL_VOLTAGE_LOW_FAILURE":                     0x00800000,
    "BMS_CELL_VOLTAGE_HIGH_FAILURE":                    0x01000000,
    "BATTERY_CURRENT_TOO_HIGH":                         0x02000000,
    "BMS_CELL_VOLTAGE_TOO_LOW":                         0x04000000,
    "BMS_CELL_VOLTAGE_TOO_HIGH":                        0x08000000,
    "BMS_TOTAL_VOLTAGE_TOO_LOW":                        0x10000000,
    "BMS_TOTAL_VOLTAGE_TOO_HIGH":                       0x20000000,
    "BATTERY_TEMP_TOO_LOW":                             0x40000000,
    "BATTERY_TEMP_TOO_HIGH":                            0x80000000,
}

BMS_SEVERITY: dict[str, str] = {
    "BMS_LOW_SOC_WARNING_20":           "notice",
    "BMS_CELL_VOLTAGE_LOW":             "notice",
    "BMS_CELL_VOLTAGE_HIGH":            "notice",
    "BMS_TOTAL_VOLTAGE_LOW":            "notice",
    "BMS_TOTAL_VOLTAGE_HIGH":           "notice",
    "BMS_LOW_SOC_WARNING_10":           "warning",
    "BMS_LOW_SOH_WARNING":              "warning",
    "BMS_CELL_TEMP_DIFFERENCE_WARNING": "warning",
    "BMS_CELL_VOLTAGE_DIFFERENCE_WARNING": "warning",
    "BMS_BATTERY_CURRENT_HIGH":         "warning",
    "BATTERY_TEMP_LOW":                 "warning",
    "BATTERY_TEMP_HIGH":                "warning",
    "BMS_CELL_VOLTAGE_DIFF_TOO_HIGH":   "fault",
    "BMS_CELL_VOLTAGE_LOW_FAILURE":     "fault",
    "BMS_CELL_VOLTAGE_HIGH_FAILURE":    "fault",
    "BATTERY_CURRENT_TOO_HIGH":         "fault",
    "BMS_CELL_VOLTAGE_TOO_LOW":         "fault",
    "BMS_CELL_VOLTAGE_TOO_HIGH":        "fault",
    "BMS_TOTAL_VOLTAGE_TOO_LOW":        "fault",
    "BMS_TOTAL_VOLTAGE_TOO_HIGH":       "fault",
    "BATTERY_TEMP_TOO_LOW":             "fault",
    "BATTERY_TEMP_TOO_HIGH":            "fault",
}

PRIMARY_ESC_FAULTS = (
    "ESC_FAULT_STALL_PROTECTION",
    "ESC_EVENT_0x400000",
)

SEVERITY_ORDER = {"fault": 3, "warning": 2, "notice": 1, "none": 0, "anomaly": 0}

def severity_max(*levels: str) -> str:
    """取多个 severity 的最高级。"""
    return max(levels, key=lambda s: SEVERITY_ORDER.get(s, 0))

6.2 session_analyzer.py — P0/P1 关键新字段伪码

ANALYZER_VERSION = "1.0.3"   # bump

def is_invalid_motor_speed(motor_speed) -> bool:
    """固件阈值:|speed| >= 20000 (0.1 RPM = 2000 RPM)"""
    if motor_speed is None:
        return False
    v = int(motor_speed)
    return v <= -20000 or v >= 20000

def analyze_trajectory_row(row, data, ...):
    rep = analyze_session(row, data)          # fault_engine (不变)
    tl  = data.get("trajectoryList") or []
    n   = max(len(tl), 1)

    # ── 新增: invalid_speed_points ─────────────────────────
    invalid_speed_cnt = sum(
        1 for p in tl
        if not is_corrupt_motor_speed(p.get("motorSpeed"))   # 排除脏包
        and is_invalid_motor_speed(p.get("motorSpeed"))
    )

    # ── 新增: 分级统计 ─────────────────────────────────────
    esc_matched_severity = {}        # code -> severity
    ecu_matched_severity = {}
    bms_notice_cnt = bms_warning_cnt = bms_fault_cnt = 0

    for p in tl:
        for name in match_esc_faults(int(p.get("escEventCode") or 0)):
            esc_matched_severity[name] = ESC_SEVERITY.get(name, "unknown")
        for name in match_ecu_faults(int(p.get("ecuEventCode") or 0)):
            ecu_matched_severity[name] = ECU_SEVERITY.get(name, "unknown")
        for name in match_bms_faults(int(p.get("bmsEventCode") or 0)):
            sev = BMS_SEVERITY.get(name, "unknown")
            if sev == "notice":   bms_notice_cnt  += 1
            elif sev == "warning": bms_warning_cnt += 1
            elif sev == "fault":   bms_fault_cnt   += 1

    has_protocol_fault = (
        any(s == "fault" for s in esc_matched_severity.values()) or
        any(s == "fault" for s in ecu_matched_severity.values()) or
        bms_fault_cnt > 0
    )
    has_warning = (
        any(s in ("warning","notice") for s in esc_matched_severity.values()) or
        any(s in ("warning","notice") for s in ecu_matched_severity.values()) or
        bms_warning_cnt > 0 or bms_notice_cnt > 0
    ) and not has_protocol_fault

    stall_cnt      = rep.esc_fault_points.get("ESC_FAULT_STALL_PROTECTION", 0)
    esc0x400000    = rep.esc_fault_points.get("ESC_EVENT_0x400000", 0)

    has_board_out_of_control = bool(
        stall_cnt > 0
        or esc0x400000 > 0
        or invalid_speed_cnt > 0
    )

    # has_fault 重定义(收口)
    has_fault = has_protocol_fault or has_board_out_of_control

    # 展示级别
    if has_protocol_fault:
        fault_severity_display = "fault"
    elif has_board_out_of_control:
        fault_severity_display = "warning"
    elif has_warning:
        fault_severity_display = "warning"
    elif any(s == "notice" for s in {**esc_matched_severity, **ecu_matched_severity}.values()) or bms_notice_cnt > 0:
        fault_severity_display = "notice"
    else:
        fault_severity_display = "none"

    return {
        # ... 原有字段 ...
        "invalid_speed_points":       invalid_speed_cnt,
        "has_board_out_of_control":   1 if has_board_out_of_control else 0,
        "has_protocol_fault":         1 if has_protocol_fault else 0,
        "has_warning":                1 if has_warning else 0,
        "fault_severity_display":     fault_severity_display,
        "esc_event_0x400000_points":  esc0x400000,
        "overvoltage_points":         esc0x400000,    # 向后兼容,同值双写
        "bms_notice_points":          bms_notice_cnt,
        "bms_warning_points":         bms_warning_cnt,
        "bms_critical_points":        bms_fault_cnt,
        "bms_fault_points":           bms_fault_cnt,  # v1.0.3 重定义:仅故障级
        # ...
    }

6.3 故障明细 _build_fault_details 扩充

def _build_fault_details(trajectory_id, tl):
    ...
    rows.append({
        "trajectory_id":              trajectory_id,
        "fault_domain":               domain,
        "fault_code":                 name,
        "severity_level":             severity_map.get(name, "unknown"),   # 新增
        "board_out_of_control_related": 1 if name in BOC_CODES else 0,    # 新增
        "fault_mask":                 mask,
        "point_count":                cnt,
        "point_ratio":                round(cnt / n, 4),
        "first_seen_ms":              first_ms,
    })

7. 三级分级落地方案

7.1 分级判定决策树

每条轨迹点 event_code
    ├─ ESC: match_esc_faults()
    │     → 记录 severity (fault/warning/notice)
    │     → 若 board_out_of_control=true → 累计
    ├─ ECU: match_ecu_faults() → severity 均为 fault
    └─ BMS: match_bms_faults()
          → 按 severity 分桶计数

会话级合成:
    has_protocol_fault  = 任一 fault 级码命中
    has_board_out_of_control = stall OR 0x400000 OR invalid_speed
    has_warning         = 有 warning/notice 且无 fault
    has_fault           = protocol_fault OR board_out_of_control
    fault_severity_display = fault > warning > notice > none

7.2 has_fault 新旧对比示例

会话类型 v1.0.2 has_fault v1.0.3 has_fault 说明
只有 BMS 0x10(低电 10%) 1 ❌误判 0 ✅ 告警,不是故障
BMS 0x10 + ESC 堵转 1 1 ✅ 有 board_out_of_control
只有 ESC 堵转 0x20000 1 1 ✅ has_board_out_of_control
只有 invalid_speed 0 ❌漏检 1 ✅ 第三条失控未落地
只有 ECU 通信故障 1 1 ✅ 协议级故障
只有 ESC 离水空转 0x2000000 1 ❌误判 0(has_warning=1)✅ 告警

8. 迁移策略

8.1 版本兼容矩阵

字段 v1.0.2 值 v1.0.3 变化 向后兼容
overvoltage_points 0x400000 计数 同值(双写) ✅
bms_fault_points 全 BMS 之和 仅故障级之和 ❌ breaking,需重算
has_fault 宽定义 收口定义 ❌ breaking
severity 自研打分 不变 ✅
fault_codes JSON esc/ecu/bms 不变 ✅
新增列 — DEFAULT 0/'none' ✅ 存量行安全

8.2 部署顺序

# Step 1: 执行迁移 SQL(新列 DEFAULT 安全,不阻塞读)
mysql -h HOST -u USER -p surf-dev < sql/migrations/20260601_tla_v1_0_3_graded_fault.sql

# Step 2: 部署新代码(ANALYZER_VERSION = "1.0.3")
docker compose build && docker compose up -d

# Step 3: 触发全量重算(仅重算 bms_fault_points/has_fault 语义变化行)
FORCE_REANALYZE=1 BATCH_SIZE=200 docker compose run --rm analyzer
# 或分批:设置 WATERMARK_ADD_TIME 从头跑

# Step 4: 验证
# - 查 bms_fault_points=0 但 bms_warning_points>0 的会话(原来 has_fault=1 的变化)
# - 查 has_board_out_of_control=1 的会话(新增失控标记)
# - 查 invalid_speed_points>0 的会话(新增超范围转速)

8.3 回滚方案

# 回滚代码到 v1.0.2
git checkout v1.0.2-tag && docker compose build && docker compose up -d

# 新列不影响旧代码(DEFAULT 值),无需 DROP
# 如需清空新列数据:
UPDATE surf_motion_trajectory_analysis
  SET invalid_speed_points=0, has_board_out_of_control=0,
      has_protocol_fault=0, has_warning=0,
      fault_severity_display='none'
  WHERE analyzer_version = '1.0.3';

9. 分阶段路线图

Phase 0(立即,P0,预计 1-2 天)

任务 文件 说明
ESC_EVENT_0x400000 改名 esc_faults.py ESC_FAULT_OVERVOLTAGE_PROTECTION → ESC_EVENT_0x400000
invalid_speed_points 计算 session_analyzer.py |motorSpeed| >= 20000,排除脏包
has_board_out_of_control 合成 session_analyzer.py 三条 OR
esc_event_0x400000_points + 双写 session_analyzer.py overvoltage 向后兼容
迁移 SQL P0 列 sql/migrations/ 4 个新列
bump → 1.0.3 session_analyzer.py 触发重算

Phase 1(1~2 周,P1)

任务 文件 说明
ESC 码表扩至 24 码 + severity dict esc_faults.py 参考第 6.1 节
ECU 码表换用协议名 + severity esc_faults.py 6 码全覆盖
BMS 码表扩至 22 码 + severity esc_faults.py 分三级
has_protocol_fault / has_warning session_analyzer.py 见第 6.2 节
fault_severity_display session_analyzer.py 展示最高级别
BMS 细分三列 session_analyzer.py + SQL notice/warning/critical
故障明细表扩列 repository.py + SQL severity_level、boc_related
日聚合表扩列 + refresh_daily_stats 更新 repository.py + SQL 4 个新聚合列
analysis/session_analyzer.py 同步 analysis/ 与 tla 版本对齐

Phase 2(1 月内,P2)

任务 文件 说明
fault_catalog.yaml 实现 tla/fault_catalog.yaml 见第 5 节
esc_faults.py 改为 YAML 加载 esc_faults.py 热更新能力
告警规则 YAML tla/alarm_rules.yaml 阈值配置化
单元测试 tests/ 每条协议码覆盖
监控面板 SQL 视图 sql/views/ 报表/接口用

10. 各领域视角补充

10.1 产品经理视角

用户感知一致性原则:后台「故障会话」必须与 App 红色弹窗体验一致。当前 App 用 backIdx 明确区分提醒/告警/故障,后台分析应 1:1 映射。

可量化的 KPI: - 「板体失控率」= has_board_out_of_control 会话数 / 总会话数,按固件版本分组 - 「纯告警会话」= has_warning AND NOT has_fault,监控是否因低电量引起大量误报 - 「故障转化率」= 同一设备 SN 首次 has_protocol_fault 后 7 天内是否报修

决策建议:每次固件发版前,比对新旧版本的 session_board_out_of_control 日率,作为发版 Go/No-Go 依据之一。

10.2 嵌入式工程师视角

关键对齐点: 1. esc_event = (running_error << 16) | selfcheck_error — 这个编码公式是分析正确性的根基,分析器需严格对齐。 2. feature_motorstuck 固件中 ECU 将 |speed| >= 20000 置到 running_error bit6(0x400000),而非 ESC 固件 bit1(0x20000)。两个固件分支并存时会看到不同的码,分析器必须两条都检测。 3. 转速单位:CAN 协议 0.1 RPM,20000 = 2000 RPM,在分析日志中 motorSpeed 的原始值与此一致,无需换算。 4. MAVLink EventCodeProcessByBit 每 bit 间隔约 12s 发一次,轨迹点事件码不是每点必有,分析时应用累计而非瞬时判断。

建议:在 fault_catalog.yaml 的 notes 字段标注每个码的固件来源文件和函数,便于固件升级时的影响面评估。

10.3 软件架构视角

当前架构优点(保留): - 无状态 cron + 幂等 UPSERT,可随时重跑 - 指纹 + 版本跳过逻辑,避免重复计算 - job 表水位,支持断点续跑

改进点: 1. 配置与代码解耦:P2 阶段 fault_catalog.yaml 外置后,新增故障码无需重新部署,只需 FORCE_REANALYZE=1 重跑即可 2. 版本语义化:ANALYZER_VERSION 应遵循语义版本(major.minor.patch),major bump = schema breaking change,minor = 新字段,patch = bug fix 3. 错误隔离:当前 upsert_failed 在主流程异常时调用,建议增加重试 DLQ(Dead Letter Queue)机制,避免单条 OSS 下载失败阻塞整批

10.4 系统架构视角

数据流全貌:

固件 (ECU/ESC/BMS)
  → MAVLink 事件 (111)
  → RC App 采样 (HomeFragment, 定时)
  → SailingRecordItem (本地 SQLite)
  → OSS JSON 上传
  → surf_motion_trajectory_history (MySQL)
  → TLA 分析 (cron)
  → surf_motion_trajectory_analysis (MySQL)
  → 报表/接口

瓶颈与风险: - OSS JSON 下载无并发(顺序处理),批量大时延迟高 → 建议 P2 阶段引入 concurrent.futures.ThreadPoolExecutor 并发下载,IO 密集型适合线程池 - MySQL 分析表无分区,随数据量增长单表扫描变慢 → 建议 session_date 分区(按月)或建专用只读副本供报表 - 单进程 cron 无监控 → 建议暴露 Prometheus metrics 或写日志到可观测平台

10.5 C/固件工程师视角

UpdateESCErrorCodeByRunningStatus 编码行为(esc_manager.c)需与分析对齐的细节:

  1. running_error 由 ESC 固件上报,ECU 可能 覆盖 某些位(motorstuck 分支 bit6)
  2. 分析器检测 0x400000 时无法区分「真过压」vs「ECU 合成」,只能通过以下辅助信号判断:
  3. 同时 |motorSpeed| >= 20000 → 大概率是 ECU 合成
  4. electricCurrent 极低(近 0)且 motorSpeed 极高 → 可能是真过压
  5. ESC_FAULT_MASK(ECU 上报前屏蔽掉 SW_HW_INCOMPATIBLE、BOOTLOADER_UNSUPPORTED 等)导致这些码在日志里极少出现,分析器覆盖即可,不必为其设告警阈值

10.6 C++ 工程师视角(MAVLink 协议层)

10.7 硬件工程师视角

传感器链路影响分析: - D4A081xx 脏包是 CAN 总线 bit error 或 UART framing 错误导致,与电气质量相关;高频出现说明 PCB 走线/屏蔽/接地有问题 - |motorSpeed| >= 20000 超范围既可能是 ESC 霍尔传感器故障,也可能是 CAN 帧解码错误(降频 10:1 过程中) - 建议:在故障明细表中增加 hardware_suspect 标注,将 D4A081 + 超范围转速合并为「硬件/链路异常」维度,供硬件工程师排查

10.8 数据工程师视角

SQL 视图建议(报表层):

-- 板体失控监控视图(按固件版本分组)
CREATE OR REPLACE VIEW v_board_out_of_control_by_version AS
SELECT
    session_date,
    esc_version, ecu_version, bms_version,
    COUNT(*)                                    AS total_sessions,
    SUM(has_board_out_of_control)               AS boc_sessions,
    ROUND(AVG(has_board_out_of_control)*100, 2) AS boc_rate_pct,
    SUM(stall_points)                           AS stall_pts_total,
    SUM(invalid_speed_points)                   AS invalid_spd_total,
    SUM(esc_event_0x400000_points)              AS esc_0x400000_total
FROM surf_motion_trajectory_analysis
WHERE analyze_status = 'done'
GROUP BY session_date, esc_version, ecu_version, bms_version;

-- 故障分级分布视图
CREATE OR REPLACE VIEW v_fault_severity_dist AS
SELECT
    session_date,
    fault_severity_display,
    COUNT(*) AS cnt
FROM surf_motion_trajectory_analysis
WHERE analyze_status = 'done'
GROUP BY session_date, fault_severity_display;

-- 设备健康度视图(按遥控器 SN)
CREATE OR REPLACE VIEW v_device_health AS
SELECT
    remote_sn,
    COUNT(*)                             AS total_sessions,
    SUM(has_board_out_of_control)        AS boc_count,
    SUM(has_protocol_fault)              AS fault_count,
    MAX(session_date)                    AS last_session_date,
    MAX(esc_version)                     AS latest_esc_version
FROM surf_motion_trajectory_analysis
WHERE analyze_status = 'done'
GROUP BY remote_sn;

数据质量监控(建议纳入 cron 告警):

-- 异常重算比例过高时预警
SELECT COUNT(*) AS reanalyzed
FROM surf_motion_trajectory_analysis
WHERE analyzer_version = '1.0.3'
  AND analyzed_at > NOW() - INTERVAL 1 HOUR;

-- 失败率监控
SELECT
    SUM(sessions_failed) / SUM(sessions_scanned) AS fail_rate
FROM surf_log_analysis_job
WHERE started_at > NOW() - INTERVAL 1 DAY;

10.9 全栈架构视角

下游接口影响(如后台 API 消费分析表):

接口字段 v1.0.2 语义 v1.0.3 变化 前端适配建议
has_fault 宽 收口 原来「故障」列表会变短,需告知前端
severity 自研分 不变 不影响
fault_severity_display 无 新增 推荐用此字段驱动 UI 色标
has_board_out_of_control 无 新增 新增「失控会话」筛选入口
overvoltage_points 0x400000 同值双写 无需改
bms_fault_points 全 BMS 之和 仅故障级 ⚠️ 破坏性,前端数值会降低

建议:后台 API 在 v1.0.3 发版后增加版本字段返回 analyzer_version,前端按版本渲染说明文案。


11. 待研发确认项

# 问题 影响范围 优先级
A 0x400000 语义:真过压 vs ECU 合成 vs 协议跌落位?固件是否计划统一? ESC_EVENT_0x400000 命名、board_out_of_control 纳入是否正确 P0
B |motorSpeed| >= 20000 阈值是否在 master 固件与 motorstuck 分支一致?其他固件版本是否相同? invalid_speed_points 准确性 P0
C running_error bit6 在 motorstuck 分支是否已计划修复(映射到正确的 bit1)? 如修复,0x400000 在新固件不再出现,无需 board_out_of_control P1
D BMS bms_event 是单一事件码(等值)还是组合掩码(位与)?RC App 用等值比较,但日志里多 bit 同时置位的情况是否存在? BMS 码匹配方式 P1
E 超速告警 是否在 escEventCode 中有对应 bit?还是仅靠 motorSpeed 阈值检测? 告警分级完整性 P2
F FAULT_BOARD_ATTITUDE (0x04000000) 协议候选 tilt_fault,是否等同于「倾斜故障」?还是仅指航行姿态判断? ECU 故障展示文案 P2

12. 变更记录

版本 日期 说明
v1.0 2026-05-31 原改进方案(LOG_ANALYSIS_IMPROVEMENT_PLAN_PROTOCOL.md)
v2.0 2026-06-01 基于故障码分级表 v1.0,多领域视角完整方案;Schema v2、码表重设计、迁移策略