|
|
|
@ -1,4 +1,4 @@ |
|
|
|
|
#include "tools/cabana/signaledit.h" |
|
|
|
|
#include "tools/cabana/signalview.h" |
|
|
|
|
|
|
|
|
|
#include <QApplication> |
|
|
|
|
#include <QCompleter> |
|
|
|
@ -62,10 +62,21 @@ void SignalModel::updateState(const QHash<MessageId, CanData> *msgs) { |
|
|
|
|
auto &dat = can->lastMessage(msg_id).dat; |
|
|
|
|
int row = 0; |
|
|
|
|
for (auto item : root->children) { |
|
|
|
|
item->sig_val = QString::number(get_raw_value((uint8_t *)dat.constData(), dat.size(), *item->sig), 'f', item->sig->precision); |
|
|
|
|
double value = get_raw_value((uint8_t *)dat.constData(), dat.size(), *item->sig); |
|
|
|
|
item->sig_val = QString::number(value, 'f', item->sig->precision); |
|
|
|
|
|
|
|
|
|
// Show unit
|
|
|
|
|
if (!item->sig->unit.isEmpty()) { |
|
|
|
|
item->sig_val += " " + item->sig->unit; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Show enum string
|
|
|
|
|
for (auto &[val, desc] : item->sig->val_desc) { |
|
|
|
|
if (std::abs(value - val.toInt()) < 1e-6) { |
|
|
|
|
item->sig_val = desc; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
emit dataChanged(index(row, 1), index(row, 1), {Qt::DisplayRole}); |
|
|
|
|
++row; |
|
|
|
|
} |
|
|
|
@ -366,17 +377,15 @@ void SignalItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op |
|
|
|
|
|
|
|
|
|
void SignalItemDelegate::drawSparkline(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { |
|
|
|
|
static std::vector<QPointF> points; |
|
|
|
|
// TODO: get seconds from settings.
|
|
|
|
|
const uint64_t chart_seconds = 15; // seconds
|
|
|
|
|
const auto &msg_id = ((SignalView *)parent())->msg_id; |
|
|
|
|
const auto &msgs = can->events().at(msg_id); |
|
|
|
|
uint64_t ts = (can->lastMessage(msg_id).ts + can->routeStartTime()) * 1e9; |
|
|
|
|
auto first = std::lower_bound(msgs.cbegin(), msgs.cend(), CanEvent{.mono_time = (uint64_t)std::max<int64_t>(ts - chart_seconds * 1e9, 0)}); |
|
|
|
|
if (first != msgs.cend()) { |
|
|
|
|
auto first = std::lower_bound(msgs.cbegin(), msgs.cend(), CanEvent{.mono_time = (uint64_t)std::max<int64_t>(ts - settings.sparkline_range * 1e9, 0)}); |
|
|
|
|
auto last = std::upper_bound(first, msgs.cend(), CanEvent{.mono_time = ts}); |
|
|
|
|
if (first != last) { |
|
|
|
|
double min = std::numeric_limits<double>::max(); |
|
|
|
|
double max = std::numeric_limits<double>::lowest(); |
|
|
|
|
const auto sig = ((SignalModel::Item *)index.internalPointer())->sig; |
|
|
|
|
auto last = std::lower_bound(first, msgs.cend(), CanEvent{.mono_time = ts}); |
|
|
|
|
points.clear(); |
|
|
|
|
for (auto it = first; it != last; ++it) { |
|
|
|
|
double value = get_raw_value(it->dat, it->size, *sig); |
|
|
|
@ -391,7 +400,7 @@ void SignalItemDelegate::drawSparkline(QPainter *painter, const QStyleOptionView |
|
|
|
|
|
|
|
|
|
int h_margin = option.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin); |
|
|
|
|
int v_margin = std::max(option.widget->style()->pixelMetric(QStyle::PM_FocusFrameVMargin) + 2, 4); |
|
|
|
|
const double xscale = (option.rect.width() - 175.0 * option.widget->devicePixelRatioF() - h_margin * 2) / chart_seconds; |
|
|
|
|
const double xscale = (option.rect.width() - 175.0 - h_margin * 2) / settings.sparkline_range; |
|
|
|
|
const double yscale = (option.rect.height() - v_margin * 2) / (max - min); |
|
|
|
|
const int left = option.rect.left(); |
|
|
|
|
const int top = option.rect.top() + v_margin; |
|
|
|
@ -401,6 +410,13 @@ void SignalItemDelegate::drawSparkline(QPainter *painter, const QStyleOptionView |
|
|
|
|
} |
|
|
|
|
painter->setPen(getColor(sig)); |
|
|
|
|
painter->drawPolyline(points.data(), points.size()); |
|
|
|
|
if ((points.back().x() - points.front().x()) / points.size() > 10) { |
|
|
|
|
painter->setPen(Qt::NoPen); |
|
|
|
|
painter->setBrush(getColor(sig)); |
|
|
|
|
for (const auto &pt : points) { |
|
|
|
|
painter->drawEllipse(pt, 2, 2); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -451,6 +467,17 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), |
|
|
|
|
filter_edit->setPlaceholderText(tr("filter signals")); |
|
|
|
|
hl->addWidget(filter_edit); |
|
|
|
|
hl->addStretch(1); |
|
|
|
|
|
|
|
|
|
// WARNING: increasing the maximum range can result in severe performance degradation.
|
|
|
|
|
// 30s is a reasonable value at present.
|
|
|
|
|
const int max_range = 30; // 30s
|
|
|
|
|
settings.sparkline_range = std::clamp(settings.sparkline_range, 1, max_range); |
|
|
|
|
hl->addWidget(sparkline_label = new QLabel()); |
|
|
|
|
hl->addWidget(sparkline_range_slider = new QSlider(Qt::Horizontal, this)); |
|
|
|
|
sparkline_range_slider->setRange(1, max_range); |
|
|
|
|
sparkline_range_slider->setValue(settings.sparkline_range); |
|
|
|
|
sparkline_range_slider->setToolTip(tr("Sparkline time range")); |
|
|
|
|
|
|
|
|
|
auto collapse_btn = toolButton("dash-square", tr("Collapse All")); |
|
|
|
|
collapse_btn->setIconSize({12, 12}); |
|
|
|
|
hl->addWidget(collapse_btn); |
|
|
|
@ -473,8 +500,10 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts), |
|
|
|
|
main_layout->setSpacing(0); |
|
|
|
|
main_layout->addWidget(title_bar); |
|
|
|
|
main_layout->addWidget(tree); |
|
|
|
|
updateToolBar(); |
|
|
|
|
|
|
|
|
|
QObject::connect(filter_edit, &QLineEdit::textEdited, model, &SignalModel::setFilter); |
|
|
|
|
QObject::connect(sparkline_range_slider, &QSlider::valueChanged, this, &SignalView::setSparklineRange); |
|
|
|
|
QObject::connect(collapse_btn, &QPushButton::clicked, tree, &QTreeView::collapseAll); |
|
|
|
|
QObject::connect(tree, &QAbstractItemView::clicked, this, &SignalView::rowClicked); |
|
|
|
|
QObject::connect(tree, &QTreeView::viewportEntered, [this]() { emit highlight(nullptr); }); |
|
|
|
@ -521,7 +550,7 @@ void SignalView::rowsChanged() { |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
signal_count_lb->setText(tr("Signals: %1").arg(model->rowCount())); |
|
|
|
|
updateToolBar(); |
|
|
|
|
updateChartState(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -569,6 +598,17 @@ void SignalView::signalHovered(const cabana::Signal *sig) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SignalView::updateToolBar() { |
|
|
|
|
signal_count_lb->setText(tr("Signals: %1").arg(model->rowCount())); |
|
|
|
|
sparkline_label->setText(QString("Range: %1 ").arg(utils::formatSeconds(settings.sparkline_range))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SignalView::setSparklineRange(int value) { |
|
|
|
|
settings.sparkline_range = value; |
|
|
|
|
updateToolBar(); |
|
|
|
|
model->updateState(nullptr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SignalView::leaveEvent(QEvent *event) { |
|
|
|
|
emit highlight(nullptr); |
|
|
|
|
QWidget::leaveEvent(event); |