#include "isotp_interpreterwindow.h" #include "ui_isotp_interpreterwindow.h" #include "mainwindow.h" #include "helpwindow.h" #include "filterutility.h" ISOTP_InterpreterWindow::ISOTP_InterpreterWindow(const QVector *frames, QWidget *parent) : QDialog(parent), ui(new Ui::ISOTP_InterpreterWindow) { ui->setupUi(this); setWindowFlags(Qt::Window); modelFrames = frames; m_isAutoScrolling = false; decoder = new ISOTP_HANDLER; udsDecoder = new UDS_HANDLER; decoder->setReception(true); decoder->setProcessAll(true); udsDecoder->setReception(false); connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &ISOTP_InterpreterWindow::updatedFrames); connect(MainWindow::getReference(), &MainWindow::framesUpdated, decoder, &ISOTP_HANDLER::updatedFrames); connect(decoder, &ISOTP_HANDLER::newISOMessage, this, &ISOTP_InterpreterWindow::newISOMessage); connect(udsDecoder, &UDS_HANDLER::newUDSMessage, this, &ISOTP_InterpreterWindow::newUDSMessage); connect(ui->listFilter, &QListWidget::itemChanged, this, &ISOTP_InterpreterWindow::listFilterItemChanged); connect(ui->btnAll, &QPushButton::clicked, this, &ISOTP_InterpreterWindow::filterAll); connect(ui->btnNone, &QPushButton::clicked, this, &ISOTP_InterpreterWindow::filterNone); connect(ui->btnCaptured, &QPushButton::clicked, this, &ISOTP_InterpreterWindow::interpretCapturedFrames); connect(ui->tableIsoFrames, &QTableWidget::itemSelectionChanged, this, &ISOTP_InterpreterWindow::showDetailView); connect(ui->btnClearList, &QPushButton::clicked, this, &ISOTP_InterpreterWindow::clearList); connect(ui->btnSaveList, &QPushButton::clicked, this, &ISOTP_InterpreterWindow::saveList); connect(ui->cbUseExtendedAddressing, SIGNAL(toggled(bool)), this, SLOT(useExtendedAddressing(bool))); connect(ui->tableIsoFrames->verticalScrollBar(), &QScrollBar::valueChanged, this, &ISOTP_InterpreterWindow::onManualScroll); QStringList headers; headers << "Timestamp" << "ID" << "Bus" << "Dir" << "Length" << "Data"; ui->tableIsoFrames->setColumnCount(6); ui->tableIsoFrames->setColumnWidth(0, 100); ui->tableIsoFrames->setColumnWidth(1, 50); ui->tableIsoFrames->setColumnWidth(2, 50); ui->tableIsoFrames->setColumnWidth(3, 50); ui->tableIsoFrames->setColumnWidth(4, 75); ui->tableIsoFrames->setColumnWidth(5, 200); ui->tableIsoFrames->setHorizontalHeaderLabels(headers); QHeaderView *HorzHdr = ui->tableIsoFrames->horizontalHeader(); HorzHdr->setStretchLastSection(true); connect(HorzHdr, SIGNAL(sectionClicked(int)), this, SLOT(headerClicked(int))); decoder->setReception(true); decoder->setFlowCtrl(false); decoder->setProcessAll(true); } ISOTP_InterpreterWindow::~ISOTP_InterpreterWindow() { delete decoder; delete ui; } void ISOTP_InterpreterWindow::showEvent(QShowEvent* event) { QDialog::showEvent(event); readSettings(); QProgressDialog progress(qApp->activeWindow()); progress.setWindowModality(Qt::WindowModal); progress.setLabelText("Analyzing Frames..."); progress.setCancelButton(nullptr); progress.setRange(0,0); progress.setMinimumDuration(0); progress.show(); qApp->processEvents(); decoder->updatedFrames(-2); //decoder->rapidFrames(nullptr, *modelFrames); progress.cancel(); installEventFilter(this); } void ISOTP_InterpreterWindow::closeEvent(QCloseEvent *event) { Q_UNUSED(event) removeEventFilter(this); writeSettings(); } bool ISOTP_InterpreterWindow::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyRelease) { QKeyEvent *keyEvent = static_cast(event); switch (keyEvent->key()) { case Qt::Key_F1: HelpWindow::getRef()->showHelp("isotp_decoder.md"); break; } return true; } else { // standard event processing return QObject::eventFilter(obj, event); } return false; } void ISOTP_InterpreterWindow::readSettings() { QSettings settings; if (settings.value("Main/SaveRestorePositions", false).toBool()) { resize(settings.value("ISODecodeWindow/WindowSize", this->size()).toSize()); move(Utility::constrainedWindowPos(settings.value("ISODecodeWindow/WindowPos", QPoint(50, 50)).toPoint())); } } void ISOTP_InterpreterWindow::writeSettings() { QSettings settings; if (settings.value("Main/SaveRestorePositions", false).toBool()) { settings.setValue("ISODecodeWindow/WindowSize", size()); settings.setValue("ISODecodeWindow/WindowPos", pos()); } } //erase current list then repopulate as if all the previously captured frames just came in again. void ISOTP_InterpreterWindow::interpretCapturedFrames() { clearList(); decoder->updatedFrames(-2); } void ISOTP_InterpreterWindow::listFilterItemChanged(QListWidgetItem *item) { if (item) { int id = FilterUtility::getIdAsInt(item); bool state = item->checkState(); //qDebug() << id << "*" << state; idFilters[id] = state; } } void ISOTP_InterpreterWindow::filterAll() { for (int i = 0 ; i < ui->listFilter->count(); i++) { ui->listFilter->item(i)->setCheckState(Qt::Checked); //idFilters[ui->listFilter->item(i)->text().toInt(nullptr, 16)] = true; } } void ISOTP_InterpreterWindow::filterNone() { for (int i = 0 ; i < ui->listFilter->count(); i++) { ui->listFilter->item(i)->setCheckState(Qt::Unchecked); //idFilters[ui->listFilter->item(i)->text().toInt(nullptr, 16)] = false; } } void ISOTP_InterpreterWindow::clearList() { qDebug() << "Clearing the table"; ui->tableIsoFrames->clearContents(); ui->tableIsoFrames->model()->removeRows(0, ui->tableIsoFrames->rowCount()); messages.clear(); //idFilters.clear(); } /* * A bit complicated as the list doesn't have the detailed analysis in it. Have to take each list entry * and process it to get the details then save those details to the file as well. */ void ISOTP_InterpreterWindow::saveList() { QString buildString; QString filename; QFileDialog dialog(this); QSettings settings; QStringList filters; filters.append(QString(tr("Text File (*.txt)"))); dialog.setFileMode(QFileDialog::AnyFile); dialog.setNameFilters(filters); dialog.setViewMode(QFileDialog::Detail); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setDirectory(settings.value("FrameInfo/LoadSaveDirectory", dialog.directory().path()).toString()); if (dialog.exec() == QDialog::Accepted) { settings.setValue("FrameInfo/LoadSaveDirectory", dialog.directory().path()); filename = dialog.selectedFiles()[0]; if (!filename.contains('.')) filename += ".txt"; if (dialog.selectedNameFilter() == filters[0]) { QFile *outFile = new QFile(filename); if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) { delete outFile; return; } int rows = messages.count(); for (int r = 0 ; r < rows; r++) { ISOTP_MESSAGE msg = messages.at(r); const unsigned char *data = reinterpret_cast(msg.payload().constData()); int dataLen = msg.payload().length(); if (msg.reportedLength != dataLen) { continue; } buildString.append(QString::number(msg.timeStamp().microSeconds()) + " " + QString::number(msg.frameId(), 16) + " "); //buildString.append(tr("Raw Payload: ")); for (int i = 0; i < dataLen; i++) { buildString.append(Utility::formatNumber(data[i])); buildString.append(" "); } buildString.append("\n\n"); UDS_MESSAGE udsMsg; bool result; udsMsg = udsDecoder->tryISOtoUDS(msg, &result); if (result) buildString.append(udsDecoder->getDetailedMessageAnalysis(udsMsg)); buildString.append("\n*********************************************************\n"); outFile->write(buildString.toUtf8()); } outFile->close(); delete outFile; } } } void ISOTP_InterpreterWindow::useExtendedAddressing(bool checked) { decoder->setExtendedAddressing(checked); this->interpretCapturedFrames(); } void ISOTP_InterpreterWindow::updatedFrames(int numFrames) { if (numFrames == -1) //all frames deleted. Kill the display { clearList(); } else if (numFrames == -2) //all new set of frames. Reset { clearList(); } else //just got some new frames. See if they are relevant. { } } void ISOTP_InterpreterWindow::headerClicked(int logicalIndex) { ui->tableIsoFrames->setSortingEnabled(false); ui->tableIsoFrames->sortByColumn(logicalIndex, Qt::SortOrder::AscendingOrder); } void ISOTP_InterpreterWindow::showDetailView() { QString buildString; ISOTP_MESSAGE *msg; int rowNum = ui->tableIsoFrames->currentRow(); ui->txtFrameDetails->clear(); if (rowNum == -1) return; msg = &messages[rowNum]; const unsigned char *data = reinterpret_cast(msg->payload().constData()); int dataLen = msg->payload().length(); if (msg->reportedLength != dataLen) { buildString.append("Message didn't have the correct number of bytes.\rExpected " + QString::number(msg->reportedLength) + " got " + QString::number(dataLen) + "\r\r"); } buildString.append(tr("Raw Payload: ")); for (int i = 0; i < dataLen; i++) { buildString.append(Utility::formatNumber(data[i])); buildString.append(" "); } buildString.append("\r\r"); ui->txtFrameDetails->setPlainText(buildString); //pass this frame to the UDS decoder to see if it feels it could be a UDS related message udsDecoder->gotISOTPFrame(messages[rowNum]); } void ISOTP_InterpreterWindow::newUDSMessage(UDS_MESSAGE msg) { //qDebug() << "Got UDS message in ISOTP Interpreter"; QString buildText; buildText = ui->txtFrameDetails->toPlainText(); buildText.append(udsDecoder->getDetailedMessageAnalysis(msg)); ui->txtFrameDetails->setPlainText(buildText); } void ISOTP_InterpreterWindow::onManualScroll() { // If the user manually scrolls, check if they're at the bottom if (!m_isAutoScrolling) { QScrollBar *scrollBar = ui->tableIsoFrames->verticalScrollBar(); if (scrollBar->value() == scrollBar->maximum()) { // At bottom - enable autoscroll ui->cbAutoscroll->setChecked(true); } else { // Not at bottom - disable autoscroll ui->cbAutoscroll->setChecked(false); } } } void ISOTP_InterpreterWindow::newISOMessage(ISOTP_MESSAGE msg) { int rowNum; QString tempString; const unsigned char *data = reinterpret_cast(msg.payload().constData()); int dataLen = msg.payload().length(); if ((msg.reportedLength != dataLen) && !ui->cbShowIncomplete->isChecked()) return; if (idFilters.find(msg.frameId()) == idFilters.end()) { idFilters.insert(msg.frameId(), true); FilterUtility::createCheckableFilterItem(msg.frameId(), false, ui->listFilter); } if (!idFilters[msg.frameId()]) return; messages.append(msg); rowNum = ui->tableIsoFrames->rowCount(); ui->tableIsoFrames->insertRow(rowNum); QTableWidgetItem *item = new QTableWidgetItem; item->setData(Qt::EditRole, Utility::formatTimestamp(msg.timeStamp().microSeconds())); //ui->tableIsoFrames->setItem(rowNum, 0, (double)msg.timestamp, Utility::formatTimestamp(msg.timestamp))); ui->tableIsoFrames->setItem(rowNum, 0, item); ui->tableIsoFrames->setItem(rowNum, 1, new QTableWidgetItem(QString::number(msg.frameId(), 16))); ui->tableIsoFrames->setItem(rowNum, 2, new QTableWidgetItem(QString::number(msg.bus))); if (msg.isReceived) ui->tableIsoFrames->setItem(rowNum, 3, new QTableWidgetItem("Rx")); else ui->tableIsoFrames->setItem(rowNum, 3, new QTableWidgetItem("Tx")); ui->tableIsoFrames->setItem(rowNum, 4, new QTableWidgetItem(QString::number(msg.payload().length()))); for (int i = 0; i < dataLen; i++) { // if last character is the newline if (i == dataLen -1 && data[i] == '\n') { continue; } tempString.append(QString(QChar(data[i]))); } ui->tableIsoFrames->setItem(rowNum, 5, new QTableWidgetItem(tempString)); // autoscroll to the newly added row if autoscroll is enabled if (ui->cbAutoscroll->isChecked()) { m_isAutoScrolling = true; ui->tableIsoFrames->scrollToItem(ui->tableIsoFrames->item(rowNum, 0), QAbstractItemView::PositionAtBottom); // scroll down one additional row height ui->tableIsoFrames->verticalScrollBar()->setValue(ui->tableIsoFrames->verticalScrollBar()->value() + ui->tableIsoFrames->rowHeight(rowNum)); m_isAutoScrolling = false; } }