diff --git a/tools/cabana/streams/pandastream.cc b/tools/cabana/streams/pandastream.cc index 6eab40a652..5b3bf890e8 100644 --- a/tools/cabana/streams/pandastream.cc +++ b/tools/cabana/streams/pandastream.cc @@ -1,9 +1,12 @@ #include "tools/cabana/streams/pandastream.h" -#include +#include #include +#include #include +#include "selfdrive/ui/qt/util.h" + PandaStream::PandaStream(QObject *parent, PandaStreamConfig config_) : config(config_), LiveStream(parent) { if (config.serial.isEmpty()) { auto serials = Panda::list(); @@ -97,16 +100,105 @@ OpenPandaWidget::OpenPandaWidget(AbstractStream **stream) : AbstractOpenStreamWi main_layout->addStretch(1); QFormLayout *form_layout = new QFormLayout(); - form_layout->addRow(tr("Serial"), serial_edit = new QLineEdit(this)); - serial_edit->setPlaceholderText(tr("Leave empty to use default serial")); + QHBoxLayout *serial_layout = new QHBoxLayout(); + serial_edit = new QComboBox(); + serial_edit->setFixedWidth(300); + serial_layout->addWidget(serial_edit); + + QPushButton *refresh = new QPushButton(tr("Refresh")); + refresh->setFixedWidth(100); + serial_layout->addWidget(refresh); + form_layout->addRow(tr("Serial"), serial_layout); main_layout->addLayout(form_layout); + + config_layout = new QFormLayout(); + main_layout->addLayout(config_layout); + main_layout->addStretch(1); + + QObject::connect(refresh, &QPushButton::clicked, this, &OpenPandaWidget::refreshSerials); + QObject::connect(serial_edit, &QComboBox::currentTextChanged, this, &OpenPandaWidget::buildConfigForm); + + // Populate serials + refreshSerials(); + buildConfigForm(); +} + +void OpenPandaWidget::refreshSerials() { + serial_edit->clear(); + for (auto serial : Panda::list()) { + serial_edit->addItem(QString::fromStdString(serial)); + } +} + +void OpenPandaWidget::buildConfigForm() { + clearLayout(config_layout); + QString serial = serial_edit->currentText(); + + bool has_fd = false; + bool has_panda = !serial.isEmpty(); + + if (has_panda) { + try { + Panda 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) { + has_panda = false; + } + } + + if (has_panda) { + config.serial = serial; + config.bus_config.resize(3); + for (int i = 0; i < config.bus_config.size(); i++) { + QHBoxLayout *bus_layout = new QHBoxLayout; + + // CAN Speed + bus_layout->addWidget(new QLabel(tr("CAN Speed (kbps):"))); + QComboBox *can_speed = new QComboBox; + for (int j = 0; j < std::size(speeds); j++) { + can_speed->addItem(QString::number(speeds[j])); + + if (data_speeds[j] == config.bus_config[i].can_speed_kbps) { + can_speed->setCurrentIndex(j); + } + } + QObject::connect(can_speed, qOverload(&QComboBox::currentIndexChanged), [=](int index) {config.bus_config[i].can_speed_kbps = speeds[index];}); + bus_layout->addWidget(can_speed); + + // CAN-FD Speed + if (has_fd) { + QCheckBox *enable_fd = new QCheckBox("CAN-FD"); + bus_layout->addWidget(enable_fd); + bus_layout->addWidget(new QLabel(tr("Data Speed (kbps):"))); + QComboBox *data_speed = new QComboBox; + for (int j = 0; j < std::size(data_speeds); j++) { + data_speed->addItem(QString::number(data_speeds[j])); + + if (data_speeds[j] == config.bus_config[i].data_speed_kbps) { + data_speed->setCurrentIndex(j); + } + } + + data_speed->setEnabled(false); + bus_layout->addWidget(data_speed); + + QObject::connect(data_speed, qOverload(&QComboBox::currentIndexChanged), [=](int index) {config.bus_config[i].data_speed_kbps = data_speeds[index];}); + QObject::connect(enable_fd, &QCheckBox::stateChanged, data_speed, &QComboBox::setEnabled); + QObject::connect(enable_fd, &QCheckBox::stateChanged, [=](int state) {config.bus_config[i].can_fd = (bool)state;}); + } + + config_layout->addRow(tr("Bus %1:").arg(i), bus_layout); + } + } else { + config.serial = ""; + config_layout->addWidget(new QLabel(tr("No panda found"))); + } } bool OpenPandaWidget::open() { try { - PandaStreamConfig config = {.serial = serial_edit->text()}; *stream = new PandaStream(qApp, config); } catch (std::exception &e) { QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to connect to panda: '%1'").arg(e.what())); diff --git a/tools/cabana/streams/pandastream.h b/tools/cabana/streams/pandastream.h index df7ccded51..f726c5cfb6 100644 --- a/tools/cabana/streams/pandastream.h +++ b/tools/cabana/streams/pandastream.h @@ -1,8 +1,15 @@ #pragma once +#include +#include +#include + #include "tools/cabana/streams/livestream.h" #include "selfdrive/boardd/panda.h" +const uint32_t speeds[] = {10U, 20U, 50U, 100U, 125U, 250U, 500U, 1000U}; +const uint32_t data_speeds[] = {10U, 20U, 50U, 100U, 125U, 250U, 500U, 1000U, 2000U, 5000U}; + struct BusConfig { int can_speed_kbps = 500; int data_speed_kbps = 2000; @@ -40,5 +47,10 @@ public: QString title() override { return tr("&Panda"); } private: - QLineEdit *serial_edit; + void refreshSerials(); + void buildConfigForm(); + + QComboBox *serial_edit; + QFormLayout *config_layout; + PandaStreamConfig config = {}; };