Merge branch 'master' of github.com:collin80/SavvyCAN

This commit is contained in:
Collin Kidder
2024-07-09 20:20:54 -04:00
11 changed files with 225 additions and 26 deletions
+4 -3
View File
@@ -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:
+4
View File
@@ -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
+20 -1
View File
@@ -491,7 +491,26 @@ void LAWICELSerial::readSerialData()
{
qDebug() << "Got CR!";
buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, 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
+1
View File
@@ -61,6 +61,7 @@ protected:
QSerialPort *serial;
int framesRapid;
CANFrame buildFrame;
qint64 buildTimestamp;
bool can0Enabled;
bool can0ListenOnly;
bool canFd;
+2 -2
View File
@@ -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;
}
+43
View File
@@ -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";
+1
View File
@@ -106,6 +106,7 @@ private:
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);
};
+126 -15
View File
@@ -191,6 +191,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector<CANFrame>* 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<CANFrame>* 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<CANFrame>* 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<CANFrame>* 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<CANFrame>* 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;
}
@@ -5050,9 +5064,13 @@ bool FrameFileIO::loadWiresharkFile(QString filename, QVector<CANFrame>* 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);
@@ -5079,7 +5097,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 +5107,96 @@ bool FrameFileIO::isWiresharkFile(QString filename)
return true;
}
bool FrameFileIO::loadWiresharkSocketCANFile(QString filename, QVector<CANFrame>* 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<quint32>(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;
}
+2
View File
@@ -54,6 +54,7 @@ public:
static bool loadCLX000File(QString filename, QVector<CANFrame>* frames);
static bool loadCANServerFile(QString filename, QVector<CANFrame>* frames);
static bool loadWiresharkFile(QString filename, QVector<CANFrame>* frames);
static bool loadWiresharkSocketCANFile(QString filename, QVector<CANFrame>* 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 @@ public:
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<CANFrame>*);
static bool saveNativeCSVFile(QString, const QVector<CANFrame>*);
+18 -3
View File
@@ -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);
}
+4 -2
View File
@@ -8,7 +8,9 @@
#include <winsock.h>
#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 *);