|
|
@ -11,11 +11,6 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include "tools/cabana/commands.h" |
|
|
|
#include "tools/cabana/commands.h" |
|
|
|
|
|
|
|
|
|
|
|
static QString msg_node_from_id(const MessageId &id) { |
|
|
|
|
|
|
|
auto msg = dbc()->msg(id); |
|
|
|
|
|
|
|
return msg ? msg->transmitter : QString(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget(parent) { |
|
|
|
MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget(parent) { |
|
|
|
QVBoxLayout *main_layout = new QVBoxLayout(this); |
|
|
|
QVBoxLayout *main_layout = new QVBoxLayout(this); |
|
|
|
main_layout->setContentsMargins(0, 0, 0, 0); |
|
|
|
main_layout->setContentsMargins(0, 0, 0, 0); |
|
|
@ -23,12 +18,10 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget |
|
|
|
// toolbar
|
|
|
|
// toolbar
|
|
|
|
main_layout->addWidget(createToolBar()); |
|
|
|
main_layout->addWidget(createToolBar()); |
|
|
|
// message table
|
|
|
|
// message table
|
|
|
|
view = new MessageView(this); |
|
|
|
main_layout->addWidget(view = new MessageView(this)); |
|
|
|
model = new MessageListModel(this); |
|
|
|
view->setItemDelegate(delegate = new MessageBytesDelegate(view, settings.multiple_lines_hex)); |
|
|
|
header = new MessageViewHeader(this); |
|
|
|
view->setModel(model = new MessageListModel(this)); |
|
|
|
view->setItemDelegate(delegate = new MessageBytesDelegate(view, settings.multiple_lines_bytes)); |
|
|
|
view->setHeader(header = new MessageViewHeader(this)); |
|
|
|
view->setHeader(header); |
|
|
|
|
|
|
|
view->setModel(model); |
|
|
|
|
|
|
|
view->setSortingEnabled(true); |
|
|
|
view->setSortingEnabled(true); |
|
|
|
view->sortByColumn(MessageListModel::Column::NAME, Qt::AscendingOrder); |
|
|
|
view->sortByColumn(MessageListModel::Column::NAME, Qt::AscendingOrder); |
|
|
|
view->setAllColumnsShowFocus(true); |
|
|
|
view->setAllColumnsShowFocus(true); |
|
|
@ -36,6 +29,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget |
|
|
|
view->setItemsExpandable(false); |
|
|
|
view->setItemsExpandable(false); |
|
|
|
view->setIndentation(0); |
|
|
|
view->setIndentation(0); |
|
|
|
view->setRootIsDecorated(false); |
|
|
|
view->setRootIsDecorated(false); |
|
|
|
|
|
|
|
view->setUniformRowHeights(!settings.multiple_lines_hex); |
|
|
|
|
|
|
|
|
|
|
|
// Must be called before setting any header parameters to avoid overriding
|
|
|
|
// Must be called before setting any header parameters to avoid overriding
|
|
|
|
restoreHeaderState(settings.message_header_state); |
|
|
|
restoreHeaderState(settings.message_header_state); |
|
|
@ -44,15 +38,14 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget |
|
|
|
header->setStretchLastSection(true); |
|
|
|
header->setStretchLastSection(true); |
|
|
|
header->setContextMenuPolicy(Qt::CustomContextMenu); |
|
|
|
header->setContextMenuPolicy(Qt::CustomContextMenu); |
|
|
|
|
|
|
|
|
|
|
|
main_layout->addWidget(view); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// suppress
|
|
|
|
// suppress
|
|
|
|
QHBoxLayout *suppress_layout = new QHBoxLayout(); |
|
|
|
QHBoxLayout *suppress_layout = new QHBoxLayout(); |
|
|
|
suppress_add = new QPushButton("Suppress Highlighted"); |
|
|
|
suppress_layout->addWidget(suppress_add = new QPushButton("Suppress Highlighted")); |
|
|
|
suppress_clear = new QPushButton(); |
|
|
|
suppress_layout->addWidget(suppress_clear = new QPushButton()); |
|
|
|
suppress_layout->addWidget(suppress_add); |
|
|
|
suppress_clear->setToolTip(tr("Clear suppressed")); |
|
|
|
suppress_layout->addWidget(suppress_clear); |
|
|
|
suppress_layout->addStretch(1); |
|
|
|
QCheckBox *suppress_defined_signals = new QCheckBox(tr("Suppress Defined Signals"), this); |
|
|
|
QCheckBox *suppress_defined_signals = new QCheckBox(tr("Suppress Signals"), this); |
|
|
|
|
|
|
|
suppress_defined_signals->setToolTip(tr("Suppress defined signals")); |
|
|
|
suppress_defined_signals->setChecked(settings.suppress_defined_signals); |
|
|
|
suppress_defined_signals->setChecked(settings.suppress_defined_signals); |
|
|
|
suppress_layout->addWidget(suppress_defined_signals); |
|
|
|
suppress_layout->addWidget(suppress_defined_signals); |
|
|
|
main_layout->addLayout(suppress_layout); |
|
|
|
main_layout->addLayout(suppress_layout); |
|
|
@ -62,10 +55,7 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget |
|
|
|
QObject::connect(header, &MessageViewHeader::filtersUpdated, model, &MessageListModel::setFilterStrings); |
|
|
|
QObject::connect(header, &MessageViewHeader::filtersUpdated, model, &MessageListModel::setFilterStrings); |
|
|
|
QObject::connect(header, &MessageViewHeader::customContextMenuRequested, this, &MessagesWidget::headerContextMenuEvent); |
|
|
|
QObject::connect(header, &MessageViewHeader::customContextMenuRequested, this, &MessagesWidget::headerContextMenuEvent); |
|
|
|
QObject::connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, header, &MessageViewHeader::updateHeaderPositions); |
|
|
|
QObject::connect(view->horizontalScrollBar(), &QScrollBar::valueChanged, header, &MessageViewHeader::updateHeaderPositions); |
|
|
|
QObject::connect(suppress_defined_signals, &QCheckBox::stateChanged, [=](int state) { |
|
|
|
QObject::connect(suppress_defined_signals, &QCheckBox::stateChanged, can, &AbstractStream::suppressDefinedSignals); |
|
|
|
settings.suppress_defined_signals = (state == Qt::Checked); |
|
|
|
|
|
|
|
emit settings.changed(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
QObject::connect(can, &AbstractStream::msgsReceived, model, &MessageListModel::msgsReceived); |
|
|
|
QObject::connect(can, &AbstractStream::msgsReceived, model, &MessageListModel::msgsReceived); |
|
|
|
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MessagesWidget::dbcModified); |
|
|
|
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &MessagesWidget::dbcModified); |
|
|
|
QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &MessagesWidget::dbcModified); |
|
|
|
QObject::connect(UndoStack::instance(), &QUndoStack::indexChanged, this, &MessagesWidget::dbcModified); |
|
|
@ -76,24 +66,17 @@ MessagesWidget::MessagesWidget(QWidget *parent) : menu(new QMenu(this)), QWidget |
|
|
|
view->updateBytesSectionSize(); |
|
|
|
view->updateBytesSectionSize(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
QObject::connect(view->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex &previous) { |
|
|
|
QObject::connect(view->selectionModel(), &QItemSelectionModel::currentChanged, [=](const QModelIndex ¤t, const QModelIndex &previous) { |
|
|
|
if (current.isValid() && current.row() < model->msgs.size()) { |
|
|
|
if (current.isValid() && current.row() < model->items_.size()) { |
|
|
|
auto &id = model->msgs[current.row()]; |
|
|
|
const auto &id = model->items_[current.row()].id; |
|
|
|
if (!current_msg_id || id != *current_msg_id) { |
|
|
|
if (!current_msg_id || id != *current_msg_id) { |
|
|
|
current_msg_id = id; |
|
|
|
current_msg_id = id; |
|
|
|
emit msgSelectionChanged(*current_msg_id); |
|
|
|
emit msgSelectionChanged(*current_msg_id); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
QObject::connect(suppress_add, &QPushButton::clicked, [=]() { |
|
|
|
QObject::connect(suppress_add, &QPushButton::clicked, this, &MessagesWidget::suppressHighlighted); |
|
|
|
model->suppress(); |
|
|
|
QObject::connect(suppress_clear, &QPushButton::clicked, this, &MessagesWidget::suppressHighlighted); |
|
|
|
updateSuppressedButtons(); |
|
|
|
suppressHighlighted(); |
|
|
|
}); |
|
|
|
|
|
|
|
QObject::connect(suppress_clear, &QPushButton::clicked, [=]() { |
|
|
|
|
|
|
|
model->clearSuppress(); |
|
|
|
|
|
|
|
updateSuppressedButtons(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
updateSuppressedButtons(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setWhatsThis(tr(R"( |
|
|
|
setWhatsThis(tr(R"( |
|
|
|
<b>Message View</b><br/> |
|
|
|
<b>Message View</b><br/> |
|
|
@ -126,19 +109,22 @@ void MessagesWidget::dbcModified() { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessagesWidget::selectMessage(const MessageId &msg_id) { |
|
|
|
void MessagesWidget::selectMessage(const MessageId &msg_id) { |
|
|
|
auto it = std::find(model->msgs.cbegin(), model->msgs.cend(), msg_id); |
|
|
|
auto it = std::find_if(model->items_.cbegin(), model->items_.cend(), |
|
|
|
if (it != model->msgs.cend()) { |
|
|
|
[&msg_id](auto &item) { return item.id == msg_id; }); |
|
|
|
view->setCurrentIndex(model->index(std::distance(model->msgs.cbegin(), it), 0)); |
|
|
|
if (it != model->items_.cend()) { |
|
|
|
|
|
|
|
view->setCurrentIndex(model->index(std::distance(model->items_.cbegin(), it), 0)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessagesWidget::updateSuppressedButtons() { |
|
|
|
void MessagesWidget::suppressHighlighted() { |
|
|
|
if (model->suppressed_bytes.empty()) { |
|
|
|
if (sender() == suppress_add) { |
|
|
|
suppress_clear->setEnabled(false); |
|
|
|
size_t n = can->suppressHighlighted(); |
|
|
|
suppress_clear->setText("Clear Suppressed"); |
|
|
|
suppress_clear->setText(tr("Clear (%1)").arg(n)); |
|
|
|
} else { |
|
|
|
|
|
|
|
suppress_clear->setEnabled(true); |
|
|
|
suppress_clear->setEnabled(true); |
|
|
|
suppress_clear->setText(QString("Clear Suppressed (%1)").arg(model->suppressed_bytes.size())); |
|
|
|
} else { |
|
|
|
|
|
|
|
can->clearSuppressed(); |
|
|
|
|
|
|
|
suppress_clear->setText(tr("Clear")); |
|
|
|
|
|
|
|
suppress_clear->setEnabled(false); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -160,12 +146,13 @@ void MessagesWidget::menuAboutToShow() { |
|
|
|
menu->addSeparator(); |
|
|
|
menu->addSeparator(); |
|
|
|
auto action = menu->addAction(tr("Mutlti-Line bytes"), this, &MessagesWidget::setMultiLineBytes); |
|
|
|
auto action = menu->addAction(tr("Mutlti-Line bytes"), this, &MessagesWidget::setMultiLineBytes); |
|
|
|
action->setCheckable(true); |
|
|
|
action->setCheckable(true); |
|
|
|
action->setChecked(settings.multiple_lines_bytes); |
|
|
|
action->setChecked(settings.multiple_lines_hex); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessagesWidget::setMultiLineBytes(bool multi) { |
|
|
|
void MessagesWidget::setMultiLineBytes(bool multi) { |
|
|
|
settings.multiple_lines_bytes = multi; |
|
|
|
settings.multiple_lines_hex = multi; |
|
|
|
delegate->setMultipleLines(multi); |
|
|
|
delegate->setMultipleLines(multi); |
|
|
|
|
|
|
|
view->setUniformRowHeights(!multi); |
|
|
|
view->updateBytesSectionSize(); |
|
|
|
view->updateBytesSectionSize(); |
|
|
|
view->doItemsLayout(); |
|
|
|
view->doItemsLayout(); |
|
|
|
} |
|
|
|
} |
|
|
@ -188,7 +175,7 @@ QVariant MessageListModel::headerData(int section, Qt::Orientation orientation, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
QVariant MessageListModel::data(const QModelIndex &index, int role) const { |
|
|
|
QVariant MessageListModel::data(const QModelIndex &index, int role) const { |
|
|
|
if (!index.isValid() || index.row() >= msgs.size()) return {}; |
|
|
|
if (!index.isValid() || index.row() >= items_.size()) return {}; |
|
|
|
|
|
|
|
|
|
|
|
auto getFreq = [](const CanData &d) { |
|
|
|
auto getFreq = [](const CanData &d) { |
|
|
|
if (d.freq > 0 && (can->currentSec() - d.ts - 1.0 / settings.fps) < (5.0 / d.freq)) { |
|
|
|
if (d.freq > 0 && (can->currentSec() - d.ts - 1.0 / settings.fps) < (5.0 / d.freq)) { |
|
|
@ -198,33 +185,24 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const auto &id = msgs[index.row()]; |
|
|
|
const auto &item = items_[index.row()]; |
|
|
|
auto &can_data = can->lastMessage(id); |
|
|
|
|
|
|
|
if (role == Qt::DisplayRole) { |
|
|
|
if (role == Qt::DisplayRole) { |
|
|
|
switch (index.column()) { |
|
|
|
switch (index.column()) { |
|
|
|
case Column::NAME: return msgName(id); |
|
|
|
case Column::NAME: return item.name; |
|
|
|
case Column::SOURCE: return id.source != INVALID_SOURCE ? QString::number(id.source) : "N/A"; |
|
|
|
case Column::SOURCE: return item.id.source != INVALID_SOURCE ? QString::number(item.id.source) : "N/A"; |
|
|
|
case Column::ADDRESS: return QString::number(id.address, 16); |
|
|
|
case Column::ADDRESS: return QString::number(item.id.address, 16); |
|
|
|
case Column::NODE: return msg_node_from_id(id); |
|
|
|
case Column::NODE: return item.node; |
|
|
|
case Column::FREQ: return id.source != INVALID_SOURCE ? getFreq(can_data) : "N/A"; |
|
|
|
case Column::FREQ: return item.id.source != INVALID_SOURCE ? getFreq(*item.data) : "N/A"; |
|
|
|
case Column::COUNT: return id.source != INVALID_SOURCE ? QString::number(can_data.count) : "N/A"; |
|
|
|
case Column::COUNT: return item.id.source != INVALID_SOURCE ? QString::number(item.data->count) : "N/A"; |
|
|
|
case Column::DATA: return id.source != INVALID_SOURCE ? toHex(can_data.dat) : "N/A"; |
|
|
|
case Column::DATA: return item.id.source != INVALID_SOURCE ? "" : "N/A"; |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (role == ColorsRole) { |
|
|
|
} else if (role == ColorsRole) { |
|
|
|
QVector<QColor> colors = can_data.colors; |
|
|
|
return QVariant::fromValue((void*)(&item.data->colors)); |
|
|
|
if (!suppressed_bytes.empty()) { |
|
|
|
} else if (role == BytesRole && index.column() == Column::DATA && item.id.source != INVALID_SOURCE) { |
|
|
|
for (int i = 0; i < colors.size(); i++) { |
|
|
|
return QVariant::fromValue((void*)(&item.data->dat)); |
|
|
|
if (suppressed_bytes.contains({id, i})) { |
|
|
|
|
|
|
|
colors[i] = QColor(255, 255, 255, 0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return QVariant::fromValue(colors); |
|
|
|
|
|
|
|
} else if (role == BytesRole && index.column() == Column::DATA && id.source != INVALID_SOURCE) { |
|
|
|
|
|
|
|
return can_data.dat; |
|
|
|
|
|
|
|
} else if (role == Qt::ToolTipRole && index.column() == Column::NAME) { |
|
|
|
} else if (role == Qt::ToolTipRole && index.column() == Column::NAME) { |
|
|
|
auto msg = dbc()->msg(id); |
|
|
|
auto msg = dbc()->msg(item.id); |
|
|
|
auto tooltip = msg ? msg->name : UNTITLED; |
|
|
|
auto tooltip = item.name; |
|
|
|
if (msg && !msg->comment.isEmpty()) tooltip += "<br /><span style=\"color:gray;\">" + msg->comment + "</span>"; |
|
|
|
if (msg && !msg->comment.isEmpty()) tooltip += "<br /><span style=\"color:gray;\">" + msg->comment + "</span>"; |
|
|
|
return tooltip; |
|
|
|
return tooltip; |
|
|
|
} |
|
|
|
} |
|
|
@ -232,31 +210,31 @@ QVariant MessageListModel::data(const QModelIndex &index, int role) const { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessageListModel::setFilterStrings(const QMap<int, QString> &filters) { |
|
|
|
void MessageListModel::setFilterStrings(const QMap<int, QString> &filters) { |
|
|
|
filter_str = filters; |
|
|
|
filters_ = filters; |
|
|
|
filterAndSort(); |
|
|
|
filterAndSort(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessageListModel::dbcModified() { |
|
|
|
void MessageListModel::dbcModified() { |
|
|
|
dbc_address.clear(); |
|
|
|
dbc_messages_.clear(); |
|
|
|
for (const auto &[_, m] : dbc()->getMessages(-1)) { |
|
|
|
for (const auto &[_, m] : dbc()->getMessages(-1)) { |
|
|
|
dbc_address.insert(m.address); |
|
|
|
dbc_messages_.insert(MessageId{.source = INVALID_SOURCE, .address = m.address}); |
|
|
|
} |
|
|
|
} |
|
|
|
filterAndSort(); |
|
|
|
filterAndSort(true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessageListModel::sortMessages(std::vector<MessageId> &new_msgs) { |
|
|
|
void MessageListModel::sortItems(std::vector<MessageListModel::Item> &items) { |
|
|
|
auto do_sort = [order = sort_order](std::vector<MessageId> &m, auto proj) { |
|
|
|
auto do_sort = [order = sort_order](std::vector<MessageListModel::Item> &m, auto proj) { |
|
|
|
std::sort(m.begin(), m.end(), [order, proj = std::move(proj)](auto &l, auto &r) { |
|
|
|
std::stable_sort(m.begin(), m.end(), [order, proj = std::move(proj)](auto &l, auto &r) { |
|
|
|
return order == Qt::AscendingOrder ? proj(l) < proj(r) : proj(l) > proj(r); |
|
|
|
return order == Qt::AscendingOrder ? proj(l) < proj(r) : proj(l) > proj(r); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
}; |
|
|
|
switch (sort_column) { |
|
|
|
switch (sort_column) { |
|
|
|
case Column::NAME: do_sort(new_msgs, [](auto &id) { return std::make_pair(msgName(id), id); }); break; |
|
|
|
case Column::NAME: do_sort(items, [](auto &item) { return std::tie(item.name, item.id); }); break; |
|
|
|
case Column::SOURCE: do_sort(new_msgs, [](auto &id) { return std::tie(id.source, id); }); break; |
|
|
|
case Column::SOURCE: do_sort(items, [](auto &item) { return std::tie(item.id.source, item.id); }); break; |
|
|
|
case Column::ADDRESS: do_sort(new_msgs, [](auto &id) { return std::tie(id.address, id);}); break; |
|
|
|
case Column::ADDRESS: do_sort(items, [](auto &item) { return std::tie(item.id.address, item.id);}); break; |
|
|
|
case Column::NODE: do_sort(new_msgs, [](auto &id) { return std::make_pair(msg_node_from_id(id), id);}); break; |
|
|
|
case Column::NODE: do_sort(items, [](auto &item) { return std::tie(item.node, item.id);}); break; |
|
|
|
case Column::FREQ: do_sort(new_msgs, [](auto &id) { return std::tie(can->lastMessage(id).freq, id); }); break; |
|
|
|
case Column::FREQ: do_sort(items, [](auto &item) { return std::tie(item.data->freq, item.id); }); break; |
|
|
|
case Column::COUNT: do_sort(new_msgs, [](auto &id) { return std::tie(can->lastMessage(id).count, id); }); break; |
|
|
|
case Column::COUNT: do_sort(items, [](auto &item) { return std::tie(item.data->count, item.id); }); break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -275,83 +253,85 @@ static bool parseRange(const QString &filter, uint32_t value, int base = 10) { |
|
|
|
return ok && value >= min && value <= max; |
|
|
|
return ok && value >= min && value <= max; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, const QMap<int, QString> &filters) { |
|
|
|
bool MessageListModel::match(const MessageListModel::Item &item) { |
|
|
|
|
|
|
|
if (filters_.isEmpty()) |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
|
bool match = true; |
|
|
|
bool match = true; |
|
|
|
for (auto it = filters.cbegin(); it != filters.cend() && match; ++it) { |
|
|
|
for (auto it = filters_.cbegin(); it != filters_.cend() && match; ++it) { |
|
|
|
const QString &txt = it.value(); |
|
|
|
const QString &txt = it.value(); |
|
|
|
QRegularExpression re(txt, QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption); |
|
|
|
|
|
|
|
switch (it.key()) { |
|
|
|
switch (it.key()) { |
|
|
|
case Column::NAME: { |
|
|
|
case Column::NAME: { |
|
|
|
const auto msg = dbc()->msg(id); |
|
|
|
match = item.name.contains(txt, Qt::CaseInsensitive); |
|
|
|
match = re.match(msg ? msg->name : UNTITLED).hasMatch(); |
|
|
|
if (!match) { |
|
|
|
match = match || (msg && std::any_of(msg->sigs.cbegin(), msg->sigs.cend(), |
|
|
|
const auto m = dbc()->msg(item.id); |
|
|
|
[&re](const auto &s) { return re.match(s->name).hasMatch(); })); |
|
|
|
match = m && std::any_of(m->sigs.cbegin(), m->sigs.cend(), |
|
|
|
|
|
|
|
[&txt](const auto &s) { return s->name.contains(txt, Qt::CaseInsensitive); }); |
|
|
|
|
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
case Column::SOURCE: |
|
|
|
case Column::SOURCE: |
|
|
|
match = parseRange(txt, id.source); |
|
|
|
match = parseRange(txt, item.id.source); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case Column::ADDRESS: { |
|
|
|
case Column::ADDRESS: |
|
|
|
match = re.match(QString::number(id.address, 16)).hasMatch(); |
|
|
|
match = QString::number(item.id.address, 16).contains(txt, Qt::CaseInsensitive); |
|
|
|
match = match || parseRange(txt, id.address, 16); |
|
|
|
match = match || parseRange(txt, item.id.address, 16); |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
case Column::NODE: |
|
|
|
case Column::NODE: |
|
|
|
match = re.match(msg_node_from_id(id)).hasMatch(); |
|
|
|
match = item.node.contains(txt, Qt::CaseInsensitive); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case Column::FREQ: |
|
|
|
case Column::FREQ: |
|
|
|
// TODO: Hide stale messages?
|
|
|
|
// TODO: Hide stale messages?
|
|
|
|
match = parseRange(txt, data.freq); |
|
|
|
match = parseRange(txt, item.data->freq); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case Column::COUNT: |
|
|
|
case Column::COUNT: |
|
|
|
match = parseRange(txt, data.count); |
|
|
|
match = parseRange(txt, item.data->count); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case Column::DATA: { |
|
|
|
case Column::DATA: |
|
|
|
match = QString(data.dat.toHex()).contains(txt, Qt::CaseInsensitive); |
|
|
|
match = utils::toHex(item.data->dat).contains(txt, Qt::CaseInsensitive); |
|
|
|
match = match || re.match(QString(data.dat.toHex())).hasMatch(); |
|
|
|
|
|
|
|
match = match || re.match(QString(data.dat.toHex(' '))).hasMatch(); |
|
|
|
|
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return match; |
|
|
|
return match; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessageListModel::filterAndSort() { |
|
|
|
void MessageListModel::filterAndSort(bool force_reset) { |
|
|
|
std::vector<MessageId> new_msgs; |
|
|
|
// merge CAN and DBC messages
|
|
|
|
new_msgs.reserve(can->last_msgs.size() + dbc_address.size()); |
|
|
|
std::vector<MessageId> all_messages; |
|
|
|
|
|
|
|
all_messages.reserve(can->lastMessages().size() + dbc_messages_.size()); |
|
|
|
auto address = dbc_address; |
|
|
|
auto dbc_msgs = dbc_messages_; |
|
|
|
for (auto it = can->last_msgs.cbegin(); it != can->last_msgs.cend(); ++it) { |
|
|
|
for (const auto &[id, m] : can->lastMessages()) { |
|
|
|
if (filter_str.isEmpty() || matchMessage(it.key(), it.value(), filter_str)) { |
|
|
|
all_messages.push_back(id); |
|
|
|
new_msgs.push_back(it.key()); |
|
|
|
dbc_msgs.erase(MessageId{.source = INVALID_SOURCE, .address = id.address}); |
|
|
|
} |
|
|
|
|
|
|
|
address.remove(it.key().address); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
std::copy(dbc_msgs.begin(), dbc_msgs.end(), std::back_inserter(all_messages)); |
|
|
|
|
|
|
|
|
|
|
|
// merge all DBC messages
|
|
|
|
// filter and sort
|
|
|
|
for (auto &addr : address) { |
|
|
|
std::vector<Item> items; |
|
|
|
MessageId id{.source = INVALID_SOURCE, .address = addr}; |
|
|
|
for (const auto &id : all_messages) { |
|
|
|
if (filter_str.isEmpty() || matchMessage(id, {}, filter_str)) { |
|
|
|
auto msg = dbc()->msg(id); |
|
|
|
new_msgs.push_back(id); |
|
|
|
Item item = {.id = id, |
|
|
|
} |
|
|
|
.name = msg ? msg->name : UNTITLED, |
|
|
|
|
|
|
|
.node = msg ? msg->transmitter : QString(), |
|
|
|
|
|
|
|
.data = &can->lastMessage(id)}; |
|
|
|
|
|
|
|
if (match(item)) |
|
|
|
|
|
|
|
items.emplace_back(item); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
sortItems(items); |
|
|
|
|
|
|
|
|
|
|
|
sortMessages(new_msgs); |
|
|
|
if (force_reset || items_ != items) { |
|
|
|
|
|
|
|
|
|
|
|
if (msgs != new_msgs) { |
|
|
|
|
|
|
|
beginResetModel(); |
|
|
|
beginResetModel(); |
|
|
|
msgs = std::move(new_msgs); |
|
|
|
items_ = std::move(items); |
|
|
|
endResetModel(); |
|
|
|
endResetModel(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessageListModel::msgsReceived(const QHash<MessageId, CanData> *new_msgs, bool has_new_ids) { |
|
|
|
void MessageListModel::msgsReceived(const std::set<MessageId> *new_msgs, bool has_new_ids) { |
|
|
|
if (has_new_ids || filter_str.contains(Column::FREQ) || filter_str.contains(Column::COUNT) || filter_str.contains(Column::DATA)) { |
|
|
|
if (has_new_ids || filters_.contains(Column::FREQ) || filters_.contains(Column::COUNT) || filters_.contains(Column::DATA)) { |
|
|
|
filterAndSort(); |
|
|
|
filterAndSort(); |
|
|
|
} |
|
|
|
} |
|
|
|
for (int i = 0; i < msgs.size(); ++i) { |
|
|
|
for (int i = 0; i < items_.size(); ++i) { |
|
|
|
if (new_msgs->contains(msgs[i])) { |
|
|
|
if (!new_msgs || new_msgs->count(items_[i].id)) { |
|
|
|
for (int col = Column::FREQ; col < columnCount(); ++col) |
|
|
|
for (int col = Column::FREQ; col < columnCount(); ++col) |
|
|
|
emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole}); |
|
|
|
emit dataChanged(index(i, col), index(i, col), {Qt::DisplayRole}); |
|
|
|
} |
|
|
|
} |
|
|
@ -359,31 +339,13 @@ void MessageListModel::msgsReceived(const QHash<MessageId, CanData> *new_msgs, b |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessageListModel::sort(int column, Qt::SortOrder order) { |
|
|
|
void MessageListModel::sort(int column, Qt::SortOrder order) { |
|
|
|
if (column != columnCount() - 1) { |
|
|
|
if (column != Column::DATA) { |
|
|
|
sort_column = column; |
|
|
|
sort_column = column; |
|
|
|
sort_order = order; |
|
|
|
sort_order = order; |
|
|
|
filterAndSort(); |
|
|
|
filterAndSort(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MessageListModel::suppress() { |
|
|
|
|
|
|
|
const double cur_ts = can->currentSec(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (auto &id : msgs) { |
|
|
|
|
|
|
|
auto &can_data = can->lastMessage(id); |
|
|
|
|
|
|
|
for (int i = 0; i < can_data.dat.size(); i++) { |
|
|
|
|
|
|
|
const double dt = cur_ts - can_data.last_change_t[i]; |
|
|
|
|
|
|
|
if (dt < 2.0) { |
|
|
|
|
|
|
|
suppressed_bytes.insert({id, i}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MessageListModel::clearSuppress() { |
|
|
|
|
|
|
|
suppressed_bytes.clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MessageView
|
|
|
|
// MessageView
|
|
|
|
|
|
|
|
|
|
|
|
void MessageView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { |
|
|
|
void MessageView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { |
|
|
@ -414,14 +376,11 @@ void MessageView::updateBytesSectionSize() { |
|
|
|
auto delegate = ((MessageBytesDelegate *)itemDelegate()); |
|
|
|
auto delegate = ((MessageBytesDelegate *)itemDelegate()); |
|
|
|
int max_bytes = 8; |
|
|
|
int max_bytes = 8; |
|
|
|
if (!delegate->multipleLines()) { |
|
|
|
if (!delegate->multipleLines()) { |
|
|
|
for (auto it = can->last_msgs.constBegin(); it != can->last_msgs.constEnd(); ++it) { |
|
|
|
for (const auto &[_, m] : can->lastMessages()) { |
|
|
|
max_bytes = std::max(max_bytes, it.value().dat.size()); |
|
|
|
max_bytes = std::max<int>(max_bytes, m.dat.size()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
int width = delegate->widthForBytes(max_bytes); |
|
|
|
header()->resizeSection(MessageListModel::Column::DATA, delegate->sizeForBytes(max_bytes).width()); |
|
|
|
if (header()->sectionSize(MessageListModel::Column::DATA) != width) { |
|
|
|
|
|
|
|
header()->resizeSection(MessageListModel::Column::DATA, width); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// MessageViewHeader
|
|
|
|
// MessageViewHeader
|
|
|
@ -446,8 +405,7 @@ void MessageViewHeader::updateHeaderPositions() { |
|
|
|
for (int i = 0; i < count(); i++) { |
|
|
|
for (int i = 0; i < count(); i++) { |
|
|
|
if (editors[i]) { |
|
|
|
if (editors[i]) { |
|
|
|
int h = editors[i]->sizeHint().height(); |
|
|
|
int h = editors[i]->sizeHint().height(); |
|
|
|
editors[i]->move(sectionViewportPosition(i), sz.height()); |
|
|
|
editors[i]->setGeometry(sectionViewportPosition(i), sz.height(), sectionSize(i), h); |
|
|
|
editors[i]->resize(sectionSize(i), h); |
|
|
|
|
|
|
|
editors[i]->setHidden(isSectionHidden(i)); |
|
|
|
editors[i]->setHidden(isSectionHidden(i)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|