diff --git a/tools/cabana/SConscript b/tools/cabana/SConscript
index b7321e1f8d..3ff4862800 100644
--- a/tools/cabana/SConscript
+++ b/tools/cabana/SConscript
@@ -19,7 +19,7 @@ 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_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)
+ 'canmessages.cc', 'commands.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'):
diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc
index 678fe5f876..bcd2b88a81 100644
--- a/tools/cabana/binaryview.cc
+++ b/tools/cabana/binaryview.cc
@@ -165,14 +165,14 @@ void BinaryViewModel::setMessage(const QString &message_id) {
if ((dbc_msg = dbc()->msg(msg_id))) {
row_count = dbc_msg->size;
items.resize(row_count * column_count);
- for (int i = 0; i < dbc_msg->sigs.size(); ++i) {
- const auto &sig = dbc_msg->sigs[i];
+ int i = 0;
+ for (auto &[name, sig] : dbc_msg->sigs) {
auto [start, end] = getSignalRange(&sig);
for (int j = start; j <= end; ++j) {
int bit_index = sig.is_little_endian ? bigEndianBitIndex(j) : j;
int idx = column_count * (bit_index / 8) + bit_index % 8;
if (idx >= items.size()) {
- qWarning() << "signal " << sig.name.c_str() << "out of bounds.start_bit:" << sig.start_bit << "size:" << sig.size;
+ qWarning() << "signal " << name << "out of bounds.start_bit:" << sig.start_bit << "size:" << sig.size;
break;
}
if (j == start) sig.is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true;
@@ -180,6 +180,7 @@ void BinaryViewModel::setMessage(const QString &message_id) {
items[idx].bg_color = getColor(i);
items[idx].sigs.push_back(&sig);
}
+ ++i;
}
} else {
row_count = can->lastMessage(msg_id).dat.size();
diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h
index 05bfe7e79f..2d6fc5c18b 100644
--- a/tools/cabana/binaryview.h
+++ b/tools/cabana/binaryview.h
@@ -50,7 +50,7 @@ public:
private:
QString msg_id;
- const Msg *dbc_msg;
+ const DBCMsg *dbc_msg;
int row_count = 0;
const int column_count = 9;
};
diff --git a/tools/cabana/chartswidget.cc b/tools/cabana/chartswidget.cc
index 3a170bccdc..875ed80ac5 100644
--- a/tools/cabana/chartswidget.cc
+++ b/tools/cabana/chartswidget.cc
@@ -240,7 +240,7 @@ void ChartView::resizeEvent(QResizeEvent *event) {
void ChartView::updateTitle() {
chart()->setTitle(signal->name.c_str());
- msg_title->setHtml(tr("%1 %2").arg(dbc()->msg(id)->name.c_str()).arg(id));
+ msg_title->setHtml(tr("%1 %2").arg(dbc()->msg(id)->name).arg(id));
}
void ChartView::updateFromSettings() {
diff --git a/tools/cabana/commands.cc b/tools/cabana/commands.cc
new file mode 100644
index 0000000000..b3f5cb1c66
--- /dev/null
+++ b/tools/cabana/commands.cc
@@ -0,0 +1,75 @@
+#include "tools/cabana/commands.h"
+
+// EditMsgCommand
+
+EditMsgCommand::EditMsgCommand(const QString &id, const QString &title, int size, QUndoCommand *parent)
+ : id(id), new_title(title), new_size(size), QUndoCommand(parent) {
+ if (auto msg = dbc()->msg(id)) {
+ old_title = msg->name;
+ old_size = msg->size;
+ }
+ setText(QObject::tr("Edit message %1:%2").arg(DBCManager::parseId(id).second).arg(title));
+}
+
+void EditMsgCommand::undo() {
+ if (old_title.isEmpty())
+ dbc()->removeMsg(id);
+ else
+ dbc()->updateMsg(id, old_title, old_size);
+}
+
+void EditMsgCommand::redo() {
+ dbc()->updateMsg(id, new_title, new_size);
+}
+
+// RemoveMsgCommand
+
+RemoveMsgCommand::RemoveMsgCommand(const QString &id, QUndoCommand *parent) : id(id), QUndoCommand(parent) {
+ if (auto msg = dbc()->msg(id)) {
+ message = *msg;
+ setText(QObject::tr("Remove message %1:%2").arg(DBCManager::parseId(id).second).arg(message.name));
+ }
+}
+
+void RemoveMsgCommand::undo() {
+ if (!message.name.isEmpty()) {
+ dbc()->updateMsg(id, message.name, message.size);
+ for (auto &[name, s] : message.sigs)
+ dbc()->addSignal(id, s);
+ }
+}
+
+void RemoveMsgCommand::redo() {
+ if (!message.name.isEmpty())
+ dbc()->removeMsg(id);
+}
+
+// AddSigCommand
+
+AddSigCommand::AddSigCommand(const QString &id, const Signal &sig, QUndoCommand *parent)
+ : id(id), signal(sig), QUndoCommand(parent) {
+ setText(QObject::tr("Add signal %1 to %2").arg(sig.name.c_str()).arg(DBCManager::parseId(id).second));
+}
+
+void AddSigCommand::undo() { dbc()->removeSignal(id, signal.name.c_str()); }
+void AddSigCommand::redo() { dbc()->addSignal(id, signal); }
+
+// RemoveSigCommand
+
+RemoveSigCommand::RemoveSigCommand(const QString &id, const Signal *sig, QUndoCommand *parent)
+ : id(id), signal(*sig), QUndoCommand(parent) {
+ setText(QObject::tr("Remove signal %1 from %2").arg(signal.name.c_str()).arg(DBCManager::parseId(id).second));
+}
+
+void RemoveSigCommand::undo() { dbc()->addSignal(id, signal); }
+void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name.c_str()); }
+
+// EditSignalCommand
+
+EditSignalCommand::EditSignalCommand(const QString &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent)
+ : id(id), old_signal(*sig), new_signal(new_sig), QUndoCommand(parent) {
+ setText(QObject::tr("Edit signal %1").arg(old_signal.name.c_str()));
+}
+
+void EditSignalCommand::undo() { dbc()->updateSignal(id, new_signal.name.c_str(), old_signal); }
+void EditSignalCommand::redo() { dbc()->updateSignal(id, old_signal.name.c_str(), new_signal); }
diff --git a/tools/cabana/commands.h b/tools/cabana/commands.h
new file mode 100644
index 0000000000..7ea1f66653
--- /dev/null
+++ b/tools/cabana/commands.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include
+
+#include "tools/cabana/canmessages.h"
+#include "tools/cabana/dbcmanager.h"
+
+class EditMsgCommand : public QUndoCommand {
+public:
+ EditMsgCommand(const QString &id, const QString &title, int size, QUndoCommand *parent = nullptr);
+ void undo() override;
+ void redo() override;
+
+private:
+ const QString id;
+ QString old_title, new_title;
+ int old_size = 0, new_size = 0;
+};
+
+class RemoveMsgCommand : public QUndoCommand {
+public:
+ RemoveMsgCommand(const QString &id, QUndoCommand *parent = nullptr);
+ void undo() override;
+ void redo() override;
+
+private:
+ const QString id;
+ DBCMsg message;
+};
+
+class AddSigCommand : public QUndoCommand {
+public:
+ AddSigCommand(const QString &id, const Signal &sig, QUndoCommand *parent = nullptr);
+ void undo() override;
+ void redo() override;
+
+private:
+ const QString id;
+ Signal signal = {};
+};
+
+class RemoveSigCommand : public QUndoCommand {
+public:
+ RemoveSigCommand(const QString &id, const Signal *sig, QUndoCommand *parent = nullptr);
+ void undo() override;
+ void redo() override;
+
+private:
+ const QString id;
+ Signal signal = {};
+};
+
+class EditSignalCommand : public QUndoCommand {
+public:
+ EditSignalCommand(const QString &id, const Signal *sig, const Signal &new_sig, QUndoCommand *parent = nullptr);
+ void undo() override;
+ void redo() override;
+
+private:
+ const QString id;
+ Signal old_signal = {};
+ Signal new_signal = {};
+};
diff --git a/tools/cabana/dbcmanager.cc b/tools/cabana/dbcmanager.cc
index abdd9a08df..ebad2387cf 100644
--- a/tools/cabana/dbcmanager.cc
+++ b/tools/cabana/dbcmanager.cc
@@ -10,32 +10,36 @@ DBCManager::~DBCManager() {}
void DBCManager::open(const QString &dbc_file_name) {
dbc = const_cast(dbc_lookup(dbc_file_name.toStdString()));
- updateMsgMap();
- emit DBCFileChanged();
+ initMsgMap();
}
void DBCManager::open(const QString &name, const QString &content) {
std::istringstream stream(content.toStdString());
dbc = const_cast(dbc_parse_from_stream(name.toStdString(), stream));
- updateMsgMap();
- emit DBCFileChanged();
+ initMsgMap();
}
-void DBCManager::updateMsgMap() {
- msg_map.clear();
- for (auto &msg : dbc->msgs)
- msg_map[msg.address] = &msg;
+void DBCManager::initMsgMap() {
+ msgs.clear();
+ for (auto &msg : dbc->msgs) {
+ auto &m = msgs[msg.address];
+ m.name = msg.name.c_str();
+ m.size = msg.size;
+ for (auto &s : msg.sigs)
+ m.sigs[QString::fromStdString(s.name)] = s;
+ }
+ emit DBCFileChanged();
}
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) {
+ for (auto &[address, m] : msgs) {
+ dbc_string += QString("BO_ %1 %2: %3 XXX\n").arg(address).arg(m.name).arg(m.size);
+ for (auto &[name, sig] : m.sigs) {
dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [0|0] \"\" XXX\n")
- .arg(sig.name.c_str())
+ .arg(name)
.arg(sig.start_bit)
.arg(sig.size)
.arg(sig.is_little_endian ? '1' : '0')
@@ -49,48 +53,45 @@ QString DBCManager::generateDBC() {
}
void DBCManager::updateMsg(const QString &id, const QString &name, uint32_t size) {
- auto [bus, address] = parseId(id);
- if (auto m = const_cast(msg(address))) {
- m->name = name.toStdString();
- m->size = size;
- } else {
- m = &dbc->msgs.emplace_back(Msg{.address = address, .name = name.toStdString(), .size = size});
- msg_map[address] = m;
- }
+ auto [_, address] = parseId(id);
+ auto &m = msgs[address];
+ m.name = name;
+ m.size = size;
emit msgUpdated(address);
}
void DBCManager::removeMsg(const QString &id) {
uint32_t address = parseId(id).second;
- auto it = std::find_if(dbc->msgs.begin(), dbc->msgs.end(), [address](auto &m) { return m.address == address; });
- if (it != dbc->msgs.end()) {
- dbc->msgs.erase(it);
- updateMsgMap();
- emit msgRemoved(address);
- }
+ msgs.erase(address);
+ emit msgRemoved(address);
}
void DBCManager::addSignal(const QString &id, const Signal &sig) {
- if (Msg *m = const_cast(msg(id))) {
- emit signalAdded(&m->sigs.emplace_back(sig));
+ if (auto m = const_cast(msg(id))) {
+ auto &s = m->sigs[sig.name.c_str()];
+ s = sig;
+ emit signalAdded(&s);
}
}
void DBCManager::updateSignal(const QString &id, const QString &sig_name, const Signal &sig) {
- if (Msg *m = const_cast(msg(id))) {
- auto it = std::find_if(m->sigs.begin(), m->sigs.end(), [=](auto &sig) { return sig_name == sig.name.c_str(); });
- if (it != m->sigs.end()) {
- *it = sig;
- emit signalUpdated(&(*it));
- }
+ if (auto m = const_cast(msg(id))) {
+ // change key name
+ QString new_name = QString::fromStdString(sig.name);
+ auto node = m->sigs.extract(sig_name);
+ node.key() = new_name;
+ auto it = m->sigs.insert(std::move(node));
+ auto &s = m->sigs[new_name];
+ s = sig;
+ emit signalUpdated(&s);
}
}
void DBCManager::removeSignal(const QString &id, const QString &sig_name) {
- if (Msg *m = const_cast(msg(id))) {
- auto it = std::find_if(m->sigs.begin(), m->sigs.end(), [=](auto &sig) { return sig_name == sig.name.c_str(); });
+ if (auto m = const_cast(msg(id))) {
+ auto it = m->sigs.find(sig_name);
if (it != m->sigs.end()) {
- emit signalRemoved(&(*it));
+ emit signalRemoved(&(it->second));
m->sigs.erase(it);
}
}
diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h
index 81c723a20d..d2262527d4 100644
--- a/tools/cabana/dbcmanager.h
+++ b/tools/cabana/dbcmanager.h
@@ -1,9 +1,16 @@
#pragma once
+#include