diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 8d64e4d..acc556d 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -46,7 +46,9 @@ SOURCES += main.cpp\ dbcloadsavewindow.cpp \ fuzzingwindow.cpp \ udsscanwindow.cpp \ - can_structs.cpp + can_structs.cpp \ + isotp_interpreterwindow.cpp \ + isotp_decoder.cpp HEADERS += mainwindow.h \ can_structs.h \ @@ -79,7 +81,9 @@ HEADERS += mainwindow.h \ rangestatewindow.h \ dbcloadsavewindow.h \ fuzzingwindow.h \ - udsscanwindow.h + udsscanwindow.h \ + isotp_interpreterwindow.h \ + isotp_decoder.h FORMS += mainwindow.ui \ graphingwindow.ui \ @@ -100,7 +104,8 @@ FORMS += mainwindow.ui \ rangestatewindow.ui \ dbcloadsavewindow.ui \ fuzzingwindow.ui \ - udsscanwindow.ui + udsscanwindow.ui \ + isotp_interpreterwindow.ui DISTFILES += diff --git a/can_structs.h b/can_structs.h index e9668bd..7a4fc6e 100644 --- a/can_structs.h +++ b/can_structs.h @@ -29,7 +29,7 @@ public: }; //the same as the CANFrame struct but with arbitrary data size. -struct ISOTP_FRAME +struct ISOTP_MESSAGE { public: int ID; diff --git a/isotp_decoder.cpp b/isotp_decoder.cpp new file mode 100644 index 0000000..93e1ec3 --- /dev/null +++ b/isotp_decoder.cpp @@ -0,0 +1,160 @@ +#include "isotp_decoder.h" + +ISOTP_DECODER::ISOTP_DECODER(const QVector *frames, QObject *parent) + : QObject(parent) +{ + modelFrames = frames; + useExtendedAddressing = false; +} + +void ISOTP_DECODER::setExtendedAddressing(bool mode) +{ + useExtendedAddressing = mode; +} + +//remember, negative numbers are special -1 = all frames deleted, -2 = totally new set of frames. +void ISOTP_DECODER::updatedFrames(int numFrames) +{ + if (numFrames == -1) //all frames deleted. Kill the display + { + } + else if (numFrames == -2) //all new set of frames. Reset + { + for (int i = 0; i < modelFrames->length(); i++) processFrame(modelFrames->at(i)); + } + else //just got some new frames. See if they are relevant. + { + for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + { + processFrame(modelFrames->at(i)); + } + } +} + +void ISOTP_DECODER::processFrame(const CANFrame &frame) +{ + uint64_t ID = frame.ID; + int frameType; + int frameLen; + int ln; + int offset; + ISOTP_MESSAGE msg; + ISOTP_MESSAGE *pMsg; + + frameType = 0; + frameLen = 0; + + if (useExtendedAddressing) + { + ID = ID << 8; + ID += frame.data[0]; + frameType = frame.data[1] >> 4; + frameLen = frame.data[1] & 0xF; + } + else + { + frameType = frame.data[0] >> 4; + frameLen = frame.data[0] & 0xF; + } + + switch(frameType) + { + case 0: //single frame message + checkNeedFlush(ID); + + if (frameLen == 0) return; //length of zero isn't valid. + if (frameLen > 6 && useExtendedAddressing) return; //impossible + if (frameLen > 7) return; + + msg.bus = frame.bus; + msg.extended = frame.extended; + msg.ID = ID; + msg.isReceived = frame.isReceived; + msg.len = frameLen; + msg.data.reserve(frameLen); + msg.timestamp = frame.timestamp; + if (useExtendedAddressing) for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+2]); + else for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+1]); + qDebug() << "Emitting single frame ISOTP message"; + emit newISOMessage(msg); + break; + case 1: //first frame of a multi-frame message + checkNeedFlush(ID); + msg.bus = frame.bus; + msg.extended = frame.extended; + msg.ID = ID; + msg.timestamp = frame.timestamp; + msg.isReceived = frame.isReceived; + frameLen = frameLen << 8; + if (useExtendedAddressing) + { + frameLen += frame.data[2]; + frameLen = frameLen & 0xFFF; + msg.len = frameLen; + msg.data.reserve(frameLen); + for (int j = 0; j < 5; j++) msg.data.append(frame.data[3 + j]); + } + else + { + frameLen += frame.data[1]; + frameLen = frameLen & 0xFFF; + msg.len = frameLen; + msg.data.reserve(frameLen); + for (int j = 0; j < 6; j++) msg.data.append(frame.data[2 + j]); + } + messageBuffer.append(msg); + break; + case 2: //subsequent frames for multi-frame messages + pMsg = NULL; + for (int i = 0; i < messageBuffer.length(); i++) + { + if (messageBuffer[i].ID == ID) + { + pMsg = &messageBuffer[i]; + break; + } + } + if (!pMsg) return; + ln = pMsg->len - pMsg->data.count(); + offset = pMsg->data.count(); + if (useExtendedAddressing) + { + if (ln > 6) ln = 6; + for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+2]); + } + else + { + if (ln > 7) ln = 7; + for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+1]); + } + if (pMsg->len <= pMsg->data.count()) + { + qDebug() << "Emitting multiframe ISOTP message"; + emit newISOMessage(*pMsg); + } + break; + case 3: //flow control messages -ignored for now + break; + } +} + +void ISOTP_DECODER::checkNeedFlush(uint64_t ID) +{ + for (int i = 0; i < messageBuffer.length(); i++) + { + if (messageBuffer[i].ID == ID) + { + //warning... this code will work for direct signals as emit turns into a function call + //and thus the other side will have time to do its processing before control returns + //and we delete the message on our side. But, if this code were used cross thread the + //emit would be a queued message instead and control would immediately return + //here and then the message would be deleted before being delivered. + //To fix that the message would have to be passed by value instead which I'd like to avoid. + //Thus, don't use this across threads unless you like to debug strange issues. + qDebug() << "Flushing a partial frame"; + emit newISOMessage(messageBuffer[i]); + messageBuffer.removeAt(i); + return; + } + } +} diff --git a/isotp_decoder.h b/isotp_decoder.h new file mode 100644 index 0000000..51d130c --- /dev/null +++ b/isotp_decoder.h @@ -0,0 +1,31 @@ +#ifndef ISOTP_DECODER_H +#define ISOTP_DECODER_H + +#include +#include +#include "can_structs.h" + +class ISOTP_DECODER : public QObject +{ + Q_OBJECT + +public: + explicit ISOTP_DECODER(const QVector *frames, QObject *parent = 0); + void setExtendedAddressing(bool mode); + +public slots: + void updatedFrames(int); + +signals: + void newISOMessage(ISOTP_MESSAGE &msg); + +private: + QList messageBuffer; + const QVector *modelFrames; + bool useExtendedAddressing; + + void processFrame(const CANFrame &frame); + void checkNeedFlush(uint64_t ID); +}; + +#endif // ISOTP_DECODER_H diff --git a/isotp_interpreterwindow.cpp b/isotp_interpreterwindow.cpp new file mode 100644 index 0000000..14a4cd8 --- /dev/null +++ b/isotp_interpreterwindow.cpp @@ -0,0 +1,97 @@ +#include "isotp_interpreterwindow.h" +#include "ui_isotp_interpreterwindow.h" +#include "mainwindow.h" + +ISOTP_InterpreterWindow::ISOTP_InterpreterWindow(const QVector *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::ISOTP_InterpreterWindow) +{ + ui->setupUi(this); + modelFrames = frames; + + decoder = new ISOTP_DECODER(modelFrames); + + connect(MainWindow::getReference(), &MainWindow::framesUpdated, decoder, &ISOTP_DECODER::updatedFrames); + connect(decoder, &ISOTP_DECODER::newISOMessage, this, &ISOTP_InterpreterWindow::newISOMessage); + + 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); +} + +ISOTP_InterpreterWindow::~ISOTP_InterpreterWindow() +{ + delete decoder; + delete ui; +} + +void ISOTP_InterpreterWindow::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + readSettings(); + decoder->updatedFrames(-2); +} + +void ISOTP_InterpreterWindow::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event); + writeSettings(); +} + +void ISOTP_InterpreterWindow::readSettings() +{ + QSettings settings; + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + resize(settings.value("ISODecodeWindow/WindowSize", this->size()).toSize()); + move(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()); + } +} + +void ISOTP_InterpreterWindow::newISOMessage(ISOTP_MESSAGE &msg) +{ + int rowNum; + QString tempString; + + messages.append(msg); + + rowNum = ui->tableIsoFrames->rowCount(); + ui->tableIsoFrames->insertRow(rowNum); + + ui->tableIsoFrames->setItem(rowNum, 0, new QTableWidgetItem(QString::number(msg.timestamp))); + ui->tableIsoFrames->setItem(rowNum, 1, new QTableWidgetItem(QString::number(msg.ID, 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.len))); + + for (int i = 0; i < msg.data.length(); i++) + { + tempString.append(Utility::formatNumber(msg.data[i])); + tempString.append(" "); + } + ui->tableIsoFrames->setItem(rowNum, 5, new QTableWidgetItem(tempString)); + +} + + diff --git a/isotp_interpreterwindow.h b/isotp_interpreterwindow.h new file mode 100644 index 0000000..a8992ba --- /dev/null +++ b/isotp_interpreterwindow.h @@ -0,0 +1,36 @@ +#ifndef ISOTP_INTERPRETERWINDOW_H +#define ISOTP_INTERPRETERWINDOW_H + +#include +#include "can_structs.h" +#include "isotp_decoder.h" + +namespace Ui { +class ISOTP_InterpreterWindow; +} + +class ISOTP_InterpreterWindow : public QDialog +{ + Q_OBJECT + +public: + explicit ISOTP_InterpreterWindow(const QVector *frames, QWidget *parent = 0); + ~ISOTP_InterpreterWindow(); + void showEvent(QShowEvent*); + +private slots: + void newISOMessage(ISOTP_MESSAGE &msg); + +private: + Ui::ISOTP_InterpreterWindow *ui; + ISOTP_DECODER *decoder; + + const QVector *modelFrames; + QVector messages; + + void closeEvent(QCloseEvent *event); + void readSettings(); + void writeSettings(); +}; + +#endif // ISOTP_INTERPRETERWINDOW_H diff --git a/isotp_interpreterwindow.ui b/isotp_interpreterwindow.ui new file mode 100644 index 0000000..eb92d78 --- /dev/null +++ b/isotp_interpreterwindow.ui @@ -0,0 +1,48 @@ + + + ISOTP_InterpreterWindow + + + + 0 + 0 + 792 + 571 + + + + ISO-TP Decoder + + + + + + Interpreted Messages Overview + + + + + + + + + + Data Details of Selected Message + + + + + + + + + + Interpret Data as UDS + + + + + + + + diff --git a/mainwindow.cpp b/mainwindow.cpp index 1782687..5426539 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -98,6 +98,7 @@ MainWindow::MainWindow(QWidget *parent) : dbcFileWindow = NULL; fuzzingWindow = NULL; udsScanWindow = NULL; + isoWindow = NULL; dbcHandler = new DBCHandler; bDirty = false; inhibitFilterUpdate = false; @@ -140,6 +141,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->actionDBC_File_Manager, &QAction::triggered, this, &MainWindow::showDBCFileWindow); connect(ui->actionFuzzing, &QAction::triggered, this, &MainWindow::showFuzzingWindow); connect(ui->actionUDS_Scanner, &QAction::triggered, this, &MainWindow::showUDSScanWindow); + connect(ui->actionISO_TP_Decoder, &QAction::triggered, this, &MainWindow::showISOInterpreterWindow); lbStatusConnected.setText(tr("Not connected")); updateFileStatus(); @@ -274,6 +276,12 @@ MainWindow::~MainWindow() delete udsScanWindow; } + if (isoWindow) + { + isoWindow->close(); + delete isoWindow; + } + delete elapsedTime; delete ui; @@ -299,6 +307,7 @@ void MainWindow::exitApp() if (dbcFileWindow) dbcFileWindow->close(); if (fuzzingWindow) fuzzingWindow->close(); if (udsScanWindow) udsScanWindow->close(); + if (isoWindow) isoWindow->close(); this->close(); } @@ -884,6 +893,18 @@ void MainWindow::showFrameDataAnalysis() frameInfoWindow->show(); } +void MainWindow::showISOInterpreterWindow() +{ + if (!isoWindow) + { + if (!useFiltered) + isoWindow = new ISOTP_InterpreterWindow(model->getListReference()); + else + isoWindow = new ISOTP_InterpreterWindow(model->getFilteredListReference()); + } + isoWindow->show(); +} + void MainWindow::showFrameSenderWindow() { if (!frameSenderWindow) diff --git a/mainwindow.h b/mainwindow.h index bbe6416..fc8115f 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -25,6 +25,7 @@ #include "dbcloadsavewindow.h" #include "fuzzingwindow.h" #include "udsscanwindow.h" +#include "isotp_interpreterwindow.h" namespace Ui { class MainWindow; @@ -65,6 +66,7 @@ private slots: void showDBCFileWindow(); void showFuzzingWindow(); void showUDSScanWindow(); + void showISOInterpreterWindow(); void exitApp(); void handleSaveDecoded(); void changeBaudRates(); @@ -141,6 +143,7 @@ private: DBCLoadSaveWindow *dbcFileWindow; FuzzingWindow *fuzzingWindow; UDSScanWindow *udsScanWindow; + ISOTP_InterpreterWindow *isoWindow; //various private storage QLabel lbStatusConnected; diff --git a/mainwindow.ui b/mainwindow.ui index 4e7a167..fafd407 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -210,6 +210,7 @@ + @@ -387,6 +388,11 @@ UDS Scanner + + + ISO-TP Decoder + + diff --git a/udsscanwindow.ui b/udsscanwindow.ui index f0e1c2a..0c157bf 100644 --- a/udsscanwindow.ui +++ b/udsscanwindow.ui @@ -11,7 +11,7 @@ - Dialog + UDS Scanner