feat: build vehicle data platform and production pipeline

This commit is contained in:
lingniu
2026-07-14 12:35:33 +08:00
parent b452be3b94
commit bb59303a4b
270 changed files with 88016 additions and 1975 deletions

View File

@@ -0,0 +1,42 @@
import type { HistoryLocationRow, TrackPlaybackEvent, TrackPlaybackResponse } from '../../api/types';
export function formatDuration(seconds: number) {
const safe = Math.max(0, Math.floor(seconds || 0));
const hours = Math.floor(safe / 3600);
const minutes = Math.floor((safe % 3600) / 60);
const remainder = safe % 60;
return [hours, minutes, remainder].map((value) => String(value).padStart(2, '0')).join(':');
}
export function sampledEventIndex(event: TrackPlaybackEvent, sampledCount: number, originalCount: number) {
if (sampledCount <= 1 || originalCount <= 1) return 0;
if (Number.isInteger(event.sampledIndex) && event.sampledIndex >= 0) {
return Math.min(sampledCount - 1, event.sampledIndex);
}
return Math.max(0, Math.min(sampledCount - 1, Math.round(event.index * (sampledCount - 1) / (originalCount - 1))));
}
function csvCell(value: unknown) {
const text = String(value ?? '');
return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
}
export function trackCsv(track: TrackPlaybackResponse) {
const headers = ['VIN', '车牌', '协议', '设备时间', '服务时间', '经度', '纬度', '速度(km/h)', '总里程(km)'];
const rows = track.points.map((point) => [point.vin, point.plate, point.protocol, point.deviceTime, point.serverTime, point.longitude, point.latitude, point.speedKmh, point.totalMileageKm]);
return `\uFEFF${[headers, ...rows].map((row) => row.map(csvCell).join(',')).join('\n')}`;
}
export function downloadTrackCsv(track: TrackPlaybackResponse) {
const blob = new Blob([trackCsv(track)], { type: 'text/csv;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `track-${track.plate || track.vin}-${track.summary.startTime.slice(0, 10) || 'latest'}.csv`;
link.click();
URL.revokeObjectURL(url);
}
export function validTrackPoints(points: HistoryLocationRow[]) {
return points.filter((point) => Number.isFinite(point.longitude) && Number.isFinite(point.latitude) && point.longitude >= 73 && point.longitude <= 135 && point.latitude >= 18 && point.latitude <= 54);
}