mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-07-07 05:42:06 +08:00
Cabana: add save DBC dialog (#26282)
* export DBC to text edit * added saveAs & Copy To Clipboard * cleanup * cleanup include * add test case * rename variable * fix precision
This commit is contained in:
@@ -232,6 +232,7 @@ jobs:
|
||||
./selfdrive/loggerd/tests/test_logger &&\
|
||||
./system/proclogd/tests/test_proclog && \
|
||||
./tools/replay/tests/test_replay && \
|
||||
./tools/cabana/tests/test_cabana && \
|
||||
./system/camerad/test/ae_gray_test && \
|
||||
coverage xml"
|
||||
- name: "Upload coverage to Codecov"
|
||||
|
||||
@@ -4,4 +4,4 @@ moc_*
|
||||
_cabana
|
||||
settings
|
||||
car_fingerprint_to_dbc.json
|
||||
|
||||
tests/_test_cabana
|
||||
|
||||
@@ -18,5 +18,9 @@ cabana_env = qt_env.Clone()
|
||||
prev_moc_path = cabana_env['QT_MOCHPREFIX']
|
||||
cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_'
|
||||
cabana_env.Execute('./generate_dbc_json.py --out car_fingerprint_to_dbc.json')
|
||||
cabana_env.Program('_cabana', ['cabana.cc', 'mainwin.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc',
|
||||
cabana_lib = cabana_env.Library("cabana_lib", ['mainwin.cc', 'binaryview.cc', 'chartswidget.cc', 'historylog.cc', 'videowidget.cc', 'signaledit.cc', 'dbcmanager.cc',
|
||||
'canmessages.cc', 'messageswidget.cc', 'settings.cc', 'detailwidget.cc'], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
|
||||
cabana_env.Program('_cabana', ['cabana.cc', cabana_lib], LIBS=cabana_libs, FRAMEWORKS=base_frameworks)
|
||||
|
||||
if GetOption('test'):
|
||||
cabana_env.Program('tests/_test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs])
|
||||
|
||||
@@ -28,8 +28,25 @@ void DBCManager::open(const QString &name, const QString &content) {
|
||||
emit DBCFileChanged();
|
||||
}
|
||||
|
||||
void save(const QString &dbc_file_name) {
|
||||
// TODO: save DBC to file
|
||||
QString DBCManager::generateDBC() {
|
||||
if (!dbc) return {};
|
||||
|
||||
QString dbc_string;
|
||||
for (auto &m : dbc->msgs) {
|
||||
dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(m.address).arg(m.name.c_str()).arg(m.size);
|
||||
for (auto &sig : m.sigs) {
|
||||
dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [0|0] \"\" XXX\n")
|
||||
.arg(sig.name.c_str())
|
||||
.arg(sig.start_bit)
|
||||
.arg(sig.size)
|
||||
.arg(sig.is_little_endian ? '1' : '0')
|
||||
.arg(sig.is_signed ? '-' : '+')
|
||||
.arg(sig.factor, 0, 'g', 20)
|
||||
.arg(sig.offset);
|
||||
}
|
||||
dbc_string += "\n";
|
||||
}
|
||||
return dbc_string;
|
||||
}
|
||||
|
||||
void DBCManager::updateMsg(const QString &id, const QString &name, uint32_t size) {
|
||||
|
||||
@@ -13,8 +13,7 @@ public:
|
||||
|
||||
void open(const QString &dbc_file_name);
|
||||
void open(const QString &name, const QString &content);
|
||||
void save(const QString &dbc_file_name);
|
||||
|
||||
QString generateDBC();
|
||||
void addSignal(const QString &id, const Signal &sig);
|
||||
void updateSignal(const QString &id, const QString &sig_name, const Signal &sig);
|
||||
void removeSignal(const QString &id, const QString &sig_name);
|
||||
@@ -24,6 +23,7 @@ public:
|
||||
inline QString name() const { return dbc_name; }
|
||||
|
||||
void updateMsg(const QString &id, const QString &name, uint32_t size);
|
||||
inline const DBC *getDBC() const { return dbc; }
|
||||
inline const Msg *msg(const QString &id) const { return msg(addressFromId(id)); }
|
||||
inline const Msg *msg(uint32_t address) const {
|
||||
auto it = msg_map.find(address);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <QCompleter>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QFontDatabase>
|
||||
#include <QHeaderView>
|
||||
#include <QLineEdit>
|
||||
@@ -69,9 +71,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
|
||||
QObject::connect(can, &CANMessages::updated, [this]() { model->updateState(); });
|
||||
QObject::connect(dbc_combo, SIGNAL(activated(const QString &)), SLOT(loadDBCFromName(const QString &)));
|
||||
QObject::connect(load_from_paste, &QPushButton::clicked, this, &MessagesWidget::loadDBCFromPaste);
|
||||
QObject::connect(save_btn, &QPushButton::clicked, [=]() {
|
||||
// TODO: save DBC to file
|
||||
});
|
||||
QObject::connect(save_btn, &QPushButton::clicked, this, &MessagesWidget::saveDBC);
|
||||
QObject::connect(table_widget->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex &previous) {
|
||||
if (current.isValid()) {
|
||||
emit msgSelectionChanged(current.data(Qt::UserRole).toString());
|
||||
@@ -79,7 +79,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
|
||||
});
|
||||
|
||||
QFile json_file("./car_fingerprint_to_dbc.json");
|
||||
if(json_file.open(QIODevice::ReadOnly)) {
|
||||
if (json_file.open(QIODevice::ReadOnly)) {
|
||||
fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll());
|
||||
}
|
||||
}
|
||||
@@ -103,14 +103,20 @@ void MessagesWidget::loadDBCFromPaste() {
|
||||
|
||||
void MessagesWidget::loadDBCFromFingerprint() {
|
||||
auto fingerprint = can->carFingerprint();
|
||||
if (!fingerprint.isEmpty() && dbc()->name().isEmpty()) {
|
||||
if (!fingerprint.isEmpty() && dbc()->name().isEmpty()) {
|
||||
auto dbc_name = fingerprint_to_dbc[fingerprint];
|
||||
if (dbc_name != QJsonValue::Undefined) {
|
||||
if (dbc_name != QJsonValue::Undefined) {
|
||||
loadDBCFromName(dbc_name.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesWidget::saveDBC() {
|
||||
SaveDBCDialog dlg(this);
|
||||
dlg.dbc_edit->setText(dbc()->generateDBC());
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
// MessageListModel
|
||||
|
||||
QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
@@ -224,6 +230,8 @@ void MessageListModel::sort(int column, Qt::SortOrder order) {
|
||||
}
|
||||
}
|
||||
|
||||
// LoadDBCDialog
|
||||
|
||||
LoadDBCDialog::LoadDBCDialog(QWidget *parent) : QDialog(parent) {
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
dbc_edit = new QTextEdit(this);
|
||||
@@ -233,7 +241,48 @@ LoadDBCDialog::LoadDBCDialog(QWidget *parent) : QDialog(parent) {
|
||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
main_layout->addWidget(buttonBox);
|
||||
|
||||
setFixedWidth(640);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
setMinimumSize({640, 480});
|
||||
QObject::connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
QObject::connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
// SaveDBCDialog
|
||||
|
||||
SaveDBCDialog::SaveDBCDialog(QWidget *parent) : QDialog(parent) {
|
||||
setWindowTitle(tr("Save DBC"));
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
dbc_edit = new QTextEdit(this);
|
||||
dbc_edit->setAcceptRichText(false);
|
||||
main_layout->addWidget(dbc_edit);
|
||||
|
||||
QPushButton *copy_to_clipboard = new QPushButton(tr("Copy To Clipboard"), this);
|
||||
QPushButton *save_as = new QPushButton(tr("Save As"), this);
|
||||
|
||||
QHBoxLayout *btn_layout = new QHBoxLayout();
|
||||
btn_layout->addStretch();
|
||||
btn_layout->addWidget(copy_to_clipboard);
|
||||
btn_layout->addWidget(save_as);
|
||||
main_layout->addLayout(btn_layout);
|
||||
setMinimumSize({640, 480});
|
||||
|
||||
QObject::connect(copy_to_clipboard, &QPushButton::clicked, this, &SaveDBCDialog::copytoClipboard);
|
||||
QObject::connect(save_as, &QPushButton::clicked, this, &SaveDBCDialog::saveAs);
|
||||
}
|
||||
|
||||
void SaveDBCDialog::copytoClipboard() {
|
||||
dbc_edit->selectAll();
|
||||
dbc_edit->copy();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void SaveDBCDialog::saveAs() {
|
||||
QString file_name = QFileDialog::getSaveFileName(this, tr("Save File"),
|
||||
QDir::homePath() + "/untitled.dbc", tr("DBC (*.dbc)"));
|
||||
if (!file_name.isEmpty()) {
|
||||
QFile file(file_name);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
file.write(dbc_edit->toPlainText().toUtf8());
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,16 @@ public:
|
||||
QTextEdit *dbc_edit;
|
||||
};
|
||||
|
||||
class SaveDBCDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SaveDBCDialog(QWidget *parent);
|
||||
void copytoClipboard();
|
||||
void saveAs();
|
||||
QTextEdit *dbc_edit;
|
||||
};
|
||||
|
||||
class MessageListModel : public QAbstractTableModel {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -52,6 +62,7 @@ public slots:
|
||||
void loadDBCFromName(const QString &name);
|
||||
void loadDBCFromFingerprint();
|
||||
void loadDBCFromPaste();
|
||||
void saveDBC();
|
||||
|
||||
signals:
|
||||
void msgSelectionChanged(const QString &message_id);
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
cd "$(dirname "$0")"
|
||||
export LD_LIBRARY_PATH="../../../opendbc/can:$LD_LIBRARY_PATH"
|
||||
exec ./_test_cabana "$1"
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
|
||||
TEST_CASE("DBCManager::generateDBC") {
|
||||
DBCManager dbc_origin(nullptr);
|
||||
dbc_origin.open("toyota_new_mc_pt_generated");
|
||||
QString dbc_string = dbc_origin.generateDBC();
|
||||
|
||||
DBCManager dbc_from_generated(nullptr);
|
||||
dbc_from_generated.open("", dbc_string);
|
||||
|
||||
auto dbc = dbc_origin.getDBC();
|
||||
auto new_dbc = dbc_from_generated.getDBC();
|
||||
REQUIRE(dbc->msgs.size() == new_dbc->msgs.size());
|
||||
for (int i = 0; i < dbc->msgs.size(); ++i) {
|
||||
REQUIRE(dbc->msgs[i].name == new_dbc->msgs[i].name);
|
||||
REQUIRE(dbc->msgs[i].address == new_dbc->msgs[i].address);
|
||||
REQUIRE(dbc->msgs[i].size == new_dbc->msgs[i].size);
|
||||
REQUIRE(dbc->msgs[i].sigs.size() == new_dbc->msgs[i].sigs.size());
|
||||
auto &sig = dbc->msgs[i].sigs;
|
||||
auto &new_sig = new_dbc->msgs[i].sigs;
|
||||
for (int j = 0; j < sig.size(); ++j) {
|
||||
REQUIRE(sig[j].name == new_sig[j].name);
|
||||
REQUIRE(sig[j].start_bit == new_sig[j].start_bit);
|
||||
REQUIRE(sig[j].msb == new_sig[j].msb);
|
||||
REQUIRE(sig[j].lsb == new_sig[j].lsb);
|
||||
REQUIRE(sig[j].size == new_sig[j].size);
|
||||
REQUIRE(sig[j].is_signed == new_sig[j].is_signed);
|
||||
REQUIRE(sig[j].factor == new_sig[j].factor);
|
||||
REQUIRE(sig[j].offset == new_sig[j].offset);
|
||||
REQUIRE(sig[j].is_little_endian == new_sig[j].is_little_endian);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "catch2/catch.hpp"
|
||||
#include <QCoreApplication>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// unit tests for Qt
|
||||
QCoreApplication app(argc, argv);
|
||||
const int res = Catch::Session().run(argc, argv);
|
||||
return (res < 0xff ? res : 0xff);
|
||||
}
|
||||
Reference in New Issue
Block a user