diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0098be8f..6a48abc8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: chmod a+x linuxdeployqt-continuous-x86_64.AppImage ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/SavvyCAN.desktop -appimage -extra-plugins=iconengines,platformthemes/libqgtk3.so,canbus - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: SavvyCAN-Linux_x64.AppImage path: SavvyCAN-*x86_64.AppImage @@ -67,7 +67,7 @@ jobs: cp /usr/local/share/qt/plugins/canbus/* SavvyCAN.app/Frameworks macdeployqt SavvyCAN.app -dmg - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: SavvyCAN-macOS_x64.dmg path: SavvyCAN.dmg @@ -122,7 +122,7 @@ jobs: mkdir package/canbus copy "${Env:Qt6_Dir}/plugins/canbus/*.*" package/canbus/ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: SavvyCAN-Windows_x64 path: package @@ -135,7 +135,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4.1.7 - name: Display structure of downloaded files run: zip -r SavvyCAN-Windows_x64_CIBuild.zip SavvyCAN-Windows_x64 diff --git a/README.md b/README.md index cdcdbb59..e6f6aa1a 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ of this program. It can load and save in several formats: 9. Vehicle Spy log files 10. CANDump / Kayak (Read only) 11. PCAN Viewer (Read Only) +12. Wireshark socketcan PCAP file (Read only) ## Dependencies @@ -47,9 +48,9 @@ to download it separately. This project requires 5.14.0 or higher because of a dependency on QtSerialBus and other new additions to Qt. -NOTE: As the code in this master branch sits, it will not properly compile with QT6. -However, there is a QT6WIP branch that should successfully compile. The QT6WIP branch may not -function entirely properly yet. +NOTE: As the code in this master branch sits, it does compile with QT6. Support for QT6 is approximately "beta" quality. Most all functions should work, please send issues if found. + +It appears that the current binary build for MacOS requires at least MacOS 10.15 ## Instructions for compiling: diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 2020d543..597bc8ad 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -4,6 +4,10 @@ # #------------------------------------------------- +!versionAtLeast(QT_VERSION, 6.5.0) { + error("Current version of Qt ($${QT_VERSION}) is too old, this project requires Qt 6.5 or newer") +} + QT = core gui printsupport qml serialbus serialport widgets help network opengl dbus CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT @@ -257,6 +261,10 @@ unix { DISTFILES += SavvyCAN.desktop } +windows { +RC_ICONS=icons/SavvyIcon.ico +} + examplefiles.files=examples examplefiles.path = $$PREFIX/share/savvycan/examples INSTALLS += examplefiles @@ -270,4 +278,3 @@ helpfiles.path = $$PREFIX/bin/help INSTALLS += helpfiles INSTALLS += target - diff --git a/bus_protocols/uds_handler.cpp b/bus_protocols/uds_handler.cpp index 1b6febc7..2e1797f6 100644 --- a/bus_protocols/uds_handler.cpp +++ b/bus_protocols/uds_handler.cpp @@ -181,11 +181,11 @@ UDS_HANDLER::~UDS_HANDLER() delete isoHandler; } -void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) +UDS_MESSAGE UDS_HANDLER::tryISOtoUDS(ISOTP_MESSAGE msg, bool *result) { - qDebug() << "UDS handler got ISOTP frame"; const unsigned char *data = reinterpret_cast(msg.payload().constData()); int dataLen = msg.payload().length(); + *result = true; UDS_MESSAGE udsMsg; udsMsg.bus = msg.bus; udsMsg.setExtendedFrameFormat(msg.hasExtendedFrameFormat()); @@ -207,9 +207,17 @@ void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) { udsMsg.service = data[1]; if (dataLen > 2) udsMsg.subFunc = data[2]; - else return; + else + { + *result = false; + return udsMsg; + }; } - else return; + else + { + *result = false; + return udsMsg; + }; udsMsg.payload().remove(0, 2); } else @@ -218,8 +226,23 @@ void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) if (dataLen > 1) udsMsg.subFunc = data[1]; udsMsg.payload().remove(0, 1); } - emit newUDSMessage(udsMsg); } + else + { + *result = false; + }; + return udsMsg; +} + +void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) +{ + qDebug() << "UDS handler got ISOTP frame"; + UDS_MESSAGE udsMsg; + + bool result; + udsMsg = tryISOtoUDS(msg, &result); + + if (result) emit newUDSMessage(udsMsg); } void UDS_HANDLER::setFlowCtrl(bool state) @@ -486,7 +509,7 @@ QString UDS_HANDLER::getDetailedMessageAnalysis(const UDS_MESSAGE &msg) buildString.append(Utility::formatHexNum(data[i]) + " "); } break; - case UDS_SERVICES::WRITE_BY_ID: + case UDS_SERVICES::WRITE_BY_ID: if (dataLen > 3) { int writeID = (data[1] * 256 + data[2]); diff --git a/bus_protocols/uds_handler.h b/bus_protocols/uds_handler.h index 5c5ea4f3..b1578ed5 100644 --- a/bus_protocols/uds_handler.h +++ b/bus_protocols/uds_handler.h @@ -105,6 +105,7 @@ class UDS_HANDLER : public QObject QString getShortDesc(QVector &codeVector, int code); QString getLongDesc(QVector &codeVector, int code); QString getDetailedMessageAnalysis(const UDS_MESSAGE &msg); + UDS_MESSAGE tryISOtoUDS(ISOTP_MESSAGE msg, bool *result); public slots: void gotISOTPFrame(ISOTP_MESSAGE msg); diff --git a/connections/lawicel_serial.cpp b/connections/lawicel_serial.cpp index af3d164b..6279f0c1 100644 --- a/connections/lawicel_serial.cpp +++ b/connections/lawicel_serial.cpp @@ -482,7 +482,26 @@ void LAWICELSerial::readSerialData() { qDebug() << "Got CR!"; - buildFrame.setTimeStamp(QDateTime::currentMSecsSinceEpoch() * 1000l); + if (useSystemTime) + { + buildTimestamp = QDateTime::currentMSecsSinceEpoch() * 1000l; + } + else + { + //If total length is greater than command, header and data, timestamps must be enabled. + if (data.length() > (5 + mBuildLine.mid(4, 1).toInt() * 2 + 1)) + { + //Four bytes after the end of the data bytes. + buildTimestamp = mBuildLine.mid(5 + mBuildLine.mid(4, 1).toInt() * 2, 4).toInt(nullptr, 16) * 1000l; + } + else + { + //Default to system time if timestamps are disabled. + buildTimestamp = QDateTime::currentMSecsSinceEpoch() * 1000l; + } + } + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, buildTimestamp)); + switch (mBuildLine[0].toLatin1()) { case 't': //standard frame diff --git a/connections/lawicel_serial.h b/connections/lawicel_serial.h index 3d32a886..cfee96db 100644 --- a/connections/lawicel_serial.h +++ b/connections/lawicel_serial.h @@ -61,6 +61,7 @@ private slots: QSerialPort *serial; int framesRapid; CANFrame buildFrame; + qint64 buildTimestamp; bool can0Enabled; bool can0ListenOnly; bool canFd; diff --git a/dbc/dbc_classes.cpp b/dbc/dbc_classes.cpp index 76ae78e5..f271bf42 100644 --- a/dbc/dbc_classes.cpp +++ b/dbc/dbc_classes.cpp @@ -218,12 +218,12 @@ QString DBC_SIGNAL::makePrettyOutput(double floatVal, int64_t intVal, bool outpu } } if (!foundVal) outputString += QString::number(intVal); - if (outputUnit) outputString += unitName; + if (outputUnit) outputString += " " + unitName; } else //otherwise display the actual number and unit (if it exists) { outputString += (isInteger ? QString::number(intVal) : QString::number(floatVal)); - if (outputUnit) outputString += unitName; + if (outputUnit) outputString += " " + unitName; } return outputString; } diff --git a/dbc/dbchandler.cpp b/dbc/dbchandler.cpp index 404c1d94..2414b6cd 100644 --- a/dbc/dbchandler.cpp +++ b/dbc/dbchandler.cpp @@ -624,6 +624,44 @@ bool DBCFile::parseSignalMultiplexValueLine(QString line) return false; } +bool DBCFile::parseSignalValueTypeLine(QString line) +{ + QRegularExpression regex; + QRegularExpressionMatch match; + qDebug() << "Found a signal valtype line"; + regex.setPattern("^SIG\\_VALTYPE\\_ *(\\d+) *([-\\w]+) *: *(\\d+);"); + match = regex.match(line); + + // captured 1 is the message id + // captured 2 is the signal name + // captured 3 is the valtype + if (!match.hasMatch()) { return false; } + uint32_t id = match.captured(1).toULong() & 0x1FFFFFFFUL; + + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toULong() & 0x1FFFFFFFUL); + if (msg == nullptr) { return false; } + + DBC_SIGNAL *thisSignal = msg->sigHandler->findSignalByName(match.captured(2)); + if (thisSignal == nullptr) { return false; } + int valType = match.captured(3).toInt(); + + switch (valType) { + case 1: { + thisSignal->valType = SP_FLOAT; + break; + } + case 2: { + thisSignal->valType = DP_FLOAT; + break; + } + default: { + return false; + } + } + return true; +} + + bool DBCFile::parseValueLine(QString line) { QRegularExpression regex; @@ -891,6 +929,11 @@ bool DBCFile::loadFile(QString fileName) if (!parseSignalMultiplexValueLine(line)) numSigFaults++; } + if (line.startsWith("SIG_VALTYPE_ ")) //defines a signal value type + { + if (!parseSignalValueTypeLine(line)) numSigFaults++; + } + if (line.startsWith("BU_:")) //line specifies the nodes on this canbus { qDebug() << "Found a BU line"; diff --git a/dbc/dbchandler.h b/dbc/dbchandler.h index a51b0d05..32c6f517 100644 --- a/dbc/dbchandler.h +++ b/dbc/dbchandler.h @@ -100,6 +100,7 @@ class DBCFile: public QObject bool parseSignalMultiplexValueLine(QString line); DBC_MESSAGE* parseMessageLine(QString line); bool parseValueLine(QString line); + bool parseSignalValueTypeLine(QString line); bool parseAttributeLine(QString line); bool parseDefaultAttrLine(QString line); }; diff --git a/framefileio.cpp b/framefileio.cpp index 3f443a98..8c5b20c7 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -191,6 +191,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache filters.append(QString(tr("CLX000 (*.txt *.TXT)"))); filters.append(QString(tr("CANServer Binary Log (*.log *.LOG)"))); filters.append(QString(tr("Wireshark (*.pcap *.PCAP *.pcapng *.PCAPNG)"))); + filters.append(QString(tr("Wireshark SocketCAN (*.pcap *.PCAP"))); dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString()); dialog.setFileMode(QFileDialog::ExistingFile); @@ -237,6 +238,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache if (selectedNameFilter == filters[22]) result = loadCLX000File(filename, frameCache); if (selectedNameFilter == filters[23]) result = loadCANServerFile(filename, frameCache); if (selectedNameFilter == filters[24]) result = loadWiresharkFile(filename, frameCache); + if (selectedNameFilter == filters[25]) result = loadWiresharkSocketCANFile(filename, frameCache); progress.cancel(); @@ -288,6 +290,28 @@ bool FrameFileIO::autoDetectLoadFile(QString filename, QVector* frames } } + // Attempt to load socket CAN first to avoid generic wireshark logic catching it + qDebug() << "Attempting Wireshark Socket CAN Log"; + if (isWiresharkSocketCANFile(filename)) + { + if (loadWiresharkSocketCANFile(filename, frames)) + { + qDebug() << "Loaded as Wireshark SocketCAN Log successfully!"; + return true; + } + } + + // This and the decoder above were both moved above TeslaAPFile as they match based on magic numbers and sometimes these files were falling into the TeslaAP decoder + qDebug() << "Attempting Wireshark Log"; + if (isWiresharkFile(filename)) + { + if (loadWiresharkFile(filename, frames)) + { + qDebug() << "Loaded as Wireshark Log successfully!"; + return true; + } + } + qDebug() << "Attempting Tesla AP Snapshot"; if (isTeslaAPFile(filename)) { @@ -308,16 +332,6 @@ bool FrameFileIO::autoDetectLoadFile(QString filename, QVector* frames } } - qDebug() << "Attempting Wireshark Log"; - if (isWiresharkFile(filename)) - { - if (loadWiresharkFile(filename, frames)) - { - qDebug() << "Loaded as Wireshark Log successfully!"; - return true; - } - } - qDebug() << "Attempting canalyzer ASC"; if (isCanalyzerASC(filename)) { @@ -5021,7 +5035,7 @@ bool FrameFileIO::loadWiresharkFile(QString filename, QVector* frames) QByteArray ba = filename.toLocal8Bit(); - pcap_data_file = pcap_open_offline(ba.data(), errbuf); + pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_ANY); if (!pcap_data_file) { return false; } @@ -5048,9 +5062,13 @@ bool FrameFileIO::loadWiresharkFile(QString filename, QVector* frames) thisFrame.isReceived = true; // TODO: check if tx detection is possible thisFrame.setFrameType(QCanBusFrame::DataFrame); - thisFrame.setFrameId((0xff & *(packetData+17)) << 8 | (0xff & *(packetData+16))); - if (thisFrame.frameId() <= 0x7FF) thisFrame.setExtendedFrameFormat(false); - else thisFrame.setExtendedFrameFormat(true); + if ((0x80 & *(packetData+19))) { + thisFrame.setExtendedFrameFormat(true); + thisFrame.setFrameId((0x3f & *(packetData+19))<<24 | (0xff & *(packetData+18)) << 16 | (0xff & *(packetData+17)) << 8 | (0xff & *(packetData+16))); + } else { + thisFrame.setExtendedFrameFormat(false); + thisFrame.setFrameId((0xff & *(packetData+17)) << 8 | (0xff & *(packetData+16))); + } thisFrame.bus = 0; int numBytes = *(packetData+20); QByteArray bytes(numBytes, 0); @@ -5077,7 +5095,7 @@ bool FrameFileIO::isWiresharkFile(QString filename) char errbuf[PCAP_ERRBUF_SIZE]; QByteArray ba = filename.toLocal8Bit(); - pcap_data_file = pcap_open_offline(ba.data(), errbuf); + pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_ANY); if (!pcap_data_file) { return false; } @@ -5087,3 +5105,96 @@ bool FrameFileIO::isWiresharkFile(QString filename) return true; } + +bool FrameFileIO::loadWiresharkSocketCANFile(QString filename, QVector* frames) +{ + pcap_t *pcap_data_file; + CANFrame thisFrame; + long long startTimestamp = 0; + long long timeStamp; + int lineCounter = 0; + bool foundErrors = false; + pcap_pkthdr packetHeader; + const char *packetData = NULL; + char errbuf[PCAP_ERRBUF_SIZE]; + + QByteArray ba = filename.toLocal8Bit(); + + pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_SOCKETCAN); + if (!pcap_data_file) { + return false; + } + + packetData = (const char*)pcap_next(pcap_data_file, &packetHeader); + while (packetData) { + lineCounter++; + if (lineCounter > 100) { + qApp->processEvents(); + lineCounter = 0; + } + thisFrame.bus = 0; + + // Timestamp + timeStamp = packetHeader.ts.tv_sec * 1000000 + packetHeader.ts.tv_usec; + if (0 == startTimestamp) { + startTimestamp = timeStamp; + } + timeStamp -= startTimestamp; + thisFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, timeStamp)); + + // ID and extended frame format + const quint32 can_id = qFromBigEndian(packetData); + if (can_id & 0x80000000) { + thisFrame.setExtendedFrameFormat(true); + thisFrame.setFrameId(0x1fffffff & can_id); + } else { + thisFrame.setExtendedFrameFormat(false); + thisFrame.setFrameId(0x7ff & can_id); + } + + // Frame type + if (can_id & 0x20000000U) { + thisFrame.setFrameType(QCanBusFrame::ErrorFrame); + } else if (can_id & 0x40000000U) { + thisFrame.setFrameType(QCanBusFrame::RemoteRequestFrame); + } else { + thisFrame.setFrameType(QCanBusFrame::DataFrame); + } + + // Direction - This isn't actually officially supported, but CAN Bus Debugger device logs set this byte to 1 to indicate a TX frame and 0 for RX + quint8 direction = (quint8) *(packetData + 6); + thisFrame.isReceived = (direction != 1); + + // Data + quint8 numBytes = (quint8) *(packetData + 4); + if (numBytes > 8) { + numBytes = 8; + } + QByteArray bytes(numBytes, 0); + for (int d = 0; d < numBytes; d++) { + bytes[d] = *(packetData + 8 + d); + } + thisFrame.setPayload(bytes); + frames->append(thisFrame); + + packetData = (const char*) pcap_next(pcap_data_file, &packetHeader); + } + pcap_close(pcap_data_file); + pcap_data_file = NULL; + return !foundErrors; +} + +bool FrameFileIO::isWiresharkSocketCANFile(QString filename) +{ + pcap_t *pcap_data_file; + char errbuf[PCAP_ERRBUF_SIZE]; + QByteArray ba = filename.toLocal8Bit(); + + pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_SOCKETCAN); + if (!pcap_data_file) { + return false; + } + pcap_close(pcap_data_file); + pcap_data_file = NULL; + return true; +} diff --git a/framefileio.h b/framefileio.h index 3d525174..71715917 100644 --- a/framefileio.h +++ b/framefileio.h @@ -54,6 +54,7 @@ class FrameFileIO: public QObject static bool loadCLX000File(QString filename, QVector* frames); static bool loadCANServerFile(QString filename, QVector* frames); static bool loadWiresharkFile(QString filename, QVector* frames); + static bool loadWiresharkSocketCANFile(QString filename, QVector* frames); //functions that pre-scan a file to try to figure out if they could read it. Used to automatically determine //file type and load it. @@ -80,6 +81,7 @@ class FrameFileIO: public QObject static bool isCLX000File(QString filename); static bool isCANServerFile(QString filename); static bool isWiresharkFile(QString filename); + static bool isWiresharkSocketCANFile(QString filename); static bool saveCRTDFile(QString, const QVector*); static bool saveNativeCSVFile(QString, const QVector*); diff --git a/icons/SavvyIcon.ico b/icons/SavvyIcon.ico new file mode 100644 index 00000000..c47683cd Binary files /dev/null and b/icons/SavvyIcon.ico differ diff --git a/pcaplite.cpp b/pcaplite.cpp index 189b2ce7..a0f6c902 100644 --- a/pcaplite.cpp +++ b/pcaplite.cpp @@ -19,7 +19,7 @@ static unsigned char pcap_buffer[MAX_CAN_PACKET_SIZE]; static pcap_t p; -pcap *pcap_open_offline(const char *filename, char *error_text) { +pcap *pcap_open_offline(const char *filename, char *error_text, int expected_link_type) { FILE *file; snprintf(error_text, PCAP_ERRBUF_SIZE, "OK"); @@ -48,6 +48,23 @@ pcap *pcap_open_offline(const char *filename, char *error_text) { return (NULL); } + unsigned int link_type; + fseek(file, PCAP_FILE_HEADER_LENGTH - 4, SEEK_SET); + bytes_read = fread(&link_type, 1, sizeof(link_type), file); + if (bytes_read != sizeof(link_type)) { + snprintf(error_text, PCAP_ERRBUF_SIZE, "Cannot read linktype word"); + fclose(file); + return (NULL); + } + if (expected_link_type >= 0) { + // Check the link type + if ((int) link_type != expected_link_type) { + snprintf(error_text, PCAP_ERRBUF_SIZE, "This link type is not supported by this decoder"); + fclose(file); + return (NULL); + } + } + // set the format // and seek past file header if (MAGIC_NG == magic) { @@ -61,14 +78,12 @@ pcap *pcap_open_offline(const char *filename, char *error_text) { return (NULL); } fseek(file, section_length, SEEK_SET); - } else { p.is_ng = 0; fseek(file, PCAP_FILE_HEADER_LENGTH, SEEK_SET); } p.file = file; - return(&p); } diff --git a/pcaplite.h b/pcaplite.h index 8a481530..02fbb77b 100644 --- a/pcaplite.h +++ b/pcaplite.h @@ -8,7 +8,9 @@ #include #endif -#define PCAP_ERRBUF_SIZE 256 +#define PCAP_ERRBUF_SIZE (256) +#define PCAP_LINKTYPE_SOCKETCAN (227) +#define PCAP_LINKTYPE_ANY (-1) struct pcap_pkthdr { struct timeval ts; /* time stamp */ @@ -23,7 +25,7 @@ struct pcap { typedef struct pcap pcap_t; -pcap *pcap_open_offline(const char *, char *); +pcap *pcap_open_offline(const char *, char *, int); const unsigned char *pcap_next(pcap_t *, struct pcap_pkthdr *); diff --git a/re/isotp_interpreterwindow.cpp b/re/isotp_interpreterwindow.cpp index 5f8bb10b..045d6080 100644 --- a/re/isotp_interpreterwindow.cpp +++ b/re/isotp_interpreterwindow.cpp @@ -31,6 +31,7 @@ ISOTP_InterpreterWindow::ISOTP_InterpreterWindow(const QVector *frames 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))); QStringList headers; @@ -172,6 +173,80 @@ void ISOTP_InterpreterWindow::clearList() //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); diff --git a/re/isotp_interpreterwindow.h b/re/isotp_interpreterwindow.h index 14167ded..3b3a6b21 100644 --- a/re/isotp_interpreterwindow.h +++ b/re/isotp_interpreterwindow.h @@ -26,6 +26,7 @@ private slots: void showDetailView(); void updatedFrames(int); void clearList(); + void saveList(); void listFilterItemChanged(QListWidgetItem *item); void filterAll(); void filterNone(); diff --git a/ui/isotp_interpreterwindow.ui b/ui/isotp_interpreterwindow.ui index ec1ed3ba..6f7f67db 100644 --- a/ui/isotp_interpreterwindow.ui +++ b/ui/isotp_interpreterwindow.ui @@ -40,6 +40,13 @@ + + + + Save List + + + diff --git a/ui/newgraphdialog.ui b/ui/newgraphdialog.ui index 9b2f036e..46d7684c 100644 --- a/ui/newgraphdialog.ui +++ b/ui/newgraphdialog.ui @@ -7,7 +7,7 @@ 0 0 681 - 828 + 797 @@ -15,7 +15,7 @@ - + @@ -49,57 +49,183 @@ - 300 - 300 + 280 + 280 - 300 - 300 + 280 + 280 - + + + + + + Qt::Vertical + + + + + + + + + + 0 + 0 + + + + Graph a DBC Signal: + + + Qt::AlignCenter + + + + + + + Node: + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + Message: + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + Signal: + + + Qt::AlignCenter + + + + + + + + + + Copy Signal Parameters + + + + + + + + 12 + true + + + + + + + + + + + Qt::Vertical + + + + 20 + 30 + + + + + + + + + + + + Qt::Horizontal + + + + + + + + - Data Len: + Bit Len: - + - + Little Endian - + (LSB First) - + Signed: - + - + QFrame::NoFrame @@ -109,71 +235,96 @@ - + - + Bias: - + - + Scale - + - + Stride - + - + + + + Associated Bus + + + + + + + -1 + + + + + + + + + Qt::Vertical + + + + + + Only Points - + - + Point Style - + - + Line Thickness - + 15 @@ -183,14 +334,14 @@ - + Line Color - + true @@ -203,14 +354,14 @@ - + Fill Color - + true @@ -223,148 +374,17 @@ - - - - Add this graph - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Associated Bus - - - - - - - -1 - - - - - - - - - - - - 0 - 0 - - - - Graph a DBC Signal: - - - Qt::AlignCenter - - - - - - - Node: - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - Message: - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - Signal: - - - Qt::AlignCenter - - - - - - - - - - Copy Signal Parameters - - - - - - - - 12 - true - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - + + + + Add this graph + + + @@ -378,15 +398,6 @@ txtName txtID - txtDataLen - cbIntel - cbSigned - txtMask - txtBias - txtScale - txtStride - colorSwatch - btnAddGraph cbMessages cbSignals btnCopySignal