mirror of https://github.com/commaai/openpilot.git
cabana: support multiplexed signals (#28309)
* support muxed signals * write multiplexor in generateDBC * edit multiplex_switch_value in signalView * no overlapping warning for mux signals * group signals by multiplexer indicator * display freq for each multiplexed signals * remove all multiplexed signals after switch deleted * disable switch value * cleanup * historyView: use getValue * sort by switch value * check address * rename variables * rename signale type * parse multiplexed signals in dbcmanater * cache signal color in member variable * cleanup num_decimals * remove sources from dbcmanager and cleanup code * fix sort * check mltiplex in operator== * fix sizehint * convert multipledxed to normal after changing multiplxor to normal * throw error on multiple 'M' signals * add comment * parse multipled signals in test case * cleanup * change order * cleanup open * display multiplexed/overlapping signals in binaryview * sort overlapped signals by size * refactor dbcmanager * trimmed * parse multiplexed signals in test case * cleanup * merge master * space * use pointer for sigs * alldbcFiles * cleanup * cleanup sparkline * use std::vector * skip draw sparkline if isnull * bigger capacity
This commit is contained in:
parent
7128daebba
commit
e08569b0f3
|
@ -219,9 +219,12 @@ void BinaryView::refresh() {
|
|||
|
||||
QSet<const cabana::Signal *> BinaryView::getOverlappingSignals() const {
|
||||
QSet<const cabana::Signal *> overlapping;
|
||||
for (auto &item : model->items) {
|
||||
if (item.sigs.size() > 1)
|
||||
for (auto s : item.sigs) overlapping += s;
|
||||
for (const auto &item : model->items) {
|
||||
if (item.sigs.size() > 1) {
|
||||
for (auto s : item.sigs) {
|
||||
if (s->type == cabana::Signal::Type::Normal) overlapping += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return overlapping;
|
||||
}
|
||||
|
|
|
@ -289,14 +289,16 @@ void ChartView::updateSeries(const cabana::Signal *sig, bool clear) {
|
|||
const double route_start_time = can->routeStartTime();
|
||||
for (auto end = msgs.cend(); first != end; ++first) {
|
||||
const CanEvent *e = *first;
|
||||
double value = get_raw_value(e->dat, e->size, *s.sig);
|
||||
double ts = e->mono_time / 1e9 - route_start_time; // seconds
|
||||
s.vals.append({ts, value});
|
||||
if (!s.step_vals.empty()) {
|
||||
s.step_vals.append({ts, s.step_vals.back().y()});
|
||||
double value = 0;
|
||||
if (s.sig->getValue(e->dat, e->size, &value)) {
|
||||
double ts = e->mono_time / 1e9 - route_start_time; // seconds
|
||||
s.vals.append({ts, value});
|
||||
if (!s.step_vals.empty()) {
|
||||
s.step_vals.append({ts, s.step_vals.back().y()});
|
||||
}
|
||||
s.step_vals.append({ts, value});
|
||||
s.last_value_mono_time = e->mono_time;
|
||||
}
|
||||
s.step_vals.append({ts, value});
|
||||
s.last_value_mono_time = e->mono_time;
|
||||
}
|
||||
if (!can->liveStreaming()) {
|
||||
s.segment_tree.build(s.vals);
|
||||
|
|
|
@ -22,21 +22,30 @@ void Sparkline::update(const MessageId &msg_id, const cabana::Signal *sig, doubl
|
|||
if (first != last) {
|
||||
if (update_values) {
|
||||
values.clear();
|
||||
values.reserve(std::distance(first, last));
|
||||
if (values.capacity() < std::distance(first, last)) {
|
||||
values.reserve(std::distance(first, last) * 2);
|
||||
}
|
||||
min_val = std::numeric_limits<double>::max();
|
||||
max_val = std::numeric_limits<double>::lowest();
|
||||
for (auto it = first; it != last; ++it) {
|
||||
const CanEvent *e = *it;
|
||||
double value = get_raw_value(e->dat, e->size, *sig);
|
||||
values.emplace_back((e->mono_time - (*first)->mono_time) / 1e9, value);
|
||||
if (min_val > value) min_val = value;
|
||||
if (max_val < value) max_val = value;
|
||||
double value = 0;
|
||||
if (sig->getValue(e->dat, e->size, &value)) {
|
||||
values.emplace_back((e->mono_time - (*first)->mono_time) / 1e9, value);
|
||||
if (min_val > value) min_val = value;
|
||||
if (max_val < value) max_val = value;
|
||||
}
|
||||
}
|
||||
if (min_val == max_val) {
|
||||
min_val -= 1;
|
||||
max_val += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
values.clear();
|
||||
}
|
||||
|
||||
if (!values.empty()) {
|
||||
render(sig->color, size);
|
||||
} else {
|
||||
pixmap = QPixmap();
|
||||
|
@ -49,7 +58,7 @@ void Sparkline::render(const QColor &color, QSize size) {
|
|||
const double xscale = (size.width() - 1) / (double)time_range;
|
||||
const double yscale = (size.height() - 3) / (max_val - min_val);
|
||||
points.clear();
|
||||
points.reserve(values.size());
|
||||
points.reserve(values.capacity());
|
||||
for (auto &v : values) {
|
||||
points.emplace_back(v.x() * xscale, 1 + std::abs(v.y() - max_val) * yscale);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ class Sparkline {
|
|||
public:
|
||||
void update(const MessageId &msg_id, const cabana::Signal *sig, double last_msg_ts, int range, QSize size);
|
||||
const QSize size() const { return pixmap.size() / pixmap.devicePixelRatio(); }
|
||||
inline double freq() const {
|
||||
return values.empty() ? 0 : values.size() / std::max(values.back().x() - values.front().x(), 1.0);
|
||||
}
|
||||
|
||||
QPixmap pixmap;
|
||||
double min_val = 0;
|
||||
|
|
|
@ -62,22 +62,43 @@ void AddSigCommand::redo() { dbc()->addSignal(id, signal); }
|
|||
// RemoveSigCommand
|
||||
|
||||
RemoveSigCommand::RemoveSigCommand(const MessageId &id, const cabana::Signal *sig, QUndoCommand *parent)
|
||||
: id(id), signal(*sig), QUndoCommand(parent) {
|
||||
setText(QObject::tr("remove signal %1 from %2:%3").arg(signal.name).arg(msgName(id)).arg(id.address));
|
||||
: id(id), QUndoCommand(parent) {
|
||||
sigs.push_back(*sig);
|
||||
if (sig->type == cabana::Signal::Type::Multiplexor) {
|
||||
for (const auto &s : dbc()->msg(id)->sigs) {
|
||||
if (s->type == cabana::Signal::Type::Multiplexed) {
|
||||
sigs.push_back(*s);
|
||||
}
|
||||
}
|
||||
}
|
||||
setText(QObject::tr("remove signal %1 from %2:%3").arg(sig->name).arg(msgName(id)).arg(id.address));
|
||||
}
|
||||
|
||||
void RemoveSigCommand::undo() { dbc()->addSignal(id, signal); }
|
||||
void RemoveSigCommand::redo() { dbc()->removeSignal(id, signal.name); }
|
||||
void RemoveSigCommand::undo() { for (const auto &s : sigs) dbc()->addSignal(id, s); }
|
||||
void RemoveSigCommand::redo() { for (const auto &s : sigs) dbc()->removeSignal(id, s.name); }
|
||||
|
||||
// EditSignalCommand
|
||||
|
||||
EditSignalCommand::EditSignalCommand(const MessageId &id, const cabana::Signal *sig, const cabana::Signal &new_sig, QUndoCommand *parent)
|
||||
: id(id), old_signal(*sig), new_signal(new_sig), QUndoCommand(parent) {
|
||||
setText(QObject::tr("edit signal %1 in %2:%3").arg(old_signal.name).arg(msgName(id)).arg(id.address));
|
||||
: id(id), QUndoCommand(parent) {
|
||||
sigs.push_back({*sig, new_sig});
|
||||
if (sig->type == cabana::Signal::Type::Multiplexor && new_sig.type == cabana::Signal::Type::Normal) {
|
||||
// convert all multiplexed signals to normal signals
|
||||
auto msg = dbc()->msg(id);
|
||||
assert(msg);
|
||||
for (const auto &s : msg->sigs) {
|
||||
if (s->type == cabana::Signal::Type::Multiplexed) {
|
||||
auto new_s = *s;
|
||||
new_s.type = cabana::Signal::Type::Normal;
|
||||
sigs.push_back({*s, new_s});
|
||||
}
|
||||
}
|
||||
}
|
||||
setText(QObject::tr("edit signal %1 in %2:%3").arg(sig->name).arg(msgName(id)).arg(id.address));
|
||||
}
|
||||
|
||||
void EditSignalCommand::undo() { dbc()->updateSignal(id, new_signal.name, old_signal); }
|
||||
void EditSignalCommand::redo() { dbc()->updateSignal(id, old_signal.name, new_signal); }
|
||||
void EditSignalCommand::undo() { for (const auto &s : sigs) dbc()->updateSignal(id, s.second.name, s.first); }
|
||||
void EditSignalCommand::redo() { for (const auto &s : sigs) dbc()->updateSignal(id, s.first.name, s.second); }
|
||||
|
||||
namespace UndoStack {
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
|
||||
private:
|
||||
const MessageId id;
|
||||
cabana::Signal signal = {};
|
||||
QList<cabana::Signal> sigs;
|
||||
};
|
||||
|
||||
class EditSignalCommand : public QUndoCommand {
|
||||
|
@ -59,8 +59,7 @@ public:
|
|||
|
||||
private:
|
||||
const MessageId id;
|
||||
cabana::Signal old_signal = {};
|
||||
cabana::Signal new_signal = {};
|
||||
QList<std::pair<cabana::Signal, cabana::Signal>> sigs; // QList<{old_sig, new_sig}>
|
||||
};
|
||||
|
||||
namespace UndoStack {
|
||||
|
|
|
@ -77,12 +77,18 @@ QString cabana::Msg::newSignalName() {
|
|||
|
||||
void cabana::Msg::update() {
|
||||
mask = QVector<uint8_t>(size, 0x00).toList();
|
||||
multiplexor = nullptr;
|
||||
|
||||
// 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);
|
||||
return std::tie(r->type, l->multiplex_value, l->start_bit, l->name) <
|
||||
std::tie(l->type, r->multiplex_value, r->start_bit, r->name);
|
||||
});
|
||||
|
||||
for (auto &sig : sigs) {
|
||||
for (auto sig : sigs) {
|
||||
if (sig->type == cabana::Signal::Type::Multiplexor) {
|
||||
multiplexor = sig;
|
||||
}
|
||||
sig->update();
|
||||
|
||||
// update mask
|
||||
|
@ -101,6 +107,13 @@ void cabana::Msg::update() {
|
|||
i = sig->is_little_endian ? i - 1 : i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto sig : sigs) {
|
||||
sig->multiplexor = sig->type == cabana::Signal::Type::Multiplexed ? multiplexor : nullptr;
|
||||
if (!sig->multiplexor) {
|
||||
sig->multiplex_value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cabana::Signal
|
||||
|
@ -131,6 +144,24 @@ QString cabana::Signal::formatValue(double value) const {
|
|||
return val_str;
|
||||
}
|
||||
|
||||
bool cabana::Signal::getValue(const uint8_t *data, size_t data_size, double *val) const {
|
||||
if (multiplexor && get_raw_value(data, data_size, *multiplexor) != multiplex_value) {
|
||||
return false;
|
||||
}
|
||||
*val = get_raw_value(data, data_size, *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cabana::Signal::operator==(const cabana::Signal &other) const {
|
||||
return name == other.name && size == other.size &&
|
||||
start_bit == other.start_bit &&
|
||||
msb == other.msb && lsb == other.lsb &&
|
||||
is_signed == other.is_signed && is_little_endian == other.is_little_endian &&
|
||||
factor == other.factor && offset == other.offset &&
|
||||
min == other.min && max == other.max && comment == other.comment && unit == other.unit && val_desc == other.val_desc &&
|
||||
multiplex_value == other.multiplex_value && type == other.type;
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
static QVector<int> BIG_ENDIAN_START_BITS = []() {
|
||||
|
@ -163,15 +194,6 @@ double get_raw_value(const uint8_t *data, size_t data_size, const cabana::Signal
|
|||
return val * sig.factor + sig.offset;
|
||||
}
|
||||
|
||||
bool cabana::operator==(const cabana::Signal &l, const cabana::Signal &r) {
|
||||
return l.name == r.name && l.size == r.size &&
|
||||
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.min == r.min && l.max == r.max && l.comment == r.comment && l.unit == r.unit && l.val_desc == r.val_desc;
|
||||
}
|
||||
|
||||
int bigEndianStartBitsIndex(int start_bit) { return BIG_ENDIAN_START_BITS[start_bit]; }
|
||||
int bigEndianBitIndex(int index) { return BIG_ENDIAN_START_BITS.indexOf(index); }
|
||||
|
||||
|
|
|
@ -52,8 +52,18 @@ public:
|
|||
Signal() = default;
|
||||
Signal(const Signal &other) = default;
|
||||
void update();
|
||||
bool getValue(const uint8_t *data, size_t data_size, double *val) const;
|
||||
QString formatValue(double value) const;
|
||||
bool operator==(const cabana::Signal &other) const;
|
||||
inline bool operator!=(const cabana::Signal &other) const { return !(*this == other); }
|
||||
|
||||
enum class Type {
|
||||
Normal = 0,
|
||||
Multiplexed,
|
||||
Multiplexor
|
||||
};
|
||||
|
||||
Type type = Type::Normal;
|
||||
QString name;
|
||||
int start_bit, msb, lsb, size;
|
||||
double factor, offset;
|
||||
|
@ -65,6 +75,10 @@ public:
|
|||
ValueDescription val_desc;
|
||||
int precision = 0;
|
||||
QColor color;
|
||||
|
||||
// Multiplexed
|
||||
int multiplex_value = 0;
|
||||
Signal *multiplexor = nullptr;
|
||||
};
|
||||
|
||||
class Msg {
|
||||
|
@ -79,6 +93,7 @@ public:
|
|||
int indexOf(const cabana::Signal *sig) const;
|
||||
cabana::Signal *sig(const QString &sig_name) const;
|
||||
QString newSignalName();
|
||||
void update();
|
||||
inline const std::vector<cabana::Signal *> &getSignals() const { return sigs; }
|
||||
|
||||
uint32_t address;
|
||||
|
@ -88,12 +103,9 @@ public:
|
|||
std::vector<cabana::Signal *> sigs;
|
||||
|
||||
QList<uint8_t> mask;
|
||||
void update();
|
||||
cabana::Signal *multiplexor = nullptr;
|
||||
};
|
||||
|
||||
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
|
||||
|
|
|
@ -16,7 +16,7 @@ DBCFile::DBCFile(const QString &dbc_file_name, QObject *parent) : QObject(parent
|
|||
if (dbc_file_name.endsWith(AUTO_SAVE_EXTENSION)) {
|
||||
filename.chop(AUTO_SAVE_EXTENSION.length());
|
||||
}
|
||||
open(file.readAll());
|
||||
parse(file.readAll());
|
||||
} else {
|
||||
throw std::runtime_error("Failed to open file.");
|
||||
}
|
||||
|
@ -24,35 +24,7 @@ DBCFile::DBCFile(const QString &dbc_file_name, QObject *parent) : QObject(parent
|
|||
|
||||
DBCFile::DBCFile(const QString &name, const QString &content, QObject *parent) : QObject(parent), name_(name), filename("") {
|
||||
// Open from clipboard
|
||||
open(content);
|
||||
}
|
||||
|
||||
void DBCFile::open(const QString &content) {
|
||||
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.address = msg.address;
|
||||
m.name = msg.name.c_str();
|
||||
m.size = msg.size;
|
||||
for (auto &s : msg.sigs) {
|
||||
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.update();
|
||||
}
|
||||
parseExtraInfo(content);
|
||||
|
||||
delete dbc;
|
||||
parse(content);
|
||||
}
|
||||
|
||||
bool DBCFile::save() {
|
||||
|
@ -110,7 +82,7 @@ int DBCFile::signalCount() {
|
|||
return std::accumulate(msgs.cbegin(), msgs.cend(), 0, [](int &n, const auto &m) { return n + m.second.sigs.size(); });
|
||||
}
|
||||
|
||||
void DBCFile::parseExtraInfo(const QString &content) {
|
||||
void DBCFile::parse(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]+)\] \"(.*)\" (.*))");
|
||||
|
@ -120,23 +92,31 @@ void DBCFile::parseExtraInfo(const QString &content) {
|
|||
|
||||
int line_num = 0;
|
||||
QString line;
|
||||
auto dbc_assert = [&line_num, &line, this](bool condition) {
|
||||
if (!condition) throw std::runtime_error(QString("[%1:%2]: %3").arg(filename).arg(line_num).arg(line).toStdString());
|
||||
auto dbc_assert = [&line_num, &line, this](bool condition, const QString &msg = "") {
|
||||
if (!condition) throw std::runtime_error(QString("[%1:%2]%3: %4").arg(filename).arg(line_num).arg(msg).arg(line).toStdString());
|
||||
};
|
||||
auto get_sig = [this](uint32_t address, const QString &name) -> cabana::Signal * {
|
||||
auto m = (cabana::Msg *)msg(address);
|
||||
return m ? (cabana::Signal *)m->sig(name) : nullptr;
|
||||
};
|
||||
|
||||
msgs.clear();
|
||||
QTextStream stream((QString *)&content);
|
||||
uint32_t address = 0;
|
||||
cabana::Msg *current_msg = nullptr;
|
||||
int multiplexor_cnt = 0;
|
||||
while (!stream.atEnd()) {
|
||||
++line_num;
|
||||
line = stream.readLine().trimmed();
|
||||
if (line.startsWith("BO_ ")) {
|
||||
multiplexor_cnt = 0;
|
||||
auto match = bo_regexp.match(line);
|
||||
dbc_assert(match.hasMatch());
|
||||
address = match.captured(1).toUInt();
|
||||
auto address = match.captured(1).toUInt();
|
||||
dbc_assert(msgs.count(address) == 0, QString("Duplicate message address: %1").arg(address));
|
||||
current_msg = &msgs[address];
|
||||
current_msg->address = address;
|
||||
current_msg->name = match.captured(2);
|
||||
current_msg->size = match.captured(3).toULong();
|
||||
} else if (line.startsWith("SG_ ")) {
|
||||
int offset = 0;
|
||||
auto match = sg_regexp.match(line);
|
||||
|
@ -145,11 +125,40 @@ void DBCFile::parseExtraInfo(const QString &content) {
|
|||
offset = 1;
|
||||
}
|
||||
dbc_assert(match.hasMatch());
|
||||
if (auto s = get_sig(address, match.captured(1))) {
|
||||
s->min = match.captured(8 + offset).toDouble();
|
||||
s->max = match.captured(9 + offset).toDouble();
|
||||
s->unit = match.captured(10 + offset);
|
||||
dbc_assert(current_msg, "No Message");
|
||||
auto name = match.captured(1);
|
||||
dbc_assert(current_msg->sig(name) == nullptr, "Duplicate signal name");
|
||||
cabana::Signal s{};
|
||||
if (offset == 1) {
|
||||
auto indicator = match.captured(2);
|
||||
if (indicator == "M") {
|
||||
// Only one signal within a single message can be the multiplexer switch.
|
||||
dbc_assert(++multiplexor_cnt < 2, "Multiple multiplexor");
|
||||
s.type = cabana::Signal::Type::Multiplexor;
|
||||
} else {
|
||||
dbc_assert(multiplexor_cnt == 1, "No multiplexor");
|
||||
s.type = cabana::Signal::Type::Multiplexed;
|
||||
s.multiplex_value = indicator.mid(1).toInt();
|
||||
}
|
||||
}
|
||||
s.name = name;
|
||||
s.start_bit = match.captured(offset + 2).toInt();
|
||||
s.size = match.captured(offset + 3).toInt();
|
||||
s.is_little_endian = match.captured(offset + 4).toInt() == 1;
|
||||
s.is_signed = match.captured(offset + 5) == "-";
|
||||
s.factor = match.captured(offset + 6).toDouble();
|
||||
s.offset = match.captured(offset + 7).toDouble();
|
||||
if (s.is_little_endian) {
|
||||
s.lsb = s.start_bit;
|
||||
s.msb = s.start_bit + s.size - 1;
|
||||
} else {
|
||||
s.lsb = bigEndianStartBitsIndex(bigEndianBitIndex(s.start_bit) + s.size - 1);
|
||||
s.msb = s.start_bit;
|
||||
}
|
||||
s.min = match.captured(8 + offset).toDouble();
|
||||
s.max = match.captured(9 + offset).toDouble();
|
||||
s.unit = match.captured(10 + offset);
|
||||
current_msg->sigs.push_back(new cabana::Signal(s));
|
||||
} else if (line.startsWith("VAL_ ")) {
|
||||
auto match = val_regexp.match(line);
|
||||
dbc_assert(match.hasMatch());
|
||||
|
@ -185,6 +194,10 @@ void DBCFile::parseExtraInfo(const QString &content) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &[_, m] : msgs) {
|
||||
m.update();
|
||||
}
|
||||
}
|
||||
|
||||
QString DBCFile::generateDBC() {
|
||||
|
@ -195,8 +208,15 @@ QString DBCFile::generateDBC() {
|
|||
message_comment += QString("CM_ BO_ %1 \"%2\";\n").arg(address).arg(m.comment);
|
||||
}
|
||||
for (auto sig : m.getSignals()) {
|
||||
dbc_string += QString(" SG_ %1 : %2|%3@%4%5 (%6,%7) [%8|%9] \"%10\" XXX\n")
|
||||
QString multiplexer_indicator;
|
||||
if (sig->type == cabana::Signal::Type::Multiplexor) {
|
||||
multiplexer_indicator = "M ";
|
||||
} else if (sig->type == cabana::Signal::Type::Multiplexed) {
|
||||
multiplexer_indicator = QString("m%1 ").arg(sig->multiplex_value);
|
||||
}
|
||||
dbc_string += QString(" SG_ %1 %2: %3|%4@%5%6 (%7,%8) [%9|%10] \"%11\" XXX\n")
|
||||
.arg(sig->name)
|
||||
.arg(multiplexer_indicator)
|
||||
.arg(sig->start_bit)
|
||||
.arg(sig->size)
|
||||
.arg(sig->is_little_endian ? '1' : '0')
|
||||
|
|
|
@ -15,8 +15,6 @@ public:
|
|||
DBCFile(const QString &name, const QString &content, QObject *parent=nullptr);
|
||||
~DBCFile() {}
|
||||
|
||||
void open(const QString &content);
|
||||
|
||||
bool save();
|
||||
bool saveAs(const QString &new_filename);
|
||||
bool autoSave();
|
||||
|
@ -40,7 +38,7 @@ public:
|
|||
QString filename;
|
||||
|
||||
private:
|
||||
void parseExtraInfo(const QString &content);
|
||||
void parse(const QString &content);
|
||||
std::map<uint32_t, cabana::Msg> msgs;
|
||||
QString name_;
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ class DBCManager : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DBCManager(QObject *parent) {}
|
||||
DBCManager(QObject *parent) : QObject(parent) {}
|
||||
~DBCManager() {}
|
||||
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);
|
||||
|
|
|
@ -167,7 +167,8 @@ void DetailWidget::editMsg() {
|
|||
int size = msg ? msg->size : can->lastMessage(msg_id).dat.size();
|
||||
EditMessageDialog dlg(msg_id, msgName(msg_id), size, this);
|
||||
if (dlg.exec()) {
|
||||
UndoStack::push(new EditMsgCommand(msg_id, dlg.name_edit->text().trimmed(), dlg.size_spin->value(), dlg.comment_edit->toPlainText().trimmed()));
|
||||
UndoStack::push(new EditMsgCommand(msg_id, dlg.name_edit->text().trimmed(),
|
||||
dlg.size_spin->value(), dlg.comment_edit->toPlainText().trimmed()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ std::deque<HistoryLogModel::Message> HistoryLogModel::fetchData(InputIt first, I
|
|||
for (; first != last && (*first)->mono_time > min_time; ++first) {
|
||||
const CanEvent *e = *first;
|
||||
for (int i = 0; i < sigs.size(); ++i) {
|
||||
values[i] = get_raw_value(e->dat, e->size, *sigs[i]);
|
||||
sigs[i]->getValue(e->dat, e->size, &values[i]);
|
||||
}
|
||||
if (!filter_cmp || filter_cmp(values[filter_sig_idx], filter_value)) {
|
||||
auto &m = msgs.emplace_back();
|
||||
|
|
|
@ -482,7 +482,7 @@ void MainWindow::updateLoadSaveMenus() {
|
|||
manage_dbcs_menu->clear();
|
||||
manage_dbcs_menu->setEnabled(dynamic_cast<DummyStream *>(can) == nullptr);
|
||||
|
||||
for (uint8_t source : can->sources) {
|
||||
for (int 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)};
|
||||
|
|
|
@ -32,6 +32,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : QWidget(parent) {
|
|||
auto delegate = new MessageBytesDelegate(view, settings.multiple_lines_bytes);
|
||||
|
||||
view->setItemDelegate(delegate);
|
||||
view->setHeader(header);
|
||||
view->setModel(model);
|
||||
view->setHeader(header);
|
||||
view->setSortingEnabled(true);
|
||||
|
@ -130,7 +131,7 @@ void MessagesWidget::dbcModified() {
|
|||
void MessagesWidget::selectMessage(const MessageId &msg_id) {
|
||||
auto it = std::find(model->msgs.cbegin(), model->msgs.cend(), msg_id);
|
||||
if (it != model->msgs.cend()) {
|
||||
view->selectionModel()->setCurrentIndex(model->index(std::distance(model->msgs.cbegin(), it), 0), QItemSelectionModel::Rows | QItemSelectionModel::ClearAndSelect);
|
||||
view->setCurrentIndex(model->index(std::distance(model->msgs.cbegin(), it), 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +210,7 @@ void MessageListModel::setFilterStrings(const QMap<int, QString> &filters) {
|
|||
|
||||
void MessageListModel::dbcModified() {
|
||||
dbc_address.clear();
|
||||
for (const auto &[_, m] : dbc()->getMessages(0)) {
|
||||
for (const auto &[_, m] : dbc()->getMessages(-1)) {
|
||||
dbc_address.insert(m.address);
|
||||
}
|
||||
fetchData();
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
|
||||
// SignalModel
|
||||
|
||||
static QString signalTypeToString(cabana::Signal::Type type) {
|
||||
if (type == cabana::Signal::Type::Multiplexor) return "Multiplexor Signal";
|
||||
else if (type == cabana::Signal::Type::Multiplexed) return "Multiplexed Signal";
|
||||
else return "Normal Signal";
|
||||
}
|
||||
|
||||
SignalModel::SignalModel(QObject *parent) : root(new Item), QAbstractItemModel(parent) {
|
||||
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &SignalModel::refresh);
|
||||
QObject::connect(dbc(), &DBCManager::msgUpdated, this, &SignalModel::handleMsgChanged);
|
||||
|
@ -30,7 +36,8 @@ SignalModel::SignalModel(QObject *parent) : root(new Item), QAbstractItemModel(p
|
|||
void SignalModel::insertItem(SignalModel::Item *parent_item, int pos, const cabana::Signal *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 Value", "Maximum Value", "Value Descriptions"};
|
||||
QString titles[]{"Name", "Size", "Little Endian", "Signed", "Offset", "Factor", "Type", "Multiplex Value", "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)});
|
||||
}
|
||||
|
@ -87,6 +94,9 @@ Qt::ItemFlags SignalModel::flags(const QModelIndex &index) const {
|
|||
if (index.column() == 1 && item->type != Item::Sig && item->type != Item::ExtraInfo) {
|
||||
flags |= (item->type == Item::Endian || item->type == Item::Signed) ? Qt::ItemIsUserCheckable : Qt::ItemIsEditable;
|
||||
}
|
||||
if (item->type == Item::MultiplexValue && item->sig->type != cabana::Signal::Type::Multiplexed) {
|
||||
flags &= ~Qt::ItemIsEnabled;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
@ -124,6 +134,8 @@ QVariant SignalModel::data(const QModelIndex &index, int role) const {
|
|||
case Item::Sig: return item->sig_val;
|
||||
case Item::Name: return item->sig->name;
|
||||
case Item::Size: return item->sig->size;
|
||||
case Item::SignalType: return signalTypeToString(item->sig->type);
|
||||
case Item::MultiplexValue: return item->sig->multiplex_value;
|
||||
case Item::Offset: return doubleToString(item->sig->offset);
|
||||
case Item::Factor: return doubleToString(item->sig->factor);
|
||||
case Item::Unit: return item->sig->unit;
|
||||
|
@ -160,6 +172,8 @@ bool SignalModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||
switch (item->type) {
|
||||
case Item::Name: s.name = value.toString(); break;
|
||||
case Item::Size: s.size = value.toInt(); break;
|
||||
case Item::SignalType: s.type = (cabana::Signal::Type)value.toInt(); break;
|
||||
case Item::MultiplexValue: s.multiplex_value = 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;
|
||||
|
@ -265,6 +279,14 @@ void SignalModel::handleSignalAdded(MessageId id, const cabana::Signal *sig) {
|
|||
void SignalModel::handleSignalUpdated(const cabana::Signal *sig) {
|
||||
if (int row = signalRow(sig); row != -1) {
|
||||
emit dataChanged(index(row, 0), index(row, 1), {Qt::DisplayRole, Qt::EditRole, Qt::CheckStateRole});
|
||||
|
||||
// move row when the order changes.
|
||||
int to = dbc()->msg(msg_id)->indexOf(sig);
|
||||
if (to != row) {
|
||||
beginMoveRows({}, row, row, {}, to > row ? to + 1 : to);
|
||||
root->children.move(row, to);
|
||||
endMoveRows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,13 +311,18 @@ SignalItemDelegate::SignalItemDelegate(QObject *parent) : QStyledItemDelegate(pa
|
|||
QSize SignalItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||
int width = option.widget->size().width() / 2;
|
||||
if (index.column() == 0) {
|
||||
auto text = index.data(Qt::DisplayRole).toString();
|
||||
int spacing = option.widget->style()->pixelMetric(QStyle::PM_TreeViewIndentation) + color_label_width + 8;
|
||||
auto text = index.data(Qt::DisplayRole).toString();;
|
||||
auto item = (SignalModel::Item *)index.internalPointer();
|
||||
if (item->type == SignalModel::Item::Sig && item->sig->type != cabana::Signal::Type::Normal) {
|
||||
text += item->sig->type == cabana::Signal::Type::Multiplexor ? QString(" M ") : QString(" m%1 ").arg(item->sig->multiplex_value);
|
||||
spacing += (option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1) * 2;
|
||||
}
|
||||
auto it = width_cache.find(text);
|
||||
if (it == width_cache.end()) {
|
||||
int spacing = option.widget->style()->pixelMetric(QStyle::PM_TreeViewIndentation) + color_label_width + 8;
|
||||
it = width_cache.insert(text, option.fontMetrics.width(text) + spacing);
|
||||
it = width_cache.insert(text, option.fontMetrics.width(text));
|
||||
}
|
||||
width = std::min<int>(option.widget->size().width() / 3.0, it.value());
|
||||
width = std::min<int>(option.widget->size().width() / 3.0, it.value() + spacing);
|
||||
}
|
||||
return {width, QApplication::fontMetrics().height()};
|
||||
}
|
||||
|
@ -334,11 +361,24 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
|||
painter->drawText(icon_rect, Qt::AlignCenter, QString::number(item->row() + 1));
|
||||
|
||||
r.setLeft(icon_rect.right() + h_margin * 2);
|
||||
// multiplexer indicator
|
||||
if (item->sig->type != cabana::Signal::Type::Normal) {
|
||||
QString indicator = item->sig->type == cabana::Signal::Type::Multiplexor ? QString(" M ") : QString(" m%1 ").arg(item->sig->multiplex_value);
|
||||
QRect indicator_rect{r.x(), r.y(), option.fontMetrics.width(indicator), r.height()};
|
||||
painter->setBrush(Qt::gray);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRoundedRect(indicator_rect, 3, 3);
|
||||
painter->setPen(Qt::white);
|
||||
painter->drawText(indicator_rect, Qt::AlignCenter, indicator);
|
||||
r.setLeft(indicator_rect.right() + h_margin * 2);
|
||||
}
|
||||
|
||||
// name
|
||||
auto text = option.fontMetrics.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, r.width());
|
||||
painter->setPen(option.palette.color(option.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text));
|
||||
painter->setFont(option.font);
|
||||
painter->drawText(r, option.displayAlignment, text);
|
||||
} else if (index.column() == 1) {
|
||||
} else if (index.column() == 1 && !item->sparkline.pixmap.isNull()) {
|
||||
// sparkline
|
||||
QSize sparkline_size = item->sparkline.pixmap.size() / item->sparkline.pixmap.devicePixelRatio();
|
||||
painter->drawPixmap(QRect(r.topLeft(), sparkline_size), item->sparkline.pixmap);
|
||||
|
@ -356,8 +396,15 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
|
|||
painter->drawText(rect, Qt::AlignLeft | Qt::AlignBottom, min);
|
||||
QFontMetrics fm(minmax_font);
|
||||
value_adjust = std::max(fm.width(min), fm.width(max)) + 5;
|
||||
} else if (item->sig->type == cabana::Signal::Type::Multiplexed) {
|
||||
// display freq of multiplexed signal
|
||||
painter->setFont(label_font);
|
||||
QString freq = QString("%1 hz").arg(item->sparkline.freq(), 0, 'g', 2);
|
||||
painter->drawText(rect.adjusted(5, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter, freq);
|
||||
QFontMetrics fm(label_font);
|
||||
value_adjust = fm.width(freq) + 10;
|
||||
}
|
||||
// value
|
||||
// signal value
|
||||
painter->setFont(option.font);
|
||||
rect.adjust(value_adjust, 0, -button_size.width(), 0);
|
||||
auto text = option.fontMetrics.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, rect.width());
|
||||
|
@ -371,10 +418,11 @@ 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 || item->type == SignalModel::Item::Min || item->type == SignalModel::Item::Max) {
|
||||
item->type == SignalModel::Item::Factor || item->type == SignalModel::Item::MultiplexValue ||
|
||||
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);
|
||||
e->setValidator(item->type == SignalModel::Item::Name ? name_validator : double_validator);
|
||||
|
||||
if (item->type == SignalModel::Item::Name) {
|
||||
QCompleter *completer = new QCompleter(dbc()->signalNames());
|
||||
|
@ -389,6 +437,15 @@ QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionVie
|
|||
spin->setFrame(false);
|
||||
spin->setRange(1, 64);
|
||||
return spin;
|
||||
} else if (item->type == SignalModel::Item::SignalType) {
|
||||
QComboBox *c = new QComboBox(parent);
|
||||
c->addItem(signalTypeToString(cabana::Signal::Type::Normal), (int)cabana::Signal::Type::Normal);
|
||||
if (!dbc()->msg(((SignalModel *)index.model())->msg_id)->multiplexor) {
|
||||
c->addItem(signalTypeToString(cabana::Signal::Type::Multiplexor), (int)cabana::Signal::Type::Multiplexor);
|
||||
} else if (item->sig->type != cabana::Signal::Type::Multiplexor) {
|
||||
c->addItem(signalTypeToString(cabana::Signal::Type::Multiplexed), (int)cabana::Signal::Type::Multiplexed);
|
||||
}
|
||||
return c;
|
||||
} else if (item->type == SignalModel::Item::Desc) {
|
||||
ValueDescriptionDlg dlg(item->sig->val_desc, parent);
|
||||
dlg.setWindowTitle(item->sig->name);
|
||||
|
@ -400,6 +457,15 @@ QWidget *SignalItemDelegate::createEditor(QWidget *parent, const QStyleOptionVie
|
|||
return QStyledItemDelegate::createEditor(parent, option, index);
|
||||
}
|
||||
|
||||
void SignalItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
|
||||
auto item = (SignalModel::Item *)index.internalPointer();
|
||||
if (item->type == SignalModel::Item::SignalType) {
|
||||
model->setData(index, ((QComboBox*)editor)->currentData().toInt());
|
||||
return;
|
||||
}
|
||||
QStyledItemDelegate::setModelData(editor, model, index);
|
||||
}
|
||||
|
||||
// SignalView
|
||||
|
||||
SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), QFrame(parent) {
|
||||
|
@ -438,6 +504,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
|
|||
tree->setHeaderHidden(true);
|
||||
tree->setMouseTracking(true);
|
||||
tree->setExpandsOnDoubleClick(false);
|
||||
tree->setEditTriggers(QAbstractItemView::AllEditTriggers);
|
||||
tree->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
tree->header()->setStretchLastSection(true);
|
||||
tree->setMinimumHeight(300);
|
||||
|
@ -579,8 +646,10 @@ void SignalView::updateState(const QHash<MessageId, CanData> *msgs) {
|
|||
if (model->rowCount() == 0 || (msgs && !msgs->contains(model->msg_id)) || last_msg.dat.size() == 0) return;
|
||||
|
||||
for (auto item : model->root->children) {
|
||||
double value = get_raw_value((uint8_t *)last_msg.dat.constData(), last_msg.dat.size(), *item->sig);
|
||||
item->sig_val = item->sig->formatValue(value);
|
||||
double value = 0;
|
||||
if (item->sig->getValue((uint8_t *)last_msg.dat.constData(), last_msg.dat.size(), &value)) {
|
||||
item->sig_val = item->sig->formatValue(value);
|
||||
}
|
||||
max_value_width = std::max(max_value_width, fontMetrics().width(item->sig_val));
|
||||
}
|
||||
|
||||
|
@ -620,11 +689,6 @@ void SignalView::resizeEvent(QResizeEvent* event) {
|
|||
QFrame::resizeEvent(event);
|
||||
}
|
||||
|
||||
void SignalView::leaveEvent(QEvent *event) {
|
||||
emit highlight(nullptr);
|
||||
QWidget::leaveEvent(event);
|
||||
}
|
||||
|
||||
// ValueDescriptionDlg
|
||||
|
||||
ValueDescriptionDlg::ValueDescriptionDlg(const ValueDescription &descriptions, QWidget *parent) : QDialog(parent) {
|
||||
|
|
|
@ -15,7 +15,7 @@ class SignalModel : public QAbstractItemModel {
|
|||
Q_OBJECT
|
||||
public:
|
||||
struct Item {
|
||||
enum Type {Root, Sig, Name, Size, Endian, Signed, Offset, Factor, ExtraInfo, Unit, Comment, Min, Max, Desc };
|
||||
enum Type {Root, Sig, Name, Size, Endian, Signed, Offset, Factor, SignalType, MultiplexValue, ExtraInfo, Unit, Comment, Min, Max, Desc };
|
||||
~Item() { qDeleteAll(children); }
|
||||
inline int row() { return parent->children.indexOf(this); }
|
||||
|
||||
|
@ -86,6 +86,7 @@ public:
|
|||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
|
||||
|
||||
QValidator *name_validator, *double_validator;
|
||||
QFont label_font, minmax_font;
|
||||
|
@ -112,7 +113,6 @@ signals:
|
|||
|
||||
private:
|
||||
void rowsChanged();
|
||||
void leaveEvent(QEvent *event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void updateToolBar();
|
||||
void setSparklineRange(int value);
|
||||
|
@ -130,6 +130,10 @@ private:
|
|||
// Bypass the slow call to QTreeView::dataChanged.
|
||||
QAbstractItemView::dataChanged(topLeft, bottomRight, roles);
|
||||
}
|
||||
void leaveEvent(QEvent *event) override {
|
||||
emit ((SignalView *)parentWidget())->highlight(nullptr);
|
||||
QTreeView::leaveEvent(event);
|
||||
}
|
||||
};
|
||||
int max_value_width = 0;
|
||||
TreeView *tree;
|
||||
|
|
|
@ -180,7 +180,7 @@ void CanData::compute(const char *can_data, const int size, double current_sec,
|
|||
dat.resize(size);
|
||||
bit_change_counts.resize(size);
|
||||
colors = QVector(size, QColor(0, 0, 0, 0));
|
||||
last_change_t = QVector(size, ts);
|
||||
last_change_t.assign(size, ts);
|
||||
last_delta.resize(size);
|
||||
same_delta_counter.resize(size);
|
||||
} else {
|
||||
|
|
|
@ -20,10 +20,10 @@ struct CanData {
|
|||
double freq = 0;
|
||||
QByteArray dat;
|
||||
QVector<QColor> colors;
|
||||
QVector<double> last_change_t;
|
||||
QVector<std::array<uint32_t, 8>> bit_change_counts;
|
||||
QVector<int> last_delta;
|
||||
QVector<int> same_delta_counter;
|
||||
std::vector<double> last_change_t;
|
||||
std::vector<std::array<uint32_t, 8>> bit_change_counts;
|
||||
std::vector<int> last_delta;
|
||||
std::vector<int> same_delta_counter;
|
||||
};
|
||||
|
||||
struct CanEvent {
|
||||
|
|
|
@ -55,11 +55,12 @@ bool ReplayStream::eventFilter(const Event *event) {
|
|||
const auto dat = c.getDat();
|
||||
updateEvent(id, current_sec, (const uint8_t*)dat.begin(), dat.size());
|
||||
}
|
||||
double ts = millis_since_boot();
|
||||
if ((ts - prev_update_ts) > (1000.0 / settings.fps)) {
|
||||
if (postEvents()) {
|
||||
prev_update_ts = ts;
|
||||
}
|
||||
}
|
||||
|
||||
double ts = millis_since_boot();
|
||||
if ((ts - prev_update_ts) > (1000.0 / settings.fps)) {
|
||||
if (postEvents()) {
|
||||
prev_update_ts = ts;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -11,7 +11,7 @@ public:
|
|||
void start() override;
|
||||
bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE);
|
||||
bool eventFilter(const Event *event);
|
||||
void seekTo(double ts) override { replay->seekTo(std::max(double(0), ts), false); };
|
||||
void seekTo(double ts) override { replay->seekTo(std::max(double(0), ts), false); }
|
||||
inline QString routeName() const override { return replay->route()->name(); }
|
||||
inline QString carFingerprint() const override { return replay->carFingerprint().c_str(); }
|
||||
double totalSeconds() const override { return replay->totalSeconds(); }
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
const std::string TEST_RLOG_URL = "https://commadata2.blob.core.windows.net/commadata2/4cf7a6ad03080c90/2021-09-29--13-46-36/0/rlog.bz2";
|
||||
|
||||
TEST_CASE("DBCFile::generateDBC") {
|
||||
QString fn = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, "toyota_new_mc_pt_generated");
|
||||
QString fn = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, "tesla_can");
|
||||
DBCFile dbc_origin(fn);
|
||||
DBCFile dbc_from_generated("", dbc_origin.generateDBC());
|
||||
|
||||
REQUIRE(dbc_origin.msgCount() == dbc_from_generated.msgCount());
|
||||
auto msgs = dbc_origin.getMessages();
|
||||
auto new_msgs = dbc_from_generated.getMessages();
|
||||
auto &msgs = dbc_origin.getMessages();
|
||||
auto &new_msgs = dbc_from_generated.getMessages();
|
||||
for (auto &[id, m] : msgs) {
|
||||
auto &new_m = new_msgs.at(id);
|
||||
REQUIRE(m.name == new_m.name);
|
||||
|
@ -76,6 +76,10 @@ BO_ 160 message_1: 8 XXX
|
|||
SG_ signal_1 : 0|12@1+ (1,0) [0|4095] "unit" XXX
|
||||
SG_ signal_2 : 12|1@1+ (1.0,0.0) [0.0|1] "" XXX
|
||||
|
||||
BO_ 162 message_1: 8 XXX
|
||||
SG_ signal_1 M : 0|12@1+ (1,0) [0|4095] "unit" XXX
|
||||
SG_ signal_2 M4 : 12|1@1+ (1.0,0.0) [0.0|1] "" XXX
|
||||
|
||||
VAL_ 160 signal_1 0 "disabled" 1.2 "initializing" 2 "fault";
|
||||
|
||||
CM_ BO_ 160 "message comment" ;
|
||||
|
@ -109,4 +113,14 @@ CM_ SG_ 160 signal_2 "multiple line comment
|
|||
|
||||
auto &sig_2 = msg->sigs[1];
|
||||
REQUIRE(sig_2->comment == "multiple line comment\n1\n2");
|
||||
|
||||
// multiplexed signals
|
||||
msg = file.msg(162);
|
||||
REQUIRE(msg != nullptr);
|
||||
REQUIRE(msg->sigs.size() == 2);
|
||||
REQUIRE(msg->sigs[0]->type == cabana::Signal::Type::Multiplexor);
|
||||
REQUIRE(msg->sigs[1]->type == cabana::Signal::Type::Multiplexed);
|
||||
REQUIRE(msg->sigs[1]->multiplex_value == 4);
|
||||
REQUIRE(msg->sigs[1]->start_bit == 12);
|
||||
REQUIRE(msg->sigs[1]->size == 1);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ FindSimilarBitsDlg::FindSimilarBitsDlg(QWidget *parent) : QDialog(parent, Qt::Wi
|
|||
|
||||
msg_cb = new QComboBox(this);
|
||||
// TODO: update when src_bus_combo changes
|
||||
for (auto &[address, msg] : dbc()->getMessages(0)) {
|
||||
for (auto &[address, msg] : dbc()->getMessages(-1)) {
|
||||
msg_cb->addItem(msg.name, address);
|
||||
}
|
||||
msg_cb->model()->sort(0);
|
||||
|
|
Loading…
Reference in New Issue