|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|
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_ (?<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) { |
|
|
|
|