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 | ||||
| #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