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
Willem Melching 2 years ago committed by GitHub
parent 3681c1d1b2
commit 6b98b83ad4
  1. 1
      tools/cabana/README.md
  2. 4
      tools/cabana/SConscript
  3. 8
      tools/cabana/cabana.cc
  4. 6
      tools/cabana/streams/abstractstream.h
  5. 6
      tools/cabana/streams/pandastream.h
  6. 115
      tools/cabana/streams/socketcanstream.cc
  7. 50
      tools/cabana/streams/socketcanstream.h
  8. 5
      tools/cabana/streamselector.cc
  9. 1
      tools/install_ubuntu_dependencies.sh

@ -17,6 +17,7 @@ Options:
--stream read can messages from live streaming --stream read can messages from live streaming
--panda read can messages from panda --panda read can messages from panda
--panda-serial <panda-serial> read can messages from panda with given serial --panda-serial <panda-serial> read can messages from panda with given serial
--socketcan <socketcan> read can messages from given SocketCAN device
--zmq <zmq> the ip address on which to receive zmq --zmq <zmq> the ip address on which to receive zmq
messages messages
--data_dir <data_dir> local directory with routes --data_dir <data_dir> local directory with routes

@ -9,9 +9,11 @@ base_libs = [common, messaging, cereal, visionipc, transformations, 'zmq',
if arch == "Darwin": if arch == "Darwin":
base_frameworks.append('OpenCL') base_frameworks.append('OpenCL')
base_frameworks.append('QtCharts') base_frameworks.append('QtCharts')
base_frameworks.append('QtSerialBus')
else: else:
base_libs.append('OpenCL') base_libs.append('OpenCL')
base_libs.append('Qt5Charts') base_libs.append('Qt5Charts')
base_libs.append('Qt5SerialBus')
qt_libs = ['qt_util'] + base_libs qt_libs = ['qt_util'] + base_libs
@ -27,7 +29,7 @@ assets_src = "assets/assets.qrc"
cabana_env.Command(assets, assets_src, f"rcc $SOURCES -o $TARGET") cabana_env.Command(assets, assets_src, f"rcc $SOURCES -o $TARGET")
cabana_env.Depends(assets, Glob('/assets/*', exclude=[assets, assets_src, "assets/assets.o"])) cabana_env.Depends(assets, Glob('/assets/*', exclude=[assets, assets_src, "assets/assets.o"]))
cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/pandastream.cc', 'streams/devicestream.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'historylog.cc', 'videowidget.cc', 'signalview.cc', cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'streams/socketcanstream.cc', 'streams/pandastream.cc', 'streams/devicestream.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'historylog.cc', 'videowidget.cc', 'signalview.cc',
'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc', 'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc',
'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc', 'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc',
'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) 'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'util.cc', 'detailwidget.cc', 'tools/findsimilarbits.cc', 'tools/findsignal.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)

@ -7,6 +7,7 @@
#include "tools/cabana/streams/devicestream.h" #include "tools/cabana/streams/devicestream.h"
#include "tools/cabana/streams/pandastream.h" #include "tools/cabana/streams/pandastream.h"
#include "tools/cabana/streams/replaystream.h" #include "tools/cabana/streams/replaystream.h"
#include "tools/cabana/streams/socketcanstream.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
QCoreApplication::setApplicationName("Cabana"); QCoreApplication::setApplicationName("Cabana");
@ -28,6 +29,9 @@ int main(int argc, char *argv[]) {
cmd_parser.addOption({"stream", "read can messages from live streaming"}); cmd_parser.addOption({"stream", "read can messages from live streaming"});
cmd_parser.addOption({"panda", "read can messages from panda"}); cmd_parser.addOption({"panda", "read can messages from panda"});
cmd_parser.addOption({"panda-serial", "read can messages from panda with given serial", "panda-serial"}); cmd_parser.addOption({"panda-serial", "read can messages from panda with given serial", "panda-serial"});
if (SocketCanStream::available()) {
cmd_parser.addOption({"socketcan", "read can messages from given SocketCAN device", "socketcan"});
}
cmd_parser.addOption({"zmq", "the ip address on which to receive zmq messages", "zmq"}); cmd_parser.addOption({"zmq", "the ip address on which to receive zmq messages", "zmq"});
cmd_parser.addOption({"data_dir", "local directory with routes", "data_dir"}); cmd_parser.addOption({"data_dir", "local directory with routes", "data_dir"});
cmd_parser.addOption({"no-vipc", "do not output video"}); cmd_parser.addOption({"no-vipc", "do not output video"});
@ -50,6 +54,10 @@ int main(int argc, char *argv[]) {
qWarning() << e.what(); qWarning() << e.what();
return 0; return 0;
} }
} else if (cmd_parser.isSet("socketcan")) {
SocketCanStreamConfig config = {};
config.device = cmd_parser.value("socketcan");
stream = new SocketCanStream(&app, config);
} else { } else {
uint32_t replay_flags = REPLAY_FLAG_NONE; uint32_t replay_flags = REPLAY_FLAG_NONE;
if (cmd_parser.isSet("ecam")) { if (cmd_parser.isSet("ecam")) {

@ -34,6 +34,12 @@ struct CanEvent {
uint8_t dat[]; uint8_t dat[];
}; };
struct BusConfig {
int can_speed_kbps = 500;
int data_speed_kbps = 2000;
bool can_fd = false;
};
class AbstractStream : public QObject { class AbstractStream : public QObject {
Q_OBJECT Q_OBJECT

@ -10,12 +10,6 @@
const uint32_t speeds[] = {10U, 20U, 50U, 100U, 125U, 250U, 500U, 1000U}; const uint32_t speeds[] = {10U, 20U, 50U, 100U, 125U, 250U, 500U, 1000U};
const uint32_t data_speeds[] = {10U, 20U, 50U, 100U, 125U, 250U, 500U, 1000U, 2000U, 5000U}; const uint32_t data_speeds[] = {10U, 20U, 50U, 100U, 125U, 250U, 500U, 1000U, 2000U, 5000U};
struct BusConfig {
int can_speed_kbps = 500;
int data_speed_kbps = 2000;
bool can_fd = false;
};
struct PandaStreamConfig { struct PandaStreamConfig {
QString serial = ""; QString serial = "";
std::vector<BusConfig> bus_config; std::vector<BusConfig> bus_config;

@ -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 = {};
};

@ -6,9 +6,11 @@
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include "streams/socketcanstream.h"
#include "tools/cabana/streams/devicestream.h" #include "tools/cabana/streams/devicestream.h"
#include "tools/cabana/streams/pandastream.h" #include "tools/cabana/streams/pandastream.h"
#include "tools/cabana/streams/replaystream.h" #include "tools/cabana/streams/replaystream.h"
#include "tools/cabana/streams/socketcanstream.h"
StreamSelector::StreamSelector(AbstractStream **stream, QWidget *parent) : QDialog(parent) { StreamSelector::StreamSelector(AbstractStream **stream, QWidget *parent) : QDialog(parent) {
setWindowTitle(tr("Open stream")); setWindowTitle(tr("Open stream"));
@ -40,6 +42,9 @@ StreamSelector::StreamSelector(AbstractStream **stream, QWidget *parent) : QDial
addStreamWidget(ReplayStream::widget(stream)); addStreamWidget(ReplayStream::widget(stream));
addStreamWidget(PandaStream::widget(stream)); addStreamWidget(PandaStream::widget(stream));
if (SocketCanStream::available()) {
addStreamWidget(SocketCanStream::widget(stream));
}
addStreamWidget(DeviceStream::widget(stream)); addStreamWidget(DeviceStream::widget(stream));
QObject::connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject); QObject::connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject);

@ -73,6 +73,7 @@ function install_ubuntu_common_requirements() {
libqt5sql5-sqlite \ libqt5sql5-sqlite \
libqt5svg5-dev \ libqt5svg5-dev \
libqt5charts5-dev \ libqt5charts5-dev \
libqt5serialbus5-dev \
libqt5x11extras5-dev \ libqt5x11extras5-dev \
libreadline-dev \ libreadline-dev \
libdw1 \ libdw1 \

Loading…
Cancel
Save