IQ.Pilot Release Commit @ e46d557
This commit is contained in:
@@ -1,67 +1,99 @@
|
||||
#include "tools/cabana/streams/socketcanstream.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/raw.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QThread>
|
||||
|
||||
SocketCanStream::SocketCanStream(QObject *parent, SocketCanStreamConfig config_) : config(config_), LiveStream(parent) {
|
||||
if (!available()) {
|
||||
throw std::runtime_error("SocketCAN plugin not available");
|
||||
throw std::runtime_error("SocketCAN not available");
|
||||
}
|
||||
|
||||
qDebug() << "Connecting to SocketCAN device" << config.device;
|
||||
fprintf(stderr, "Connecting to SocketCAN device %s\n", config.device.c_str());
|
||||
if (!connect()) {
|
||||
throw std::runtime_error("Failed to connect to SocketCAN device");
|
||||
}
|
||||
}
|
||||
|
||||
SocketCanStream::~SocketCanStream() {
|
||||
stop();
|
||||
if (sock_fd >= 0) {
|
||||
::close(sock_fd);
|
||||
sock_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool SocketCanStream::available() {
|
||||
return QCanBus::instance()->plugins().contains("socketcan");
|
||||
int fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
|
||||
if (fd < 0) return false;
|
||||
::close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
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));
|
||||
device->setConfigurationParameter(QCanBusDevice::CanFdKey, true);
|
||||
|
||||
if (!device) {
|
||||
qDebug() << "Failed to create SocketCAN device" << errorString;
|
||||
sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
|
||||
if (sock_fd < 0) {
|
||||
fprintf(stderr, "Failed to create CAN socket\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device->connectDevice()) {
|
||||
qDebug() << "Failed to connect to device";
|
||||
// Enable CAN-FD
|
||||
int fd_enable = 1;
|
||||
setsockopt(sock_fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &fd_enable, sizeof(fd_enable));
|
||||
|
||||
struct ifreq ifr = {};
|
||||
strncpy(ifr.ifr_name, config.device.c_str(), IFNAMSIZ - 1);
|
||||
if (ioctl(sock_fd, SIOCGIFINDEX, &ifr) < 0) {
|
||||
fprintf(stderr, "Failed to get interface index for %s\n", config.device.c_str());
|
||||
::close(sock_fd);
|
||||
sock_fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_can addr = {};
|
||||
addr.can_family = AF_CAN;
|
||||
addr.can_ifindex = ifr.ifr_ifindex;
|
||||
if (bind(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
fprintf(stderr, "Failed to bind CAN socket\n");
|
||||
::close(sock_fd);
|
||||
sock_fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set read timeout so the thread can check for interruption
|
||||
struct timeval tv = {.tv_sec = 0, .tv_usec = 100000}; // 100ms
|
||||
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SocketCanStream::streamThread() {
|
||||
while (!QThread::currentThread()->isInterruptionRequested()) {
|
||||
QThread::msleep(1);
|
||||
struct canfd_frame frame;
|
||||
|
||||
auto frames = device->readAllFrames();
|
||||
if (frames.size() == 0) continue;
|
||||
while (!exit_) {
|
||||
ssize_t nbytes = read(sock_fd, &frame, sizeof(frame));
|
||||
if (nbytes <= 0) continue;
|
||||
|
||||
uint8_t len = (nbytes == CAN_MTU) ? frame.len : frame.len; // works for both CAN and CAN-FD
|
||||
|
||||
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 canData = evt.initCan(1);
|
||||
canData[0].setAddress(frame.can_id & CAN_EFF_MASK);
|
||||
canData[0].setSrc(0);
|
||||
canData[0].setDat(kj::arrayPtr(frame.data, len));
|
||||
|
||||
handleEvent(capnp::messageToFlatArray(msg));
|
||||
}
|
||||
@@ -87,7 +119,7 @@ OpenSocketCanWidget::OpenSocketCanWidget(QWidget *parent) : AbstractOpenStreamWi
|
||||
main_layout->addStretch(1);
|
||||
|
||||
QObject::connect(refresh, &QPushButton::clicked, this, &OpenSocketCanWidget::refreshDevices);
|
||||
QObject::connect(device_edit, &QComboBox::currentTextChanged, this, [=]{ config.device = device_edit->currentText(); });
|
||||
QObject::connect(device_edit, &QComboBox::currentTextChanged, this, [=]{ config.device = device_edit->currentText().toStdString(); });
|
||||
|
||||
// Populate devices
|
||||
refreshDevices();
|
||||
@@ -95,12 +127,17 @@ OpenSocketCanWidget::OpenSocketCanWidget(QWidget *parent) : AbstractOpenStreamWi
|
||||
|
||||
void OpenSocketCanWidget::refreshDevices() {
|
||||
device_edit->clear();
|
||||
for (auto device : QCanBus::instance()->availableDevices(QStringLiteral("socketcan"))) {
|
||||
device_edit->addItem(device.name());
|
||||
// Scan /sys/class/net/ for CAN interfaces (type 280 = ARPHRD_CAN)
|
||||
std::error_code ec;
|
||||
for (const auto &entry : std::filesystem::directory_iterator("/sys/class/net", ec)) {
|
||||
std::ifstream type_file(entry.path() / "type");
|
||||
int type = 0;
|
||||
if (type_file >> type && type == 280) {
|
||||
device_edit->addItem(QString::fromStdString(entry.path().filename().string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AbstractStream *OpenSocketCanWidget::open() {
|
||||
try {
|
||||
return new SocketCanStream(qApp, config);
|
||||
|
||||
Reference in New Issue
Block a user