From a6645a1be189ee189517982939b1f964b8d4aa38 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Tue, 9 Dec 2025 08:36:35 +0800 Subject: [PATCH] cabana: add automatic session save/restore (#36736) adds auto session save/store --- tools/cabana/binaryview.cc | 5 +--- tools/cabana/chart/chartswidget.cc | 26 +++++++++++++++++++ tools/cabana/chart/chartswidget.h | 2 ++ tools/cabana/dbc/dbc.h | 6 +++++ tools/cabana/detailwidget.cc | 40 +++++++++++++++++++++++++----- tools/cabana/detailwidget.h | 7 +++++- tools/cabana/mainwin.cc | 36 +++++++++++++++++++++++++++ tools/cabana/mainwin.h | 2 ++ tools/cabana/settings.cc | 4 +++ tools/cabana/settings.h | 6 +++++ 10 files changed, 123 insertions(+), 11 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index eb0af5b64..b5a68c6b2 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -275,16 +275,13 @@ void BinaryViewModel::refresh() { row_count = can->lastMessage(msg_id).dat.size(); items.resize(row_count * column_count); } - int valid_rows = std::min(can->lastMessage(msg_id).dat.size(), row_count); - for (int i = 0; i < valid_rows * column_count; ++i) { - items[i].valid = true; - } endResetModel(); updateState(); } void BinaryViewModel::updateItem(int row, int col, uint8_t val, const QColor &color) { auto &item = items[row * column_count + col]; + item.valid = true; if (item.val != val || item.bg_color != color) { item.val = val; item.bg_color = color; diff --git a/tools/cabana/chart/chartswidget.cc b/tools/cabana/chart/chartswidget.cc index 3e9e452b9..aba25dcf8 100644 --- a/tools/cabana/chart/chartswidget.cc +++ b/tools/cabana/chart/chartswidget.cc @@ -322,6 +322,32 @@ void ChartsWidget::splitChart(ChartView *src_chart) { } } +QStringList ChartsWidget::serializeChartIds() const { + QStringList chart_ids; + for (auto c : charts) { + QStringList ids; + for (const auto& s : c->sigs) + ids += QString("%1|%2").arg(s.msg_id.toString(), s.sig->name); + chart_ids += ids.join(','); + } + std::reverse(chart_ids.begin(), chart_ids.end()); + return chart_ids; +} + +void ChartsWidget::restoreChartsFromIds(const QStringList& chart_ids) { + for (const auto& chart_id : chart_ids) { + int index = 0; + for (const auto& part : chart_id.split(',')) { + const auto sig_parts = part.split('|'); + if (sig_parts.size() != 2) continue; + MessageId msg_id = MessageId::fromString(sig_parts[0]); + if (auto* msg = dbc()->msg(msg_id)) + if (auto* sig = msg->sig(sig_parts[1])) + showChart(msg_id, sig, true, index++ > 0); + } + } +} + void ChartsWidget::setColumnCount(int n) { n = std::clamp(n, 1, MAX_COLUMN_COUNT); if (column_count != n) { diff --git a/tools/cabana/chart/chartswidget.h b/tools/cabana/chart/chartswidget.h index 46e7f546b..f87b1276c 100644 --- a/tools/cabana/chart/chartswidget.h +++ b/tools/cabana/chart/chartswidget.h @@ -43,6 +43,8 @@ public: ChartsWidget(QWidget *parent = nullptr); void showChart(const MessageId &id, const cabana::Signal *sig, bool show, bool merge); inline bool hasSignal(const MessageId &id, const cabana::Signal *sig) { return findChart(id, sig) != nullptr; } + QStringList serializeChartIds() const; + void restoreChartsFromIds(const QStringList &chart_ids); public slots: void setColumnCount(int n); diff --git a/tools/cabana/dbc/dbc.h b/tools/cabana/dbc/dbc.h index d2b25bc5f..134d88a91 100644 --- a/tools/cabana/dbc/dbc.h +++ b/tools/cabana/dbc/dbc.h @@ -20,6 +20,12 @@ struct MessageId { return QString("%1:%2").arg(source).arg(QString::number(address, 16).toUpper()); } + inline static MessageId fromString(const QString &str) { + auto parts = str.split(':'); + if (parts.size() != 2) return {}; + return MessageId{.source = uint8_t(parts[0].toUInt()), .address = parts[1].toUInt(nullptr, 16)}; + } + bool operator==(const MessageId &other) const { return source == other.source && address == other.address; } diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 4eda46f37..35492c8ef 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -118,10 +118,7 @@ void DetailWidget::showTabBarContextMenu(const QPoint &pt) { } } -void DetailWidget::setMessage(const MessageId &message_id) { - if (std::exchange(msg_id, message_id) == message_id) return; - - tabbar->blockSignals(true); +int DetailWidget::findOrAddTab(const MessageId& message_id) { int index = tabbar->count() - 1; for (/**/; index >= 0; --index) { if (tabbar->tabData(index).value() == message_id) break; @@ -131,6 +128,14 @@ void DetailWidget::setMessage(const MessageId &message_id) { tabbar->setTabData(index, QVariant::fromValue(message_id)); tabbar->setTabToolTip(index, msgName(message_id)); } + return index; +} + +void DetailWidget::setMessage(const MessageId &message_id) { + if (std::exchange(msg_id, message_id) == message_id) return; + + tabbar->blockSignals(true); + int index = findOrAddTab(message_id); tabbar->setCurrentIndex(index); tabbar->blockSignals(false); @@ -142,6 +147,29 @@ void DetailWidget::setMessage(const MessageId &message_id) { setUpdatesEnabled(true); } +std::pair DetailWidget::serializeMessageIds() const { + QStringList msgs; + for (int i = 0; i < tabbar->count(); ++i) { + MessageId id = tabbar->tabData(i).value(); + msgs.append(id.toString()); + } + return std::make_pair(msg_id.toString(), msgs); +} + +void DetailWidget::restoreTabs(const QString active_msg_id, const QStringList& msg_ids) { + tabbar->blockSignals(true); + for (const auto& str_id : msg_ids) { + MessageId id = MessageId::fromString(str_id); + if (dbc()->msg(id) != nullptr) + findOrAddTab(id); + } + tabbar->blockSignals(false); + + auto active_id = MessageId::fromString(active_msg_id); + if (dbc()->msg(active_id) != nullptr) + setMessage(active_id); +} + void DetailWidget::refresh() { QStringList warnings; auto msg = dbc()->msg(msg_id); @@ -244,13 +272,13 @@ CenterWidget::CenterWidget(QWidget *parent) : QWidget(parent) { main_layout->addWidget(welcome_widget = createWelcomeWidget()); } -void CenterWidget::setMessage(const MessageId &msg_id) { +DetailWidget* CenterWidget::ensureDetailWidget() { if (!detail_widget) { delete welcome_widget; welcome_widget = nullptr; layout()->addWidget(detail_widget = new DetailWidget(((MainWindow*)parentWidget())->charts_widget, this)); } - detail_widget->setMessage(msg_id); + return detail_widget; } void CenterWidget::clear() { diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 6df164b44..0fe1535c7 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -34,9 +34,12 @@ public: DetailWidget(ChartsWidget *charts, QWidget *parent); void setMessage(const MessageId &message_id); void refresh(); + std::pair serializeMessageIds() const; + void restoreTabs(const QString active_msg_id, const QStringList &msg_ids); private: void createToolBar(); + int findOrAddTab(const MessageId& message_id); void showTabBarContextMenu(const QPoint &pt); void editMsg(); void removeMsg(); @@ -60,7 +63,9 @@ class CenterWidget : public QWidget { Q_OBJECT public: CenterWidget(QWidget *parent); - void setMessage(const MessageId &msg_id); + void setMessage(const MessageId &message_id) { ensureDetailWidget()->setMessage(message_id); } + DetailWidget* getDetailWidget() { return detail_widget; } + DetailWidget* ensureDetailWidget(); void clear(); private: diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index d65fc5b76..2d070acff 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -235,6 +235,8 @@ void MainWindow::DBCFileChanged() { title.push_back(tr("(%1) %2").arg(toString(dbc()->sources(f)), f->name())); } setWindowFilePath(title.join(" | ")); + + QTimer::singleShot(0, this, &::MainWindow::restoreSessionState); } void MainWindow::selectAndOpenStream() { @@ -563,6 +565,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { settings.message_header_state = messages_widget->saveHeaderState(); } + saveSessionState(); QWidget::closeEvent(event); } @@ -607,6 +610,39 @@ void MainWindow::toggleFullScreen() { } } +void MainWindow::saveSessionState() { + settings.recent_dbc_file = ""; + settings.active_msg_id = ""; + settings.selected_msg_ids.clear(); + settings.active_charts.clear(); + + for (auto &f : dbc()->allDBCFiles()) + if (!f->isEmpty()) { settings.recent_dbc_file = f->filename; break; } + + if (auto *detail = center_widget->getDetailWidget()) { + auto [active_id, ids] = detail->serializeMessageIds(); + settings.active_msg_id = active_id; + settings.selected_msg_ids = ids; + } + if (charts_widget) + settings.active_charts = charts_widget->serializeChartIds(); +} + +void MainWindow::restoreSessionState() { + if (settings.recent_dbc_file.isEmpty() || dbc()->nonEmptyDBCCount() == 0) return; + + QString dbc_file; + for (auto& f : dbc()->allDBCFiles()) + if (!f->isEmpty()) { dbc_file = f->filename; break; } + if (dbc_file != settings.recent_dbc_file) return; + + if (!settings.selected_msg_ids.isEmpty()) + center_widget->ensureDetailWidget()->restoreTabs(settings.active_msg_id, settings.selected_msg_ids); + + if (charts_widget != nullptr && !settings.active_charts.empty()) + charts_widget->restoreChartsFromIds(settings.active_charts); +} + // HelpOverlay HelpOverlay::HelpOverlay(MainWindow *parent) : QWidget(parent) { setAttribute(Qt::WA_NoSystemBackground, true); diff --git a/tools/cabana/mainwin.h b/tools/cabana/mainwin.h index 9bc94c090..1da59f93e 100644 --- a/tools/cabana/mainwin.h +++ b/tools/cabana/mainwin.h @@ -72,6 +72,8 @@ protected: void updateLoadSaveMenus(); void createDockWidgets(); void eventsMerged(); + void saveSessionState(); + void restoreSessionState(); VideoWidget *video_widget = nullptr; QDockWidget *video_dock; diff --git a/tools/cabana/settings.cc b/tools/cabana/settings.cc index cccc9b6d9..e7b1129a3 100644 --- a/tools/cabana/settings.cc +++ b/tools/cabana/settings.cc @@ -41,6 +41,10 @@ void settings_op(SettingOperation op) { op(s, "log_path", settings.log_path); op(s, "drag_direction", (int &)settings.drag_direction); op(s, "suppress_defined_signals", settings.suppress_defined_signals); + op(s, "recent_dbc_file", settings.recent_dbc_file); + op(s, "active_msg_id", settings.active_msg_id); + op(s, "selected_msg_ids", settings.selected_msg_ids); + op(s, "active_charts", settings.active_charts); } Settings::Settings() { diff --git a/tools/cabana/settings.h b/tools/cabana/settings.h index e75c519ac..7ab50d149 100644 --- a/tools/cabana/settings.h +++ b/tools/cabana/settings.h @@ -46,6 +46,12 @@ public: QByteArray message_header_state; DragDirection drag_direction = MsbFirst; + // session data + QString recent_dbc_file; + QString active_msg_id; + QStringList selected_msg_ids; + QStringList active_charts; + signals: void changed(); };