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
			
			
				vw-mqb-aeb
			
			
		
							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