package dbc_test import ( "fmt" "reflect" "regexp" "testing" "github.com/fiskerinc/cloud-services/pkg/dbc" fm29_frs90 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frs90" fm29_frsd0 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd0" fm29_frsd21 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd21" fm29_frsd390 "github.com/fiskerinc/cloud-services/pkg/dbc/fm29_frsd390" "github.com/fiskerinc/cloud-services/pkg/dbc/state" "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/golang/protobuf/proto" "github.com/jinzhu/copier" ) const vin = "11111111111111111" var epoch = int64(1642455023642165) var payloadFRS90 = kafka_grpc.GRPC_BatchPayload{ Version: fm29_frs90.Hash, Data: &kafka_grpc.GRPC_CANData{ Frames: []*kafka_grpc.GRPC_CANFrame{ { Epoch: epoch, ID: 608, Value: []byte("AAEAAAAAAAA="), }, { Epoch: epoch, ID: 757, Value: []byte("CgAAAAAUAAA="), }, { Epoch: epoch, ID: 801, Value: []byte("AAAUKDxQ/wA="), }, { Epoch: epoch, ID: 816, Value: []byte("AAAAfQAAAAA="), }, { Epoch: epoch, ID: 821, Value: []byte("AAAAAAgAAP8="), }, { Epoch: epoch, ID: 835, Value: []byte("AAAFbAAAMgA="), }, { Epoch: epoch, ID: 873, Value: []byte("AAB4jKAAAAE="), }, { Epoch: epoch, ID: 882, Value: []byte("AAAwADCKADA="), }, { Epoch: epoch, ID: 883, Value: []byte("AAAAAACcAAA="), }, { Epoch: epoch, ID: 902, Value: []byte("AQAAAAAAAAA="), }, { Epoch: epoch, ID: 1284, Value: []byte("AAAAAAAwNAA="), }, { Epoch: epoch, ID: 1298, Value: []byte("EAAAAAAAAAA="), }, { Epoch: epoch, ID: 1304, Value: []byte("AgAAAAAAAAA="), }, { Epoch: epoch, ID: 1329, Value: []byte("AAADlAQAAAA="), }, { Epoch: epoch, ID: 1408, Value: []byte("AAAAABOIAAA="), }, { Epoch: epoch, ID: 1410, Value: []byte("nAB4AAAAAAA="), }, { Epoch: epoch, ID: 1317, Value: []byte("AABAg6NJ64c="), }, }, }, } var payloadFRSD0 = kafka_grpc.GRPC_BatchPayload{ Version: fm29_frsd0.Hash, Data: &kafka_grpc.GRPC_CANData{ Frames: []*kafka_grpc.GRPC_CANFrame{ { Epoch: epoch, ID: 608, Value: []byte("AAAA+gH0Aec="), }, { Epoch: epoch, ID: 757, Value: []byte("CgAAAAAUAAA="), }, { Epoch: epoch, ID: 801, Value: []byte("AAAUKDxQ/wA="), }, { Epoch: epoch, ID: 816, Value: []byte("AAAAfQAAAAA="), }, { Epoch: epoch, ID: 821, Value: []byte("AAAAAAgAAP8="), }, { Epoch: epoch, ID: 835, Value: []byte("AAAFbAAAZAA="), }, { Epoch: epoch, ID: 873, Value: []byte("AAB4jKAAAAE="), }, { Epoch: epoch, ID: 882, Value: []byte("AAAwADCKADA="), }, { Epoch: epoch, ID: 883, Value: []byte("AAAAAACcAAA="), }, { Epoch: epoch, ID: 902, Value: []byte("AQAAAAAAAAA="), }, { Epoch: epoch, ID: 1284, Value: []byte("AAAAAAAwNAA="), }, { Epoch: epoch, ID: 1298, Value: []byte("EAAAAAAAAAA="), }, { Epoch: epoch, ID: 1304, Value: []byte("AgAAAAAAAAA="), }, { Epoch: epoch, ID: 1329, Value: []byte("AAADlAQAAAA="), }, { Epoch: epoch, ID: 1408, Value: []byte("AMgBLBOIAZA="), }, { Epoch: epoch, ID: 1410, Value: []byte("nAB4AAAAAAA="), }, { Epoch: epoch, ID: 1317, Value: []byte("ZAVAgAAAAAA="), }, { Epoch: epoch, ID: 1318, Value: []byte("A414OAeAC0A="), }, }, }, } var expectedValuesFRS90 = map[string]interface{}{ state.BCM_AP_FL_LeFrntWinPosnInfo: interface{}(10), state.BCM_AP_FL_LeReWinPosnInfo: interface{}(30), state.BCM_AP_FL_RiFrntWinPosnInfo: interface{}(20), state.BCM_AP_FL_RiReWinPosnInfo: interface{}(40), state.BCM_AP_RW_WinPosnInfo: interface{}(80), state.BCM_AP_TL_LeReWinPosnInfo: interface{}(60), state.BCM_AP_TL_RiReWinPosnInfo: interface{}(70), state.BCM_CenLockSwtSts: interface{}(3), state.BCM_DrFrntDoorSts: interface{}(false), state.BCM_FrntDrDoorLockSts: interface{}(true), state.BCM_FrntHoodLidSts: interface{}(true), state.BCM_HeatedSteerWhlSt: interface{}(true), state.BCM_LeReDoorSts: interface{}(true), state.BCM_PasFrntDoorSts: interface{}(false), state.BCM_ReDefrstHeatgCmd: interface{}(true), state.BCM_RiReDoorSts: interface{}(true), state.BCM_SunroofPosnInfo: interface{}(50), state.BMS_Bat_SOH: interface{}(20), state.BMS_Bat_SoC_usable: interface{}(10), state.BMS_BattAvrgT: interface{}(90), state.BMS_PwrBattRmngCpSOC: interface{}(50), state.DSMC_DrvrSeatHeatgSts: interface{}(2), state.ECC_InsdT: interface{}(30), state.ECC_OutdT: interface{}(30), state.ECC_RemTSetSts: interface{}(120), state.ICC_TotMilg_ODO: interface{}(float64(2345)), state.PSM_PassSeatHeatgSts: interface{}(4), state.TBOX_GPSHei: interface{}(float64(16)), state.TBOX_GPSLati: interface{}(float64(35.831)), state.TBOX_GPSLongi: interface{}(float64(-120.398)), state.VCU_DCChrgRmngTi: interface{}(5000), state.VCU_DrvgMilg: interface{}(1234), state.VCU_VehChrgDchgMod: interface{}("DC_charging"), state.UPDATED_AT: interface{}([]byte{34, 50, 48, 50, 50, 45, 48, 49, 45, 49, 55, 84, 50, 49, 58, 51, 48, 58, 50, 51, 46, 54, 52, 50, 49, 54, 53, 90, 34}), } var expectedValuesFRSD0 = map[string]interface{}{ state.BCM_AP_FL_LeFrntWinPosnInfo: interface{}(10), state.BCM_AP_FL_LeReWinPosnInfo: interface{}(30), state.BCM_AP_FL_RiFrntWinPosnInfo: interface{}(20), state.BCM_AP_FL_RiReWinPosnInfo: interface{}(40), state.BCM_AP_RW_WinPosnInfo: interface{}(80), state.BCM_AP_TL_LeReWinPosnInfo: interface{}(60), state.BCM_AP_TL_RiReWinPosnInfo: interface{}(70), state.BCM_CenLockSwtSts: interface{}(3), state.BCM_DrFrntDoorSts: interface{}(false), state.BCM_FrntDrDoorLockSts: interface{}(true), state.BCM_FrntHoodLidSts: interface{}(true), state.BCM_HeatedSteerWhlSt: interface{}(true), state.BCM_LeReDoorSts: interface{}(true), state.BCM_PasFrntDoorSts: interface{}(false), state.BCM_ReDefrstHeatgCmd: interface{}(true), state.BCM_RiReDoorSts: interface{}(true), state.BCM_SunroofPosnInfo: interface{}(50), state.BMS_Bat_SOH: interface{}(20), state.BMS_Bat_SoC_usable: interface{}(10), state.BMS_BattAvrgT: interface{}(90), state.BMS_PwrBattRmngCpSOC: interface{}(50), state.DSMC_DrvrSeatHeatgSts: interface{}(2), state.ECC_InsdT: interface{}(30), state.ECC_OutdT: interface{}(30), state.ECC_RemTSetSts: interface{}(120), state.ICC_TotMilg_ODO: interface{}(float64(2345)), state.PSM_PassSeatHeatgSts: interface{}(4), state.TBOX_GPSHei: interface{}(float64(16)), state.TBOX_GPSLati: interface{}(float64(35.832)), state.TBOX_GPSLongi: interface{}(float64(-120.397)), state.VCU_DCChrgRmngTi: interface{}(5000), state.VCU_DrvgMilg: interface{}(1234), state.VCU_VehChrgDchgMod: interface{}("DC_charging"), state.UPDATED_AT: interface{}([]byte{34, 50, 48, 50, 50, 45, 48, 49, 45, 49, 55, 84, 50, 49, 58, 51, 48, 58, 50, 51, 46, 54, 52, 50, 49, 54, 53, 90, 34}), } var expectedValuesFRSD21 = map[string]interface{}{ state.BCM_AP_FL_LeFrntWinPosnInfo: interface{}(10), state.BCM_AP_FL_LeReWinPosnInfo: interface{}(30), state.BCM_AP_FL_RiFrntWinPosnInfo: interface{}(20), state.BCM_AP_FL_RiReWinPosnInfo: interface{}(40), state.BCM_AP_RW_WinPosnInfo: interface{}(80), state.BCM_AP_TL_LeReWinPosnInfo: interface{}(60), state.BCM_AP_TL_RiReWinPosnInfo: interface{}(70), state.BCM_CenLockSwtSts: interface{}(3), state.BCM_DrFrntDoorSts: interface{}(false), state.BCM_FrntDrDoorLockSts: interface{}(true), state.BCM_FrntHoodLidSts: interface{}(true), state.BCM_HeatedSteerWhlSt: interface{}(true), state.BCM_LeReDoorSts: interface{}(true), state.BCM_PasFrntDoorSts: interface{}(false), state.BCM_ReDefrstHeatgCmd: interface{}(true), state.BCM_RiReDoorSts: interface{}(true), state.BCM_SunroofPosnInfo: interface{}(50), state.BMS_Bat_SOH: interface{}(20), state.BMS_Bat_SoC_usable: interface{}(10), state.BMS_BattAvrgT: interface{}(90), state.BMS_PwrBattRmngCpSOC: interface{}(50), state.DSMC_DrvrSeatHeatgSts: interface{}(2), state.ECC_InsdT: interface{}(30), state.ECC_OutdT: interface{}(30), state.ECC_RemTSetSts: interface{}(120), state.ICC_TotMilg_ODO: interface{}(2345), state.PSM_PassSeatHeatgSts: interface{}(4), state.TBOX_GPSHei: interface{}(float64(2802.4)), state.TBOX_GPSLati: interface{}(float64(-54.168)), state.TBOX_GPSLongi: interface{}(float64(-300.397)), state.VCU_DCChrgRmngTi: interface{}(5000), state.VCU_DrvgMilg: interface{}(1234), state.VCU_VehChrgDchgMod: interface{}("DC_charging"), state.UPDATED_AT: interface{}([]byte{34, 50, 48, 50, 50, 45, 48, 49, 45, 49, 55, 84, 50, 49, 58, 51, 48, 58, 50, 51, 46, 54, 52, 50, 49, 54, 53, 90, 34}), } func getExpectedValuesFRS390() map[string]interface{} { expectedValuesFRS390 := map[string]interface{}{} copier.Copy(&expectedValuesFRS390, &expectedValuesFRSD21) // DBC 390 does not have a definition for VCU_DCChrgRmngTi delete(expectedValuesFRS390, state.VCU_DCChrgRmngTi) expectedValuesFRS390[state.TBOX_GPSLati] = interface{}(float64(35.832)) expectedValuesFRS390[state.TBOX_GPSLongi] = interface{}(float64(-120.397)) return expectedValuesFRS390 } func copyPayload(source *kafka_grpc.GRPC_BatchPayload, hash string) *kafka_grpc.GRPC_BatchPayload { payload := kafka_grpc.GRPC_BatchPayload{} proto.Merge(&payload, source) payload.Version = hash return &payload } func ignoreSignalUpdateTimestamp(input map[string]interface{}) map[string]interface{} { m := make(map[string]interface{}) pattern, _ := regexp.Compile(`:updated$`) for key, val := range input { matched := pattern.MatchString(key) if matched { continue } m[key] = val } return m } func TestFM29_FRS90_ParseState(t *testing.T) { payloadFRSD21 := copyPayload(&payloadFRSD0, fm29_frsd21.Hash) payloadFRSD390 := copyPayload(&payloadFRSD0, fm29_frsd390.Hash) expectedValuesFRS390 := getExpectedValuesFRS390() collection := dbc.NewDBCCollection() type testCase struct { name string payload *kafka_grpc.GRPC_BatchPayload expected map[string]interface{} expectedCommandCount int } tests := []testCase{ { name: "FRS90", payload: &payloadFRS90, expected: expectedValuesFRS90, expectedCommandCount: 34, }, { name: "FRSD0", payload: &payloadFRSD0, expected: expectedValuesFRSD0, expectedCommandCount: 34, }, { name: "FRSD21", payload: payloadFRSD21, expected: expectedValuesFRSD21, expectedCommandCount: 34, }, { name: "FRSD390", payload: payloadFRSD390, expected: expectedValuesFRS390, expectedCommandCount: 33, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { keyvalues, err := collection.ParseState(vin, test.payload, nil) if err != nil { t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("ParseState %s", test.name), nil, err) } expectedCommandCount := 2*test.expectedCommandCount - 1 if len(keyvalues) != expectedCommandCount { t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("key value len %s", test.name), expectedCommandCount, len(keyvalues)) } mKeyVal := ignoreSignalUpdateTimestamp(keyvalues) if !reflect.DeepEqual(mKeyVal, test.expected) { t.Errorf(testhelper.TestErrorTemplate, fmt.Sprintf("key value compare %s", test.name), test.expected, keyvalues) } }) } } func TestBadMsgID(t *testing.T) { collection := dbc.NewDBCCollection() payload := kafka_grpc.GRPC_BatchPayload{ Version: fm29_frsd0.Hash, Data: &kafka_grpc.GRPC_CANData{ Frames: []*kafka_grpc.GRPC_CANFrame{ { Epoch: epoch, ID: 576, Value: []byte("AAAAA414OAeAC0A="), }, }, }, } // This test should not set anything in Redis cache keyvalues, _ := collection.ParseState(vin, &payload, nil) if len(keyvalues) != 0 { t.Errorf(testhelper.TestErrorTemplate, "bad message id", 0, len(keyvalues)) } } func BenchmarkNewFM29_FRS90_DBC(b *testing.B) { for n := 0; n < b.N; n++ { dbc.NewFM29_FRS90_DBC() } } func BenchmarkParseState(b *testing.B) { collection := dbc.NewDBCCollection() for n := 0; n < b.N; n++ { collection.ParseState(vin, &payloadFRS90, nil) } }