#include "tools/cabana/streams/socketcanstream.h" #include #include #include SocketCanStream::SocketCanStream(QObject *parent, SocketCanStreamConfig config_) : config(config_), LiveStream(parent) { if (!available()) { throw std::runtime_error("SocketCAN plugin not available"); } qDebug() << "Connecting to SocketCAN device" << config.device; if (!connect()) { throw std::runtime_error("Failed to connect to SocketCAN device"); } } bool SocketCanStream::available() { return QCanBus::instance()->plugins().contains("socketcan"); } bool SocketCanStream::connect() { // Connecting might generate some warnings about missing socketcan/libsocketcan libraries // These are expected and can be ignored, we don't need the advanced features of libsocketcan QString errorString; device.reset(QCanBus::instance()->createDevice("socketcan", config.device, &errorString)); if (!device) { qDebug() << "Failed to create SocketCAN device" << errorString; return false; } if (!device->connectDevice()) { qDebug() << "Failed to connect to device"; return false; } return true; } void SocketCanStream::streamThread() { while (!QThread::currentThread()->isInterruptionRequested()) { QThread::msleep(1); auto frames = device->readAllFrames(); if (frames.size() == 0) continue; MessageBuilder msg; auto evt = msg.initEvent(); auto canData = evt.initCan(frames.size()); for (uint i = 0; i < frames.size(); i++) { if (!frames[i].isValid()) continue; canData[i].setAddress(frames[i].frameId()); canData[i].setSrc(0); auto payload = frames[i].payload(); canData[i].setDat(kj::arrayPtr((uint8_t*)payload.data(), payload.size())); } auto bytes = msg.toBytes(); handleEvent((const char*)bytes.begin(), bytes.size()); } } AbstractOpenStreamWidget *SocketCanStream::widget(AbstractStream **stream) { return new OpenSocketCanWidget(stream); } OpenSocketCanWidget::OpenSocketCanWidget(AbstractStream **stream) : AbstractOpenStreamWidget(stream) { QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->addStretch(1); QFormLayout *form_layout = new QFormLayout(); QHBoxLayout *device_layout = new QHBoxLayout(); device_edit = new QComboBox(); device_edit->setFixedWidth(300); device_layout->addWidget(device_edit); QPushButton *refresh = new QPushButton(tr("Refresh")); refresh->setFixedWidth(100); device_layout->addWidget(refresh); form_layout->addRow(tr("Device"), device_layout); main_layout->addLayout(form_layout); main_layout->addStretch(1); QObject::connect(refresh, &QPushButton::clicked, this, &OpenSocketCanWidget::refreshDevices); QObject::connect(device_edit, &QComboBox::currentTextChanged, this, [=]{ config.device = device_edit->currentText(); }); // Populate devices refreshDevices(); } void OpenSocketCanWidget::refreshDevices() { device_edit->clear(); for (auto device : QCanBus::instance()->availableDevices(QStringLiteral("socketcan"))) { device_edit->addItem(device.name()); } } bool OpenSocketCanWidget::open() { try { *stream = new SocketCanStream(qApp, config); } catch (std::exception &e) { QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to connect to SocketCAN device: '%1'").arg(e.what())); return false; } return true; }