|  |  |  | @ -1,9 +1,12 @@ | 
			
		
	
		
			
				
					|  |  |  |  | #include "tools/cabana/streams/pandastream.h" | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #include <QFormLayout> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QLabel> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QMessageBox> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QPushButton> | 
			
		
	
		
			
				
					|  |  |  |  | #include <QVBoxLayout> | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #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<int>(&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<int>(&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())); | 
			
		
	
	
		
			
				
					|  |  |  | 
 |