diff --git a/dbc/dbc_classes.cpp b/dbc/dbc_classes.cpp index c0f11ac..9dab43a 100644 --- a/dbc/dbc_classes.cpp +++ b/dbc/dbc_classes.cpp @@ -40,6 +40,7 @@ bool DBC_SIGNAL::processAsText(const CANFrame &frame, QString &outString, bool o { int64_t result = 0; bool isSigned = false; + bool isInteger = false; double endResult; if (valType == STRING) @@ -70,7 +71,9 @@ bool DBC_SIGNAL::processAsText(const CANFrame &frame, QString &outString, bool o { result = Utility::processIntegerSignal(frame.payload(), startBit, signalSize, intelByteOrder, isSigned); endResult = ((double)result * factor) + bias; - result = (int64_t)endResult; + result = (int64_t)endResult; + // if factor is an integer, we don't need the possibly human-unreadable float representation + isInteger = (factor == ceilf(factor)); } else if (valType == SP_FLOAT) { @@ -95,12 +98,12 @@ bool DBC_SIGNAL::processAsText(const CANFrame &frame, QString &outString, bool o endResult = (*((double *)(&result)) * factor) + bias; } - outString = makePrettyOutput(endResult, result, outputName); + outString = makePrettyOutput(endResult, result, outputName, isInteger); cachedValue = endResult; return true; } -QString DBC_SIGNAL::makePrettyOutput(double floatVal, int64_t intVal, bool outputName) +QString DBC_SIGNAL::makePrettyOutput(double floatVal, int64_t intVal, bool outputName, bool isInteger) { QString outputString; @@ -122,7 +125,7 @@ QString DBC_SIGNAL::makePrettyOutput(double floatVal, int64_t intVal, bool outpu } else //otherwise display the actual number and unit (if it exists) { - outputString += QString::number(floatVal) + unitName; + outputString += (isInteger ? QString::number(intVal) : QString::number(floatVal)) + unitName; } return outputString; } diff --git a/dbc/dbc_classes.h b/dbc/dbc_classes.h index ac9279e..3006737 100644 --- a/dbc/dbc_classes.h +++ b/dbc/dbc_classes.h @@ -109,7 +109,7 @@ public: //TODO: this is sloppy. It shouldn't all be public! bool processAsText(const CANFrame &frame, QString &outString, bool outputName = true); bool processAsInt(const CANFrame &frame, int32_t &outValue); bool processAsDouble(const CANFrame &frame, double &outValue); - QString makePrettyOutput(double floatVal, int64_t intVal, bool outputName = true); + QString makePrettyOutput(double floatVal, int64_t intVal, bool outputName = true, bool isInteger = false); DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name); DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx); diff --git a/examples/CarBusAnalyzer.trc b/examples/CarBusAnalyzer.trc new file mode 100644 index 0000000..1a99096 --- /dev/null +++ b/examples/CarBusAnalyzer.trc @@ -0,0 +1 @@ +@ TEXT @ 2 @ 64 @ 0 @ 55 @ 10078 @ 00:00:10.078 @ 46,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 46,734 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 47,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 47,781 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 48,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 48,437 1 0004 7C0 8 01 3E 00 00 00 00 00 00 00000000 > 48,453 1 0004 7C8 8 01 7E 00 00 00 00 00 00 00000000 ~ 48,453 1 0004 7C0 8 02 13 81 00 00 00 00 00 00000000 48,484 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 48,484 1 0004 7C0 8 02 13 82 00 00 00 00 00 00000000 48,500 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 48,687 1 0004 7C0 8 02 13 81 00 00 00 00 00 00000000 48,703 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 48,703 1 0004 7C0 8 02 13 82 00 00 00 00 00 00000000 48,719 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 48,812 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 48,891 1 0004 7C0 8 02 13 81 00 00 00 00 00 00000000 48,906 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 48,906 1 0004 7C0 8 02 13 82 00 00 00 00 00 00000000 48,937 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 49,109 1 0004 7C0 8 02 13 81 00 00 00 00 00 00000000 49,125 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,125 1 0004 7C0 8 02 13 82 00 00 00 00 00 00000000 49,141 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,312 1 0004 7C0 8 02 13 81 00 00 00 00 00 00000000 49,328 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,328 1 0004 7C0 8 02 13 82 00 00 00 00 00 00000000 49,344 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,516 1 0004 7C0 8 02 13 81 00 00 00 00 00 00000000 49,531 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,531 1 0004 7C0 8 02 13 82 00 00 00 00 00 00000000 49,547 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,719 1 0004 7C0 8 02 13 81 00 00 00 00 00 00000000 49,734 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,750 1 0004 7C0 8 02 13 82 00 00 00 00 00 00000000 49,766 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,859 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 49,937 1 0004 7C0 8 02 13 81 00 00 00 00 00 00000000 49,953 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 49,953 1 0004 7C0 8 02 13 82 00 00 00 00 00 00000000 49,969 1 0004 7C8 8 02 53 00 00 00 00 00 00 00000000 S 50,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 50,891 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 51,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 51,937 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 52,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 52,969 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 53,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 54,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 54,016 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 55,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 55,062 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ 56,016 1 0004 45A 8 5A 04 00 00 00 00 00 00 00000000 Z 56,094 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ \ No newline at end of file diff --git a/framefileio.cpp b/framefileio.cpp index a841b84..daf95c0 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -147,7 +147,6 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache filters.append(QString(tr("Autodetect File Type (*.*)"))); filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); filters.append(QString(tr("CRTD Logs (*.crt *.crtd *.CRT *.CRTD)"))); - filters.append(QString(tr("Generic ID/Data CSV (*.csv *.CSV)"))); filters.append(QString(tr("BusMaster Log (*.log *.LOG)"))); filters.append(QString(tr("Microchip Log (*.can *.CAN *.log *.LOG)"))); filters.append(QString(tr("Vector trace files (*.trace *.TRACE)"))); @@ -161,7 +160,9 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache filters.append(QString(tr("Kvaser Log Hex (*.txt *.TXT)"))); filters.append(QString(tr("CANalyzer Ascii Log (*.asc *.ASC)"))); filters.append(QString(tr("CANalyzer Binary Log Files (*.blf *.BLF)"))); + filters.append(QString(tr("CARBUS Analyzer Trace Files (*.trc *.TRC)"))); filters.append(QString(tr("CANHacker Trace Files (*.trc *.TRC)"))); + filters.append(QString(tr("Generic ID/Data CSV (*.csv *.CSV)"))); filters.append(QString(tr("Cabana Log (*.csv *.CSV)"))); filters.append(QString(tr("CANOpen Magic (*.csv *.CSV)"))); @@ -173,6 +174,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache if (dialog.exec() == QDialog::Accepted) { filename = dialog.selectedFiles()[0]; + QString selectedNameFilter = dialog.selectedNameFilter(); QProgressDialog progress(qApp->activeWindow()); progress.setWindowModality(Qt::WindowModal); @@ -184,26 +186,27 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache qApp->processEvents(); - if (dialog.selectedNameFilter() == filters[0]) result = autoDetectLoadFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[1]) result = loadNativeCSVFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[2]) result = loadCRTDFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[3]) result = loadGenericCSVFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[4]) result = loadLogFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[5]) result = loadMicrochipFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[6]) result = loadTraceFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[7]) result = loadIXXATFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[8]) result = loadCANDOFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[9]) result = loadVehicleSpyFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[10]) result = loadCanDumpFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[11]) result = loadLawicelFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[12]) result = loadPCANFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[13]) result = loadKvaserFile(filename, frameCache, false); - if (dialog.selectedNameFilter() == filters[14]) result = loadKvaserFile(filename, frameCache, true); - if (dialog.selectedNameFilter() == filters[15]) result = loadCanalyzerASC(filename, frameCache); - if (dialog.selectedNameFilter() == filters[16]) result = loadCanalyzerBLF(filename, frameCache); - if (dialog.selectedNameFilter() == filters[17]) result = loadCANHackerFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[18]) result = loadCabanaFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[19]) result = loadCANOpenFile(filename, frameCache); + if (selectedNameFilter == filters[0]) result = autoDetectLoadFile(filename, frameCache); + if (selectedNameFilter == filters[1]) result = loadNativeCSVFile(filename, frameCache); + if (selectedNameFilter == filters[2]) result = loadCRTDFile(filename, frameCache); + if (selectedNameFilter == filters[3]) result = loadLogFile(filename, frameCache); + if (selectedNameFilter == filters[4]) result = loadMicrochipFile(filename, frameCache); + if (selectedNameFilter == filters[5]) result = loadTraceFile(filename, frameCache); + if (selectedNameFilter == filters[6]) result = loadIXXATFile(filename, frameCache); + if (selectedNameFilter == filters[7]) result = loadCANDOFile(filename, frameCache); + if (selectedNameFilter == filters[8]) result = loadVehicleSpyFile(filename, frameCache); + if (selectedNameFilter == filters[9]) result = loadCanDumpFile(filename, frameCache); + if (selectedNameFilter == filters[10]) result = loadLawicelFile(filename, frameCache); + if (selectedNameFilter == filters[11]) result = loadPCANFile(filename, frameCache); + if (selectedNameFilter == filters[12]) result = loadKvaserFile(filename, frameCache, false); + if (selectedNameFilter == filters[13]) result = loadKvaserFile(filename, frameCache, true); + if (selectedNameFilter == filters[14]) result = loadCanalyzerASC(filename, frameCache); + if (selectedNameFilter == filters[15]) result = loadCanalyzerBLF(filename, frameCache); + if (selectedNameFilter == filters[16]) result = loadCARBUSAnalyzerFile(filename, frameCache); + if (selectedNameFilter == filters[17]) result = loadCANHackerFile(filename, frameCache); + if (selectedNameFilter == filters[18]) result = loadGenericCSVFile(filename, frameCache); + if (selectedNameFilter == filters[19]) result = loadCabanaFile(filename, frameCache); + if (selectedNameFilter == filters[20]) result = loadCANOpenFile(filename, frameCache); progress.cancel(); @@ -315,6 +318,16 @@ bool FrameFileIO::autoDetectLoadFile(QString filename, QVector* frames } } + qDebug() << "Attempting 'CARBUS Analyzer'"; + if (isCARBUSAnalyzerFile(filename)) + { + if (loadCARBUSAnalyzerFile(filename, frames)) + { + qDebug() << "Loaded as 'CARBUS Analyzer' successfully!"; + return true; + } + } + qDebug() << "Attempting canhacker"; if (isCANHackerFile(filename)) { @@ -710,6 +723,99 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector* frames) return !foundErrors; } +bool FrameFileIO::isCARBUSAnalyzerFile(QString filename) +{ + QFile *inFile = new QFile(filename); + QByteArray line; + + bool isMatch = false; + + // not Text mode because file contains `\r` new lines + if (!inFile->open(QIODevice::ReadOnly)) + { + delete inFile; + return false; + } + try + { + //read header + line = inFile->readLine().toUpper(); + if (line.startsWith("@ TEXT @")) return true; + } catch (...) + { + isMatch = false; + } + + inFile->close(); + delete inFile; + return isMatch; +} + +// CARBUS Analayzer trace format: +//@ TEXT @ 2 @ 64 @ 0 @ 591 @ 38782 @ 00:00:38.782 @ +//14,687 1 0004 4E0 8 24 00 00 00 00 00 00 00 00000000 $ +// timestamp: sec,ms +bool FrameFileIO::loadCARBUSAnalyzerFile(QString filename, QVector* frames) +{ + QFile *inFile = new QFile(filename); + CANFrame thisFrame; + QString line; + int lineCounter = 0; + bool foundErrors = false; + + if (!inFile->open(QIODevice::ReadOnly)) + { + delete inFile; + return false; + } + + // readLine() works only with "\n" and "\r\n" + QString localReadAll = inFile->readAll().replace("\r", "\r\n"); + + QTextStream txt(&localReadAll); + + line = txt.readLine().toUpper(); //read out the header first and discard it. + + while (!txt.atEnd()) { + lineCounter++; + if (lineCounter > 100) + { + qApp->processEvents(); + lineCounter = 0; + } + line = txt.readLine().simplified(); + if (line.length() > 2) + { + QList tokens = line.split(QRegExp("\\s+")); + if (tokens.length() > 3) + { + QString time = tokens[0].replace(",","."); + + thisFrame.timestamp = static_cast(time.toDouble() * 1000); + thisFrame.ID = static_cast(tokens[3].toInt(nullptr, 16)); + thisFrame.extended = (thisFrame.ID > 0x7FF); + thisFrame.isReceived = true; + thisFrame.remote = false; + thisFrame.bus = 0; + thisFrame.len = tokens[4].toInt(nullptr, 16); + for (int d = 0; d < thisFrame.len; d++) + { + if (tokens[d + 5] != "") + { + thisFrame.data[d] = static_cast(tokens[d + 5].toInt(nullptr, 16)); + } + else thisFrame.data[d] = 0; + } + frames->append(thisFrame); + } + else foundErrors = true; + } + } + inFile->close(); + delete inFile; + return !foundErrors; +} + bool FrameFileIO::isCANHackerFile(QString filename) { @@ -726,7 +832,7 @@ bool FrameFileIO::isCANHackerFile(QString filename) try { line = inFile->readLine().toUpper(); //read out the header first and discard it. - if (!line.contains("CANHACKER")) return false; + if (line.contains("CANHACKER")) return true; while (!inFile->atEnd()) { lineCounter++; @@ -3102,6 +3208,10 @@ bool FrameFileIO::isCanDumpFile(QString filename) } int ID = tokens[2].toULong(nullptr, 16); if (ID > 0x1FFFFFFF || ID == 0) isMatch = false; + if (tokens[3].size() < 2) { + isMatch = false; + continue; + } int len = tokens[3].at(1) - '0'; if (len < 0 || len > 8) isMatch = false; } diff --git a/framefileio.h b/framefileio.h index 06da738..81259b3 100644 --- a/framefileio.h +++ b/framefileio.h @@ -46,6 +46,7 @@ public: static bool loadKvaserFile(QString, QVector*, bool); static bool loadCanalyzerASC(QString, QVector*); static bool loadCanalyzerBLF(QString, QVector*); + static bool loadCARBUSAnalyzerFile(QString filename, QVector* frames); static bool loadCANHackerFile(QString filename, QVector* frames); static bool loadCabanaFile(QString filename, QVector* frames); static bool loadCANOpenFile(QString filename, QVector* frames); @@ -67,6 +68,7 @@ public: static bool isKvaserFile(QString); static bool isCanalyzerASC(QString); static bool isCanalyzerBLF(QString); + static bool isCARBUSAnalyzerFile(QString filename); static bool isCANHackerFile(QString filename); static bool isCabanaFile(QString filename); static bool isCANOpenFile(QString filename); diff --git a/mainwindow.cpp b/mainwindow.cpp index a80a9f3..dc4ab13 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -882,7 +882,7 @@ void MainWindow::updateFileStatus() QString output; if (model->rowCount() == 0) { - output = tr("No file loaded"); + output = tr("No packets loaded"); } else {