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