From f372bf3a1b9d7ab0207f09175ef0fa9104b63a5d Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 25 Apr 2024 06:56:25 +0800 Subject: [PATCH] cabana: some improvements (#32161) some improvements old-commit-hash: 2d1078ee5bcddc12122a06ffaefef31f9656ef65 --- tools/cabana/chart/chart.cc | 6 ++-- tools/cabana/dbc/dbc.h | 4 +-- tools/cabana/historylog.h | 2 +- tools/cabana/messageswidget.cc | 44 +++++++++++--------------- tools/cabana/messageswidget.h | 1 + tools/cabana/streams/abstractstream.cc | 18 +++++------ tools/cabana/utils/util.cc | 38 ++++++++++++---------- tools/cabana/utils/util.h | 2 ++ 8 files changed, 59 insertions(+), 56 deletions(-) diff --git a/tools/cabana/chart/chart.cc b/tools/cabana/chart/chart.cc index 6f08a9f20b..dd34782e33 100644 --- a/tools/cabana/chart/chart.cc +++ b/tools/cabana/chart/chart.cc @@ -111,6 +111,8 @@ void ChartView::setTheme(QChart::ChartTheme theme) { axis_y->setLabelsBrush(palette().text()); chart()->legend()->setLabelColor(palette().color(QPalette::Text)); } + axis_x->setLineVisible(false); + axis_y->setLineVisible(false); for (auto &s : sigs) { s.series->setColor(s.sig->color); } @@ -745,8 +747,8 @@ void ChartView::drawTimeline(QPainter *painter) { const auto plot_area = chart()->plotArea(); // draw vertical time line qreal x = std::clamp(chart()->mapToPosition(QPointF{cur_sec, 0}).x(), plot_area.left(), plot_area.right()); - painter->setPen(QPen(chart()->titleBrush().color(), 2)); - painter->drawLine(QPointF{x, plot_area.top()}, QPointF{x, plot_area.bottom() + 1}); + painter->setPen(QPen(chart()->titleBrush().color(), 1)); + painter->drawLine(QPointF{x, plot_area.top() - 1}, QPointF{x, plot_area.bottom() + 1}); // draw current time under the axis-x QString time_str = QString::number(cur_sec, 'f', 2); diff --git a/tools/cabana/dbc/dbc.h b/tools/cabana/dbc/dbc.h index 71838a1df5..da44319b5c 100644 --- a/tools/cabana/dbc/dbc.h +++ b/tools/cabana/dbc/dbc.h @@ -29,11 +29,11 @@ struct MessageId { } bool operator<(const MessageId &other) const { - return std::pair{source, address} < std::pair{other.source, other.address}; + return std::tie(source, address) < std::tie(other.source, other.address); } bool operator>(const MessageId &other) const { - return std::pair{source, address} > std::pair{other.source, other.address}; + return std::tie(source, address) > std::tie(other.source, other.address); } }; diff --git a/tools/cabana/historylog.h b/tools/cabana/historylog.h index 8c9ee922f8..1ac6e5bbad 100644 --- a/tools/cabana/historylog.h +++ b/tools/cabana/historylog.h @@ -53,7 +53,7 @@ public: std::function filter_cmp = nullptr; std::deque messages; std::vector sigs; - bool hex_mode = true; + bool hex_mode = false; }; class LogsWidget : public QFrame { diff --git a/tools/cabana/messageswidget.cc b/tools/cabana/messageswidget.cc index 4c3efa0385..396cfdc38b 100644 --- a/tools/cabana/messageswidget.cc +++ b/tools/cabana/messageswidget.cc @@ -43,7 +43,6 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget view->setItemsExpandable(false); view->setIndentation(0); view->setRootIsDecorated(false); - view->setUniformRowHeights(!settings.multiple_lines_hex); // Must be called before setting any header parameters to avoid overriding restoreHeaderState(settings.message_header_state); @@ -135,15 +134,9 @@ void MessagesWidget::selectMessage(const MessageId &msg_id) { } void MessagesWidget::suppressHighlighted() { - if (sender() == suppress_add) { - size_t n = can->suppressHighlighted(); - suppress_clear->setText(tr("Clear (%1)").arg(n)); - suppress_clear->setEnabled(true); - } else { - can->clearSuppressed(); - suppress_clear->setText(tr("Clear")); - suppress_clear->setEnabled(false); - } + int n = sender() == suppress_add ? can->suppressHighlighted() : (can->clearSuppressed(), 0); + suppress_clear->setText(n > 0 ? tr("Clear (%1)").arg(n) : tr("Clear")); + suppress_clear->setEnabled(n > 0); } void MessagesWidget::headerContextMenuEvent(const QPoint &pos) { @@ -174,7 +167,6 @@ void MessagesWidget::menuAboutToShow() { void MessagesWidget::setMultiLineBytes(bool multi) { settings.multiple_lines_hex = multi; delegate->setMultipleLines(multi); - view->setUniformRowHeights(!multi); view->updateBytesSectionSize(); view->doItemsLayout(); } @@ -207,22 +199,22 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { } }; + const static QString NA = QStringLiteral("N/A"); const auto &item = items_[index.row()]; - const auto &data = can->lastMessage(item.id); if (role == Qt::DisplayRole) { switch (index.column()) { case Column::NAME: return item.name; - case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : "N/A"; + case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : NA; case Column::ADDRESS: return QString::number(item.id.address, 16); case Column::NODE: return item.node; - case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(data.freq) : "N/A"; - case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(data.count) : "N/A"; - case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : "N/A"; + case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(can->lastMessage(item.id).freq) : NA; + case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(can->lastMessage(item.id).count) : NA; + case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : NA; } } else if (role == ColorsRole) { - return QVariant::fromValue((void*)(&data.colors)); + return QVariant::fromValue((void*)(&can->lastMessage(item.id).colors)); } else if (role == BytesRole && index.column() == Column::DATA && item.id.source != INVALID_SOURCE) { - return QVariant::fromValue((void*)(&data.dat)); + return QVariant::fromValue((void*)(&can->lastMessage(item.id).dat)); } else if (role == Qt::ForegroundRole && !item.active) { return settings.theme == DARK_THEME ? QApplication::palette().color(QPalette::Text).darker(150) : QColor(Qt::gray); } else if (role == Qt::ToolTipRole && index.column() == Column::NAME) { @@ -366,18 +358,17 @@ bool MessageListModel::filterAndSort() { } void MessageListModel::msgsReceived(const std::set *new_msgs, bool has_new_ids) { - if (has_new_ids || filters_.contains(Column::FREQ) || filters_.contains(Column::COUNT) || filters_.contains(Column::DATA)) { + if (has_new_ids || ((filters_.count(Column::FREQ) || filters_.count(Column::COUNT) || filters_.count(Column::DATA)) && + ++sort_threshold_ == settings.fps)) { + sort_threshold_ = 0; if (filterAndSort()) return; } - for (int i = 0; i < items_.size(); ++i) { - auto &item = items_[i]; - bool prev_active = item.active; + + for (auto &item : items_) { item.active = isMessageActive(item.id); - if (item.active != prev_active || !new_msgs || new_msgs->count(item.id)) { - for (int col = 0; col < columnCount(); ++col) - emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole}); - } } + // Update viewport + emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); } void MessageListModel::sort(int column, Qt::SortOrder order) { @@ -420,6 +411,7 @@ void MessageView::updateBytesSectionSize() { max_bytes = std::max(max_bytes, m.dat.size()); } } + setUniformRowHeights(!delegate->multipleLines() || max_bytes <= 8); header()->resizeSection(MessageListModel::Column::DATA, delegate->sizeForBytes(max_bytes).width()); } diff --git a/tools/cabana/messageswidget.h b/tools/cabana/messageswidget.h index c110db2b56..823fcb74e3 100644 --- a/tools/cabana/messageswidget.h +++ b/tools/cabana/messageswidget.h @@ -61,6 +61,7 @@ private: std::set dbc_messages_; int sort_column = 0; Qt::SortOrder sort_order = Qt::AscendingOrder; + int sort_threshold_ = 0; }; class MessageView : public QTreeView { diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 9c52908c36..593d1bf5d8 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -130,6 +130,7 @@ void AbstractStream::updateLastMsgsTo(double sec) { current_sec_ = sec; uint64_t last_ts = (sec + routeStartTime()) * 1e9; std::unordered_map msgs; + msgs.reserve(events_.size()); for (const auto &[id, ev] : events_) { auto it = std::upper_bound(ev.begin(), ev.end(), last_ts, CompareCanEvent()); @@ -171,6 +172,8 @@ const CanEvent *AbstractStream::newEvent(uint64_t mono_time, const cereal::CanDa void AbstractStream::mergeEvents(const std::vector &events) { static MessageEventsMap msg_events; std::for_each(msg_events.begin(), msg_events.end(), [](auto &e) { e.second.clear(); }); + + // Group events by message ID for (auto e : events) { msg_events[{.source = e->src, .address = e->address}].push_back(e); } @@ -207,7 +210,7 @@ inline QColor blend(const QColor &a, const QColor &b) { return QColor((a.red() + b.red()) / 2, (a.green() + b.green()) / 2, (a.blue() + b.blue()) / 2, (a.alpha() + b.alpha()) / 2); } -// Calculate the frequency of the past minute. +// Calculate the frequency from the past one minute data double calc_freq(const MessageId &msg_id, double current_sec) { const auto &events = can->events(msg_id); uint64_t cur_mono_time = (can->routeStartTime() + current_sec) * 1e9; @@ -255,11 +258,8 @@ void CanData::compute(const MessageId &msg_id, const uint8_t *can_data, const in if (last != cur) { const int delta = cur - last; // Keep track if signal is changing randomly, or mostly moving in the same direction - if (std::signbit(delta) == std::signbit(last_change.delta)) { - last_change.same_delta_counter = std::min(16, last_change.same_delta_counter + 1); - } else { - last_change.same_delta_counter = std::max(0, last_change.same_delta_counter - 4); - } + last_change.same_delta_counter += std::signbit(delta) == std::signbit(last_change.delta) ? 1 : -4; + last_change.same_delta_counter = std::clamp(last_change.same_delta_counter, 0, 16); const double delta_t = ts - last_change.ts; // Mostly moves in the same direction, color based on delta up/down @@ -272,10 +272,10 @@ void CanData::compute(const MessageId &msg_id, const uint8_t *can_data, const in } // Track bit level changes - const uint8_t tmp = (cur ^ last); + const uint8_t diff = (cur ^ last); for (int bit = 0; bit < 8; bit++) { - if (tmp & (1 << (7 - bit))) { - last_change.bit_change_counts[bit] += 1; + if (diff & (1u << bit)) { + ++last_change.bit_change_counts[7 - bit]; } } diff --git a/tools/cabana/utils/util.cc b/tools/cabana/utils/util.cc index f85ea6d105..4556f90850 100644 --- a/tools/cabana/utils/util.cc +++ b/tools/cabana/utils/util.cc @@ -51,7 +51,8 @@ std::pair SegmentTree::get_minmax(int n, int left, int right, in // MessageBytesDelegate -MessageBytesDelegate::MessageBytesDelegate(QObject *parent, bool multiple_lines) : multiple_lines(multiple_lines), QStyledItemDelegate(parent) { +MessageBytesDelegate::MessageBytesDelegate(QObject *parent, bool multiple_lines) + : font_metrics(QApplication::font()), multiple_lines(multiple_lines), QStyledItemDelegate(parent) { fixed_font = QFontDatabase::systemFont(QFontDatabase::FixedFont); byte_size = QFontMetrics(fixed_font).size(Qt::TextSingleLine, "00 ") + QSize(0, 2); for (int i = 0; i < 256; ++i) { @@ -73,31 +74,38 @@ QSize MessageBytesDelegate::sizeHint(const QStyleOptionViewItem &option, const Q } void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - auto data = index.data(BytesRole); - if (!data.isValid()) { - return QStyledItemDelegate::paint(painter, option, index); - } - - QFont old_font = painter->font(); - QPen old_pen = painter->pen(); if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, option.palette.brush(QPalette::Normal, QPalette::Highlight)); } - const QPoint pt{option.rect.left() + h_margin, option.rect.top() + v_margin}; - painter->setFont(fixed_font); - const auto &bytes = *static_cast*>(data.value()); - const auto &colors = *static_cast*>(index.data(ColorsRole).value()); + QRect item_rect = option.rect.adjusted(h_margin, v_margin, -h_margin, -v_margin); + QColor highlighted_color = option.palette.color(QPalette::HighlightedText); auto text_color = index.data(Qt::ForegroundRole).value(); bool inactive = text_color.isValid(); if (!inactive) { text_color = option.palette.color(QPalette::Text); } + auto data = index.data(BytesRole); + if (!data.isValid()) { + painter->setFont(option.font); + painter->setPen(option.state & QStyle::State_Selected ? highlighted_color : text_color); + QString text = font_metrics.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, item_rect.width()); + painter->drawText(item_rect, Qt::AlignLeft | Qt::AlignVCenter, text); + return; + } + + // Paint hex column + const auto &bytes = *static_cast *>(data.value()); + const auto &colors = *static_cast *>(index.data(ColorsRole).value()); + painter->setFont(fixed_font); + const QPen text_pen(option.state & QStyle::State_Selected ? highlighted_color : text_color); + const QPoint pt = item_rect.topLeft(); for (int i = 0; i < bytes.size(); ++i) { int row = !multiple_lines ? 0 : i / 8; int column = !multiple_lines ? i : i % 8; - QRect r = QRect({pt.x() + column * byte_size.width(), pt.y() + row * byte_size.height()}, byte_size); + QRect r({pt.x() + column * byte_size.width(), pt.y() + row * byte_size.height()}, byte_size); + if (!inactive && i < colors.size() && colors[i].alpha() > 0) { if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(QPalette::Text)); @@ -105,12 +113,10 @@ void MessageBytesDelegate::paint(QPainter *painter, const QStyleOptionViewItem & } painter->fillRect(r, colors[i]); } else { - painter->setPen(option.state & QStyle::State_Selected ? option.palette.color(QPalette::HighlightedText) : text_color); + painter->setPen(text_pen); } utils::drawStaticText(painter, r, hex_text_table[bytes[i]]); } - painter->setFont(old_font); - painter->setPen(old_pen); } // TabBar diff --git a/tools/cabana/utils/util.h b/tools/cabana/utils/util.h index 218b8eeb51..0ebd7e41f8 100644 --- a/tools/cabana/utils/util.h +++ b/tools/cabana/utils/util.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,7 @@ public: private: std::array hex_text_table; + QFontMetrics font_metrics; QFont fixed_font; QSize byte_size = {}; bool multiple_lines = false;