openpilot v0.9.6 release

date: 2024-01-12T10:13:37
master commit: ba792d576a49a0899b88a753fa1c52956bedf9e6
This commit is contained in:
FrogAi
2024-01-12 22:39:28 -07:00
commit 08e9fb1edc
1881 changed files with 653708 additions and 0 deletions

24
opendbc/can/SConscript Normal file
View File

@@ -0,0 +1,24 @@
Import('env', 'envCython', 'cereal', 'common')
import os
envDBC = env.Clone()
dbc_file_path = '-DDBC_FILE_PATH=\'"%s"\'' % (envDBC.Dir("..").abspath)
envDBC['CXXFLAGS'] += [dbc_file_path]
src = ["dbc.cc", "parser.cc", "packer.cc", "common.cc"]
libs = [common, "capnp", "kj", "zmq"]
# shared library for openpilot
libdbc = envDBC.SharedLibrary('libdbc', src, LIBS=libs)
# static library for tools like cabana
envDBC.Library('libdbc_static', src, LIBS=libs)
# Build packer and parser
lenv = envCython.Clone()
lenv["LINKFLAGS"] += [libdbc[0].get_labspath()]
parser = lenv.Program('parser_pyx.so', 'parser_pyx.pyx')
packer = lenv.Program('packer_pyx.so', 'packer_pyx.pyx')
lenv.Depends(parser, libdbc)
lenv.Depends(packer, libdbc)

0
opendbc/can/__init__.py Normal file
View File

View File

@@ -0,0 +1,2 @@
from opendbc.can.parser_pyx import CANDefine # pylint: disable=no-name-in-module, import-error
assert CANDefine

246
opendbc/can/common.cc Normal file
View File

@@ -0,0 +1,246 @@
#include "opendbc/can/common.h"
unsigned int honda_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d) {
int s = 0;
bool extended = address > 0x7FF;
while (address) { s += (address & 0xF); address >>= 4; }
for (int i = 0; i < d.size(); i++) {
uint8_t x = d[i];
if (i == d.size()-1) x >>= 4; // remove checksum
s += (x & 0xF) + (x >> 4);
}
s = 8-s;
if (extended) s += 3; // extended can
return s & 0xF;
}
unsigned int toyota_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d) {
unsigned int s = d.size();
while (address) { s += address & 0xFF; address >>= 8; }
for (int i = 0; i < d.size() - 1; i++) { s += d[i]; }
return s & 0xFF;
}
unsigned int subaru_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d) {
unsigned int s = 0;
while (address) { s += address & 0xFF; address >>= 8; }
// skip checksum in first byte
for (int i = 1; i < d.size(); i++) { s += d[i]; }
return s & 0xFF;
}
unsigned int chrysler_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d) {
// jeep chrysler canbus checksum from http://illmatics.com/Remote%20Car%20Hacking.pdf
uint8_t checksum = 0xFF;
for (int j = 0; j < (d.size() - 1); j++) {
uint8_t shift = 0x80;
uint8_t curr = d[j];
for (int i = 0; i < 8; i++) {
uint8_t bit_sum = curr & shift;
uint8_t temp_chk = checksum & 0x80U;
if (bit_sum != 0U) {
bit_sum = 0x1C;
if (temp_chk != 0U) {
bit_sum = 1;
}
checksum = checksum << 1;
temp_chk = checksum | 1U;
bit_sum ^= temp_chk;
} else {
if (temp_chk != 0U) {
bit_sum = 0x1D;
}
checksum = checksum << 1;
bit_sum ^= checksum;
}
checksum = bit_sum;
shift = shift >> 1;
}
}
return ~checksum & 0xFF;
}
// Static lookup table for fast computation of CRCs
uint8_t crc8_lut_8h2f[256]; // CRC8 poly 0x2F, aka 8H2F/AUTOSAR
uint16_t crc16_lut_xmodem[256]; // CRC16 poly 0x1021, aka XMODEM
void gen_crc_lookup_table_8(uint8_t poly, uint8_t crc_lut[]) {
uint8_t crc;
int i, j;
for (i = 0; i < 256; i++) {
crc = i;
for (j = 0; j < 8; j++) {
if ((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ poly);
else
crc <<= 1;
}
crc_lut[i] = crc;
}
}
void gen_crc_lookup_table_16(uint16_t poly, uint16_t crc_lut[]) {
uint16_t crc;
int i, j;
for (i = 0; i < 256; i++) {
crc = i << 8;
for (j = 0; j < 8; j++) {
if ((crc & 0x8000) != 0) {
crc = (uint16_t)((crc << 1) ^ poly);
} else {
crc <<= 1;
}
}
crc_lut[i] = crc;
}
}
void init_crc_lookup_tables() {
// At init time, set up static lookup tables for fast CRC computation.
gen_crc_lookup_table_8(0x2F, crc8_lut_8h2f); // CRC-8 8H2F/AUTOSAR for Volkswagen
gen_crc_lookup_table_16(0x1021, crc16_lut_xmodem); // CRC-16 XMODEM for HKG CAN FD
}
unsigned int volkswagen_mqb_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d) {
// Volkswagen uses standard CRC8 8H2F/AUTOSAR, but they compute it with
// a magic variable padding byte tacked onto the end of the payload.
// https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
uint8_t crc = 0xFF; // Standard init value for CRC8 8H2F/AUTOSAR
// CRC the payload first, skipping over the first byte where the CRC lives.
for (int i = 1; i < d.size(); i++) {
crc ^= d[i];
crc = crc8_lut_8h2f[crc];
}
// Look up and apply the magic final CRC padding byte, which permutes by CAN
// address, and additionally (for SOME addresses) by the message counter.
uint8_t counter = d[1] & 0x0F;
switch (address) {
case 0x86: // LWI_01 Steering Angle
crc ^= (uint8_t[]){0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86}[counter];
break;
case 0x9F: // LH_EPS_03 Electric Power Steering
crc ^= (uint8_t[]){0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5}[counter];
break;
case 0xAD: // Getriebe_11 Automatic Gearbox
crc ^= (uint8_t[]){0x3F, 0x69, 0x39, 0xDC, 0x94, 0xF9, 0x14, 0x64, 0xD8, 0x6A, 0x34, 0xCE, 0xA2, 0x55, 0xB5, 0x2C}[counter];
break;
case 0xFD: // ESP_21 Electronic Stability Program
crc ^= (uint8_t[]){0xB4, 0xEF, 0xF8, 0x49, 0x1E, 0xE5, 0xC2, 0xC0, 0x97, 0x19, 0x3C, 0xC9, 0xF1, 0x98, 0xD6, 0x61}[counter];
break;
case 0x106: // ESP_05 Electronic Stability Program
crc ^= (uint8_t[]){0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}[counter];
break;
case 0x117: // ACC_10 Automatic Cruise Control
crc ^= (uint8_t[]){0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16}[counter];
break;
case 0x120: // TSK_06 Drivetrain Coordinator
crc ^= (uint8_t[]){0xC4, 0xE2, 0x4F, 0xE4, 0xF8, 0x2F, 0x56, 0x81, 0x9F, 0xE5, 0x83, 0x44, 0x05, 0x3F, 0x97, 0xDF}[counter];
break;
case 0x121: // Motor_20 Driver Throttle Inputs
crc ^= (uint8_t[]){0xE9, 0x65, 0xAE, 0x6B, 0x7B, 0x35, 0xE5, 0x5F, 0x4E, 0xC7, 0x86, 0xA2, 0xBB, 0xDD, 0xEB, 0xB4}[counter];
break;
case 0x122: // ACC_06 Automatic Cruise Control
crc ^= (uint8_t[]){0x37, 0x7D, 0xF3, 0xA9, 0x18, 0x46, 0x6D, 0x4D, 0x3D, 0x71, 0x92, 0x9C, 0xE5, 0x32, 0x10, 0xB9}[counter];
break;
case 0x126: // HCA_01 Heading Control Assist
crc ^= (uint8_t[]){0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA}[counter];
break;
case 0x12B: // GRA_ACC_01 Steering wheel controls for ACC
crc ^= (uint8_t[]){0x6A, 0x38, 0xB4, 0x27, 0x22, 0xEF, 0xE1, 0xBB, 0xF8, 0x80, 0x84, 0x49, 0xC7, 0x9E, 0x1E, 0x2B}[counter];
break;
case 0x12E: // ACC_07 Automatic Cruise Control
crc ^= (uint8_t[]){0xF8, 0xE5, 0x97, 0xC9, 0xD6, 0x07, 0x47, 0x21, 0x66, 0xDD, 0xCF, 0x6F, 0xA1, 0x94, 0x74, 0x63}[counter];
break;
case 0x187: // EV_Gearshift "Gear" selection data for EVs with no gearbox
crc ^= (uint8_t[]){0x7F, 0xED, 0x17, 0xC2, 0x7C, 0xEB, 0x44, 0x21, 0x01, 0xFA, 0xDB, 0x15, 0x4A, 0x6B, 0x23, 0x05}[counter];
break;
case 0x30C: // ACC_02 Automatic Cruise Control
crc ^= (uint8_t[]){0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}[counter];
break;
case 0x30F: // SWA_01 Lane Change Assist (SpurWechselAssistent)
crc ^= (uint8_t[]){0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C}[counter];
break;
case 0x324: // ACC_04 Automatic Cruise Control
crc ^= (uint8_t[]){0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27}[counter];
break;
case 0x3C0: // Klemmen_Status_01 ignition and starting status
crc ^= (uint8_t[]){0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3}[counter];
break;
case 0x65D: // ESP_20 Electronic Stability Program
crc ^= (uint8_t[]){0xAC, 0xB3, 0xAB, 0xEB, 0x7A, 0xE1, 0x3B, 0xF7, 0x73, 0xBA, 0x7C, 0x9E, 0x06, 0x5F, 0x02, 0xD9}[counter];
break;
default: // As-yet undefined CAN message, CRC check expected to fail
printf("Attempt to CRC check undefined Volkswagen message 0x%02X\n", address);
crc ^= (uint8_t[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}[counter];
break;
}
crc = crc8_lut_8h2f[crc];
return crc ^ 0xFF; // Return after standard final XOR for CRC8 8H2F/AUTOSAR
}
unsigned int xor_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d) {
uint8_t checksum = 0;
int checksum_byte = sig.start_bit / 8;
// Simple XOR over the payload, except for the byte where the checksum lives.
for (int i = 0; i < d.size(); i++) {
if (i != checksum_byte) {
checksum ^= d[i];
}
}
return checksum;
}
unsigned int pedal_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d) {
uint8_t crc = 0xFF;
uint8_t poly = 0xD5; // standard crc8
// skip checksum byte
for (int i = d.size()-2; i >= 0; i--) {
crc ^= d[i];
for (int j = 0; j < 8; j++) {
if ((crc & 0x80) != 0) {
crc = (uint8_t)((crc << 1) ^ poly);
} else {
crc <<= 1;
}
}
}
return crc;
}
unsigned int hkg_can_fd_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d) {
uint16_t crc = 0;
for (int i = 2; i < d.size(); i++) {
crc = (crc << 8) ^ crc16_lut_xmodem[(crc >> 8) ^ d[i]];
}
// Add address to crc
crc = (crc << 8) ^ crc16_lut_xmodem[(crc >> 8) ^ ((address >> 0) & 0xFF)];
crc = (crc << 8) ^ crc16_lut_xmodem[(crc >> 8) ^ ((address >> 8) & 0xFF)];
if (d.size() == 8) {
crc ^= 0x5f29;
} else if (d.size() == 16) {
crc ^= 0x041d;
} else if (d.size() == 24) {
crc ^= 0x819d;
} else if (d.size() == 32) {
crc ^= 0x9f5b;
}
return crc;
}

102
opendbc/can/common.h Normal file
View File

@@ -0,0 +1,102 @@
#pragma once
#include <map>
#include <string>
#include <utility>
#include <unordered_map>
#include <vector>
#include <capnp/dynamic.h>
#include <capnp/serialize.h>
#ifndef DYNAMIC_CAPNP
#include "cereal/gen/cpp/log.capnp.h"
#endif
#include "opendbc/can/common_dbc.h"
#define INFO printf
#define WARN printf
#define DEBUG(...)
//#define DEBUG printf
#define MAX_BAD_COUNTER 5
#define CAN_INVALID_CNT 5
void init_crc_lookup_tables();
// Car specific functions
unsigned int honda_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
unsigned int toyota_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
unsigned int subaru_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
unsigned int chrysler_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
unsigned int volkswagen_mqb_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
unsigned int xor_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
unsigned int hkg_can_fd_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
unsigned int pedal_checksum(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
class MessageState {
public:
std::string name;
uint32_t address;
unsigned int size;
std::vector<Signal> parse_sigs;
std::vector<double> vals;
std::vector<std::vector<double>> all_vals;
uint64_t last_seen_nanos;
uint64_t check_threshold;
uint8_t counter;
uint8_t counter_fail;
bool ignore_checksum = false;
bool ignore_counter = false;
bool parse(uint64_t nanos, const std::vector<uint8_t> &dat);
bool update_counter_generic(int64_t v, int cnt_size);
};
class CANParser {
private:
const int bus;
kj::Array<capnp::word> aligned_buf;
const DBC *dbc = NULL;
std::unordered_map<uint32_t, MessageState> message_states;
public:
bool can_valid = false;
bool bus_timeout = false;
uint64_t first_nanos = 0;
uint64_t last_nanos = 0;
uint64_t last_nonempty_nanos = 0;
uint64_t bus_timeout_threshold = 0;
uint64_t can_invalid_cnt = CAN_INVALID_CNT;
CANParser(int abus, const std::string& dbc_name,
const std::vector<std::pair<uint32_t, int>> &messages);
CANParser(int abus, const std::string& dbc_name, bool ignore_checksum, bool ignore_counter);
#ifndef DYNAMIC_CAPNP
void update_string(const std::string &data, bool sendcan);
void update_strings(const std::vector<std::string> &data, std::vector<SignalValue> &vals, bool sendcan);
void UpdateCans(uint64_t nanos, const capnp::List<cereal::CanData>::Reader& cans);
#endif
void UpdateCans(uint64_t nanos, const capnp::DynamicStruct::Reader& cans);
void UpdateValid(uint64_t nanos);
void query_latest(std::vector<SignalValue> &vals, uint64_t last_ts = 0);
};
class CANPacker {
private:
const DBC *dbc = NULL;
std::map<std::pair<uint32_t, std::string>, Signal> signal_lookup;
std::map<uint32_t, Msg> message_lookup;
std::map<uint32_t, uint32_t> counters;
public:
CANPacker(const std::string& dbc_name);
std::vector<uint8_t> pack(uint32_t address, const std::vector<SignalPackValue> &values);
Msg* lookup_message(uint32_t address);
};

75
opendbc/can/common.pxd Normal file
View File

@@ -0,0 +1,75 @@
# distutils: language = c++
# cython: language_level=3
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t
from libcpp cimport bool
from libcpp.pair cimport pair
from libcpp.string cimport string
from libcpp.vector cimport vector
ctypedef unsigned int (*calc_checksum_type)(uint32_t, const Signal&, const vector[uint8_t] &)
cdef extern from "common_dbc.h":
ctypedef enum SignalType:
DEFAULT,
COUNTER,
HONDA_CHECKSUM,
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
VOLKSWAGEN_MQB_CHECKSUM,
XOR_CHECKSUM,
SUBARU_CHECKSUM,
CHRYSLER_CHECKSUM
HKG_CAN_FD_CHECKSUM,
cdef struct Signal:
string name
int start_bit, msb, lsb, size
bool is_signed
double factor, offset
bool is_little_endian
SignalType type
calc_checksum_type calc_checksum
cdef struct Msg:
string name
uint32_t address
unsigned int size
vector[Signal] sigs
cdef struct Val:
string name
uint32_t address
string def_val
vector[Signal] sigs
cdef struct DBC:
string name
vector[Msg] msgs
vector[Val] vals
cdef struct SignalValue:
uint32_t address
uint64_t ts_nanos
string name
double value
vector[double] all_values
cdef struct SignalPackValue:
string name
double value
cdef extern from "common.h":
cdef const DBC* dbc_lookup(const string) except +
cdef cppclass CANParser:
bool can_valid
bool bus_timeout
CANParser(int, string, vector[pair[uint32_t, int]]) except +
void update_strings(vector[string]&, vector[SignalValue]&, bool) except +
cdef cppclass CANPacker:
CANPacker(string)
vector[uint8_t] pack(uint32_t, vector[SignalPackValue]&)

77
opendbc/can/common_dbc.h Normal file
View File

@@ -0,0 +1,77 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
struct SignalPackValue {
std::string name;
double value;
};
struct SignalValue {
uint32_t address;
uint64_t ts_nanos;
std::string name;
double value; // latest value
std::vector<double> all_values; // all values from this cycle
};
enum SignalType {
DEFAULT,
COUNTER,
HONDA_CHECKSUM,
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
VOLKSWAGEN_MQB_CHECKSUM,
XOR_CHECKSUM,
SUBARU_CHECKSUM,
CHRYSLER_CHECKSUM,
HKG_CAN_FD_CHECKSUM,
};
struct Signal {
std::string name;
int start_bit, msb, lsb, size;
bool is_signed;
double factor, offset;
bool is_little_endian;
SignalType type;
unsigned int (*calc_checksum)(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
};
struct Msg {
std::string name;
uint32_t address;
unsigned int size;
std::vector<Signal> sigs;
};
struct Val {
std::string name;
uint32_t address;
std::string def_val;
std::vector<Signal> sigs;
};
struct DBC {
std::string name;
std::vector<Msg> msgs;
std::vector<Val> vals;
};
typedef struct ChecksumState {
int checksum_size;
int counter_size;
int checksum_start_bit;
int counter_start_bit;
bool little_endian;
SignalType checksum_type;
unsigned int (*calc_checksum)(uint32_t address, const Signal &sig, const std::vector<uint8_t> &d);
} ChecksumState;
DBC* dbc_parse(const std::string& dbc_path);
DBC* dbc_parse_from_stream(const std::string &dbc_name, std::istream &stream, ChecksumState *checksum = nullptr, bool allow_duplicate_msg_name=false);
const DBC* dbc_lookup(const std::string& dbc_name);
std::vector<std::string> get_dbc_names();

258
opendbc/can/dbc.cc Normal file
View File

@@ -0,0 +1,258 @@
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <map>
#include <regex>
#include <set>
#include <sstream>
#include <vector>
#include <mutex>
#include <iterator>
#include <cstring>
#include <clocale>
#include "opendbc/can/common.h"
#include "opendbc/can/common_dbc.h"
std::regex bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))");
std::regex sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
std::regex sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
std::regex val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))");
std::regex val_split_regexp{R"([\"]+)"}; // split on "
#define DBC_ASSERT(condition, message) \
do { \
if (!(condition)) { \
std::stringstream is; \
is << "[" << dbc_name << ":" << line_num << "] " << message; \
throw std::runtime_error(is.str()); \
} \
} while (false)
inline bool startswith(const std::string& str, const char* prefix) {
return str.find(prefix, 0) == 0;
}
inline bool startswith(const std::string& str, std::initializer_list<const char*> prefix_list) {
for (auto prefix : prefix_list) {
if (startswith(str, prefix)) return true;
}
return false;
}
inline bool endswith(const std::string& str, const char* suffix) {
return str.find(suffix, 0) == (str.length() - strlen(suffix));
}
inline std::string& trim(std::string& s, const char* t = " \t\n\r\f\v") {
s.erase(s.find_last_not_of(t) + 1);
return s.erase(0, s.find_first_not_of(t));
}
ChecksumState* get_checksum(const std::string& dbc_name) {
ChecksumState* s = nullptr;
if (startswith(dbc_name, {"honda_", "acura_"})) {
s = new ChecksumState({4, 2, 3, 5, false, HONDA_CHECKSUM, &honda_checksum});
} else if (startswith(dbc_name, {"toyota_", "lexus_"})) {
s = new ChecksumState({8, -1, 7, -1, false, TOYOTA_CHECKSUM, &toyota_checksum});
} else if (startswith(dbc_name, "hyundai_canfd")) {
s = new ChecksumState({16, -1, 0, -1, true, HKG_CAN_FD_CHECKSUM, &hkg_can_fd_checksum});
} else if (startswith(dbc_name, "vw_mqb_2010")) {
s = new ChecksumState({8, 4, 0, 0, true, VOLKSWAGEN_MQB_CHECKSUM, &volkswagen_mqb_checksum});
} else if (startswith(dbc_name, "vw_golf_mk4")) {
s = new ChecksumState({8, 4, 0, -1, true, XOR_CHECKSUM, &xor_checksum});
} else if (startswith(dbc_name, "subaru_global_")) {
s = new ChecksumState({8, -1, 0, -1, true, SUBARU_CHECKSUM, &subaru_checksum});
} else if (startswith(dbc_name, "chrysler_")) {
s = new ChecksumState({8, -1, 7, -1, false, CHRYSLER_CHECKSUM, &chrysler_checksum});
} else if (startswith(dbc_name, "comma_body")) {
s = new ChecksumState({8, 4, 7, 3, false, PEDAL_CHECKSUM, &pedal_checksum});
}
return s;
}
void set_signal_type(Signal& s, ChecksumState* chk, const std::string& dbc_name, int line_num) {
s.calc_checksum = nullptr;
if (chk) {
if (s.name == "CHECKSUM") {
DBC_ASSERT(chk->checksum_size == -1 || s.size == chk->checksum_size, "CHECKSUM is not " << chk->checksum_size << " bits long");
DBC_ASSERT(chk->checksum_start_bit == -1 || (s.start_bit % 8) == chk->checksum_start_bit, " CHECKSUM starts at wrong bit");
DBC_ASSERT(s.is_little_endian == chk->little_endian, "CHECKSUM has wrong endianness");
DBC_ASSERT(chk->calc_checksum != nullptr, "CHECKSUM calculate function not supplied");
s.type = chk->checksum_type;
s.calc_checksum = chk->calc_checksum;
} else if (s.name == "COUNTER") {
DBC_ASSERT(chk->counter_size == -1 || s.size == chk->counter_size, "COUNTER is not " << chk->counter_size << " bits long");
DBC_ASSERT(chk->counter_start_bit == -1 || (s.start_bit % 8) == chk->counter_start_bit, "COUNTER starts at wrong bit");
DBC_ASSERT(chk->little_endian == s.is_little_endian, "COUNTER has wrong endianness");
s.type = COUNTER;
}
}
// TODO: CAN packer/parser shouldn't know anything about interceptors or pedals
if (s.name == "CHECKSUM_PEDAL") {
DBC_ASSERT(s.size == 8, "INTERCEPTOR CHECKSUM is not 8 bits long");
s.type = PEDAL_CHECKSUM;
} else if (s.name == "COUNTER_PEDAL") {
DBC_ASSERT(s.size == 4, "INTERCEPTOR COUNTER is not 4 bits long");
s.type = COUNTER;
}
}
DBC* dbc_parse_from_stream(const std::string &dbc_name, std::istream &stream, ChecksumState *checksum, bool allow_duplicate_msg_name) {
uint32_t address = 0;
std::set<uint32_t> address_set;
std::set<std::string> msg_name_set;
std::map<uint32_t, std::set<std::string>> signal_name_sets;
std::map<uint32_t, std::vector<Signal>> signals;
DBC* dbc = new DBC;
dbc->name = dbc_name;
std::setlocale(LC_NUMERIC, "C");
// used to find big endian LSB from MSB and size
std::vector<int> be_bits;
for (int i = 0; i < 64; i++) {
for (int j = 7; j >= 0; j--) {
be_bits.push_back(j + i * 8);
}
}
std::string line;
int line_num = 0;
std::smatch match;
// TODO: see if we can speed up the regex statements in this loop, SG_ is specifically the slowest
while (std::getline(stream, line)) {
line = trim(line);
line_num += 1;
if (startswith(line, "BO_ ")) {
// new group
bool ret = std::regex_match(line, match, bo_regexp);
DBC_ASSERT(ret, "bad BO: " << line);
Msg& msg = dbc->msgs.emplace_back();
address = msg.address = std::stoul(match[1].str()); // could be hex
msg.name = match[2].str();
msg.size = std::stoul(match[3].str());
// check for duplicates
DBC_ASSERT(address_set.find(address) == address_set.end(), "Duplicate message address: " << address << " (" << msg.name << ")");
address_set.insert(address);
if (!allow_duplicate_msg_name) {
DBC_ASSERT(msg_name_set.find(msg.name) == msg_name_set.end(), "Duplicate message name: " << msg.name);
msg_name_set.insert(msg.name);
}
} else if (startswith(line, "SG_ ")) {
// new signal
int offset = 0;
if (!std::regex_search(line, match, sg_regexp)) {
bool ret = std::regex_search(line, match, sgm_regexp);
DBC_ASSERT(ret, "bad SG: " << line);
offset = 1;
}
Signal& sig = signals[address].emplace_back();
sig.name = match[1].str();
sig.start_bit = std::stoi(match[offset + 2].str());
sig.size = std::stoi(match[offset + 3].str());
sig.is_little_endian = std::stoi(match[offset + 4].str()) == 1;
sig.is_signed = match[offset + 5].str() == "-";
sig.factor = std::stod(match[offset + 6].str());
sig.offset = std::stod(match[offset + 7].str());
set_signal_type(sig, checksum, dbc_name, line_num);
if (sig.is_little_endian) {
sig.lsb = sig.start_bit;
sig.msb = sig.start_bit + sig.size - 1;
} else {
auto it = find(be_bits.begin(), be_bits.end(), sig.start_bit);
sig.lsb = be_bits[(it - be_bits.begin()) + sig.size - 1];
sig.msb = sig.start_bit;
}
DBC_ASSERT(sig.lsb < (64 * 8) && sig.msb < (64 * 8), "Signal out of bounds: " << line);
// Check for duplicate signal names
DBC_ASSERT(signal_name_sets[address].find(sig.name) == signal_name_sets[address].end(), "Duplicate signal name: " << sig.name);
signal_name_sets[address].insert(sig.name);
} else if (startswith(line, "VAL_ ")) {
// new signal value/definition
bool ret = std::regex_search(line, match, val_regexp);
DBC_ASSERT(ret, "bad VAL: " << line);
auto& val = dbc->vals.emplace_back();
val.address = std::stoul(match[1].str()); // could be hex
val.name = match[2].str();
auto defvals = match[3].str();
std::sregex_token_iterator it{defvals.begin(), defvals.end(), val_split_regexp, -1};
// convert strings to UPPER_CASE_WITH_UNDERSCORES
std::vector<std::string> words{it, {}};
for (auto& w : words) {
w = trim(w);
std::transform(w.begin(), w.end(), w.begin(), ::toupper);
std::replace(w.begin(), w.end(), ' ', '_');
}
// join string
std::stringstream s;
std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(s, " "));
val.def_val = s.str();
val.def_val = trim(val.def_val);
}
}
for (auto& m : dbc->msgs) {
m.sigs = signals[m.address];
}
for (auto& v : dbc->vals) {
v.sigs = signals[v.address];
}
return dbc;
}
DBC* dbc_parse(const std::string& dbc_path) {
std::ifstream infile(dbc_path);
if (!infile) return nullptr;
const std::string dbc_name = std::filesystem::path(dbc_path).filename();
std::unique_ptr<ChecksumState> checksum(get_checksum(dbc_name));
return dbc_parse_from_stream(dbc_name, infile, checksum.get());
}
const std::string get_dbc_root_path() {
char *basedir = std::getenv("BASEDIR");
if (basedir != NULL) {
return std::string(basedir) + "/opendbc";
} else {
return DBC_FILE_PATH;
}
}
const DBC* dbc_lookup(const std::string& dbc_name) {
static std::mutex lock;
static std::map<std::string, DBC*> dbcs;
std::string dbc_file_path = dbc_name;
if (!std::filesystem::exists(dbc_file_path)) {
dbc_file_path = get_dbc_root_path() + "/" + dbc_name + ".dbc";
}
std::unique_lock lk(lock);
auto it = dbcs.find(dbc_name);
if (it == dbcs.end()) {
it = dbcs.insert(it, {dbc_name, dbc_parse(dbc_file_path)});
}
return it->second;
}
std::vector<std::string> get_dbc_names() {
static const std::string& dbc_file_path = get_dbc_root_path();
std::vector<std::string> dbcs;
for (std::filesystem::directory_iterator i(dbc_file_path), end; i != end; i++) {
if (!is_directory(i->path())) {
std::string filename = i->path().filename();
if (!startswith(filename, "_") && endswith(filename, ".dbc")) {
dbcs.push_back(filename.substr(0, filename.length() - 4));
}
}
}
return dbcs;
}

98
opendbc/can/packer.cc Normal file
View File

@@ -0,0 +1,98 @@
#include <algorithm>
#include <cassert>
#include <cmath>
#include <map>
#include <stdexcept>
#include <utility>
#include "opendbc/can/common.h"
void set_value(std::vector<uint8_t> &msg, const Signal &sig, int64_t ival) {
int i = sig.lsb / 8;
int bits = sig.size;
if (sig.size < 64) {
ival &= ((1ULL << sig.size) - 1);
}
while (i >= 0 && i < msg.size() && bits > 0) {
int shift = (int)(sig.lsb / 8) == i ? sig.lsb % 8 : 0;
int size = std::min(bits, 8 - shift);
msg[i] &= ~(((1ULL << size) - 1) << shift);
msg[i] |= (ival & ((1ULL << size) - 1)) << shift;
bits -= size;
ival >>= size;
i = sig.is_little_endian ? i+1 : i-1;
}
}
CANPacker::CANPacker(const std::string& dbc_name) {
dbc = dbc_lookup(dbc_name);
assert(dbc);
for (const auto& msg : dbc->msgs) {
message_lookup[msg.address] = msg;
for (const auto& sig : msg.sigs) {
signal_lookup[std::make_pair(msg.address, sig.name)] = sig;
}
}
init_crc_lookup_tables();
}
std::vector<uint8_t> CANPacker::pack(uint32_t address, const std::vector<SignalPackValue> &signals) {
std::vector<uint8_t> ret(message_lookup[address].size, 0);
// set all values for all given signal/value pairs
bool counter_set = false;
for (const auto& sigval : signals) {
auto sig_it = signal_lookup.find(std::make_pair(address, sigval.name));
if (sig_it == signal_lookup.end()) {
// TODO: do something more here. invalid flag like CANParser?
WARN("undefined signal %s - %d\n", sigval.name.c_str(), address);
continue;
}
const auto &sig = sig_it->second;
int64_t ival = (int64_t)(round((sigval.value - sig.offset) / sig.factor));
if (ival < 0) {
ival = (1ULL << sig.size) + ival;
}
set_value(ret, sig, ival);
counter_set = counter_set || (sigval.name == "COUNTER");
if (counter_set) {
counters[address] = sigval.value;
}
}
// set message counter
auto sig_it_counter = signal_lookup.find(std::make_pair(address, "COUNTER"));
if (!counter_set && sig_it_counter != signal_lookup.end()) {
const auto& sig = sig_it_counter->second;
if (counters.find(address) == counters.end()) {
counters[address] = 0;
}
set_value(ret, sig, counters[address]);
counters[address] = (counters[address] + 1) % (1 << sig.size);
}
// set message checksum
auto sig_it_checksum = signal_lookup.find(std::make_pair(address, "CHECKSUM"));
if (sig_it_checksum != signal_lookup.end()) {
const auto &sig = sig_it_checksum->second;
if (sig.calc_checksum != nullptr) {
unsigned int checksum = sig.calc_checksum(address, sig, ret);
set_value(ret, sig, checksum);
}
}
return ret;
}
// This function has a definition in common.h and is used in PlotJuggler
Msg* CANPacker::lookup_message(uint32_t address) {
return &message_lookup[address];
}

2
opendbc/can/packer.py Normal file
View File

@@ -0,0 +1,2 @@
from opendbc.can.packer_pyx import CANPacker # pylint: disable=no-name-in-module, import-error
assert CANPacker

View File

@@ -0,0 +1,49 @@
# distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
from libc.stdint cimport uint8_t
from libcpp.vector cimport vector
from libcpp.map cimport map
from libcpp.string cimport string
from .common cimport CANPacker as cpp_CANPacker
from .common cimport dbc_lookup, SignalPackValue, DBC
cdef class CANPacker:
cdef:
cpp_CANPacker *packer
const DBC *dbc
map[string, int] name_to_address
def __init__(self, dbc_name):
self.dbc = dbc_lookup(dbc_name)
if not self.dbc:
raise RuntimeError(f"Can't lookup {dbc_name}")
self.packer = new cpp_CANPacker(dbc_name)
for i in range(self.dbc[0].msgs.size()):
msg = self.dbc[0].msgs[i]
self.name_to_address[string(msg.name)] = msg.address
cdef vector[uint8_t] pack(self, addr, values):
cdef vector[SignalPackValue] values_thing
values_thing.reserve(len(values))
cdef SignalPackValue spv
for name, value in values.iteritems():
spv.name = name.encode("utf8")
spv.value = value
values_thing.push_back(spv)
return self.packer.pack(addr, values_thing)
cpdef make_can_msg(self, name_or_addr, bus, values):
cdef int addr
if isinstance(name_or_addr, int):
addr = name_or_addr
else:
addr = self.name_to_address[name_or_addr.encode("utf8")]
cdef vector[uint8_t] val = self.pack(addr, values)
return [addr, 0, (<char *>&val[0])[:val.size()], bus]

326
opendbc/can/parser.cc Normal file
View File

@@ -0,0 +1,326 @@
#include <algorithm>
#include <cassert>
#include <cstring>
#include <limits>
#include <stdexcept>
#include <sstream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "cereal/logger/logger.h"
#include "opendbc/can/common.h"
int64_t get_raw_value(const std::vector<uint8_t> &msg, const Signal &sig) {
int64_t ret = 0;
int i = sig.msb / 8;
int bits = sig.size;
while (i >= 0 && i < msg.size() && bits > 0) {
int lsb = (int)(sig.lsb / 8) == i ? sig.lsb : i*8;
int msb = (int)(sig.msb / 8) == i ? sig.msb : (i+1)*8 - 1;
int size = msb - lsb + 1;
uint64_t d = (msg[i] >> (lsb - (i*8))) & ((1ULL << size) - 1);
ret |= d << (bits - size);
bits -= size;
i = sig.is_little_endian ? i-1 : i+1;
}
return ret;
}
bool MessageState::parse(uint64_t nanos, const std::vector<uint8_t> &dat) {
std::vector<double> tmp_vals(parse_sigs.size());
bool checksum_failed = false;
bool counter_failed = false;
for (int i = 0; i < parse_sigs.size(); i++) {
const auto &sig = parse_sigs[i];
int64_t tmp = get_raw_value(dat, sig);
if (sig.is_signed) {
tmp -= ((tmp >> (sig.size-1)) & 0x1) ? (1ULL << sig.size) : 0;
}
//DEBUG("parse 0x%X %s -> %ld\n", address, sig.name, tmp);
if (!ignore_checksum) {
if (sig.calc_checksum != nullptr && sig.calc_checksum(address, sig, dat) != tmp) {
checksum_failed = true;
}
}
if (!ignore_counter) {
if (sig.type == SignalType::COUNTER && !update_counter_generic(tmp, sig.size)) {
counter_failed = true;
}
}
tmp_vals[i] = tmp * sig.factor + sig.offset;
}
// only update values if both checksum and counter are valid
if (checksum_failed || counter_failed) {
LOGE("0x%X message checks failed, checksum failed %d, counter failed %d", address, checksum_failed, counter_failed);
return false;
}
for (int i = 0; i < parse_sigs.size(); i++) {
vals[i] = tmp_vals[i];
all_vals[i].push_back(vals[i]);
}
last_seen_nanos = nanos;
return true;
}
bool MessageState::update_counter_generic(int64_t v, int cnt_size) {
if (((counter + 1) & ((1 << cnt_size) -1)) != v) {
counter_fail = std::min(counter_fail + 1, MAX_BAD_COUNTER);
if (counter_fail > 1) {
INFO("0x%X COUNTER FAIL #%d -- %d -> %d\n", address, counter_fail, counter, (int)v);
}
} else if (counter_fail > 0) {
counter_fail--;
}
counter = v;
return counter_fail < MAX_BAD_COUNTER;
}
CANParser::CANParser(int abus, const std::string& dbc_name, const std::vector<std::pair<uint32_t, int>> &messages)
: bus(abus), aligned_buf(kj::heapArray<capnp::word>(1024)) {
dbc = dbc_lookup(dbc_name);
assert(dbc);
init_crc_lookup_tables();
bus_timeout_threshold = std::numeric_limits<uint64_t>::max();
for (const auto& [address, frequency] : messages) {
// disallow duplicate message checks
if (message_states.find(address) != message_states.end()) {
std::stringstream is;
is << "Duplicate Message Check: " << address;
throw std::runtime_error(is.str());
}
MessageState &state = message_states[address];
state.address = address;
// state.check_frequency = op.check_frequency,
// msg is not valid if a message isn't received for 10 consecutive steps
if (frequency > 0) {
state.check_threshold = (1000000000ULL / frequency) * 10;
// bus timeout threshold should be 10x the fastest msg
bus_timeout_threshold = std::min(bus_timeout_threshold, state.check_threshold);
}
const Msg* msg = NULL;
for (const auto& m : dbc->msgs) {
if (m.address == address) {
msg = &m;
break;
}
}
if (!msg) {
fprintf(stderr, "CANParser: could not find message 0x%X in DBC %s\n", address, dbc_name.c_str());
assert(false);
}
state.name = msg->name;
state.size = msg->size;
assert(state.size <= 64); // max signal size is 64 bytes
// track all signals for this message
state.parse_sigs = msg->sigs;
state.vals.resize(msg->sigs.size());
state.all_vals.resize(msg->sigs.size());
}
}
CANParser::CANParser(int abus, const std::string& dbc_name, bool ignore_checksum, bool ignore_counter)
: bus(abus) {
// Add all messages and signals
dbc = dbc_lookup(dbc_name);
assert(dbc);
init_crc_lookup_tables();
for (const auto& msg : dbc->msgs) {
MessageState state = {
.name = msg.name,
.address = msg.address,
.size = msg.size,
.ignore_checksum = ignore_checksum,
.ignore_counter = ignore_counter,
};
for (const auto& sig : msg.sigs) {
state.parse_sigs.push_back(sig);
state.vals.push_back(0);
state.all_vals.push_back({});
}
message_states[state.address] = state;
}
}
#ifndef DYNAMIC_CAPNP
void CANParser::update_string(const std::string &data, bool sendcan) {
// format for board, make copy due to alignment issues.
const size_t buf_size = (data.length() / sizeof(capnp::word)) + 1;
if (aligned_buf.size() < buf_size) {
aligned_buf = kj::heapArray<capnp::word>(buf_size);
}
memcpy(aligned_buf.begin(), data.data(), data.length());
// extract the messages
capnp::FlatArrayMessageReader cmsg(aligned_buf.slice(0, buf_size));
cereal::Event::Reader event = cmsg.getRoot<cereal::Event>();
if (first_nanos == 0) {
first_nanos = event.getLogMonoTime();
}
last_nanos = event.getLogMonoTime();
auto cans = sendcan ? event.getSendcan() : event.getCan();
UpdateCans(last_nanos, cans);
UpdateValid(last_nanos);
}
void CANParser::update_strings(const std::vector<std::string> &data, std::vector<SignalValue> &vals, bool sendcan) {
uint64_t current_nanos = 0;
for (const auto &d : data) {
update_string(d, sendcan);
if (current_nanos == 0) {
current_nanos = last_nanos;
}
}
query_latest(vals, current_nanos);
}
void CANParser::UpdateCans(uint64_t nanos, const capnp::List<cereal::CanData>::Reader& cans) {
//DEBUG("got %d messages\n", cans.size());
bool bus_empty = true;
// parse the messages
for (const auto cmsg : cans) {
if (cmsg.getSrc() != bus) {
// DEBUG("skip %d: wrong bus\n", cmsg.getAddress());
continue;
}
bus_empty = false;
auto state_it = message_states.find(cmsg.getAddress());
if (state_it == message_states.end()) {
// DEBUG("skip %d: not specified\n", cmsg.getAddress());
continue;
}
auto dat = cmsg.getDat();
if (dat.size() > 64) {
DEBUG("got message longer than 64 bytes: 0x%X %zu\n", cmsg.getAddress(), dat.size());
continue;
}
// TODO: this actually triggers for some cars. fix and enable this
//if (dat.size() != state_it->second.size) {
// DEBUG("got message with unexpected length: expected %d, got %zu for %d", state_it->second.size, dat.size(), cmsg.getAddress());
// continue;
//}
std::vector<uint8_t> data(dat.size(), 0);
memcpy(data.data(), dat.begin(), dat.size());
state_it->second.parse(nanos, data);
}
// update bus timeout
if (!bus_empty) {
last_nonempty_nanos = nanos;
}
bus_timeout = (nanos - last_nonempty_nanos) > bus_timeout_threshold;
}
#endif
void CANParser::UpdateCans(uint64_t nanos, const capnp::DynamicStruct::Reader& cmsg) {
// assume message struct is `cereal::CanData` and parse
assert(cmsg.has("address") && cmsg.has("src") && cmsg.has("dat") && cmsg.has("busTime"));
if (cmsg.get("src").as<uint8_t>() != bus) {
DEBUG("skip %d: wrong bus\n", cmsg.get("address").as<uint32_t>());
return;
}
auto state_it = message_states.find(cmsg.get("address").as<uint32_t>());
if (state_it == message_states.end()) {
DEBUG("skip %d: not specified\n", cmsg.get("address").as<uint32_t>());
return;
}
auto dat = cmsg.get("dat").as<capnp::Data>();
if (dat.size() > 64) return; // shouldn't ever happen
std::vector<uint8_t> data(dat.size(), 0);
memcpy(data.data(), dat.begin(), dat.size());
state_it->second.parse(nanos, data);
}
void CANParser::UpdateValid(uint64_t nanos) {
const bool show_missing = (last_nanos - first_nanos) > 8e9;
bool _valid = true;
bool _counters_valid = true;
for (const auto& kv : message_states) {
const auto& state = kv.second;
if (state.counter_fail >= MAX_BAD_COUNTER) {
_counters_valid = false;
}
const bool missing = state.last_seen_nanos == 0;
const bool timed_out = (nanos - state.last_seen_nanos) > state.check_threshold;
if (state.check_threshold > 0 && (missing || timed_out)) {
if (show_missing && !bus_timeout) {
if (missing) {
LOGE("0x%X '%s' NOT SEEN", state.address, state.name.c_str());
} else if (timed_out) {
LOGE("0x%X '%s' TIMED OUT", state.address, state.name.c_str());
}
}
_valid = false;
}
}
can_invalid_cnt = _valid ? 0 : (can_invalid_cnt + 1);
can_valid = (can_invalid_cnt < CAN_INVALID_CNT) && _counters_valid;
}
void CANParser::query_latest(std::vector<SignalValue> &vals, uint64_t last_ts) {
if (last_ts == 0) {
last_ts = last_nanos;
}
for (auto& kv : message_states) {
auto& state = kv.second;
if (last_ts != 0 && state.last_seen_nanos < last_ts) {
continue;
}
for (int i = 0; i < state.parse_sigs.size(); i++) {
const Signal &sig = state.parse_sigs[i];
SignalValue &v = vals.emplace_back();
v.address = state.address;
v.ts_nanos = state.last_seen_nanos;
v.name = sig.name;
v.value = state.vals[i];
v.all_values = state.all_vals[i];
state.all_vals[i].clear();
}
}
}

2
opendbc/can/parser.py Normal file
View File

@@ -0,0 +1,2 @@
from opendbc.can.parser_pyx import CANParser, CANDefine # pylint: disable=no-name-in-module, import-error
assert CANParser, CANDefine

142
opendbc/can/parser_pyx.pyx Normal file
View File

@@ -0,0 +1,142 @@
# distutils: language = c++
# cython: c_string_encoding=ascii, language_level=3
from cython.operator cimport dereference as deref, preincrement as preinc
from libcpp.pair cimport pair
from libcpp.string cimport string
from libcpp.vector cimport vector
from libcpp.unordered_set cimport unordered_set
from libc.stdint cimport uint32_t
from .common cimport CANParser as cpp_CANParser
from .common cimport dbc_lookup, SignalValue, DBC
import numbers
from collections import defaultdict
cdef class CANParser:
cdef:
cpp_CANParser *can
const DBC *dbc
vector[SignalValue] can_values
cdef readonly:
dict vl
dict vl_all
dict ts_nanos
string dbc_name
def __init__(self, dbc_name, messages, bus=0):
self.dbc_name = dbc_name
self.dbc = dbc_lookup(dbc_name)
if not self.dbc:
raise RuntimeError(f"Can't find DBC: {dbc_name}")
self.vl = {}
self.vl_all = {}
self.ts_nanos = {}
msg_name_to_address = {}
address_to_msg_name = {}
for i in range(self.dbc[0].msgs.size()):
msg = self.dbc[0].msgs[i]
name = msg.name.decode("utf8")
msg_name_to_address[name] = msg.address
address_to_msg_name[msg.address] = name
# Convert message names into addresses and check existence in DBC
cdef vector[pair[uint32_t, int]] message_v
for i in range(len(messages)):
c = messages[i]
address = c[0] if isinstance(c[0], numbers.Number) else msg_name_to_address.get(c[0])
if address not in address_to_msg_name:
raise RuntimeError(f"could not find message {repr(c[0])} in DBC {self.dbc_name}")
message_v.push_back((address, c[1]))
name = address_to_msg_name[address]
self.vl[address] = {}
self.vl[name] = self.vl[address]
self.vl_all[address] = {}
self.vl_all[name] = self.vl_all[address]
self.ts_nanos[address] = {}
self.ts_nanos[name] = self.ts_nanos[address]
self.can = new cpp_CANParser(bus, dbc_name, message_v)
self.update_strings([])
def update_strings(self, strings, sendcan=False):
for v in self.vl_all.values():
for l in v.values(): # no-cython-lint
l.clear()
cdef vector[SignalValue] new_vals
cdef unordered_set[uint32_t] updated_addrs
self.can.update_strings(strings, new_vals, sendcan)
cdef vector[SignalValue].iterator it = new_vals.begin()
cdef SignalValue* cv
while it != new_vals.end():
cv = &deref(it)
# Cast char * directly to unicode
cv_name = <unicode>cv.name
self.vl[cv.address][cv_name] = cv.value
self.vl_all[cv.address][cv_name] = cv.all_values
self.ts_nanos[cv.address][cv_name] = cv.ts_nanos
updated_addrs.insert(cv.address)
preinc(it)
return updated_addrs
@property
def can_valid(self):
return self.can.can_valid
@property
def bus_timeout(self):
return self.can.bus_timeout
cdef class CANDefine():
cdef:
const DBC *dbc
cdef public:
dict dv
string dbc_name
def __init__(self, dbc_name):
self.dbc_name = dbc_name
self.dbc = dbc_lookup(dbc_name)
if not self.dbc:
raise RuntimeError(f"Can't find DBC: '{dbc_name}'")
address_to_msg_name = {}
for i in range(self.dbc[0].msgs.size()):
msg = self.dbc[0].msgs[i]
name = msg.name.decode("utf8")
address = msg.address
address_to_msg_name[address] = name
dv = defaultdict(dict)
for i in range(self.dbc[0].vals.size()):
val = self.dbc[0].vals[i]
sgname = val.name.decode("utf8")
def_val = val.def_val.decode("utf8")
address = val.address
msgname = address_to_msg_name[address]
# separate definition/value pairs
def_val = def_val.split()
values = [int(v) for v in def_val[::2]]
defs = def_val[1::2]
# two ways to lookup: address or msg name
dv[address][sgname] = dict(zip(values, defs))
dv[msgname][sgname] = dv[address][sgname]
self.dv = dict(dv)