Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openpilot/tools/cabana/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ assets.cc
_cabana
dbc/car_fingerprint_to_dbc.json
tests/test_cabana
tests/test_dbc_core
15 changes: 14 additions & 1 deletion openpilot/tools/cabana/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ cabana_env.Depends(assets_src, str(bootstrap_icons.SVG_PATH))
cabana_env.Depends(assets, Glob('/assets/*', exclude=[assets, assets_src, "assets/assets.o"]))

cabana_srcs = ['mainwin.cc', 'streams/pandastream.cc', 'streams/devicestream.cc', 'streams/livestream.cc', 'streams/abstractstream.cc', 'streams/replaystream.cc', 'binaryview.cc', 'historylog.cc', 'videowidget.cc', 'signalview.cc',
'streams/routes.cc', 'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc',
'streams/routes.cc', 'dbc/dbc.cc', 'dbc/dbcfile.cc', 'dbc/dbcmanager.cc', 'dbc/dbcqt.cc',
'utils/export.cc', 'utils/util.cc', 'utils/elidedlabel.cc',
'chart/chartswidget.cc', 'chart/chart.cc', 'chart/signalselector.cc', 'chart/tiplabel.cc', 'chart/sparkline.cc',
'commands.cc', 'messageswidget.cc', 'streamselector.cc', 'settings.cc', 'panda.cc',
Expand All @@ -110,6 +110,19 @@ cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_lib
if GetOption('extras'):
cabana_env.Program('tests/test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs])

# This target deliberately uses the base environment and links no Qt libraries.
# It prevents Qt dependencies from creeping back into the DBC core.
dbc_core_test_env = env.Clone()
dbc_core_test_env['CXXFLAGS'] += [opendbc_path]
dbc_core_test_objects = [
dbc_core_test_env.Object('tests/dbc_core_test_runner', 'tests/test_runner.cc'),
dbc_core_test_env.Object('tests/dbc_core_tests', 'tests/test_cabana.cc'),
dbc_core_test_env.Object('tests/dbc_core_model', 'dbc/dbc.cc'),
dbc_core_test_env.Object('tests/dbc_core_file', 'dbc/dbcfile.cc'),
dbc_core_test_env.Object('tests/dbc_core_manager', 'dbc/dbcmanager.cc'),
]
dbc_core_test_env.Program('tests/test_dbc_core', dbc_core_test_objects)

output_json_file = 'openpilot/tools/cabana/dbc/car_fingerprint_to_dbc.json'
generate_dbc = cabana_env.Command('#' + output_json_file,
['dbc/generate_dbc_json.py'],
Expand Down
13 changes: 7 additions & 6 deletions openpilot/tools/cabana/binaryview.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "tools/cabana/binaryview.h"
#include "tools/cabana/dbc/dbcqt.h"

#include <algorithm>

Expand Down Expand Up @@ -34,7 +35,7 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) {
setMouseTracking(true);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &BinaryView::refresh);
QObject::connect(dbcNotifier(), &QtDBCNotifier::DBCFileChanged, this, &BinaryView::refresh);
QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &BinaryView::refresh);

addShortcuts();
Expand Down Expand Up @@ -334,7 +335,7 @@ void BinaryViewModel::updateState() {
color.setAlpha(alpha);
updateItem(i, j, bit_val, color);
}
updateItem(i, 8, binary[i], last_msg.colors[i]);
updateItem(i, 8, binary[i], toQColor(last_msg.colors[i]));
}
}

Expand Down Expand Up @@ -421,14 +422,14 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
painter->fillRect(option.rect, item->bg_color);
}
} else if (option.state & QStyle::State_Selected) {
auto color = bin_view->resize_sig ? bin_view->resize_sig->color : option.palette.color(QPalette::Active, QPalette::Highlight);
auto color = bin_view->resize_sig ? toQColor(bin_view->resize_sig->color) : option.palette.color(QPalette::Active, QPalette::Highlight);
painter->fillRect(option.rect, color);
painter->setPen(option.palette.color(QPalette::BrightText));
} else if (!bin_view->selectionModel()->hasSelection() || std::find(item->sigs.begin(), item->sigs.end(), bin_view->resize_sig) == item->sigs.end()) { // not resizing
if (item->sigs.size() > 0) {
for (auto &s : item->sigs) {
if (s == bin_view->hovered_sig) {
painter->fillRect(option.rect, s->color.darker(125)); // 4/5x brightness
painter->fillRect(option.rect, toQColor(s->color.darker(125))); // 4/5x brightness
} else {
drawSignalCell(painter, option, index, s);
}
Expand Down Expand Up @@ -483,14 +484,14 @@ void BinaryItemDelegate::drawSignalCell(QPainter *painter, const QStyleOptionVie
painter->setClipRegion(QRegion(rc).subtracted(subtract));

auto item = (const BinaryViewModel::Item *)index.internalPointer();
QColor color = sig->color;
QColor color = toQColor(sig->color);
color.setAlpha(item->bg_color.alpha());
// Mixing the signal color with the Base background color to fade it
painter->fillRect(rc, option.palette.color(QPalette::Base));
painter->fillRect(rc, color);

// Draw edges
color = sig->color.darker(125);
color = toQColor(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());
Expand Down
19 changes: 10 additions & 9 deletions openpilot/tools/cabana/chart/chart.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "tools/cabana/chart/chart.h"
#include "tools/cabana/dbc/dbcqt.h"

#include <algorithm>
#include <limits>
Expand Down Expand Up @@ -52,10 +53,10 @@ ChartView::ChartView(const std::pair<double, double> &x_range, ChartsWidget *par
QObject::connect(axis_y, &QAbstractAxis::titleTextChanged, this, &ChartView::resetChartCache);
QObject::connect(window()->windowHandle(), &QWindow::screenChanged, this, &ChartView::resetChartCache);

QObject::connect(dbc(), &DBCManager::signalRemoved, this, &ChartView::signalRemoved);
QObject::connect(dbc(), &DBCManager::signalUpdated, this, &ChartView::signalUpdated);
QObject::connect(dbc(), &DBCManager::msgRemoved, this, &ChartView::msgRemoved);
QObject::connect(dbc(), &DBCManager::msgUpdated, this, &ChartView::msgUpdated);
QObject::connect(dbcNotifier(), &QtDBCNotifier::signalRemoved, this, &ChartView::signalRemoved);
QObject::connect(dbcNotifier(), &QtDBCNotifier::signalUpdated, this, &ChartView::signalUpdated);
QObject::connect(dbcNotifier(), &QtDBCNotifier::msgRemoved, this, &ChartView::msgRemoved);
QObject::connect(dbcNotifier(), &QtDBCNotifier::msgUpdated, this, &ChartView::msgUpdated);
}

void ChartView::createToolButtons() {
Expand Down Expand Up @@ -115,14 +116,14 @@ void ChartView::setTheme(QChart::ChartTheme theme) {
axis_x->setLineVisible(false);
axis_y->setLineVisible(false);
for (auto &s : sigs) {
s.series->setColor(s.sig->color);
s.series->setColor(toQColor(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, sig->color);
QXYSeries *series = createSeries(series_type, toQColor(sig->color));
sigs.push_back({.msg_id = msg_id, .sig = sig, .series = series});
updateSeries(sig);
updateSeriesPoints();
Expand Down Expand Up @@ -157,8 +158,8 @@ void ChartView::removeIf(std::function<bool(const SigItem &s)> predicate) {
void ChartView::signalUpdated(const cabana::Signal *sig) {
auto it = std::find_if(sigs.begin(), sigs.end(), [sig](auto &s) { return s.sig == sig; });
if (it != sigs.end()) {
if (it->series->color() != sig->color) {
setSeriesColor(it->series, sig->color);
if (it->series->color() != toQColor(sig->color)) {
setSeriesColor(it->series, toQColor(sig->color));
}
updateTitle();
updateSeries(sig);
Expand Down Expand Up @@ -838,7 +839,7 @@ void ChartView::setSeriesType(SeriesType type) {
s.series->deleteLater();
}
for (auto &s : sigs) {
s.series = createSeries(series_type, s.sig->color);
s.series = createSeries(series_type, toQColor(s.sig->color));
const auto &points = series_type == SeriesType::StepLine ? s.step_vals : s.vals;
s.series->replace(QVector<QPointF>(points.cbegin(), points.cend()));
}
Expand Down
5 changes: 3 additions & 2 deletions openpilot/tools/cabana/chart/chartswidget.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "tools/cabana/chart/chartswidget.h"
#include "tools/cabana/dbc/dbcqt.h"

#include <algorithm>
#include <future>
Expand Down Expand Up @@ -115,7 +116,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) {
align_timer->setSingleShot(true);
QObject::connect(align_timer, &QTimer::timeout, this, &ChartsWidget::alignCharts);
QObject::connect(auto_scroll_timer, &QTimer::timeout, this, &ChartsWidget::doAutoScroll);
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll);
QObject::connect(dbcNotifier(), &QtDBCNotifier::DBCFileChanged, this, &ChartsWidget::removeAll);
QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged);
QObject::connect(can, &AbstractStream::msgsReceived, this, &ChartsWidget::updateState);
QObject::connect(can, &AbstractStream::seeking, this, &ChartsWidget::updateState);
Expand Down Expand Up @@ -309,7 +310,7 @@ void ChartsWidget::splitChart(ChartView *src_chart) {
src_chart->chart()->removeSeries(it->series);

// Restore to the original color
it->series->setColor(it->sig->color);
it->series->setColor(toQColor(it->sig->color));

c->addSeries(it->series);
c->sigs.emplace_back(std::move(*it));
Expand Down
3 changes: 2 additions & 1 deletion openpilot/tools/cabana/chart/signalselector.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "tools/cabana/chart/signalselector.h"
#include "tools/cabana/dbc/dbcqt.h"

#include <QCompleter>
#include <QDialogButtonBox>
Expand Down Expand Up @@ -92,7 +93,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(sig->color.name(), QString::fromStdString(sig->name));
QString text = QString("<span style=\"color:%0;\">■ </span> %1").arg(toQColor(sig->color).name(), QString::fromStdString(sig->name));
if (show_msg_name) text += QString(" <font color=\"gray\">%0 %1</font>").arg(QString::fromStdString(msgName(id)), QString::fromStdString(id.toString()));

QLabel *label = new QLabel(text);
Expand Down
2 changes: 1 addition & 1 deletion openpilot/tools/cabana/chart/sparkline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void Sparkline::update(const cabana::Signal *sig, CanEventIter first, CanEventIt
}

freq_ = points_.size() / std::max(points_.back().x() - points_.front().x(), 1.0);
render(sig->color, range, size);
render(toQColor(sig->color), range, size);
}

void Sparkline::render(const QColor &color, int range, QSize size) {
Expand Down
46 changes: 46 additions & 0 deletions openpilot/tools/cabana/core/can_data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <array>
#include <cstdint>
#include <unordered_map>
#include <vector>

#include "tools/cabana/core/color.h"
#include "tools/cabana/core/message_id.h"

struct CanData {
void compute(const MessageId &msg_id, const uint8_t *data, int size, double current_sec,
double playback_speed, const std::vector<uint8_t> &mask, double frequency = 0);

double ts = 0.;
uint32_t count = 0;
double freq = 0;
std::vector<uint8_t> dat;
std::vector<CabanaColor> colors;

struct ByteLastChange {
double ts = 0;
int delta = 0;
int same_delta_counter = 0;
bool suppressed = false;
};
std::vector<ByteLastChange> last_changes;
std::vector<std::array<uint32_t, 8>> bit_flip_counts;
double last_freq_update_ts = 0;
};

struct CanEvent {
uint8_t src;
uint32_t address;
uint64_t mono_time;
uint8_t size;
uint8_t dat[];
};

struct CompareCanEvent {
constexpr bool operator()(const CanEvent *const event, uint64_t ts) const { return event->mono_time < ts; }
constexpr bool operator()(uint64_t ts, const CanEvent *const event) const { return ts < event->mono_time; }
};

using MessageEventsMap = std::unordered_map<MessageId, std::vector<const CanEvent *>>;
using CanEventIter = std::vector<const CanEvent *>::const_iterator;
79 changes: 79 additions & 0 deletions openpilot/tools/cabana/core/color.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#pragma once

#include <algorithm>
#include <cmath>
#include <cstdint>

struct CabanaColor {
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
uint8_t a = 255;

constexpr CabanaColor() = default;
constexpr CabanaColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255)
: r(red), g(green), b(blue), a(alpha) {}

static CabanaColor fromHsv(float hue, float saturation, float value, float alpha = 1.0f) {
const float h = hue - std::floor(hue);
const float c = value * saturation;
const float x = c * (1.0f - std::fabs(std::fmod(h * 6.0f, 2.0f) - 1.0f));
const float m = value - c;
float red = 0, green = 0, blue = 0;
switch (static_cast<int>(h * 6.0f) % 6) {
case 0: red = c; green = x; break;
case 1: red = x; green = c; break;
case 2: green = c; blue = x; break;
case 3: green = x; blue = c; break;
case 4: red = x; blue = c; break;
default: red = c; blue = x; break;
}
auto channel = [m](float v) { return static_cast<uint8_t>(std::clamp((v + m) * 255.0f, 0.0f, 255.0f) + 0.5f); };
return {channel(red), channel(green), channel(blue),
static_cast<uint8_t>(std::clamp(alpha * 255.0f, 0.0f, 255.0f) + 0.5f)};
}

CabanaColor darker(int factor = 200) const {
if (factor <= 0) return *this;
if (factor < 100) return lighter(10000 / factor);
auto [hue, saturation, value] = hsv();
return fromHsv(hue, saturation, value * 100.0f / factor, a / 255.0f);
}

CabanaColor lighter(int factor = 150) const {
if (factor <= 0) return *this;
if (factor < 100) return darker(10000 / factor);
auto [hue, saturation, value] = hsv();
const float scaled_value = value * factor / 100.0f;
if (scaled_value > 1.0f) saturation = std::max(0.0f, saturation - (scaled_value - 1.0f));
return fromHsv(hue, saturation, std::min(1.0f, scaled_value), a / 255.0f);
}

constexpr int red() const { return r; }
constexpr int green() const { return g; }
constexpr int blue() const { return b; }
constexpr int alpha() const { return a; }
float alphaF() const { return a / 255.0f; }
void setAlphaF(float alpha) { a = static_cast<uint8_t>(std::clamp(alpha * 255.0f, 0.0f, 255.0f) + 0.5f); }

constexpr bool operator==(const CabanaColor &other) const {
return r == other.r && g == other.g && b == other.b && a == other.a;
}

private:
struct Hsv { float hue; float saturation; float value; };
Hsv hsv() const {
const float red = r / 255.0f, green = g / 255.0f, blue = b / 255.0f;
const float maximum = std::max({red, green, blue});
const float minimum = std::min({red, green, blue});
const float delta = maximum - minimum;
float hue = 0;
if (delta > 0) {
if (maximum == red) hue = std::fmod((green - blue) / delta, 6.0f) / 6.0f;
else if (maximum == green) hue = ((blue - red) / delta + 2.0f) / 6.0f;
else hue = ((red - green) / delta + 4.0f) / 6.0f;
if (hue < 0) hue += 1.0f;
}
return {hue, maximum == 0 ? 0 : delta / maximum, maximum};
}
};
27 changes: 27 additions & 0 deletions openpilot/tools/cabana/core/message_id.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once
#include <cstdint>
#include <cstdio>
#include <functional>
#include <string>
#include <tuple>

constexpr int INVALID_SOURCE = 0xff;

struct MessageId {
uint8_t source = 0;
uint32_t address = 0;
std::string toString() const { char b[64]; snprintf(b, sizeof(b), "%u:%X", source, address); return b; }
static MessageId fromString(const std::string &s) {
const auto p = s.find(':');
if (p == std::string::npos) return {};
return {.source = static_cast<uint8_t>(std::stoul(s.substr(0, p))), .address = static_cast<uint32_t>(std::stoul(s.substr(p + 1), nullptr, 16))};
}
bool operator==(const MessageId &o) const { return source == o.source && address == o.address; }
bool operator!=(const MessageId &o) const { return !(*this == o); }
bool operator<(const MessageId &o) const { return std::tie(source, address) < std::tie(o.source, o.address); }
bool operator>(const MessageId &o) const { return o < *this; }
};

template <> struct std::hash<MessageId> {
size_t operator()(const MessageId &id) const noexcept { return std::hash<uint8_t>{}(id.source) ^ (std::hash<uint32_t>{}(id.address) << 1); }
};
34 changes: 34 additions & 0 deletions openpilot/tools/cabana/core/settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <string>
#include <vector>

constexpr int LIGHT_THEME = 1;
constexpr int DARK_THEME = 2;

struct CabanaSettingsState {
enum DragDirection { MsbFirst, LsbFirst, AlwaysLE, AlwaysBE };

bool absolute_time = false;
int fps = 10;
int max_cached_minutes = 30;
int chart_height = 200;
int chart_column_count = 1;
int chart_range = 3 * 60;
int chart_series_type = 0;
int theme = 0;
int sparkline_range = 15;
bool multiple_lines_hex = false;
bool log_livestream = true;
bool suppress_defined_signals = false;
std::string log_path;
std::string last_dir;
std::string last_route_dir;
std::vector<std::string> recent_files;
DragDirection drag_direction = MsbFirst;

std::string recent_dbc_file;
std::string active_msg_id;
std::vector<std::string> selected_msg_ids;
std::vector<std::string> active_charts;
};
Loading
Loading