Files
giesCANne/re/isotp_interpreterwindow.cpp

399 lines
13 KiB
C++

#include "isotp_interpreterwindow.h"
#include "ui_isotp_interpreterwindow.h"
#include "mainwindow.h"
#include "helpwindow.h"
#include "filterutility.h"
ISOTP_InterpreterWindow::ISOTP_InterpreterWindow(const QVector<CANFrame> *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<QKeyEvent *>(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<const unsigned char *>(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<const unsigned char *>(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<const unsigned char *>(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;
}
}