package feichibridge import ( "encoding/binary" "fmt" "strings" "testing" "time" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/protocol/gb32960" ) func TestDataFrameMapsFeichiFields(t *testing.T) { at := time.Date(2026, 7, 17, 14, 5, 6, 0, shanghai) record := Record{ "2000": "2026-07-17 14:05:06", "2201": "42.3", "2202": "12345.6", "2203": "D 0 0", "2208": "35", "2209": "0", "2213": "纯电", "2214": "工作", "2301": "未充电状态", "3201": "启动状态", "2613": "550.2", "2614": "-20.5", "2617": "2200", "7615": "78", "2501": "有效定位", "2502": "113.2644", "2503": "23.1291", "2601": "1", "2602": "8", "2603": "3.955", "2604": "1", "2605": "26", "2606": "3.821", "2607": "1", "2608": "3", "2609": "48", "2610": "1", "2611": "9", "2612": "37", "2003": "1:3.900_3.901_3.902", "2103": "1:35_36_37", } frame, err := (Encoder{}).DataFrame(CommandRealtime, "LTEST32960VIN0001", at, record) if err != nil { t.Fatal(err) } env, err := gb32960.ParseFrame(frame, at.UnixMilli(), "test") if err != nil { t.Fatal(err) } if env.MessageID != "0x02" || env.VIN != "LTEST32960VIN0001" { t.Fatalf("unexpected envelope: %#v", env) } if got := env.Fields["speed_kmh"]; got != 42.3 { t.Fatalf("speed = %#v", got) } if got := env.Fields["total_mileage_km"]; got != 12345.6 { t.Fatalf("mileage = %#v", got) } if got := env.Fields["soc_percent"]; got != 78 { t.Fatalf("soc = %#v", got) } if got := env.Fields["longitude"]; got != 113.2644 { t.Fatalf("longitude = %#v", got) } } func TestDataFrameSplitsMoreThan255CellVoltages(t *testing.T) { values := make([]string, 288) for index := range values { values[index] = fmt.Sprintf("%.3f", 3.5+float64(index)/1000) } frame, err := (Encoder{}).DataFrame(CommandReissue, "LTEST32960VIN0002", time.Now(), Record{ "2003": "1:" + strings.Join(values, "_"), "2613": "530", "2614": "10", }) if err != nil { t.Fatal(err) } body := frame[24 : len(frame)-1] if got := strings.Count(string(body), string([]byte{0x08})); got < 2 { t.Fatalf("expected at least two voltage units, got %d", got) } if binary.BigEndian.Uint16(frame[22:24]) != uint16(len(body)) { t.Fatal("body length mismatch") } if _, err := gb32960.ParseFrame(frame, time.Now().UnixMilli(), "test"); err != nil { t.Fatal(err) } } func TestLoginFrameLayout(t *testing.T) { frame, err := LoginFrame("FEICHIBRIDGE00001", "bridge", "secret", 7, time.Date(2026, 7, 17, 1, 2, 3, 0, shanghai)) if err != nil { t.Fatal(err) } if frame[2] != CommandLogin || frame[3] != 0xFE { t.Fatalf("unexpected login header: %x", frame[:4]) } if got := binary.BigEndian.Uint16(frame[30:32]); got != 7 { t.Fatalf("serial = %d", got) } if got, want := frame[len(frame)-1], bcc(frame[2:len(frame)-1]); got != want { t.Fatalf("BCC = %x, want %x", got, want) } }