mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-07-07 22:12:05 +08:00
cabana: refactor dbc manager (#28445)
* rafactor signal * cleanup * refacto dbcmanager * refactor dbcmanger
This commit is contained in:
@@ -165,7 +165,7 @@ void BinaryView::mousePressEvent(QMouseEvent *event) {
|
||||
if (bit_idx == s->lsb || bit_idx == s->msb) {
|
||||
anchor_index = model->bitIndex(bit_idx == s->lsb ? s->msb : s->lsb, true);
|
||||
resize_sig = s;
|
||||
delegate->selection_color = getColor(s);
|
||||
delegate->selection_color = s->color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -381,7 +381,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
||||
if (item->sigs.size() > 0) {
|
||||
for (auto &s : item->sigs) {
|
||||
if (s == bin_view->hovered_sig) {
|
||||
painter->fillRect(option.rect, getColor(bin_view->hovered_sig).darker(125)); // 4/5x brightness
|
||||
painter->fillRect(option.rect, s->color.darker(125)); // 4/5x brightness
|
||||
} else {
|
||||
drawSignalCell(painter, option, index, s);
|
||||
}
|
||||
@@ -434,15 +434,14 @@ void BinaryItemDelegate::drawSignalCell(QPainter *painter, const QStyleOptionVie
|
||||
painter->setClipRegion(QRegion(rc).subtracted(subtract));
|
||||
|
||||
auto item = (const BinaryViewModel::Item *)index.internalPointer();
|
||||
auto sig_color = getColor(sig);
|
||||
QColor color = sig_color;
|
||||
QColor color = sig->color;
|
||||
color.setAlpha(item->bg_color.alpha());
|
||||
// Mixing the signal colour with the Base background color to fade it
|
||||
painter->fillRect(rc, QApplication::palette().color(QPalette::Base));
|
||||
painter->fillRect(rc, color);
|
||||
|
||||
// Draw edges
|
||||
color = sig_color.darker(125);
|
||||
color = sig->color.darker(125);
|
||||
painter->setPen(QPen(color, 1));
|
||||
if (draw_left) painter->drawLine(rc.topLeft(), rc.bottomLeft());
|
||||
if (draw_right) painter->drawLine(rc.topRight(), rc.bottomRight());
|
||||
|
||||
@@ -38,7 +38,6 @@ ChartView::ChartView(const std::pair<double, double> &x_range, ChartsWidget *par
|
||||
setChart(chart);
|
||||
|
||||
createToolButtons();
|
||||
// TODO: enable zoomIn/seekTo in live streaming mode.
|
||||
setRubberBand(QChartView::HorizontalRubberBand);
|
||||
setMouseTracking(true);
|
||||
setTheme(settings.theme == DARK_THEME ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight);
|
||||
@@ -107,14 +106,14 @@ void ChartView::setTheme(QChart::ChartTheme theme) {
|
||||
chart()->legend()->setLabelColor(palette().color(QPalette::Text));
|
||||
}
|
||||
for (auto &s : sigs) {
|
||||
s.series->setColor(getColor(s.sig));
|
||||
s.series->setColor(s.sig->color);
|
||||
}
|
||||
}
|
||||
|
||||
void ChartView::addSignal(const MessageId &msg_id, const cabana::Signal *sig) {
|
||||
if (hasSignal(msg_id, sig)) return;
|
||||
|
||||
QXYSeries *series = createSeries(series_type, getColor(sig));
|
||||
QXYSeries *series = createSeries(series_type, sig->color);
|
||||
sigs.push_back({.msg_id = msg_id, .sig = sig, .series = series});
|
||||
updateSeries(sig);
|
||||
updateSeriesPoints();
|
||||
@@ -154,8 +153,9 @@ void ChartView::signalUpdated(const cabana::Signal *sig) {
|
||||
}
|
||||
|
||||
void ChartView::msgUpdated(MessageId id) {
|
||||
if (std::any_of(sigs.cbegin(), sigs.cend(), [=](auto &s) { return s.msg_id == id; }))
|
||||
if (std::any_of(sigs.cbegin(), sigs.cend(), [=](auto &s) { return s.msg_id.address == id.address; })) {
|
||||
updateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
void ChartView::manageSignals() {
|
||||
@@ -277,7 +277,7 @@ void ChartView::updateSeries(const cabana::Signal *sig, bool clear) {
|
||||
s.step_vals.clear();
|
||||
s.last_value_mono_time = 0;
|
||||
}
|
||||
s.series->setColor(getColor(s.sig));
|
||||
s.series->setColor(s.sig->color);
|
||||
|
||||
const auto &msgs = can->events(s.msg_id);
|
||||
s.vals.reserve(msgs.capacity());
|
||||
@@ -799,7 +799,7 @@ void ChartView::setSeriesType(SeriesType type) {
|
||||
s.series->deleteLater();
|
||||
}
|
||||
for (auto &s : sigs) {
|
||||
auto series = createSeries(series_type, getColor(s.sig));
|
||||
auto series = createSeries(series_type, s.sig->color);
|
||||
series->replace(series_type == SeriesType::StepLine ? s.step_vals : s.vals);
|
||||
s.series = series;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ private slots:
|
||||
void manageSignals();
|
||||
void handleMarkerClicked();
|
||||
void msgUpdated(MessageId id);
|
||||
void msgRemoved(MessageId id) { removeIf([=](auto &s) { return s.msg_id == id; }); }
|
||||
void msgRemoved(MessageId id) { removeIf([=](auto &s) { return s.msg_id.address == id.address && !dbc()->msg(id); }); }
|
||||
void signalRemoved(const cabana::Signal *sig) { removeIf([=](auto &s) { return s.sig == sig; }); }
|
||||
|
||||
private:
|
||||
|
||||
@@ -91,7 +91,7 @@ void SignalSelector::updateAvailableList(int index) {
|
||||
}
|
||||
|
||||
void SignalSelector::addItemToList(QListWidget *parent, const MessageId id, const cabana::Signal *sig, bool show_msg_name) {
|
||||
QString text = QString("<span style=\"color:%0;\">■ </span> %1").arg(getColor(sig).name(), sig->name);
|
||||
QString text = QString("<span style=\"color:%0;\">■ </span> %1").arg(sig->color.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);
|
||||
|
||||
@@ -37,7 +37,7 @@ void Sparkline::update(const MessageId &msg_id, const cabana::Signal *sig, doubl
|
||||
max_val += 1;
|
||||
}
|
||||
}
|
||||
render(getColor(sig), size);
|
||||
render(sig->color, size);
|
||||
} else {
|
||||
pixmap = QPixmap();
|
||||
min_val = -1;
|
||||
|
||||
+93
-20
@@ -1,32 +1,96 @@
|
||||
#include "tools/cabana/dbc/dbc.h"
|
||||
|
||||
#include "tools/cabana/util.h"
|
||||
|
||||
uint qHash(const MessageId &item) {
|
||||
return qHash(item.source) ^ qHash(item.address);
|
||||
}
|
||||
|
||||
std::vector<const cabana::Signal*> cabana::Msg::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) {
|
||||
if (l->start_bit != r->start_bit) {
|
||||
return l->start_bit < r->start_bit;
|
||||
}
|
||||
// For VECTOR__INDEPENDENT_SIG_MSG, many signals have same start bit
|
||||
return l->name < r->name;
|
||||
});
|
||||
return ret;
|
||||
// cabana::Msg
|
||||
|
||||
cabana::Msg::~Msg() {
|
||||
for (auto s : sigs) {
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
|
||||
void cabana::Msg::updateMask() {
|
||||
cabana::Signal *cabana::Msg::addSignal(const cabana::Signal &sig) {
|
||||
auto s = sigs.emplace_back(new cabana::Signal(sig));
|
||||
update();
|
||||
return s;
|
||||
}
|
||||
|
||||
cabana::Signal *cabana::Msg::updateSignal(const QString &sig_name, const cabana::Signal &new_sig) {
|
||||
auto s = sig(sig_name);
|
||||
if (s) {
|
||||
*s = new_sig;
|
||||
update();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void cabana::Msg::removeSignal(const QString &sig_name) {
|
||||
auto it = std::find_if(sigs.begin(), sigs.end(), [&](auto &s) { return s->name == sig_name; });
|
||||
if (it != sigs.end()) {
|
||||
delete *it;
|
||||
sigs.erase(it);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
cabana::Msg &cabana::Msg::operator=(const cabana::Msg &other) {
|
||||
address = other.address;
|
||||
name = other.name;
|
||||
size = other.size;
|
||||
comment = other.comment;
|
||||
|
||||
for (auto s : sigs) delete s;
|
||||
sigs.clear();
|
||||
for (auto s : other.sigs) {
|
||||
sigs.push_back(new cabana::Signal(*s));
|
||||
}
|
||||
|
||||
update();
|
||||
return *this;
|
||||
}
|
||||
|
||||
cabana::Signal *cabana::Msg::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;
|
||||
}
|
||||
|
||||
int cabana::Msg::indexOf(const cabana::Signal *sig) const {
|
||||
for (int i = 0; i < sigs.size(); ++i) {
|
||||
if (sigs[i] == sig) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString cabana::Msg::newSignalName() {
|
||||
QString new_name;
|
||||
for (int i = 1; /**/; ++i) {
|
||||
new_name = QString("NEW_SIGNAL_%1").arg(i);
|
||||
if (sig(new_name) == nullptr) break;
|
||||
}
|
||||
return new_name;
|
||||
}
|
||||
|
||||
void cabana::Msg::update() {
|
||||
mask = QVector<uint8_t>(size, 0x00).toList();
|
||||
// sort signals
|
||||
std::sort(sigs.begin(), sigs.end(), [](auto l, auto r) {
|
||||
return std::tie(l->start_bit, l->name) < std::tie(r->start_bit, r->name);
|
||||
});
|
||||
|
||||
for (auto &sig : sigs) {
|
||||
int i = sig.msb / 8;
|
||||
int bits = sig.size;
|
||||
sig->update();
|
||||
|
||||
// update mask
|
||||
int i = sig->msb / 8;
|
||||
int bits = sig->size;
|
||||
while (i >= 0 && i < size && bits > 0) {
|
||||
int lsb = (int)(sig.lsb / 8) == i ? sig.lsb : i * 8;
|
||||
int msb = (int)(sig.msb / 8) == i ? sig.msb : (i + 1) * 8 - 1;
|
||||
int lsb = (int)(sig->lsb / 8) == i ? sig->lsb : i * 8;
|
||||
int msb = (int)(sig->msb / 8) == i ? sig->msb : (i + 1) * 8 - 1;
|
||||
|
||||
int sz = msb - lsb + 1;
|
||||
int shift = (lsb - (i * 8));
|
||||
@@ -34,18 +98,27 @@ void cabana::Msg::updateMask() {
|
||||
mask[i] |= ((1ULL << sz) - 1) << shift;
|
||||
|
||||
bits -= size;
|
||||
i = sig.is_little_endian ? i - 1 : i + 1;
|
||||
i = sig->is_little_endian ? i - 1 : i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cabana::Signal::updatePrecision() {
|
||||
// cabana::Signal
|
||||
|
||||
void cabana::Signal::update() {
|
||||
float h = 19 * (float)lsb / 64.0;
|
||||
h = fmod(h, 1.0);
|
||||
size_t hash = qHash(name);
|
||||
float s = 0.25 + 0.25 * (float)(hash & 0xff) / 255.0;
|
||||
float v = 0.75 + 0.25 * (float)((hash >> 8) & 0xff) / 255.0;
|
||||
|
||||
color = QColor::fromHsvF(h, s, v);
|
||||
precision = std::max(num_decimals(factor), num_decimals(offset));
|
||||
}
|
||||
|
||||
QString cabana::Signal::formatValue(double value) const {
|
||||
// Show enum string
|
||||
for (auto &[val, desc] : val_desc) {
|
||||
for (const auto &[val, desc] : val_desc) {
|
||||
if (std::abs(value - val) < 1e-6) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
+47
-33
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <QColor>
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <limits>
|
||||
|
||||
#include "opendbc/can/common_dbc.h"
|
||||
|
||||
@@ -45,41 +46,55 @@ struct std::hash<MessageId> {
|
||||
typedef QList<std::pair<double, QString>> ValueDescription;
|
||||
|
||||
namespace cabana {
|
||||
struct Signal {
|
||||
QString name;
|
||||
int start_bit, msb, lsb, size;
|
||||
bool is_signed;
|
||||
double factor, offset;
|
||||
bool is_little_endian;
|
||||
double min, max;
|
||||
QString unit;
|
||||
QString comment;
|
||||
ValueDescription val_desc;
|
||||
int precision = 0;
|
||||
void updatePrecision();
|
||||
QString formatValue(double value) const;
|
||||
};
|
||||
|
||||
struct Msg {
|
||||
uint32_t address;
|
||||
QString name;
|
||||
uint32_t size;
|
||||
QString comment;
|
||||
QList<cabana::Signal> sigs;
|
||||
class Signal {
|
||||
public:
|
||||
Signal() = default;
|
||||
Signal(const Signal &other) = default;
|
||||
void update();
|
||||
QString formatValue(double value) const;
|
||||
|
||||
QList<uint8_t> mask;
|
||||
void updateMask();
|
||||
QString name;
|
||||
int start_bit, msb, lsb, size;
|
||||
double factor, offset;
|
||||
bool is_signed;
|
||||
bool is_little_endian;
|
||||
double min, max;
|
||||
QString unit;
|
||||
QString comment;
|
||||
ValueDescription val_desc;
|
||||
int precision = 0;
|
||||
QColor color;
|
||||
};
|
||||
|
||||
std::vector<const cabana::Signal*> getSignals() const;
|
||||
const cabana::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 Msg {
|
||||
public:
|
||||
Msg() = default;
|
||||
Msg(const Msg &other) { *this = other; }
|
||||
~Msg();
|
||||
cabana::Signal *addSignal(const cabana::Signal &sig);
|
||||
cabana::Signal *updateSignal(const QString &sig_name, const cabana::Signal &sig);
|
||||
void removeSignal(const QString &sig_name);
|
||||
Msg &operator=(const Msg &other);
|
||||
int indexOf(const cabana::Signal *sig) const;
|
||||
cabana::Signal *sig(const QString &sig_name) const;
|
||||
QString newSignalName();
|
||||
inline const std::vector<cabana::Signal *> &getSignals() const { return sigs; }
|
||||
|
||||
bool operator==(const cabana::Signal &l, const cabana::Signal &r);
|
||||
inline bool operator!=(const cabana::Signal &l, const cabana::Signal &r) { return !(l == r); }
|
||||
}
|
||||
uint32_t address;
|
||||
QString name;
|
||||
uint32_t size;
|
||||
QString comment;
|
||||
std::vector<cabana::Signal *> sigs;
|
||||
|
||||
QList<uint8_t> mask;
|
||||
void update();
|
||||
};
|
||||
|
||||
bool operator==(const cabana::Signal &l, const cabana::Signal &r);
|
||||
inline bool operator!=(const cabana::Signal &l, const cabana::Signal &r) { return !(l == r); }
|
||||
|
||||
} // namespace cabana
|
||||
|
||||
// Helper functions
|
||||
double get_raw_value(const uint8_t *data, size_t data_size, const cabana::Signal &sig);
|
||||
@@ -89,4 +104,3 @@ void updateSigSizeParamsFromRange(cabana::Signal &s, int start_bit, int size);
|
||||
std::pair<int, int> getSignalRange(const cabana::Signal *s);
|
||||
inline std::vector<std::string> allDBCNames() { return get_dbc_names(); }
|
||||
inline QString doubleToString(double value) { return QString::number(value, 'g', std::numeric_limits<double>::digits10); }
|
||||
|
||||
|
||||
+19
-99
@@ -37,20 +37,18 @@ void DBCFile::open(const QString &content) {
|
||||
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;
|
||||
sig.updatePrecision();
|
||||
auto sig = m.sigs.emplace_back(new cabana::Signal);
|
||||
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;
|
||||
}
|
||||
m.updateMask();
|
||||
m.update();
|
||||
}
|
||||
parseExtraInfo(content);
|
||||
|
||||
@@ -58,7 +56,7 @@ void DBCFile::open(const QString &content) {
|
||||
}
|
||||
|
||||
bool DBCFile::save() {
|
||||
assert (!filename.isEmpty());
|
||||
assert(!filename.isEmpty());
|
||||
if (writeContents(filename)) {
|
||||
cleanupAutoSaveFile();
|
||||
return true;
|
||||
@@ -90,41 +88,6 @@ bool DBCFile::writeContents(const QString &fn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cabana::Signal *DBCFile::addSignal(const MessageId &id, const cabana::Signal &sig) {
|
||||
if (auto m = const_cast<cabana::Msg *>(msg(id.address))) {
|
||||
m->sigs.push_back(sig);
|
||||
m->updateMask();
|
||||
return &m->sigs.last();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cabana::Signal *DBCFile::updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig) {
|
||||
if (auto m = const_cast<cabana::Msg *>(msg(id))) {
|
||||
if (auto s = (cabana::Signal *)m->sig(sig_name)) {
|
||||
*s = sig;
|
||||
m->updateMask();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cabana::Signal *DBCFile::getSignal(const MessageId &id, const QString &sig_name) {
|
||||
auto m = msg(id);
|
||||
return m ? (cabana::Signal *)m->sig(sig_name) : nullptr;
|
||||
}
|
||||
|
||||
void DBCFile::removeSignal(const MessageId &id, const QString &sig_name) {
|
||||
if (auto m = const_cast<cabana::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()) {
|
||||
m->sigs.erase(it);
|
||||
m->updateMask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBCFile::updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment) {
|
||||
auto &m = msgs[id.address];
|
||||
m.address = id.address;
|
||||
@@ -133,60 +96,17 @@ void DBCFile::updateMsg(const MessageId &id, const QString &name, uint32_t size,
|
||||
m.comment = comment;
|
||||
}
|
||||
|
||||
void DBCFile::removeMsg(const MessageId &id) {
|
||||
msgs.erase(id.address);
|
||||
}
|
||||
|
||||
QString DBCFile::newMsgName(const MessageId &id) {
|
||||
return QString("NEW_MSG_") + QString::number(id.address, 16).toUpper();
|
||||
}
|
||||
|
||||
QString DBCFile::newSignalName(const MessageId &id) {
|
||||
auto m = msg(id);
|
||||
assert(m != nullptr);
|
||||
|
||||
QString name;
|
||||
for (int i = 1; /**/; ++i) {
|
||||
name = QString("NEW_SIGNAL_%1").arg(i);
|
||||
if (m->sig(name) == nullptr) break;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
const QList<uint8_t>& DBCFile::mask(const MessageId &id) const {
|
||||
auto m = msg(id);
|
||||
return m ? m->mask : empty_mask;
|
||||
}
|
||||
|
||||
const cabana::Msg *DBCFile::msg(uint32_t address) const {
|
||||
cabana::Msg *DBCFile::msg(uint32_t address) {
|
||||
auto it = msgs.find(address);
|
||||
return it != msgs.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
const cabana::Msg* DBCFile::msg(const QString &name) {
|
||||
auto it = std::find_if(msgs.cbegin(), msgs.cend(), [&name](auto &m) { return m.second.name == name; });
|
||||
return it != msgs.cend() ? &(it->second) : nullptr;
|
||||
cabana::Msg *DBCFile::msg(const QString &name) {
|
||||
auto it = std::find_if(msgs.begin(), msgs.end(), [&name](auto &m) { return m.second.name == name; });
|
||||
return it != msgs.end() ? &(it->second) : nullptr;
|
||||
}
|
||||
|
||||
QStringList DBCFile::signalNames() const {
|
||||
// Used for autocompletion
|
||||
QStringList ret;
|
||||
for (auto const& [_, msg] : msgs) {
|
||||
for (auto sig: msg.getSignals()) {
|
||||
ret << sig->name;
|
||||
}
|
||||
}
|
||||
ret.sort();
|
||||
ret.removeDuplicates();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int DBCFile::signalCount(const MessageId &id) const {
|
||||
if (msgs.count(id.address) == 0) return 0;
|
||||
return msgs.at(id.address).sigs.size();
|
||||
}
|
||||
|
||||
int DBCFile::signalCount() const {
|
||||
int DBCFile::signalCount() {
|
||||
return std::accumulate(msgs.cbegin(), msgs.cend(), 0, [](int &n, const auto &m) { return n + m.second.sigs.size(); });
|
||||
}
|
||||
|
||||
@@ -194,8 +114,8 @@ void DBCFile::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 msg_comment_regexp(R"(^CM_ BO_ *(\w+) *\"([^"]*)\";)");
|
||||
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"([^"]*)\";)");
|
||||
static QRegularExpression msg_comment_regexp(R"(^CM_ BO_ *(\w+) *\"([^"]*)\"\s*;)");
|
||||
static QRegularExpression sg_comment_regexp(R"(^CM_ SG_ *(\w+) *(\w+) *\"([^"]*)\"\s*;)");
|
||||
static QRegularExpression val_regexp(R"(VAL_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*))");
|
||||
|
||||
int line_num = 0;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "tools/cabana/dbc/dbc.h"
|
||||
|
||||
@@ -26,30 +24,18 @@ public:
|
||||
void cleanupAutoSaveFile();
|
||||
QString generateDBC();
|
||||
|
||||
cabana::Signal *addSignal(const MessageId &id, const cabana::Signal &sig);
|
||||
cabana::Signal *updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig);
|
||||
cabana::Signal *getSignal(const MessageId &id, const QString &sig_name);
|
||||
void removeSignal(const MessageId &id, const QString &sig_name);
|
||||
|
||||
void updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment);
|
||||
void removeMsg(const MessageId &id);
|
||||
inline void removeMsg(const MessageId &id) { msgs.erase(id.address); }
|
||||
|
||||
QString newMsgName(const MessageId &id);
|
||||
QString newSignalName(const MessageId &id);
|
||||
inline const std::map<uint32_t, cabana::Msg> &getMessages() const { return msgs; }
|
||||
cabana::Msg *msg(uint32_t address);
|
||||
cabana::Msg *msg(const QString &name);
|
||||
inline cabana::Msg *msg(const MessageId &id) { return msg(id.address); }
|
||||
|
||||
const QList<uint8_t>& mask(const MessageId &id) const;
|
||||
|
||||
inline std::map<uint32_t, cabana::Msg> getMessages() const { return msgs; }
|
||||
const cabana::Msg *msg(uint32_t address) const;
|
||||
const cabana::Msg* msg(const QString &name);
|
||||
inline const cabana::Msg *msg(const MessageId &id) const { return msg(id.address); };
|
||||
|
||||
QStringList signalNames() const;
|
||||
int signalCount(const MessageId &id) const;
|
||||
int signalCount() const;
|
||||
inline int msgCount() const { return msgs.size(); }
|
||||
inline QString name() const { return name_.isEmpty() ? "untitled" : name_; }
|
||||
inline bool isEmpty() const { return (signalCount() == 0) && name_.isEmpty(); }
|
||||
int signalCount();
|
||||
inline int msgCount() { return msgs.size(); }
|
||||
inline QString name() { return name_.isEmpty() ? "untitled" : name_; }
|
||||
inline bool isEmpty() { return (signalCount() == 0) && name_.isEmpty(); }
|
||||
|
||||
QString filename;
|
||||
|
||||
@@ -57,5 +43,4 @@ private:
|
||||
void parseExtraInfo(const QString &content);
|
||||
std::map<uint32_t, cabana::Msg> msgs;
|
||||
QString name_;
|
||||
QList<uint8_t> empty_mask;
|
||||
};
|
||||
|
||||
+94
-205
@@ -1,21 +1,16 @@
|
||||
#include "tools/cabana/dbc/dbcmanager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
bool DBCManager::open(SourceSet s, const QString &dbc_file_name, QString *error) {
|
||||
for (int i = 0; i < dbc_files.size(); i++) {
|
||||
auto [ss, dbc_file] = dbc_files[i];
|
||||
|
||||
// Check if file is already open, and merge sources
|
||||
if (dbc_file->filename == dbc_file_name) {
|
||||
dbc_files[i] = {ss | s, dbc_file};
|
||||
|
||||
emit DBCFileChanged();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool DBCManager::open(const SourceSet &sources, const QString &dbc_file_name, QString *error) {
|
||||
try {
|
||||
dbc_files.push_back({s, new DBCFile(dbc_file_name, this)});
|
||||
auto it = std::find_if(dbc_files.begin(), dbc_files.end(),
|
||||
[&](auto &f) { return f.second && f.second->filename == dbc_file_name; });
|
||||
auto file = (it != dbc_files.end()) ? it->second : std::make_shared<DBCFile>(dbc_file_name, this);
|
||||
for (auto s : sources) {
|
||||
dbc_files[s] = file;
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
if (error) *error = e.what();
|
||||
return false;
|
||||
@@ -25,9 +20,12 @@ bool DBCManager::open(SourceSet s, const QString &dbc_file_name, QString *error)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DBCManager::open(SourceSet s, const QString &name, const QString &content, QString *error) {
|
||||
bool DBCManager::open(const SourceSet &sources, const QString &name, const QString &content, QString *error) {
|
||||
try {
|
||||
dbc_files.push_back({s, new DBCFile(name, content, this)});
|
||||
auto file = std::make_shared<DBCFile>(name, content, this);
|
||||
for (auto s : sources) {
|
||||
dbc_files[s] = file;
|
||||
}
|
||||
} catch (std::exception &e) {
|
||||
if (error) *error = e.what();
|
||||
return false;
|
||||
@@ -37,263 +35,154 @@ 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);
|
||||
}
|
||||
void DBCManager::close(const SourceSet &sources) {
|
||||
for (auto s : sources) {
|
||||
dbc_files[s] = nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
for (auto &[_, f] : dbc_files) {
|
||||
if (f.get() == dbc_file) f = nullptr;
|
||||
}
|
||||
|
||||
dbc_files = new_dbc_files;
|
||||
emit DBCFileChanged();
|
||||
}
|
||||
|
||||
void DBCManager::closeAll() {
|
||||
if (dbc_files.isEmpty()) return;
|
||||
|
||||
while (dbc_files.size()) {
|
||||
DBCFile *dbc_file = dbc_files.back().second;
|
||||
dbc_files.pop_back();
|
||||
delete dbc_file;
|
||||
}
|
||||
dbc_files.clear();
|
||||
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);
|
||||
assert(sources_dbc_file); // Create new DBC?
|
||||
auto [dbc_sources, dbc_file] = *sources_dbc_file;
|
||||
|
||||
cabana::Signal *s = dbc_file->addSignal(id, sig);
|
||||
|
||||
if (s != nullptr) {
|
||||
dbc_sources.insert(id.source);
|
||||
for (uint8_t source : dbc_sources) {
|
||||
emit signalAdded({.source = source, .address = id.address}, s);
|
||||
if (auto m = msg(id)) {
|
||||
if (auto s = m->addSignal(sig)) {
|
||||
emit signalAdded(id, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBCManager::updateSignal(const MessageId &id, const QString &sig_name, const cabana::Signal &sig) {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
assert(sources_dbc_file); // This should be impossible
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
|
||||
cabana::Signal *s = dbc_file->updateSignal(id, sig_name, sig);
|
||||
|
||||
if (s != nullptr) {
|
||||
emit signalUpdated(s);
|
||||
if (auto m = msg(id)) {
|
||||
if (auto s = m->updateSignal(sig_name, sig)) {
|
||||
emit signalUpdated(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBCManager::removeSignal(const MessageId &id, const QString &sig_name) {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
assert(sources_dbc_file); // This should be impossible
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
|
||||
cabana::Signal *s = dbc_file->getSignal(id, sig_name);
|
||||
|
||||
if (s != nullptr) {
|
||||
emit signalRemoved(s);
|
||||
dbc_file->removeSignal(id, sig_name);
|
||||
if (auto m = msg(id)) {
|
||||
if (auto s = m->sig(sig_name)) {
|
||||
emit signalRemoved(s);
|
||||
m->removeSignal(sig_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBCManager::updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &comment) {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
assert(sources_dbc_file); // This should be impossible
|
||||
auto [dbc_sources, dbc_file] = *sources_dbc_file;
|
||||
|
||||
auto dbc_file = findDBCFile(id);
|
||||
assert(dbc_file); // This should be impossible
|
||||
dbc_file->updateMsg(id, name, size, comment);
|
||||
|
||||
for (uint8_t source : dbc_sources) {
|
||||
emit msgUpdated({.source = source, .address = id.address});
|
||||
}
|
||||
emit msgUpdated(id);
|
||||
}
|
||||
|
||||
void DBCManager::removeMsg(const MessageId &id) {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
assert(sources_dbc_file); // This should be impossible
|
||||
auto [dbc_sources, dbc_file] = *sources_dbc_file;
|
||||
|
||||
auto dbc_file = findDBCFile(id);
|
||||
assert(dbc_file); // This should be impossible
|
||||
dbc_file->removeMsg(id);
|
||||
|
||||
for (uint8_t source : dbc_sources) {
|
||||
emit msgRemoved({.source = source, .address = id.address});
|
||||
}
|
||||
emit msgRemoved(id);
|
||||
}
|
||||
|
||||
QString DBCManager::newMsgName(const MessageId &id) {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
assert(sources_dbc_file); // This should be impossible
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
return dbc_file->newMsgName(id);
|
||||
return QString("NEW_MSG_") + QString::number(id.address, 16).toUpper();
|
||||
}
|
||||
|
||||
QString DBCManager::newSignalName(const MessageId &id) {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
assert(sources_dbc_file); // This should be impossible
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
return dbc_file->newSignalName(id);
|
||||
auto m = msg(id);
|
||||
return m ? m->newSignalName() : "";
|
||||
}
|
||||
|
||||
const QList<uint8_t>& DBCManager::mask(const MessageId &id) const {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
if (!sources_dbc_file) {
|
||||
return empty_mask;
|
||||
}
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
return dbc_file->mask(id);
|
||||
const QList<uint8_t> &DBCManager::mask(const MessageId &id) {
|
||||
static QList<uint8_t> empty_mask;
|
||||
auto m = msg(id);
|
||||
return m ? m->mask : empty_mask;
|
||||
}
|
||||
|
||||
std::map<MessageId, cabana::Msg> DBCManager::getMessages(uint8_t source) {
|
||||
std::map<MessageId, cabana::Msg> ret;
|
||||
|
||||
auto sources_dbc_file = findDBCFile({.source = source, .address = 0});
|
||||
if (!sources_dbc_file) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
|
||||
for (auto &[address, msg] : dbc_file->getMessages()) {
|
||||
MessageId id = {.source = source, .address = address};
|
||||
ret[id] = msg;
|
||||
}
|
||||
return ret;
|
||||
const std::map<uint32_t, cabana::Msg> &DBCManager::getMessages(uint8_t source) {
|
||||
static std::map<uint32_t, cabana::Msg> empty_msgs;
|
||||
auto dbc_file = findDBCFile(source);
|
||||
return dbc_file ? dbc_file->getMessages() : empty_msgs;
|
||||
}
|
||||
|
||||
const cabana::Msg *DBCManager::msg(const MessageId &id) const {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
if (!sources_dbc_file) {
|
||||
return nullptr;
|
||||
}
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
return dbc_file->msg(id);
|
||||
cabana::Msg *DBCManager::msg(const MessageId &id) {
|
||||
auto dbc_file = findDBCFile(id);
|
||||
return dbc_file ? dbc_file->msg(id) : nullptr;
|
||||
}
|
||||
|
||||
const cabana::Msg* DBCManager::msg(uint8_t source, const QString &name) {
|
||||
auto sources_dbc_file = findDBCFile({.source = source, .address = 0});
|
||||
if (!sources_dbc_file) {
|
||||
return nullptr;
|
||||
}
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
return dbc_file->msg(name);
|
||||
cabana::Msg *DBCManager::msg(uint8_t source, const QString &name) {
|
||||
auto dbc_file = findDBCFile(source);
|
||||
return dbc_file ? dbc_file->msg(name) : nullptr;
|
||||
}
|
||||
|
||||
QStringList DBCManager::signalNames() const {
|
||||
QStringList DBCManager::signalNames() {
|
||||
// Used for autocompletion
|
||||
QStringList ret;
|
||||
|
||||
for (auto &[_, dbc_file] : dbc_files) {
|
||||
ret << dbc_file->signalNames();
|
||||
for (auto &f : allDBCFiles()) {
|
||||
for (auto &[_, m] : f->getMessages()) {
|
||||
for (auto sig : m.getSignals()) {
|
||||
ret << sig->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret.sort();
|
||||
ret.removeDuplicates();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int DBCManager::signalCount(const MessageId &id) const {
|
||||
auto sources_dbc_file = findDBCFile(id);
|
||||
if (!sources_dbc_file) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto [_, dbc_file] = *sources_dbc_file;
|
||||
return dbc_file->signalCount(id);
|
||||
int DBCManager::signalCount(const MessageId &id) {
|
||||
auto m = msg(id);
|
||||
return m ? m->sigs.size() : 0;
|
||||
}
|
||||
|
||||
int DBCManager::signalCount() const {
|
||||
int ret = 0;
|
||||
|
||||
for (auto &[_, dbc_file] : dbc_files) {
|
||||
ret += dbc_file->signalCount();
|
||||
}
|
||||
|
||||
return ret;
|
||||
int DBCManager::signalCount() {
|
||||
auto files = allDBCFiles();
|
||||
return std::accumulate(files.cbegin(), files.cend(), 0, [](int &n, auto &f) { return n + f->signalCount(); });
|
||||
}
|
||||
|
||||
int DBCManager::msgCount() const {
|
||||
int ret = 0;
|
||||
|
||||
for (auto &[_, dbc_file] : dbc_files) {
|
||||
ret += dbc_file->msgCount();
|
||||
}
|
||||
|
||||
return ret;
|
||||
int DBCManager::msgCount() {
|
||||
auto files = allDBCFiles();
|
||||
return std::accumulate(files.cbegin(), files.cend(), 0, [](int &n, auto &f) { return n + f->msgCount(); });
|
||||
}
|
||||
|
||||
int DBCManager::dbcCount() const {
|
||||
return dbc_files.size();
|
||||
int DBCManager::dbcCount() {
|
||||
return allDBCFiles().size();
|
||||
}
|
||||
|
||||
int DBCManager::nonEmptyDBCCount() const {
|
||||
return std::count_if(dbc_files.cbegin(), dbc_files.cend(), [](auto &f) { return !f.second->isEmpty(); });
|
||||
int DBCManager::nonEmptyDBCCount() {
|
||||
auto files = allDBCFiles();
|
||||
return std::count_if(files.cbegin(), files.cend(), [](auto &f) { return !f->isEmpty(); });
|
||||
}
|
||||
|
||||
void DBCManager::updateSources(const SourceSet &s) {
|
||||
sources = s;
|
||||
}
|
||||
|
||||
std::optional<std::pair<SourceSet, DBCFile*>> DBCManager::findDBCFile(const uint8_t source) const {
|
||||
DBCFile *DBCManager::findDBCFile(const uint8_t source) {
|
||||
// Find DBC file that matches id.source, fall back to SOURCE_ALL if no specific DBC is found
|
||||
|
||||
for (auto &[source_set, dbc_file] : dbc_files) {
|
||||
if (source_set.contains(source)) return {{source_set, dbc_file}};
|
||||
}
|
||||
for (auto &[source_set, dbc_file] : dbc_files) {
|
||||
if (source_set == SOURCE_ALL) return {{sources, dbc_file}};
|
||||
}
|
||||
return {};
|
||||
auto it = dbc_files.count(source) ? dbc_files.find(source) : dbc_files.find(-1);
|
||||
return it != dbc_files.end() ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
std::optional<std::pair<SourceSet, DBCFile*>> DBCManager::findDBCFile(const MessageId &id) const {
|
||||
return findDBCFile(id.source);
|
||||
std::set<DBCFile *> DBCManager::allDBCFiles() {
|
||||
std::set<DBCFile *> files;
|
||||
for (const auto &[_, f] : dbc_files) {
|
||||
if (f) files.insert(f.get());
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
const SourceSet DBCManager::sources(const DBCFile *dbc_file) const {
|
||||
SourceSet sources;
|
||||
for (auto &[s, f] : dbc_files) {
|
||||
if (f.get() == dbc_file) sources.insert(s);
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
DBCManager *dbc() {
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QSet>
|
||||
|
||||
#include "tools/cabana/dbc/dbc.h"
|
||||
#include "tools/cabana/dbc/dbcfile.h"
|
||||
|
||||
typedef QSet<uint8_t> SourceSet;
|
||||
const SourceSet SOURCE_ALL = {};
|
||||
typedef std::set<int> SourceSet;
|
||||
const SourceSet SOURCE_ALL = {-1};
|
||||
const int INVALID_SOURCE = 0xff;
|
||||
inline bool operator<(const std::shared_ptr<DBCFile> &l, const std::shared_ptr<DBCFile> &r) { return l.get() < r.get(); }
|
||||
|
||||
class DBCManager : public QObject {
|
||||
Q_OBJECT
|
||||
@@ -22,12 +17,11 @@ class DBCManager : public QObject {
|
||||
public:
|
||||
DBCManager(QObject *parent) {}
|
||||
~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);
|
||||
bool open(const SourceSet &sources, const QString &dbc_file_name, QString *error = nullptr);
|
||||
bool open(const SourceSet &sources, const QString &name, const QString &content, QString *error = nullptr);
|
||||
void close(const SourceSet &sources);
|
||||
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);
|
||||
@@ -38,31 +32,23 @@ public:
|
||||
|
||||
QString newMsgName(const MessageId &id);
|
||||
QString newSignalName(const MessageId &id);
|
||||
const QList<uint8_t>& mask(const MessageId &id);
|
||||
|
||||
const QList<uint8_t>& mask(const MessageId &id) const;
|
||||
const std::map<uint32_t, cabana::Msg> &getMessages(uint8_t source);
|
||||
cabana::Msg *msg(const MessageId &id);
|
||||
cabana::Msg* msg(uint8_t source, const QString &name);
|
||||
|
||||
std::map<MessageId, cabana::Msg> getMessages(uint8_t source);
|
||||
const cabana::Msg *msg(const MessageId &id) const;
|
||||
const cabana::Msg* msg(uint8_t source, const QString &name);
|
||||
QStringList signalNames();
|
||||
int signalCount(const MessageId &id);
|
||||
int signalCount();
|
||||
int msgCount();
|
||||
int dbcCount();
|
||||
int nonEmptyDBCCount();
|
||||
|
||||
QStringList signalNames() const;
|
||||
int signalCount(const MessageId &id) const;
|
||||
int signalCount() const;
|
||||
int msgCount() const;
|
||||
int dbcCount() const;
|
||||
int nonEmptyDBCCount() const;
|
||||
|
||||
std::optional<std::pair<SourceSet, DBCFile*>> findDBCFile(const uint8_t source) const;
|
||||
std::optional<std::pair<SourceSet, DBCFile*>> findDBCFile(const MessageId &id) const;
|
||||
|
||||
QList<std::pair<SourceSet, DBCFile*>> dbc_files;
|
||||
|
||||
private:
|
||||
SourceSet sources;
|
||||
QList<uint8_t> empty_mask;
|
||||
|
||||
public slots:
|
||||
void updateSources(const SourceSet &s);
|
||||
const SourceSet sources(const DBCFile *dbc_file) const;
|
||||
DBCFile *findDBCFile(const uint8_t source);
|
||||
inline DBCFile *findDBCFile(const MessageId &id) { return findDBCFile(id.source); }
|
||||
std::set<DBCFile *> allDBCFiles();
|
||||
|
||||
signals:
|
||||
void signalAdded(MessageId id, const cabana::Signal *sig);
|
||||
@@ -71,6 +57,9 @@ signals:
|
||||
void msgUpdated(MessageId id);
|
||||
void msgRemoved(MessageId id);
|
||||
void DBCFileChanged();
|
||||
|
||||
private:
|
||||
std::map<int, std::shared_ptr<DBCFile>> dbc_files;
|
||||
};
|
||||
|
||||
DBCManager *dbc();
|
||||
@@ -80,16 +69,10 @@ inline QString msgName(const MessageId &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(", ");
|
||||
inline QString toString(const SourceSet &ss) {
|
||||
QStringList ret;
|
||||
for (auto s : ss) {
|
||||
ret << (s == -1 ? QString("all") : QString::number(s));
|
||||
}
|
||||
return ret.join(", ");
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ QVariant HistoryLogModel::headerData(int section, Qt::Orientation orientation, i
|
||||
}
|
||||
} else if (role == Qt::BackgroundRole && section > 0 && show_signals) {
|
||||
// Alpha-blend the signal color with the background to ensure contrast
|
||||
QColor sigColor = getColor(sigs[section - 1]);
|
||||
QColor sigColor = sigs[section - 1]->color;
|
||||
sigColor.setAlpha(128);
|
||||
return QBrush(sigColor);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
uint64_t last_fetch_time = 0;
|
||||
std::function<bool(double, double)> filter_cmp = nullptr;
|
||||
std::deque<Message> messages;
|
||||
std::vector<const cabana::Signal *> sigs;
|
||||
std::vector<cabana::Signal *> sigs;
|
||||
bool dynamic_mode = true;
|
||||
bool display_signals_mode = true;
|
||||
};
|
||||
|
||||
+19
-37
@@ -353,7 +353,6 @@ void MainWindow::streamStarted() {
|
||||
|
||||
QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage);
|
||||
QObject::connect(can, &AbstractStream::eventsMerged, this, &MainWindow::eventsMerged);
|
||||
QObject::connect(can, &AbstractStream::sourcesUpdated, dbc(), &DBCManager::updateSources);
|
||||
QObject::connect(can, &AbstractStream::sourcesUpdated, this, &MainWindow::updateLoadSaveMenus);
|
||||
}
|
||||
|
||||
@@ -374,7 +373,7 @@ void MainWindow::eventsMerged() {
|
||||
|
||||
void MainWindow::save() {
|
||||
// Save all open DBC files
|
||||
for (auto &[s, dbc_file] : dbc()->dbc_files) {
|
||||
for (auto dbc_file : dbc()->allDBCFiles()) {
|
||||
if (dbc_file->isEmpty()) continue;
|
||||
saveFile(dbc_file);
|
||||
}
|
||||
@@ -382,7 +381,7 @@ void MainWindow::save() {
|
||||
|
||||
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) {
|
||||
for (auto dbc_file : dbc()->allDBCFiles()) {
|
||||
if (dbc_file->isEmpty()) continue;
|
||||
saveFileAs(dbc_file);
|
||||
}
|
||||
@@ -390,7 +389,7 @@ void MainWindow::saveAs() {
|
||||
|
||||
void MainWindow::autoSave() {
|
||||
if (!UndoStack::instance()->isClean()) {
|
||||
for (auto &[_, dbc_file] : dbc()->dbc_files) {
|
||||
for (auto dbc_file : dbc()->allDBCFiles()) {
|
||||
if (!dbc_file->filename.isEmpty()) {
|
||||
dbc_file->autoSave();
|
||||
}
|
||||
@@ -399,7 +398,7 @@ void MainWindow::autoSave() {
|
||||
}
|
||||
|
||||
void MainWindow::cleanupAutoSaveFile() {
|
||||
for (auto &[_, dbc_file] : dbc()->dbc_files) {
|
||||
for (auto dbc_file : dbc()->allDBCFiles()) {
|
||||
dbc_file->cleanupAutoSaveFile();
|
||||
}
|
||||
}
|
||||
@@ -436,9 +435,7 @@ void MainWindow::saveFile(DBCFile *dbc_file) {
|
||||
}
|
||||
|
||||
void MainWindow::saveFileAs(DBCFile *dbc_file) {
|
||||
auto it = std::find_if(dbc()->dbc_files.begin(), dbc()->dbc_files.end(), [=](auto &f) { return f.second == dbc_file; });
|
||||
assert(it != dbc()->dbc_files.end());
|
||||
QString title = tr("Save File (bus: %1)").arg(toString(it->first));
|
||||
QString title = tr("Save File (bus: %1)").arg(toString(dbc()->sources(dbc_file)));
|
||||
QString fn = QFileDialog::getSaveFileName(this, title, QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)"));
|
||||
if (!fn.isEmpty()) {
|
||||
dbc_file->saveAs(fn);
|
||||
@@ -447,15 +444,9 @@ void MainWindow::saveFileAs(DBCFile *dbc_file) {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
for (auto dbc_file : dbc()->allDBCFiles()) {
|
||||
if (dbc_file->isEmpty()) continue;
|
||||
saveFileToClipboard(dbc_file);
|
||||
}
|
||||
@@ -480,13 +471,10 @@ void MainWindow::updateLoadSaveMenus() {
|
||||
// TODO: Support clipboard for multiple files
|
||||
copy_dbc_to_clipboard->setEnabled(cnt == 1);
|
||||
|
||||
QList<uint8_t> sources_sorted = can->sources.toList();
|
||||
std::sort(sources_sorted.begin(), sources_sorted.end());
|
||||
|
||||
manage_dbcs_menu->clear();
|
||||
manage_dbcs_menu->setEnabled(dynamic_cast<DummyStream *>(can) == nullptr);
|
||||
|
||||
for (uint8_t source : sources_sorted) {
|
||||
for (uint8_t source : can->sources) {
|
||||
if (source >= 64) continue; // Sent and blocked buses are handled implicitly
|
||||
|
||||
SourceSet ss = {source, uint8_t(source + 128), uint8_t(source + 192)};
|
||||
@@ -497,32 +485,26 @@ void MainWindow::updateLoadSaveMenus() {
|
||||
bus_menu->addAction(tr("Load DBC From Clipboard..."), [=]() { loadFromClipboard(ss, false); });
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
QString file_name = "No DBCs loaded";
|
||||
if (auto dbc_file = dbc()->findDBCFile(source)) {
|
||||
bus_menu->addSeparator();
|
||||
bus_menu->addAction(dbc_file->name() + " (" + toString(src) + ")")->setEnabled(false);
|
||||
bus_menu->addAction(tr("Save..."), [=]() { saveFile(it.second); });
|
||||
bus_menu->addAction(tr("Save As..."), [=]() { saveFileAs(it.second); });
|
||||
bus_menu->addAction(tr("Copy to Clipboard..."), [=]() { saveFileToClipboard(it.second); });
|
||||
bus_menu->addAction(tr("Remove from this bus..."), [=]() { removeBusFromFile(it.second, source); });
|
||||
bus_menu->addAction(tr("Remove from all buses..."), [=]() { closeFile(it.second); });
|
||||
bus_menu->addAction(dbc_file->name() + " (" + toString(dbc()->sources(dbc_file)) + ")")->setEnabled(false);
|
||||
bus_menu->addAction(tr("Save..."), [=]() { saveFile(dbc_file); });
|
||||
bus_menu->addAction(tr("Save As..."), [=]() { saveFileAs(dbc_file); });
|
||||
bus_menu->addAction(tr("Copy to Clipboard..."), [=]() { saveFileToClipboard(dbc_file); });
|
||||
bus_menu->addAction(tr("Remove from this bus..."), [=]() { closeFile(ss); });
|
||||
bus_menu->addAction(tr("Remove from all buses..."), [=]() { closeFile(dbc_file); });
|
||||
|
||||
bus_menu_fns << dbc_file->name();
|
||||
file_name = dbc_file->name();
|
||||
}
|
||||
|
||||
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));
|
||||
bus_menu->setTitle(tr("Bus %1 (%2)").arg(source).arg(file_name));
|
||||
}
|
||||
|
||||
QStringList title;
|
||||
for (auto &[src, dbc_file] : dbc()->dbc_files) {
|
||||
title.push_back(tr("(%1) %2").arg(toString(src), dbc_file->name()));
|
||||
for (auto f : dbc()->allDBCFiles()) {
|
||||
title.push_back(tr("(%1) %2").arg(toString(dbc()->sources(f)), f->name()));
|
||||
}
|
||||
setWindowFilePath(title.join(" | "));
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ protected:
|
||||
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 autoSave();
|
||||
void cleanupAutoSaveFile();
|
||||
|
||||
@@ -273,7 +273,7 @@ bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, co
|
||||
case Column::NAME: {
|
||||
const auto msg = dbc()->msg(id);
|
||||
match = re.match(msg ? msg->name : UNTITLED).hasMatch();
|
||||
match |= msg && std::any_of(msg->sigs.cbegin(), msg->sigs.cend(), [&re](const auto &s) { return re.match(s.name).hasMatch(); });
|
||||
match |= msg && std::any_of(msg->sigs.cbegin(), msg->sigs.cend(), [&re](const auto &s) { return re.match(s->name).hasMatch(); });
|
||||
break;
|
||||
}
|
||||
case Column::SOURCE:
|
||||
|
||||
@@ -171,7 +171,6 @@ bool SignalModel::setData(const QModelIndex &index, const QVariant &value, int r
|
||||
case Item::Desc: s.val_desc = value.value<ValueDescription>(); break;
|
||||
default: return false;
|
||||
}
|
||||
s.updatePrecision();
|
||||
bool ret = saveSignal(item->sig, s);
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, Qt::CheckStateRole});
|
||||
return ret;
|
||||
@@ -249,17 +248,14 @@ void SignalModel::removeSignal(const cabana::Signal *sig) {
|
||||
}
|
||||
|
||||
void SignalModel::handleMsgChanged(MessageId id) {
|
||||
if (id == msg_id) {
|
||||
if (id.address == msg_id.address) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void SignalModel::handleSignalAdded(MessageId id, const cabana::Signal *sig) {
|
||||
if (id == msg_id) {
|
||||
int i = 0;
|
||||
for (; i < root->children.size(); ++i) {
|
||||
if (sig->start_bit < root->children[i]->sig->start_bit) break;
|
||||
}
|
||||
int i = dbc()->msg(msg_id)->indexOf(sig);
|
||||
beginInsertRows({}, i, i);
|
||||
insertItem(root.get(), i, sig);
|
||||
endInsertRows();
|
||||
@@ -348,7 +344,7 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
||||
path.addRoundedRect(icon_rect, 3, 3);
|
||||
painter->setPen(item->highlight ? Qt::white : Qt::black);
|
||||
painter->setFont(label_font);
|
||||
painter->fillPath(path, getColor(item->sig).darker(item->highlight ? 125 : 0));
|
||||
painter->fillPath(path, item->sig->color.darker(item->highlight ? 125 : 0));
|
||||
painter->drawText(icon_rect, Qt::AlignCenter, QString::number(item->row() + 1));
|
||||
|
||||
r.setLeft(icon_rect.right() + h_margin * 2);
|
||||
|
||||
@@ -78,7 +78,7 @@ BO_ 160 message_1: 8 XXX
|
||||
|
||||
VAL_ 160 signal_1 0 "disabled" 1.2 "initializing" 2 "fault";
|
||||
|
||||
CM_ BO_ 160 "message comment";
|
||||
CM_ BO_ 160 "message comment" ;
|
||||
CM_ SG_ 160 signal_1 "signal comment";
|
||||
CM_ SG_ 160 signal_2 "multiple line comment
|
||||
1
|
||||
@@ -94,19 +94,19 @@ CM_ SG_ 160 signal_2 "multiple line comment
|
||||
REQUIRE(msg->sigs.size() == 2);
|
||||
REQUIRE(file.msg("message_1") != nullptr);
|
||||
|
||||
auto &sig_1 = msg->sigs[0];
|
||||
REQUIRE(sig_1.name == "signal_1");
|
||||
REQUIRE(sig_1.start_bit == 0);
|
||||
REQUIRE(sig_1.size == 12);
|
||||
REQUIRE(sig_1.min == 0);
|
||||
REQUIRE(sig_1.max == 4095);
|
||||
REQUIRE(sig_1.unit == "unit");
|
||||
REQUIRE(sig_1.comment == "signal comment");
|
||||
REQUIRE(sig_1.val_desc.size() == 3);
|
||||
REQUIRE(sig_1.val_desc[0] == std::pair<double, QString>{0, "disabled"});
|
||||
REQUIRE(sig_1.val_desc[1] == std::pair<double, QString>{1.2, "initializing"});
|
||||
REQUIRE(sig_1.val_desc[2] == std::pair<double, QString>{2, "fault"});
|
||||
auto sig_1 = msg->sigs[0];
|
||||
REQUIRE(sig_1->name == "signal_1");
|
||||
REQUIRE(sig_1->start_bit == 0);
|
||||
REQUIRE(sig_1->size == 12);
|
||||
REQUIRE(sig_1->min == 0);
|
||||
REQUIRE(sig_1->max == 4095);
|
||||
REQUIRE(sig_1->unit == "unit");
|
||||
REQUIRE(sig_1->comment == "signal comment");
|
||||
REQUIRE(sig_1->val_desc.size() == 3);
|
||||
REQUIRE(sig_1->val_desc[0] == std::pair<double, QString>{0, "disabled"});
|
||||
REQUIRE(sig_1->val_desc[1] == std::pair<double, QString>{1.2, "initializing"});
|
||||
REQUIRE(sig_1->val_desc[2] == std::pair<double, QString>{2, "fault"});
|
||||
|
||||
auto &sig_2 = msg->sigs[1];
|
||||
REQUIRE(sig_2.comment == "multiple line comment\n1\n2");
|
||||
REQUIRE(sig_2->comment == "multiple line comment\n1\n2");
|
||||
}
|
||||
|
||||
@@ -24,14 +24,12 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::Wi
|
||||
for (uint8_t bus : can->sources) {
|
||||
cb->addItem(QString::number(bus), bus);
|
||||
}
|
||||
cb->model()->sort(0);
|
||||
cb->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
msg_cb = new QComboBox(this);
|
||||
// TODO: update when src_bus_combo changes
|
||||
for (auto &[id, msg] : dbc()->getMessages(0)) {
|
||||
msg_cb->addItem(msg.name, id.address);
|
||||
for (auto &[address, msg] : dbc()->getMessages(0)) {
|
||||
msg_cb->addItem(msg.name, address);
|
||||
}
|
||||
msg_cb->model()->sort(0);
|
||||
msg_cb->setCurrentIndex(0);
|
||||
|
||||
+3
-17
@@ -1,5 +1,6 @@
|
||||
#include "tools/cabana/util.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QFontDatabase>
|
||||
#include <QHelpEvent>
|
||||
#include <QLocale>
|
||||
@@ -144,17 +145,6 @@ void TabBar::closeTabClicked() {
|
||||
}
|
||||
}
|
||||
|
||||
QColor getColor(const cabana::Signal *sig) {
|
||||
float h = 19 * (float)sig->lsb / 64.0;
|
||||
h = fmod(h, 1.0);
|
||||
|
||||
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;
|
||||
|
||||
return QColor::fromHsvF(h, s, v);
|
||||
}
|
||||
|
||||
NameValidator::NameValidator(QObject *parent) : QRegExpValidator(QRegExp("^(\\w+)"), parent) {}
|
||||
|
||||
QValidator::State NameValidator::validate(QString &input, int &pos) const {
|
||||
@@ -237,10 +227,6 @@ QString toHex(uint8_t byte) {
|
||||
|
||||
int num_decimals(double num) {
|
||||
const QString string = QString::number(num);
|
||||
const QStringList split = string.split('.');
|
||||
if (split.size() == 1) {
|
||||
return 0;
|
||||
} else {
|
||||
return split[1].size();
|
||||
}
|
||||
auto dot_pos = string.indexOf('.');
|
||||
return dot_pos == -1 ? 0 : string.size() - dot_pos - 1;
|
||||
}
|
||||
|
||||
+1
-4
@@ -2,12 +2,10 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QDoubleValidator>
|
||||
#include <QColor>
|
||||
#include <QFont>
|
||||
#include <QRegExpValidator>
|
||||
#include <QStringBuilder>
|
||||
@@ -22,7 +20,7 @@ class LogSlider : public QSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LogSlider(double factor, Qt::Orientation orientation, QWidget *parent = nullptr) : factor(factor), QSlider(orientation, parent) {};
|
||||
LogSlider(double factor, Qt::Orientation orientation, QWidget *parent = nullptr) : factor(factor), QSlider(orientation, parent) {}
|
||||
|
||||
void setRange(double min, double max) {
|
||||
log_min = factor * std::log10(min);
|
||||
@@ -81,7 +79,6 @@ private:
|
||||
|
||||
inline QString toHex(const QByteArray &dat) { return dat.toHex(' ').toUpper(); }
|
||||
QString toHex(uint8_t byte);
|
||||
QColor getColor(const cabana::Signal *sig);
|
||||
|
||||
class NameValidator : public QRegExpValidator {
|
||||
Q_OBJECT
|
||||
|
||||
Reference in New Issue
Block a user