cabana: show and edit all messages present in DBC files (#28108)

* show all messages present in DBC files

* set last section stretch

* user can't resize data section

* re-fetch if filtering freq|count|data

* reserve set space

* use contains

* emit signalAdded for all related sources
old-commit-hash: f7e024f2f2
beeps
Dean Lee 2 years ago committed by GitHub
parent 6ebc95c87d
commit ca13898b23
  1. 1
      tools/cabana/dbc/dbc.h
  2. 2
      tools/cabana/dbc/dbcfile.cc
  3. 1
      tools/cabana/dbc/dbcmanager.cc
  4. 1
      tools/cabana/dbc/dbcmanager.h
  5. 4
      tools/cabana/detailwidget.cc
  6. 72
      tools/cabana/messageswidget.cc
  7. 10
      tools/cabana/messageswidget.h
  8. 4
      tools/cabana/signalview.cc
  9. 11
      tools/cabana/streams/abstractstream.cc
  10. 4
      tools/cabana/streams/abstractstream.h

@ -61,6 +61,7 @@ namespace cabana {
};
struct Msg {
uint32_t address;
QString name;
uint32_t size;
QList<cabana::Signal> sigs;

@ -39,6 +39,7 @@ void DBCFile::open(const QString &content) {
msgs.clear();
for (auto &msg : dbc->msgs) {
auto &m = msgs[msg.address];
m.address = msg.address;
m.name = msg.name.c_str();
m.size = msg.size;
for (auto &s : msg.sigs) {
@ -145,6 +146,7 @@ void DBCFile::removeSignal(const MessageId &id, const QString &sig_name) {
void DBCFile::updateMsg(const MessageId &id, const QString &name, uint32_t size) {
auto &m = msgs[id.address];
m.address = id.address;
m.name = name;
m.size = size;
}

@ -119,6 +119,7 @@ void DBCManager::addSignal(const MessageId &id, const cabana::Signal &sig) {
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);
}

@ -15,6 +15,7 @@
typedef QSet<uint8_t> SourceSet;
const SourceSet SOURCE_ALL = {};
const int INVALID_SOURCE = 0xff;
class DBCManager : public QObject {
Q_OBJECT

@ -129,7 +129,9 @@ void DetailWidget::refresh() {
QStringList warnings;
auto msg = dbc()->msg(msg_id);
if (msg) {
if (msg->size != can->lastMessage(msg_id).dat.size()) {
if (msg_id.source == INVALID_SOURCE) {
warnings.push_back(tr("No messages received."));
} else if (msg->size != can->lastMessage(msg_id).dat.size()) {
warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size));
}
for (auto s : binary_view->getOverlappingSignals()) {

@ -15,10 +15,11 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
title_layout->addWidget(num_msg_label);
title_layout->addStretch();
title_layout->addWidget(multiple_lines_bytes = new QCheckBox(tr("Multiple Lines Bytes"), this));
title_layout->addWidget(multiple_lines_bytes = new QCheckBox(tr("Multiple Lines &Bytes"), this));
multiple_lines_bytes->setToolTip(tr("Display bytes in multiple lines"));
multiple_lines_bytes->setChecked(settings.multiple_lines_bytes);
QPushButton *clear_filters = new QPushButton(tr("Clear Filters"));
QPushButton *clear_filters = new QPushButton(tr("&Clear Filters"));
clear_filters->setEnabled(false);
title_layout->addWidget(clear_filters);
main_layout->addLayout(title_layout);
@ -42,6 +43,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
// Must be called before setting any header parameters to avoid overriding
restoreHeaderState(settings.message_header_state);
view->header()->setSectionsMovable(true);
view->header()->setSectionResizeMode(MessageListModel::Column::DATA, QHeaderView::Fixed);
// Header context menu
view->header()->setContextMenuPolicy(Qt::CustomContextMenu);
@ -62,6 +64,9 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
// signals/slots
QObject::connect(header, &MessageViewHeader::filtersUpdated, model, &MessageListModel::setFilterStrings);
QObject::connect(header, &MessageViewHeader::filtersUpdated, [=](const QMap<int, QString> &filters) {
clear_filters->setEnabled(!filters.isEmpty());
});
QObject::connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, header, &MessageViewHeader::updateHeaderPositions);
QObject::connect(clear_filters, &QPushButton::clicked, header, &MessageViewHeader::clearFilters);
QObject::connect(multiple_lines_bytes, &QCheckBox::stateChanged, [=](int state) {
@ -108,7 +113,6 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
});
updateSuppressedButtons();
dbcModified();
setWhatsThis(tr(R"(
<b>Message View</b><br/>
@ -122,12 +126,13 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
void MessagesWidget::dbcModified() {
num_msg_label->setText(tr("%1 Messages, %2 Signals").arg(dbc()->msgCount()).arg(dbc()->signalCount()));
model->fetchData();
model->dbcModified();
}
void MessagesWidget::selectMessage(const MessageId &msg_id) {
if (int row = model->msgs.indexOf(msg_id); row != -1) {
view->selectionModel()->setCurrentIndex(model->index(row, 0), QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
auto it = std::find(model->msgs.cbegin(), model->msgs.cend(), msg_id);
if (it != model->msgs.cend()) {
view->selectionModel()->setCurrentIndex(model->index(std::distance(model->msgs.cbegin(), it), 0), QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
}
}
@ -180,11 +185,11 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) {
switch (index.column()) {
case Column::NAME: return msgName(id);
case Column::SOURCE: return id.source;
case Column::SOURCE: return id.source != INVALID_SOURCE ? QString::number(id.source) : "N/A" ;
case Column::ADDRESS: return QString::number(id.address, 16);
case Column::FREQ: return getFreq(can_data);
case Column::COUNT: return can_data.count;
case Column::DATA: return toHex(can_data.dat);
case Column::FREQ: return id.source != INVALID_SOURCE ? getFreq(can_data) : "N/A";
case Column::COUNT: return id.source != INVALID_SOURCE ? QString::number(can_data.count) : "N/A";
case Column::DATA: return id.source != INVALID_SOURCE ? toHex(can_data.dat) : "N/A";
}
} else if (role == ColorsRole) {
QVector<QColor> colors = can_data.colors;
@ -196,7 +201,7 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const {
}
}
return QVariant::fromValue(colors);
} else if (role == BytesRole && index.column() == Column::DATA) {
} else if (role == BytesRole && index.column() == Column::DATA && id.source != INVALID_SOURCE) {
return can_data.dat;
}
return {};
@ -207,7 +212,15 @@ void MessageListModel::setFilterStrings(const QMap<int, QString> &filters) {
fetchData();
}
void MessageListModel::sortMessages(Qt::SortOrder sort_order, int sort_column, QList<MessageId> &new_msgs) {
void MessageListModel::dbcModified() {
dbc_address.clear();
for (const auto &[_, m] : dbc()->getMessages(0)) {
dbc_address.insert(m.address);
}
fetchData();
}
void MessageListModel::sortMessages(std::vector<MessageId> &new_msgs) {
if (sort_column == Column::NAME) {
std::sort(new_msgs.begin(), new_msgs.end(), [=](auto &l, auto &r) {
auto ll = std::pair{msgName(l), l};
@ -241,7 +254,7 @@ void MessageListModel::sortMessages(Qt::SortOrder sort_order, int sort_column, Q
}
}
static std::pair<unsigned int, unsigned int> parseRange(QString &filter, bool *ok = nullptr, int base = 10) {
static std::pair<unsigned int, unsigned int> parseRange(const QString &filter, bool *ok = nullptr, int base = 10) {
// Parse out filter string into a range (e.g. "1" -> {1, 1}, "1-3" -> {1, 3}, "1-" -> {1, inf})
bool ok1 = true, ok2 = true;
unsigned int parsed1 = std::numeric_limits<unsigned int>::min();
@ -263,14 +276,14 @@ static std::pair<unsigned int, unsigned int> parseRange(QString &filter, bool *o
}
}
bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, QMap<int, QString> &filters) {
bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, const QMap<int, QString> &filters) {
auto cs = Qt::CaseInsensitive;
bool match = true;
bool convert_ok;
for (int column = Column::NAME; column <= Column::DATA; column++) {
if (!filters.contains(column)) continue;
QString txt = filters[column];
const QString &txt = filters[column];
QRegularExpression re(txt, QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
@ -341,25 +354,38 @@ bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, QM
void MessageListModel::fetchData() {
QList<MessageId> new_msgs;
for (auto it = can->last_msgs.begin(); it != can->last_msgs.end(); ++it) {
if (matchMessage(it.key(), it.value(), filter_str)) {
std::vector<MessageId> new_msgs;
new_msgs.reserve(can->last_msgs.size() + dbc_address.size());
auto address = dbc_address;
for (auto it = can->last_msgs.cbegin(); it != can->last_msgs.cend(); ++it) {
if (filter_str.isEmpty() || matchMessage(it.key(), it.value(), filter_str)) {
new_msgs.push_back(it.key());
}
address.remove(it.key().address);
}
sortMessages(sort_order, sort_column, new_msgs);
// merge all DBC messages
for (auto &addr : address) {
MessageId id{.source = INVALID_SOURCE, .address = addr};
if (filter_str.isEmpty() || matchMessage(id, {}, filter_str)) {
new_msgs.push_back(id);
}
}
sortMessages(new_msgs);
if (msgs != new_msgs) {
beginResetModel();
msgs = new_msgs;
msgs = std::move(new_msgs);
endResetModel();
}
}
void MessageListModel::msgsReceived(const QHash<MessageId, CanData> *new_msgs) {
QList<MessageId> prev_msgs = msgs;
void MessageListModel::msgsReceived(const QHash<MessageId, CanData> *new_msgs, bool has_new_ids) {
if (has_new_ids || filter_str.contains(Column::FREQ) || filter_str.contains(Column::COUNT) || filter_str.contains(Column::DATA)) {
fetchData();
}
for (int i = 0; i < msgs.size(); ++i) {
if (new_msgs->contains(msgs[i])) {
for (int col = Column::FREQ; col < columnCount(); ++col)

@ -34,20 +34,22 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override { return msgs.size(); }
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
void setFilterStrings(const QMap<int, QString> &filters);
void msgsReceived(const QHash<MessageId, CanData> *new_msgs = nullptr);
void msgsReceived(const QHash<MessageId, CanData> *new_msgs, bool has_new_ids);
void fetchData();
void suppress();
void clearSuppress();
void reset();
void forceResetModel();
QList<MessageId> msgs;
void dbcModified();
std::vector<MessageId> msgs;
QSet<std::pair<MessageId, int>> suppressed_bytes;
private:
static void sortMessages(Qt::SortOrder sort_order, int sort_column, QList<MessageId> &new_msgs);
static bool matchMessage(const MessageId &id, const CanData &data, QMap<int, QString> &filters);
void sortMessages(std::vector<MessageId> &new_msgs);
bool matchMessage(const MessageId &id, const CanData &data, const QMap<int, QString> &filters);
QMap<int, QString> filter_str;
QSet<uint32_t> dbc_address;
int sort_column = 0;
Qt::SortOrder sort_order = Qt::AscendingOrder;
};

@ -590,9 +590,9 @@ void SignalView::handleSignalUpdated(const cabana::Signal *sig) {
}
void SignalView::updateState(const QHash<MessageId, CanData> *msgs) {
if (model->rowCount() == 0 || (msgs && !msgs->contains(model->msg_id))) return;
const auto &last_msg = can->lastMessage(model->msg_id);
if (model->rowCount() == 0 || (msgs && !msgs->contains(model->msg_id)) || last_msg.dat.size() == 0) return;
for (auto item : model->root->children) {
double value = get_raw_value((uint8_t *)last_msg.dat.constData(), last_msg.dat.size(), *item->sig);
item->sig_val = item->sig->formatValue(value);

@ -12,6 +12,7 @@ AbstractStream::AbstractStream(QObject *parent) : QObject(parent) {
void AbstractStream::updateMessages(QHash<MessageId, CanData> *messages) {
auto prev_src_size = sources.size();
auto prev_msg_size = last_msgs.size();
for (auto it = messages->begin(); it != messages->end(); ++it) {
const auto &id = it.key();
last_msgs[id] = it.value();
@ -21,7 +22,7 @@ void AbstractStream::updateMessages(QHash<MessageId, CanData> *messages) {
emit sourcesUpdated(sources);
}
emit updated();
emit msgsReceived(messages);
emit msgsReceived(messages, prev_msg_size != last_msgs.size());
delete messages;
processing = false;
}
@ -50,6 +51,12 @@ bool AbstractStream::postEvents() {
return false;
}
const std::vector<const CanEvent *> &AbstractStream::events(const MessageId &id) const {
static std::vector<const CanEvent *> empty_events;
auto it = events_.find(id);
return it != events_.end() ? it->second : empty_events;
}
const CanData &AbstractStream::lastMessage(const MessageId &id) {
static CanData empty_data = {};
auto it = last_msgs.find(id);
@ -81,7 +88,7 @@ void AbstractStream::updateLastMsgsTo(double sec) {
// use a timer to prevent recursive calls
QTimer::singleShot(0, [this]() {
emit updated();
emit msgsReceived(&last_msgs);
emit msgsReceived(&last_msgs, true);
});
}

@ -55,7 +55,7 @@ public:
virtual bool isPaused() const { return false; }
virtual void pause(bool pause) {}
const std::vector<const CanEvent *> &allEvents() const { return all_events_; }
const std::vector<const CanEvent *> &events(const MessageId &id) const { return events_.at(id); }
const std::vector<const CanEvent *> &events(const MessageId &id) const;
virtual const std::vector<std::tuple<int, int, TimelineType>> getTimeline() { return {}; }
signals:
@ -65,7 +65,7 @@ signals:
void streamStarted();
void eventsMerged();
void updated();
void msgsReceived(const QHash<MessageId, CanData> *);
void msgsReceived(const QHash<MessageId, CanData> *new_msgs, bool has_new_ids);
void sourcesUpdated(const SourceSet &s);
public:

Loading…
Cancel
Save