cabana: socketcan support (#27952)
* empty socketcan class
* works on linux with vcan
* add open stream widget
* fix MacOS build
* update readme
* unused
* no socketcan on C3
* fix in cabana sconstruct
* serial -> device
old-commit-hash: 2b0e4d46bb
beeps
parent
3681c1d1b2
commit
6b98b83ad4
9 changed files with 189 additions and 7 deletions
@ -0,0 +1,115 @@ |
||||
#include "tools/cabana/streams/socketcanstream.h" |
||||
#include "socketcanstream.h" |
||||
|
||||
#include <QLabel> |
||||
#include <QMessageBox> |
||||
#include <QPushButton> |
||||
|
||||
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; |
||||
} |
@ -0,0 +1,50 @@ |
||||
#pragma once |
||||
|
||||
#include <QtSerialBus/QCanBus> |
||||
#include <QtSerialBus/QCanBusDevice> |
||||
#include <QtSerialBus/QCanBusDeviceInfo> |
||||
|
||||
#include <QComboBox> |
||||
#include <QFormLayout> |
||||
#include <QVBoxLayout> |
||||
|
||||
#include "tools/cabana/streams/livestream.h" |
||||
|
||||
struct SocketCanStreamConfig { |
||||
QString device = ""; // TODO: support multiple devices/buses at once
|
||||
}; |
||||
|
||||
class SocketCanStream : public LiveStream { |
||||
Q_OBJECT |
||||
public: |
||||
SocketCanStream(QObject *parent, SocketCanStreamConfig config_ = {}); |
||||
static AbstractOpenStreamWidget *widget(AbstractStream **stream); |
||||
|
||||
static bool available(); |
||||
|
||||
inline QString routeName() const override { |
||||
return QString("Live Streaming From Socket CAN %1").arg(config.device); |
||||
} |
||||
|
||||
protected: |
||||
void streamThread() override; |
||||
bool connect(); |
||||
|
||||
SocketCanStreamConfig config = {}; |
||||
std::unique_ptr<QCanBusDevice> device; |
||||
}; |
||||
|
||||
class OpenSocketCanWidget : public AbstractOpenStreamWidget { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
OpenSocketCanWidget(AbstractStream **stream); |
||||
bool open() override; |
||||
QString title() override { return tr("&SocketCAN"); } |
||||
|
||||
private: |
||||
void refreshDevices(); |
||||
|
||||
QComboBox *device_edit; |
||||
SocketCanStreamConfig config = {}; |
||||
}; |
Loading…
Reference in new issue