mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-02-18 21:14:01 +08:00
cabana: simplify stream management and remove problematic autosave feature (#32945)
simplify code and remove problematic code
old-commit-hash: c17c34187b
This commit is contained in:
@@ -38,26 +38,19 @@ int main(int argc, char *argv[]) {
|
||||
cmd_parser.addOption({"dbc", "dbc file to open", "dbc"});
|
||||
cmd_parser.process(app);
|
||||
|
||||
QString dbc_file = cmd_parser.isSet("dbc") ? cmd_parser.value("dbc") : "";
|
||||
|
||||
AbstractStream *stream = nullptr;
|
||||
|
||||
if (cmd_parser.isSet("stream")) {
|
||||
stream = new DeviceStream(&app, cmd_parser.value("zmq"));
|
||||
} else if (cmd_parser.isSet("panda") || cmd_parser.isSet("panda-serial")) {
|
||||
PandaStreamConfig config = {};
|
||||
if (cmd_parser.isSet("panda-serial")) {
|
||||
config.serial = cmd_parser.value("panda-serial");
|
||||
}
|
||||
try {
|
||||
stream = new PandaStream(&app, config);
|
||||
stream = new PandaStream(&app, {.serial = cmd_parser.value("panda-serial")});
|
||||
} catch (std::exception &e) {
|
||||
qWarning() << e.what();
|
||||
return 0;
|
||||
}
|
||||
} else if (cmd_parser.isSet("socketcan")) {
|
||||
SocketCanStreamConfig config = {};
|
||||
config.device = cmd_parser.value("socketcan");
|
||||
stream = new SocketCanStream(&app, config);
|
||||
stream = new SocketCanStream(&app, {.device = cmd_parser.value("socketcan")});
|
||||
} else {
|
||||
uint32_t replay_flags = REPLAY_FLAG_NONE;
|
||||
if (cmd_parser.isSet("ecam")) replay_flags |= REPLAY_FLAG_ECAM;
|
||||
@@ -73,24 +66,14 @@ int main(int argc, char *argv[]) {
|
||||
route = DEMO_ROUTE;
|
||||
}
|
||||
if (!route.isEmpty()) {
|
||||
auto replay_stream = new ReplayStream(&app);
|
||||
auto replay_stream = std::make_unique<ReplayStream>(&app);
|
||||
if (!replay_stream->loadRoute(route, cmd_parser.value("data_dir"), replay_flags)) {
|
||||
return 0;
|
||||
}
|
||||
stream = replay_stream;
|
||||
stream = replay_stream.release();
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow w;
|
||||
if (stream) {
|
||||
stream->start();
|
||||
if (!dbc_file.isEmpty()) {
|
||||
w.loadFile(dbc_file);
|
||||
}
|
||||
} else {
|
||||
w.openStream();
|
||||
}
|
||||
w.show();
|
||||
|
||||
MainWindow w(stream, cmd_parser.value("dbc"));
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -155,11 +155,10 @@ void ChartView::removeIf(std::function<bool(const SigItem &s)> predicate) {
|
||||
}
|
||||
|
||||
void ChartView::signalUpdated(const cabana::Signal *sig) {
|
||||
if (std::any_of(sigs.cbegin(), sigs.cend(), [=](auto &s) { return s.sig == sig; })) {
|
||||
for (const auto &s : sigs) {
|
||||
if (s.sig == sig && s.series->color() != sig->color) {
|
||||
setSeriesColor(s.series, sig->color);
|
||||
}
|
||||
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);
|
||||
}
|
||||
updateTitle();
|
||||
updateSeries(sig);
|
||||
|
||||
@@ -110,7 +110,6 @@ private:
|
||||
QTimer *align_timer;
|
||||
int current_theme = 0;
|
||||
bool value_tip_visible_ = false;
|
||||
friend class ZoomCommand;
|
||||
friend class ChartView;
|
||||
friend class ChartsContainer;
|
||||
};
|
||||
|
||||
@@ -83,7 +83,8 @@ void SignalSelector::updateAvailableList(int index) {
|
||||
MessageId msg_id = msgs_combo->itemData(index).value<MessageId>();
|
||||
auto selected_items = seletedItems();
|
||||
for (auto s : dbc()->msg(msg_id)->getSignals()) {
|
||||
bool is_selected = std::any_of(selected_items.begin(), selected_items.end(), [=, sig = s](auto it) { return it->msg_id == msg_id && it->sig == sig; });
|
||||
bool is_selected = std::any_of(selected_items.begin(), selected_items.end(),
|
||||
[sig = s, &msg_id](auto it) { return it->msg_id == msg_id && it->sig == sig; });
|
||||
if (!is_selected) {
|
||||
addItemToList(available_list, msg_id, s);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <limits>
|
||||
#include <QPainter>
|
||||
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
|
||||
void Sparkline::update(const MessageId &msg_id, const cabana::Signal *sig, double last_msg_ts, int range, QSize size) {
|
||||
const auto &msgs = can->events(msg_id);
|
||||
|
||||
@@ -14,11 +12,6 @@ void Sparkline::update(const MessageId &msg_id, const cabana::Signal *sig, doubl
|
||||
auto first = std::lower_bound(msgs.cbegin(), msgs.cend(), range_start, CompareCanEvent());
|
||||
auto last = std::upper_bound(first, msgs.cend(), range_end, CompareCanEvent());
|
||||
|
||||
if (first == last || size.isEmpty()) {
|
||||
pixmap = QPixmap();
|
||||
return;
|
||||
}
|
||||
|
||||
points.clear();
|
||||
double value = 0;
|
||||
for (auto it = first; it != last; ++it) {
|
||||
@@ -27,7 +20,7 @@ void Sparkline::update(const MessageId &msg_id, const cabana::Signal *sig, doubl
|
||||
}
|
||||
}
|
||||
|
||||
if (points.empty()) {
|
||||
if (points.empty() || size.isEmpty()) {
|
||||
pixmap = QPixmap();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "tools/cabana/dbc/dbc.h"
|
||||
#include "tools/cabana/streams/abstractstream.h"
|
||||
|
||||
class Sparkline {
|
||||
public:
|
||||
|
||||
@@ -9,10 +9,6 @@ DBCFile::DBCFile(const QString &dbc_file_name) {
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
name_ = QFileInfo(dbc_file_name).baseName();
|
||||
filename = dbc_file_name;
|
||||
// Remove auto save file extension
|
||||
if (dbc_file_name.endsWith(AUTO_SAVE_EXTENSION)) {
|
||||
filename.chop(AUTO_SAVE_EXTENSION.length());
|
||||
}
|
||||
parse(file.readAll());
|
||||
} else {
|
||||
throw std::runtime_error("Failed to open file.");
|
||||
@@ -20,17 +16,12 @@ DBCFile::DBCFile(const QString &dbc_file_name) {
|
||||
}
|
||||
|
||||
DBCFile::DBCFile(const QString &name, const QString &content) : name_(name), filename("") {
|
||||
// Open from clipboard
|
||||
parse(content);
|
||||
}
|
||||
|
||||
bool DBCFile::save() {
|
||||
assert(!filename.isEmpty());
|
||||
if (writeContents(filename)) {
|
||||
cleanupAutoSaveFile();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return writeContents(filename);
|
||||
}
|
||||
|
||||
bool DBCFile::saveAs(const QString &new_filename) {
|
||||
@@ -38,16 +29,6 @@ bool DBCFile::saveAs(const QString &new_filename) {
|
||||
return save();
|
||||
}
|
||||
|
||||
bool DBCFile::autoSave() {
|
||||
return !filename.isEmpty() && writeContents(filename + AUTO_SAVE_EXTENSION);
|
||||
}
|
||||
|
||||
void DBCFile::cleanupAutoSaveFile() {
|
||||
if (!filename.isEmpty()) {
|
||||
QFile::remove(filename + AUTO_SAVE_EXTENSION);
|
||||
}
|
||||
}
|
||||
|
||||
bool DBCFile::writeContents(const QString &fn) {
|
||||
QFile file(fn);
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
@@ -75,7 +56,7 @@ cabana::Msg *DBCFile::msg(const QString &name) {
|
||||
return it != msgs.end() ? &(it->second) : nullptr;
|
||||
}
|
||||
|
||||
cabana::Signal *DBCFile::signal(uint32_t address, const QString name) {
|
||||
cabana::Signal *DBCFile::signal(uint32_t address, const QString &name) {
|
||||
auto m = msg(address);
|
||||
return m ? (cabana::Signal *)m->sig(name) : nullptr;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
#include "tools/cabana/dbc/dbc.h"
|
||||
|
||||
const QString AUTO_SAVE_EXTENSION = ".tmp";
|
||||
|
||||
class DBCFile {
|
||||
public:
|
||||
DBCFile(const QString &dbc_file_name);
|
||||
@@ -15,9 +13,7 @@ public:
|
||||
|
||||
bool save();
|
||||
bool saveAs(const QString &new_filename);
|
||||
bool autoSave();
|
||||
bool writeContents(const QString &fn);
|
||||
void cleanupAutoSaveFile();
|
||||
QString generateDBC();
|
||||
|
||||
void updateMsg(const MessageId &id, const QString &name, uint32_t size, const QString &node, const QString &comment);
|
||||
@@ -27,7 +23,7 @@ public:
|
||||
cabana::Msg *msg(uint32_t address);
|
||||
cabana::Msg *msg(const QString &name);
|
||||
inline cabana::Msg *msg(const MessageId &id) { return msg(id.address); }
|
||||
cabana::Signal *signal(uint32_t address, const QString name);
|
||||
cabana::Signal *signal(uint32_t address, const QString &name);
|
||||
|
||||
inline QString name() const { return name_.isEmpty() ? "untitled" : name_; }
|
||||
inline bool isEmpty() const { return msgs.empty() && name_.isEmpty(); }
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <QJsonObject>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
#include <QProgressDialog>
|
||||
#include <QResizeEvent>
|
||||
#include <QShortcut>
|
||||
#include <QTextDocument>
|
||||
@@ -23,9 +24,8 @@
|
||||
#include "tools/cabana/streamselector.h"
|
||||
#include "tools/cabana/tools/findsignal.h"
|
||||
#include "tools/cabana/utils/export.h"
|
||||
#include "tools/replay/replay.h"
|
||||
|
||||
MainWindow::MainWindow() : QMainWindow() {
|
||||
MainWindow::MainWindow(AbstractStream *stream, const QString &dbc_file) : QMainWindow() {
|
||||
loadFingerprints();
|
||||
createDockWindows();
|
||||
setCentralWidget(center_widget = new CenterWidget(this));
|
||||
@@ -65,10 +65,10 @@ MainWindow::MainWindow() : QMainWindow() {
|
||||
QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress);
|
||||
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MainWindow::DBCFileChanged);
|
||||
QObject::connect(UndoStack::instance(), &QUndoStack::cleanChanged, this, &MainWindow::undoStackCleanChanged);
|
||||
QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &MainWindow::undoStackIndexChanged);
|
||||
QObject::connect(&settings, &Settings::changed, this, &MainWindow::updateStatus);
|
||||
QObject::connect(StreamNotifier::instance(), &StreamNotifier::changingStream, this, &MainWindow::changingStream);
|
||||
QObject::connect(StreamNotifier::instance(), &StreamNotifier::streamStarted, this, &MainWindow::streamStarted);
|
||||
|
||||
QTimer::singleShot(0, this, [=]() { stream ? openStream(stream, dbc_file) : selectAndOpenStream(); });
|
||||
show();
|
||||
}
|
||||
|
||||
void MainWindow::loadFingerprints() {
|
||||
@@ -76,16 +76,12 @@ void MainWindow::loadFingerprints() {
|
||||
if (json_file.open(QIODevice::ReadOnly)) {
|
||||
fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll());
|
||||
}
|
||||
// get opendbc names
|
||||
for (auto fn : QDir(OPENDBC_FILE_PATH).entryList({"*.dbc"}, QDir::Files, QDir::Name)) {
|
||||
opendbc_names << QFileInfo(fn).baseName();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::createActions() {
|
||||
// File menu
|
||||
QMenu *file_menu = menuBar()->addMenu(tr("&File"));
|
||||
file_menu->addAction(tr("Open Stream..."), this, &MainWindow::openStream);
|
||||
file_menu->addAction(tr("Open Stream..."), this, &MainWindow::selectAndOpenStream);
|
||||
close_stream_act = file_menu->addAction(tr("Close stream"), this, &MainWindow::closeStream);
|
||||
export_to_csv_act = file_menu->addAction(tr("Export to CSV..."), this, &MainWindow::exportToCSV);
|
||||
close_stream_act->setEnabled(false);
|
||||
@@ -96,20 +92,15 @@ void MainWindow::createActions() {
|
||||
file_menu->addAction(tr("Open DBC File..."), [this]() { openFile(); }, QKeySequence::Open);
|
||||
|
||||
manage_dbcs_menu = file_menu->addMenu(tr("Manage &DBC Files"));
|
||||
QObject::connect(manage_dbcs_menu, &QMenu::aboutToShow, this, &MainWindow::updateLoadSaveMenus);
|
||||
|
||||
open_recent_menu = file_menu->addMenu(tr("Open &Recent"));
|
||||
for (int i = 0; i < MAX_RECENT_FILES; ++i) {
|
||||
recent_files_acts[i] = new QAction(this);
|
||||
recent_files_acts[i]->setVisible(false);
|
||||
QObject::connect(recent_files_acts[i], &QAction::triggered, this, &MainWindow::openRecentFile);
|
||||
open_recent_menu->addAction(recent_files_acts[i]);
|
||||
}
|
||||
updateRecentFileActions();
|
||||
QObject::connect(open_recent_menu, &QMenu::aboutToShow, this, &MainWindow::updateRecentFileMenu);
|
||||
|
||||
file_menu->addSeparator();
|
||||
QMenu *load_opendbc_menu = file_menu->addMenu(tr("Load DBC from commaai/opendbc"));
|
||||
// load_opendbc_menu->setStyleSheet("QMenu { menu-scrollable: true; }");
|
||||
for (const auto &dbc_name : opendbc_names) {
|
||||
for (const auto &dbc_name : QDir(OPENDBC_FILE_PATH).entryList({"*.dbc"}, QDir::Files, QDir::Name)) {
|
||||
load_opendbc_menu->addAction(dbc_name, [this, name = dbc_name]() { loadDBCFromOpendbc(name); });
|
||||
}
|
||||
|
||||
@@ -180,6 +171,7 @@ void MainWindow::createDockWidgets() {
|
||||
messages_widget = new MessagesWidget(this);
|
||||
messages_dock->setWidget(messages_widget);
|
||||
QObject::connect(messages_widget, &MessagesWidget::titleChanged, messages_dock, &QDockWidget::setWindowTitle);
|
||||
QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage);
|
||||
|
||||
// right panel
|
||||
charts_widget = new ChartsWidget(this);
|
||||
@@ -209,67 +201,52 @@ void MainWindow::createStatusBar() {
|
||||
progress_bar->setVisible(false);
|
||||
statusBar()->addWidget(new QLabel(tr("For Help, Press F1")));
|
||||
statusBar()->addPermanentWidget(progress_bar);
|
||||
|
||||
statusBar()->addPermanentWidget(status_label = new QLabel(this));
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void MainWindow::createShortcuts() {
|
||||
auto shortcut = new QShortcut(QKeySequence(Qt::Key_Space), this, nullptr, nullptr, Qt::ApplicationShortcut);
|
||||
QObject::connect(shortcut, &QShortcut::activated, []() { can->pause(!can->isPaused()); });
|
||||
QObject::connect(shortcut, &QShortcut::activated, this, []() {
|
||||
if (can) can->pause(!can->isPaused());
|
||||
});
|
||||
// TODO: add more shortcuts here.
|
||||
}
|
||||
|
||||
void MainWindow::undoStackIndexChanged(int index) {
|
||||
int count = UndoStack::instance()->count();
|
||||
if (count >= 0) {
|
||||
QString command_text;
|
||||
if (index == count) {
|
||||
command_text = (count == prev_undostack_count ? "Redo " : "") + UndoStack::instance()->text(index - 1);
|
||||
} else if (index < prev_undostack_index) {
|
||||
command_text = tr("Undo %1").arg(UndoStack::instance()->text(index));
|
||||
} else if (index > prev_undostack_index) {
|
||||
command_text = tr("Redo %1").arg(UndoStack::instance()->text(index - 1));
|
||||
}
|
||||
statusBar()->showMessage(command_text, 2000);
|
||||
}
|
||||
prev_undostack_index = index;
|
||||
prev_undostack_count = count;
|
||||
autoSave();
|
||||
updateLoadSaveMenus();
|
||||
}
|
||||
|
||||
void MainWindow::undoStackCleanChanged(bool clean) {
|
||||
if (clean) {
|
||||
prev_undostack_index = 0;
|
||||
prev_undostack_count = 0;
|
||||
}
|
||||
setWindowModified(!clean);
|
||||
}
|
||||
|
||||
void MainWindow::DBCFileChanged() {
|
||||
UndoStack::instance()->clear();
|
||||
updateLoadSaveMenus();
|
||||
|
||||
// Update file menu
|
||||
int cnt = dbc()->nonEmptyDBCCount();
|
||||
save_dbc->setText(cnt > 1 ? tr("Save %1 DBCs...").arg(cnt) : tr("Save DBC..."));
|
||||
save_dbc->setEnabled(cnt > 0);
|
||||
save_dbc_as->setEnabled(cnt == 1);
|
||||
// TODO: Support clipboard for multiple files
|
||||
copy_dbc_to_clipboard->setEnabled(cnt == 1);
|
||||
manage_dbcs_menu->setEnabled(dynamic_cast<DummyStream *>(can) == nullptr);
|
||||
|
||||
QStringList title;
|
||||
for (auto f : dbc()->allDBCFiles()) {
|
||||
title.push_back(tr("(%1) %2").arg(toString(dbc()->sources(f)), f->name()));
|
||||
}
|
||||
setWindowFilePath(title.join(" | "));
|
||||
}
|
||||
|
||||
void MainWindow::openStream() {
|
||||
AbstractStream *stream = nullptr;
|
||||
StreamSelector dlg(&stream, this);
|
||||
void MainWindow::selectAndOpenStream() {
|
||||
StreamSelector dlg(this);
|
||||
if (dlg.exec()) {
|
||||
if (!dlg.dbcFile().isEmpty()) {
|
||||
loadFile(dlg.dbcFile());
|
||||
}
|
||||
stream->start();
|
||||
statusBar()->showMessage(tr("Route %1 loaded").arg(can->routeName()), 2000);
|
||||
openStream(dlg.stream(), dlg.dbcFile());
|
||||
} else if (!can) {
|
||||
stream = new DummyStream(this);
|
||||
stream->start();
|
||||
openStream(new DummyStream(this));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::closeStream() {
|
||||
AbstractStream *stream = new DummyStream(this);
|
||||
stream->start();
|
||||
openStream(new DummyStream(this));
|
||||
if (dbc()->nonEmptyDBCCount() > 0) {
|
||||
emit dbc()->DBCFileChanged();
|
||||
}
|
||||
@@ -301,18 +278,8 @@ void MainWindow::loadFile(const QString &fn, SourceSet s) {
|
||||
if (!fn.isEmpty()) {
|
||||
closeFile(s);
|
||||
|
||||
QString dbc_fn = fn;
|
||||
// Prompt user to load auto saved file if it exists.
|
||||
if (QFile::exists(fn + AUTO_SAVE_EXTENSION)) {
|
||||
auto ret = QMessageBox::question(this, tr("Auto saved DBC found"), tr("Auto saved DBC file from previous session found. Do you want to load it instead?"));
|
||||
if (ret == QMessageBox::Yes) {
|
||||
dbc_fn += AUTO_SAVE_EXTENSION;
|
||||
UndoStack::instance()->resetClean(); // Force user to save on close so the auto saved file is not lost
|
||||
}
|
||||
}
|
||||
|
||||
QString error;
|
||||
if (dbc()->open(s, dbc_fn, &error)) {
|
||||
if (dbc()->open(s, fn, &error)) {
|
||||
updateRecentFiles(fn);
|
||||
statusBar()->showMessage(tr("DBC File %1 loaded").arg(fn), 2000);
|
||||
} else {
|
||||
@@ -323,15 +290,8 @@ void MainWindow::loadFile(const QString &fn, SourceSet s) {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::openRecentFile() {
|
||||
if (auto action = qobject_cast<QAction *>(sender())) {
|
||||
loadFile(action->data().toString());
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::loadDBCFromOpendbc(const QString &name) {
|
||||
QString opendbc_file_path = QString("%1/%2.dbc").arg(OPENDBC_FILE_PATH, name);
|
||||
loadFile(opendbc_file_path);
|
||||
loadFile(QString("%1/%2").arg(OPENDBC_FILE_PATH, name));
|
||||
}
|
||||
|
||||
void MainWindow::loadFromClipboard(SourceSet s, bool close_all) {
|
||||
@@ -349,13 +309,19 @@ void MainWindow::loadFromClipboard(SourceSet s, bool close_all) {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::changingStream() {
|
||||
void MainWindow::openStream(AbstractStream *stream, const QString &dbc_file) {
|
||||
center_widget->clear();
|
||||
delete messages_widget;
|
||||
delete video_splitter;
|
||||
}
|
||||
|
||||
void MainWindow::streamStarted() {
|
||||
delete can;
|
||||
can = stream;
|
||||
can->setParent(this); // take ownership
|
||||
can->start();
|
||||
|
||||
loadFile(dbc_file);
|
||||
statusBar()->showMessage(tr("Stream [%1] started").arg(can->routeName()), 2000);
|
||||
|
||||
bool has_stream = dynamic_cast<DummyStream *>(can) == nullptr;
|
||||
close_stream_act->setEnabled(has_stream);
|
||||
export_to_csv_act->setEnabled(has_stream);
|
||||
@@ -372,9 +338,20 @@ void MainWindow::streamStarted() {
|
||||
newFile();
|
||||
}
|
||||
|
||||
QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, center_widget, &CenterWidget::setMessage);
|
||||
QObject::connect(can, &AbstractStream::eventsMerged, this, &MainWindow::eventsMerged);
|
||||
QObject::connect(can, &AbstractStream::sourcesUpdated, this, &MainWindow::updateLoadSaveMenus);
|
||||
|
||||
if (has_stream) {
|
||||
auto wait_dlg = new QProgressDialog(
|
||||
can->liveStreaming() ? tr("Waiting for the live stream to start...") : tr("Loading segment data..."),
|
||||
tr("&Abort"), 0, 100, this);
|
||||
wait_dlg->setWindowModality(Qt::WindowModal);
|
||||
wait_dlg->setFixedSize(400, wait_dlg->sizeHint().height());
|
||||
QObject::connect(wait_dlg, &QProgressDialog::canceled, this, &MainWindow::close);
|
||||
QObject::connect(can, &AbstractStream::eventsMerged, wait_dlg, &QProgressDialog::deleteLater);
|
||||
QObject::connect(this, &MainWindow::updateProgressBar, wait_dlg, [=](uint64_t cur, uint64_t total, bool success) {
|
||||
wait_dlg->setValue((int)((cur / (double)total) * 100));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::eventsMerged() {
|
||||
@@ -383,12 +360,8 @@ void MainWindow::eventsMerged() {
|
||||
.arg(can->routeName())
|
||||
.arg(car_fingerprint.isEmpty() ? tr("Unknown Car") : car_fingerprint));
|
||||
// Don't overwrite already loaded DBC
|
||||
if (!dbc()->nonEmptyDBCCount() && !car_fingerprint.isEmpty()) {
|
||||
auto dbc_name = fingerprint_to_dbc[car_fingerprint];
|
||||
if (dbc_name != QJsonValue::Undefined) {
|
||||
// Prevent dialog that load autosaved file from blocking replay->start().
|
||||
QTimer::singleShot(0, this, [dbc_name, this]() { loadDBCFromOpendbc(dbc_name.toString()); });
|
||||
}
|
||||
if (!dbc()->nonEmptyDBCCount() && fingerprint_to_dbc.object().contains(car_fingerprint)) {
|
||||
QTimer::singleShot(0, this, [this]() { loadDBCFromOpendbc(fingerprint_to_dbc[car_fingerprint].toString() + ".dbc"); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -409,22 +382,6 @@ void MainWindow::saveAs() {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::autoSave() {
|
||||
if (!UndoStack::instance()->isClean()) {
|
||||
for (auto dbc_file : dbc()->allDBCFiles()) {
|
||||
if (!dbc_file->filename.isEmpty()) {
|
||||
dbc_file->autoSave();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::cleanupAutoSaveFile() {
|
||||
for (auto dbc_file : dbc()->allDBCFiles()) {
|
||||
dbc_file->cleanupAutoSaveFile();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::closeFile(SourceSet s) {
|
||||
remindSaveChanges();
|
||||
if (s == SOURCE_ALL) {
|
||||
@@ -448,7 +405,6 @@ void MainWindow::saveFile(DBCFile *dbc_file) {
|
||||
assert(dbc_file != nullptr);
|
||||
if (!dbc_file->filename.isEmpty()) {
|
||||
dbc_file->save();
|
||||
updateLoadSaveMenus();
|
||||
UndoStack::instance()->setClean();
|
||||
statusBar()->showMessage(tr("File saved"), 2000);
|
||||
} else if (!dbc_file->isEmpty()) {
|
||||
@@ -464,7 +420,6 @@ void MainWindow::saveFileAs(DBCFile *dbc_file) {
|
||||
UndoStack::instance()->setClean();
|
||||
statusBar()->showMessage(tr("File saved as %1").arg(fn), 2000);
|
||||
updateRecentFiles(fn);
|
||||
updateLoadSaveMenus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,16 +438,7 @@ void MainWindow::saveFileToClipboard(DBCFile *dbc_file) {
|
||||
}
|
||||
|
||||
void MainWindow::updateLoadSaveMenus() {
|
||||
int cnt = dbc()->nonEmptyDBCCount();
|
||||
save_dbc->setText(cnt > 1 ? tr("Save %1 DBCs...").arg(cnt) : tr("Save DBC..."));
|
||||
save_dbc->setEnabled(cnt > 0);
|
||||
save_dbc_as->setEnabled(cnt == 1);
|
||||
|
||||
// TODO: Support clipboard for multiple files
|
||||
copy_dbc_to_clipboard->setEnabled(cnt == 1);
|
||||
|
||||
manage_dbcs_menu->clear();
|
||||
manage_dbcs_menu->setEnabled(dynamic_cast<DummyStream *>(can) == nullptr);
|
||||
|
||||
for (int source : can->sources) {
|
||||
if (source >= 64) continue; // Sent and blocked buses are handled implicitly
|
||||
@@ -505,8 +451,8 @@ void MainWindow::updateLoadSaveMenus() {
|
||||
bus_menu->addAction(tr("Load DBC From Clipboard..."), [=]() { loadFromClipboard(ss, false); });
|
||||
|
||||
// Show sub-menu for each dbc for this source.
|
||||
QString file_name = "No DBCs loaded";
|
||||
if (auto dbc_file = dbc()->findDBCFile(source)) {
|
||||
auto dbc_file = dbc()->findDBCFile(source);
|
||||
if (dbc_file) {
|
||||
bus_menu->addSeparator();
|
||||
bus_menu->addAction(dbc_file->name() + " (" + toString(dbc()->sources(dbc_file)) + ")")->setEnabled(false);
|
||||
bus_menu->addAction(tr("Save..."), [=]() { saveFile(dbc_file); });
|
||||
@@ -514,19 +460,11 @@ void MainWindow::updateLoadSaveMenus() {
|
||||
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); });
|
||||
|
||||
file_name = dbc_file->name();
|
||||
}
|
||||
bus_menu->setTitle(tr("Bus %1 (%2)").arg(source).arg(dbc_file ? dbc_file->name() : "No DBCs loaded"));
|
||||
|
||||
manage_dbcs_menu->addMenu(bus_menu);
|
||||
bus_menu->setTitle(tr("Bus %1 (%2)").arg(source).arg(file_name));
|
||||
}
|
||||
|
||||
QStringList title;
|
||||
for (auto f : dbc()->allDBCFiles()) {
|
||||
title.push_back(tr("(%1) %2").arg(toString(dbc()->sources(f)), f->name()));
|
||||
}
|
||||
setWindowFilePath(title.join(" | "));
|
||||
}
|
||||
|
||||
void MainWindow::updateRecentFiles(const QString &fn) {
|
||||
@@ -536,21 +474,21 @@ void MainWindow::updateRecentFiles(const QString &fn) {
|
||||
settings.recent_files.removeLast();
|
||||
}
|
||||
settings.last_dir = QFileInfo(fn).absolutePath();
|
||||
updateRecentFileActions();
|
||||
}
|
||||
|
||||
void MainWindow::updateRecentFileActions() {
|
||||
void MainWindow::updateRecentFileMenu() {
|
||||
open_recent_menu->clear();
|
||||
|
||||
int num_recent_files = std::min<int>(settings.recent_files.size(), MAX_RECENT_FILES);
|
||||
if (!num_recent_files) {
|
||||
open_recent_menu->addAction(tr("No Recent Files"))->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_recent_files; ++i) {
|
||||
QString text = tr("&%1 %2").arg(i + 1).arg(QFileInfo(settings.recent_files[i]).fileName());
|
||||
recent_files_acts[i]->setText(text);
|
||||
recent_files_acts[i]->setData(settings.recent_files[i]);
|
||||
recent_files_acts[i]->setVisible(true);
|
||||
open_recent_menu->addAction(text, this, [this, i=i](){ loadFile(settings.recent_files[i]); });
|
||||
}
|
||||
for (int i = num_recent_files; i < MAX_RECENT_FILES; ++i) {
|
||||
recent_files_acts[i]->setVisible(false);
|
||||
}
|
||||
open_recent_menu->setEnabled(num_recent_files > 0);
|
||||
}
|
||||
|
||||
void MainWindow::remindSaveChanges() {
|
||||
@@ -606,7 +544,6 @@ void MainWindow::toggleChartsDocking() {
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent *event) {
|
||||
cleanupAutoSaveFile();
|
||||
remindSaveChanges();
|
||||
|
||||
installDownloadProgressHandler(nullptr);
|
||||
@@ -618,7 +555,7 @@ void MainWindow::closeEvent(QCloseEvent *event) {
|
||||
// save states
|
||||
settings.geometry = saveGeometry();
|
||||
settings.window_state = saveState();
|
||||
if (!can->liveStreaming()) {
|
||||
if (can && !can->liveStreaming()) {
|
||||
settings.video_splitter_state = video_splitter->saveState();
|
||||
}
|
||||
settings.message_header_state = messages_widget->saveHeaderState();
|
||||
|
||||
@@ -20,22 +20,20 @@ class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow();
|
||||
MainWindow(AbstractStream *stream, const QString &dbc_file);
|
||||
void toggleChartsDocking();
|
||||
void showStatusMessage(const QString &msg, int timeout = 0) { statusBar()->showMessage(msg, timeout); }
|
||||
void loadFile(const QString &fn, SourceSet s = SOURCE_ALL);
|
||||
ChartsWidget *charts_widget = nullptr;
|
||||
|
||||
public slots:
|
||||
void openStream();
|
||||
void selectAndOpenStream();
|
||||
void openStream(AbstractStream *stream, const QString &dbc_file = {});
|
||||
void closeStream();
|
||||
void exportToCSV();
|
||||
void changingStream();
|
||||
void streamStarted();
|
||||
|
||||
void newFile(SourceSet s = SOURCE_ALL);
|
||||
void openFile(SourceSet s = SOURCE_ALL);
|
||||
void openRecentFile();
|
||||
void loadDBCFromOpendbc(const QString &name);
|
||||
void save();
|
||||
void saveAs();
|
||||
@@ -55,10 +53,8 @@ protected:
|
||||
void saveFileToClipboard(DBCFile *dbc_file);
|
||||
void loadFingerprints();
|
||||
void loadFromClipboard(SourceSet s = SOURCE_ALL, bool close_all = true);
|
||||
void autoSave();
|
||||
void cleanupAutoSaveFile();
|
||||
void updateRecentFiles(const QString &fn);
|
||||
void updateRecentFileActions();
|
||||
void updateRecentFileMenu();
|
||||
void createActions();
|
||||
void createDockWindows();
|
||||
void createStatusBar();
|
||||
@@ -70,7 +66,6 @@ protected:
|
||||
void findSimilarBits();
|
||||
void findSignal();
|
||||
void undoStackCleanChanged(bool clean);
|
||||
void undoStackIndexChanged(int index);
|
||||
void onlineHelp();
|
||||
void toggleFullScreen();
|
||||
void updateStatus();
|
||||
@@ -88,10 +83,8 @@ protected:
|
||||
QProgressBar *progress_bar;
|
||||
QLabel *status_label;
|
||||
QJsonDocument fingerprint_to_dbc;
|
||||
QStringList opendbc_names;
|
||||
QSplitter *video_splitter = nullptr;
|
||||
enum { MAX_RECENT_FILES = 15 };
|
||||
QAction *recent_files_acts[MAX_RECENT_FILES] = {};
|
||||
QMenu *open_recent_menu = nullptr;
|
||||
QMenu *manage_dbcs_menu = nullptr;
|
||||
QMenu *tools_menu = nullptr;
|
||||
@@ -101,8 +94,6 @@ protected:
|
||||
QAction *save_dbc_as = nullptr;
|
||||
QAction *copy_dbc_to_clipboard = nullptr;
|
||||
QString car_fingerprint;
|
||||
int prev_undostack_index = 0;
|
||||
int prev_undostack_count = 0;
|
||||
QByteArray default_state;
|
||||
};
|
||||
|
||||
|
||||
@@ -494,6 +494,7 @@ SignalView::SignalView(ChartsWidget *charts, QWidget *parent) : charts(charts),
|
||||
}
|
||||
|
||||
void SignalView::setMessage(const MessageId &id) {
|
||||
max_value_width = 0;
|
||||
filter_edit->clear();
|
||||
model->setMessage(id);
|
||||
}
|
||||
@@ -617,7 +618,6 @@ void SignalView::updateState(const std::set<MessageId> *msgs) {
|
||||
const auto &last_msg = can->lastMessage(model->msg_id);
|
||||
if (model->rowCount() == 0 || (msgs && !msgs->count(model->msg_id)) || last_msg.dat.size() == 0) return;
|
||||
|
||||
int max_value_width = 0;
|
||||
for (auto item : model->root->children) {
|
||||
double value = 0;
|
||||
if (item->sig->getValue(last_msg.dat.data(), last_msg.dat.size(), &value)) {
|
||||
|
||||
@@ -135,6 +135,7 @@ private:
|
||||
QTreeView::leaveEvent(event);
|
||||
}
|
||||
};
|
||||
int max_value_width = 0;
|
||||
int value_column_width = 0;
|
||||
TreeView *tree;
|
||||
QLabel *sparkline_label;
|
||||
|
||||
@@ -10,11 +10,6 @@ static const int EVENT_NEXT_BUFFER_SIZE = 6 * 1024 * 1024; // 6MB
|
||||
|
||||
AbstractStream *can = nullptr;
|
||||
|
||||
StreamNotifier *StreamNotifier::instance() {
|
||||
static StreamNotifier notifier;
|
||||
return ¬ifier;
|
||||
}
|
||||
|
||||
AbstractStream::AbstractStream(QObject *parent) : QObject(parent) {
|
||||
assert(parent != nullptr);
|
||||
event_buffer_ = std::make_unique<MonotonicBuffer>(EVENT_NEXT_BUFFER_SIZE);
|
||||
@@ -24,12 +19,6 @@ AbstractStream::AbstractStream(QObject *parent) : QObject(parent) {
|
||||
QObject::connect(this, &AbstractStream::seeking, this, [this](double sec) { current_sec_ = sec; });
|
||||
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &AbstractStream::updateMasks);
|
||||
QObject::connect(dbc(), &DBCManager::maskUpdated, this, &AbstractStream::updateMasks);
|
||||
QObject::connect(this, &AbstractStream::streamStarted, [this]() {
|
||||
emit StreamNotifier::instance()->changingStream();
|
||||
delete can;
|
||||
can = this;
|
||||
emit StreamNotifier::instance()->streamStarted();
|
||||
});
|
||||
}
|
||||
|
||||
void AbstractStream::updateMasks() {
|
||||
|
||||
@@ -96,7 +96,6 @@ signals:
|
||||
void seeking(double sec);
|
||||
void seekedTo(double sec);
|
||||
void timeRangeChanged(const std::optional<std::pair<double, double>> &range);
|
||||
void streamStarted();
|
||||
void eventsMerged(const MessageEventsMap &events_map);
|
||||
void msgsReceived(const std::set<MessageId> *new_msgs, bool has_new_ids);
|
||||
void sourcesUpdated(const SourceSet &s);
|
||||
@@ -133,15 +132,11 @@ private:
|
||||
class AbstractOpenStreamWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AbstractOpenStreamWidget(AbstractStream **stream, QWidget *parent = nullptr) : stream(stream), QWidget(parent) {}
|
||||
virtual bool open() = 0;
|
||||
virtual QString title() = 0;
|
||||
AbstractOpenStreamWidget(QWidget *parent = nullptr) : QWidget(parent) {}
|
||||
virtual AbstractStream *open() = 0;
|
||||
|
||||
signals:
|
||||
void enableOpenButton(bool);
|
||||
|
||||
protected:
|
||||
AbstractStream **stream = nullptr;
|
||||
};
|
||||
|
||||
class DummyStream : public AbstractStream {
|
||||
@@ -149,17 +144,7 @@ class DummyStream : public AbstractStream {
|
||||
public:
|
||||
DummyStream(QObject *parent) : AbstractStream(parent) {}
|
||||
QString routeName() const override { return tr("No Stream"); }
|
||||
void start() override { emit streamStarted(); }
|
||||
};
|
||||
|
||||
class StreamNotifier : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
StreamNotifier(QObject *parent = nullptr) : QObject(parent) {}
|
||||
static StreamNotifier* instance();
|
||||
signals:
|
||||
void streamStarted();
|
||||
void changingStream();
|
||||
void start() override {}
|
||||
};
|
||||
|
||||
// A global pointer referring to the unique AbstractStream object
|
||||
|
||||
@@ -33,13 +33,9 @@ void DeviceStream::streamThread() {
|
||||
}
|
||||
}
|
||||
|
||||
AbstractOpenStreamWidget *DeviceStream::widget(AbstractStream **stream) {
|
||||
return new OpenDeviceWidget(stream);
|
||||
}
|
||||
|
||||
// OpenDeviceWidget
|
||||
|
||||
OpenDeviceWidget::OpenDeviceWidget(AbstractStream **stream) : AbstractOpenStreamWidget(stream) {
|
||||
OpenDeviceWidget::OpenDeviceWidget(QWidget *parent) : AbstractOpenStreamWidget(parent) {
|
||||
QRadioButton *msgq = new QRadioButton(tr("MSGQ"));
|
||||
QRadioButton *zmq = new QRadioButton(tr("ZMQ"));
|
||||
ip_address = new QLineEdit(this);
|
||||
@@ -62,9 +58,8 @@ OpenDeviceWidget::OpenDeviceWidget(AbstractStream **stream) : AbstractOpenStream
|
||||
zmq->setChecked(true);
|
||||
}
|
||||
|
||||
bool OpenDeviceWidget::open() {
|
||||
AbstractStream *OpenDeviceWidget::open() {
|
||||
QString ip = ip_address->text().isEmpty() ? "127.0.0.1" : ip_address->text();
|
||||
bool msgq = group->checkedId() == 0;
|
||||
*stream = new DeviceStream(qApp, msgq ? "" : ip);
|
||||
return true;
|
||||
return new DeviceStream(qApp, msgq ? "" : ip);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ class DeviceStream : public LiveStream {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DeviceStream(QObject *parent, QString address = {});
|
||||
static AbstractOpenStreamWidget *widget(AbstractStream **stream);
|
||||
inline QString routeName() const override {
|
||||
return QString("Live Streaming From %1").arg(zmq_address.isEmpty() ? "127.0.0.1" : zmq_address);
|
||||
}
|
||||
@@ -20,9 +19,8 @@ class OpenDeviceWidget : public AbstractOpenStreamWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OpenDeviceWidget(AbstractStream **stream);
|
||||
bool open() override;
|
||||
QString title() override { return tr("&Device"); }
|
||||
OpenDeviceWidget(QWidget *parent = nullptr);
|
||||
AbstractStream *open() override;
|
||||
|
||||
private:
|
||||
QLineEdit *ip_address;
|
||||
|
||||
@@ -53,7 +53,6 @@ void LiveStream::startUpdateTimer() {
|
||||
}
|
||||
|
||||
void LiveStream::start() {
|
||||
emit streamStarted();
|
||||
stream_thread->start();
|
||||
startUpdateTimer();
|
||||
begin_date_time = QDateTime::currentDateTime();
|
||||
|
||||
@@ -77,13 +77,9 @@ void PandaStream::streamThread() {
|
||||
}
|
||||
}
|
||||
|
||||
AbstractOpenStreamWidget *PandaStream::widget(AbstractStream **stream) {
|
||||
return new OpenPandaWidget(stream);
|
||||
}
|
||||
|
||||
// OpenPandaWidget
|
||||
|
||||
OpenPandaWidget::OpenPandaWidget(AbstractStream **stream) : AbstractOpenStreamWidget(stream) {
|
||||
OpenPandaWidget::OpenPandaWidget(QWidget *parent) : AbstractOpenStreamWidget(parent) {
|
||||
form_layout = new QFormLayout(this);
|
||||
if (can && dynamic_cast<PandaStream *>(can) != nullptr) {
|
||||
form_layout->addWidget(new QLabel(tr("Already connected to %1.").arg(can->routeName())));
|
||||
@@ -182,12 +178,11 @@ void OpenPandaWidget::buildConfigForm() {
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenPandaWidget::open() {
|
||||
AbstractStream *OpenPandaWidget::open() {
|
||||
try {
|
||||
*stream = new PandaStream(qApp, config);
|
||||
return true;
|
||||
return new PandaStream(qApp, config);
|
||||
} catch (std::exception &e) {
|
||||
QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to connect to panda: '%1'").arg(e.what()));
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ class PandaStream : public LiveStream {
|
||||
public:
|
||||
PandaStream(QObject *parent, PandaStreamConfig config_ = {});
|
||||
~PandaStream() { stop(); }
|
||||
static AbstractOpenStreamWidget *widget(AbstractStream **stream);
|
||||
inline QString routeName() const override {
|
||||
return QString("Panda: %1").arg(config.serial);
|
||||
}
|
||||
@@ -45,9 +44,8 @@ class OpenPandaWidget : public AbstractOpenStreamWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OpenPandaWidget(AbstractStream **stream);
|
||||
bool open() override;
|
||||
QString title() override { return tr("&Panda"); }
|
||||
OpenPandaWidget(QWidget *parent = nullptr);
|
||||
AbstractStream *open() override;
|
||||
|
||||
private:
|
||||
void refreshSerials();
|
||||
|
||||
@@ -83,11 +83,6 @@ bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint
|
||||
return success;
|
||||
}
|
||||
|
||||
void ReplayStream::start() {
|
||||
emit streamStarted();
|
||||
replay->start();
|
||||
}
|
||||
|
||||
bool ReplayStream::eventFilter(const Event *event) {
|
||||
static double prev_update_ts = 0;
|
||||
if (event->which == cereal::Event::Which::CAN) {
|
||||
@@ -115,13 +110,9 @@ void ReplayStream::pause(bool pause) {
|
||||
}
|
||||
|
||||
|
||||
AbstractOpenStreamWidget *ReplayStream::widget(AbstractStream **stream) {
|
||||
return new OpenReplayWidget(stream);
|
||||
}
|
||||
|
||||
// OpenReplayWidget
|
||||
|
||||
OpenReplayWidget::OpenReplayWidget(AbstractStream **stream) : AbstractOpenStreamWidget(stream) {
|
||||
OpenReplayWidget::OpenReplayWidget(QWidget *parent) : AbstractOpenStreamWidget(parent) {
|
||||
QGridLayout *grid_layout = new QGridLayout(this);
|
||||
grid_layout->addWidget(new QLabel(tr("Route")), 0, 0);
|
||||
grid_layout->addWidget(route_edit = new QLineEdit(this), 0, 1);
|
||||
@@ -154,7 +145,7 @@ OpenReplayWidget::OpenReplayWidget(AbstractStream **stream) : AbstractOpenStream
|
||||
});
|
||||
}
|
||||
|
||||
bool OpenReplayWidget::open() {
|
||||
AbstractStream *OpenReplayWidget::open() {
|
||||
QString route = route_edit->text();
|
||||
QString data_dir;
|
||||
if (int idx = route.lastIndexOf('/'); idx != -1 && util::file_exists(route.toStdString())) {
|
||||
@@ -173,8 +164,8 @@ bool OpenReplayWidget::open() {
|
||||
if (flags == REPLAY_FLAG_NONE && !cameras[0]->isChecked()) flags = REPLAY_FLAG_NO_VIPC;
|
||||
|
||||
if (replay_stream->loadRoute(route, data_dir, flags)) {
|
||||
*stream = replay_stream.release();
|
||||
return replay_stream.release();
|
||||
}
|
||||
}
|
||||
return *stream != nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class ReplayStream : public AbstractStream {
|
||||
|
||||
public:
|
||||
ReplayStream(QObject *parent);
|
||||
void start() override;
|
||||
void start() override { replay->start(); }
|
||||
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); }
|
||||
@@ -31,7 +31,6 @@ public:
|
||||
inline Replay *getReplay() const { return replay.get(); }
|
||||
inline bool isPaused() const override { return replay->isPaused(); }
|
||||
void pause(bool pause) override;
|
||||
static AbstractOpenStreamWidget *widget(AbstractStream **stream);
|
||||
|
||||
private:
|
||||
void mergeSegments();
|
||||
@@ -44,9 +43,8 @@ class OpenReplayWidget : public AbstractOpenStreamWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OpenReplayWidget(AbstractStream **stream);
|
||||
bool open() override;
|
||||
QString title() override { return tr("&Replay"); }
|
||||
OpenReplayWidget(QWidget *parent = nullptr);
|
||||
AbstractStream *open() override;
|
||||
|
||||
private:
|
||||
QLineEdit *route_edit;
|
||||
|
||||
@@ -66,11 +66,7 @@ void SocketCanStream::streamThread() {
|
||||
}
|
||||
}
|
||||
|
||||
AbstractOpenStreamWidget *SocketCanStream::widget(AbstractStream **stream) {
|
||||
return new OpenSocketCanWidget(stream);
|
||||
}
|
||||
|
||||
OpenSocketCanWidget::OpenSocketCanWidget(AbstractStream **stream) : AbstractOpenStreamWidget(stream) {
|
||||
OpenSocketCanWidget::OpenSocketCanWidget(QWidget *parent) : AbstractOpenStreamWidget(parent) {
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->addStretch(1);
|
||||
|
||||
@@ -104,12 +100,11 @@ void OpenSocketCanWidget::refreshDevices() {
|
||||
}
|
||||
|
||||
|
||||
bool OpenSocketCanWidget::open() {
|
||||
AbstractStream *OpenSocketCanWidget::open() {
|
||||
try {
|
||||
*stream = new SocketCanStream(qApp, config);
|
||||
return new SocketCanStream(qApp, config);
|
||||
} catch (std::exception &e) {
|
||||
QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to connect to SocketCAN device: '%1'").arg(e.what()));
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ class SocketCanStream : public LiveStream {
|
||||
public:
|
||||
SocketCanStream(QObject *parent, SocketCanStreamConfig config_ = {});
|
||||
~SocketCanStream() { stop(); }
|
||||
static AbstractOpenStreamWidget *widget(AbstractStream **stream);
|
||||
static bool available();
|
||||
|
||||
inline QString routeName() const override {
|
||||
@@ -37,9 +36,8 @@ class OpenSocketCanWidget : public AbstractOpenStreamWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OpenSocketCanWidget(AbstractStream **stream);
|
||||
bool open() override;
|
||||
QString title() override { return tr("&SocketCAN"); }
|
||||
OpenSocketCanWidget(QWidget *parent = nullptr);
|
||||
AbstractStream *open() override;
|
||||
|
||||
private:
|
||||
void refreshDevices();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "tools/cabana/streams/replaystream.h"
|
||||
#include "tools/cabana/streams/socketcanstream.h"
|
||||
|
||||
StreamSelector::StreamSelector(AbstractStream **stream, QWidget *parent) : QDialog(parent) {
|
||||
StreamSelector::StreamSelector(QWidget *parent) : QDialog(parent) {
|
||||
setWindowTitle(tr("Open stream"));
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
tab = new QTabWidget(this);
|
||||
@@ -33,17 +33,17 @@ StreamSelector::StreamSelector(AbstractStream **stream, QWidget *parent) : QDial
|
||||
btn_box = new QDialogButtonBox(QDialogButtonBox::Open | QDialogButtonBox::Cancel);
|
||||
layout->addWidget(btn_box);
|
||||
|
||||
addStreamWidget(ReplayStream::widget(stream));
|
||||
addStreamWidget(PandaStream::widget(stream));
|
||||
addStreamWidget(new OpenReplayWidget, tr("&Replay"));
|
||||
addStreamWidget(new OpenPandaWidget, tr("&Panda"));
|
||||
if (SocketCanStream::available()) {
|
||||
addStreamWidget(SocketCanStream::widget(stream));
|
||||
addStreamWidget(new OpenSocketCanWidget, tr("&SocketCAN"));
|
||||
}
|
||||
addStreamWidget(DeviceStream::widget(stream));
|
||||
addStreamWidget(new OpenDeviceWidget, tr("&Device"));
|
||||
|
||||
QObject::connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
QObject::connect(btn_box, &QDialogButtonBox::accepted, [=]() {
|
||||
setEnabled(false);
|
||||
if (((AbstractOpenStreamWidget *)tab->currentWidget())->open()) {
|
||||
if (stream_ = ((AbstractOpenStreamWidget *)tab->currentWidget())->open(); stream_) {
|
||||
accept();
|
||||
}
|
||||
setEnabled(true);
|
||||
@@ -57,8 +57,8 @@ StreamSelector::StreamSelector(AbstractStream **stream, QWidget *parent) : QDial
|
||||
});
|
||||
}
|
||||
|
||||
void StreamSelector::addStreamWidget(AbstractOpenStreamWidget *w) {
|
||||
tab->addTab(w, w->title());
|
||||
void StreamSelector::addStreamWidget(AbstractOpenStreamWidget *w, const QString &title) {
|
||||
tab->addTab(w, title);
|
||||
auto open_btn = btn_box->button(QDialogButtonBox::Open);
|
||||
QObject::connect(w, &AbstractOpenStreamWidget::enableOpenButton, open_btn, &QPushButton::setEnabled);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,13 @@ class StreamSelector : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StreamSelector(AbstractStream **stream, QWidget *parent = nullptr);
|
||||
void addStreamWidget(AbstractOpenStreamWidget *w);
|
||||
StreamSelector(QWidget *parent = nullptr);
|
||||
void addStreamWidget(AbstractOpenStreamWidget *w, const QString &title);
|
||||
QString dbcFile() const { return dbc_file->text(); }
|
||||
AbstractStream *stream() const { return stream_; }
|
||||
|
||||
private:
|
||||
AbstractStream *stream_ = nullptr;
|
||||
QLineEdit *dbc_file;
|
||||
QTabWidget *tab;
|
||||
QDialogButtonBox *btn_box;
|
||||
|
||||
Reference in New Issue
Block a user