cabana: remove QtSerialBus (#37523)

This commit is contained in:
Adeeb Shihadeh
2026-03-01 16:12:04 -08:00
committed by GitHub
parent ce04d25f7d
commit 3478ac1338
13 changed files with 133 additions and 94 deletions

View File

@@ -68,10 +68,8 @@ base_libs = [common, messaging, cereal, visionipc, 'm', 'ssl', 'crypto', 'pthrea
if arch == "Darwin":
base_frameworks.append('QtCharts')
base_frameworks.append('QtSerialBus')
else:
base_libs.append('Qt5Charts')
base_libs.append('Qt5SerialBus')
qt_libs = base_libs

View File

@@ -111,7 +111,8 @@ void BinaryView::highlight(const cabana::Signal *sig) {
if (sig != hovered_sig) {
for (int i = 0; i < model->items.size(); ++i) {
auto &item_sigs = model->items[i].sigs;
if ((sig && item_sigs.contains(sig)) || (hovered_sig && item_sigs.contains(hovered_sig))) {
auto has = [](const auto &v, auto p) { return std::find(v.begin(), v.end(), p) != v.end(); };
if ((sig && has(item_sigs, sig)) || (hovered_sig && has(item_sigs, hovered_sig))) {
auto index = model->index(i / model->columnCount(), i % model->columnCount());
emit model->dataChanged(index, index, {Qt::DisplayRole});
}
@@ -157,7 +158,7 @@ void BinaryView::mousePressEvent(QMouseEvent *event) {
void BinaryView::highlightPosition(const QPoint &pos) {
if (auto index = indexAt(viewport()->mapFromGlobal(pos)); index.isValid()) {
auto item = (BinaryViewModel::Item *)index.internalPointer();
const cabana::Signal *sig = item->sigs.isEmpty() ? nullptr : item->sigs.back();
const cabana::Signal *sig = item->sigs.empty() ? nullptr : item->sigs.back();
highlight(sig);
}
}
@@ -208,12 +209,12 @@ void BinaryView::refresh() {
highlightPosition(QCursor::pos());
}
QSet<const cabana::Signal *> BinaryView::getOverlappingSignals() const {
QSet<const cabana::Signal *> overlapping;
std::set<const cabana::Signal *> BinaryView::getOverlappingSignals() const {
std::set<const cabana::Signal *> overlapping;
for (const auto &item : model->items) {
if (item.sigs.size() > 1) {
for (auto s : item.sigs) {
if (s->type == cabana::Signal::Type::Normal) overlapping += s;
if (s->type == cabana::Signal::Type::Normal) overlapping.insert(s);
}
}
}
@@ -404,7 +405,9 @@ bool BinaryItemDelegate::hasSignal(const QModelIndex &index, int dx, int dy, con
if (!index.isValid()) return false;
auto model = (const BinaryViewModel*)(index.model());
int idx = (index.row() + dy) * model->columnCount() + index.column() + dx;
return (idx >=0 && idx < model->items.size()) ? model->items[idx].sigs.contains(sig) : false;
if (idx < 0 || idx >= (int)model->items.size()) return false;
auto &s = model->items[idx].sigs;
return std::find(s.begin(), s.end(), sig) != s.end();
}
void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
@@ -421,7 +424,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
auto color = bin_view->resize_sig ? bin_view->resize_sig->color : option.palette.color(QPalette::Active, QPalette::Highlight);
painter->fillRect(option.rect, color);
painter->setPen(option.palette.color(QPalette::BrightText));
} else if (!bin_view->selectionModel()->hasSelection() || !item->sigs.contains(bin_view->resize_sig)) { // not resizing
} else if (!bin_view->selectionModel()->hasSelection() || std::find(item->sigs.begin(), item->sigs.end(), bin_view->resize_sig) == item->sigs.end()) { // not resizing
if (item->sigs.size() > 0) {
for (auto &s : item->sigs) {
if (s == bin_view->hovered_sig) {
@@ -433,7 +436,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
} else if (item->valid && item->bg_color.alpha() > 0) {
painter->fillRect(option.rect, item->bg_color);
}
auto color_role = item->sigs.contains(bin_view->hovered_sig) ? QPalette::BrightText : QPalette::Text;
auto color_role = (std::find(item->sigs.begin(), item->sigs.end(), bin_view->hovered_sig) != item->sigs.end()) ? QPalette::BrightText : QPalette::Text;
painter->setPen(option.palette.color(bin_view->is_message_active ? QPalette::Normal : QPalette::Disabled, color_role));
}

View File

@@ -1,10 +1,9 @@
#pragma once
#include <set>
#include <tuple>
#include <vector>
#include <QList>
#include <QSet>
#include <QStyledItemDelegate>
#include <QTableView>
@@ -51,7 +50,7 @@ public:
bool is_msb = false;
bool is_lsb = false;
uint8_t val;
QList<const cabana::Signal *> sigs;
std::vector<const cabana::Signal *> sigs;
bool valid = false;
};
std::vector<Item> items;
@@ -68,7 +67,7 @@ public:
BinaryView(QWidget *parent = nullptr);
void setMessage(const MessageId &message_id);
void highlight(const cabana::Signal *sig);
QSet<const cabana::Signal*> getOverlappingSignals() const;
std::set<const cabana::Signal*> getOverlappingSignals() const;
void updateState() { model->updateState(); }
void paintEvent(QPaintEvent *event) override {
is_message_active = can->isMessageActive(model->msg_id);

View File

@@ -166,7 +166,7 @@ void ChartsWidget::removeTab(int index) {
void ChartsWidget::updateTabBar() {
for (int i = 0; i < tabbar->count(); ++i) {
const auto &charts_in_tab = tab_charts[tabbar->tabData(i).toInt()];
tabbar->setTabText(i, QString("Tab %1 (%2)").arg(i + 1).arg(charts_in_tab.count()));
tabbar->setTabText(i, QString("Tab %1 (%2)").arg(i + 1).arg((int)charts_in_tab.size()));
}
}
@@ -204,7 +204,7 @@ void ChartsWidget::showValueTip(double sec) {
}
void ChartsWidget::updateState() {
if (charts.isEmpty()) return;
if (charts.empty()) return;
const auto &time_range = can->timeRange();
const double cur_sec = can->currentSec();
@@ -248,7 +248,7 @@ void ChartsWidget::updateToolBar() {
redo_zoom_action->setVisible(is_zoomed);
reset_zoom_action->setVisible(is_zoomed);
reset_zoom_btn->setText(is_zoomed ? tr("%1-%2").arg(can->timeRange()->first, 0, 'f', 2).arg(can->timeRange()->second, 0, 'f', 2) : "");
remove_all_btn->setEnabled(!charts.isEmpty());
remove_all_btn->setEnabled(!charts.empty());
}
void ChartsWidget::settingChanged() {
@@ -282,9 +282,9 @@ ChartView *ChartsWidget::createChart(int pos) {
chart->setMinimumWidth(CHART_MIN_WIDTH);
chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
QObject::connect(chart, &ChartView::axisYLabelWidthChanged, align_timer, qOverload<>(&QTimer::start));
pos = std::clamp(pos, 0, charts.size());
charts.insert(pos, chart);
currentCharts().insert(pos, chart);
pos = std::clamp(pos, 0, (int)charts.size());
charts.insert(charts.begin() + pos, chart);
currentCharts().insert(currentCharts().begin() + pos, chart);
updateLayout(true);
updateToolBar();
return chart;
@@ -303,7 +303,7 @@ void ChartsWidget::showChart(const MessageId &id, const cabana::Signal *sig, boo
void ChartsWidget::splitChart(ChartView *src_chart) {
if (src_chart->sigs.size() > 1) {
int pos = charts.indexOf(src_chart) + 1;
int pos = std::find(charts.begin(), charts.end(), src_chart) - charts.begin() + 1;
for (auto it = src_chart->sigs.begin() + 1; it != src_chart->sigs.end(); /**/) {
auto c = createChart(pos);
src_chart->chart()->removeSeries(it->series);
@@ -434,7 +434,7 @@ void ChartsWidget::newChart() {
SignalSelector dlg(tr("New Chart"), this);
if (dlg.exec() == QDialog::Accepted) {
auto items = dlg.seletedItems();
if (!items.isEmpty()) {
if (!items.empty()) {
auto c = createChart();
for (auto it : items) {
c->addSignal(it->msg_id, it->sig);
@@ -444,10 +444,10 @@ void ChartsWidget::newChart() {
}
void ChartsWidget::removeChart(ChartView *chart) {
charts.removeOne(chart);
charts.erase(std::remove(charts.begin(), charts.end(), chart), charts.end());
chart->deleteLater();
for (auto &[_, list] : tab_charts) {
list.removeOne(chart);
list.erase(std::remove(list.begin(), list.end(), chart), list.end());
}
updateToolBar();
updateLayout(true);
@@ -461,7 +461,7 @@ void ChartsWidget::removeAll() {
}
tab_charts.clear();
if (!charts.isEmpty()) {
if (!charts.empty()) {
for (auto c : charts) {
delete c;
}
@@ -561,10 +561,11 @@ void ChartsContainer::dropEvent(QDropEvent *event) {
auto chart = qobject_cast<ChartView *>(event->source());
if (w != chart) {
for (auto &[_, list] : charts_widget->tab_charts) {
list.removeOne(chart);
list.erase(std::remove(list.begin(), list.end(), chart), list.end());
}
int to = w ? charts_widget->currentCharts().indexOf(w) + 1 : 0;
charts_widget->currentCharts().insert(to, chart);
auto &cur = charts_widget->currentCharts();
int to = w ? std::find(cur.begin(), cur.end(), w) - cur.begin() + 1 : 0;
cur.insert(cur.begin() + to, chart);
charts_widget->updateLayout(true);
charts_widget->updateTabBar();
event->acceptProposedAction();

View File

@@ -81,7 +81,7 @@ private:
bool eventFilter(QObject *obj, QEvent *event) override;
void newTab();
void removeTab(int index);
inline QList<ChartView *> &currentCharts() { return tab_charts[tabbar->tabData(tabbar->currentIndex()).toInt()]; }
inline std::vector<ChartView *> &currentCharts() { return tab_charts[tabbar->tabData(tabbar->currentIndex()).toInt()]; }
ChartView *findChart(const MessageId &id, const cabana::Signal *sig);
QLabel *title_label;
@@ -100,8 +100,8 @@ private:
QUndoStack *zoom_undo_stack;
ToolButton *remove_all_btn;
QList<ChartView *> charts;
std::unordered_map<int, QList<ChartView *>> tab_charts;
std::vector<ChartView *> charts;
std::unordered_map<int, std::vector<ChartView *>> tab_charts;
TabBar *tabbar;
ChartsContainer *charts_container;
QScrollArea *charts_scroll;

View File

@@ -102,8 +102,8 @@ void SignalSelector::addItemToList(QListWidget *parent, const MessageId id, cons
parent->setItemWidget(new_item, label);
}
QList<SignalSelector::ListItem *> SignalSelector::seletedItems() {
QList<SignalSelector::ListItem *> ret;
std::vector<SignalSelector::ListItem *> SignalSelector::seletedItems() {
std::vector<SignalSelector::ListItem *> ret;
for (int i = 0; i < selected_list->count(); ++i) ret.push_back((ListItem *)selected_list->item(i));
return ret;
}

View File

@@ -15,7 +15,7 @@ public:
};
SignalSelector(QString title, QWidget *parent);
QList<ListItem *> seletedItems();
std::vector<ListItem *> seletedItems();
inline void addSelected(const MessageId &id, const cabana::Signal *sig) { addItemToList(selected_list, id, sig, true); }
private:

View File

@@ -1,6 +1,14 @@
#include "tools/cabana/streams/socketcanstream.h"
#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 <QDebug>
#include <QDir>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QMessageBox>
@@ -9,7 +17,7 @@
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.c_str();
@@ -18,50 +26,73 @@ SocketCanStream::SocketCanStream(QObject *parent, SocketCanStreamConfig config_)
}
}
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", QString::fromStdString(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) {
qDebug() << "Failed to create CAN socket";
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) {
qDebug() << "Failed to get interface index for" << 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) {
qDebug() << "Failed to bind CAN socket";
::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 (!QThread::currentThread()->isInterruptionRequested()) {
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));
}
@@ -95,12 +126,19 @@ 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)
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");
if (type_file.open(QIODevice::ReadOnly)) {
int type = type_file.readAll().trimmed().toInt();
if (type == 280) {
device_edit->addItem(iface);
}
}
}
}
AbstractStream *OpenSocketCanWidget::open() {
try {
return new SocketCanStream(qApp, config);

View File

@@ -1,10 +1,5 @@
#pragma once
#include <memory>
#include <QtSerialBus/QCanBus>
#include <QtSerialBus/QCanBusDevice>
#include <QtSerialBus/QCanBusDeviceInfo>
#include <QComboBox>
#include "tools/cabana/streams/livestream.h"
@@ -17,7 +12,7 @@ class SocketCanStream : public LiveStream {
Q_OBJECT
public:
SocketCanStream(QObject *parent, SocketCanStreamConfig config_ = {});
~SocketCanStream() { stop(); }
~SocketCanStream();
static bool available();
inline std::string routeName() const override {
@@ -29,7 +24,7 @@ protected:
bool connect();
SocketCanStreamConfig config = {};
std::unique_ptr<QCanBusDevice> device;
int sock_fd = -1;
};
class OpenSocketCanWidget : public AbstractOpenStreamWidget {

View File

@@ -33,7 +33,7 @@ void FindSignalModel::search(std::function<bool(double)> cmp) {
beginResetModel();
std::mutex lock;
const auto prev_sigs = !histories.isEmpty() ? histories.back() : initial_signals;
const auto prev_sigs = !histories.empty() ? histories.back() : initial_signals;
filtered_signals.clear();
filtered_signals.reserve(prev_sigs.size());
@@ -71,11 +71,11 @@ void FindSignalModel::search(std::function<bool(double)> cmp) {
}
void FindSignalModel::undo() {
if (!histories.isEmpty()) {
if (!histories.empty()) {
beginResetModel();
histories.pop_back();
filtered_signals.clear();
if (!histories.isEmpty()) filtered_signals = histories.back();
if (!histories.empty()) filtered_signals = histories.back();
endResetModel();
}
}
@@ -186,7 +186,7 @@ FindSignalDlg::FindSignalDlg(QWidget *parent) : QDialog(parent, Qt::WindowFlags(
}
void FindSignalDlg::search() {
if (model->histories.isEmpty()) {
if (model->histories.empty()) {
setInitialSignals();
}
auto v1 = value1->text().toDouble();
@@ -260,12 +260,12 @@ void FindSignalDlg::setInitialSignals() {
}
void FindSignalDlg::modelReset() {
properties_group->setEnabled(model->histories.isEmpty());
message_group->setEnabled(model->histories.isEmpty());
search_btn->setText(model->histories.isEmpty() ? tr("Find") : tr("Find Next"));
reset_btn->setEnabled(!model->histories.isEmpty());
properties_group->setEnabled(model->histories.empty());
message_group->setEnabled(model->histories.empty());
search_btn->setText(model->histories.empty() ? tr("Find") : tr("Find Next"));
reset_btn->setEnabled(!model->histories.empty());
undo_btn->setEnabled(model->histories.size() > 1);
search_btn->setEnabled(model->rowCount() > 0 || model->histories.isEmpty());
search_btn->setEnabled(model->rowCount() > 0 || model->histories.empty());
stats_label->setVisible(true);
stats_label->setText(tr("%1 matches. right click on an item to create signal. double click to open message").arg(model->filtered_signals.size()));
}

View File

@@ -2,6 +2,8 @@
#include <algorithm>
#include <limits>
#include <string>
#include <vector>
#include <QAbstractTableModel>
#include <QCheckBox>
@@ -26,14 +28,14 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 3; }
int rowCount(const QModelIndex &parent = QModelIndex()) const override { return std::min(filtered_signals.size(), 300); }
int rowCount(const QModelIndex &parent = QModelIndex()) const override { return std::min((int)filtered_signals.size(), 300); }
void search(std::function<bool(double)> cmp);
void reset();
void undo();
QList<SearchSignal> filtered_signals;
QList<SearchSignal> initial_signals;
QList<QList<SearchSignal>> histories;
std::vector<SearchSignal> filtered_signals;
std::vector<SearchSignal> initial_signals;
std::vector<std::vector<SearchSignal>> histories;
uint64_t last_time = std::numeric_limits<uint64_t>::max();
};

View File

@@ -1,6 +1,7 @@
#include "tools/cabana/tools/findsimilarbits.h"
#include <algorithm>
#include <unordered_map>
#include <QGridLayout>
#include <QHeaderView>
@@ -114,10 +115,10 @@ void FindSimilarBitsDlg::find() {
search_btn->setEnabled(true);
}
QList<FindSimilarBitsDlg::mismatched_struct> FindSimilarBitsDlg::calcBits(uint8_t bus, uint32_t selected_address, int byte_idx,
int bit_idx, uint8_t find_bus, bool equal, int min_msgs_cnt) {
QHash<uint32_t, QVector<uint32_t>> mismatches;
QHash<uint32_t, uint32_t> msg_count;
std::vector<FindSimilarBitsDlg::mismatched_struct> FindSimilarBitsDlg::calcBits(uint8_t bus, uint32_t selected_address, int byte_idx,
int bit_idx, uint8_t find_bus, bool equal, int min_msgs_cnt) {
std::unordered_map<uint32_t, std::vector<uint32_t>> mismatches;
std::unordered_map<uint32_t, uint32_t> msg_count;
const auto &events = can->allEvents();
int bit_to_find = -1;
for (const CanEvent *e : events) {
@@ -143,14 +144,14 @@ QList<FindSimilarBitsDlg::mismatched_struct> FindSimilarBitsDlg::calcBits(uint8_
}
}
QList<mismatched_struct> result;
std::vector<mismatched_struct> result;
result.reserve(mismatches.size());
for (auto it = mismatches.begin(); it != mismatches.end(); ++it) {
if (auto cnt = msg_count[it.key()]; cnt > min_msgs_cnt) {
auto &mismatched = it.value();
for (int i = 0; i < mismatched.size(); ++i) {
if (auto cnt = msg_count[it->first]; cnt > (uint32_t)min_msgs_cnt) {
auto &mismatched = it->second;
for (int i = 0; i < (int)mismatched.size(); ++i) {
if (float perc = (mismatched[i] / (double)cnt) * 100; perc < 50) {
result.push_back({it.key(), (uint32_t)i / 8, (uint32_t)i % 8, mismatched[i], cnt, perc});
result.push_back({it->first, (uint32_t)i / 8, (uint32_t)i % 8, mismatched[i], cnt, perc});
}
}
}

View File

@@ -1,5 +1,7 @@
#pragma once
#include <vector>
#include <QComboBox>
#include <QDialog>
#include <QLineEdit>
@@ -22,7 +24,7 @@ private:
uint32_t address, byte_idx, bit_idx, mismatches, total;
float perc;
};
QList<mismatched_struct> calcBits(uint8_t bus, uint32_t selected_address, int byte_idx, int bit_idx, uint8_t find_bus,
std::vector<mismatched_struct> calcBits(uint8_t bus, uint32_t selected_address, int byte_idx, int bit_idx, uint8_t find_bus,
bool equal, int min_msgs_cnt);
void find();