Skip to content

Commit

Permalink
Merge branch 'master' of github.com:collin80/SavvyCAN
Browse files Browse the repository at this point in the history
  • Loading branch information
collin80 committed Jul 10, 2024
2 parents f769dd1 + 514845f commit d76d3af
Show file tree
Hide file tree
Showing 11 changed files with 225 additions and 26 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:

Expand Down
4 changes: 4 additions & 0 deletions SavvyCAN.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 20 additions & 1 deletion connections/lawicel_serial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions connections/lawicel_serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ private slots:
QSerialPort *serial;
int framesRapid;
CANFrame buildFrame;
qint64 buildTimestamp;
bool can0Enabled;
bool can0ListenOnly;
bool canFd;
Expand Down
4 changes: 2 additions & 2 deletions dbc/dbc_classes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
43 changes: 43 additions & 0 deletions dbc/dbchandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand Down
1 change: 1 addition & 0 deletions dbc/dbchandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
Expand Down
141 changes: 126 additions & 15 deletions framefileio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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))
{
Expand All @@ -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))
{
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
Expand All @@ -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;
}
Expand All @@ -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 changes: 2 additions & 0 deletions framefileio.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class FrameFileIO: public QObject
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.
Expand All @@ -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<CANFrame>*);
static bool saveNativeCSVFile(QString, const QVector<CANFrame>*);
Expand Down
Loading

0 comments on commit d76d3af

Please sign in to comment.