cabana: improve DBCFile::parse() (#32160)
improve parse() old-commit-hash: a6396be53e54aa81644a10c2936706d0f1a2d6e8
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
|
||||
DBCFile::DBCFile(const QString &dbc_file_name) {
|
||||
QFile file(dbc_file_name);
|
||||
@@ -76,117 +75,44 @@ cabana::Msg *DBCFile::msg(const QString &name) {
|
||||
return it != msgs.end() ? &(it->second) : nullptr;
|
||||
}
|
||||
|
||||
cabana::Signal *DBCFile::signal(uint32_t address, const QString name) {
|
||||
auto m = msg(address);
|
||||
return m ? (cabana::Signal *)m->sig(name) : nullptr;
|
||||
}
|
||||
|
||||
void DBCFile::parse(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 msg_comment_regexp(R"(^CM_ BO_ *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)");
|
||||
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)");
|
||||
static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))");
|
||||
msgs.clear();
|
||||
|
||||
int line_num = 0;
|
||||
QString line;
|
||||
auto dbc_assert = [&line_num, &line, this](bool condition, const QString &msg = "") {
|
||||
if (!condition) throw std::runtime_error(QString("[%1:%2]%3: %4").arg(filename).arg(line_num).arg(msg).arg(line).toStdString());
|
||||
};
|
||||
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;
|
||||
};
|
||||
|
||||
msgs.clear();
|
||||
QTextStream stream((QString *)&content);
|
||||
cabana::Msg *current_msg = nullptr;
|
||||
int multiplexor_cnt = 0;
|
||||
bool seen_first = false;
|
||||
QTextStream stream((QString *)&content);
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
++line_num;
|
||||
QString raw_line = stream.readLine();
|
||||
line = raw_line.trimmed();
|
||||
|
||||
bool seen = true;
|
||||
if (line.startsWith("BO_ ")) {
|
||||
multiplexor_cnt = 0;
|
||||
auto match = bo_regexp.match(line);
|
||||
dbc_assert(match.hasMatch());
|
||||
auto address = match.captured(1).toUInt();
|
||||
dbc_assert(msgs.count(address) == 0, QString("Duplicate message address: %1").arg(address));
|
||||
current_msg = &msgs[address];
|
||||
current_msg->address = address;
|
||||
current_msg->name = match.captured(2);
|
||||
current_msg->size = match.captured(3).toULong();
|
||||
current_msg->transmitter = match.captured(4).trimmed();
|
||||
} else if (line.startsWith("SG_ ")) {
|
||||
int offset = 0;
|
||||
auto match = sg_regexp.match(line);
|
||||
if (!match.hasMatch()) {
|
||||
match = sgm_regexp.match(line);
|
||||
offset = 1;
|
||||
try {
|
||||
if (line.startsWith("BO_ ")) {
|
||||
multiplexor_cnt = 0;
|
||||
current_msg = parseBO(line);
|
||||
} else if (line.startsWith("SG_ ")) {
|
||||
parseSG(line, current_msg, multiplexor_cnt);
|
||||
} else if (line.startsWith("VAL_ ")) {
|
||||
parseVAL(line);
|
||||
} else if (line.startsWith("CM_ BO_")) {
|
||||
parseCM_BO(line, content, raw_line, stream);
|
||||
} else if (line.startsWith("CM_ SG_ ")) {
|
||||
parseCM_SG(line, content, raw_line, stream);
|
||||
} else {
|
||||
seen = false;
|
||||
}
|
||||
dbc_assert(match.hasMatch());
|
||||
dbc_assert(current_msg, "No Message");
|
||||
auto name = match.captured(1);
|
||||
dbc_assert(current_msg->sig(name) == nullptr, "Duplicate signal name");
|
||||
cabana::Signal s{};
|
||||
if (offset == 1) {
|
||||
auto indicator = match.captured(2);
|
||||
if (indicator == "M") {
|
||||
// Only one signal within a single message can be the multiplexer switch.
|
||||
dbc_assert(++multiplexor_cnt < 2, "Multiple multiplexor");
|
||||
s.type = cabana::Signal::Type::Multiplexor;
|
||||
} else {
|
||||
s.type = cabana::Signal::Type::Multiplexed;
|
||||
s.multiplex_value = indicator.mid(1).toInt();
|
||||
}
|
||||
}
|
||||
s.name = name;
|
||||
s.start_bit = match.captured(offset + 2).toInt();
|
||||
s.size = match.captured(offset + 3).toInt();
|
||||
s.is_little_endian = match.captured(offset + 4).toInt() == 1;
|
||||
s.is_signed = match.captured(offset + 5) == "-";
|
||||
s.factor = match.captured(offset + 6).toDouble();
|
||||
s.offset = match.captured(offset + 7).toDouble();
|
||||
s.min = match.captured(8 + offset).toDouble();
|
||||
s.max = match.captured(9 + offset).toDouble();
|
||||
s.unit = match.captured(10 + offset);
|
||||
s.receiver_name = match.captured(11 + offset).trimmed();
|
||||
|
||||
current_msg->sigs.push_back(new cabana::Signal(s));
|
||||
} else if (line.startsWith("VAL_ ")) {
|
||||
auto match = val_regexp.match(line);
|
||||
dbc_assert(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.toDouble(), desc});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (line.startsWith("CM_ BO_")) {
|
||||
if (!line.endsWith("\";")) {
|
||||
int pos = stream.pos() - raw_line.length() - 1;
|
||||
line = content.mid(pos, content.indexOf("\";", pos));
|
||||
}
|
||||
auto match = msg_comment_regexp.match(line);
|
||||
dbc_assert(match.hasMatch());
|
||||
if (auto m = (cabana::Msg *)msg(match.captured(1).toUInt())) {
|
||||
m->comment = match.captured(2).trimmed().replace("\\\"", "\"");
|
||||
}
|
||||
} else if (line.startsWith("CM_ SG_ ")) {
|
||||
if (!line.endsWith("\";")) {
|
||||
int pos = stream.pos() - raw_line.length() - 1;
|
||||
line = content.mid(pos, content.indexOf("\";", pos));
|
||||
}
|
||||
auto match = sg_comment_regexp.match(line);
|
||||
dbc_assert(match.hasMatch());
|
||||
if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) {
|
||||
s->comment = match.captured(3).trimmed().replace("\\\"", "\"");
|
||||
}
|
||||
} else {
|
||||
seen = false;
|
||||
} catch (std::exception &e) {
|
||||
throw std::runtime_error(QString("[%1:%2]%3: %4").arg(filename).arg(line_num).arg(e.what()).arg(line).toStdString());
|
||||
}
|
||||
|
||||
if (seen) {
|
||||
@@ -201,6 +127,127 @@ void DBCFile::parse(const QString &content) {
|
||||
}
|
||||
}
|
||||
|
||||
cabana::Msg *DBCFile::parseBO(const QString &line) {
|
||||
static QRegularExpression bo_regexp(R"(^BO_ (?<address>\w+) (?<name>\w+) *: (?<size>\w+) (?<transmitter>\w+))");
|
||||
|
||||
QRegularExpressionMatch match = bo_regexp.match(line);
|
||||
if (!match.hasMatch())
|
||||
throw std::runtime_error("Invalid BO_ line format");
|
||||
|
||||
uint32_t address = match.captured("address").toUInt();
|
||||
if (msgs.count(address) > 0)
|
||||
throw std::runtime_error(QString("Duplicate message address: %1").arg(address).toStdString());
|
||||
|
||||
// Create a new message object
|
||||
cabana::Msg *msg = &msgs[address];
|
||||
msg->address = address;
|
||||
msg->name = match.captured("name");
|
||||
msg->size = match.captured("size").toULong();
|
||||
msg->transmitter = match.captured("transmitter").trimmed();
|
||||
return msg;
|
||||
}
|
||||
|
||||
void DBCFile::parseCM_BO(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream) {
|
||||
static QRegularExpression msg_comment_regexp(R"(^CM_ BO_ *(?<address>\w+) *\"(?<comment>(?:[^"\\]|\\.)*)\"\s*;)");
|
||||
|
||||
QString parse_line = line;
|
||||
if (!parse_line.endsWith("\";")) {
|
||||
int pos = stream.pos() - raw_line.length() - 1;
|
||||
parse_line = content.mid(pos, content.indexOf("\";", pos));
|
||||
}
|
||||
auto match = msg_comment_regexp.match(parse_line);
|
||||
if (!match.hasMatch())
|
||||
throw std::runtime_error("Invalid message comment format");
|
||||
|
||||
if (auto m = (cabana::Msg *)msg(match.captured("address").toUInt()))
|
||||
m->comment = match.captured("comment").trimmed().replace("\\\"", "\"");
|
||||
}
|
||||
|
||||
void DBCFile::parseSG(const QString &line, cabana::Msg *current_msg, int &multiplexor_cnt) {
|
||||
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]+)\] \"(.*)\" (.*))");
|
||||
|
||||
if (!current_msg)
|
||||
throw std::runtime_error("No Message");
|
||||
|
||||
int offset = 0;
|
||||
auto match = sg_regexp.match(line);
|
||||
if (!match.hasMatch()) {
|
||||
match = sgm_regexp.match(line);
|
||||
offset = 1;
|
||||
}
|
||||
if (!match.hasMatch())
|
||||
throw std::runtime_error("Invalid SG_ line format");
|
||||
|
||||
QString name = match.captured(1);
|
||||
if (current_msg->sig(name) != nullptr)
|
||||
throw std::runtime_error("Duplicate signal name");
|
||||
|
||||
cabana::Signal s{};
|
||||
if (offset == 1) {
|
||||
auto indicator = match.captured(2);
|
||||
if (indicator == "M") {
|
||||
++multiplexor_cnt;
|
||||
// Only one signal within a single message can be the multiplexer switch.
|
||||
if (multiplexor_cnt >= 2)
|
||||
throw std::runtime_error("Multiple multiplexor");
|
||||
|
||||
s.type = cabana::Signal::Type::Multiplexor;
|
||||
} else {
|
||||
s.type = cabana::Signal::Type::Multiplexed;
|
||||
s.multiplex_value = indicator.mid(1).toInt();
|
||||
}
|
||||
}
|
||||
s.name = name;
|
||||
s.start_bit = match.captured(offset + 2).toInt();
|
||||
s.size = match.captured(offset + 3).toInt();
|
||||
s.is_little_endian = match.captured(offset + 4).toInt() == 1;
|
||||
s.is_signed = match.captured(offset + 5) == "-";
|
||||
s.factor = match.captured(offset + 6).toDouble();
|
||||
s.offset = match.captured(offset + 7).toDouble();
|
||||
s.min = match.captured(8 + offset).toDouble();
|
||||
s.max = match.captured(9 + offset).toDouble();
|
||||
s.unit = match.captured(10 + offset);
|
||||
s.receiver_name = match.captured(11 + offset).trimmed();
|
||||
current_msg->sigs.push_back(new cabana::Signal(s));
|
||||
}
|
||||
|
||||
void DBCFile::parseCM_SG(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream) {
|
||||
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"((?:[^"\\]|\\.)*)\"\s*;)");
|
||||
|
||||
QString parse_line = line;
|
||||
if (!parse_line.endsWith("\";")) {
|
||||
int pos = stream.pos() - raw_line.length() - 1;
|
||||
parse_line = content.mid(pos, content.indexOf("\";", pos));
|
||||
}
|
||||
auto match = sg_comment_regexp.match(parse_line);
|
||||
if (!match.hasMatch())
|
||||
throw std::runtime_error("Invalid CM_ SG_ line format");
|
||||
|
||||
if (auto s = signal(match.captured(1).toUInt(), match.captured(2))) {
|
||||
s->comment = match.captured(3).trimmed().replace("\\\"", "\"");
|
||||
}
|
||||
}
|
||||
|
||||
void DBCFile::parseVAL(const QString &line) {
|
||||
static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))");
|
||||
|
||||
auto match = val_regexp.match(line);
|
||||
if (!match.hasMatch())
|
||||
throw std::runtime_error("invalid VAL_ line format");
|
||||
|
||||
if (auto s = signal(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.toDouble(), desc});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString DBCFile::generateDBC() {
|
||||
QString dbc_string, comment, val_desc;
|
||||
for (const auto &[address, m] : msgs) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "tools/cabana/dbc/dbc.h"
|
||||
|
||||
@@ -26,6 +27,7 @@ public:
|
||||
cabana::Msg *msg(uint32_t address);
|
||||
cabana::Msg *msg(const QString &name);
|
||||
inline cabana::Msg *msg(const MessageId &id) { return msg(id.address); }
|
||||
cabana::Signal *signal(uint32_t address, const QString name);
|
||||
|
||||
inline QString name() const { return name_.isEmpty() ? "untitled" : name_; }
|
||||
inline bool isEmpty() const { return msgs.empty() && name_.isEmpty(); }
|
||||
@@ -34,6 +36,12 @@ public:
|
||||
|
||||
private:
|
||||
void parse(const QString &content);
|
||||
cabana::Msg *parseBO(const QString &line);
|
||||
void parseSG(const QString &line, cabana::Msg *current_msg, int &multiplexor_cnt);
|
||||
void parseCM_BO(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream);
|
||||
void parseCM_SG(const QString &line, const QString &content, const QString &raw_line, const QTextStream &stream);
|
||||
void parseVAL(const QString &line);
|
||||
|
||||
QString header;
|
||||
std::map<uint32_t, cabana::Msg> msgs;
|
||||
QString name_;
|
||||
|
||||
Reference in New Issue
Block a user