From cdea057d4bea3b8614240e27a19e7da94434cf36 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Wed, 29 May 2024 03:02:15 +0800 Subject: [PATCH] cabana: fix panda stream issues (#32537) fix segfault old-commit-hash: 6b3d2b5a80c61e83a925ff28cc2394f9a9c0a996 --- tools/cabana/streams/abstractstream.h | 5 +++++ tools/cabana/streams/livestream.cc | 9 +++++++- tools/cabana/streams/livestream.h | 1 + tools/cabana/streams/pandastream.cc | 31 +++++++++++++++++--------- tools/cabana/streams/pandastream.h | 5 +++-- tools/cabana/streams/socketcanstream.h | 1 + tools/cabana/streamselector.cc | 5 +++-- tools/cabana/streamselector.h | 2 ++ 8 files changed, 44 insertions(+), 15 deletions(-) diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index 3d63ee49bd..cfd423b368 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -64,6 +64,7 @@ public: AbstractStream(QObject *parent); virtual ~AbstractStream() {} virtual void start() = 0; + virtual void stop() {} virtual bool liveStreaming() const { return true; } virtual void seekTo(double ts) {} virtual QString routeName() const = 0; @@ -128,11 +129,15 @@ private: }; class AbstractOpenStreamWidget : public QWidget { + Q_OBJECT public: AbstractOpenStreamWidget(AbstractStream **stream, QWidget *parent = nullptr) : stream(stream), QWidget(parent) {} virtual bool open() = 0; virtual QString title() = 0; +signals: + void enableOpenButton(bool); + protected: AbstractStream **stream = nullptr; }; diff --git a/tools/cabana/streams/livestream.cc b/tools/cabana/streams/livestream.cc index 4e1f6d77d9..d9d96e23b0 100644 --- a/tools/cabana/streams/livestream.cc +++ b/tools/cabana/streams/livestream.cc @@ -42,6 +42,10 @@ LiveStream::LiveStream(QObject *parent) : AbstractStream(parent) { QObject::connect(stream_thread, &QThread::finished, stream_thread, &QThread::deleteLater); } +LiveStream::~LiveStream() { + stop(); +} + void LiveStream::startUpdateTimer() { update_timer.stop(); update_timer.start(1000.0 / settings.fps, this); @@ -55,11 +59,14 @@ void LiveStream::start() { begin_date_time = QDateTime::currentDateTime(); } -LiveStream::~LiveStream() { +void LiveStream::stop() { + if (!stream_thread) return; + update_timer.stop(); stream_thread->requestInterruption(); stream_thread->quit(); stream_thread->wait(); + stream_thread = nullptr; } // called in streamThread diff --git a/tools/cabana/streams/livestream.h b/tools/cabana/streams/livestream.h index 7a0f8cd834..9ca7e8d745 100644 --- a/tools/cabana/streams/livestream.h +++ b/tools/cabana/streams/livestream.h @@ -14,6 +14,7 @@ public: LiveStream(QObject *parent); virtual ~LiveStream(); void start() override; + void stop() override; inline QDateTime beginDateTime() const { return begin_date_time; } inline double routeStartTime() const override { return begin_event_ts / 1e9; } void setSpeed(float speed) override { speed_ = speed; } diff --git a/tools/cabana/streams/pandastream.cc b/tools/cabana/streams/pandastream.cc index 308391a10c..030783a899 100644 --- a/tools/cabana/streams/pandastream.cc +++ b/tools/cabana/streams/pandastream.cc @@ -6,12 +6,17 @@ #include #include #include +#include -PandaStream::PandaStream(QObject *parent, PandaStreamConfig config_) : config(config_), LiveStream(parent) {} +PandaStream::PandaStream(QObject *parent, PandaStreamConfig config_) : config(config_), LiveStream(parent) { + if (!connect()) { + throw std::runtime_error("Failed to connect to panda"); + } +} bool PandaStream::connect() { try { - qDebug() << "Connecting to panda with serial" << config.serial; + qDebug() << "Connecting to panda " << config.serial; panda.reset(new Panda(config.serial.toStdString())); config.bus_config.resize(3); qDebug() << "Connected"; @@ -80,6 +85,13 @@ AbstractOpenStreamWidget *PandaStream::widget(AbstractStream **stream) { OpenPandaWidget::OpenPandaWidget(AbstractStream **stream) : AbstractOpenStreamWidget(stream) { form_layout = new QFormLayout(this); + if (can && dynamic_cast(can) != nullptr) { + form_layout->addWidget(new QLabel(tr("Already connected to %1.").arg(can->routeName()))); + form_layout->addWidget(new QLabel("Close the current connection via [File menu -> Close Stream] before connecting to another Panda.")); + QTimer::singleShot(0, [this]() { emit enableOpenButton(false); }); + return; + } + QHBoxLayout *serial_layout = new QHBoxLayout(); serial_layout->addWidget(serial_edit = new QComboBox()); @@ -116,6 +128,7 @@ void OpenPandaWidget::buildConfigForm() { Panda panda(serial.toStdString()); has_fd = (panda.hw_type == cereal::PandaState::PandaType::RED_PANDA) || (panda.hw_type == cereal::PandaState::PandaType::RED_PANDA_V2); } catch (const std::exception& e) { + qDebug() << "failed to open panda" << serial; has_panda = false; } } @@ -170,13 +183,11 @@ void OpenPandaWidget::buildConfigForm() { } bool OpenPandaWidget::open() { - if (!config.serial.isEmpty()) { - auto panda_stream = std::make_unique(qApp, config); - if (panda_stream->connect()) { - *stream = panda_stream.release(); - return true; - } + try { + *stream = new PandaStream(qApp, config); + return true; + } catch (std::exception &e) { + QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to connect to panda: '%1'").arg(e.what())); + return false; } - QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to connect to panda")); - return false; } diff --git a/tools/cabana/streams/pandastream.h b/tools/cabana/streams/pandastream.h index ce0adfc9f8..c50407be1a 100644 --- a/tools/cabana/streams/pandastream.h +++ b/tools/cabana/streams/pandastream.h @@ -21,13 +21,14 @@ class PandaStream : public LiveStream { Q_OBJECT public: PandaStream(QObject *parent, PandaStreamConfig config_ = {}); - bool connect(); + ~PandaStream() { stop(); } static AbstractOpenStreamWidget *widget(AbstractStream **stream); inline QString routeName() const override { - return QString("Live Streaming From Panda %1").arg(config.serial); + return QString("Panda: %1").arg(config.serial); } protected: + bool connect(); void streamThread() override; std::unique_ptr panda; diff --git a/tools/cabana/streams/socketcanstream.h b/tools/cabana/streams/socketcanstream.h index e0fb826acb..952f1aaa5c 100644 --- a/tools/cabana/streams/socketcanstream.h +++ b/tools/cabana/streams/socketcanstream.h @@ -17,6 +17,7 @@ class SocketCanStream : public LiveStream { Q_OBJECT public: SocketCanStream(QObject *parent, SocketCanStreamConfig config_ = {}); + ~SocketCanStream() { stop(); } static AbstractOpenStreamWidget *widget(AbstractStream **stream); static bool available(); diff --git a/tools/cabana/streamselector.cc b/tools/cabana/streamselector.cc index d12f1a5df7..209b577c9a 100644 --- a/tools/cabana/streamselector.cc +++ b/tools/cabana/streamselector.cc @@ -1,6 +1,5 @@ #include "tools/cabana/streamselector.h" -#include #include #include #include @@ -31,7 +30,7 @@ StreamSelector::StreamSelector(AbstractStream **stream, QWidget *parent) : QDial line->setFrameStyle(QFrame::HLine | QFrame::Sunken); layout->addWidget(line); - auto btn_box = new QDialogButtonBox(QDialogButtonBox::Open | QDialogButtonBox::Cancel); + btn_box = new QDialogButtonBox(QDialogButtonBox::Open | QDialogButtonBox::Cancel); layout->addWidget(btn_box); addStreamWidget(ReplayStream::widget(stream)); @@ -60,4 +59,6 @@ StreamSelector::StreamSelector(AbstractStream **stream, QWidget *parent) : QDial void StreamSelector::addStreamWidget(AbstractOpenStreamWidget *w) { tab->addTab(w, w->title()); + auto open_btn = btn_box->button(QDialogButtonBox::Open); + QObject::connect(w, &AbstractOpenStreamWidget::enableOpenButton, open_btn, &QPushButton::setEnabled); } diff --git a/tools/cabana/streamselector.h b/tools/cabana/streamselector.h index 19b438d55a..a702fc76ad 100644 --- a/tools/cabana/streamselector.h +++ b/tools/cabana/streamselector.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -17,4 +18,5 @@ public: private: QLineEdit *dbc_file; QTabWidget *tab; + QDialogButtonBox *btn_box; };