package gb32960 import ( "encoding/hex" "testing" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) func TestExtractFramesKeepsPartialRemainderAndParsesHeader(t *testing.T) { first := buildFrame(0x02, 0xfe, "LNBSCB3D4R1234567", []byte{0x1a, 0x06, 0x30, 0x16, 0x23, 0x57}) second := buildFrame(0x05, 0xfe, "PLATFORM-LOGIN001", nil) stream := append([]byte{0x00, 0x01}, first...) stream = append(stream, second[:len(second)-3]...) frames, remainder, err := ExtractFrames(stream) if err != nil { t.Fatalf("ExtractFrames() error = %v", err) } if len(frames) != 1 { t.Fatalf("expected one complete frame, got %d", len(frames)) } if string(remainder) != string(second[:len(second)-3]) { t.Fatalf("expected partial second frame as remainder, got %s", hex.EncodeToString(remainder)) } env, err := ParseFrame(frames[0], 1782745114999, "115.29.187.205:32960") if err != nil { t.Fatalf("ParseFrame() error = %v", err) } if env.Protocol != envelope.ProtocolGB32960 || env.MessageID != "0x02" { t.Fatalf("unexpected envelope header: %#v", env) } if env.VIN != "LNBSCB3D4R1234567" { t.Fatalf("unexpected vin: %q", env.VIN) } } func TestParseFrameRejectsBadBCC(t *testing.T) { frame := buildFrame(0x02, 0xfe, "LNBSCB3D4R1234567", nil) frame[len(frame)-1] ^= 0xff if _, err := ParseFrame(frame, 1782745114999, ""); err == nil { t.Fatal("expected bad bcc error") } } func TestParseFrameExtractsRealtimeVehicleMileageAndPosition(t *testing.T) { body := []byte{0x1a, 0x06, 0x30, 0x16, 0x23, 0x57} body = append(body, 0x01) body = append(body, 0x01, // vehicle status 0x03, // charge status 0x02, // running mode 0x01, 0x2c, // speed: 30.0km/h 0x00, 0x01, 0x86, 0xa0, // total mileage: 10000.0km 0x15, 0xe5, // total voltage: 560.5V 0x27, 0x10, // total current: 0A after -1000 offset 85, 0x01, 0x00, 0x27, 0x10, 0x00, 0x00) body = append(body, 0x05) body = append(body, 0x00, 0x07, 0x36, 0x50, 0x40, // longitude: 121.000000 0x01, 0xd2, 0x4f, 0x00) // latitude: 30.560000 frame := buildFrame(0x02, 0xfe, "LNBSCB3D4R1234567", body) env, err := ParseFrame(frame, 1782745114999, "115.29.187.205:32960") if err != nil { t.Fatalf("ParseFrame() error = %v", err) } assertFloatField(t, env, envelope.FieldSpeedKMH, 30.0) assertFloatField(t, env, envelope.FieldTotalMileageKM, 10000.0) assertFloatField(t, env, envelope.FieldLongitude, 121.0) assertFloatField(t, env, envelope.FieldLatitude, 30.56) if env.Fields[envelope.FieldSOCPercent] != 85 { t.Fatalf("unexpected soc: %#v", env.Fields[envelope.FieldSOCPercent]) } if env.EventTimeMS == 1782745114999 { t.Fatal("event time should come from GB32960 device timestamp") } } func assertFloatField(t *testing.T, env envelope.FrameEnvelope, key string, want float64) { t.Helper() got, ok := env.Fields[key].(float64) if !ok { t.Fatalf("field %s missing or not float64: %#v", key, env.Fields[key]) } if got != want { t.Fatalf("field %s = %v, want %v", key, got, want) } } func buildFrame(command byte, response byte, vin string, body []byte) []byte { frame := []byte{'#', '#', command, response} vinBytes := []byte(vin) if len(vinBytes) < 17 { vinBytes = append(vinBytes, make([]byte, 17-len(vinBytes))...) } frame = append(frame, vinBytes[:17]...) frame = append(frame, 0x01, byte(len(body)>>8), byte(len(body))) frame = append(frame, body...) return append(frame, bcc(frame[2:])) }