diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index ba50b101fc..678fe5f876 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -13,6 +13,10 @@ const int CELL_HEIGHT = 26; +inline int get_bit_index(const QModelIndex &index, bool little_endian) { + return index.row() * 8 + (little_endian ? 7 - index.column() : index.column()); +} + BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { model = new BinaryViewModel(this); setModel(model); @@ -37,31 +41,49 @@ void BinaryView::highlight(const Signal *sig) { } void BinaryView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) { - QModelIndex tl = indexAt({qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom())}); - QModelIndex br = indexAt({qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom())}); - if (!tl.isValid() || !br.isValid()) + auto index = indexAt(viewport()->mapFromGlobal(QCursor::pos())); + if (!anchor_index.isValid() || !index.isValid()) return; - if (tl < anchor_index) { - br = anchor_index; - } else if (anchor_index < br) { - tl = anchor_index; - } QItemSelection selection; - for (int row = tl.row(); row <= br.row(); ++row) { - int left_col = (row == tl.row()) ? tl.column() : 0; - int right_col = (row == br.row()) ? br.column() : 7; - selection.merge({model->index(row, left_col), model->index(row, right_col)}, flags); + auto [tl, br] = std::minmax(anchor_index, index); + if ((resize_sig && resize_sig->is_little_endian) || (!resize_sig && index < anchor_index)) { + // little_endian selection + if (tl.row() == br.row()) { + selection.merge({model->index(tl.row(), tl.column()), model->index(tl.row(), br.column())}, flags); + } else { + for (int row = tl.row(); row <= br.row(); ++row) { + int left_col = (row == br.row()) ? br.column() : 0; + int right_col = (row == tl.row()) ? tl.column() : 7; + selection.merge({model->index(row, left_col), model->index(row, right_col)}, flags); + } + } + } else { + // big endian selection + for (int row = tl.row(); row <= br.row(); ++row) { + int left_col = (row == tl.row()) ? tl.column() : 0; + int right_col = (row == br.row()) ? br.column() : 7; + selection.merge({model->index(row, left_col), model->index(row, right_col)}, flags); + } } selectionModel()->select(selection, flags); } void BinaryView::mousePressEvent(QMouseEvent *event) { delegate->setSelectionColor(style()->standardPalette().color(QPalette::Active, QPalette::Highlight)); - anchor_index = indexAt(event->pos()); - if (getResizingSignal() != nullptr) { + if (auto index = indexAt(event->pos()); index.isValid() && index.column() != 8) { + anchor_index = index; auto item = (const BinaryViewModel::Item *)anchor_index.internalPointer(); - delegate->setSelectionColor(item->bg_color); + if (item && item->sigs.size() > 0) { + int bit_idx = get_bit_index(anchor_index, true); + for (auto s : item->sigs) { + if (bit_idx == s->lsb || bit_idx == s->msb) { + resize_sig = s; + delegate->setSelectionColor(item->bg_color); + break; + } + } + } } QTableView::mousePressEvent(event); } @@ -80,23 +102,35 @@ void BinaryView::mouseMoveEvent(QMouseEvent *event) { void BinaryView::mouseReleaseEvent(QMouseEvent *event) { QTableView::mouseReleaseEvent(event); - if (auto indexes = selectedIndexes(); !indexes.isEmpty()) { - int from = indexes.first().row() * 8 + indexes.first().column(); - int to = indexes.back().row() * 8 + indexes.back().column(); - if (auto sig = getResizingSignal()) { - auto [sig_from, sig_to] = getSignalRange(sig); - if (from >= sig_from && to <= sig_to) { // reduce size - emit(from == sig_from ? resizeSignal(sig, std::min(to + 1, sig_to), sig_to) - : resizeSignal(sig, sig_from, std::max(from - 1, sig_from))); - } else { // increase size - emit resizeSignal(sig, std::min(from, sig_from), std::max(to, sig_to)); + auto release_index = indexAt(event->pos()); + if (release_index.isValid() && anchor_index.isValid()) { + if (release_index.column() == 8) { + release_index = model->index(release_index.row(), 7); + } + bool little_endian = (resize_sig && resize_sig->is_little_endian) || (!resize_sig && release_index < anchor_index); + int release_bit_idx = get_bit_index(release_index, little_endian); + int archor_bit_idx = get_bit_index(anchor_index, little_endian); + if (resize_sig) { + auto [sig_from, sig_to] = getSignalRange(resize_sig); + int start_bit, end_bit; + if (archor_bit_idx == sig_from) { + std::tie(start_bit, end_bit) = std::minmax(release_bit_idx, sig_to); + if (start_bit >= sig_from && start_bit <= sig_to) + start_bit = std::min(start_bit + 1, sig_to); + } else { + std::tie(start_bit, end_bit) = std::minmax(release_bit_idx, sig_from); + if (end_bit >= sig_from && end_bit <= sig_to) + end_bit = std::max(end_bit - 1, sig_from); } + emit resizeSignal(resize_sig, start_bit, end_bit - start_bit + 1); } else { - emit addSignal(from, to); + auto [sart_bit, end_bit] = std::minmax(archor_bit_idx, release_bit_idx); + emit addSignal(sart_bit, end_bit - sart_bit + 1, little_endian); } - clearSelection(); } + clearSelection(); anchor_index = QModelIndex(); + resize_sig = nullptr; } void BinaryView::leaveEvent(QEvent *event) { @@ -107,34 +141,17 @@ void BinaryView::leaveEvent(QEvent *event) { void BinaryView::setMessage(const QString &message_id) { model->setMessage(message_id); clearSelection(); + anchor_index = QModelIndex(); + resize_sig = nullptr; + hovered_sig = nullptr; updateState(); } -const Signal *BinaryView::getResizingSignal() const { - if (anchor_index.isValid()) { - auto item = (const BinaryViewModel::Item *)anchor_index.internalPointer(); - if (item && item->sigs.size() > 0) { - int archor_pos = anchor_index.row() * 8 + anchor_index.column(); - for (auto s : item->sigs) { - auto [sig_from, sig_to] = getSignalRange(s); - if (archor_pos == sig_from || archor_pos == sig_to) - return s; - } - } - } - return nullptr; -} - QSet BinaryView::getOverlappingSignals() const { QSet overlapping; - for (int i = 0; i < model->rowCount(); ++i) { - for (int j = 0; j < model->columnCount() - 1; ++j) { - auto item = (const BinaryViewModel::Item *)model->index(i, j).internalPointer(); - if (item && item->sigs.size() > 1) { - for (auto s : item->sigs) - overlapping.insert(s); - } - } + for (auto &item : model->items) { + if (item.sigs.size() > 1) + for (auto s : item.sigs) overlapping += s; } return overlapping; } @@ -142,53 +159,37 @@ QSet BinaryView::getOverlappingSignals() const { // BinaryViewModel void BinaryViewModel::setMessage(const QString &message_id) { - msg_id = message_id; - beginResetModel(); + msg_id = message_id; items.clear(); - row_count = 0; - - dbc_msg = dbc()->msg(msg_id); - if (dbc_msg) { + if ((dbc_msg = dbc()->msg(msg_id))) { row_count = dbc_msg->size; items.resize(row_count * column_count); for (int i = 0; i < dbc_msg->sigs.size(); ++i) { const auto &sig = dbc_msg->sigs[i]; auto [start, end] = getSignalRange(&sig); for (int j = start; j <= end; ++j) { - int idx = column_count * (j / 8) + j % 8; + int bit_index = sig.is_little_endian ? bigEndianBitIndex(j) : j; + int idx = column_count * (bit_index / 8) + bit_index % 8; if (idx >= items.size()) { qWarning() << "signal " << sig.name.c_str() << "out of bounds.start_bit:" << sig.start_bit << "size:" << sig.size; break; } - if (j == start) { - sig.is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true; - } else if (j == end) { - sig.is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true; - } + if (j == start) sig.is_little_endian ? items[idx].is_lsb = true : items[idx].is_msb = true; + if (j == end) sig.is_little_endian ? items[idx].is_msb = true : items[idx].is_lsb = true; items[idx].bg_color = getColor(i); - items[idx].sigs.push_back(&dbc_msg->sigs[i]); + items[idx].sigs.push_back(&sig); } } } else { row_count = can->lastMessage(msg_id).dat.size(); items.resize(row_count * column_count); } - endResetModel(); } -QModelIndex BinaryViewModel::index(int row, int column, const QModelIndex &parent) const { - return createIndex(row, column, (void *)&items[row * column_count + column]); -} - -Qt::ItemFlags BinaryViewModel::flags(const QModelIndex &index) const { - return (index.column() == column_count - 1) ? Qt::ItemIsEnabled : Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - void BinaryViewModel::updateState() { auto prev_items = items; - const auto &binary = can->lastMessage(msg_id).dat; // data size may changed. if (!dbc_msg && binary.size() != row_count) { @@ -198,7 +199,6 @@ void BinaryViewModel::updateState() { items.resize(row_count * column_count); endResetModel(); } - char hex[3] = {'\0'}; for (int i = 0; i < std::min(binary.size(), row_count); ++i) { for (int j = 0; j < column_count - 1; ++j) { @@ -248,9 +248,8 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op BinaryView *bin_view = (BinaryView *)parent(); painter->save(); - bool hover = std::find_if(item->sigs.begin(), item->sigs.end(), [=](auto s) { return s == bin_view->hoveredSignal(); }) != - item->sigs.end(); // background + bool hover = item->sigs.contains(bin_view->hoveredSignal()); QColor bg_color = hover ? hoverColor(item->bg_color) : item->bg_color; if (option.state & QStyle::State_Selected) { bg_color = selection_color; @@ -258,7 +257,7 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op painter->fillRect(option.rect, bg_color); // text - if (index.column() == 8) { // hex column + if (index.column() == 8) { // hex column painter->setFont(hex_font); } else if (hover) { painter->setPen(Qt::white); diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index a907a673bf..05bfe7e79f 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -28,12 +28,16 @@ public: BinaryViewModel(QObject *parent) : QAbstractTableModel(parent) {} void setMessage(const QString &message_id); void updateState(); - Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { return {}; } int rowCount(const QModelIndex &parent = QModelIndex()) const override { return row_count; } int columnCount(const QModelIndex &parent = QModelIndex()) const override { return column_count; } + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { + return createIndex(row, column, (void *)&items[row * column_count + column]); + } + Qt::ItemFlags flags(const QModelIndex &index) const override { + return (index.column() == column_count - 1) ? Qt::ItemIsEnabled : Qt::ItemIsEnabled | Qt::ItemIsSelectable; + } struct Item { QColor bg_color = QColor(Qt::white); @@ -42,13 +46,13 @@ public: QString val = "0"; QList sigs; }; + std::vector items; private: QString msg_id; const Msg *dbc_msg; int row_count = 0; const int column_count = 9; - std::vector items; }; class BinaryView : public QTableView { @@ -64,7 +68,7 @@ public: signals: void signalHovered(const Signal *sig); - void addSignal(int from, int size); + void addSignal(int start_bit, int size, bool little_endian); void resizeSignal(const Signal *sig, int from, int size); private: @@ -73,10 +77,10 @@ private: void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void leaveEvent(QEvent *event) override; - const Signal *getResizingSignal() const; QModelIndex anchor_index; BinaryViewModel *model; BinaryItemDelegate *delegate; + const Signal *resize_sig = nullptr; const Signal *hovered_sig = nullptr; }; diff --git a/tools/cabana/dbcmanager.cc b/tools/cabana/dbcmanager.cc index 184fab38eb..abdd9a08df 100644 --- a/tools/cabana/dbcmanager.cc +++ b/tools/cabana/dbcmanager.cc @@ -147,9 +147,9 @@ double get_raw_value(uint8_t *data, size_t data_size, const Signal &sig) { return value; } -void updateSigSizeParamsFromRange(Signal &s, int from, int to) { - s.start_bit = s.is_little_endian ? from : bigEndianBitIndex(from); - s.size = to - from + 1; +void updateSigSizeParamsFromRange(Signal &s, int start_bit, int size) { + s.start_bit = s.is_little_endian ? start_bit : bigEndianBitIndex(start_bit); + s.size = size; if (s.is_little_endian) { s.lsb = s.start_bit; s.msb = s.start_bit + s.size - 1; diff --git a/tools/cabana/dbcmanager.h b/tools/cabana/dbcmanager.h index 2b6aca41d7..81c723a20d 100644 --- a/tools/cabana/dbcmanager.h +++ b/tools/cabana/dbcmanager.h @@ -49,7 +49,7 @@ private: double get_raw_value(uint8_t *data, size_t data_size, const Signal &sig); int bigEndianStartBitsIndex(int start_bit); int bigEndianBitIndex(int index); -void updateSigSizeParamsFromRange(Signal &s, int from, int to); +void updateSigSizeParamsFromRange(Signal &s, int start_bit, int size); std::pair getSignalRange(const Signal *s); DBCManager *dbc(); inline QString msgName(const QString &id, const char *def = "untitled") { diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 287c75a617..f96b60847b 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -130,15 +130,13 @@ void DetailWidget::setMessage(const QString &message_id) { break; } } + msg_id = message_id; if (index == -1) { index = tabbar->addTab(message_id); tabbar->setTabToolTip(index, msgName(message_id)); } tabbar->setCurrentIndex(index); - msg_id = message_id; dbcMsgChanged(); - - scroll->verticalScrollBar()->setValue(0); } void DetailWidget::dbcMsgChanged(int show_form_idx) { @@ -187,6 +185,7 @@ void DetailWidget::dbcMsgChanged(int show_form_idx) { warning_label->setText(warnings.join('\n')); warning_widget->setVisible(!warnings.isEmpty()); setUpdatesEnabled(true); + scroll->verticalScrollBar()->setValue(0); } void DetailWidget::updateState() { @@ -232,24 +231,34 @@ void DetailWidget::removeMsg() { } } -void DetailWidget::addSignal(int from, int to) { - if (auto msg = dbc()->msg(msg_id)) { - Signal sig = {}; +void DetailWidget::addSignal(int start_bit, int size, bool little_endian) { + auto msg = dbc()->msg(msg_id); + if (!msg) { for (int i = 1; /**/; ++i) { - sig.name = "NEW_SIGNAL_" + std::to_string(i); - auto it = std::find_if(msg->sigs.begin(), msg->sigs.end(), [&](auto &s) { return sig.name == s.name; }); - if (it == msg->sigs.end()) break; + std::string name = "NEW_MSG_" + std::to_string(i); + auto it = std::find_if(dbc()->getDBC()->msgs.begin(), dbc()->getDBC()->msgs.end(), [&](auto &m) { return m.name == name; }); + if (it == dbc()->getDBC()->msgs.end()) { + dbc()->updateMsg(msg_id, name.c_str(), can->lastMessage(msg_id).dat.size()); + msg = dbc()->msg(msg_id); + break; + } } - sig.is_little_endian = false, - updateSigSizeParamsFromRange(sig, from, to); - dbc()->addSignal(msg_id, sig); - dbcMsgChanged(msg->sigs.size() - 1); } + Signal sig = {}; + for (int i = 1; /**/; ++i) { + sig.name = "NEW_SIGNAL_" + std::to_string(i); + auto it = std::find_if(msg->sigs.begin(), msg->sigs.end(), [&](auto &s) { return sig.name == s.name; }); + if (it == msg->sigs.end()) break; + } + sig.is_little_endian = little_endian; + updateSigSizeParamsFromRange(sig, start_bit, size); + dbc()->addSignal(msg_id, sig); + dbcMsgChanged(msg->sigs.size() - 1); } -void DetailWidget::resizeSignal(const Signal *sig, int from, int to) { +void DetailWidget::resizeSignal(const Signal *sig, int start_bit, int size) { Signal s = *sig; - updateSigSizeParamsFromRange(s, from, to); + updateSigSizeParamsFromRange(s, start_bit, size); saveSignal(sig, s); } @@ -272,8 +281,7 @@ void DetailWidget::saveSignal(const Signal *sig, const Signal &new_sig) { } dbc()->updateSignal(msg_id, sig->name.c_str(), new_sig); - // update binary view and history log - updateState(); + dbcMsgChanged(); } void DetailWidget::removeSignal(const Signal *sig) { diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 7cb3a505ee..915e0bde60 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -30,7 +30,7 @@ public: private: void updateChartState(const QString &id, const Signal *sig, bool opened); void showTabBarContextMenu(const QPoint &pt); - void addSignal(int start_bit, int to); + void addSignal(int start_bit, int size, bool little_endian); void resizeSignal(const Signal *sig, int from, int to); void saveSignal(const Signal *sig, const Signal &new_sig); void removeSignal(const Signal *sig); diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index ee91887d0a..99365294b8 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -28,7 +28,6 @@ SignalForm::SignalForm(QWidget *parent) : QWidget(parent) { endianness->addItems({"Little", "Big"}); form_layout->addRow(tr("Endianness"), endianness); - ; form_layout->addRow(tr("lsb"), lsb = new QLabel()); form_layout->addRow(tr("msb"), msb = new QLabel()); @@ -130,7 +129,18 @@ void SignalEdit::saveSignal() { s.offset = form->offset->text().toDouble(); s.factor = form->factor->text().toDouble(); s.is_signed = form->sign->currentIndex() == 0; - s.is_little_endian = form->endianness->currentIndex() == 0; + bool little_endian = form->endianness->currentIndex() == 0; + if (little_endian != s.is_little_endian) { + int start = std::floor(s.start_bit / 8); + if (little_endian) { + int end = std::floor((s.start_bit - s.size + 1) / 8); + s.start_bit = start == end ? s.start_bit - s.size + 1 : bigEndianStartBitsIndex(s.start_bit); + } else { + int end = std::floor((s.start_bit + s.size - 1) / 8); + s.start_bit = start == end ? s.start_bit + s.size - 1 : bigEndianBitIndex(s.start_bit); + } + s.is_little_endian = little_endian; + } if (s.is_little_endian) { s.lsb = s.start_bit; s.msb = s.start_bit + s.size - 1;