5.7 KiB
Multi-Source Mileage Statistics Design
Goal
Vehicle mileage statistics must keep every protocol source that reports data, while still exposing one reliable default result for BI and API consumers. The design separates source facts from elected business results so multi-source JT808, GB32960, and Yutong MQTT data can be audited, corrected, and re-elected without losing raw source evidence.
Current Problem
The current vehicle_daily_mileage table stores one row per vin + stat_date + protocol. That prevents duplicate counting, but it also forces source election too early. When multiple source platforms report the same VIN, mixing them can create false mileage jumps; selecting only one source can undercount when a reliable alternative exists.
The observed example was LA9GG64L7PBAF4001 on 2026-07-08 for JT808. Two source IPs reported different total mileage ranges. The correct model is to retain each source as its own candidate and then elect the recommended candidate for default queries.
Tables
vehicle_data_source
Stores source platform metadata discovered from incoming data and later maintained by operators.
Identity:
protocolsource_ip
Fields:
idprotocolsource_iplatest_source_endpointplatform_nametrust_priorityenabledfirst_seen_atlatest_seen_atremarkcreated_atupdated_at
Rules:
protocol + source_ipis unique.- Ports are not part of source identity because sender ports change often.
- Runtime may update
latest_source_endpoint,first_seen_at, andlatest_seen_at. - Runtime must not overwrite manually maintained
platform_name,trust_priority,enabled, orremark.
vehicle_daily_mileage_source
Stores one daily candidate per source.
Identity:
vinstat_dateprotocolsource_key
Fields:
vinstat_dateprotocolsource_keysource_ipsource_endpointphonedevice_idplatform_namefirst_total_mileage_kmlatest_total_mileage_kmdaily_mileage_kmsample_countfirst_event_timelatest_event_timequality_statusquality_reasonis_selectedcreated_atupdated_at
Rules:
- Candidate rows are facts by source. They should not be deleted just because they are not selected.
source_keyis normalized as protocol-specific device identity plus source IP. For JT808 that is usuallyphone@source_ip.- A candidate can be marked invalid without being removed.
- Candidate statistics never mix different
source_keyvalues.
vehicle_daily_mileage
Keeps the existing default query surface for BI and APIs.
Rules:
- One row per
vin + stat_date + protocol. - The row is a projection from one selected
vehicle_daily_mileage_sourcerow. - It stores selected source metadata for fast default queries.
Election
The default selected source is chosen from candidate rows. Selection priority:
- Source must be enabled in
vehicle_data_source. - Prefer lower
trust_prioritywhen configured. - Prefer candidates with a valid same-source previous-day baseline.
- Reject impossible deltas, such as negative mileage or mileage above the configured daily maximum.
- Prefer more samples when priority and quality are tied.
- Prefer later
latest_event_timewhen still tied.
If trusted source A is missing on a day, source B can be selected only if B has its own previous-day baseline. The system must not calculate B today - A yesterday.
Data Flow
- Gateway/history/realtime parsing continues to emit protocol fields without non-protocol synthetic fields.
- Stats writer extracts protocol-specific total mileage from the existing fields event.
- Stats writer upserts
vehicle_data_sourcebyprotocol + source_ip. - Stats writer upserts
vehicle_daily_mileage_sourcebyvin + stat_date + protocol + source_key. - Election updates
vehicle_daily_mileagefrom candidates. - History backfill can rebuild candidates from TDengine raw frames and then rebuild final projections.
API Behavior
Default daily mileage API continues to read vehicle_daily_mileage.
Future query extension:
includeCandidates=truereturns all candidates for each selected row.- Candidate rows include
platform_name,source_ip,source_endpoint,quality_status, andquality_reason.
Edge Cases
- Multiple ports from the same platform source are collapsed by source IP.
- A new source with no previous-day baseline is stored as a candidate with
NO_PREVIOUS_BASELINEand is not selected by default. - A source that reports a total-mileage reset is stored as a candidate with an invalid quality status until a clear policy is applied.
- If all candidates are invalid, no final row is projected for that VIN/day/protocol.
- Manual platform names and priority changes should allow re-election without reparsing raw data.
Performance
- Real-time writes are small upserts keyed by indexed dimensions.
- Default BI/API reads stay fast because they read the final projection table.
- Candidate/audit queries are opt-in.
- Backfill aggregates by source and date instead of writing every raw frame to MySQL.
Testing
Automated tests should cover:
- Source IP extraction and endpoint normalization.
- Source table upsert preserving manually maintained fields.
- Candidate stats never mixing different source keys.
- Election choosing the continuous source for
LA9GG64L7PBAF4001-style data. - Election skipping
B today - A yesterday. - Backfill rebuilding candidates and final projection.
Rollout
- Add schemas and repository helpers.
- Write candidate rows in stats writer and backfill.
- Add election projection into
vehicle_daily_mileage. - Rebuild
2026-07-08JT808 statistics and verifyLA9GG64L7PBAF4001. - Deploy to ECS and monitor stat-writer lag and write errors.