diff --git a/tools/cabana/dbc/dbcfile.cc b/tools/cabana/dbc/dbcfile.cc index bf246fd342..4a1c52819a 100644 --- a/tools/cabana/dbc/dbcfile.cc +++ b/tools/cabana/dbc/dbcfile.cc @@ -3,7 +3,6 @@ #include #include #include -#include 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; - } - 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)); + 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; } - 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_ (?
\w+) (?\w+) *: (?\w+) (?\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_ *(?
\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 = 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) { diff --git a/tools/cabana/dbc/dbcfile.h b/tools/cabana/dbc/dbcfile.h index 29f19a80e4..20de04a861 100644 --- a/tools/cabana/dbc/dbcfile.h +++ b/tools/cabana/dbc/dbcfile.h @@ -1,6 +1,7 @@ #pragma once #include +#include #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 msgs; QString name_;