功能:扩展开放平台氢耗溯源与合作站数据
This commit is contained in:
@@ -255,15 +255,20 @@ test -n "$MYSQL_DSN"
|
||||
/opt/lingniu-vehicle-platform/releases/$PLATFORM_RELEASE/deploy/migrations/033_open_mileage_range_snapshot.sql \
|
||||
/opt/lingniu-vehicle-platform/releases/$PLATFORM_RELEASE/deploy/migrations/034_alert_notification_retry.sql \
|
||||
/opt/lingniu-vehicle-platform/releases/$PLATFORM_RELEASE/deploy/migrations/035_alert_notification_dispatch.sql \
|
||||
/opt/lingniu-vehicle-platform/releases/$PLATFORM_RELEASE/deploy/migrations/036_alert_rule_archive.sql
|
||||
/opt/lingniu-vehicle-platform/releases/$PLATFORM_RELEASE/deploy/migrations/036_alert_rule_archive.sql \
|
||||
/opt/lingniu-vehicle-platform/releases/$PLATFORM_RELEASE/deploy/migrations/042_hydrogen_segment_stream_state.sql
|
||||
```
|
||||
|
||||
Migration `023` creates vehicle open-platform appKey records, per-vehicle grant intervals, precomputed daily energy, and immutable API/admin audits. Apply it before serving `/api/v1/vehicles/*` or enabling `lingniu-vehicle-open-stat.timer`.
|
||||
|
||||
Migration `029` creates the local VIN-to-tank-capacity projection and pressure-calculation evidence columns. Migration `030` adds the per-refuel-cycle low-water mark used to prevent pressure and temperature oscillation from being counted repeatedly. The stat writer synchronizes capacity from `ln_asset_management.vehicle_info.vehicle_model_id → vehicle_model.tank_capacity` at startup and every six hours, then serves frame-time lookups from process memory.
|
||||
Migration `029` creates the local VIN-to-tank-capacity projection and pressure-calculation evidence columns. Migration `043` creates the effective-dated VIN energy-parameter table. Migration `030` adds the per-refuel-cycle low-water mark used to prevent pressure and temperature oscillation from being counted repeatedly. At startup and every six hours, the stat writer projects `vehicle_model.tank_capacity` and `vehicle_model.battery_capacity` through `vehicle_info.vehicle_model_id` to VIN-scoped calculation parameters. Rated battery energy is therefore model-specific rather than hard-coded by tonnage.
|
||||
|
||||
Migration `038` adds daily pure-hydrogen mileage to the elected mileage table and its per-source evidence table. Deploy the migration before the updated stat writer and API so GB32960 `engine_work_state=2` and Yutong `TRIANGLE_STATE=4/11` intervals can be accumulated and returned as `pureHydrogenMileageKm`.
|
||||
|
||||
Migration `042` creates the per-VIN/per-day bounded state used by the GB32960 hydrogen segment stream. Apply it before switching `vehicle-stat-writer`; the previous writer ignores the additive table, so rolling back the binary does not require dropping it.
|
||||
|
||||
For the first switch to the segment stream, stop `lingniu-go-stat-writer.service`, run the current-day `open-platform-stat` rebuild once with `-seed-stream-state`, then switch and restart the writer. The seed transaction stores the rebuilt total and the exact last event-time watermark for every VIN, preventing already rebuilt Kafka backlog from being counted again. Do not pass this flag to the normal completed-day timer.
|
||||
|
||||
Migration `031` adds explicit metric, geofence, stationary and offline automation triggers. It must be applied before the API and both alert evaluators are restarted; geofence rules deliberately require one positioning protocol to avoid multi-source coordinate drift and duplicate boundary events.
|
||||
|
||||
Migration `035` adds rule recipient-group references plus notification leases and dispatch indexes. Apply it before publishing an API/evaluator that writes `notification_targets_json` or enabling `lingniu-vehicle-alert-notification-dispatcher`. The dispatcher must remain disabled until every configured gateway has passed a signed canary that returns a non-empty provider message ID.
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# 氢耗可信计算与溯源(V2)
|
||||
|
||||
## 结果口径
|
||||
|
||||
- `raw_consumption_kg`:物理耗氢量,仅由GB/T 32960氢压、氢温、车型储氢容积经NIST实气压缩因子换算后,按有效区间质量下降累计。
|
||||
- `battery_equivalent_kg`:动力电池净放电能量折算氢量。仅使用已确认的车型/VIN电池容量;停车充电区间不参与计算。
|
||||
- `soc_balanced_consumption_kg`:SOC平衡氢耗,用于把不同首尾SOC的运行区间调整到可比较口径,不覆盖物理耗氢量。
|
||||
|
||||
压力—质量公式:
|
||||
|
||||
```text
|
||||
Z = 1 + Σ ai × (100 / Tk)^bi × P^ci
|
||||
m = P × 1000 × 0.00201588 × V / (8.314472 × Tk × Z)
|
||||
```
|
||||
|
||||
SOC能量守恒公式:
|
||||
|
||||
```text
|
||||
E_battery_discharge = C_battery × (SOC_start - SOC_end) / 100
|
||||
m_battery_equivalent = E_battery_discharge / 16
|
||||
m_soc_balanced = m_raw_hydrogen + m_battery_equivalent
|
||||
```
|
||||
|
||||
电池净放电为车辆提供了额外能量,因此等效总能耗应增加;电池净充电时该项为负。邮件示例中将净放电量从物理耗氢中扣除,不符合能量守恒,V2不采用该符号方向。
|
||||
|
||||
## 区间规则
|
||||
|
||||
1. 上电后等待60秒、下电前提前60秒,端点使用连续5条有效样本的中位质量对应报文。
|
||||
2. 停车充电由整车充电状态与车辆状态识别,充电区间切断且不参与SOC修正;充电结束后重新建立基线。
|
||||
3. 压力累计上升超过3MPa且保持5分钟识别为加氢;无连续报文时仍保留质量回升阈值作为补充识别。
|
||||
4. 纯电状态30分钟内压力下降超过5MPa的样本标记无效。
|
||||
5. 数据间隔超过5分钟、质量异常下降、运行模式切换均切断区间。
|
||||
6. 每个有效区间至少10条样本;首尾各5条取中位质量以抑制单点波动。
|
||||
|
||||
## 溯源数据
|
||||
|
||||
每日结果保留算法版本、参数JSON和区间证据JSON。每个区间包含起止时间、起止原始报文ID、数据源、压力、温度、质量、SOC、里程、分项公式结果、样本数及质量原因。统计页面的“可溯源”入口展示相同信息。
|
||||
|
||||
车型/VIN的动力电池容量及氢电换算系数由 `vehicle_hydrogen_energy_parameter` 维护。未配置或未经确认时,系统仍输出物理耗氢,但不生成SOC平衡结果。
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
cd vehicle-data-platform/apps/api
|
||||
go test ./internal/openplatform -run 'TestPressureHydrogenMassNISTValidationGridHasAtLeast100Samples|TestTrustedHydrogenCalculatorProducesTraceableSOCBalancedResultFrom120Samples' -v
|
||||
|
||||
cd ../web
|
||||
NODE_OPTIONS=--localstorage-file=/tmp/codex-vitest-localstorage pnpm exec vitest run src/v2/pages/StatisticsPage.test.tsx
|
||||
```
|
||||
|
||||
压力—质量测试使用12个压力点与10个温度点,共120个独立样本;日计算测试使用120条连续车辆报文,验证上下电窗口、区间证据和SOC能量守恒。
|
||||
@@ -343,35 +343,40 @@ PUT /portal-api/account/password
|
||||
|
||||
## 用氢量统计
|
||||
|
||||
当天数据由 `vehicle-stat-writer` 直接消费 GB32960 fields Kafka 流,统一采用压力法:
|
||||
用氢量只接收 `GB32960` 帧,不读取 JT808、宇通 MQTT 或其他协议。统一采用压力法:
|
||||
|
||||
```text
|
||||
剩余氢量 kg = NIST氢气密度(最高氢压 MPa, 最高氢温 ℃) × 车型氢瓶容量 L / 1000
|
||||
```
|
||||
|
||||
- 不使用广东扩展直接上报的剩余氢量、飞驰剩余百分比或百公里氢耗推算质量;
|
||||
- `vehicle-stat-writer` 每六小时从 `ln_asset_management.vehicle_info → vehicle_model.tank_capacity` 同步 VIN 容积到本地 `vehicle_hydrogen_tank_capacity`;
|
||||
- 同步完成后一次性加载到进程内存,高频帧计算不访问资产库、Redis或MySQL;缺少VIN容积时拒绝生成氢量;
|
||||
- 压力法使用 NIST 实氢气密度方程,允许 0–70 MPa、220–1000 K 的输入范围。
|
||||
- 不使用广东扩展直接上报的剩余氢量、飞驰剩余百分比、宇通 MQTT 剩余氢量或百公里氢耗推算质量;
|
||||
- `vehicle-stat-writer` 启动时及每六小时从 `ln_asset_management.vehicle_info → vehicle_model` 同步车型参数:`tank_capacity` 按 VIN 投影到 `vehicle_hydrogen_tank_capacity`,`battery_capacity` 按 VIN 投影到 `vehicle_hydrogen_energy_parameter`;
|
||||
- 额定电量按车辆关联的具体车型读取,不按吨位写死;后续生效日期更晚的人工业务参数仍可覆盖自动同步基线;
|
||||
- 储氢容积同步后一次性加载到统计进程内存,高频帧计算不访问资产库、Redis或MySQL;额定电量由日氢耗任务按VIN和统计日期读取;缺少VIN容积时拒绝生成氢量;
|
||||
- 压力法使用 NIST 实氢气密度方程,允许 0–70 MPa、220–1000 K 的输入范围;
|
||||
- GB32960 上报的是“最高氢压”和“最高氢温”,两者不保证来自同一探头,也不保证代表静置平衡后的储氢瓶状态。因此压力法结果是车载信号估算值,不能用加氢站计量值直接反标公式系数。
|
||||
|
||||
每个有效样本在同一个 MySQL 事务中更新
|
||||
`vehicle_open_hydrogen_stream_state`,并投影到
|
||||
当天准实时数据由 `vehicle-stat-writer` 消费 GB32960 fields Kafka 流。每个有效样本在同一个 MySQL 事务中更新
|
||||
`vehicle_open_hydrogen_segment_stream_state`,并投影到
|
||||
`vehicle_open_daily_energy`。Kafka offset 只在事务提交成功后提交,因此服务重启或消息重放不会重复累计。状态按
|
||||
`VIN + 日期 + source_endpoint` 保存,每条消息只做常数次数据库操作,不扫描当天历史数据。
|
||||
`VIN + 日期` 保存并合并全部连接端点;每条消息只做常数次数据库操作,不扫描当天历史数据。
|
||||
|
||||
统计规则:
|
||||
|
||||
- 先按 VIN 和 `source_endpoint` 分别统计,避免多个采集源的序列交错产生虚假变化;
|
||||
- 同一 VIN 优先采用质量正常、样本数量更多的数据源,并记录入库;
|
||||
- 每次加氢之间维护质量低水位,只累计新的有效最低质量,避免压力温度波动反复计量;
|
||||
- 已结束日期先按 VIN 合并全部 `source_endpoint`,同一事件时间只保留一条可信样本;连接端口仅用于追踪,不再作为互斥统计来源;
|
||||
- 只有相邻两条样本都确认燃料电池处于工作状态时,才纳入工作段;优先采用广东扩展 `engine_work_state=2`,缺失时以燃料电池电流大于 `1 A` 作为回退;完全没有工作区间的日期标记为 `NO_DATA`;
|
||||
- 加氢、超过 5 分钟的数据断点、燃料电池停机和异常跳变会切分工作段;每段开始与结束各取 5 条样本的氢质量中位数做差,再累加为当日用氢量;
|
||||
- 每个可计算工作段至少需要 10 条有效样本;段首尾差值未超过动态噪声阈值时不累计;
|
||||
- 燃料电池未工作时,下降的压力质量只更新防重复计算的低水位,不降低对外剩余氢量;压力或质量恢复上升后再更新剩余量;
|
||||
- 抖动阈值按该VIN容积对应的 `0.2 MPa` 质量变化动态计算,最小 `0.05 kg`、最大 `1 kg`;
|
||||
- 质量相对本轮低水位上升超过 `max(1 kg, 当前质量×5%)` 时识别为加氢并重置低水位;
|
||||
- 相邻样本在 60 秒内无工作耗氢却恢复至少 `8 MPa` 时,按压力信号恢复处理,重置低水位但不增加加氢次数;
|
||||
- 单次下降超过 `OPEN_STAT_HYDROGEN_MAX_DROP_KG` 时过滤并标记 `SUSPECT`,默认 `20 kg`;
|
||||
- 至少两条有效样本才标记 `OK`;
|
||||
- 对外只返回 `quality_status=OK` 的结果。
|
||||
|
||||
当天接口结果是准实时的进行中累计值。乱序、重复和迟到样本不会覆盖更新的状态;跨日迟到数据由
|
||||
`open-platform-stat` 夜间批处理兜底。批处理默认重算昨天和前天,以修正迟到上报,并作为已结束日期的权威结果。
|
||||
分段计算由流式状态机直接执行:每车仅保留当前段的首 5 帧、尾 5 帧、低水位、上一帧和事件时间水位,状态不会随当天帧数增长。水位只覆盖当前自然日 `00:00:00–24:00:00`,不向前后日期扩窗,也不借用次日帧;早于或等于当前事件时间水位的延迟帧直接忽略。流式结果采用与日终相同的工作段、中位数、加氢和异常跳变规则;已结束日期仍由 `open-platform-stat` 重算,作为最终对账结果。
|
||||
|
||||
历史重放只接收事件时间和接收时间同时落在统计日内的 GB32960 帧。每个统计日先读取有数据且配置了氢瓶容积的 VIN,再在同一进程内按单车单日并行查询;默认 4 个工作线程,全部车辆成功后才一次性事务替换当天结果。单车帧按 `event_time` 排序并单遍去重;JSON 只读取压力、温度和工作状态字段,不展开整帧,从而减少全车排序与反序列化开销。可通过 `-vin-workers` 或 `OPEN_STAT_VIN_WORKERS` 调整工作线程数。
|
||||
|
||||
```bash
|
||||
/opt/lingniu-vehicle-platform/current/open-platform-stat \
|
||||
@@ -380,3 +385,6 @@ PUT /portal-api/account/password
|
||||
|
||||
systemctl enable --now lingniu-vehicle-open-stat.timer
|
||||
```
|
||||
|
||||
流式算法首次切换时,先停止 `lingniu-go-stat-writer.service`,再对当天执行一次
|
||||
`-seed-stream-state`。该参数会在同一个 MySQL 事务中替换当天结果并写入每车最后事件时间水位;恢复 Kafka 消费后,已经包含在重算结果中的 backlog 不会再次累计。正常日终任务不使用此参数。
|
||||
|
||||
Reference in New Issue
Block a user