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
pull/26283/head^2
Dean Lee 3 years ago committed by GitHub
parent 5de54c35a9
commit 80b088c332
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/workflows/selfdrive_tests.yaml
  2. 2
      tools/cabana/.gitignore
  3. 6
      tools/cabana/SConscript
  4. 21
      tools/cabana/dbcmanager.cc
  5. 4
      tools/cabana/dbcmanager.h
  6. 63
      tools/cabana/messageswidget.cc
  7. 11
      tools/cabana/messageswidget.h
  8. 4
      tools/cabana/tests/test_cabana
  9. 35
      tools/cabana/tests/test_cabana.cc
  10. 10
      tools/cabana/tests/test_runner.cc

@ -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 &current, 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());
}
}
@ -111,6 +111,12 @@ void MessagesWidget::loadDBCFromFingerprint() {
}
}
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);

@ -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);
}
Loading…
Cancel
Save