From dcaaacf4fcf131cff1eaf8225f241570ab25b771 Mon Sep 17 00:00:00 2001 From: Guinea Wheek Date: Thu, 2 May 2024 13:34:18 -0700 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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);