canbana: complete basic functions (#25965)
* add chart header
* get all signal val from logs
* loop in selected range
* clear list before append
* automatically zoom on yaxis
* cleanup
* sync charts
* fix event_begin_sec
* set the color of rubber
* add TODO
* sync slider with charts
* keep video aspect ratio
* sync plot buttons
* reduce flickers
* cleanup
* refactor detail view
* clear counters
* more
use qcamera
old-commit-hash: a6ba073231
taco
parent
73f0c74b9b
commit
f93f4e9f9b
22 changed files with 833 additions and 534 deletions
@ -1,102 +1,70 @@ |
||||
#pragma once |
||||
#include <QComboBox> |
||||
|
||||
#include <QDialog> |
||||
#include <QDialogButtonBox> |
||||
#include <QLabel> |
||||
#include <QLineEdit> |
||||
#include <QPushButton> |
||||
#include <QSpinBox> |
||||
#include <QTableWidget> |
||||
#include <QVBoxLayout> |
||||
#include <QWidget> |
||||
#include <optional> |
||||
|
||||
#include "opendbc/can/common.h" |
||||
#include "opendbc/can/common_dbc.h" |
||||
#include "selfdrive/ui/qt/widgets/controls.h" |
||||
#include "tools/cabana/parser.h" |
||||
#include "tools/cabana/signaledit.h" |
||||
|
||||
class SignalForm : public QWidget { |
||||
class HistoryLog : public QWidget { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
SignalForm(const Signal &sig, QWidget *parent); |
||||
std::optional<Signal> getSignal(); |
||||
QLineEdit *name, *unit, *comment, *val_desc; |
||||
QSpinBox *size, *msb, *lsb, *factor, *offset, *min_val, *max_val; |
||||
QComboBox *sign, *endianness; |
||||
}; |
||||
|
||||
class MessagesView : public QWidget { |
||||
Q_OBJECT |
||||
public: |
||||
HistoryLog(QWidget *parent); |
||||
void clear(); |
||||
void updateState(); |
||||
|
||||
public: |
||||
MessagesView(QWidget *parent); |
||||
void setMessages(const std::list<CanData> &data); |
||||
std::vector<QLabel *> messages; |
||||
QVBoxLayout *message_layout; |
||||
private: |
||||
QLabel *labels[LOG_SIZE] = {}; |
||||
}; |
||||
|
||||
class BinaryView : public QWidget { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
public: |
||||
BinaryView(QWidget *parent); |
||||
void setMsg(const QString &id); |
||||
void setMsg(const CanData *can_data); |
||||
void setData(const QByteArray &binary); |
||||
|
||||
QTableWidget *table; |
||||
}; |
||||
|
||||
class SignalEdit : public QWidget { |
||||
class EditMessageDialog : public QDialog { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
SignalEdit(const QString &id, const Signal &sig, int idx, QWidget *parent); |
||||
public: |
||||
EditMessageDialog(const QString &id, QWidget *parent); |
||||
|
||||
protected: |
||||
void save(); |
||||
|
||||
signals: |
||||
void removed(); |
||||
protected: |
||||
void remove(); |
||||
QLineEdit *name_edit; |
||||
QSpinBox *size_spin; |
||||
QString id; |
||||
QString name_; |
||||
ElidedLabel *title; |
||||
SignalForm *form; |
||||
QWidget *edit_container; |
||||
QPushButton *remove_btn; |
||||
}; |
||||
|
||||
class DetailWidget : public QWidget { |
||||
Q_OBJECT |
||||
public: |
||||
|
||||
public: |
||||
DetailWidget(QWidget *parent); |
||||
void setMsg(const QString &id); |
||||
void setMsg(const CanData *c); |
||||
|
||||
public slots: |
||||
private: |
||||
void updateState(); |
||||
void addSignal(); |
||||
void editMsg(); |
||||
|
||||
protected: |
||||
QLabel *name_label = nullptr; |
||||
const CanData *can_data = nullptr; |
||||
QLabel *name_label, *time_label; |
||||
QPushButton *edit_btn, *add_sig_btn; |
||||
QVBoxLayout *signal_edit_layout; |
||||
Signal *sig = nullptr; |
||||
MessagesView *messages_view; |
||||
QString msg_id; |
||||
HistoryLog *history_log; |
||||
BinaryView *binary_view; |
||||
std::vector<SignalEdit *> signal_edit; |
||||
}; |
||||
|
||||
class EditMessageDialog : public QDialog { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
EditMessageDialog(const QString &id, QWidget *parent); |
||||
}; |
||||
|
||||
class AddSignalDialog : public QDialog { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
AddSignalDialog(const QString &id, QWidget *parent); |
||||
}; |
||||
|
@ -0,0 +1,203 @@ |
||||
#include "tools/cabana/signaledit.h" |
||||
|
||||
#include <QDialogButtonBox> |
||||
#include <QHBoxLayout> |
||||
#include <QLabel> |
||||
#include <QMessageBox> |
||||
#include <QVBoxLayout> |
||||
|
||||
// SignalForm
|
||||
|
||||
SignalForm::SignalForm(const Signal &sig, QWidget *parent) : QWidget(parent) { |
||||
QVBoxLayout *v_layout = new QVBoxLayout(this); |
||||
|
||||
QHBoxLayout *h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Name"))); |
||||
name = new QLineEdit(sig.name.c_str()); |
||||
h->addWidget(name); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Size"))); |
||||
size = new QSpinBox(); |
||||
size->setValue(sig.size); |
||||
h->addWidget(size); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Most significant bit"))); |
||||
msb = new QSpinBox(); |
||||
msb->setValue(sig.msb); |
||||
h->addWidget(msb); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Endianness"))); |
||||
endianness = new QComboBox(); |
||||
endianness->addItems({"Little", "Big"}); |
||||
endianness->setCurrentIndex(sig.is_little_endian ? 0 : 1); |
||||
h->addWidget(endianness); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("sign"))); |
||||
sign = new QComboBox(); |
||||
sign->addItems({"Signed", "Unsigned"}); |
||||
sign->setCurrentIndex(sig.is_signed ? 0 : 1); |
||||
h->addWidget(sign); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Factor"))); |
||||
factor = new QSpinBox(); |
||||
factor->setValue(sig.factor); |
||||
h->addWidget(factor); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Offset"))); |
||||
offset = new QSpinBox(); |
||||
offset->setValue(sig.offset); |
||||
h->addWidget(offset); |
||||
v_layout->addLayout(h); |
||||
|
||||
// TODO: parse the following parameters in opendbc
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Unit"))); |
||||
unit = new QLineEdit(); |
||||
h->addWidget(unit); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Comment"))); |
||||
comment = new QLineEdit(); |
||||
h->addWidget(comment); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Minimum value"))); |
||||
min_val = new QSpinBox(); |
||||
h->addWidget(min_val); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Maximum value"))); |
||||
max_val = new QSpinBox(); |
||||
h->addWidget(max_val); |
||||
v_layout->addLayout(h); |
||||
|
||||
h = new QHBoxLayout(); |
||||
h->addWidget(new QLabel(tr("Value descriptions"))); |
||||
val_desc = new QLineEdit(); |
||||
h->addWidget(val_desc); |
||||
v_layout->addLayout(h); |
||||
} |
||||
|
||||
std::optional<Signal> SignalForm::getSignal() { |
||||
Signal sig = {}; |
||||
sig.name = name->text().toStdString(); |
||||
sig.size = size->text().toInt(); |
||||
sig.offset = offset->text().toDouble(); |
||||
sig.factor = factor->text().toDouble(); |
||||
sig.msb = msb->text().toInt(); |
||||
sig.is_signed = sign->currentIndex() == 0; |
||||
sig.is_little_endian = endianness->currentIndex() == 0; |
||||
if (sig.is_little_endian) { |
||||
sig.lsb = sig.start_bit; |
||||
sig.msb = sig.start_bit + sig.size - 1; |
||||
} else { |
||||
sig.lsb = bigEndianStartBitsIndex(bigEndianBitIndex(sig.start_bit) + sig.size - 1); |
||||
sig.msb = sig.start_bit; |
||||
} |
||||
return (sig.name.empty() || sig.size <= 0) ? std::nullopt : std::optional(sig); |
||||
} |
||||
|
||||
// SignalEdit
|
||||
|
||||
SignalEdit::SignalEdit(const QString &id, const Signal &sig, const QString &color, QWidget *parent) : id(id), name_(sig.name.c_str()), QWidget(parent) { |
||||
QVBoxLayout *main_layout = new QVBoxLayout(this); |
||||
main_layout->setContentsMargins(0, 0, 0, 0); |
||||
|
||||
// title
|
||||
QHBoxLayout *title_layout = new QHBoxLayout(); |
||||
QLabel *icon = new QLabel(">"); |
||||
icon->setStyleSheet("font-weight:bold"); |
||||
title_layout->addWidget(icon); |
||||
title = new ElidedLabel(this); |
||||
title->setText(sig.name.c_str()); |
||||
title->setStyleSheet(QString("font-weight:bold; color:%1").arg(color)); |
||||
connect(title, &ElidedLabel::clicked, [=]() { |
||||
edit_container->isVisible() ? edit_container->hide() : edit_container->show(); |
||||
icon->setText(edit_container->isVisible() ? "▼" : ">"); |
||||
}); |
||||
title_layout->addWidget(title); |
||||
title_layout->addStretch(); |
||||
plot_btn = new QPushButton("📈"); |
||||
plot_btn->setStyleSheet("font-size:16px"); |
||||
plot_btn->setToolTip(tr("Show Plot")); |
||||
plot_btn->setContentsMargins(5, 5, 5, 5); |
||||
plot_btn->setFixedSize(30, 30); |
||||
QObject::connect(plot_btn, &QPushButton::clicked, [=]() { emit parser->showPlot(id, name_); }); |
||||
title_layout->addWidget(plot_btn); |
||||
main_layout->addLayout(title_layout); |
||||
|
||||
edit_container = new QWidget(this); |
||||
QVBoxLayout *v_layout = new QVBoxLayout(edit_container); |
||||
form = new SignalForm(sig, this); |
||||
v_layout->addWidget(form); |
||||
|
||||
QHBoxLayout *h = new QHBoxLayout(); |
||||
remove_btn = new QPushButton(tr("Remove Signal")); |
||||
QObject::connect(remove_btn, &QPushButton::clicked, this, &SignalEdit::remove); |
||||
h->addWidget(remove_btn); |
||||
h->addStretch(); |
||||
QPushButton *save_btn = new QPushButton(tr("Save")); |
||||
QObject::connect(save_btn, &QPushButton::clicked, this, &SignalEdit::save); |
||||
h->addWidget(save_btn); |
||||
v_layout->addLayout(h); |
||||
|
||||
edit_container->setVisible(false); |
||||
main_layout->addWidget(edit_container); |
||||
} |
||||
|
||||
void SignalEdit::save() { |
||||
if (auto sig = const_cast<Signal *>(parser->getSig(id, name_))) { |
||||
if (auto s = form->getSignal()) { |
||||
*sig = *s; |
||||
// TODO: reset the chart for sig
|
||||
} |
||||
} |
||||
} |
||||
|
||||
void SignalEdit::remove() { |
||||
QMessageBox msgbox; |
||||
msgbox.setText(tr("Remove signal")); |
||||
msgbox.setInformativeText(tr("Are you sure you want to remove signal '%1'").arg(name_)); |
||||
msgbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); |
||||
msgbox.setDefaultButton(QMessageBox::Cancel); |
||||
if (msgbox.exec()) { |
||||
parser->removeSignal(id, name_); |
||||
deleteLater(); |
||||
} |
||||
} |
||||
|
||||
// AddSignalDialog
|
||||
|
||||
AddSignalDialog::AddSignalDialog(const QString &id, QWidget *parent) : QDialog(parent) { |
||||
setWindowTitle(tr("Add signal to %1").arg(parser->getMsg(id)->name.c_str())); |
||||
QVBoxLayout *main_layout = new QVBoxLayout(this); |
||||
Signal sig = {.name = "untitled"}; |
||||
auto form = new SignalForm(sig, this); |
||||
main_layout->addWidget(form); |
||||
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); |
||||
main_layout->addWidget(buttonBox); |
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); |
||||
connect(buttonBox, &QDialogButtonBox::accepted, [=]() { |
||||
if (auto msg = const_cast<Msg *>(parser->getMsg(id))) { |
||||
if (auto signal = form->getSignal()) { |
||||
msg->sigs.push_back(*signal); |
||||
} |
||||
} |
||||
QDialog::accept(); |
||||
}); |
||||
} |
@ -0,0 +1,50 @@ |
||||
#pragma once |
||||
|
||||
#include <optional> |
||||
|
||||
#include <QComboBox> |
||||
#include <QDialog> |
||||
#include <QLineEdit> |
||||
#include <QPushButton> |
||||
#include <QSpinBox> |
||||
|
||||
#include "selfdrive/ui/qt/widgets/controls.h" |
||||
#include "tools/cabana/parser.h" |
||||
|
||||
class SignalForm : public QWidget { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
SignalForm(const Signal &sig, QWidget *parent); |
||||
std::optional<Signal> getSignal(); |
||||
|
||||
QLineEdit *name, *unit, *comment, *val_desc; |
||||
QSpinBox *size, *msb, *lsb, *factor, *offset, *min_val, *max_val; |
||||
QComboBox *sign, *endianness; |
||||
}; |
||||
|
||||
class SignalEdit : public QWidget { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
SignalEdit(const QString &id, const Signal &sig, const QString &color, QWidget *parent = nullptr); |
||||
void save(); |
||||
|
||||
protected: |
||||
void remove(); |
||||
|
||||
QString id; |
||||
QString name_; |
||||
QPushButton *plot_btn; |
||||
ElidedLabel *title; |
||||
SignalForm *form; |
||||
QWidget *edit_container; |
||||
QPushButton *remove_btn; |
||||
}; |
||||
|
||||
class AddSignalDialog : public QDialog { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
AddSignalDialog(const QString &id, QWidget *parent); |
||||
}; |
Loading…
Reference in new issue