From dcaaacf4fcf131cff1eaf8225f241570ab25b771 Mon Sep 17 00:00:00 2001 From: Guinea Wheek Date: Thu, 2 May 2024 13:34:18 -0700 Subject: [PATCH 01/14] add support for SIG_VALTYPE_ --- dbc/dbchandler.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ dbc/dbchandler.h | 1 + 2 files changed, 44 insertions(+) diff --git a/dbc/dbchandler.cpp b/dbc/dbchandler.cpp index acf3aa1c..f55118b0 100644 --- a/dbc/dbchandler.cpp +++ b/dbc/dbchandler.cpp @@ -674,6 +674,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; @@ -941,6 +979,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 3327c18d..14f19f8c 100644 --- a/dbc/dbchandler.h +++ b/dbc/dbchandler.h @@ -106,6 +106,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); }; From 080fc4d9bf171e8ebd3c1be71530913603a28eb0 Mon Sep 17 00:00:00 2001 From: Ben Evans Date: Fri, 10 May 2024 07:32:26 +1000 Subject: [PATCH 02/14] Add support for reading Wireshark SocketCAN log format --- README.md | 1 + framefileio.cpp | 131 +++++++++++++++++++++++++++++++++++++++++++----- framefileio.h | 2 + pcaplite.cpp | 21 ++++++-- pcaplite.h | 6 ++- 5 files changed, 144 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index cdcdbb59..8c7c66ce 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 diff --git a/framefileio.cpp b/framefileio.cpp index f7e86589..4649b1ae 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)) { @@ -5023,7 +5037,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; } @@ -5079,7 +5093,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; } @@ -5089,3 +5103,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/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 *); From 53c0dae40c007a6cc68fa9a099cc7c32749d1872 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Mon, 13 May 2024 19:30:58 -0400 Subject: [PATCH 03/14] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cdcdbb59..0574fef9 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,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: From 353e619808cc0ba5e57c6b8cc40c2840f5f82b28 Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Thu, 23 May 2024 10:55:19 -0600 Subject: [PATCH 04/14] make qmake check Qt version This provides a more helpful compile-time error message, hopefully preventing issues like #727 (https://github.com/collin80/SavvyCAN/issues/727). --- SavvyCAN.pro | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 5512e427..ceb0aaef 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -4,6 +4,10 @@ # #------------------------------------------------- +!versionAtLeast(QT_VERSION, 5.14.0) { + error("Current version of Qt ($${QT_VERSION}) is too old, this project requires Qt 5.14 or newer") +} + QT = core gui printsupport qml serialbus serialport widgets help network opengl CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT From 7d2759fdfbcdb1b9bc9fb687a7f6b8e8b063fea5 Mon Sep 17 00:00:00 2001 From: Ben Evans Date: Wed, 29 May 2024 19:24:58 +1000 Subject: [PATCH 05/14] Fix indentation issues. --- framefileio.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/framefileio.cpp b/framefileio.cpp index 4649b1ae..eded03bd 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -5113,15 +5113,15 @@ bool FrameFileIO::loadWiresharkSocketCANFile(QString filename, QVector int lineCounter = 0; bool foundErrors = false; pcap_pkthdr packetHeader; - const char *packetData = NULL; - char errbuf[PCAP_ERRBUF_SIZE]; + 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; - } + if (!pcap_data_file) { + return false; + } packetData = (const char*)pcap_next(pcap_data_file, &packetHeader); while (packetData) { @@ -5143,8 +5143,8 @@ bool FrameFileIO::loadWiresharkSocketCANFile(QString filename, QVector // ID and extended frame format const quint32 can_id = qFromBigEndian(packetData); if (can_id & 0x80000000) { - thisFrame.setExtendedFrameFormat(true); - thisFrame.setFrameId(0x1fffffff & can_id); + thisFrame.setExtendedFrameFormat(true); + thisFrame.setFrameId(0x1fffffff & can_id); } else { thisFrame.setExtendedFrameFormat(false); thisFrame.setFrameId(0x7ff & can_id); @@ -5178,7 +5178,7 @@ bool FrameFileIO::loadWiresharkSocketCANFile(QString filename, QVector packetData = (const char*) pcap_next(pcap_data_file, &packetHeader); } pcap_close(pcap_data_file); - pcap_data_file = NULL; + pcap_data_file = NULL; return !foundErrors; } @@ -5189,10 +5189,10 @@ bool FrameFileIO::isWiresharkSocketCANFile(QString filename) QByteArray ba = filename.toLocal8Bit(); pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_SOCKETCAN); - if (!pcap_data_file) { - return false; - } + if (!pcap_data_file) { + return false; + } pcap_close(pcap_data_file); - pcap_data_file = NULL; + pcap_data_file = NULL; return true; } From d433c8e7c5ec19f123fc6ca8cbb6114b6c1bd0d7 Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Tue, 18 Jun 2024 13:58:45 -0600 Subject: [PATCH 06/14] signal makePrettyOutput: add a space between value and unit Instead of rendering as "45Celsius" it now renders as "45 Celsius". --- dbc/dbc_classes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbc/dbc_classes.cpp b/dbc/dbc_classes.cpp index da44f804..9ea594a9 100644 --- a/dbc/dbc_classes.cpp +++ b/dbc/dbc_classes.cpp @@ -214,12 +214,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; } From 1b61ae6e2658a39217a9d820c53717fc7156d9aa Mon Sep 17 00:00:00 2001 From: Superwofy Date: Fri, 21 Jun 2024 12:55:26 +0100 Subject: [PATCH 07/14] Added/corrected timestamps for LAWICEL protocol. Fixed System Clock timestamps and added decoding for timestamps coming from the tool. --- connections/lawicel_serial.cpp | 21 ++++++++++++++++++++- connections/lawicel_serial.h | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/connections/lawicel_serial.cpp b/connections/lawicel_serial.cpp index 34b4fd4f..ddf9e4dd 100644 --- a/connections/lawicel_serial.cpp +++ b/connections/lawicel_serial.cpp @@ -491,7 +491,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; From bc391094f45461f1107188e9b43bd827798655b6 Mon Sep 17 00:00:00 2001 From: Chavdar Kirov Date: Thu, 4 Jul 2024 01:09:39 +0300 Subject: [PATCH 08/14] Support for ext CAN IDs in wireshark files --- framefileio.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framefileio.cpp b/framefileio.cpp index 5dc82d48..e64a7679 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -4882,9 +4882,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); From f769dd14004304b62c0baed094566cacfaa70a4e Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Tue, 9 Jul 2024 20:18:24 -0400 Subject: [PATCH 09/14] Fix timestamp in lawicel connection code --- connections/lawicel_serial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connections/lawicel_serial.cpp b/connections/lawicel_serial.cpp index 34b4fd4f..91e58f5b 100644 --- a/connections/lawicel_serial.cpp +++ b/connections/lawicel_serial.cpp @@ -491,7 +491,7 @@ void LAWICELSerial::readSerialData() { qDebug() << "Got CR!"; - buildFrame.setTimeStamp(QDateTime::currentMSecsSinceEpoch() * 1000l); + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, QDateTime::currentMSecsSinceEpoch() * 1000l)); switch (mBuildLine[0].toLatin1()) { case 't': //standard frame From 1f5b16bd9dda82d593685d20a3951c4f40cda1fc Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Tue, 9 Jul 2024 20:43:19 -0400 Subject: [PATCH 10/14] Rearrange new graph window so it fits on a 800 pixel tall screen --- ui/newgraphdialog.ui | 367 ++++++++++++++++++++++--------------------- 1 file changed, 189 insertions(+), 178 deletions(-) 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 From 6d5bc49cffa9785bbeeaf97081f014afc98f2b7d Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Tue, 13 Aug 2024 20:59:52 -0400 Subject: [PATCH 11/14] Add a save function to ISO-TP decoder window --- bus_protocols/uds_handler.cpp | 33 ++++++++++++--- bus_protocols/uds_handler.h | 1 + re/isotp_interpreterwindow.cpp | 75 ++++++++++++++++++++++++++++++++++ re/isotp_interpreterwindow.h | 1 + ui/isotp_interpreterwindow.ui | 7 ++++ 5 files changed, 111 insertions(+), 6 deletions(-) diff --git a/bus_protocols/uds_handler.cpp b/bus_protocols/uds_handler.cpp index 697f758b..f39b5bb6 100644 --- a/bus_protocols/uds_handler.cpp +++ b/bus_protocols/uds_handler.cpp @@ -182,11 +182,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().count(); + *result = true; UDS_MESSAGE udsMsg; udsMsg.bus = msg.bus; udsMsg.setExtendedFrameFormat(msg.hasExtendedFrameFormat()); @@ -208,9 +208,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 @@ -220,9 +228,22 @@ void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) udsMsg.payload().remove(0, 1); } } - else return; + 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); - emit newUDSMessage(udsMsg); + if (result) emit newUDSMessage(udsMsg); } void UDS_HANDLER::setFlowCtrl(bool state) 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/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 + + + From 2b8c3a3194605a2dd458df957c80bac8b2d4fb61 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Tue, 20 Aug 2024 21:42:34 -0400 Subject: [PATCH 12/14] Add an application icon to Windows build --- SavvyCAN.pro | 4 ++++ icons/SavvyIcon.ico | Bin 0 -> 59329 bytes 2 files changed, 4 insertions(+) create mode 100644 icons/SavvyIcon.ico diff --git a/SavvyCAN.pro b/SavvyCAN.pro index ceb0aaef..ef56a83a 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -261,6 +261,10 @@ unix { DISTFILES += SavvyCAN.desktop } +windows { +RC_ICONS=icons/SavvyIcon.ico +} + examplefiles.files=examples examplefiles.path = $$PREFIX/share/savvycan/examples INSTALLS += examplefiles diff --git a/icons/SavvyIcon.ico b/icons/SavvyIcon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c47683cdf7a5cbfb2920912c6b0b3dbf9d1bbead GIT binary patch literal 59329 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

!9_c?&`Y{(9-W3M}a(hn*{L4V~d8L!Bm1o;Z!Q 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

4-F2OfKbSjAkvcb7x{d1I41BU4Br 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 literal 0 HcmV?d00001 From d8c9e17146c6fd346579fec9a4114db7605ac36c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:16:44 +0000 Subject: [PATCH 13/14] Bump actions/download-artifact from 3 to 4.1.7 in /.github/workflows Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.1.7. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4.1.7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b163053..0009e875 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -126,7 +126,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 From 2836977e42c697b85c9d4d1d7f366cb79036f23c Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Wed, 4 Sep 2024 20:24:36 -0400 Subject: [PATCH 14/14] Update build.yml Need to also update actions/upload-artifact or download-artifact v4 won't work. --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0009e875..a2c7e6fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,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 @@ -62,7 +62,7 @@ jobs: cp -R help/* SavvyCAN.app/Contents/MacOS/help macdeployqt SavvyCAN.app -dmg - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: SavvyCAN-macOS_x64.dmg path: SavvyCAN.dmg @@ -113,7 +113,7 @@ jobs: mkdir package/canbus copy "${Env:Qt5_Dir}/plugins/canbus/*.*" package/canbus/ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: SavvyCAN-Windows_x64 path: package