You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							187 lines
						
					
					
						
							6.1 KiB
						
					
					
				
			
		
		
	
	
							187 lines
						
					
					
						
							6.1 KiB
						
					
					
				#include "tools/cabana/streams/pandastream.h"
 | 
						|
 | 
						|
#include <QDebug>
 | 
						|
#include <QCheckBox>
 | 
						|
#include <QLabel>
 | 
						|
#include <QMessageBox>
 | 
						|
#include <QPushButton>
 | 
						|
#include <QThread>
 | 
						|
#include <QTimer>
 | 
						|
 | 
						|
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 " << config.serial;
 | 
						|
    panda.reset(new Panda(config.serial.toStdString()));
 | 
						|
    config.bus_config.resize(3);
 | 
						|
    qDebug() << "Connected";
 | 
						|
  } catch (const std::exception& e) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  panda->set_safety_model(cereal::CarParams::SafetyModel::SILENT);
 | 
						|
  for (int bus = 0; bus < config.bus_config.size(); bus++) {
 | 
						|
    panda->set_can_speed_kbps(bus, config.bus_config[bus].can_speed_kbps);
 | 
						|
 | 
						|
    // CAN-FD
 | 
						|
    if (panda->hw_type == cereal::PandaState::PandaType::RED_PANDA || panda->hw_type == cereal::PandaState::PandaType::RED_PANDA_V2) {
 | 
						|
      if (config.bus_config[bus].can_fd) {
 | 
						|
        panda->set_data_speed_kbps(bus, config.bus_config[bus].data_speed_kbps);
 | 
						|
      } else {
 | 
						|
        // Hack to disable can-fd by setting data speed to a low value
 | 
						|
        panda->set_data_speed_kbps(bus, 10);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void PandaStream::streamThread() {
 | 
						|
  std::vector<can_frame> raw_can_data;
 | 
						|
 | 
						|
  while (!QThread::currentThread()->isInterruptionRequested()) {
 | 
						|
    QThread::msleep(1);
 | 
						|
 | 
						|
    if (!panda->connected()) {
 | 
						|
      qDebug() << "Connection to panda lost. Attempting reconnect.";
 | 
						|
      if (!connect()){
 | 
						|
        QThread::msleep(1000);
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    raw_can_data.clear();
 | 
						|
    if (!panda->can_receive(raw_can_data)) {
 | 
						|
      qDebug() << "failed to receive";
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    MessageBuilder msg;
 | 
						|
    auto evt = msg.initEvent();
 | 
						|
    auto canData = evt.initCan(raw_can_data.size());
 | 
						|
    for (uint i = 0; i<raw_can_data.size(); i++) {
 | 
						|
      canData[i].setAddress(raw_can_data[i].address);
 | 
						|
      canData[i].setDat(kj::arrayPtr((uint8_t*)raw_can_data[i].dat.data(), raw_can_data[i].dat.size()));
 | 
						|
      canData[i].setSrc(raw_can_data[i].src);
 | 
						|
    }
 | 
						|
 | 
						|
    handleEvent(capnp::messageToFlatArray(msg));
 | 
						|
 | 
						|
    panda->send_heartbeat(false);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// OpenPandaWidget
 | 
						|
 | 
						|
OpenPandaWidget::OpenPandaWidget(QWidget *parent) : AbstractOpenStreamWidget(parent) {
 | 
						|
  form_layout = new QFormLayout(this);
 | 
						|
  if (can && dynamic_cast<PandaStream *>(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());
 | 
						|
 | 
						|
  QPushButton *refresh = new QPushButton(tr("Refresh"));
 | 
						|
  refresh->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
 | 
						|
  serial_layout->addWidget(refresh);
 | 
						|
  form_layout->addRow(tr("Serial"), serial_layout);
 | 
						|
 | 
						|
  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() {
 | 
						|
  for (int i = form_layout->rowCount() - 1; i > 0; --i) {
 | 
						|
    form_layout->removeRow(i);
 | 
						|
  }
 | 
						|
 | 
						|
  QString serial = serial_edit->currentText();
 | 
						|
  bool has_fd = false;
 | 
						|
  bool has_panda = !serial.isEmpty();
 | 
						|
  if (has_panda) {
 | 
						|
    try {
 | 
						|
      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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  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;});
 | 
						|
      }
 | 
						|
 | 
						|
      form_layout->addRow(tr("Bus %1:").arg(i), bus_layout);
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    config.serial = "";
 | 
						|
    form_layout->addWidget(new QLabel(tr("No panda found")));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
AbstractStream *OpenPandaWidget::open() {
 | 
						|
  try {
 | 
						|
    return new PandaStream(qApp, config);
 | 
						|
  } catch (std::exception &e) {
 | 
						|
    QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to connect to panda: '%1'").arg(e.what()));
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 |