openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

214 lines
7.2 KiB

#include "tools/cabana/dbcmanager.h"
#include <QDebug>
#include <QFile>
#include <QRegularExpression>
#include <QTextStream>
#include <QVector>
#include <limits>
#include <sstream>
bool DBCManager::open(const QString &dbc_file_name, QString *error) {
QString opendbc_file_path = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, dbc_file_name);
QFile file(opendbc_file_path);
if (file.open(QIODevice::ReadOnly)) {
return open(dbc_file_name, file.readAll(), error);
}
return false;
}
bool DBCManager::open(const QString &name, const QString &content, QString *error) {
try {
std::istringstream stream(content.toStdString());
auto dbc = const_cast<DBC *>(dbc_parse_from_stream(name.toStdString(), stream));
msgs.clear();
for (auto &msg : dbc->msgs) {
auto &m = msgs[msg.address];
m.name = msg.name.c_str();
m.size = msg.size;
for (auto &s : msg.sigs) {
m.sigs.push_back({});
auto &sig = m.sigs.last();
sig.name = s.name.c_str();
sig.start_bit = s.start_bit;
sig.msb = s.msb;
sig.lsb = s.lsb;
sig.size = s.size;
sig.is_signed = s.is_signed;
sig.factor = s.factor;
sig.offset = s.offset;
sig.is_little_endian = s.is_little_endian;
sig.updatePrecision();
}
}
parseExtraInfo(content);
name_ = name;
emit DBCFileChanged();
delete dbc;
} catch (std::exception &e) {
if (error) *error = e.what();
return false;
}
return true;
}
void DBCManager::parseExtraInfo(const QString &content) {
static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))");
static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"(.*)\";)");
static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (.*);)");
auto get_sig = [this](uint32_t address, const QString &name) -> cabana::Signal * {
auto m = (cabana::Msg *)msg(address);
return m ? (cabana::Signal *)m->sig(name) : nullptr;
};
QTextStream stream((QString *)&content);
uint32_t address = 0;
while (!stream.atEnd()) {
QString line = stream.readLine().trimmed();
if (line.startsWith("BO_ ")) {
if (auto match = bo_regexp.match(line); match.hasMatch()) {
address = match.captured(1).toUInt();
}
} else if (line.startsWith("SG_ ")) {
int offset = 0;
auto match = sg_regexp.match(line);
if (!match.hasMatch()) {
match = sgm_regexp.match(line);
offset = 1;
}
if (match.hasMatch()) {
if (auto s = get_sig(address, match.captured(1))) {
s->min = match.captured(8 + offset);
s->max = match.captured(9 + offset);
s->unit = match.captured(10 + offset);
}
}
} else if (line.startsWith("VAL_ ")) {
if (auto match = val_regexp.match(line); match.hasMatch()) {
if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) {
QStringList desc_list = match.captured(3).trimmed().split('"');
for (int i = 0; i < desc_list.size(); i += 2) {
auto val = desc_list[i].trimmed();
if (!val.isEmpty() && (i + 1) < desc_list.size()) {
auto desc = desc_list[i+1].trimmed();
s->val_desc.push_back({val, desc});
}
}
}
}
} else if (line.startsWith("CM_ SG_ ")) {
if (auto match = sg_comment_regexp.match(line); match.hasMatch()) {
if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) {
s->comment = match.captured(3).trimmed();
}
}
}
}
}
QString DBCManager::generateDBC() {
QString dbc_string, signal_comment, val_desc;
for (auto &[address, m] : msgs) {
dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(address).arg(m.name).arg(m.size);
for (auto sig : m.getSignals()) {
dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [%8|%9] \"%10\" XXX\n")
.arg(sig->name)
.arg(sig->start_bit)
.arg(sig->size)
.arg(sig->is_little_endian ? '1' : '0')
.arg(sig->is_signed ? '-' : '+')
.arg(sig->factor, 0, 'g', std::numeric_limits<double>::digits10)
.arg(sig->offset, 0, 'g', std::numeric_limits<double>::digits10)
.arg(sig->min)
.arg(sig->max)
.arg(sig->unit);
if (!sig->comment.isEmpty()) {
signal_comment += QString("CM_ SG_ %1 %2 \"%3\";\n").arg(address).arg(sig->name).arg(sig->comment);
}
if (!sig->val_desc.isEmpty()) {
QStringList text;
for (auto &[val, desc] : sig->val_desc) {
text << QString("%1 \"%2\"").arg(val, desc);
}
val_desc += QString("VAL_ %1 %2 %3;\n").arg(address).arg(sig->name).arg(text.join(" "));
}
}
dbc_string += "\n";
}
return dbc_string + signal_comment + val_desc;
}
void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size) {
auto &m = msgs[id.address];
m.name = name;
m.size = size;
// This DBC applies to all active sources, emit for every source
for (uint8_t source : sources) {
emit msgUpdated({.source = source, .address = id.address});
}
}
void DBCManager::removeMsg(const MessageId &id) {
msgs.erase(id.address);
// This DBC applies to all active sources, emit for every source
for (uint8_t source : sources) {
emit msgRemoved({.source = source, .address = id.address});
}
}
void DBCManager::addSignal(const MessageId &id, const cabana::Signal &sig) {
if (auto m = const_cast<cabana::Msg *>(msg(id.address))) {
m->sigs.push_back(sig);
auto s = &m->sigs.last();
// This DBC applies to all active sources, emit for every source
for (uint8_t source : sources) {
emit signalAdded({.source = source, .address = id.address}, s);
}
}
}
void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig) {
if (auto m = const_cast<cabana::Msg *>(msg(id))) {
if (auto s = (cabana::Signal *)m->sig(sig_name)) {
*s = sig;
emit signalUpdated(s);
}
}
}
void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) {
if (auto m = const_cast<cabana::Msg *>(msg(id))) {
auto it = std::find_if(m->sigs.begin(), m->sigs.end(), [&](auto &s) { return s.name == sig_name; });
if (it != m->sigs.end()) {
emit signalRemoved(&(*it));
m->sigs.erase(it);
}
}
}
QStringList DBCManager::signalNames() {
// Used for autocompletion
QStringList ret;
for (auto const& [_, msg] : msgs) {
for (auto sig: msg.getSignals()) {
ret << sig->name;
}
}
ret.sort();
ret.removeDuplicates();
return ret;
}
void DBCManager::updateSources(const QSet<uint8_t> &s) {
sources = s;
}
DBCManager *dbc() {
static DBCManager dbc_manager(nullptr);
return &dbc_manager;
}