cabana: more advanced DBC management features (#27968)

* cabana: improve menu to manage individual DBCs

* remove from single bus

* comments

* close explicitly

* new before open

* whitespace

* remove old code

* clipboard actions

* cleanup actions

* sections instead of nested menu

* consistent bus format
old-commit-hash: 7f5cda00b5
beeps
Willem Melching 2 years ago committed by GitHub
parent 1045c7d836
commit e32ba8719a
  1. 4
      tools/cabana/dbc/dbcfile.cc
  2. 69
      tools/cabana/dbc/dbcmanager.cc
  3. 17
      tools/cabana/dbc/dbcmanager.h
  4. 239
      tools/cabana/mainwin.cc
  5. 15
      tools/cabana/mainwin.h

@ -222,11 +222,11 @@ int DBCFile::msgCount() const {
}
QString DBCFile::name() const {
return name_;
return name_.isEmpty() ? "untitled" : name_;
}
bool DBCFile::isEmpty() const {
return (signalCount() == 0) && name().isEmpty();
return (signalCount() == 0) && name_.isEmpty();
}
void DBCFile::parseExtraInfo(const QString &content) {

@ -19,20 +19,6 @@ bool DBCManager::open(SourceSet s, const QString &dbc_file_name, QString *error)
emit DBCFileChanged();
return true;
}
// Check if there is already a file for this sourceset, then replace it
if (ss == s) {
try {
dbc_files[i] = {s, new DBCFile(dbc_file_name, this)};
delete dbc_file;
emit DBCFileChanged();
return true;
} catch (std::exception &e) {
if (error) *error = e.what();
return false;
}
}
}
try {
@ -58,6 +44,38 @@ bool DBCManager::open(SourceSet s, const QString &name, const QString &content,
return true;
}
void DBCManager::close(SourceSet s) {
// Build new list of dbc files, removing the ones that match the sourceset
QList<std::pair<SourceSet, DBCFile*>> new_dbc_files;
for (auto entry : dbc_files) {
if (entry.first == s) {
delete entry.second;
} else {
new_dbc_files.push_back(entry);
}
}
dbc_files = new_dbc_files;
emit DBCFileChanged();
}
void DBCManager::close(DBCFile *dbc_file) {
assert(dbc_file != nullptr);
// Build new list of dbc files, removing the one that matches dbc_file*
QList<std::pair<SourceSet, DBCFile*>> new_dbc_files;
for (auto entry : dbc_files) {
if (entry.second == dbc_file) {
delete entry.second;
} else {
new_dbc_files.push_back(entry);
}
}
dbc_files = new_dbc_files;
emit DBCFileChanged();
}
void DBCManager::closeAll() {
if (dbc_files.isEmpty()) return;
@ -69,6 +87,29 @@ void DBCManager::closeAll() {
emit DBCFileChanged();
}
void DBCManager::removeSourcesFromFile(DBCFile *dbc_file, SourceSet s) {
assert(dbc_file != nullptr);
// Build new list of dbc files, for the given dbc_file* remove s from the current sources
QList<std::pair<SourceSet, DBCFile*>> new_dbc_files;
for (auto entry : dbc_files) {
if (entry.second == dbc_file) {
SourceSet ss = (entry.first == SOURCE_ALL) ? sources : entry.first;
ss -= s;
if (ss.empty()) { // Close file if no more sources remain
delete dbc_file;
} else {
new_dbc_files.push_back({ss, dbc_file});
}
} else {
new_dbc_files.push_back(entry);
}
}
dbc_files = new_dbc_files;
emit DBCFileChanged();
}
void DBCManager::addSignal(const MessageId &id, const cabana::Signal &sig) {
auto sources_dbc_file = findDBCFile(id);

@ -24,7 +24,10 @@ public:
~DBCManager() {}
bool open(SourceSet s, const QString &dbc_file_name, QString *error = nullptr);
bool open(SourceSet s, const QString &name, const QString &content, QString *error = nullptr);
void close(SourceSet s);
void close(DBCFile *dbc_file);
void closeAll();
void removeSourcesFromFile(DBCFile *dbc_file, SourceSet s);
void addSignal(const MessageId &id, const cabana::Signal &sig);
void updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig);
@ -73,3 +76,17 @@ inline QString msgName(const MessageId &id) {
auto msg = dbc()->msg(id);
return msg ? msg->name : UNTITLED;
}
inline QString toString(SourceSet ss) {
if (ss == SOURCE_ALL) {
return "all";
} else {
QStringList ret;
QList source_list = ss.values();
std::sort(source_list.begin(), source_list.end());
for (auto s : source_list) {
ret << QString::number(s);
}
return ret.join(", ");
}
}

@ -92,8 +92,7 @@ void MainWindow::createActions() {
file_menu->addAction(tr("New DBC File"), this, &MainWindow::newFile)->setShortcuts(QKeySequence::New);
file_menu->addAction(tr("Open DBC File..."), this, &MainWindow::openFile)->setShortcuts(QKeySequence::Open);
open_dbc_for_source = file_menu->addMenu(tr("Open &DBC File for Bus"));
open_dbc_for_source->setEnabled(false);
manage_dbcs_menu = file_menu->addMenu(tr("Manage &DBC Files"));
open_recent_menu = file_menu->addMenu(tr("Open &Recent"));
for (int i = 0; i < MAX_RECENT_FILES; ++i) {
@ -113,7 +112,7 @@ void MainWindow::createActions() {
load_opendbc_menu->addAction(QString::fromStdString(name), this, &MainWindow::openOpendbcFile);
}
file_menu->addAction(tr("Load DBC From Clipboard"), this, &MainWindow::loadDBCFromClipboard);
file_menu->addAction(tr("Load DBC From Clipboard"), [=]() { loadFromClipboard(); });
file_menu->addSeparator();
save_dbc = file_menu->addAction(tr("Save DBC..."), this, &MainWindow::save);
@ -122,7 +121,7 @@ void MainWindow::createActions() {
save_dbc_as = file_menu->addAction(tr("Save DBC As..."), this, &MainWindow::saveAs);
save_dbc_as->setShortcuts(QKeySequence::SaveAs);
copy_dbc_to_clipboard = file_menu->addAction(tr("Copy DBC To Clipboard"), this, &MainWindow::saveDBCToClipboard);
copy_dbc_to_clipboard = file_menu->addAction(tr("Copy DBC To Clipboard"), this, &MainWindow::saveToClipboard);
file_menu->addSeparator();
file_menu->addAction(tr("Settings..."), this, &MainWindow::setOption)->setShortcuts(QKeySequence::Preferences);
@ -275,18 +274,20 @@ void MainWindow::openFile() {
}
}
void MainWindow::openFileForSource() {
if (auto action = qobject_cast<QAction *>(sender())) {
uint8_t source = action->data().value<uint8_t>();
assert(source < 64);
QString fn = QFileDialog::getOpenFileName(this, tr("Open File"), settings.last_dir, "DBC (*.dbc)");
if (!fn.isEmpty()) {
loadFile(fn, {source, uint8_t(source + 128), uint8_t(source + 192)}, false);
}
void MainWindow::openFileForSource(SourceSet s) {
QString fn = QFileDialog::getOpenFileName(this, tr("Open File"), settings.last_dir, "DBC (*.dbc)");
if (!fn.isEmpty()) {
loadFile(fn, s, false);
}
}
void MainWindow::newFileForSource(SourceSet s) {
remindSaveChanges();
dbc()->close(s);
dbc()->open(s, "", "");
}
void MainWindow::loadFile(const QString &fn, SourceSet s, bool close_all) {
if (!fn.isEmpty()) {
QString dbc_fn = fn;
@ -307,6 +308,7 @@ void MainWindow::loadFile(const QString &fn, SourceSet s, bool close_all) {
dbc()->closeAll();
}
dbc()->close(s);
bool ret = dbc()->open(s, dbc_fn, &error);
if (ret) {
updateRecentFiles(fn);
@ -346,13 +348,17 @@ void MainWindow::loadDBCFromOpendbc(const QString &name) {
updateLoadSaveMenus();
}
void MainWindow::loadDBCFromClipboard() {
void MainWindow::loadFromClipboard(SourceSet s, bool close_all) {
remindSaveChanges();
QString dbc_str = QGuiApplication::clipboard()->text();
QString error;
dbc()->closeAll();
bool ret = dbc()->open(SOURCE_ALL, "", dbc_str, &error);
if (close_all) {
dbc()->closeAll();
}
dbc()->close(s);
bool ret = dbc()->open(s, "", dbc_str, &error);
if (ret && dbc()->msgCount() > 0) {
QMessageBox::information(this, tr("Load From Clipboard"), tr("DBC Successfully Loaded!"));
} else {
@ -388,9 +394,22 @@ void MainWindow::loadDBCFromFingerprint() {
}
void MainWindow::save() {
saveFile();
// Save all open DBC files
for (auto &[s, dbc_file] : dbc()->dbc_files) {
if (dbc_file->isEmpty()) continue;
saveFile(dbc_file);
}
}
void MainWindow::saveAs() {
// Save as all open DBC files. Should not be called with more than 1 file open
for (auto &[s, dbc_file] : dbc()->dbc_files) {
if (dbc_file->isEmpty()) continue;
saveFileAs(dbc_file);
}
}
void MainWindow::autoSave() {
if (!UndoStack::instance()->isClean()) {
for (auto &[_, dbc_file] : dbc()->dbc_files) {
@ -407,46 +426,86 @@ void MainWindow::cleanupAutoSaveFile() {
}
}
void MainWindow::saveFile() {
// Save all open DBC files
for (auto &[s, dbc_file] : dbc()->dbc_files) {
if (!dbc_file->filename.isEmpty()) {
dbc_file->save();
updateRecentFiles(dbc_file->filename);
} else if (!dbc_file->isEmpty()) {
QString fn = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)"));
if (!fn.isEmpty()) {
dbc_file->saveAs(fn);
updateRecentFiles(fn);
}
void MainWindow::closeFile(DBCFile *dbc_file) {
assert(dbc_file != nullptr);
remindSaveChanges();
dbc()->close(dbc_file);
// Ensure we always have at least one file open
if (dbc()->dbcCount() == 0) {
newFile();
}
}
void MainWindow::saveFile(DBCFile *dbc_file) {
assert(dbc_file != nullptr);
SourceSet s;
for (auto &[s_, dbc_file_] : dbc()->dbc_files) {
if (dbc_file_ == dbc_file) {
s = s_;
break;
}
}
if (!dbc_file->filename.isEmpty()) {
dbc_file->save();
updateRecentFiles(dbc_file->filename);
} else if (!dbc_file->isEmpty()) {
QString fn = QFileDialog::getSaveFileName(this, tr("Save File (bus: %1)").arg(toString(s)), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)"));
if (!fn.isEmpty()) {
dbc_file->saveAs(fn);
updateRecentFiles(fn);
}
}
UndoStack::instance()->setClean();
statusBar()->showMessage(tr("File saved"), 2000);
updateLoadSaveMenus();
}
void MainWindow::saveAs() {
// Assume only one file is open
for (auto &[s, dbc_file] : dbc()->dbc_files) {
if (dbc_file->isEmpty()) continue;
QString fn = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)"));
if (!fn.isEmpty()) {
dbc_file->saveAs(fn);
updateRecentFiles(fn);
void MainWindow::saveFileAs(DBCFile *dbc_file) {
assert(dbc_file != nullptr);
SourceSet s;
for (auto &[s_, dbc_file_] : dbc()->dbc_files) {
if (dbc_file_ == dbc_file) {
s = s_;
break;
}
}
QString fn = QFileDialog::getSaveFileName(this, tr("Save File (bus: %1)").arg(toString(s)), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)"));
if (!fn.isEmpty()) {
dbc_file->saveAs(fn);
updateRecentFiles(fn);
updateLoadSaveMenus();
}
}
void MainWindow::saveDBCToClipboard() {
// Assume only one file is open
void MainWindow::removeBusFromFile(DBCFile *dbc_file, uint8_t source) {
assert(dbc_file != nullptr);
SourceSet ss = {source, uint8_t(source + 128), uint8_t(source + 192)};
dbc()->removeSourcesFromFile(dbc_file, ss);
updateLoadSaveMenus();
}
void MainWindow::saveToClipboard() {
// Copy all open DBC files to clipboard. Should not be called with more than 1 file open
for (auto &[s, dbc_file] : dbc()->dbc_files) {
if (dbc_file->isEmpty()) continue;
QGuiApplication::clipboard()->setText(dbc_file->generateDBC());
QMessageBox::information(this, tr("Copy To Clipboard"), tr("DBC Successfully copied!"));
saveFileToClipboard(dbc_file);
}
}
void MainWindow::saveFileToClipboard(DBCFile *dbc_file) {
assert(dbc_file != nullptr);
QGuiApplication::clipboard()->setText(dbc_file->generateDBC());
QMessageBox::information(this, tr("Copy To Clipboard"), tr("DBC Successfully copied!"));
}
void MainWindow::updateSources(const SourceSet &s) {
sources = s;
updateLoadSaveMenus();
@ -462,7 +521,6 @@ void MainWindow::updateLoadSaveMenus() {
save_dbc->setText(tr("Save DBC..."));
}
// TODO: Support save as for multiple files
save_dbc_as->setEnabled(cnt == 1);
// TODO: Support clipboard for multiple files
@ -472,35 +530,92 @@ void MainWindow::updateLoadSaveMenus() {
QList<uint8_t> sources_sorted = sources.toList();
std::sort(sources_sorted.begin(), sources_sorted.end());
open_dbc_for_source->setEnabled(sources.size() > 0);
open_dbc_for_source->clear();
manage_dbcs_menu->clear();
std::map<QString, QStringList> dbc_files;
for (uint8_t source : sources_sorted) {
if (source >= 64) continue; // Sent and blocked buses are handled implicitly
QAction *action = new QAction(this);
auto d = dbc()->findDBCFile(source);
QString name = tr("no DBC");
if (d) {
if (!d->second->name().isEmpty()) {
name = tr("%1").arg(d->second->name());
} else {
name = "untitled";
SourceSet ss = {source, uint8_t(source + 128), uint8_t(source + 192)};
QMenu *bus_menu = new QMenu(this);
// New
QAction *new_action = new QAction(this);
new_action->setText(tr("New DBC File..."));
QObject::connect(new_action, &QAction::triggered, [=]() { newFileForSource(ss); });
bus_menu->addAction(new_action);
// Open
QAction *open_action = new QAction(this);
open_action->setText(tr("Open DBC File..."));
QObject::connect(open_action, &QAction::triggered, [=]() { openFileForSource(ss); });
bus_menu->addAction(open_action);
// Open
QAction *load_clipboard_action = new QAction(this);
load_clipboard_action->setText(tr("Load DBC From Clipboard..."));
QObject::connect(load_clipboard_action, &QAction::triggered, [=]() { loadFromClipboard(ss, false); });
bus_menu->addAction(load_clipboard_action);
// Show sub-menu for each dbc for this source.
QStringList bus_menu_fns;
for (auto it : dbc()->dbc_files) {
auto &[src, dbc_file] = it;
if (!src.contains(source) && (src != SOURCE_ALL)) {
continue;
}
dbc_files[d->second->filename].push_back(QString::number(source));
QString fn = dbc_file->filename.isEmpty() ? "untitled" : QFileInfo(dbc_file->filename).baseName();
// QMenu *manage_menu = bus_menu;
bus_menu->addSeparator();
QAction *fn_action = new QAction(this);
fn_action->setText(fn + " (" + toString(src) + ")");
fn_action->setEnabled(false);
bus_menu->addAction(fn_action);
// Save
QAction *save_action = new QAction(this);
save_action->setText(tr("Save..."));
bus_menu->addAction(save_action);
QObject::connect(save_action, &QAction::triggered, [=](){ saveFile(it.second); });
// Save as
QAction *save_as_action = new QAction(this);
save_as_action->setText(tr("Save As..."));
bus_menu->addAction(save_as_action);
QObject::connect(save_as_action, &QAction::triggered, [=](){ saveFileAs(it.second); });
// Copy to clipboard
QAction *save_clipboard_action = new QAction(this);
save_clipboard_action->setText(tr("Copy to Clipboard..."));
bus_menu->addAction(save_clipboard_action);
QObject::connect(save_clipboard_action, &QAction::triggered, [=](){ saveFileToClipboard(it.second); });
// Remove from this bus
QAction *remove_action = new QAction(this);
remove_action->setText(tr("Remove from this bus..."));
bus_menu->addAction(remove_action);
QObject::connect(remove_action, &QAction::triggered, [=](){ removeBusFromFile(it.second, source); });
// Close/Remove from all buses
QAction *close_action = new QAction(this);
close_action->setText(tr("Remove from all buses..."));
bus_menu->addAction(close_action);
QObject::connect(close_action, &QAction::triggered, [=](){ closeFile(it.second); });
bus_menu_fns << fn;
}
action->setText(tr("Bus %1 (current: %2)").arg(source).arg(name));
action->setData(source);
QObject::connect(action, &QAction::triggered, this, &MainWindow::openFileForSource);
open_dbc_for_source->addAction(action);
manage_dbcs_menu->addMenu(bus_menu);
QString bus_menu_title = bus_menu_fns.size() ? bus_menu_fns.join(", ") : "No DBCs loaded";
bus_menu->setTitle(tr("Bus %1 (%2)").arg(source).arg(bus_menu_title));
}
QStringList title;
for (auto &[filename, src] : dbc_files) {
QString bus = dbc_files.size() == 1 ? "all" : src.join(",");
title.push_back("[" + bus + "]" + QFileInfo(filename).baseName());
for (auto &[src, dbc_file] : dbc()->dbc_files) {
QString fn = dbc_file->filename.isEmpty() ? "untitled" : QFileInfo(dbc_file->filename).baseName();
title.push_back(tr("(%1) %2").arg(toString(src)).arg(fn));
}
setWindowFilePath(title.join(" | "));
}

@ -27,15 +27,13 @@ public slots:
void openRoute();
void newFile();
void openFile();
void openFileForSource();
void openRecentFile();
void openOpendbcFile();
void loadDBCFromOpendbc(const QString &name);
void loadDBCFromFingerprint();
void loadDBCFromClipboard();
void save();
void saveAs();
void saveDBCToClipboard();
void saveToClipboard();
void updateSources(const SourceSet &s);
signals:
@ -44,7 +42,14 @@ signals:
protected:
void remindSaveChanges();
void saveFile();
void closeFile(DBCFile *dbc_file);
void saveFile(DBCFile *dbc_file);
void saveFileAs(DBCFile *dbc_file);
void saveFileToClipboard(DBCFile *dbc_file);
void removeBusFromFile(DBCFile *dbc_file, uint8_t source);
void loadFromClipboard(SourceSet s = SOURCE_ALL, bool close_all = true);
void openFileForSource(SourceSet s);
void newFileForSource(SourceSet s);
void autoSave();
void cleanupAutoSaveFile();
void updateRecentFiles(const QString &fn);
@ -79,7 +84,7 @@ protected:
enum { MAX_RECENT_FILES = 15 };
QAction *recent_files_acts[MAX_RECENT_FILES] = {};
QMenu *open_recent_menu = nullptr;
QMenu *open_dbc_for_source = nullptr;
QMenu *manage_dbcs_menu = nullptr;
QAction *save_dbc = nullptr;
QAction *save_dbc_as = nullptr;
QAction *copy_dbc_to_clipboard = nullptr;

Loading…
Cancel
Save