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 |
#pragma once |
||||||
#include <QComboBox> |
|
||||||
#include <QDialog> |
#include <QDialog> |
||||||
#include <QDialogButtonBox> |
|
||||||
#include <QLabel> |
#include <QLabel> |
||||||
#include <QLineEdit> |
|
||||||
#include <QPushButton> |
#include <QPushButton> |
||||||
#include <QSpinBox> |
|
||||||
#include <QTableWidget> |
#include <QTableWidget> |
||||||
#include <QVBoxLayout> |
#include <QVBoxLayout> |
||||||
#include <QWidget> |
#include <QWidget> |
||||||
#include <optional> |
|
||||||
|
|
||||||
#include "opendbc/can/common.h" |
#include "opendbc/can/common.h" |
||||||
#include "opendbc/can/common_dbc.h" |
#include "opendbc/can/common_dbc.h" |
||||||
#include "selfdrive/ui/qt/widgets/controls.h" |
|
||||||
#include "tools/cabana/parser.h" |
#include "tools/cabana/parser.h" |
||||||
|
#include "tools/cabana/signaledit.h" |
||||||
|
|
||||||
class SignalForm : public QWidget { |
class HistoryLog : public QWidget { |
||||||
Q_OBJECT |
Q_OBJECT |
||||||
|
|
||||||
public: |
public: |
||||||
SignalForm(const Signal &sig, QWidget *parent); |
HistoryLog(QWidget *parent); |
||||||
std::optional<Signal> getSignal(); |
void clear(); |
||||||
QLineEdit *name, *unit, *comment, *val_desc; |
void updateState(); |
||||||
QSpinBox *size, *msb, *lsb, *factor, *offset, *min_val, *max_val; |
|
||||||
QComboBox *sign, *endianness; |
|
||||||
}; |
|
||||||
|
|
||||||
class MessagesView : public QWidget { |
|
||||||
Q_OBJECT |
|
||||||
|
|
||||||
public: |
private: |
||||||
MessagesView(QWidget *parent); |
QLabel *labels[LOG_SIZE] = {}; |
||||||
void setMessages(const std::list<CanData> &data); |
|
||||||
std::vector<QLabel *> messages; |
|
||||||
QVBoxLayout *message_layout; |
|
||||||
}; |
}; |
||||||
|
|
||||||
class BinaryView : public QWidget { |
class BinaryView : public QWidget { |
||||||
Q_OBJECT |
Q_OBJECT |
||||||
|
|
||||||
public: |
public: |
||||||
BinaryView(QWidget *parent); |
BinaryView(QWidget *parent); |
||||||
void setMsg(const QString &id); |
void setMsg(const CanData *can_data); |
||||||
void setData(const QByteArray &binary); |
void setData(const QByteArray &binary); |
||||||
|
|
||||||
QTableWidget *table; |
QTableWidget *table; |
||||||
}; |
}; |
||||||
|
|
||||||
class SignalEdit : public QWidget { |
class EditMessageDialog : public QDialog { |
||||||
Q_OBJECT |
Q_OBJECT |
||||||
|
|
||||||
public: |
public: |
||||||
SignalEdit(const QString &id, const Signal &sig, int idx, QWidget *parent); |
EditMessageDialog(const QString &id, QWidget *parent); |
||||||
|
|
||||||
|
protected: |
||||||
void save(); |
void save(); |
||||||
|
|
||||||
signals: |
QLineEdit *name_edit; |
||||||
void removed(); |
QSpinBox *size_spin; |
||||||
protected: |
|
||||||
void remove(); |
|
||||||
QString id; |
QString id; |
||||||
QString name_; |
|
||||||
ElidedLabel *title; |
|
||||||
SignalForm *form; |
|
||||||
QWidget *edit_container; |
|
||||||
QPushButton *remove_btn; |
|
||||||
}; |
}; |
||||||
|
|
||||||
class DetailWidget : public QWidget { |
class DetailWidget : public QWidget { |
||||||
Q_OBJECT |
Q_OBJECT |
||||||
public: |
|
||||||
|
public: |
||||||
DetailWidget(QWidget *parent); |
DetailWidget(QWidget *parent); |
||||||
void setMsg(const QString &id); |
void setMsg(const CanData *c); |
||||||
|
|
||||||
public slots: |
private: |
||||||
void updateState(); |
void updateState(); |
||||||
|
void addSignal(); |
||||||
|
void editMsg(); |
||||||
|
|
||||||
protected: |
const CanData *can_data = nullptr; |
||||||
QLabel *name_label = nullptr; |
QLabel *name_label, *time_label; |
||||||
QPushButton *edit_btn, *add_sig_btn; |
QPushButton *edit_btn, *add_sig_btn; |
||||||
QVBoxLayout *signal_edit_layout; |
QVBoxLayout *signal_edit_layout; |
||||||
Signal *sig = nullptr; |
HistoryLog *history_log; |
||||||
MessagesView *messages_view; |
|
||||||
QString msg_id; |
|
||||||
BinaryView *binary_view; |
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