Files
lingniu-vehicle-ingest/go/vehicle-gateway/internal/protocol/gb32960/parser_test.go
2026-07-01 21:43:16 +08:00

107 lines
3.4 KiB
Go

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:]))
}