cabana: refactor dbc manager (#28445)

* rafactor signal

* cleanup

* refacto dbcmanager

* refactor dbcmanger
old-commit-hash: ff4aae85fe
beeps
Dean Lee 2 years ago committed by GitHub
parent 97e62cf6c5
commit eba8100ea5
  1. 9
      tools/cabana/binaryview.cc
  2. 12
      tools/cabana/chart/chart.cc
  3. 2
      tools/cabana/chart/chart.h
  4. 2
      tools/cabana/chart/signalselector.cc
  5. 2
      tools/cabana/chart/sparkline.cc
  6. 113
      tools/cabana/dbc/dbc.cc
  7. 88
      tools/cabana/dbc/dbc.h
  8. 118
      tools/cabana/dbc/dbcfile.cc
  9. 33
      tools/cabana/dbc/dbcfile.h
  10. 295
      tools/cabana/dbc/dbcmanager.cc
  11. 77
      tools/cabana/dbc/dbcmanager.h
  12. 2
      tools/cabana/historylog.cc
  13. 2
      tools/cabana/historylog.h
  14. 58
      tools/cabana/mainwin.cc
  15. 1
      tools/cabana/mainwin.h
  16. 2
      tools/cabana/messageswidget.cc
  17. 10
      tools/cabana/signalview.cc
  18. 28
      tools/cabana/tests/test_cabana.cc
  19. 6
      tools/cabana/tools/findsimilarbits.cc
  20. 20
      tools/cabana/util.cc
  21. 5
      tools/cabana/util.h

@ -165,7 +165,7 @@ void BinaryView::mousePressEvent(QMouseEvent *event) {
if (bit_idx == s->lsb || bit_idx == s->msb) { if (bit_idx == s->lsb || bit_idx == s->msb) {
anchor_index = model->bitIndex(bit_idx == s->lsb ? s->msb : s->lsb, true); anchor_index = model->bitIndex(bit_idx == s->lsb ? s->msb : s->lsb, true);
resize_sig = s; resize_sig = s;
delegate->selection_color = getColor(s); delegate->selection_color = s->color;
break; break;
} }
} }
@ -381,7 +381,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
if (item->sigs.size() > 0) { if (item->sigs.size() > 0) {
for (auto &s : item->sigs) { for (auto &s : item->sigs) {
if (s == bin_view->hovered_sig) { if (s == bin_view->hovered_sig) {
painter->fillRect(option.rect, getColor(bin_view->hovered_sig).darker(125)); // 4/5x brightness painter->fillRect(option.rect, s->color.darker(125)); // 4/5x brightness
} else { } else {
drawSignalCell(painter, option, index, s); drawSignalCell(painter, option, index, s);
} }
@ -434,15 +434,14 @@ void BinaryItemDelegate::drawSignalCell(QPainter *painter, const QStyleOptionVie
painter->setClipRegion(QRegion(rc).subtracted(subtract)); painter->setClipRegion(QRegion(rc).subtracted(subtract));
auto item = (const BinaryViewModel::Item *)index.internalPointer(); auto item = (const BinaryViewModel::Item *)index.internalPointer();
auto sig_color = getColor(sig); QColor color = sig->color;
QColor color = sig_color;
color.setAlpha(item->bg_color.alpha()); color.setAlpha(item->bg_color.alpha());
// Mixing the signal colour with the Base background color to fade it // Mixing the signal colour with the Base background color to fade it
painter->fillRect(rc, QApplication::palette().color(QPalette::Base)); painter->fillRect(rc, QApplication::palette().color(QPalette::Base));
painter->fillRect(rc, color); painter->fillRect(rc, color);
// Draw edges // Draw edges
color = sig_color.darker(125); color = sig->color.darker(125);
painter->setPen(QPen(color, 1)); painter->setPen(QPen(color, 1));
if (draw_left) painter->drawLine(rc.topLeft(), rc.bottomLeft()); if (draw_left) painter->drawLine(rc.topLeft(), rc.bottomLeft());
if (draw_right) painter->drawLine(rc.topRight(), rc.bottomRight()); if (draw_right) painter->drawLine(rc.topRight(), rc.bottomRight());

@ -38,7 +38,6 @@ ChartView::ChartView(const std::pair<double, double> &x_range, ChartsWidget *par
setChart(chart); setChart(chart);
createToolButtons(); createToolButtons();
// TODO: enable zoomIn/seekTo in live streaming mode.
setRubberBand(QChartView::HorizontalRubberBand); setRubberBand(QChartView::HorizontalRubberBand);
setMouseTracking(true); setMouseTracking(true);
setTheme(settings.theme == DARK_THEME ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight); setTheme(settings.theme == DARK_THEME ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight);
@ -107,14 +106,14 @@ void ChartView::setTheme(QChart::ChartTheme theme) {
chart()->legend()->setLabelColor(palette().color(QPalette::Text)); chart()->legend()->setLabelColor(palette().color(QPalette::Text));
} }
for (auto &s : sigs) { for (auto &s : sigs) {
s.series->setColor(getColor(s.sig)); s.series->setColor(s.sig->color);
} }
} }
void ChartView::addSignal(const MessageId &msg_id, const cabana::Signal *sig) { void ChartView::addSignal(const MessageId &msg_id, const cabana::Signal *sig) {
if (hasSignal(msg_id, sig)) return; if (hasSignal(msg_id, sig)) return;
QXYSeries *series = createSeries(series_type, getColor(sig)); QXYSeries *series = createSeries(series_type, sig->color);
sigs.push_back({.msg_id = msg_id, .sig = sig, .series = series}); sigs.push_back({.msg_id = msg_id, .sig = sig, .series = series});
updateSeries(sig); updateSeries(sig);
updateSeriesPoints(); updateSeriesPoints();
@ -154,8 +153,9 @@ void ChartView::signalUpdated(const cabana::Signal *sig) {
} }
void ChartView::msgUpdated(MessageId id) { void ChartView::msgUpdated(MessageId id) {
if (std::any_of(sigs.cbegin(), sigs.cend(), [=](auto &s) { return s.msg_id == id; })) if (std::any_of(sigs.cbegin(), sigs.cend(), [=](auto &s) { return s.msg_id.address == id.address; })) {
updateTitle(); updateTitle();
}
} }
void ChartView::manageSignals() { void ChartView::manageSignals() {
@ -277,7 +277,7 @@ void ChartView::updateSeries(const cabana::Signal *sig, bool clear) {
s.step_vals.clear(); s.step_vals.clear();
s.last_value_mono_time = 0; s.last_value_mono_time = 0;
} }
s.series->setColor(getColor(s.sig)); s.series->setColor(s.sig->color);
const auto &msgs = can->events(s.msg_id); const auto &msgs = can->events(s.msg_id);
s.vals.reserve(msgs.capacity()); s.vals.reserve(msgs.capacity());
@ -799,7 +799,7 @@ void ChartView::setSeriesType(SeriesType type) {
s.series->deleteLater(); s.series->deleteLater();
} }
for (auto &s : sigs) { for (auto &s : sigs) {
auto series = createSeries(series_type, getColor(s.sig)); auto series = createSeries(series_type, s.sig->color);
series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals); series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals);
s.series = series; s.series = series;
} }

@ -56,7 +56,7 @@ private slots:
void manageSignals(); void manageSignals();
void handleMarkerClicked(); void handleMarkerClicked();
void msgUpdated(MessageId id); void msgUpdated(MessageId id);
void msgRemoved(MessageId id) { removeIf([=](auto &s) { return s.msg_id == id; }); } void msgRemoved(MessageId id) { removeIf([=](auto &s) { return s.msg_id.address == id.address && !dbc()->msg(id); }); }
void signalRemoved(const cabana::Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); } void signalRemoved(const cabana::Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); }
private: private:

@ -91,7 +91,7 @@ void SignalSelector::updateAvailableList(int index) {
} }
void SignalSelector::addItemToList(QListWidget *parent, const MessageId id, const cabana::Signal *sig, bool show_msg_name) { void SignalSelector::addItemToList(QListWidget *parent, const MessageId id, const cabana::Signal *sig, bool show_msg_name) {
QString text = QString("<span style=\"color:%0;\">■ </span> %1").arg(getColor(sig).name(), sig->name); QString text = QString("<span style=\"color:%0;\">■ </span> %1").arg(sig->color.name(), sig->name);
if (show_msg_name) text += QString(" <font color=\"gray\">%0 %1</font>").arg(msgName(id), id.toString()); if (show_msg_name) text += QString(" <font color=\"gray\">%0 %1</font>").arg(msgName(id), id.toString());
QLabel *label = new QLabel(text); QLabel *label = new QLabel(text);

@ -37,7 +37,7 @@ void Sparkline::update(const MessageId &msg_id, const cabana::Signal *sig, doubl
max_val += 1; max_val += 1;
} }
} }
render(getColor(sig), size); render(sig->color, size);
} else { } else {
pixmap = QPixmap(); pixmap = QPixmap();
min_val = -1; min_val = -1;

@ -1,32 +1,96 @@
#include "tools/cabana/dbc/dbc.h" #include "tools/cabana/dbc/dbc.h"
#include "tools/cabana/util.h" #include "tools/cabana/util.h"
uint qHash(const MessageId &item) { uint qHash(const MessageId &item) {
return qHash(item.source) ^ qHash(item.address); return qHash(item.source) ^ qHash(item.address);
} }
std::vector<const cabana::Signal*> cabana::Msg::getSignals() const { // cabana::Msg
std::vector<const Signal*> ret;
ret.reserve(sigs.size()); cabana::Msg::~Msg() {
for (auto &sig : sigs) ret.push_back(&sig); for (auto s : sigs) {
std::sort(ret.begin(), ret.end(), [](auto l, auto r) { delete s;
if (l->start_bit != r->start_bit) { }
return l->start_bit < r->start_bit; }
}
// For VECTOR__INDEPENDENT_SIG_MSG, many signals have same start bit cabana::Signal *cabana::Msg::addSignal(const cabana::Signal &sig) {
return l->name < r->name; auto s = sigs.emplace_back(new cabana::Signal(sig));
}); update();
return ret; return s;
}
cabana::Signal *cabana::Msg::updateSignal(const QString &sig_name, const cabana::Signal &new_sig) {
auto s = sig(sig_name);
if (s) {
*s = new_sig;
update();
}
return s;
}
void cabana::Msg::removeSignal(const QString &sig_name) {
auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s->name == sig_name; });
if (it != sigs.end()) {
delete *it;
sigs.erase(it);
update();
}
}
cabana::Msg &cabana::Msg::operator=(const cabana::Msg &other) {
address = other.address;
name = other.name;
size = other.size;
comment = other.comment;
for (auto s : sigs) delete s;
sigs.clear();
for (auto s : other.sigs) {
sigs.push_back(new cabana::Signal(*s));
}
update();
return *this;
}
cabana::Signal *cabana::Msg::sig(const QString &sig_name) const {
auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s->name == sig_name; });
return it != sigs.end() ? *it : nullptr;
}
int cabana::Msg::indexOf(const cabana::Signal *sig) const {
for (int i = 0; i < sigs.size(); ++i) {
if (sigs[i] == sig) return i;
}
return -1;
} }
void cabana::Msg::updateMask() { QString cabana::Msg::newSignalName() {
QString new_name;
for (int i = 1; /**/; ++i) {
new_name = QString("NEW_SIGNAL_%1").arg(i);
if (sig(new_name) == nullptr) break;
}
return new_name;
}
void cabana::Msg::update() {
mask = QVector<uint8_t>(size, 0x00).toList(); mask = QVector<uint8_t>(size, 0x00).toList();
// sort signals
std::sort(sigs.begin(), sigs.end(), [](auto l, auto r) {
return std::tie(l->start_bit, l->name) < std::tie(r->start_bit, r->name);
});
for (auto &sig : sigs) { for (auto &sig : sigs) {
int i = sig.msb / 8; sig->update();
int bits = sig.size;
// update mask
int i = sig->msb / 8;
int bits = sig->size;
while (i >= 0 && i < size && bits > 0) { while (i >= 0 && i < size && bits > 0) {
int lsb = (int)(sig.lsb / 8) == i ? sig.lsb : i * 8; 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 msb = (int)(sig->msb / 8) == i ? sig->msb : (i + 1) * 8 - 1;
int sz = msb - lsb + 1; int sz = msb - lsb + 1;
int shift = (lsb - (i * 8)); int shift = (lsb - (i * 8));
@ -34,18 +98,27 @@ void cabana::Msg::updateMask() {
mask[i] |= ((1ULL << sz) - 1) << shift; mask[i] |= ((1ULL << sz) - 1) << shift;
bits -= size; bits -= size;
i = sig.is_little_endian ? i - 1 : i + 1; i = sig->is_little_endian ? i - 1 : i + 1;
} }
} }
} }
void cabana::Signal::updatePrecision() { // cabana::Signal
void cabana::Signal::update() {
float h = 19 * (float)lsb / 64.0;
h = fmod(h, 1.0);
size_t hash = qHash(name);
float s = 0.25 + 0.25 * (float)(hash & 0xff) / 255.0;
float v = 0.75 + 0.25 * (float)((hash >> 8) & 0xff) / 255.0;
color = QColor::fromHsvF(h, s, v);
precision = std::max(num_decimals(factor), num_decimals(offset)); precision = std::max(num_decimals(factor), num_decimals(offset));
} }
QString cabana::Signal::formatValue(double value) const { QString cabana::Signal::formatValue(double value) const {
// Show enum string // Show enum string
for (auto &[val, desc] : val_desc) { for (const auto &[val, desc] : val_desc) {
if (std::abs(value - val) < 1e-6) { if (std::abs(value - val) < 1e-6) {
return desc; return desc;
} }

@ -1,9 +1,10 @@
#pragma once #pragma once
#include <limits> #include <QColor>
#include <QList> #include <QList>
#include <QMetaType> #include <QMetaType>
#include <QString> #include <QString>
#include <limits>
#include "opendbc/can/common_dbc.h" #include "opendbc/can/common_dbc.h"
@ -45,41 +46,55 @@ struct std::hash<MessageId> {
typedef QList<std::pair<double, QString>> ValueDescription; typedef QList<std::pair<double, QString>> ValueDescription;
namespace cabana { namespace cabana {
struct Signal {
QString name; class Signal {
int start_bit, msb, lsb, size; public:
bool is_signed; Signal() = default;
double factor, offset; Signal(const Signal &other) = default;
bool is_little_endian; void update();
double min, max; QString formatValue(double value) const;
QString unit;
QString comment; QString name;
ValueDescription val_desc; int start_bit, msb, lsb, size;
int precision = 0; double factor, offset;
void updatePrecision(); bool is_signed;
QString formatValue(double value) const; bool is_little_endian;
}; double min, max;
QString unit;
struct Msg { QString comment;
uint32_t address; ValueDescription val_desc;
QString name; int precision = 0;
uint32_t size; QColor color;
QString comment; };
QList<cabana::Signal> sigs;
class Msg {
QList<uint8_t> mask; public:
void updateMask(); Msg() = default;
Msg(const Msg &other) { *this = other; }
std::vector<const cabana::Signal*> getSignals() const; ~Msg();
const cabana::Signal *sig(const QString &sig_name) const { cabana::Signal *addSignal(const cabana::Signal &sig);
auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.name == sig_name; }); cabana::Signal *updateSignal(const QString &sig_name, const cabana::Signal &sig);
return it != sigs.end() ? &(*it) : nullptr; void removeSignal(const QString &sig_name);
} Msg &operator=(const Msg &other);
}; int indexOf(const cabana::Signal *sig) const;
cabana::Signal *sig(const QString &sig_name) const;
bool operator==(const cabana::Signal &l, const cabana::Signal &r); QString newSignalName();
inline bool operator!=(const cabana::Signal &l, const cabana::Signal &r) { return !(l == r); } inline const std::vector<cabana::Signal *> &getSignals() const { return sigs; }
}
uint32_t address;
QString name;
uint32_t size;
QString comment;
std::vector<cabana::Signal *> sigs;
QList<uint8_t> mask;
void update();
};
bool operator==(const cabana::Signal &l, const cabana::Signal &r);
inline bool operator!=(const cabana::Signal &l, const cabana::Signal &r) { return !(l == r); }
} // namespace cabana
// Helper functions // Helper functions
double get_raw_value(const uint8_t *data, size_t data_size, const cabana::Signal &sig); double get_raw_value(const uint8_t *data, size_t data_size, const cabana::Signal &sig);
@ -89,4 +104,3 @@ void updateSigSizeParamsFromRange(cabana::Signal &s, int start_bit, int size);
std::pair<int, int> getSignalRange(const cabana::Signal *s); std::pair<int, int> getSignalRange(const cabana::Signal *s);
inline std::vector<std::string> allDBCNames() { return get_dbc_names(); } inline std::vector<std::string> allDBCNames() { return get_dbc_names(); }
inline QString doubleToString(double value) { return QString::number(value, 'g', std::numeric_limits<double>::digits10); } inline QString doubleToString(double value) { return QString::number(value, 'g', std::numeric_limits<double>::digits10); }

@ -37,20 +37,18 @@ void DBCFile::open(const QString &content) {
m.name = msg.name.c_str(); m.name = msg.name.c_str();
m.size = msg.size; m.size = msg.size;
for (auto &s : msg.sigs) { for (auto &s : msg.sigs) {
m.sigs.push_back({}); auto sig = m.sigs.emplace_back(new cabana::Signal);
auto &sig = m.sigs.last(); sig->name = s.name.c_str();
sig.name = s.name.c_str(); sig->start_bit = s.start_bit;
sig.start_bit = s.start_bit; sig->msb = s.msb;
sig.msb = s.msb; sig->lsb = s.lsb;
sig.lsb = s.lsb; sig->size = s.size;
sig.size = s.size; sig->is_signed = s.is_signed;
sig.is_signed = s.is_signed; sig->factor = s.factor;
sig.factor = s.factor; sig->offset = s.offset;
sig.offset = s.offset; sig->is_little_endian = s.is_little_endian;
sig.is_little_endian = s.is_little_endian;
sig.updatePrecision();
} }
m.updateMask(); m.update();
} }
parseExtraInfo(content); parseExtraInfo(content);
@ -58,7 +56,7 @@ void DBCFile::open(const QString &content) {
} }
bool DBCFile::save() { bool DBCFile::save() {
assert (!filename.isEmpty()); assert(!filename.isEmpty());
if (writeContents(filename)) { if (writeContents(filename)) {
cleanupAutoSaveFile(); cleanupAutoSaveFile();
return true; return true;
@ -90,41 +88,6 @@ bool DBCFile::writeContents(const QString &fn) {
return false; return false;
} }
cabana::Signal *DBCFile::addSignal(const MessageId &id, const cabana::Signal &sig) {
if (auto m = const_cast<cabana::Msg *>(msg(id.address))) {
m->sigs.push_back(sig);
m->updateMask();
return &m->sigs.last();
}
return nullptr;
}
cabana::Signal *DBCFile::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;
m->updateMask();
return s;
}
}
return nullptr;
}
cabana::Signal *DBCFile::getSignal(const MessageId &id, const QString &sig_name) {
auto m = msg(id);
return m ? (cabana::Signal *)m->sig(sig_name) : nullptr;
}
void DBCFile::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()) {
m->sigs.erase(it);
m->updateMask();
}
}
}
void DBCFile::updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment) { void DBCFile::updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment) {
auto &m = msgs[id.address]; auto &m = msgs[id.address];
m.address = id.address; m.address = id.address;
@ -133,60 +96,17 @@ void DBCFile::updateMsg(const MessageId &id, const QString &name, uint32_t size,
m.comment = comment; m.comment = comment;
} }
void DBCFile::removeMsg(const MessageId &id) { cabana::Msg *DBCFile::msg(uint32_t address) {
msgs.erase(id.address);
}
QString DBCFile::newMsgName(const MessageId &id) {
return QString("NEW_MSG_") + QString::number(id.address, 16).toUpper();
}
QString DBCFile::newSignalName(const MessageId &id) {
auto m = msg(id);
assert(m != nullptr);
QString name;
for (int i = 1; /**/; ++i) {
name = QString("NEW_SIGNAL_%1").arg(i);
if (m->sig(name) == nullptr) break;
}
return name;
}
const QList<uint8_t>& DBCFile::mask(const MessageId &id) const {
auto m = msg(id);
return m ? m->mask : empty_mask;
}
const cabana::Msg *DBCFile::msg(uint32_t address) const {
auto it = msgs.find(address); auto it = msgs.find(address);
return it != msgs.end() ? &it->second : nullptr; return it != msgs.end() ? &it->second : nullptr;
} }
const cabana::Msg* DBCFile::msg(const QString &name) { cabana::Msg *DBCFile::msg(const QString &name) {
auto it = std::find_if(msgs.cbegin(), msgs.cend(), [&name](auto &m) { return m.second.name == name; }); auto it = std::find_if(msgs.begin(), msgs.end(), [&name](auto &m) { return m.second.name == name; });
return it != msgs.cend() ? &(it->second) : nullptr; return it != msgs.end() ? &(it->second) : nullptr;
}
QStringList DBCFile::signalNames() const {
// Used for autocompletion
QStringList ret;
for (auto const& [_, msg] : msgs) {
for (auto sig: msg.getSignals()) {
ret << sig->name;
}
}
ret.sort();
ret.removeDuplicates();
return ret;
}
int DBCFile::signalCount(const MessageId &id) const {
if (msgs.count(id.address) == 0) return 0;
return msgs.at(id.address).sigs.size();
} }
int DBCFile::signalCount() const { int DBCFile::signalCount() {
return std::accumulate(msgs.cbegin(), msgs.cend(), 0, [](int &n, const auto &m) { return n + m.second.sigs.size(); }); return std::accumulate(msgs.cbegin(), msgs.cend(), 0, [](int &n, const auto &m) { return n + m.second.sigs.size(); });
} }
@ -194,8 +114,8 @@ void DBCFile::parseExtraInfo(const QString &content) {
static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))"); 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 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 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+) *\"([^"]*)\";)"); static QRegularExpression msg_comment_regexp(R"(^CM_ BO_ *(\w+) *\"([^"]*)\"\s*;)");
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"([^"]*)\";)"); static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"([^"]*)\"\s*;)");
static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))"); static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))");
int line_num = 0; int line_num = 0;

@ -1,9 +1,7 @@
#pragma once #pragma once
#include <map> #include <map>
#include <QList>
#include <QObject> #include <QObject>
#include <QString>
#include "tools/cabana/dbc/dbc.h" #include "tools/cabana/dbc/dbc.h"
@ -26,30 +24,18 @@ public:
void cleanupAutoSaveFile(); void cleanupAutoSaveFile();
QString generateDBC(); QString generateDBC();
cabana::Signal *addSignal(const MessageId &id, const cabana::Signal &sig);
cabana::Signal *updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig);
cabana::Signal *getSignal(const MessageId &id, const QString &sig_name);
void removeSignal(const MessageId &id, const QString &sig_name);
void updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment); void updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment);
void removeMsg(const MessageId &id); inline void removeMsg(const MessageId &id) { msgs.erase(id.address); }
QString newMsgName(const MessageId &id);
QString newSignalName(const MessageId &id);
const QList<uint8_t>& mask(const MessageId &id) const;
inline std::map<uint32_t, cabana::Msg> getMessages() const { return msgs; } inline const std::map<uint32_t, cabana::Msg> &getMessages() const { return msgs; }
const cabana::Msg *msg(uint32_t address) const; cabana::Msg *msg(uint32_t address);
const cabana::Msg* msg(const QString &name); cabana::Msg *msg(const QString &name);
inline const cabana::Msg *msg(const MessageId &id) const { return msg(id.address); }; inline cabana::Msg *msg(const MessageId &id) { return msg(id.address); }
QStringList signalNames() const; int signalCount();
int signalCount(const MessageId &id) const; inline int msgCount() { return msgs.size(); }
int signalCount() const; inline QString name() { return name_.isEmpty() ? "untitled" : name_; }
inline int msgCount() const { return msgs.size(); } inline bool isEmpty() { return (signalCount() == 0) && name_.isEmpty(); }
inline QString name() const { return name_.isEmpty() ? "untitled" : name_; }
inline bool isEmpty() const { return (signalCount() == 0) && name_.isEmpty(); }
QString filename; QString filename;
@ -57,5 +43,4 @@ private:
void parseExtraInfo(const QString &content); void parseExtraInfo(const QString &content);
std::map<uint32_t, cabana::Msg> msgs; std::map<uint32_t, cabana::Msg> msgs;
QString name_; QString name_;
QList<uint8_t> empty_mask;
}; };

@ -1,21 +1,16 @@
#include "tools/cabana/dbc/dbcmanager.h" #include "tools/cabana/dbc/dbcmanager.h"
#include <algorithm>
bool DBCManager::open(SourceSet s, const QString &dbc_file_name, QString *error) {
for (int i = 0; i < dbc_files.size(); i++) {
auto [ss, dbc_file] = dbc_files[i];
// Check if file is already open, and merge sources
if (dbc_file->filename == dbc_file_name) {
dbc_files[i] = {ss | s, dbc_file};
emit DBCFileChanged(); #include <algorithm>
return true; #include <numeric>
}
}
bool DBCManager::open(const SourceSet &sources, const QString &dbc_file_name, QString *error) {
try { try {
dbc_files.push_back({s, new DBCFile(dbc_file_name, this)}); auto it = std::find_if(dbc_files.begin(), dbc_files.end(),
[&](auto &f) { return f.second && f.second->filename == dbc_file_name; });
auto file = (it != dbc_files.end()) ? it->second : std::make_shared<DBCFile>(dbc_file_name, this);
for (auto s : sources) {
dbc_files[s] = file;
}
} catch (std::exception &e) { } catch (std::exception &e) {
if (error) *error = e.what(); if (error) *error = e.what();
return false; return false;
@ -25,9 +20,12 @@ bool DBCManager::open(SourceSet s, const QString &dbc_file_name, QString *error)
return true; return true;
} }
bool DBCManager::open(SourceSet s, const QString &name, const QString &content, QString *error) { bool DBCManager::open(const SourceSet &sources, const QString &name, const QString &content, QString *error) {
try { try {
dbc_files.push_back({s, new DBCFile(name, content, this)}); auto file = std::make_shared<DBCFile>(name, content, this);
for (auto s : sources) {
dbc_files[s] = file;
}
} catch (std::exception &e) { } catch (std::exception &e) {
if (error) *error = e.what(); if (error) *error = e.what();
return false; return false;
@ -37,263 +35,154 @@ bool DBCManager::open(SourceSet s, const QString &name, const QString &content,
return true; return true;
} }
void DBCManager::close(SourceSet s) { void DBCManager::close(const SourceSet &sources) {
// Build new list of dbc files, removing the ones that match the sourceset for (auto s : sources) {
QList<std::pair<SourceSet, DBCFile*>> new_dbc_files; dbc_files[s] = nullptr;
for (auto entry : dbc_files) {
if (entry.first == s) {
delete entry.second;
} else {
new_dbc_files.push_back(entry);
}
} }
dbc_files = new_dbc_files;
emit DBCFileChanged(); emit DBCFileChanged();
} }
void DBCManager::close(DBCFile *dbc_file) { void DBCManager::close(DBCFile *dbc_file) {
assert(dbc_file != nullptr); for (auto &[_, f] : dbc_files) {
if (f.get() == dbc_file) f = nullptr;
// Build new list of dbc files, removing the one that matches dbc_file*
QList<std::pair<SourceSet, DBCFile*>> new_dbc_files;
for (auto entry : dbc_files) {
if (entry.second == dbc_file) {
delete entry.second;
} else {
new_dbc_files.push_back(entry);
}
} }
dbc_files = new_dbc_files;
emit DBCFileChanged(); emit DBCFileChanged();
} }
void DBCManager::closeAll() { void DBCManager::closeAll() {
if (dbc_files.isEmpty()) return; dbc_files.clear();
while (dbc_files.size()) {
DBCFile *dbc_file = dbc_files.back().second;
dbc_files.pop_back();
delete dbc_file;
}
emit DBCFileChanged(); emit DBCFileChanged();
} }
void DBCManager::removeSourcesFromFile(DBCFile *dbc_file, SourceSet s) {
assert(dbc_file != nullptr);
// Build new list of dbc files, for the given dbc_file* remove s from the current sources
QList<std::pair<SourceSet, DBCFile*>> new_dbc_files;
for (auto entry : dbc_files) {
if (entry.second == dbc_file) {
SourceSet ss = (entry.first == SOURCE_ALL) ? sources : entry.first;
ss -= s;
if (ss.empty()) { // Close file if no more sources remain
delete dbc_file;
} else {
new_dbc_files.push_back({ss, dbc_file});
}
} else {
new_dbc_files.push_back(entry);
}
}
dbc_files = new_dbc_files;
emit DBCFileChanged();
}
void DBCManager::addSignal(const MessageId &id, const cabana::Signal &sig) { void DBCManager::addSignal(const MessageId &id, const cabana::Signal &sig) {
auto sources_dbc_file = findDBCFile(id); if (auto m = msg(id)) {
assert(sources_dbc_file); // Create new DBC? if (auto s = m->addSignal(sig)) {
auto [dbc_sources, dbc_file] = *sources_dbc_file; emit signalAdded(id, s);
cabana::Signal *s = dbc_file->addSignal(id, sig);
if (s != nullptr) {
dbc_sources.insert(id.source);
for (uint8_t source : dbc_sources) {
emit signalAdded({.source = source, .address = id.address}, s);
} }
} }
} }
void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig) { void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig) {
auto sources_dbc_file = findDBCFile(id); if (auto m = msg(id)) {
assert(sources_dbc_file); // This should be impossible if (auto s = m->updateSignal(sig_name, sig)) {
auto [_, dbc_file] = *sources_dbc_file; emit signalUpdated(s);
}
cabana::Signal *s = dbc_file->updateSignal(id, sig_name, sig);
if (s != nullptr) {
emit signalUpdated(s);
} }
} }
void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) { void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) {
auto sources_dbc_file = findDBCFile(id); if (auto m = msg(id)) {
assert(sources_dbc_file); // This should be impossible if (auto s = m->sig(sig_name)) {
auto [_, dbc_file] = *sources_dbc_file; emit signalRemoved(s);
m->removeSignal(sig_name);
cabana::Signal *s = dbc_file->getSignal(id, sig_name); }
if (s != nullptr) {
emit signalRemoved(s);
dbc_file->removeSignal(id, sig_name);
} }
} }
void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment) { void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment) {
auto sources_dbc_file = findDBCFile(id); auto dbc_file = findDBCFile(id);
assert(sources_dbc_file); // This should be impossible assert(dbc_file); // This should be impossible
auto [dbc_sources, dbc_file] = *sources_dbc_file;
dbc_file->updateMsg(id, name, size, comment); dbc_file->updateMsg(id, name, size, comment);
emit msgUpdated(id);
for (uint8_t source : dbc_sources) {
emit msgUpdated({.source = source, .address = id.address});
}
} }
void DBCManager::removeMsg(const MessageId &id) { void DBCManager::removeMsg(const MessageId &id) {
auto sources_dbc_file = findDBCFile(id); auto dbc_file = findDBCFile(id);
assert(sources_dbc_file); // This should be impossible assert(dbc_file); // This should be impossible
auto [dbc_sources, dbc_file] = *sources_dbc_file;
dbc_file->removeMsg(id); dbc_file->removeMsg(id);
emit msgRemoved(id);
for (uint8_t source : dbc_sources) {
emit msgRemoved({.source = source, .address = id.address});
}
} }
QString DBCManager::newMsgName(const MessageId &id) { QString DBCManager::newMsgName(const MessageId &id) {
auto sources_dbc_file = findDBCFile(id); return QString("NEW_MSG_") + QString::number(id.address, 16).toUpper();
assert(sources_dbc_file); // This should be impossible
auto [_, dbc_file] = *sources_dbc_file;
return dbc_file->newMsgName(id);
} }
QString DBCManager::newSignalName(const MessageId &id) { QString DBCManager::newSignalName(const MessageId &id) {
auto sources_dbc_file = findDBCFile(id); auto m = msg(id);
assert(sources_dbc_file); // This should be impossible return m ? m->newSignalName() : "";
auto [_, dbc_file] = *sources_dbc_file;
return dbc_file->newSignalName(id);
} }
const QList<uint8_t>& DBCManager::mask(const MessageId &id) const { const QList<uint8_t> &DBCManager::mask(const MessageId &id) {
auto sources_dbc_file = findDBCFile(id); static QList<uint8_t> empty_mask;
if (!sources_dbc_file) { auto m = msg(id);
return empty_mask; return m ? m->mask : empty_mask;
}
auto [_, dbc_file] = *sources_dbc_file;
return dbc_file->mask(id);
} }
std::map<MessageId, cabana::Msg> DBCManager::getMessages(uint8_t source) { const std::map<uint32_t, cabana::Msg> &DBCManager::getMessages(uint8_t source) {
std::map<MessageId, cabana::Msg> ret; static std::map<uint32_t, cabana::Msg> empty_msgs;
auto dbc_file = findDBCFile(source);
auto sources_dbc_file = findDBCFile({.source = source, .address = 0}); return dbc_file ? dbc_file->getMessages() : empty_msgs;
if (!sources_dbc_file) {
return ret;
}
auto [_, dbc_file] = *sources_dbc_file;
for (auto &[address, msg] : dbc_file->getMessages()) {
MessageId id = {.source = source, .address = address};
ret[id] = msg;
}
return ret;
} }
const cabana::Msg *DBCManager::msg(const MessageId &id) const { cabana::Msg *DBCManager::msg(const MessageId &id) {
auto sources_dbc_file = findDBCFile(id); auto dbc_file = findDBCFile(id);
if (!sources_dbc_file) { return dbc_file ? dbc_file->msg(id) : nullptr;
return nullptr;
}
auto [_, dbc_file] = *sources_dbc_file;
return dbc_file->msg(id);
} }
const cabana::Msg* DBCManager::msg(uint8_t source, const QString &name) { cabana::Msg *DBCManager::msg(uint8_t source, const QString &name) {
auto sources_dbc_file = findDBCFile({.source = source, .address = 0}); auto dbc_file = findDBCFile(source);
if (!sources_dbc_file) { return dbc_file ? dbc_file->msg(name) : nullptr;
return nullptr;
}
auto [_, dbc_file] = *sources_dbc_file;
return dbc_file->msg(name);
} }
QStringList DBCManager::signalNames() const { QStringList DBCManager::signalNames() {
// Used for autocompletion
QStringList ret; QStringList ret;
for (auto &f : allDBCFiles()) {
for (auto &[_, dbc_file] : dbc_files) { for (auto &[_, m] : f->getMessages()) {
ret << dbc_file->signalNames(); for (auto sig : m.getSignals()) {
ret << sig->name;
}
}
} }
ret.sort(); ret.sort();
ret.removeDuplicates(); ret.removeDuplicates();
return ret; return ret;
} }
int DBCManager::signalCount(const MessageId &id) const { int DBCManager::signalCount(const MessageId &id) {
auto sources_dbc_file = findDBCFile(id); auto m = msg(id);
if (!sources_dbc_file) { return m ? m->sigs.size() : 0;
return 0;
}
auto [_, dbc_file] = *sources_dbc_file;
return dbc_file->signalCount(id);
}
int DBCManager::signalCount() const {
int ret = 0;
for (auto &[_, dbc_file] : dbc_files) {
ret += dbc_file->signalCount();
}
return ret;
} }
int DBCManager::msgCount() const { int DBCManager::signalCount() {
int ret = 0; auto files = allDBCFiles();
return std::accumulate(files.cbegin(), files.cend(), 0, [](int &n, auto &f) { return n + f->signalCount(); });
for (auto &[_, dbc_file] : dbc_files) {
ret += dbc_file->msgCount();
}
return ret;
} }
int DBCManager::dbcCount() const { int DBCManager::msgCount() {
return dbc_files.size(); auto files = allDBCFiles();
return std::accumulate(files.cbegin(), files.cend(), 0, [](int &n, auto &f) { return n + f->msgCount(); });
} }
int DBCManager::nonEmptyDBCCount() const { int DBCManager::dbcCount() {
return std::count_if(dbc_files.cbegin(), dbc_files.cend(), [](auto &f) { return !f.second->isEmpty(); }); return allDBCFiles().size();
} }
void DBCManager::updateSources(const SourceSet &s) { int DBCManager::nonEmptyDBCCount() {
sources = s; auto files = allDBCFiles();
return std::count_if(files.cbegin(), files.cend(), [](auto &f) { return !f->isEmpty(); });
} }
std::optional<std::pair<SourceSet, DBCFile*>> DBCManager::findDBCFile(const uint8_t source) const { DBCFile *DBCManager::findDBCFile(const uint8_t source) {
// Find DBC file that matches id.source, fall back to SOURCE_ALL if no specific DBC is found // Find DBC file that matches id.source, fall back to SOURCE_ALL if no specific DBC is found
auto it = dbc_files.count(source) ? dbc_files.find(source) : dbc_files.find(-1);
return it != dbc_files.end() ? it->second.get() : nullptr;
}
for (auto &[source_set, dbc_file] : dbc_files) { std::set<DBCFile *> DBCManager::allDBCFiles() {
if (source_set.contains(source)) return {{source_set, dbc_file}}; std::set<DBCFile *> files;
for (const auto &[_, f] : dbc_files) {
if (f) files.insert(f.get());
} }
for (auto &[source_set, dbc_file] : dbc_files) { return files;
if (source_set == SOURCE_ALL) return {{sources, dbc_file}};
}
return {};
} }
std::optional<std::pair<SourceSet, DBCFile*>> DBCManager::findDBCFile(const MessageId &id) const { const SourceSet DBCManager::sources(const DBCFile *dbc_file) const {
return findDBCFile(id.source); SourceSet sources;
for (auto &[s, f] : dbc_files) {
if (f.get() == dbc_file) sources.insert(s);
}
return sources;
} }
DBCManager *dbc() { DBCManager *dbc() {

@ -1,20 +1,15 @@
#pragma once #pragma once
#include <memory>
#include <map> #include <map>
#include <optional> #include <set>
#include <QList>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <QSet>
#include "tools/cabana/dbc/dbc.h"
#include "tools/cabana/dbc/dbcfile.h" #include "tools/cabana/dbc/dbcfile.h"
typedef QSet<uint8_t> SourceSet; typedef std::set<int> SourceSet;
const SourceSet SOURCE_ALL = {}; const SourceSet SOURCE_ALL = {-1};
const int INVALID_SOURCE = 0xff; const int INVALID_SOURCE = 0xff;
inline bool operator<(const std::shared_ptr<DBCFile> &l, const std::shared_ptr<DBCFile> &r) { return l.get() < r.get(); }
class DBCManager : public QObject { class DBCManager : public QObject {
Q_OBJECT Q_OBJECT
@ -22,12 +17,11 @@ class DBCManager : public QObject {
public: public:
DBCManager(QObject *parent) {} DBCManager(QObject *parent) {}
~DBCManager() {} ~DBCManager() {}
bool open(SourceSet s, const QString &dbc_file_name, QString *error = nullptr); bool open(const SourceSet &sources, const QString &dbc_file_name, QString *error = nullptr);
bool open(SourceSet s, const QString &name, const QString &content, QString *error = nullptr); bool open(const SourceSet &sources, const QString &name, const QString &content, QString *error = nullptr);
void close(SourceSet s); void close(const SourceSet &sources);
void close(DBCFile *dbc_file); void close(DBCFile *dbc_file);
void closeAll(); void closeAll();
void removeSourcesFromFile(DBCFile *dbc_file, SourceSet s);
void addSignal(const MessageId &id, const cabana::Signal &sig); void addSignal(const MessageId &id, const cabana::Signal &sig);
void updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig); void updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig);
@ -38,31 +32,23 @@ public:
QString newMsgName(const MessageId &id); QString newMsgName(const MessageId &id);
QString newSignalName(const MessageId &id); QString newSignalName(const MessageId &id);
const QList<uint8_t>& mask(const MessageId &id);
const QList<uint8_t>& mask(const MessageId &id) const; const std::map<uint32_t, cabana::Msg> &getMessages(uint8_t source);
cabana::Msg *msg(const MessageId &id);
std::map<MessageId, cabana::Msg> getMessages(uint8_t source); cabana::Msg* msg(uint8_t source, const QString &name);
const cabana::Msg *msg(const MessageId &id) const;
const cabana::Msg* msg(uint8_t source, const QString &name);
QStringList signalNames() const;
int signalCount(const MessageId &id) const;
int signalCount() const;
int msgCount() const;
int dbcCount() const;
int nonEmptyDBCCount() const;
std::optional<std::pair<SourceSet, DBCFile*>> findDBCFile(const uint8_t source) const;
std::optional<std::pair<SourceSet, DBCFile*>> findDBCFile(const MessageId &id) const;
QList<std::pair<SourceSet, DBCFile*>> dbc_files; QStringList signalNames();
int signalCount(const MessageId &id);
int signalCount();
int msgCount();
int dbcCount();
int nonEmptyDBCCount();
private: const SourceSet sources(const DBCFile *dbc_file) const;
SourceSet sources; DBCFile *findDBCFile(const uint8_t source);
QList<uint8_t> empty_mask; inline DBCFile *findDBCFile(const MessageId &id) { return findDBCFile(id.source); }
std::set<DBCFile *> allDBCFiles();
public slots:
void updateSources(const SourceSet &s);
signals: signals:
void signalAdded(MessageId id, const cabana::Signal *sig); void signalAdded(MessageId id, const cabana::Signal *sig);
@ -71,6 +57,9 @@ signals:
void msgUpdated(MessageId id); void msgUpdated(MessageId id);
void msgRemoved(MessageId id); void msgRemoved(MessageId id);
void DBCFileChanged(); void DBCFileChanged();
private:
std::map<int, std::shared_ptr<DBCFile>> dbc_files;
}; };
DBCManager *dbc(); DBCManager *dbc();
@ -80,16 +69,10 @@ inline QString msgName(const MessageId &id) {
return msg ? msg->name : UNTITLED; return msg ? msg->name : UNTITLED;
} }
inline QString toString(SourceSet ss) { inline QString toString(const SourceSet &ss) {
if (ss == SOURCE_ALL) { QStringList ret;
return "all"; for (auto s : ss) {
} else { ret << (s == -1 ? QString("all") : QString::number(s));
QStringList ret;
QList source_list = ss.values();
std::sort(source_list.begin(), source_list.end());
for (auto s : source_list) {
ret << QString::number(s);
}
return ret.join(", ");
} }
return ret.join(", ");
} }

@ -64,7 +64,7 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i
} }
} else if (role == Qt::BackgroundRole && section > 0 && show_signals) { } else if (role == Qt::BackgroundRole && section > 0 && show_signals) {
// Alpha-blend the signal color with the background to ensure contrast // Alpha-blend the signal color with the background to ensure contrast
QColor sigColor = getColor(sigs[section - 1]); QColor sigColor = sigs[section - 1]->color;
sigColor.setAlpha(128); sigColor.setAlpha(128);
return QBrush(sigColor); return QBrush(sigColor);
} }

@ -62,7 +62,7 @@ public:
uint64_t last_fetch_time = 0; uint64_t last_fetch_time = 0;
std::function<bool(double, double)> filter_cmp = nullptr; std::function<bool(double, double)> filter_cmp = nullptr;
std::deque<Message> messages; std::deque<Message> messages;
std::vector<const cabana::Signal *> sigs; std::vector<cabana::Signal *> sigs;
bool dynamic_mode = true; bool dynamic_mode = true;
bool display_signals_mode = true; bool display_signals_mode = true;
}; };

@ -353,7 +353,6 @@ void MainWindow::streamStarted() {
QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage); QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage);
QObject::connect(can, &AbstractStream::eventsMerged, this, &MainWindow::eventsMerged); QObject::connect(can, &AbstractStream::eventsMerged, this, &MainWindow::eventsMerged);
QObject::connect(can, &AbstractStream::sourcesUpdated, dbc(), &DBCManager::updateSources);
QObject::connect(can, &AbstractStream::sourcesUpdated, this, &MainWindow::updateLoadSaveMenus); QObject::connect(can, &AbstractStream::sourcesUpdated, this, &MainWindow::updateLoadSaveMenus);
} }
@ -374,7 +373,7 @@ void MainWindow::eventsMerged() {
void MainWindow::save() { void MainWindow::save() {
// Save all open DBC files // Save all open DBC files
for (auto &[s, dbc_file] : dbc()->dbc_files) { for (auto dbc_file : dbc()->allDBCFiles()) {
if (dbc_file->isEmpty()) continue; if (dbc_file->isEmpty()) continue;
saveFile(dbc_file); saveFile(dbc_file);
} }
@ -382,7 +381,7 @@ void MainWindow::save() {
void MainWindow::saveAs() { void MainWindow::saveAs() {
// Save as all open DBC files. Should not be called with more than 1 file open // Save as all open DBC files. Should not be called with more than 1 file open
for (auto &[s, dbc_file] : dbc()->dbc_files) { for (auto dbc_file : dbc()->allDBCFiles()) {
if (dbc_file->isEmpty()) continue; if (dbc_file->isEmpty()) continue;
saveFileAs(dbc_file); saveFileAs(dbc_file);
} }
@ -390,7 +389,7 @@ void MainWindow::saveAs() {
void MainWindow::autoSave() { void MainWindow::autoSave() {
if (!UndoStack::instance()->isClean()) { if (!UndoStack::instance()->isClean()) {
for (auto &[_, dbc_file] : dbc()->dbc_files) { for (auto dbc_file : dbc()->allDBCFiles()) {
if (!dbc_file->filename.isEmpty()) { if (!dbc_file->filename.isEmpty()) {
dbc_file->autoSave(); dbc_file->autoSave();
} }
@ -399,7 +398,7 @@ void MainWindow::autoSave() {
} }
void MainWindow::cleanupAutoSaveFile() { void MainWindow::cleanupAutoSaveFile() {
for (auto &[_, dbc_file] : dbc()->dbc_files) { for (auto dbc_file : dbc()->allDBCFiles()) {
dbc_file->cleanupAutoSaveFile(); dbc_file->cleanupAutoSaveFile();
} }
} }
@ -436,9 +435,7 @@ void MainWindow::saveFile(DBCFile *dbc_file) {
} }
void MainWindow::saveFileAs(DBCFile *dbc_file) { void MainWindow::saveFileAs(DBCFile *dbc_file) {
auto it = std::find_if(dbc()->dbc_files.begin(), dbc()->dbc_files.end(), [=](auto &f) { return f.second == dbc_file; }); QString title = tr("Save File (bus: %1)").arg(toString(dbc()->sources(dbc_file)));
assert(it != dbc()->dbc_files.end());
QString title = tr("Save File (bus: %1)").arg(toString(it->first));
QString fn = QFileDialog::getSaveFileName(this, title, QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)")); QString fn = QFileDialog::getSaveFileName(this, title, QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)"));
if (!fn.isEmpty()) { if (!fn.isEmpty()) {
dbc_file->saveAs(fn); dbc_file->saveAs(fn);
@ -447,15 +444,9 @@ void MainWindow::saveFileAs(DBCFile *dbc_file) {
} }
} }
void MainWindow::removeBusFromFile(DBCFile *dbc_file, uint8_t source) {
assert(dbc_file != nullptr);
SourceSet ss = {source, uint8_t(source + 128), uint8_t(source + 192)};
dbc()->removeSourcesFromFile(dbc_file, ss);
}
void MainWindow::saveToClipboard() { void MainWindow::saveToClipboard() {
// Copy all open DBC files to clipboard. Should not be called with more than 1 file open // Copy all open DBC files to clipboard. Should not be called with more than 1 file open
for (auto &[s, dbc_file] : dbc()->dbc_files) { for (auto dbc_file : dbc()->allDBCFiles()) {
if (dbc_file->isEmpty()) continue; if (dbc_file->isEmpty()) continue;
saveFileToClipboard(dbc_file); saveFileToClipboard(dbc_file);
} }
@ -480,13 +471,10 @@ void MainWindow::updateLoadSaveMenus() {
// TODO: Support clipboard for multiple files // TODO: Support clipboard for multiple files
copy_dbc_to_clipboard->setEnabled(cnt == 1); copy_dbc_to_clipboard->setEnabled(cnt == 1);
QList<uint8_t> sources_sorted = can->sources.toList();
std::sort(sources_sorted.begin(), sources_sorted.end());
manage_dbcs_menu->clear(); manage_dbcs_menu->clear();
manage_dbcs_menu->setEnabled(dynamic_cast<DummyStream *>(can) == nullptr); manage_dbcs_menu->setEnabled(dynamic_cast<DummyStream *>(can) == nullptr);
for (uint8_t source : sources_sorted) { for (uint8_t source : can->sources) {
if (source >= 64) continue; // Sent and blocked buses are handled implicitly if (source >= 64) continue; // Sent and blocked buses are handled implicitly
SourceSet ss = {source, uint8_t(source + 128), uint8_t(source + 192)}; SourceSet ss = {source, uint8_t(source + 128), uint8_t(source + 192)};
@ -497,32 +485,26 @@ void MainWindow::updateLoadSaveMenus() {
bus_menu->addAction(tr("Load DBC From Clipboard..."), [=]() { loadFromClipboard(ss, false); }); bus_menu->addAction(tr("Load DBC From Clipboard..."), [=]() { loadFromClipboard(ss, false); });
// Show sub-menu for each dbc for this source. // Show sub-menu for each dbc for this source.
QStringList bus_menu_fns; QString file_name = "No DBCs loaded";
for (auto it : dbc()->dbc_files) { if (auto dbc_file = dbc()->findDBCFile(source)) {
auto &[src, dbc_file] = it;
if (!src.contains(source) && (src != SOURCE_ALL)) {
continue;
}
bus_menu->addSeparator(); bus_menu->addSeparator();
bus_menu->addAction(dbc_file->name() + " (" + toString(src) + ")")->setEnabled(false); bus_menu->addAction(dbc_file->name() + " (" + toString(dbc()->sources(dbc_file)) + ")")->setEnabled(false);
bus_menu->addAction(tr("Save..."), [=]() { saveFile(it.second); }); bus_menu->addAction(tr("Save..."), [=]() { saveFile(dbc_file); });
bus_menu->addAction(tr("Save As..."), [=]() { saveFileAs(it.second); }); bus_menu->addAction(tr("Save As..."), [=]() { saveFileAs(dbc_file); });
bus_menu->addAction(tr("Copy to Clipboard..."), [=]() { saveFileToClipboard(it.second); }); bus_menu->addAction(tr("Copy to Clipboard..."), [=]() { saveFileToClipboard(dbc_file); });
bus_menu->addAction(tr("Remove from this bus..."), [=]() { removeBusFromFile(it.second, source); }); bus_menu->addAction(tr("Remove from this bus..."), [=]() { closeFile(ss); });
bus_menu->addAction(tr("Remove from all buses..."), [=]() { closeFile(it.second); }); bus_menu->addAction(tr("Remove from all buses..."), [=]() { closeFile(dbc_file); });
bus_menu_fns << dbc_file->name(); file_name = dbc_file->name();
} }
manage_dbcs_menu->addMenu(bus_menu); manage_dbcs_menu->addMenu(bus_menu);
QString bus_menu_title = bus_menu_fns.size() ? bus_menu_fns.join(", ") : "No DBCs loaded"; bus_menu->setTitle(tr("Bus %1 (%2)").arg(source).arg(file_name));
bus_menu->setTitle(tr("Bus %1 (%2)").arg(source).arg(bus_menu_title));
} }
QStringList title; QStringList title;
for (auto &[src, dbc_file] : dbc()->dbc_files) { for (auto f : dbc()->allDBCFiles()) {
title.push_back(tr("(%1) %2").arg(toString(src), dbc_file->name())); title.push_back(tr("(%1) %2").arg(toString(dbc()->sources(f)), f->name()));
} }
setWindowFilePath(title.join(" | ")); setWindowFilePath(title.join(" | "));
} }

@ -50,7 +50,6 @@ protected:
void saveFile(DBCFile *dbc_file); void saveFile(DBCFile *dbc_file);
void saveFileAs(DBCFile *dbc_file); void saveFileAs(DBCFile *dbc_file);
void saveFileToClipboard(DBCFile *dbc_file); void saveFileToClipboard(DBCFile *dbc_file);
void removeBusFromFile(DBCFile *dbc_file, uint8_t source);
void loadFromClipboard(SourceSet s = SOURCE_ALL, bool close_all = true); void loadFromClipboard(SourceSet s = SOURCE_ALL, bool close_all = true);
void autoSave(); void autoSave();
void cleanupAutoSaveFile(); void cleanupAutoSaveFile();

@ -273,7 +273,7 @@ bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, co
case Column::NAME: { case Column::NAME: {
const auto msg = dbc()->msg(id); const auto msg = dbc()->msg(id);
match = re.match(msg ? msg->name : UNTITLED).hasMatch(); match = re.match(msg ? msg->name : UNTITLED).hasMatch();
match |= msg && std::any_of(msg->sigs.cbegin(), msg->sigs.cend(), [&re](const auto &s) { return re.match(s.name).hasMatch(); }); match |= msg && std::any_of(msg->sigs.cbegin(), msg->sigs.cend(), [&re](const auto &s) { return re.match(s->name).hasMatch(); });
break; break;
} }
case Column::SOURCE: case Column::SOURCE:

@ -171,7 +171,6 @@ bool SignalModel::setData(const QModelIndex &index, const QVariant &value, int r
case Item::Desc: s.val_desc = value.value<ValueDescription>(); break; case Item::Desc: s.val_desc = value.value<ValueDescription>(); break;
default: return false; default: return false;
} }
s.updatePrecision();
bool ret = saveSignal(item->sig, s); bool ret = saveSignal(item->sig, s);
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, Qt::CheckStateRole}); emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, Qt::CheckStateRole});
return ret; return ret;
@ -249,17 +248,14 @@ void SignalModel::removeSignal(const cabana::Signal *sig) {
} }
void SignalModel::handleMsgChanged(MessageId id) { void SignalModel::handleMsgChanged(MessageId id) {
if (id == msg_id) { if (id.address == msg_id.address) {
refresh(); refresh();
} }
} }
void SignalModel::handleSignalAdded(MessageId id, const cabana::Signal *sig) { void SignalModel::handleSignalAdded(MessageId id, const cabana::Signal *sig) {
if (id == msg_id) { if (id == msg_id) {
int i = 0; int i = dbc()->msg(msg_id)->indexOf(sig);
for (; i < root->children.size(); ++i) {
if (sig->start_bit < root->children[i]->sig->start_bit) break;
}
beginInsertRows({}, i, i); beginInsertRows({}, i, i);
insertItem(root.get(), i, sig); insertItem(root.get(), i, sig);
endInsertRows(); endInsertRows();
@ -348,7 +344,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
path.addRoundedRect(icon_rect, 3, 3); path.addRoundedRect(icon_rect, 3, 3);
painter->setPen(item->highlight ? Qt::white : Qt::black); painter->setPen(item->highlight ? Qt::white : Qt::black);
painter->setFont(label_font); painter->setFont(label_font);
painter->fillPath(path, getColor(item->sig).darker(item->highlight ? 125 : 0)); painter->fillPath(path, item->sig->color.darker(item->highlight ? 125 : 0));
painter->drawText(icon_rect, Qt::AlignCenter, QString::number(item->row() + 1)); painter->drawText(icon_rect, Qt::AlignCenter, QString::number(item->row() + 1));
r.setLeft(icon_rect.right() + h_margin * 2); r.setLeft(icon_rect.right() + h_margin * 2);

@ -78,7 +78,7 @@ BO_ 160 message_1: 8 XXX
VAL_ 160 signal_1 0 "disabled" 1.2 "initializing" 2 "fault"; VAL_ 160 signal_1 0 "disabled" 1.2 "initializing" 2 "fault";
CM_ BO_ 160 "message comment"; CM_ BO_ 160 "message comment" ;
CM_ SG_ 160 signal_1 "signal comment"; CM_ SG_ 160 signal_1 "signal comment";
CM_ SG_ 160 signal_2 "multiple line comment CM_ SG_ 160 signal_2 "multiple line comment
1 1
@ -94,19 +94,19 @@ CM_ SG_ 160 signal_2 "multiple line comment
REQUIRE(msg->sigs.size() == 2); REQUIRE(msg->sigs.size() == 2);
REQUIRE(file.msg("message_1") != nullptr); REQUIRE(file.msg("message_1") != nullptr);
auto &sig_1 = msg->sigs[0]; auto sig_1 = msg->sigs[0];
REQUIRE(sig_1.name == "signal_1"); REQUIRE(sig_1->name == "signal_1");
REQUIRE(sig_1.start_bit == 0); REQUIRE(sig_1->start_bit == 0);
REQUIRE(sig_1.size == 12); REQUIRE(sig_1->size == 12);
REQUIRE(sig_1.min == 0); REQUIRE(sig_1->min == 0);
REQUIRE(sig_1.max == 4095); REQUIRE(sig_1->max == 4095);
REQUIRE(sig_1.unit == "unit"); REQUIRE(sig_1->unit == "unit");
REQUIRE(sig_1.comment == "signal comment"); REQUIRE(sig_1->comment == "signal comment");
REQUIRE(sig_1.val_desc.size() == 3); REQUIRE(sig_1->val_desc.size() == 3);
REQUIRE(sig_1.val_desc[0] == std::pair<double, QString>{0, "disabled"}); REQUIRE(sig_1->val_desc[0] == std::pair<double, QString>{0, "disabled"});
REQUIRE(sig_1.val_desc[1] == std::pair<double, QString>{1.2, "initializing"}); REQUIRE(sig_1->val_desc[1] == std::pair<double, QString>{1.2, "initializing"});
REQUIRE(sig_1.val_desc[2] == std::pair<double, QString>{2, "fault"}); REQUIRE(sig_1->val_desc[2] == std::pair<double, QString>{2, "fault"});
auto &sig_2 = msg->sigs[1]; auto &sig_2 = msg->sigs[1];
REQUIRE(sig_2.comment == "multiple line comment\n1\n2"); REQUIRE(sig_2->comment == "multiple line comment\n1\n2");
} }

@ -24,14 +24,12 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::Wi
for (uint8_t bus : can->sources) { for (uint8_t bus : can->sources) {
cb->addItem(QString::number(bus), bus); cb->addItem(QString::number(bus), bus);
} }
cb->model()->sort(0);
cb->setCurrentIndex(0);
} }
msg_cb = new QComboBox(this); msg_cb = new QComboBox(this);
// TODO: update when src_bus_combo changes // TODO: update when src_bus_combo changes
for (auto &[id, msg] : dbc()->getMessages(0)) { for (auto &[address, msg] : dbc()->getMessages(0)) {
msg_cb->addItem(msg.name, id.address); msg_cb->addItem(msg.name, address);
} }
msg_cb->model()->sort(0); msg_cb->model()->sort(0);
msg_cb->setCurrentIndex(0); msg_cb->setCurrentIndex(0);

@ -1,5 +1,6 @@
#include "tools/cabana/util.h" #include "tools/cabana/util.h"
#include <QColor>
#include <QFontDatabase> #include <QFontDatabase>
#include <QHelpEvent> #include <QHelpEvent>
#include <QLocale> #include <QLocale>
@ -144,17 +145,6 @@ void TabBar::closeTabClicked() {
} }
} }
QColor getColor(const cabana::Signal *sig) {
float h = 19 * (float)sig->lsb / 64.0;
h = fmod(h, 1.0);
size_t hash = qHash(sig->name);
float s = 0.25 + 0.25 * (float)(hash & 0xff) / 255.0;
float v = 0.75 + 0.25 * (float)((hash >> 8) & 0xff) / 255.0;
return QColor::fromHsvF(h, s, v);
}
NameValidator::NameValidator(QObject *parent) : QRegExpValidator(QRegExp("^(\\w+)"), parent) {} NameValidator::NameValidator(QObject *parent) : QRegExpValidator(QRegExp("^(\\w+)"), parent) {}
QValidator::State NameValidator::validate(QString &input, int &pos) const { QValidator::State NameValidator::validate(QString &input, int &pos) const {
@ -237,10 +227,6 @@ QString toHex(uint8_t byte) {
int num_decimals(double num) { int num_decimals(double num) {
const QString string = QString::number(num); const QString string = QString::number(num);
const QStringList split = string.split('.'); auto dot_pos = string.indexOf('.');
if (split.size() == 1) { return dot_pos == -1 ? 0 : string.size() - dot_pos - 1;
return 0;
} else {
return split[1].size();
}
} }

@ -2,12 +2,10 @@
#include <cmath> #include <cmath>
#include <QAbstractItemView>
#include <QApplication> #include <QApplication>
#include <QByteArray> #include <QByteArray>
#include <QDateTime> #include <QDateTime>
#include <QDoubleValidator> #include <QDoubleValidator>
#include <QColor>
#include <QFont> #include <QFont>
#include <QRegExpValidator> #include <QRegExpValidator>
#include <QStringBuilder> #include <QStringBuilder>
@ -22,7 +20,7 @@ class LogSlider : public QSlider {
Q_OBJECT Q_OBJECT
public: public:
LogSlider(double factor, Qt::Orientation orientation, QWidget *parent = nullptr) : factor(factor), QSlider(orientation, parent) {}; LogSlider(double factor, Qt::Orientation orientation, QWidget *parent = nullptr) : factor(factor), QSlider(orientation, parent) {}
void setRange(double min, double max) { void setRange(double min, double max) {
log_min = factor * std::log10(min); log_min = factor * std::log10(min);
@ -81,7 +79,6 @@ private:
inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); } inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); }
QString toHex(uint8_t byte); QString toHex(uint8_t byte);
QColor getColor(const cabana::Signal *sig);
class NameValidator : public QRegExpValidator { class NameValidator : public QRegExpValidator {
Q_OBJECT Q_OBJECT

Loading…
Cancel
Save