From a96490618cb9b9e8fa2ecd6626a5f46abf3425de Mon Sep 17 00:00:00 2001 From: lingniu Date: Fri, 3 Jul 2026 13:17:31 +0800 Subject: [PATCH] fix(go): collapse gb32960 auxiliary snapshot --- .../internal/realtime/repository.go | 23 ++++++++- .../internal/realtime/repository_test.go | 47 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/go/vehicle-gateway/internal/realtime/repository.go b/go/vehicle-gateway/internal/realtime/repository.go index 54e00c7d..e8bfeab2 100644 --- a/go/vehicle-gateway/internal/realtime/repository.go +++ b/go/vehicle-gateway/internal/realtime/repository.go @@ -482,14 +482,19 @@ func normalizeGB32960RealtimeParsed(parsed map[string]any) { } for _, unit := range units { unitMap, ok := unit.(map[string]any) - if !ok || strings.TrimSpace(strconvAny(unitMap["name"])) != "gd_fc_stack" { + if !ok { continue } value, ok := unitMap["value"].(map[string]any) if !ok { continue } - collapseGDFCStackValue(value) + switch strings.TrimSpace(strconvAny(unitMap["name"])) { + case "gd_fc_stack": + collapseGDFCStackValue(value) + case "gd_fc_auxiliary": + collapseGDFCAuxiliaryValue(value) + } } } @@ -528,6 +533,20 @@ func isGDFCStackFragmentField(key string) bool { } } +func collapseGDFCAuxiliaryValue(value map[string]any) { + subsystems, ok := asAnySlice(value["subsystems"]) + if !ok || len(subsystems) == 0 { + return + } + latest := subsystems[len(subsystems)-1] + subsystem, ok := latest.(map[string]any) + if !ok { + return + } + value["subsystem_count"] = 1 + value["subsystems"] = []any{cloneMap(subsystem)} +} + func fieldTimes(fields map[string]any, eventMS int64) map[string]int64 { out := make(map[string]int64, len(fields)) for key := range fields { diff --git a/go/vehicle-gateway/internal/realtime/repository_test.go b/go/vehicle-gateway/internal/realtime/repository_test.go index c53eed8e..ad840ec2 100644 --- a/go/vehicle-gateway/internal/realtime/repository_test.go +++ b/go/vehicle-gateway/internal/realtime/repository_test.go @@ -271,6 +271,53 @@ func TestRepositoryCollapsesGB32960VendorStackFragmentsIntoSingleRealtimeSummary } } +func TestRepositoryReplacesGB32960VendorAuxiliarySubsystemsWithLatestState(t *testing.T) { + repo, closeFn := newTestRepository(t) + defer closeFn() + ctx := context.Background() + + for i, voltage := range []float64{372, 348} { + if err := repo.Update(ctx, envelope.FrameEnvelope{ + Protocol: envelope.ProtocolGB32960, + VIN: "VIN001", + EventTimeMS: int64(1000 + i), + ReceivedAtMS: int64(1100 + i), + Parsed: map[string]any{ + "data_units": []any{ + map[string]any{ + "type": "0x31", + "name": "gd_fc_auxiliary", + "value": map[string]any{ + "subsystem_count": 1, + "subsystems": []any{map[string]any{ + "air_compressor_motor_voltage_v": voltage, + "water_pump_voltage_v": voltage, + }}, + }, + }, + }, + }, + }); err != nil { + t.Fatalf("Update() error = %v", err) + } + } + + realtimeRaw, err := repo.GetRealtimeRaw(ctx, "VIN001", envelope.ProtocolGB32960) + if err != nil { + t.Fatalf("GetRealtimeRaw() error = %v", err) + } + unit := realtimeRaw["data_units"].([]any)[0].(map[string]any) + value := unit["value"].(map[string]any) + subsystems := value["subsystems"].([]any) + if len(subsystems) != 1 { + t.Fatalf("auxiliary subsystems should keep current state, not append historical frames: %#v", subsystems) + } + subsystem := subsystems[0].(map[string]any) + if subsystem["air_compressor_motor_voltage_v"] != float64(348) { + t.Fatalf("auxiliary subsystem should keep latest values: %#v", subsystem) + } +} + func TestRepositoryWritesRealtimeKVHashes(t *testing.T) { repo, closeFn := newTestRepository(t) defer closeFn()