package dbc import ( "encoding/base64" "math" "strings" "github.com/fiskerinc/cloud-services/pkg/common" "github.com/fiskerinc/cloud-services/pkg/dbc/diagnostics" "github.com/fiskerinc/cloud-services/pkg/dbc/state" "github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc" can "github.com/fiskerinc/cloud-services/pkg/can-go" "github.com/fiskerinc/cloud-services/pkg/can-go/pkg/descriptor" "github.com/pkg/errors" ) func NewDBC(d *descriptor.Database, f diagnostics.Flags, s map[int]state.StateFunc) DBCInterface { return &DBC{database: d, diagnosticFlags: f, stateFuncs: s} } type DBCInterface interface { GetActiveDiagnosticFlags(vin string, msg *kafka_grpc.GRPC_CANFrame) ([]common.CANSignal, bool, error) GetStateFunc(id int) (state.StateFunc, bool) GenerateCANSignals(vin string, msg *kafka_grpc.GRPC_CANFrame) ([]common.CANSignal, error) GenerateCANSignalsMap(vin string, msg *kafka_grpc.GRPC_CANFrame) (map[string]common.CANSignal, error) DecodeMessage(msg *kafka_grpc.GRPC_CANFrame) ([]descriptor.DecodedSignal, error) GetDBCHash() string SetDBCHash(string) GetDatabase() *descriptor.Database } type DBC struct { database *descriptor.Database hash string diagnosticFlags diagnostics.Flags stateFuncs map[int]state.StateFunc } func (d *DBC) GetStateFunc(id int) (state.StateFunc, bool) { f, ok := d.stateFuncs[id] return f, ok } // GenerateCANSignals parses raw can bus message (encoded in base64) to ECU data func (d *DBC) GenerateCANSignals(vin string, msg *kafka_grpc.GRPC_CANFrame) ([]common.CANSignal, error) { decodedSignals, err := d.DecodeMessage(msg) if err != nil { return nil, err } signals := make([]common.CANSignal, 0, len(decodedSignals)) for _, signal := range decodedSignals { signals = append(signals, common.CANSignal{ VIN: vin, Timestamp: float64(msg.Epoch) * math.Pow(10, -6), ID: int(msg.ID), Name: signal.Signal.Name, Value: signal.Value, }) } return signals, nil } // GenerateCANSignalSets parses raw can bus message (encoded in base64) to ECU func (d *DBC) GenerateCANSignalsMap(vin string, msg *kafka_grpc.GRPC_CANFrame) (map[string]common.CANSignal, error) { var signals map[string]common.CANSignal decodedSignals, err := d.DecodeMessage(msg) if err != nil { return signals, err } signals = make(map[string]common.CANSignal, len(decodedSignals)) for _, signal := range decodedSignals { signals[signal.Signal.Name] = common.CANSignal{ VIN: vin, Timestamp: float64(msg.Epoch) * math.Pow(10, -6), ID: int(msg.ID), Name: signal.Signal.Name, Value: signal.Value, } } return signals, nil } func (d *DBC) GetActiveDiagnosticFlags(vin string, msg *kafka_grpc.GRPC_CANFrame) ([]common.CANSignal, bool, error) { var flags []common.CANSignal diagnosticSignals, ok := d.diagnosticFlags[int(msg.ID)] if !ok { return flags, false, nil } decodedSignals, err := d.DecodeMessage(msg) if err != nil { return flags, false, err } flags = make([]common.CANSignal, 0) for _, signal := range decodedSignals { faultVal, ok := diagnosticSignals[signal.Signal.Name] if !ok { continue } else if signal.Value != faultVal { continue } flags = append(flags, common.CANSignal{ VIN: vin, Timestamp: float64(msg.Epoch) * math.Pow(10, -6), ID: int(msg.ID), Name: signal.Signal.Name, Value: signal.Value, }) } return flags, len(flags) != 0, nil } func (d *DBC) DecodeMessage(msg *kafka_grpc.GRPC_CANFrame) ([]descriptor.DecodedSignal, error) { var decodedSignals []descriptor.DecodedSignal if msg.ID >= 2048 || msg.ID < 0 { return decodedSignals, errors.Errorf("message id %v out of bounds", msg.ID) } message, ok := d.database.Message(uint32(msg.ID)) if !ok { return decodedSignals, errors.Errorf( "message id %v not found in %s, version: %s", msg.ID, d.database.SourceFile, d.database.Version) } strippedData := strings.TrimRight(string(msg.Value), "=") data, err := base64.RawStdEncoding.DecodeString(strippedData) if err != nil { return decodedSignals, errors.WithStack(err) } p := can.Payload{Data: data} decodedSignals = message.Decode(&p) return decodedSignals, nil } func (d *DBC) GetDBCHash() string { return d.hash } func (d *DBC) GetDatabase() *descriptor.Database { return d.database } func (d *DBC) SetDBCHash(hash string) { d.hash = hash }