diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript index 31083695e..2fe32d0b0 100644 --- a/tools/cabana/SConscript +++ b/tools/cabana/SConscript @@ -12,6 +12,7 @@ if arch == "Darwin": qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5" qt_dirs = [ os.path.join(qt_env['QTDIR'], "include"), + os.path.join(qt_env['QTDIR'], "include", "QtSerialBus"), ] qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules] qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")] @@ -59,6 +60,7 @@ base_libs = [common, messaging, cereal, visionipc, 'm', 'ssl', 'crypto', 'pthrea if arch == "Darwin": base_frameworks.append('OpenCL') base_frameworks.append('QtCharts') + base_frameworks.append('QtSerialBus') else: base_libs.append('OpenCL') base_libs.append('Qt5Charts') diff --git a/tools/cabana/streams/socketcanstream.cc b/tools/cabana/streams/socketcanstream.cc index 66788f93b..62bea4e0e 100644 --- a/tools/cabana/streams/socketcanstream.cc +++ b/tools/cabana/streams/socketcanstream.cc @@ -1,14 +1,20 @@ #include "tools/cabana/streams/socketcanstream.h" +#if defined(__linux__) +#include #include #include #include #include #include #include +#endif #include +#if defined(__linux__) #include +#include +#endif #include #include #include @@ -17,7 +23,11 @@ SocketCanStream::SocketCanStream(QObject *parent, SocketCanStreamConfig config_) : config(config_), LiveStream(parent) { if (!available()) { +#if defined(__linux__) throw std::runtime_error("SocketCAN not available"); +#else + throw std::runtime_error("SocketCAN plugin not available"); +#endif } qDebug() << "Connecting to SocketCAN device" << config.device; @@ -28,20 +38,27 @@ SocketCanStream::SocketCanStream(QObject *parent, SocketCanStreamConfig config_) SocketCanStream::~SocketCanStream() { stop(); +#if defined(__linux__) if (sock_fd >= 0) { ::close(sock_fd); sock_fd = -1; } +#endif } bool SocketCanStream::available() { +#if defined(__linux__) int fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (fd < 0) return false; ::close(fd); return true; +#else + return QCanBus::instance()->plugins().contains("socketcan"); +#endif } bool SocketCanStream::connect() { +#if defined(__linux__) sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (sock_fd < 0) { qDebug() << "Failed to create CAN socket"; @@ -74,9 +91,28 @@ bool SocketCanStream::connect() { setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); return true; +#else + // Connecting might generate warnings about missing socketcan/libsocketcan helpers. + // Cabana only needs the basic SocketCAN plugin path. + QString error_string; + device.reset(QCanBus::instance()->createDevice("socketcan", config.device, &error_string)); + if (!device) { + qDebug() << "Failed to create SocketCAN device" << error_string; + return false; + } + + device->setConfigurationParameter(QCanBusDevice::CanFdKey, true); + if (!device->connectDevice()) { + qDebug() << "Failed to connect to device"; + return false; + } + + return true; +#endif } void SocketCanStream::streamThread() { +#if defined(__linux__) struct canfd_frame frame; while (!QThread::currentThread()->isInterruptionRequested()) { ssize_t nbytes = read(sock_fd, &frame, sizeof(frame)); @@ -92,6 +128,30 @@ void SocketCanStream::streamThread() { handleEvent(capnp::messageToFlatArray(msg)); } +#else + while (!QThread::currentThread()->isInterruptionRequested()) { + QThread::msleep(1); + + auto frames = device->readAllFrames(); + if (frames.empty()) continue; + + MessageBuilder msg; + auto evt = msg.initEvent(); + auto can_data = evt.initCan(frames.size()); + + for (uint i = 0; i < frames.size(); ++i) { + if (!frames[i].isValid()) continue; + + can_data[i].setAddress(frames[i].frameId()); + can_data[i].setSrc(0); + + auto payload = frames[i].payload(); + can_data[i].setDat(kj::arrayPtr(reinterpret_cast(payload.data()), payload.size())); + } + + handleEvent(capnp::messageToFlatArray(msg)); + } +#endif } OpenSocketCanWidget::OpenSocketCanWidget(QWidget *parent) : AbstractOpenStreamWidget(parent) { @@ -121,6 +181,7 @@ OpenSocketCanWidget::OpenSocketCanWidget(QWidget *parent) : AbstractOpenStreamWi void OpenSocketCanWidget::refreshDevices() { device_edit->clear(); +#if defined(__linux__) QDir net_dir("/sys/class/net"); for (const auto &iface : net_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { QFile type_file(net_dir.filePath(iface) + "/type"); @@ -131,6 +192,11 @@ void OpenSocketCanWidget::refreshDevices() { } } } +#else + for (const auto &device_info : QCanBus::instance()->availableDevices(QStringLiteral("socketcan"))) { + device_edit->addItem(device_info.name()); + } +#endif } AbstractStream *OpenSocketCanWidget::open() { diff --git a/tools/cabana/streams/socketcanstream.h b/tools/cabana/streams/socketcanstream.h index 4ec3c7ec1..7639c9adf 100644 --- a/tools/cabana/streams/socketcanstream.h +++ b/tools/cabana/streams/socketcanstream.h @@ -1,6 +1,13 @@ #pragma once +#include + #include +#if !defined(__linux__) +#include +#include +#include +#endif #include "tools/cabana/streams/livestream.h" @@ -24,7 +31,11 @@ protected: bool connect(); SocketCanStreamConfig config = {}; +#if defined(__linux__) int sock_fd = -1; +#else + std::unique_ptr device; +#endif }; class OpenSocketCanWidget : public AbstractOpenStreamWidget {