fix(go): normalize gb32960 realtime vendor fragments
This commit is contained in:
@@ -247,6 +247,61 @@ func TestSnapshotWriterMergesGB32960ParsedJSONAcrossSplitRealtimeFrames(t *testi
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshotWriterCollapsesGB32960StackFragmentsWhenMergingParsedJSON(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
writer := NewSnapshotWriter(db)
|
||||
|
||||
existing := `{"data_units":[{"type":"0x30","name":"gd_fc_stack","value":{"stack_count":1,"summaries":[{"cell_count":432,"stack_water_outlet_temp_c":65,"frame_cell_start":201,"frame_cell_count":200}]}}]}`
|
||||
mock.ExpectQuery("SELECT parsed_json FROM vehicle_realtime_snapshot WHERE protocol = \\? AND vin = \\?").
|
||||
WithArgs("GB32960", "VIN001").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"parsed_json"}).AddRow(existing))
|
||||
mock.ExpectExec("INSERT INTO vehicle_realtime_snapshot").
|
||||
WithArgs(
|
||||
"GB32960",
|
||||
"VIN001",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
jsonGDFCStackSummaryArg{},
|
||||
sqlmock.AnyArg(),
|
||||
sqlmock.AnyArg(),
|
||||
sqlmock.AnyArg(),
|
||||
).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
err = writer.Update(context.Background(), envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolGB32960,
|
||||
MessageID: "0x02",
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: 1782918600000,
|
||||
ReceivedAtMS: 1782918601000,
|
||||
Parsed: map[string]any{
|
||||
"data_units": []any{
|
||||
map[string]any{
|
||||
"type": "0x30",
|
||||
"name": "gd_fc_stack",
|
||||
"value": map[string]any{
|
||||
"stack_count": 1,
|
||||
"summaries": []any{
|
||||
map[string]any{"cell_count": 432, "stack_water_outlet_temp_c": 63, "frame_cell_start": 401, "frame_cell_count": 32},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatalf("sql expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRealtimeUpsertsDoNotOverwriteWithOlderEventTime(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
@@ -641,6 +696,48 @@ type jsonDataUnitsArg struct {
|
||||
types []string
|
||||
}
|
||||
|
||||
type jsonGDFCStackSummaryArg struct{}
|
||||
|
||||
func (jsonGDFCStackSummaryArg) Match(value driver.Value) bool {
|
||||
text, ok := value.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
var parsed map[string]any
|
||||
if err := json.Unmarshal([]byte(text), &parsed); err != nil {
|
||||
return false
|
||||
}
|
||||
units, ok := parsed["data_units"].([]any)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for _, unit := range units {
|
||||
unitMap, ok := unit.(map[string]any)
|
||||
if !ok || unitMap["name"] != "gd_fc_stack" {
|
||||
continue
|
||||
}
|
||||
valueMap, ok := unitMap["value"].(map[string]any)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
summaries, ok := valueMap["summaries"].([]any)
|
||||
if !ok || len(summaries) != 1 {
|
||||
return false
|
||||
}
|
||||
summary, ok := summaries[0].(map[string]any)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for _, fragmentOnly := range []string{"frame_cell_start", "frame_cell_count", "frame_max_cell_voltage_v", "frame_min_cell_voltage_v"} {
|
||||
if _, ok := summary[fragmentOnly]; ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return summary["stack_water_outlet_temp_c"] == float64(63)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a jsonDataUnitsArg) Match(value driver.Value) bool {
|
||||
text, ok := value.(string)
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user