fix: distinguish track mileage evidence
This commit is contained in:
@@ -1036,7 +1036,7 @@ func (m *MockStore) HistoryLocationsFromTDengine(ctx context.Context, query url.
|
||||
VIN: row.VIN, Plate: row.Plate, Protocol: row.Protocol,
|
||||
Longitude: row.Longitude - float64(remaining)*0.0062,
|
||||
Latitude: row.Latitude - float64(remaining)*0.0021 + float64(index%3)*0.0008,
|
||||
SpeedKmh: speed, TotalMileageKm: row.TotalMileageKm - float64(remaining)*1.35,
|
||||
SpeedKmh: speed, TotalMileageKm: row.TotalMileageKm - float64(remaining)*1.35, MileageAvailable: true,
|
||||
DeviceTime: observedAt, ServerTime: observedAt,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ type TrackPlaybackSummary struct {
|
||||
StartTime string `json:"startTime"`
|
||||
EndTime string `json:"endTime"`
|
||||
DistanceKm float64 `json:"distanceKm"`
|
||||
DistanceMethod string `json:"distanceMethod"`
|
||||
DurationSeconds int64 `json:"durationSeconds"`
|
||||
AverageSpeedKmh float64 `json:"averageSpeedKmh"`
|
||||
MaximumSpeedKmh float64 `json:"maximumSpeedKmh"`
|
||||
@@ -1304,19 +1305,20 @@ type VehicleRealtimeRow struct {
|
||||
}
|
||||
|
||||
type HistoryLocationRow struct {
|
||||
VIN string `json:"vin"`
|
||||
Plate string `json:"plate"`
|
||||
Protocol string `json:"protocol"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
SpeedKmh float64 `json:"speedKmh"`
|
||||
SOCPercent float64 `json:"socPercent"`
|
||||
SOCAvailable bool `json:"socAvailable"`
|
||||
DirectionDeg *int64 `json:"directionDeg,omitempty"`
|
||||
AlarmFlag *int64 `json:"alarmFlag,omitempty"`
|
||||
TotalMileageKm float64 `json:"totalMileageKm"`
|
||||
DeviceTime string `json:"deviceTime"`
|
||||
ServerTime string `json:"serverTime"`
|
||||
VIN string `json:"vin"`
|
||||
Plate string `json:"plate"`
|
||||
Protocol string `json:"protocol"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
SpeedKmh float64 `json:"speedKmh"`
|
||||
SOCPercent float64 `json:"socPercent"`
|
||||
SOCAvailable bool `json:"socAvailable"`
|
||||
DirectionDeg *int64 `json:"directionDeg,omitempty"`
|
||||
AlarmFlag *int64 `json:"alarmFlag,omitempty"`
|
||||
TotalMileageKm float64 `json:"totalMileageKm"`
|
||||
MileageAvailable bool `json:"mileageAvailable"`
|
||||
DeviceTime string `json:"deviceTime"`
|
||||
ServerTime string `json:"serverTime"`
|
||||
}
|
||||
|
||||
type RawFrameRow struct {
|
||||
|
||||
@@ -616,7 +616,7 @@ func (s *ProductionStore) HistoryLocations(ctx context.Context, query url.Values
|
||||
for _, row := range realtime.Items {
|
||||
items = append(items, HistoryLocationRow{
|
||||
VIN: row.VIN, Plate: row.Plate, Protocol: row.Protocol, Longitude: row.Longitude, Latitude: row.Latitude,
|
||||
SpeedKmh: row.SpeedKmh, SOCPercent: row.SOCPercent, SOCAvailable: row.SOCPercent > 0, TotalMileageKm: row.TotalMileageKm, DeviceTime: row.LastSeen, ServerTime: row.LastSeen,
|
||||
SpeedKmh: row.SpeedKmh, SOCPercent: row.SOCPercent, SOCAvailable: row.SOCPercent > 0, TotalMileageKm: row.TotalMileageKm, MileageAvailable: row.TotalMileageKm > 0, DeviceTime: row.LastSeen, ServerTime: row.LastSeen,
|
||||
})
|
||||
}
|
||||
return Page[HistoryLocationRow]{Items: items, Total: realtime.Total, Limit: realtime.Limit, Offset: realtime.Offset}, nil
|
||||
@@ -675,6 +675,7 @@ func (s *ProductionStore) HistoryLocationsFromTDengine(ctx context.Context, quer
|
||||
row.DirectionDeg = nullInt64Pointer(direction)
|
||||
row.AlarmFlag = nullInt64Pointer(alarm)
|
||||
row.TotalMileageKm = nullFloat64(mileage)
|
||||
row.MileageAvailable = mileage.Valid
|
||||
row.DeviceTime = time.UnixMilli(ts).UTC().Format(time.RFC3339Nano)
|
||||
row.ServerTime = row.DeviceTime
|
||||
if receivedAt.Valid {
|
||||
|
||||
@@ -3474,10 +3474,14 @@ func summarizeTrack(points []HistoryLocationRow) TrackPlaybackSummary {
|
||||
summary.AverageSpeedKmh = speedTotal / float64(speedCount)
|
||||
}
|
||||
mileageDistance := points[len(points)-1].TotalMileageKm - points[0].TotalMileageKm
|
||||
if points[0].Protocol == points[len(points)-1].Protocol && mileageDistance >= 0 && mileageDistance < 10000 {
|
||||
if points[0].Protocol == points[len(points)-1].Protocol &&
|
||||
points[0].MileageAvailable && points[len(points)-1].MileageAvailable &&
|
||||
mileageDistance >= 0 && mileageDistance < 10000 {
|
||||
summary.DistanceKm = mileageDistance
|
||||
summary.DistanceMethod = "odometer"
|
||||
} else {
|
||||
summary.DistanceKm = coordinateDistance
|
||||
summary.DistanceMethod = "coordinates"
|
||||
}
|
||||
start, startOK := parseVehicleServiceTime(summary.StartTime)
|
||||
end, endOK := parseVehicleServiceTime(summary.EndTime)
|
||||
|
||||
@@ -377,15 +377,26 @@ func TestCompleteProtocolStatsIncludesCanonicalSlots(t *testing.T) {
|
||||
|
||||
func TestSummarizeTrackUsesMileageAndChronology(t *testing.T) {
|
||||
points := []HistoryLocationRow{
|
||||
{DeviceTime: "2026-07-03 10:00:00", TotalMileageKm: 100, SpeedKmh: 0, Longitude: 113.1, Latitude: 23.1},
|
||||
{DeviceTime: "2026-07-03 10:30:00", TotalMileageKm: 112.5, SpeedKmh: 50, Longitude: 113.2, Latitude: 23.2},
|
||||
{DeviceTime: "2026-07-03 10:00:00", TotalMileageKm: 100, MileageAvailable: true, SpeedKmh: 0, Longitude: 113.1, Latitude: 23.1},
|
||||
{DeviceTime: "2026-07-03 10:30:00", TotalMileageKm: 112.5, MileageAvailable: true, SpeedKmh: 50, Longitude: 113.2, Latitude: 23.2},
|
||||
}
|
||||
summary := summarizeTrack(points)
|
||||
if summary.DistanceKm != 12.5 || summary.DurationSeconds != 1800 || summary.MaximumSpeedKmh != 50 {
|
||||
if summary.DistanceKm != 12.5 || summary.DistanceMethod != "odometer" || summary.DurationSeconds != 1800 || summary.MaximumSpeedKmh != 50 {
|
||||
t.Fatalf("unexpected track summary: %+v", summary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummarizeTrackUsesCoordinatesWhenProtocolMileageIsMissing(t *testing.T) {
|
||||
points := []HistoryLocationRow{
|
||||
{Protocol: "JT808", DeviceTime: "2026-07-03 10:00:00", Longitude: 113.1, Latitude: 23.1},
|
||||
{Protocol: "JT808", DeviceTime: "2026-07-03 10:01:00", Longitude: 113.101, Latitude: 23.101},
|
||||
}
|
||||
summary := summarizeTrack(points)
|
||||
if summary.DistanceMethod != "coordinates" || summary.DistanceKm <= 0 {
|
||||
t.Fatalf("missing protocol mileage must use an explicitly labelled coordinate distance: %+v", summary)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackEventKeepsSynchronizedTelemetryEvidence(t *testing.T) {
|
||||
direction := int64(88)
|
||||
alarm := int64(2)
|
||||
|
||||
Reference in New Issue
Block a user