mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-02-23 09:03:52 +08:00
cabana: add support for load&save extra dbc info (#27203)
* support extra info * support undo/redo * fix undo/redo * cleanup * fix regexp * refactor dbcmanager * replace text in headerview * fix binary::refresh * cleanup * use QRegularExpression * add desc validation * edit val description in table * cleanup
This commit is contained in:
@@ -17,6 +17,8 @@ qt_libs = ['qt_util'] + base_libs
|
||||
|
||||
cabana_libs = [widgets, cereal, messaging, visionipc, replay_lib, opendbc,'avutil', 'avcodec', 'avformat', 'bz2', 'curl', 'yuv'] + qt_libs
|
||||
cabana_env = qt_env.Clone()
|
||||
opendbc_path = '-DOPENDBC_FILE_PATH=\'"%s"\'' % (cabana_env.Dir("../../opendbc").abspath)
|
||||
cabana_env['CXXFLAGS'] += [opendbc_path]
|
||||
|
||||
prev_moc_path = cabana_env['QT_MOCHPREFIX']
|
||||
cabana_env['QT_MOCHPREFIX'] = os.path.dirname(prev_moc_path) + '/cabana/moc_'
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include "tools/cabana/commands.h"
|
||||
#include "tools/cabana/signaledit.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
|
||||
// BinaryView
|
||||
|
||||
@@ -170,7 +169,7 @@ void BinaryView::highlightPosition(const QPoint &pos) {
|
||||
auto item = (BinaryViewModel::Item *)index.internalPointer();
|
||||
const Signal *sig = item->sigs.isEmpty() ? nullptr : item->sigs.back();
|
||||
highlight(sig);
|
||||
QToolTip::showText(pos, sig ? sig->name.c_str() : "", this, rect());
|
||||
QToolTip::showText(pos, sig ? sig->name : "", this, rect());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,22 +245,22 @@ std::tuple<int, int, bool> BinaryView::getSelection(QModelIndex index) {
|
||||
void BinaryViewModel::refresh() {
|
||||
beginResetModel();
|
||||
items.clear();
|
||||
if ((dbc_msg = dbc()->msg(*msg_id))) {
|
||||
if (auto dbc_msg = dbc()->msg(*msg_id)) {
|
||||
row_count = dbc_msg->size;
|
||||
items.resize(row_count * column_count);
|
||||
for (auto sig : dbc_msg->getSignals()) {
|
||||
auto [start, end] = getSignalRange(sig);
|
||||
for (auto &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 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 " << sig.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;
|
||||
if (j == end) sig->is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true;
|
||||
items[idx].bg_color = getColor(sig);
|
||||
items[idx].sigs.push_back(sig);
|
||||
if (j == start) sig.is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true;
|
||||
if (j == end) sig.is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true;
|
||||
items[idx].bg_color = getColor(&sig);
|
||||
items[idx].sigs.push_back(&sig);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <QTableView>
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
class BinaryItemDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
@@ -49,7 +51,6 @@ public:
|
||||
std::vector<Item> items;
|
||||
|
||||
std::optional<MessageId> msg_id;
|
||||
const DBCMsg *dbc_msg = nullptr;
|
||||
int row_count = 0;
|
||||
const int column_count = 9;
|
||||
};
|
||||
|
||||
@@ -459,7 +459,7 @@ void ChartView::updateTitle() {
|
||||
}
|
||||
for (auto &s : sigs) {
|
||||
auto decoration = s.series->isVisible() ? "none" : "line-through";
|
||||
s.series->setName(QString("<span style=\"text-decoration:%1\"><b>%2</b> <font color=\"gray\">%3 %4</font></span>").arg(decoration, s.sig->name.c_str(), msgName(s.msg_id), s.msg_id.toString()));
|
||||
s.series->setName(QString("<span style=\"text-decoration:%1\"><b>%2</b> <font color=\"gray\">%3 %4</font></span>").arg(decoration, s.sig->name, msgName(s.msg_id), s.msg_id.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,7 +672,7 @@ void ChartView::mouseMoveEvent(QMouseEvent *ev) {
|
||||
value = QString::number(it->y());
|
||||
track_pts[i] = chart()->mapToPosition(*it);
|
||||
}
|
||||
text_list.push_back(QString("<span style=\"color:%1;\">■ </span>%2: <b>%3</b>").arg(sigs[i].series->color().name(), sigs[i].sig->name.c_str(), value));
|
||||
text_list.push_back(QString("<span style=\"color:%1;\">■ </span>%2: <b>%3</b>").arg(sigs[i].series->color().name(), sigs[i].sig->name, value));
|
||||
}
|
||||
auto max = std::max_element(track_pts.begin(), track_pts.end(), [](auto &a, auto &b) { return a.x() < b.x(); });
|
||||
auto pt = (max == track_pts.end()) ? ev->pos() : *max;
|
||||
@@ -887,7 +887,7 @@ void SeriesSelector::updateAvailableList(int index) {
|
||||
available_list->clear();
|
||||
MessageId msg_id = msgs_combo->itemData(index).value<MessageId>();
|
||||
auto selected_items = seletedItems();
|
||||
for (auto &[name, s] : dbc()->msg(msg_id)->sigs) {
|
||||
for (auto &s : dbc()->msg(msg_id)->sigs) {
|
||||
bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig=&s](auto it) { return it->msg_id == msg_id && it->sig == sig; });
|
||||
if (!is_selected) {
|
||||
addItemToList(available_list, msg_id, &s);
|
||||
@@ -896,7 +896,7 @@ void SeriesSelector::updateAvailableList(int index) {
|
||||
}
|
||||
|
||||
void SeriesSelector::addItemToList(QListWidget *parent, const MessageId id, const Signal *sig, bool show_msg_name) {
|
||||
QString text = QString("<span style=\"color:%0;\">■ </span> %1").arg(getColor(sig).name(), sig->name.c_str());
|
||||
QString text = QString("<span style=\"color:%0;\">■ </span> %1").arg(getColor(sig).name(), sig->name);
|
||||
if (show_msg_name) text += QString(" <font color=\"gray\">%0 %1</font>").arg(msgName(id), id.toString());
|
||||
|
||||
QLabel *label = new QLabel(text);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
|
||||
using namespace dbcmanager;
|
||||
using namespace QtCharts;
|
||||
|
||||
const int CHART_MIN_WIDTH = 300;
|
||||
|
||||
@@ -36,7 +36,7 @@ RemoveMsgCommand::RemoveMsgCommand(const MessageId &id, QUndoCommand *parent) :
|
||||
void RemoveMsgCommand::undo() {
|
||||
if (!message.name.isEmpty()) {
|
||||
dbc()->updateMsg(id, message.name, message.size);
|
||||
for (auto &[name, s] : message.sigs)
|
||||
for (auto &s : message.sigs)
|
||||
dbc()->addSignal(id, s);
|
||||
}
|
||||
}
|
||||
@@ -50,31 +50,31 @@ void RemoveMsgCommand::redo() {
|
||||
|
||||
AddSigCommand::AddSigCommand(const MessageId &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(id.address));
|
||||
setText(QObject::tr("Add signal %1 to %2").arg(sig.name).arg(id.address));
|
||||
}
|
||||
|
||||
void AddSigCommand::undo() { dbc()->removeSignal(id, signal.name.c_str()); }
|
||||
void AddSigCommand::undo() { dbc()->removeSignal(id, signal.name); }
|
||||
void AddSigCommand::redo() { dbc()->addSignal(id, signal); }
|
||||
|
||||
// RemoveSigCommand
|
||||
|
||||
RemoveSigCommand::RemoveSigCommand(const MessageId &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(id.address));
|
||||
setText(QObject::tr("Remove signal %1 from %2").arg(signal.name).arg(id.address));
|
||||
}
|
||||
|
||||
void RemoveSigCommand::undo() { dbc()->addSignal(id, signal); }
|
||||
void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name.c_str()); }
|
||||
void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name); }
|
||||
|
||||
// EditSignalCommand
|
||||
|
||||
EditSignalCommand::EditSignalCommand(const MessageId &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()));
|
||||
setText(QObject::tr("Edit signal %1").arg(old_signal.name));
|
||||
}
|
||||
|
||||
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); }
|
||||
void EditSignalCommand::undo() { dbc()->updateSignal(id, new_signal.name, old_signal); }
|
||||
void EditSignalCommand::redo() { dbc()->updateSignal(id, old_signal.name, new_signal); }
|
||||
|
||||
namespace UndoStack {
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <QUndoStack>
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
class EditMsgCommand : public QUndoCommand {
|
||||
public:
|
||||
@@ -25,7 +27,7 @@ public:
|
||||
|
||||
private:
|
||||
const MessageId id;
|
||||
DBCMsg message;
|
||||
Msg message;
|
||||
};
|
||||
|
||||
class AddSigCommand : public QUndoCommand {
|
||||
|
||||
@@ -1,63 +1,114 @@
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include <QDebug>
|
||||
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
#include <QVector>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <QVector>
|
||||
|
||||
uint qHash(const MessageId &item) {
|
||||
return qHash(item.source) ^ qHash(item.address);
|
||||
namespace dbcmanager {
|
||||
|
||||
void sortSignalsByAddress(QList<Signal> &sigs) {
|
||||
std::sort(sigs.begin(), sigs.end(), [](auto &a, auto &b) { return a.start_bit < b.start_bit; });
|
||||
}
|
||||
|
||||
DBCManager::DBCManager(QObject *parent) : QObject(parent) {}
|
||||
|
||||
DBCManager::~DBCManager() {}
|
||||
|
||||
void DBCManager::open(const QString &dbc_file_name) {
|
||||
dbc = const_cast<DBC *>(dbc_lookup(dbc_file_name.toStdString()));
|
||||
initMsgMap();
|
||||
}
|
||||
|
||||
bool DBCManager::open(const QString &name, const QString &content, QString *error) {
|
||||
try {
|
||||
std::istringstream stream(content.toStdString());
|
||||
dbc = const_cast<DBC *>(dbc_parse_from_stream(name.toStdString(), stream));
|
||||
initMsgMap();
|
||||
return true;
|
||||
} catch (std::exception &e) {
|
||||
if (error) *error = e.what();
|
||||
bool DBCManager::open(const QString &dbc_file_name, QString *error) {
|
||||
QString opendbc_file_path = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, dbc_file_name);
|
||||
QFile file(opendbc_file_path);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
return open(dbc_file_name, file.readAll(), error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
void DBCManager::parseExtraInfo(const QString &content) {
|
||||
static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))");
|
||||
static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
|
||||
static QRegularExpression sgm_regexp(R"(^SG_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))");
|
||||
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"(.*)\";)");
|
||||
static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (.*);)");
|
||||
auto get_sig = [this](uint32_t address, const QString &name) -> Signal * {
|
||||
auto m = (Msg *)msg(address);
|
||||
return m ? (Signal *)m->sig(name) : nullptr;
|
||||
};
|
||||
|
||||
QTextStream stream((QString *)&content);
|
||||
uint32_t address = 0;
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine().trimmed();
|
||||
if (line.startsWith("BO_ ")) {
|
||||
if (auto match = bo_regexp.match(line); match.hasMatch()) {
|
||||
address = match.captured(1).toUInt();
|
||||
}
|
||||
} else if (line.startsWith("SG_ ")) {
|
||||
int offset = 0;
|
||||
auto match = sg_regexp.match(line);
|
||||
if (!match.hasMatch()) {
|
||||
match = sgm_regexp.match(line);
|
||||
offset = 1;
|
||||
}
|
||||
if (match.hasMatch()) {
|
||||
if (auto s = get_sig(address, match.captured(1))) {
|
||||
s->min = match.captured(8 + offset);
|
||||
s->max = match.captured(9 + offset);
|
||||
s->unit = match.captured(10 + offset);
|
||||
}
|
||||
}
|
||||
} else if (line.startsWith("VAL_ ")) {
|
||||
if (auto match = val_regexp.match(line); match.hasMatch()) {
|
||||
if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) {
|
||||
QStringList desc_list = match.captured(3).trimmed().split('"');
|
||||
for (int i = 0; i < desc_list.size(); i += 2) {
|
||||
auto val = desc_list[i].trimmed();
|
||||
if (!val.isEmpty() && (i + 1) < desc_list.size()) {
|
||||
auto desc = desc_list[i+1].trimmed();
|
||||
s->val_desc.push_back({val, desc});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (line.startsWith("CM_ SG_ ")) {
|
||||
if (auto match = sg_comment_regexp.match(line); match.hasMatch()) {
|
||||
if (auto s = get_sig(match.captured(1).toUInt(), match.captured(2))) {
|
||||
s->comment = match.captured(3).trimmed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
emit DBCFileChanged();
|
||||
}
|
||||
|
||||
QString DBCManager::generateDBC() {
|
||||
QString dbc_string;
|
||||
QString dbc_string, signal_comment, val_desc;
|
||||
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(name)
|
||||
for (auto &sig : m.sigs) {
|
||||
dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [%8|%9] \"%10\" XXX\n")
|
||||
.arg(sig.name)
|
||||
.arg(sig.start_bit)
|
||||
.arg(sig.size)
|
||||
.arg(sig.is_little_endian ? '1' : '0')
|
||||
.arg(sig.is_signed ? '-' : '+')
|
||||
.arg(sig.factor, 0, 'g', std::numeric_limits<double>::digits10)
|
||||
.arg(sig.offset, 0, 'g', std::numeric_limits<double>::digits10);
|
||||
.arg(sig.offset, 0, 'g', std::numeric_limits<double>::digits10)
|
||||
.arg(sig.min)
|
||||
.arg(sig.max)
|
||||
.arg(sig.unit);
|
||||
if (!sig.comment.isEmpty()) {
|
||||
signal_comment += QString("CM_ SG_ %1 %2 \"%3\";\n").arg(address).arg(sig.name).arg(sig.comment);
|
||||
}
|
||||
if (!sig.val_desc.isEmpty()) {
|
||||
QString text;
|
||||
for (auto &[val, desc] : sig.val_desc) {
|
||||
text += QString("%1 \"%2\"").arg(val, desc);
|
||||
}
|
||||
val_desc += QString("VAL_ %1 %2 %3;\n").arg(address).arg(sig.name).arg(text);
|
||||
}
|
||||
}
|
||||
dbc_string += "\n";
|
||||
}
|
||||
return dbc_string;
|
||||
return dbc_string + signal_comment + val_desc;
|
||||
}
|
||||
|
||||
void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size) {
|
||||
@@ -73,31 +124,29 @@ void DBCManager::removeMsg(const MessageId &id) {
|
||||
}
|
||||
|
||||
void DBCManager::addSignal(const MessageId &id, const Signal &sig) {
|
||||
if (auto m = const_cast<DBCMsg *>(msg(id.address))) {
|
||||
auto &s = m->sigs[sig.name.c_str()];
|
||||
s = sig;
|
||||
emit signalAdded(id.address, &s);
|
||||
if (auto m = const_cast<Msg *>(msg(id.address))) {
|
||||
m->sigs.push_back(sig);
|
||||
auto s = &m->sigs.last();
|
||||
sortSignalsByAddress(m->sigs);
|
||||
emit signalAdded(id.address, s);
|
||||
}
|
||||
}
|
||||
|
||||
void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const Signal &sig) {
|
||||
if (auto m = const_cast<DBCMsg *>(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);
|
||||
if (auto m = const_cast<Msg *>(msg(id))) {
|
||||
if (auto s = (Signal *)m->sig(sig_name)) {
|
||||
*s = sig;
|
||||
sortSignalsByAddress(m->sigs);
|
||||
emit signalUpdated(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) {
|
||||
if (auto m = const_cast<DBCMsg *>(msg(id))) {
|
||||
auto it = m->sigs.find(sig_name);
|
||||
if (auto m = const_cast<Msg *>(msg(id))) {
|
||||
auto it = std::find_if(m->sigs.begin(), m->sigs.end(), [&](auto &s) { return s.name == sig_name; });
|
||||
if (it != m->sigs.end()) {
|
||||
emit signalRemoved(&(it->second));
|
||||
emit signalRemoved(&(*it));
|
||||
m->sigs.erase(it);
|
||||
}
|
||||
}
|
||||
@@ -108,16 +157,6 @@ DBCManager *dbc() {
|
||||
return &dbc_manager;
|
||||
}
|
||||
|
||||
// DBCMsg
|
||||
|
||||
std::vector<const Signal*> DBCMsg::getSignals() const {
|
||||
std::vector<const Signal*> ret;
|
||||
ret.reserve(sigs.size());
|
||||
for (auto &[_, sig] : sigs) ret.push_back(&sig);
|
||||
std::sort(ret.begin(), ret.end(), [](auto l, auto r) { return l->start_bit < r->start_bit; });
|
||||
return ret;
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
static QVector<int> BIG_ENDIAN_START_BITS = []() {
|
||||
@@ -128,13 +167,8 @@ static QVector<int> BIG_ENDIAN_START_BITS = []() {
|
||||
return ret;
|
||||
}();
|
||||
|
||||
int bigEndianStartBitsIndex(int start_bit) {
|
||||
return BIG_ENDIAN_START_BITS[start_bit];
|
||||
}
|
||||
|
||||
int bigEndianBitIndex(int index) {
|
||||
return BIG_ENDIAN_START_BITS.indexOf(index);
|
||||
}
|
||||
int bigEndianStartBitsIndex(int start_bit) { return BIG_ENDIAN_START_BITS[start_bit]; }
|
||||
int bigEndianBitIndex(int index) { return BIG_ENDIAN_START_BITS.indexOf(index); }
|
||||
|
||||
double get_raw_value(uint8_t *data, size_t data_size, const Signal &sig) {
|
||||
int64_t val = 0;
|
||||
@@ -155,8 +189,7 @@ double get_raw_value(uint8_t *data, size_t data_size, const Signal &sig) {
|
||||
if (sig.is_signed) {
|
||||
val -= ((val >> (sig.size - 1)) & 0x1) ? (1ULL << sig.size) : 0;
|
||||
}
|
||||
double value = val * sig.factor + sig.offset;
|
||||
return value;
|
||||
return val * sig.factor + sig.offset;
|
||||
}
|
||||
|
||||
void updateSigSizeParamsFromRange(Signal &s, int start_bit, int size) {
|
||||
@@ -182,5 +215,50 @@ bool operator==(const Signal &l, const Signal &r) {
|
||||
l.start_bit == r.start_bit &&
|
||||
l.msb == r.msb && l.lsb == r.lsb &&
|
||||
l.is_signed == r.is_signed && l.is_little_endian == r.is_little_endian &&
|
||||
l.factor == r.factor && l.offset == r.offset;
|
||||
l.factor == r.factor && l.offset == r.offset &&
|
||||
l.min == r.min && l.max == r.max && l.comment == r.comment && l.unit == r.unit && l.val_desc == r.val_desc;
|
||||
}
|
||||
|
||||
} // namespace dbcmanager
|
||||
|
||||
#include "opendbc/can/common_dbc.h"
|
||||
std::vector<std::string> dbcmanager::DBCManager::allDBCNames() { return get_dbc_names(); }
|
||||
|
||||
bool dbcmanager::DBCManager::open(const QString &name, const QString &content, QString *error) {
|
||||
try {
|
||||
std::istringstream stream(content.toStdString());
|
||||
auto dbc = const_cast<DBC *>(dbc_parse_from_stream(name.toStdString(), stream));
|
||||
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.push_back({});
|
||||
auto &sig = m.sigs.last();
|
||||
sig.name = s.name.c_str();
|
||||
sig.start_bit = s.start_bit;
|
||||
sig.msb = s.msb;
|
||||
sig.lsb = s.lsb;
|
||||
sig.size = s.size;
|
||||
sig.is_signed = s.is_signed;
|
||||
sig.factor = s.factor;
|
||||
sig.offset = s.offset;
|
||||
sig.is_little_endian = s.is_little_endian;
|
||||
}
|
||||
sortSignalsByAddress(m.sigs);
|
||||
}
|
||||
parseExtraInfo(content);
|
||||
name_ = name;
|
||||
emit DBCFileChanged();
|
||||
delete dbc;
|
||||
} catch (std::exception &e) {
|
||||
if (error) *error = e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint qHash(const MessageId &item) {
|
||||
return qHash(item.source) ^ qHash(item.address);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include "opendbc/can/common_dbc.h"
|
||||
|
||||
struct MessageId {
|
||||
uint8_t source;
|
||||
@@ -30,40 +29,55 @@ struct MessageId {
|
||||
}
|
||||
};
|
||||
|
||||
uint qHash(const MessageId &item);
|
||||
Q_DECLARE_METATYPE(MessageId);
|
||||
|
||||
uint qHash(const MessageId &item);
|
||||
namespace dbcmanager {
|
||||
|
||||
struct DBCMsg {
|
||||
typedef QList<std::pair<QString, QString>> ValueDescription;
|
||||
|
||||
struct Signal {
|
||||
QString name;
|
||||
int start_bit, msb, lsb, size;
|
||||
bool is_signed;
|
||||
double factor, offset;
|
||||
bool is_little_endian;
|
||||
QString min, max, unit;
|
||||
QString comment;
|
||||
ValueDescription val_desc;
|
||||
};
|
||||
|
||||
struct Msg {
|
||||
QString name;
|
||||
uint32_t size;
|
||||
// signal must be saved as value in map to make undo stack work properly.
|
||||
std::map<QString, Signal> sigs;
|
||||
// return vector<signals>, sort by start_bits
|
||||
std::vector<const Signal*> getSignals() const;
|
||||
QList<Signal> sigs;
|
||||
|
||||
const Signal *sig(const QString &sig_name) const {
|
||||
auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s.name == sig_name; });
|
||||
return it != sigs.end() ? &(*it) : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class DBCManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DBCManager(QObject *parent);
|
||||
~DBCManager();
|
||||
|
||||
void open(const QString &dbc_file_name);
|
||||
DBCManager(QObject *parent) {}
|
||||
~DBCManager() {}
|
||||
bool open(const QString &dbc_file_name, QString *error = nullptr);
|
||||
bool open(const QString &name, const QString &content, QString *error = nullptr);
|
||||
QString generateDBC();
|
||||
void addSignal(const MessageId &id, const Signal &sig);
|
||||
void updateSignal(const MessageId &id, const QString &sig_name, const Signal &sig);
|
||||
void removeSignal(const MessageId &id, const QString &sig_name);
|
||||
|
||||
inline static std::vector<std::string> allDBCNames() { return get_dbc_names(); }
|
||||
inline QString name() const { return dbc ? dbc->name.c_str() : ""; }
|
||||
static std::vector<std::string> allDBCNames();
|
||||
inline QString name() const { return name_; }
|
||||
void updateMsg(const MessageId &id, const QString &name, uint32_t size);
|
||||
void removeMsg(const MessageId &id);
|
||||
inline const std::map<uint32_t, DBCMsg> &messages() const { return msgs; }
|
||||
inline const DBCMsg *msg(const MessageId &id) const { return msg(id.address); }
|
||||
inline const DBCMsg *msg(uint32_t address) const {
|
||||
inline const std::map<uint32_t, Msg> &messages() const { return msgs; }
|
||||
inline const Msg *msg(const MessageId &id) const { return msg(id.address); }
|
||||
inline const Msg *msg(uint32_t address) const {
|
||||
auto it = msgs.find(address);
|
||||
return it != msgs.end() ? &it->second : nullptr;
|
||||
}
|
||||
@@ -77,9 +91,9 @@ signals:
|
||||
void DBCFileChanged();
|
||||
|
||||
private:
|
||||
void initMsgMap();
|
||||
DBC *dbc = nullptr;
|
||||
std::map<uint32_t, DBCMsg> msgs;
|
||||
void parseExtraInfo(const QString &content);
|
||||
std::map<uint32_t, Msg> msgs;
|
||||
QString name_;
|
||||
};
|
||||
|
||||
const QString UNTITLED = "untitled";
|
||||
@@ -97,3 +111,5 @@ inline QString msgName(const MessageId &id) {
|
||||
auto msg = dbc()->msg(id);
|
||||
return msg ? msg->name : UNTITLED;
|
||||
}
|
||||
|
||||
} // namespace dbcmanager
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include <QToolButton>
|
||||
|
||||
#include "tools/cabana/commands.h"
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
|
||||
// DetailWidget
|
||||
|
||||
@@ -153,13 +151,13 @@ void DetailWidget::refresh() {
|
||||
if (!msg_id) return;
|
||||
|
||||
QStringList warnings;
|
||||
const DBCMsg *msg = dbc()->msg(*msg_id);
|
||||
auto msg = dbc()->msg(*msg_id);
|
||||
if (msg) {
|
||||
if (msg->size != can->lastMessage(*msg_id).dat.size()) {
|
||||
warnings.push_back(tr("Message size (%1) is incorrect.").arg(msg->size));
|
||||
}
|
||||
for (auto s : binary_view->getOverlappingSignals()) {
|
||||
warnings.push_back(tr("%1 has overlapping bits.").arg(s->name.c_str()));
|
||||
warnings.push_back(tr("%1 has overlapping bits.").arg(s->name));
|
||||
}
|
||||
} else {
|
||||
warnings.push_back(tr("Drag-Select in binary view to create new signal."));
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "tools/cabana/commands.h"
|
||||
#include "tools/cabana/util.h"
|
||||
|
||||
// HistoryLogModel
|
||||
|
||||
@@ -30,7 +31,7 @@ void HistoryLogModel::refresh() {
|
||||
beginResetModel();
|
||||
sigs.clear();
|
||||
if (auto dbc_msg = dbc()->msg(*msg_id)) {
|
||||
sigs = dbc_msg->getSignals();
|
||||
sigs = dbc_msg->sigs;
|
||||
}
|
||||
last_fetch_time = 0;
|
||||
has_more_data = true;
|
||||
@@ -47,9 +48,9 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i
|
||||
if (section == 0) {
|
||||
return "Time";
|
||||
}
|
||||
return show_signals ? QString::fromStdString(sigs[section - 1]->name).replace('_', ' ') : "Data";
|
||||
return show_signals ? sigs[section - 1].name : "Data";
|
||||
} else if (role == Qt::BackgroundRole && section > 0 && show_signals) {
|
||||
return QBrush(getColor(sigs[section - 1]));
|
||||
return QBrush(getColor(&sigs[section - 1]));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
@@ -113,7 +114,7 @@ std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData(InputIt first, I
|
||||
if (msg_id->address == c.getAddress() && msg_id->source == c.getSrc()) {
|
||||
const auto dat = c.getDat();
|
||||
for (int i = 0; i < sigs.size(); ++i) {
|
||||
values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), *(sigs[i]));
|
||||
values[i] = get_raw_value((uint8_t *)dat.begin(), dat.size(), sigs[i]);
|
||||
}
|
||||
if (!filter_cmp || filter_cmp(values[filter_sig_idx], filter_value)) {
|
||||
auto &m = msgs.emplace_back();
|
||||
@@ -170,8 +171,8 @@ QSize HeaderView::sectionSizeFromContents(int logicalIndex) const {
|
||||
return time_col_size;
|
||||
} else {
|
||||
int default_size = qMax(100, (rect().width() - time_col_size.width()) / (model()->columnCount() - 1));
|
||||
const QString text = model()->headerData(logicalIndex, this->orientation(), Qt::DisplayRole).toString();
|
||||
const QRect rect = fontMetrics().boundingRect({0, 0, default_size, 2000}, defaultAlignment(), text);
|
||||
QString text = model()->headerData(logicalIndex, this->orientation(), Qt::DisplayRole).toString();
|
||||
const QRect rect = fontMetrics().boundingRect({0, 0, default_size, 2000}, defaultAlignment(), text.replace(QChar('_'), ' '));
|
||||
QSize size = rect.size() + QSize{10, 6};
|
||||
return QSize{qMax(size.width(), default_size), size.height()};
|
||||
}
|
||||
@@ -183,7 +184,7 @@ void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalI
|
||||
painter->fillRect(rect, bg_role.value<QBrush>());
|
||||
}
|
||||
QString text = model()->headerData(logicalIndex, Qt::Horizontal, Qt::DisplayRole).toString();
|
||||
painter->drawText(rect.adjusted(5, 3, -5, -3), defaultAlignment(), text);
|
||||
painter->drawText(rect.adjusted(5, 3, -5, -3), defaultAlignment(), text.replace(QChar('_'), ' '));
|
||||
}
|
||||
|
||||
// LogsWidget
|
||||
@@ -253,8 +254,8 @@ void LogsWidget::refresh() {
|
||||
bool has_signal = model->sigs.size();
|
||||
if (has_signal) {
|
||||
signals_cb->clear();
|
||||
for (auto s : model->sigs) {
|
||||
signals_cb->addItem(s->name.c_str());
|
||||
for (auto &s : model->sigs) {
|
||||
signals_cb->addItem(s.name);
|
||||
}
|
||||
}
|
||||
value_edit->clear();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
class HeaderView : public QHeaderView {
|
||||
public:
|
||||
@@ -63,7 +64,7 @@ public:
|
||||
uint64_t last_fetch_time = 0;
|
||||
std::function<bool(double, double)> filter_cmp = nullptr;
|
||||
std::deque<Message> messages;
|
||||
std::vector<const Signal*> sigs;
|
||||
QList<Signal> sigs;
|
||||
bool dynamic_mode = true;
|
||||
bool display_signals_mode = true;
|
||||
};
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
|
||||
MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
|
||||
@@ -140,8 +138,8 @@ void MessageListModel::setFilterString(const QString &string) {
|
||||
if (id.toString().contains(txt, cs) || msgName(id).contains(txt, cs)) return true;
|
||||
// Search by signal name
|
||||
if (const auto msg = dbc()->msg(id)) {
|
||||
for (auto &signal : msg->getSignals()) {
|
||||
if (QString::fromStdString(signal->name).contains(txt, cs)) return true;
|
||||
for (auto &signal : msg->sigs) {
|
||||
if (signal.name.contains(txt, cs)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTableView>
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
class MessageListModel : public QAbstractTableModel {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "tools/cabana/signaledit.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGuiApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
@@ -23,9 +24,9 @@ SignalModel::SignalModel(QObject *parent) : root(new Item), QAbstractItemModel(p
|
||||
}
|
||||
|
||||
void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const Signal *sig) {
|
||||
Item *item = new Item{.sig = sig, .parent = parent_item, .title = sig->name.c_str(), .type = Item::Sig};
|
||||
Item *item = new Item{.sig = sig, .parent = parent_item, .title = sig->name, .type = Item::Sig};
|
||||
parent_item->children.insert(pos, item);
|
||||
QString titles[]{"Name", "Size", "Little Endian", "Signed", "Offset", "Factor", "Extra Info", "Unit", "Comment", "Minimum", "Maximum", "Description"};
|
||||
QString titles[]{"Name", "Size", "Little Endian", "Signed", "Offset", "Factor", "Extra Info", "Unit", "Comment", "Minimum Value", "Maximum Value", "Value Descriptions"};
|
||||
for (int i = 0; i < std::size(titles); ++i) {
|
||||
item->children.push_back(new Item{.sig = sig, .parent = item, .title = titles[i], .type = (Item::Type)(i + Item::Name)});
|
||||
}
|
||||
@@ -47,9 +48,9 @@ void SignalModel::refresh() {
|
||||
beginResetModel();
|
||||
root.reset(new SignalModel::Item);
|
||||
if (auto msg = dbc()->msg(msg_id)) {
|
||||
for (auto &s : msg->getSignals()) {
|
||||
if (filter_str.isEmpty() || QString::fromStdString(s->name).contains(filter_str, Qt::CaseInsensitive)) {
|
||||
insertItem(root.get(), root->children.size(), s);
|
||||
for (auto &s : msg->sigs) {
|
||||
if (filter_str.isEmpty() || s.name.contains(filter_str, Qt::CaseInsensitive)) {
|
||||
insertItem(root.get(), root->children.size(), &s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,14 +116,25 @@ QVariant SignalModel::data(const QModelIndex &index, int role) const {
|
||||
const Item *item = getItem(index);
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
if (index.column() == 0) {
|
||||
return item->type == Item::Sig ? QString::fromStdString(item->sig->name) : item->title;
|
||||
return item->type == Item::Sig ? item->sig->name : item->title;
|
||||
} else {
|
||||
switch (item->type) {
|
||||
case Item::Sig: return item->sig_val;
|
||||
case Item::Name: return QString::fromStdString(item->sig->name);
|
||||
case Item::Name: return item->sig->name;
|
||||
case Item::Size: return item->sig->size;
|
||||
case Item::Offset: return QString::number(item->sig->offset, 'f', 6);
|
||||
case Item::Factor: return QString::number(item->sig->factor, 'f', 6);
|
||||
case Item::Unit: return item->sig->unit;
|
||||
case Item::Comment: return item->sig->comment;
|
||||
case Item::Min: return item->sig->min;
|
||||
case Item::Max: return item->sig->max;
|
||||
case Item::Desc: {
|
||||
QString val_desc;
|
||||
for (auto &[val, desc] : item->sig->val_desc) {
|
||||
val_desc += QString("%1 \"%2\"").arg(val, desc);
|
||||
}
|
||||
return val_desc;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -142,12 +154,17 @@ bool SignalModel::setData(const QModelIndex &index, const QVariant &value, int r
|
||||
Item *item = getItem(index);
|
||||
Signal s = *item->sig;
|
||||
switch (item->type) {
|
||||
case Item::Name: s.name = value.toString().toStdString(); break;
|
||||
case Item::Name: s.name = value.toString(); break;
|
||||
case Item::Size: s.size = value.toInt(); break;
|
||||
case Item::Endian: s.is_little_endian = value.toBool(); break;
|
||||
case Item::Signed: s.is_signed = value.toBool(); break;
|
||||
case Item::Offset: s.offset = value.toDouble(); break;
|
||||
case Item::Factor: s.factor = value.toDouble(); break;
|
||||
case Item::Unit: s.unit = value.toString(); break;
|
||||
case Item::Comment: s.comment = value.toString(); break;
|
||||
case Item::Min: s.min = value.toString(); break;
|
||||
case Item::Max: s.max = value.toString(); break;
|
||||
case Item::Desc: s.val_desc = value.value<ValueDescription>(); break;
|
||||
default: return false;
|
||||
}
|
||||
bool ret = saveSignal(item->sig, s);
|
||||
@@ -172,8 +189,8 @@ void SignalModel::showExtraInfo(const QModelIndex &index) {
|
||||
|
||||
bool SignalModel::saveSignal(const Signal *origin_s, Signal &s) {
|
||||
auto msg = dbc()->msg(msg_id);
|
||||
if (s.name != origin_s->name && msg->sigs.count(s.name.c_str()) != 0) {
|
||||
QString text = tr("There is already a signal with the same name '%1'").arg(s.name.c_str());
|
||||
if (s.name != origin_s->name && msg->sig(s.name) != nullptr) {
|
||||
QString text = tr("There is already a signal with the same name '%1'").arg(s.name);
|
||||
QMessageBox::warning(nullptr, tr("Failed to save signal"), text);
|
||||
return false;
|
||||
}
|
||||
@@ -212,8 +229,8 @@ void SignalModel::addSignal(int start_bit, int size, bool little_endian) {
|
||||
|
||||
Signal sig = {.is_little_endian = little_endian, .factor = 1};
|
||||
for (int i = 1; /**/; ++i) {
|
||||
sig.name = "NEW_SIGNAL_" + std::to_string(i);
|
||||
if (msg->sigs.count(sig.name.c_str()) == 0) break;
|
||||
sig.name = QString("NEW_SIGNAL_%1").arg(i);
|
||||
if (msg->sig(sig.name) == nullptr) break;
|
||||
}
|
||||
updateSigSizeParamsFromRange(sig, start_bit, size);
|
||||
UndoStack::push(new AddSigCommand(msg_id, sig));
|
||||
@@ -266,8 +283,8 @@ void SignalModel::handleSignalRemoved(const Signal *sig) {
|
||||
SignalItemDelegate::SignalItemDelegate(QObject *parent) {
|
||||
name_validator = new NameValidator(this);
|
||||
double_validator = new QDoubleValidator(this);
|
||||
small_font.setPointSize(8);
|
||||
double_validator->setLocale(QLocale::C); // Match locale of QString::toDouble() instead of system
|
||||
small_font.setPointSize(8);
|
||||
}
|
||||
|
||||
void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||
@@ -304,7 +321,8 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
||||
|
||||
QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||
auto item = (SignalModel::Item *)index.internalPointer();
|
||||
if (item->type == SignalModel::Item::Name || item->type == SignalModel::Item::Offset || item->type == SignalModel::Item::Factor) {
|
||||
if (item->type == SignalModel::Item::Name || item->type == SignalModel::Item::Offset ||
|
||||
item->type == SignalModel::Item::Factor || item->type == SignalModel::Item::Min || item->type == SignalModel::Item::Max) {
|
||||
QLineEdit *e = new QLineEdit(parent);
|
||||
e->setFrame(false);
|
||||
e->setValidator(index.row() == 0 ? name_validator : double_validator);
|
||||
@@ -314,6 +332,13 @@ QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionVie
|
||||
spin->setFrame(false);
|
||||
spin->setRange(1, 64);
|
||||
return spin;
|
||||
} else if (item->type == SignalModel::Item::Desc) {
|
||||
ValueDescriptionDlg dlg(item->sig->val_desc, parent);
|
||||
dlg.setWindowTitle(item->sig->name);
|
||||
if (dlg.exec()) {
|
||||
((QAbstractItemModel *)index.model())->setData(index, QVariant::fromValue(dlg.val_desc));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return QStyledItemDelegate::createEditor(parent, option, index);
|
||||
}
|
||||
@@ -437,7 +462,7 @@ void SignalView::expandSignal(const Signal *sig) {
|
||||
void SignalView::updateChartState() {
|
||||
int i = 0;
|
||||
for (auto item : model->root->children) {
|
||||
auto plot_btn = tree->indexWidget(model->index(i, 1))->findChildren<QToolButton*>()[0];
|
||||
auto plot_btn = tree->indexWidget(model->index(i, 1))->findChildren<QToolButton *>()[0];
|
||||
bool chart_opened = charts->hasSignal(msg_id, item->sig);
|
||||
plot_btn->setChecked(chart_opened);
|
||||
plot_btn->setToolTip(chart_opened ? tr("Close Plot") : tr("Show Plot\nSHIFT click to add to previous opened plot"));
|
||||
@@ -459,3 +484,70 @@ void SignalView::leaveEvent(QEvent *event) {
|
||||
emit highlight(nullptr);
|
||||
QWidget::leaveEvent(event);
|
||||
}
|
||||
|
||||
// ValueDescriptionDlg
|
||||
|
||||
ValueDescriptionDlg::ValueDescriptionDlg(const ValueDescription &descriptions, QWidget *parent) : QDialog(parent) {
|
||||
QHBoxLayout *toolbar_layout = new QHBoxLayout();
|
||||
QPushButton *add = new QPushButton(utils::icon("plus"), "");
|
||||
QPushButton *remove = new QPushButton(utils::icon("dash"), "");
|
||||
remove->setEnabled(false);
|
||||
toolbar_layout->addWidget(add);
|
||||
toolbar_layout->addWidget(remove);
|
||||
toolbar_layout->addStretch(0);
|
||||
|
||||
table = new QTableWidget(descriptions.size(), 2, this);
|
||||
table->setItemDelegate(new Delegate(this));
|
||||
table->setHorizontalHeaderLabels({"Value", "Description"});
|
||||
table->horizontalHeader()->setStretchLastSection(true);
|
||||
table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
table->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
table->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
|
||||
table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
int row = 0;
|
||||
for (auto &[val, desc] : descriptions) {
|
||||
table->setItem(row, 0, new QTableWidgetItem(val));
|
||||
table->setItem(row, 1, new QTableWidgetItem(desc));
|
||||
++row;
|
||||
}
|
||||
|
||||
auto btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->addLayout(toolbar_layout);
|
||||
main_layout->addWidget(table);
|
||||
main_layout->addWidget(btn_box);
|
||||
setMinimumWidth(500);
|
||||
|
||||
QObject::connect(btn_box, &QDialogButtonBox::accepted, this, &ValueDescriptionDlg::save);
|
||||
QObject::connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
QObject::connect(add, &QPushButton::clicked, [this]() {
|
||||
table->setRowCount(table->rowCount() + 1);
|
||||
table->setItem(table->rowCount() - 1, 0, new QTableWidgetItem);
|
||||
table->setItem(table->rowCount() - 1, 1, new QTableWidgetItem);
|
||||
});
|
||||
QObject::connect(remove, &QPushButton::clicked, [this]() { table->removeRow(table->currentRow()); });
|
||||
QObject::connect(table, &QTableWidget::itemSelectionChanged, [=]() {
|
||||
remove->setEnabled(table->currentRow() != -1);
|
||||
});
|
||||
}
|
||||
|
||||
void ValueDescriptionDlg::save() {
|
||||
for (int i = 0; i < table->rowCount(); ++i) {
|
||||
QString val = table->item(i, 0)->text().trimmed();
|
||||
QString desc = table->item(i, 1)->text().trimmed();
|
||||
if (!val.isEmpty() && !desc.isEmpty()) {
|
||||
val_desc.push_back({val, desc});
|
||||
}
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
QWidget *ValueDescriptionDlg::Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||
QLineEdit *edit = new QLineEdit(parent);
|
||||
edit->setFrame(false);
|
||||
if (index.column() == 0) {
|
||||
edit->setValidator(new QIntValidator(edit));
|
||||
}
|
||||
return edit;
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTableWidget>
|
||||
#include <QTreeView>
|
||||
|
||||
#include "tools/cabana/chartswidget.h"
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
|
||||
class SignalModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
@@ -62,6 +61,21 @@ private:
|
||||
friend class SignalView;
|
||||
};
|
||||
|
||||
class ValueDescriptionDlg : public QDialog {
|
||||
public:
|
||||
ValueDescriptionDlg(const ValueDescription &descriptions, QWidget *parent);
|
||||
ValueDescription val_desc;
|
||||
|
||||
private:
|
||||
struct Delegate : public QStyledItemDelegate {
|
||||
Delegate(QWidget *parent) : QStyledItemDelegate(parent) {}
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
void save();
|
||||
QTableWidget *table;
|
||||
};
|
||||
|
||||
class SignalItemDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
SignalItemDelegate(QObject *parent);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "tools/cabana/streams/replaystream.h"
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
|
||||
ReplayStream::ReplayStream(uint32_t replay_flags, QObject *parent) : replay_flags(replay_flags), AbstractStream(parent, false) {
|
||||
QObject::connect(&settings, &Settings::changed, [this]() {
|
||||
if (replay) replay->setSegmentCacheLimit(settings.max_cached_minutes);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "opendbc/can/common_dbc.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
#include "tools/cabana/settings.h"
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
#include "opendbc/can/common.h"
|
||||
#undef INFO
|
||||
#include "catch2/catch.hpp"
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/replay/logreader.h"
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
// demo route, first segment
|
||||
const std::string TEST_RLOG_URL = "https://commadata2.blob.core.windows.net/commadata2/4cf7a6ad03080c90/2021-09-29--13-46-36/0/rlog.bz2";
|
||||
@@ -18,12 +20,13 @@ TEST_CASE("DBCManager::generateDBC") {
|
||||
auto &new_msgs = dbc_from_generated.messages();
|
||||
REQUIRE(msgs.size() == new_msgs.size());
|
||||
for (auto &[address, m] : msgs) {
|
||||
auto new_m = new_msgs.at(address);
|
||||
auto &new_m = new_msgs.at(address);
|
||||
REQUIRE(m.name == new_m.name);
|
||||
REQUIRE(m.size == new_m.size);
|
||||
REQUIRE(m.sigs.size() == new_m.sigs.size());
|
||||
for (auto &[name, sig] : m.sigs)
|
||||
REQUIRE(sig == new_m.sigs[name]);
|
||||
for (int i = 0; i < m.sigs.size(); ++i) {
|
||||
REQUIRE(m.sigs[i] == new_m.sigs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,13 +40,13 @@ TEST_CASE("Parse can messages") {
|
||||
REQUIRE(log.events.size() > 0);
|
||||
for (auto e : log.events) {
|
||||
if (e->which == cereal::Event::Which::CAN) {
|
||||
std::map<std::pair<uint32_t, std::string>, std::vector<double>> values_1;
|
||||
std::map<std::pair<uint32_t, QString>, std::vector<double>> values_1;
|
||||
for (const auto &c : e->event.getCan()) {
|
||||
const auto msg = dbc.msg(c.getAddress());
|
||||
if (c.getSrc() == 0 && msg) {
|
||||
for (auto &[name, sig] : msg->sigs) {
|
||||
for (auto &sig : msg->sigs) {
|
||||
double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), sig);
|
||||
values_1[{c.getAddress(), name.toStdString()}].push_back(val);
|
||||
values_1[{c.getAddress(), sig.name}].push_back(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +56,7 @@ TEST_CASE("Parse can messages") {
|
||||
for (auto &[key, v1] : values_1) {
|
||||
bool found = false;
|
||||
for (auto &v2 : values_2) {
|
||||
if (v2.address == key.first && v2.name == key.second) {
|
||||
if (v2.address == key.first && key.second == v2.name.c_str()) {
|
||||
REQUIRE(v2.all_values.size() == v1.size());
|
||||
REQUIRE(v2.all_values == v1);
|
||||
found = true;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::WindowFlags() | Qt::Window) {
|
||||
setWindowTitle(tr("Find similar bits"));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QTableWidget>
|
||||
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
class FindSimilarBitsDlg : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -106,7 +106,7 @@ QColor getColor(const Signal *sig) {
|
||||
float h = 19 * (float)sig->lsb / 64.0;
|
||||
h = fmod(h, 1.0);
|
||||
|
||||
size_t hash = qHash(QString::fromStdString(sig->name));
|
||||
size_t hash = qHash(sig->name);
|
||||
float s = 0.25 + 0.25 * (float)(hash & 0xff) / 255.0;
|
||||
float v = 0.75 + 0.25 * (float)((hash >> 8) & 0xff) / 255.0;
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QVector>
|
||||
|
||||
#include "opendbc/can/common_dbc.h"
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
|
||||
class ChangeTracker {
|
||||
public:
|
||||
@@ -37,7 +39,7 @@ public:
|
||||
|
||||
inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); }
|
||||
inline char toHex(uint value) { return "0123456789ABCDEF"[value & 0xF]; }
|
||||
QColor getColor(const Signal *sig);
|
||||
QColor getColor(const dbcmanager::Signal *sig);
|
||||
|
||||
class NameValidator : public QRegExpValidator {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
#include "selfdrive/ui/qt/widgets/cameraview.h"
|
||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||
#include "tools/cabana/dbcmanager.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
using namespace dbcmanager;
|
||||
|
||||
class Slider : public QSlider {
|
||||
Q_OBJECT
|
||||
|
||||
Reference in New Issue
Block a user