Add openpilot tools
This commit is contained in:
269
tools/cabana/tools/findsignal.cc
Normal file
269
tools/cabana/tools/findsignal.cc
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "tools/cabana/tools/findsignal.h"
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QtConcurrent>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
// FindSignalModel
|
||||
|
||||
QVariant FindSignalModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
static QString titles[] = {"Id", "Start Bit, size", "(time, value)"};
|
||||
if (role != Qt::DisplayRole) return {};
|
||||
return orientation == Qt::Horizontal ? titles[section] : QString::number(section + 1);
|
||||
}
|
||||
|
||||
QVariant FindSignalModel::data(const QModelIndex &index, int role) const {
|
||||
if (role == Qt::DisplayRole) {
|
||||
const auto &s = filtered_signals[index.row()];
|
||||
switch (index.column()) {
|
||||
case 0: return s.id.toString();
|
||||
case 1: return QString("%1, %2").arg(s.sig.start_bit).arg(s.sig.size);
|
||||
case 2: return s.values.join(" ");
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void FindSignalModel::search(std::function<bool(double)> cmp) {
|
||||
beginResetModel();
|
||||
|
||||
std::mutex lock;
|
||||
const auto prev_sigs = !histories.isEmpty() ? histories.back() : initial_signals;
|
||||
filtered_signals.clear();
|
||||
filtered_signals.reserve(prev_sigs.size());
|
||||
QtConcurrent::blockingMap(prev_sigs, [&](auto &s) {
|
||||
const auto &events = can->events(s.id);
|
||||
auto first = std::upper_bound(events.cbegin(), events.cend(), s.mono_time, CompareCanEvent());
|
||||
auto last = events.cend();
|
||||
if (last_time < std::numeric_limits<uint64_t>::max()) {
|
||||
last = std::upper_bound(events.cbegin(), events.cend(), last_time, CompareCanEvent());
|
||||
}
|
||||
|
||||
auto it = std::find_if(first, last, [&](const CanEvent *e) { return cmp(get_raw_value(e->dat, e->size, s.sig)); });
|
||||
if (it != last) {
|
||||
auto values = s.values;
|
||||
values += QString("(%1, %2)").arg((*it)->mono_time / 1e9 - can->routeStartTime(), 0, 'f', 2).arg(get_raw_value((*it)->dat, (*it)->size, s.sig));
|
||||
std::lock_guard lk(lock);
|
||||
filtered_signals.push_back({.id = s.id, .mono_time = (*it)->mono_time, .sig = s.sig, .values = values});
|
||||
}
|
||||
});
|
||||
histories.push_back(filtered_signals);
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void FindSignalModel::undo() {
|
||||
if (!histories.isEmpty()) {
|
||||
beginResetModel();
|
||||
histories.pop_back();
|
||||
filtered_signals.clear();
|
||||
if (!histories.isEmpty()) filtered_signals = histories.back();
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
|
||||
void FindSignalModel::reset() {
|
||||
beginResetModel();
|
||||
histories.clear();
|
||||
filtered_signals.clear();
|
||||
initial_signals.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
// FindSignalDlg
|
||||
FindSignalDlg::FindSignalDlg(QWidget *parent) : QDialog(parent, Qt::WindowFlags() | Qt::Window) {
|
||||
setWindowTitle(tr("Find Signal"));
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
|
||||
// Messages group
|
||||
message_group = new QGroupBox(tr("Messages"), this);
|
||||
QFormLayout *message_layout = new QFormLayout(message_group);
|
||||
message_layout->addRow(tr("Bus"), bus_edit = new QLineEdit());
|
||||
bus_edit->setPlaceholderText(tr("comma-seperated values. Leave blank for all"));
|
||||
message_layout->addRow(tr("Address"), address_edit = new QLineEdit());
|
||||
address_edit->setPlaceholderText(tr("comma-seperated hex values. Leave blank for all"));
|
||||
QHBoxLayout *hlayout = new QHBoxLayout();
|
||||
hlayout->addWidget(first_time_edit = new QLineEdit("0"));
|
||||
hlayout->addWidget(new QLabel("-"));
|
||||
hlayout->addWidget(last_time_edit = new QLineEdit("MAX"));
|
||||
hlayout->addWidget(new QLabel("seconds"));
|
||||
hlayout->addStretch(0);
|
||||
message_layout->addRow(tr("Time"), hlayout);
|
||||
|
||||
// Signal group
|
||||
properties_group = new QGroupBox(tr("Signal"));
|
||||
QFormLayout *property_layout = new QFormLayout(properties_group);
|
||||
property_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
|
||||
|
||||
hlayout = new QHBoxLayout();
|
||||
hlayout->addWidget(min_size = new QSpinBox);
|
||||
hlayout->addWidget(new QLabel("-"));
|
||||
hlayout->addWidget(max_size = new QSpinBox);
|
||||
hlayout->addWidget(litter_endian = new QCheckBox(tr("Little endian")));
|
||||
hlayout->addWidget(is_signed = new QCheckBox(tr("Signed")));
|
||||
hlayout->addStretch(0);
|
||||
min_size->setRange(1, 64);
|
||||
max_size->setRange(1, 64);
|
||||
min_size->setValue(8);
|
||||
max_size->setValue(8);
|
||||
litter_endian->setChecked(true);
|
||||
property_layout->addRow(tr("Size"), hlayout);
|
||||
property_layout->addRow(tr("Factor"), factor_edit = new QLineEdit("1.0"));
|
||||
property_layout->addRow(tr("Offset"), offset_edit = new QLineEdit("0.0"));
|
||||
|
||||
// find group
|
||||
QGroupBox *find_group = new QGroupBox(tr("Find signal"), this);
|
||||
QVBoxLayout *vlayout = new QVBoxLayout(find_group);
|
||||
hlayout = new QHBoxLayout();
|
||||
hlayout->addWidget(new QLabel(tr("Value")));
|
||||
hlayout->addWidget(compare_cb = new QComboBox(this));
|
||||
hlayout->addWidget(value1 = new QLineEdit);
|
||||
hlayout->addWidget(to_label = new QLabel("-"));
|
||||
hlayout->addWidget(value2 = new QLineEdit);
|
||||
hlayout->addWidget(undo_btn = new QPushButton(tr("Undo prev find"), this));
|
||||
hlayout->addWidget(search_btn = new QPushButton(tr("Find")));
|
||||
hlayout->addWidget(reset_btn = new QPushButton(tr("Reset"), this));
|
||||
vlayout->addLayout(hlayout);
|
||||
|
||||
compare_cb->addItems({"=", ">", ">=", "!=", "<", "<=", "between"});
|
||||
value1->setFocus(Qt::OtherFocusReason);
|
||||
value2->setVisible(false);
|
||||
to_label->setVisible(false);
|
||||
undo_btn->setEnabled(false);
|
||||
reset_btn->setEnabled(false);
|
||||
|
||||
auto double_validator = new DoubleValidator(this);
|
||||
for (auto edit : {value1, value2, factor_edit, offset_edit, first_time_edit, last_time_edit}) {
|
||||
edit->setValidator(double_validator);
|
||||
}
|
||||
|
||||
vlayout->addWidget(view = new QTableView(this));
|
||||
view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
view->horizontalHeader()->setStretchLastSection(true);
|
||||
view->horizontalHeader()->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
view->setModel(model = new FindSignalModel(this));
|
||||
|
||||
hlayout = new QHBoxLayout();
|
||||
hlayout->addWidget(message_group);
|
||||
hlayout->addWidget(properties_group);
|
||||
main_layout->addLayout(hlayout);
|
||||
main_layout->addWidget(find_group);
|
||||
main_layout->addWidget(stats_label = new QLabel());
|
||||
|
||||
setMinimumSize({700, 650});
|
||||
QObject::connect(search_btn, &QPushButton::clicked, this, &FindSignalDlg::search);
|
||||
QObject::connect(undo_btn, &QPushButton::clicked, model, &FindSignalModel::undo);
|
||||
QObject::connect(model, &QAbstractItemModel::modelReset, this, &FindSignalDlg::modelReset);
|
||||
QObject::connect(reset_btn, &QPushButton::clicked, model, &FindSignalModel::reset);
|
||||
QObject::connect(view, &QTableView::customContextMenuRequested, this, &FindSignalDlg::customMenuRequested);
|
||||
QObject::connect(view, &QTableView::doubleClicked, [this](const QModelIndex &index) {
|
||||
if (index.isValid()) emit openMessage(model->filtered_signals[index.row()].id);
|
||||
});
|
||||
QObject::connect(compare_cb, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
|
||||
to_label->setVisible(index == compare_cb->count() - 1);
|
||||
value2->setVisible(index == compare_cb->count() - 1);
|
||||
});
|
||||
}
|
||||
|
||||
void FindSignalDlg::search() {
|
||||
if (model->histories.isEmpty()) {
|
||||
setInitialSignals();
|
||||
}
|
||||
auto v1 = value1->text().toDouble();
|
||||
auto v2 = value2->text().toDouble();
|
||||
std::function<bool(double)> cmp = nullptr;
|
||||
switch (compare_cb->currentIndex()) {
|
||||
case 0: cmp = [v1](double v) { return v == v1;}; break;
|
||||
case 1: cmp = [v1](double v) { return v > v1;}; break;
|
||||
case 2: cmp = [v1](double v) { return v >= v1;}; break;
|
||||
case 3: cmp = [v1](double v) { return v != v1;}; break;
|
||||
case 4: cmp = [v1](double v) { return v < v1;}; break;
|
||||
case 5: cmp = [v1](double v) { return v <= v1;}; break;
|
||||
case 6: cmp = [v1, v2](double v) { return v >= v1 && v <= v2;}; break;
|
||||
}
|
||||
properties_group->setEnabled(false);
|
||||
message_group->setEnabled(false);
|
||||
search_btn->setEnabled(false);
|
||||
stats_label->setVisible(false);
|
||||
search_btn->setText("Finding ....");
|
||||
QTimer::singleShot(0, this, [=]() { model->search(cmp); });
|
||||
}
|
||||
|
||||
void FindSignalDlg::setInitialSignals() {
|
||||
QSet<ushort> buses;
|
||||
for (auto bus : bus_edit->text().trimmed().split(",")) {
|
||||
bus = bus.trimmed();
|
||||
if (!bus.isEmpty()) buses.insert(bus.toUShort());
|
||||
}
|
||||
|
||||
QSet<uint32_t> addresses;
|
||||
for (auto addr : address_edit->text().trimmed().split(",")) {
|
||||
addr = addr.trimmed();
|
||||
if (!addr.isEmpty()) addresses.insert(addr.toULong(nullptr, 16));
|
||||
}
|
||||
|
||||
cabana::Signal sig{};
|
||||
sig.is_little_endian = litter_endian->isChecked();
|
||||
sig.is_signed = is_signed->isChecked();
|
||||
sig.factor = factor_edit->text().toDouble();
|
||||
sig.offset = offset_edit->text().toDouble();
|
||||
|
||||
double first_time_val = first_time_edit->text().toDouble();
|
||||
double last_time_val = last_time_edit->text().toDouble();
|
||||
auto [first_sec, last_sec] = std::minmax(first_time_val, last_time_val);
|
||||
uint64_t first_time = (can->routeStartTime() + first_sec) * 1e9;
|
||||
model->last_time = std::numeric_limits<uint64_t>::max();
|
||||
if (last_sec > 0) {
|
||||
model->last_time = (can->routeStartTime() + last_sec) * 1e9;
|
||||
}
|
||||
model->initial_signals.clear();
|
||||
|
||||
for (const auto &[id, m] : can->lastMessages()) {
|
||||
if (buses.isEmpty() || buses.contains(id.source) && (addresses.isEmpty() || addresses.contains(id.address))) {
|
||||
const auto &events = can->events(id);
|
||||
auto e = std::lower_bound(events.cbegin(), events.cend(), first_time, CompareCanEvent());
|
||||
if (e != events.cend()) {
|
||||
const int total_size = m.dat.size() * 8;
|
||||
for (int size = min_size->value(); size <= max_size->value(); ++size) {
|
||||
for (int start = 0; start <= total_size - size; ++start) {
|
||||
FindSignalModel::SearchSignal s{.id = id, .mono_time = first_time, .sig = sig};
|
||||
s.sig.start_bit = start;
|
||||
s.sig.size = size;
|
||||
updateMsbLsb(s.sig);
|
||||
s.value = get_raw_value((*e)->dat, (*e)->size, s.sig);
|
||||
model->initial_signals.push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
undo_btn->setEnabled(model->histories.size() > 1);
|
||||
search_btn->setEnabled(model->rowCount() > 0 || model->histories.isEmpty());
|
||||
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()));
|
||||
}
|
||||
|
||||
void FindSignalDlg::customMenuRequested(const QPoint &pos) {
|
||||
if (auto index = view->indexAt(pos); index.isValid()) {
|
||||
QMenu menu(this);
|
||||
menu.addAction(tr("Create Signal"));
|
||||
if (menu.exec(view->mapToGlobal(pos))) {
|
||||
auto &s = model->filtered_signals[index.row()];
|
||||
UndoStack::push(new AddSigCommand(s.id, s.sig));
|
||||
emit openMessage(s.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
tools/cabana/tools/findsignal.h
Normal file
64
tools/cabana/tools/findsignal.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QTableView>
|
||||
|
||||
#include "tools/cabana/commands.h"
|
||||
#include "tools/cabana/settings.h"
|
||||
|
||||
class FindSignalModel : public QAbstractTableModel {
|
||||
public:
|
||||
struct SearchSignal {
|
||||
MessageId id = {};
|
||||
uint64_t mono_time = 0;
|
||||
cabana::Signal sig = {};
|
||||
double value = 0.;
|
||||
QStringList values;
|
||||
};
|
||||
|
||||
FindSignalModel(QObject *parent) : QAbstractTableModel(parent) {}
|
||||
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); }
|
||||
void search(std::function<bool(double)> cmp);
|
||||
void reset();
|
||||
void undo();
|
||||
|
||||
QList<SearchSignal> filtered_signals;
|
||||
QList<SearchSignal> initial_signals;
|
||||
QList<QList<SearchSignal>> histories;
|
||||
uint64_t last_time = std::numeric_limits<uint64_t>::max();
|
||||
};
|
||||
|
||||
class FindSignalDlg : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FindSignalDlg(QWidget *parent);
|
||||
|
||||
signals:
|
||||
void openMessage(const MessageId &id);
|
||||
|
||||
private:
|
||||
void search();
|
||||
void modelReset();
|
||||
void setInitialSignals();
|
||||
void customMenuRequested(const QPoint &pos);
|
||||
|
||||
QLineEdit *value1, *value2, *factor_edit, *offset_edit;
|
||||
QLineEdit *bus_edit, *address_edit, *first_time_edit, *last_time_edit;
|
||||
QComboBox *compare_cb;
|
||||
QSpinBox *min_size, *max_size;
|
||||
QCheckBox *litter_endian, *is_signed;
|
||||
QPushButton *search_btn, *reset_btn, *undo_btn;
|
||||
QGroupBox *properties_group, *message_group;
|
||||
QTableView *view;
|
||||
QLabel *to_label, *stats_label;
|
||||
FindSignalModel *model;
|
||||
};
|
||||
160
tools/cabana/tools/findsimilarbits.cc
Normal file
160
tools/cabana/tools/findsimilarbits.cc
Normal file
@@ -0,0 +1,160 @@
|
||||
#include "tools/cabana/tools/findsimilarbits.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QHBoxLayout>
|
||||
#include <QIntValidator>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
|
||||
#include "tools/cabana/dbc/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
|
||||
FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::WindowFlags() | Qt::Window) {
|
||||
setWindowTitle(tr("Find similar bits"));
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
|
||||
QHBoxLayout *src_layout = new QHBoxLayout();
|
||||
src_bus_combo = new QComboBox(this);
|
||||
find_bus_combo = new QComboBox(this);
|
||||
for (auto cb : {src_bus_combo, find_bus_combo}) {
|
||||
for (uint8_t bus : can->sources) {
|
||||
cb->addItem(QString::number(bus), bus);
|
||||
}
|
||||
}
|
||||
|
||||
msg_cb = new QComboBox(this);
|
||||
// TODO: update when src_bus_combo changes
|
||||
for (auto &[address, msg] : dbc()->getMessages(-1)) {
|
||||
msg_cb->addItem(msg.name, address);
|
||||
}
|
||||
msg_cb->model()->sort(0);
|
||||
msg_cb->setCurrentIndex(0);
|
||||
|
||||
byte_idx_sb = new QSpinBox(this);
|
||||
byte_idx_sb->setFixedWidth(50);
|
||||
byte_idx_sb->setRange(0, 63);
|
||||
|
||||
bit_idx_sb = new QSpinBox(this);
|
||||
bit_idx_sb->setFixedWidth(50);
|
||||
bit_idx_sb->setRange(0, 7);
|
||||
|
||||
src_layout->addWidget(new QLabel(tr("Bus")));
|
||||
src_layout->addWidget(src_bus_combo);
|
||||
src_layout->addWidget(msg_cb);
|
||||
src_layout->addWidget(new QLabel(tr("Byte Index")));
|
||||
src_layout->addWidget(byte_idx_sb);
|
||||
src_layout->addWidget(new QLabel(tr("Bit Index")));
|
||||
src_layout->addWidget(bit_idx_sb);
|
||||
src_layout->addStretch(0);
|
||||
|
||||
QHBoxLayout *find_layout = new QHBoxLayout();
|
||||
find_layout->addWidget(new QLabel(tr("Bus")));
|
||||
find_layout->addWidget(find_bus_combo);
|
||||
find_layout->addWidget(new QLabel(tr("Equal")));
|
||||
equal_combo = new QComboBox(this);
|
||||
equal_combo->addItems({"Yes", "No"});
|
||||
find_layout->addWidget(equal_combo);
|
||||
min_msgs = new QLineEdit(this);
|
||||
min_msgs->setValidator(new QIntValidator(this));
|
||||
min_msgs->setText("100");
|
||||
find_layout->addWidget(new QLabel(tr("Min msg count")));
|
||||
find_layout->addWidget(min_msgs);
|
||||
search_btn = new QPushButton(tr("&Find"), this);
|
||||
find_layout->addWidget(search_btn);
|
||||
find_layout->addStretch(0);
|
||||
|
||||
QGridLayout *grid_layout = new QGridLayout();
|
||||
grid_layout->addWidget(new QLabel("Find From:"), 0, 0);
|
||||
grid_layout->addLayout(src_layout, 0, 1);
|
||||
grid_layout->addWidget(new QLabel("Find In:"), 1, 0);
|
||||
grid_layout->addLayout(find_layout, 1, 1);
|
||||
main_layout->addLayout(grid_layout);
|
||||
|
||||
table = new QTableWidget(this);
|
||||
table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
table->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
table->horizontalHeader()->setStretchLastSection(true);
|
||||
main_layout->addWidget(table);
|
||||
|
||||
setMinimumSize({700, 500});
|
||||
QObject::connect(search_btn, &QPushButton::clicked, this, &FindSimilarBitsDlg::find);
|
||||
QObject::connect(table, &QTableWidget::doubleClicked, [this](const QModelIndex &index) {
|
||||
if (index.isValid()) {
|
||||
MessageId msg_id = {.source = (uint8_t)find_bus_combo->currentData().toUInt(), .address = table->item(index.row(), 0)->text().toUInt(0, 16)};
|
||||
emit openMessage(msg_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FindSimilarBitsDlg::find() {
|
||||
search_btn->setEnabled(false);
|
||||
table->clear();
|
||||
uint32_t selected_address = msg_cb->currentData().toUInt();
|
||||
auto msg_mismatched = calcBits(src_bus_combo->currentText().toUInt(), selected_address, byte_idx_sb->value(), bit_idx_sb->value(),
|
||||
find_bus_combo->currentText().toUInt(), equal_combo->currentIndex() == 0, min_msgs->text().toInt());
|
||||
table->setRowCount(msg_mismatched.size());
|
||||
table->setColumnCount(6);
|
||||
table->setHorizontalHeaderLabels({"address", "byte idx", "bit idx", "mismatches", "total msgs", "% mismatched"});
|
||||
for (int i = 0; i < msg_mismatched.size(); ++i) {
|
||||
auto &m = msg_mismatched[i];
|
||||
table->setItem(i, 0, new QTableWidgetItem(QString("%1").arg(m.address, 1, 16)));
|
||||
table->setItem(i, 1, new QTableWidgetItem(QString::number(m.byte_idx)));
|
||||
table->setItem(i, 2, new QTableWidgetItem(QString::number(m.bit_idx)));
|
||||
table->setItem(i, 3, new QTableWidgetItem(QString::number(m.mismatches)));
|
||||
table->setItem(i, 4, new QTableWidgetItem(QString::number(m.total)));
|
||||
table->setItem(i, 5, new QTableWidgetItem(QString::number(m.perc, 'f', 2)));
|
||||
}
|
||||
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;
|
||||
const auto &events = can->allEvents();
|
||||
int bit_to_find = -1;
|
||||
for (const CanEvent *e : events) {
|
||||
if (e->src == bus) {
|
||||
if (e->address == selected_address && e->size > byte_idx) {
|
||||
bit_to_find = ((e->dat[byte_idx] >> (7 - bit_idx)) & 1) != 0;
|
||||
}
|
||||
}
|
||||
if (e->src == find_bus) {
|
||||
++msg_count[e->address];
|
||||
if (bit_to_find == -1) continue;
|
||||
|
||||
auto &mismatched = mismatches[e->address];
|
||||
if (mismatched.size() < e->size * 8) {
|
||||
mismatched.resize(e->size * 8);
|
||||
}
|
||||
for (int i = 0; i < e->size; ++i) {
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
int bit = ((e->dat[i] >> (7 - j)) & 1) != 0;
|
||||
mismatched[i * 8 + j] += equal ? (bit != bit_to_find) : (bit == bit_to_find);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<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 (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});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(result.begin(), result.end(), [](auto &l, auto &r) { return l.perc < r.perc; });
|
||||
return result;
|
||||
}
|
||||
34
tools/cabana/tools/findsimilarbits.h
Normal file
34
tools/cabana/tools/findsimilarbits.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QSpinBox>
|
||||
#include <QTableWidget>
|
||||
|
||||
#include "tools/cabana/dbc/dbcmanager.h"
|
||||
|
||||
class FindSimilarBitsDlg : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FindSimilarBitsDlg(QWidget *parent);
|
||||
|
||||
signals:
|
||||
void openMessage(const MessageId &msg_id);
|
||||
|
||||
private:
|
||||
struct mismatched_struct {
|
||||
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,
|
||||
bool equal, int min_msgs_cnt);
|
||||
void find();
|
||||
|
||||
QTableWidget *table;
|
||||
QComboBox *src_bus_combo, *find_bus_combo, *msg_cb, *equal_combo;
|
||||
QSpinBox *byte_idx_sb, *bit_idx_sb;
|
||||
QPushButton *search_btn;
|
||||
QLineEdit *min_msgs;
|
||||
};
|
||||
Reference in New Issue
Block a user