feat: parse gb32960 realtime data units

This commit is contained in:
lingniu
2026-07-01 22:21:21 +08:00
parent e0068a750d
commit 5f4f4fd124
2 changed files with 329 additions and 1 deletions

View File

@@ -2,11 +2,14 @@ package gb32960
import (
"encoding/hex"
"fmt"
"testing"
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
)
const realFuelCellFrame = "232302FE4C423941333241323152304C53313730370102ED1A070116091C0102030100000008297D161F27104C020007D0000002010104564E204E205815CA271003000A000000B40002666902800200000100A101000400FFFF001205000733444601E2B5DB06013A0F6C018E0F4001014B01054A07000000000000000000080101161F271000900001900F520F530F520F640F660F650F650F650F650F660F650F650F640F640F550F550F540F560F580F570F540F570F560F550F5A0F550F580F570F5C0F5F0F5E0F610F5E0F5D0F5E0F5F0F5E0F5D0F610F5F0F610F600F600F610F610F610F600F630F630F600F610F610F610F620F620F610F6A0F6C0F6B0F6A0F6C0F6B0F6A0F6B0F6B0F6B0F6A0F610F630F630F630F640F660F640F630F630F630F580F5A0F580F5B0F550F580F5A0F5A0F590F580F5A0F620F650F650F650F660F640F640F640F640F640F660F650F640F650F660F650F670F670F670F670F630F620F680F670F650F650F670F650F610F5F0F600F620F5E0F5C0F590F4C0F490F480F440F470F480F470F420F430F450F420F450F440F440F430F530F540F520F400F410F4109010100084B4B4B4B4A4A4B4A3001026907B2FF00FF00380002002800000008006C00016C00080008000000080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008002800070007000700070007000700070007000700070007000700070007000700070007000700070007000700070007000700080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000800080008000831010E9C0005FFFFFFFF0E9CFFFFFFFF0083320E9C1B6115E212465733FFFFFFFFFF34FFFFFFFFFF002B800009690E9C2710000D000E83002402020500002700080001000000FD00000000FFFFFFFFFFFFFFFFFFFFFFFF1FFE1FF3001DBA"
func TestExtractFramesKeepsPartialRemainderAndParsesHeader(t *testing.T) {
first := buildFrame(0x02, 0xfe, "LNBSCB3D4R1234567", []byte{0x1a, 0x06, 0x30, 0x16, 0x23, 0x57})
second := buildFrame(0x05, 0xfe, "PLATFORM-LOGIN001", nil)
@@ -82,6 +85,47 @@ func TestParseFrameExtractsRealtimeVehicleMileageAndPosition(t *testing.T) {
}
}
func TestParseFrameKeepsParsingRealFuelCellReportUntilUnknownExtension(t *testing.T) {
frame, err := hex.DecodeString(realFuelCellFrame)
if err != nil {
t.Fatal(err)
}
env, err := ParseFrame(frame, 1782914969584, "115.29.187.205:32960")
if err != nil {
t.Fatalf("ParseFrame() error = %v", err)
}
if env.VIN != "LB9A32A21R0LS1707" {
t.Fatalf("vin = %q", env.VIN)
}
assertFloatField(t, env, envelope.FieldTotalMileageKM, 53490.9)
assertFloatField(t, env, "fuel_cell_hydrogen_consumption_kg_per_100km", 1.8)
assertFloatField(t, env, envelope.FieldLongitude, 120.800326)
assertFloatField(t, env, envelope.FieldLatitude, 31.634907)
units, ok := env.Parsed["data_units"].([]map[string]any)
if !ok {
t.Fatalf("data_units missing: %#v", env.Parsed["data_units"])
}
gotTypes := make([]string, 0, len(units))
for _, unit := range units {
gotTypes = append(gotTypes, fmt.Sprint(unit["type"]))
}
wantTypes := []string{"0x01", "0x02", "0x03", "0x04", "0x05", "0x06", "0x07", "0x08", "0x09", "0x30"}
if fmt.Sprint(gotTypes) != fmt.Sprint(wantTypes) {
t.Fatalf("unit types = %v, want %v", gotTypes, wantTypes)
}
if units[1]["name"] != "drive_motor" {
t.Fatalf("unexpected motor unit: %#v", units[1])
}
if units[2]["name"] != "fuel_cell" {
t.Fatalf("unexpected fuel cell unit: %#v", units[2])
}
if units[9]["parse"] != "unknown_unit" {
t.Fatalf("unexpected unknown unit: %#v", units[9])
}
}
func assertFloatField(t *testing.T, env envelope.FrameEnvelope, key string, want float64) {
t.Helper()
got, ok := env.Fields[key].(float64)