diff --git a/go/vehicle-gateway/internal/realtime/repository.go b/go/vehicle-gateway/internal/realtime/repository.go index 9c64c844..1b1eaba5 100644 --- a/go/vehicle-gateway/internal/realtime/repository.go +++ b/go/vehicle-gateway/internal/realtime/repository.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "sort" + "strconv" "strings" "time" @@ -155,6 +156,9 @@ func mergeFields(snapshot *Snapshot, fields map[string]any, eventMS int64) { snapshot.FieldTimesMS = map[string]int64{} } for key, value := range fields { + if key == envelope.FieldTotalMileageKM && !positiveNumber(value) { + continue + } if eventMS >= snapshot.FieldTimesMS[key] { snapshot.Fields[key] = value snapshot.FieldTimesMS[key] = eventMS @@ -162,6 +166,28 @@ func mergeFields(snapshot *Snapshot, fields map[string]any, eventMS int64) { } } +func positiveNumber(value any) bool { + switch typed := value.(type) { + case float64: + return typed > 0 + case float32: + return typed > 0 + case int: + return typed > 0 + case int64: + return typed > 0 + case uint16: + return typed > 0 + case uint32: + return typed > 0 + case string: + parsed, err := strconv.ParseFloat(strings.TrimSpace(typed), 64) + return err == nil && parsed > 0 + default: + return false + } +} + func cloneFields(fields map[string]any) map[string]any { if len(fields) == 0 { return map[string]any{} diff --git a/go/vehicle-gateway/internal/realtime/repository_test.go b/go/vehicle-gateway/internal/realtime/repository_test.go index 3d440fa0..880720ab 100644 --- a/go/vehicle-gateway/internal/realtime/repository_test.go +++ b/go/vehicle-gateway/internal/realtime/repository_test.go @@ -94,6 +94,47 @@ func TestRepositoryOnlineStatus(t *testing.T) { } } +func TestRepositoryDoesNotOverwritePositiveMileageWithZero(t *testing.T) { + repo, closeFn := newTestRepository(t) + defer closeFn() + ctx := context.Background() + + if err := repo.Update(ctx, envelope.FrameEnvelope{ + Protocol: envelope.ProtocolJT808, + VIN: "VIN001", + EventTimeMS: 1000, + ReceivedAtMS: 1100, + Fields: map[string]any{ + envelope.FieldTotalMileageKM: 12345.6, + }, + }); err != nil { + t.Fatalf("Update() error = %v", err) + } + if err := repo.Update(ctx, envelope.FrameEnvelope{ + Protocol: envelope.ProtocolJT808, + VIN: "VIN001", + EventTimeMS: 2000, + ReceivedAtMS: 2100, + Fields: map[string]any{ + envelope.FieldSpeedKMH: 22.0, + envelope.FieldTotalMileageKM: 0, + }, + }); err != nil { + t.Fatalf("Update() error = %v", err) + } + + merged, err := repo.GetMerged(ctx, "VIN001") + if err != nil { + t.Fatalf("GetMerged() error = %v", err) + } + if merged.Fields[envelope.FieldTotalMileageKM] != 12345.6 { + t.Fatalf("zero mileage overwrote positive value: %#v", merged.Fields) + } + if merged.Fields[envelope.FieldSpeedKMH] != 22.0 { + t.Fatalf("new speed should still merge: %#v", merged.Fields) + } +} + func TestHandlerReturnsMergedSnapshot(t *testing.T) { repo, closeFn := newTestRepository(t) defer closeFn()