dragonpilot - 基於 openpilot 的開源駕駛輔助系統
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

271 lines
9.9 KiB

#include "tools/cabana/mainwin.h"
#include <iostream>
Cabana: stable initial release (#26004) * increase form size & fix wrong charts number * set max axisy to 1.0 if no value * show 'close' button in floating window * alwasy show scroll bar * complete the logs * more * increase size to 50 * keep logs for all messages * more * rename signal * better height * avoid flicker * dont call setupdatesenabled * filter dbc files bye typing * remove all charts if dbc file changed * fix wrong idx * bolder dbc filename * update chart if signal has been edited * new signals signalAdded,signalUpdated * split class Parser into CanMessages and DBCManager * cleanup * updateState after set message * cleanup * emit msgUpdated * clear history log if selected range changed * always update time * change title layout * show selected range hide title bar if no charts less space between title and chart * custome historylogmodel for extreme fast update * move historylog to seperate file * 2 decimal * cleanup cleanup * left click on the chart to set start time * todo * show tooltip for header item&cleanup binaryview add hline to signal form * better paint * cleanup signals/slots * better range if min==max * set historylog's minheight to 300 * 3x faster,sortable message list. * zero copy in queued connection * proxymodel * clear log if loop to the begin * simplify history log * remove icon * remove assets * hide linemarker on initialization * rubber width may less than 0 * dont zoom char if selected range is too small * cleanup messageslist * don't zoom chart if selected range less than 500ms * typo * check boundary * check msg_id * capital first letter * move history log out of scrollarea * Show only one form at a time * auto scroll to header d * reduce msg size entire row clickable rename filter_msgs
3 years ago
#include <QApplication>
#include <QClipboard>
#include <QCompleter>
#include <QFile>
#include <QFileDialog>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QShortcut>
#include <QScreen>
#include <QToolBar>
#include <QUndoView>
#include <QVBoxLayout>
#include <QWidgetAction>
#include "tools/replay/util.h"
static MainWindow *main_win = nullptr;
void qLogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
if (type == QtDebugMsg) std::cout << msg.toStdString() << std::endl;
if (main_win) emit main_win->showMessage(msg, 0);
}
MainWindow::MainWindow() : QMainWindow() {
setWindowTitle("Cabana");
QWidget *central_widget = new QWidget(this);
QHBoxLayout *main_layout = new QHBoxLayout(central_widget);
main_layout->setContentsMargins(11, 11, 11, 0);
main_layout->setSpacing(0);
splitter = new QSplitter(Qt::Horizontal, this);
splitter->setHandleWidth(11);
// DBC file selector
QWidget *messages_container = new QWidget(this);
QVBoxLayout *messages_layout = new QVBoxLayout(messages_container);
messages_layout->setContentsMargins(0, 0, 0, 0);
dbc_combo = new QComboBox(this);
auto dbc_names = dbc()->allDBCNames();
for (const auto &name : dbc_names) {
dbc_combo->addItem(QString::fromStdString(name));
}
dbc_combo->model()->sort(0);
dbc_combo->setInsertPolicy(QComboBox::NoInsert);
messages_layout->addWidget(dbc_combo);
messages_widget = new MessagesWidget(this);
messages_layout->addWidget(messages_widget);
splitter->addWidget(messages_container);
charts_widget = new ChartsWidget(this);
detail_widget = new DetailWidget(charts_widget, this);
splitter->addWidget(detail_widget);
if (!settings.splitter_state.isEmpty()) {
splitter->restoreState(settings.splitter_state);
}
main_layout->addWidget(splitter);
// right widgets
QWidget *right_container = new QWidget(this);
right_container->setFixedWidth(640);
r_layout = new QVBoxLayout(right_container);
r_layout->setContentsMargins(11, 0, 0, 0);
QHBoxLayout *right_hlayout = new QHBoxLayout();
fingerprint_label = new QLabel(this);
right_hlayout->addWidget(fingerprint_label, 0, Qt::AlignLeft);
// TODO: click to select another route.
right_hlayout->addWidget(new QLabel(can->route()), 0, Qt::AlignRight);
r_layout->addLayout(right_hlayout);
video_widget = new VideoWidget(this);
r_layout->addWidget(video_widget, 0, Qt::AlignTop);
r_layout->addWidget(charts_widget, 1);
r_layout->addStretch(0);
main_layout->addWidget(right_container);
setCentralWidget(central_widget);
createActions();
createStatusBar();
createShortcuts();
qRegisterMetaType<uint64_t>("uint64_t");
qRegisterMetaType<ReplyMsgType>("ReplyMsgType");
installMessageHandler([this](ReplyMsgType type, const std::string msg) {
// use queued connection to recv the log messages from replay.
emit showMessage(QString::fromStdString(msg), 3000);
});
installDownloadProgressHandler([this](uint64_t cur, uint64_t total, bool success) {
emit updateProgressBar(cur, total, success);
});
main_win = this;
qInstallMessageHandler(qLogMessageHandler);
QFile json_file("./car_fingerprint_to_dbc.json");
if (json_file.open(QIODevice::ReadOnly)) {
fingerprint_to_dbc = QJsonDocument::fromJson(json_file.readAll());
}
QObject::connect(dbc_combo, SIGNAL(activated(const QString &)), SLOT(loadDBCFromName(const QString &)));
QObject::connect(this, &MainWindow::showMessage, statusBar(), &QStatusBar::showMessage);
QObject::connect(this, &MainWindow::updateProgressBar, this, &MainWindow::updateDownloadProgress);
Cabana: stable initial release (#26004) * increase form size & fix wrong charts number * set max axisy to 1.0 if no value * show 'close' button in floating window * alwasy show scroll bar * complete the logs * more * increase size to 50 * keep logs for all messages * more * rename signal * better height * avoid flicker * dont call setupdatesenabled * filter dbc files bye typing * remove all charts if dbc file changed * fix wrong idx * bolder dbc filename * update chart if signal has been edited * new signals signalAdded,signalUpdated * split class Parser into CanMessages and DBCManager * cleanup * updateState after set message * cleanup * emit msgUpdated * clear history log if selected range changed * always update time * change title layout * show selected range hide title bar if no charts less space between title and chart * custome historylogmodel for extreme fast update * move historylog to seperate file * 2 decimal * cleanup cleanup * left click on the chart to set start time * todo * show tooltip for header item&cleanup binaryview add hline to signal form * better paint * cleanup signals/slots * better range if min==max * set historylog's minheight to 300 * 3x faster,sortable message list. * zero copy in queued connection * proxymodel * clear log if loop to the begin * simplify history log * remove icon * remove assets * hide linemarker on initialization * rubber width may less than 0 * dont zoom char if selected range is too small * cleanup messageslist * don't zoom chart if selected range less than 500ms * typo * check boundary * check msg_id * capital first letter * move history log out of scrollarea * Show only one form at a time * auto scroll to header d * reduce msg size entire row clickable rename filter_msgs
3 years ago
QObject::connect(messages_widget, &MessagesWidget::msgSelectionChanged, detail_widget, &DetailWidget::setMessage);
QObject::connect(charts_widget, &ChartsWidget::dock, this, &MainWindow::dockCharts);
QObject::connect(charts_widget, &ChartsWidget::rangeChanged, video_widget, &VideoWidget::rangeChanged);
QObject::connect(can, &CANMessages::streamStarted, this, &MainWindow::loadDBCFromFingerprint);
QObject::connect(dbc(), &DBCManager::DBCFileChanged, [this]() {
detail_widget->undo_stack->clear();
dbc_combo->setCurrentText(QFileInfo(dbc()->name()).baseName());
setWindowTitle(tr("%1 - Cabana").arg(dbc()->name()));
});
QObject::connect(detail_widget->undo_stack, &QUndoStack::indexChanged, [this](int index) {
setWindowTitle(tr("%1%2 - Cabana").arg(index > 0 ? "* " : "").arg(dbc()->name()));
});
}
void MainWindow::createActions() {
QMenu *file_menu = menuBar()->addMenu(tr("&File"));
file_menu->addAction(tr("Open DBC File..."), this, &MainWindow::loadDBCFromFile);
file_menu->addAction(tr("Load DBC From Clipboard"), this, &MainWindow::loadDBCFromClipboard);
file_menu->addSeparator();
file_menu->addAction(tr("Save DBC As..."), this, &MainWindow::saveDBCToFile);
file_menu->addAction(tr("Copy DBC To Clipboard"), this, &MainWindow::saveDBCToClipboard);
file_menu->addSeparator();
file_menu->addAction(tr("Settings..."), this, &MainWindow::setOption);
QMenu *edit_menu = menuBar()->addMenu(tr("&Edit"));
auto undo_act = detail_widget->undo_stack->createUndoAction(this, tr("&Undo"));
undo_act->setShortcuts(QKeySequence::Undo);
edit_menu->addAction(undo_act);
auto redo_act = detail_widget->undo_stack->createRedoAction(this, tr("&Rndo"));
redo_act->setShortcuts(QKeySequence::Redo);
edit_menu->addAction(redo_act);
edit_menu->addSeparator();
QMenu *commands_menu = edit_menu->addMenu(tr("Command &List"));
auto undo_view = new QUndoView(detail_widget->undo_stack);
undo_view->setWindowTitle(tr("Command List"));
QWidgetAction *commands_act = new QWidgetAction(this);
commands_act->setDefaultWidget(undo_view);
commands_menu->addAction(commands_act);
QMenu *help_menu = menuBar()->addMenu(tr("&Help"));
help_menu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
}
void MainWindow::createStatusBar() {
progress_bar = new QProgressBar();
progress_bar->setRange(0, 100);
progress_bar->setTextVisible(true);
progress_bar->setFixedSize({230, 16});
progress_bar->setVisible(false);
statusBar()->addPermanentWidget(progress_bar);
}
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()); });
// TODO: add more shortcuts here.
}
void MainWindow::loadDBCFromName(const QString &name) {
if (name != dbc()->name())
dbc()->open(name);
}
void MainWindow::loadDBCFromFile() {
QString file_name = QFileDialog::getOpenFileName(this, tr("Open File"), settings.last_dir, "DBC (*.dbc)");
if (!file_name.isEmpty()) {
settings.last_dir = QFileInfo(file_name).absolutePath();
QFile file(file_name);
if (file.open(QIODevice::ReadOnly)) {
auto dbc_name = QFileInfo(file_name).baseName();
dbc()->open(dbc_name, file.readAll());
}
}
}
void MainWindow::loadDBCFromClipboard() {
QString dbc_str = QGuiApplication::clipboard()->text();
dbc()->open("From Clipboard", dbc_str);
QMessageBox::information(this, tr("Load From Clipboard"), tr("DBC Successfully Loaded!"));
}
void MainWindow::loadDBCFromFingerprint() {
auto fingerprint = can->carFingerprint();
fingerprint_label->setText(fingerprint);
if (!fingerprint.isEmpty() && dbc()->name().isEmpty()) {
auto dbc_name = fingerprint_to_dbc[fingerprint];
if (dbc_name != QJsonValue::Undefined) {
loadDBCFromName(dbc_name.toString());
}
}
}
void MainWindow::saveDBCToFile() {
QString file_name = QFileDialog::getSaveFileName(this, tr("Save File"),
QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)"));
if (!file_name.isEmpty()) {
settings.last_dir = QFileInfo(file_name).absolutePath();
QFile file(file_name);
if (file.open(QIODevice::WriteOnly)) {
file.write(dbc()->generateDBC().toUtf8());
detail_widget->undo_stack->clear();
}
}
}
void MainWindow::saveDBCToClipboard() {
QGuiApplication::clipboard()->setText(dbc()->generateDBC());
QMessageBox::information(this, tr("Copy To Clipboard"), tr("DBC Successfully copied!"));
}
void MainWindow::updateDownloadProgress(uint64_t cur, uint64_t total, bool success) {
if (success && cur < total) {
progress_bar->setValue((cur / (double)total) * 100);
progress_bar->setFormat(tr("Downloading %p% (%1)").arg(formattedDataSize(total).c_str()));
progress_bar->show();
} else {
progress_bar->hide();
}
}
void MainWindow::dockCharts(bool dock) {
if (dock && floating_window) {
Cabana: stable initial release (#26004) * increase form size & fix wrong charts number * set max axisy to 1.0 if no value * show 'close' button in floating window * alwasy show scroll bar * complete the logs * more * increase size to 50 * keep logs for all messages * more * rename signal * better height * avoid flicker * dont call setupdatesenabled * filter dbc files bye typing * remove all charts if dbc file changed * fix wrong idx * bolder dbc filename * update chart if signal has been edited * new signals signalAdded,signalUpdated * split class Parser into CanMessages and DBCManager * cleanup * updateState after set message * cleanup * emit msgUpdated * clear history log if selected range changed * always update time * change title layout * show selected range hide title bar if no charts less space between title and chart * custome historylogmodel for extreme fast update * move historylog to seperate file * 2 decimal * cleanup cleanup * left click on the chart to set start time * todo * show tooltip for header item&cleanup binaryview add hline to signal form * better paint * cleanup signals/slots * better range if min==max * set historylog's minheight to 300 * 3x faster,sortable message list. * zero copy in queued connection * proxymodel * clear log if loop to the begin * simplify history log * remove icon * remove assets * hide linemarker on initialization * rubber width may less than 0 * dont zoom char if selected range is too small * cleanup messageslist * don't zoom chart if selected range less than 500ms * typo * check boundary * check msg_id * capital first letter * move history log out of scrollarea * Show only one form at a time * auto scroll to header d * reduce msg size entire row clickable rename filter_msgs
3 years ago
floating_window->removeEventFilter(charts_widget);
r_layout->insertWidget(2, charts_widget, 1);
floating_window->deleteLater();
floating_window = nullptr;
} else if (!dock && !floating_window) {
floating_window = new QWidget(this);
floating_window->setWindowFlags(Qt::Window);
floating_window->setWindowTitle("Charts - Cabana");
floating_window->setLayout(new QVBoxLayout());
floating_window->layout()->addWidget(charts_widget);
Cabana: stable initial release (#26004) * increase form size & fix wrong charts number * set max axisy to 1.0 if no value * show 'close' button in floating window * alwasy show scroll bar * complete the logs * more * increase size to 50 * keep logs for all messages * more * rename signal * better height * avoid flicker * dont call setupdatesenabled * filter dbc files bye typing * remove all charts if dbc file changed * fix wrong idx * bolder dbc filename * update chart if signal has been edited * new signals signalAdded,signalUpdated * split class Parser into CanMessages and DBCManager * cleanup * updateState after set message * cleanup * emit msgUpdated * clear history log if selected range changed * always update time * change title layout * show selected range hide title bar if no charts less space between title and chart * custome historylogmodel for extreme fast update * move historylog to seperate file * 2 decimal * cleanup cleanup * left click on the chart to set start time * todo * show tooltip for header item&cleanup binaryview add hline to signal form * better paint * cleanup signals/slots * better range if min==max * set historylog's minheight to 300 * 3x faster,sortable message list. * zero copy in queued connection * proxymodel * clear log if loop to the begin * simplify history log * remove icon * remove assets * hide linemarker on initialization * rubber width may less than 0 * dont zoom char if selected range is too small * cleanup messageslist * don't zoom chart if selected range less than 500ms * typo * check boundary * check msg_id * capital first letter * move history log out of scrollarea * Show only one form at a time * auto scroll to header d * reduce msg size entire row clickable rename filter_msgs
3 years ago
floating_window->installEventFilter(charts_widget);
floating_window->setMinimumSize(QGuiApplication::primaryScreen()->size() / 2);
floating_window->showMaximized();
}
}
void MainWindow::closeEvent(QCloseEvent *event) {
if (detail_widget->undo_stack->index() > 0) {
auto ret = QMessageBox::question(this, tr("Unsaved Changes"),
tr("Are you sure you want to exit without saving?\nAny unsaved changes will be lost."),
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::No) {
event->ignore();
return;
}
}
main_win = nullptr;
if (floating_window)
floating_window->deleteLater();
settings.splitter_state = splitter->saveState();
settings.save();
QWidget::closeEvent(event);
}
void MainWindow::setOption() {
SettingsDlg dlg(this);
dlg.exec();
}