From b556016bbafd5300d08e2d3a155b2b0b3b1ca29e Mon Sep 17 00:00:00 2001 From: Sam Nobs <3915450+samsta@users.noreply.github.com> Date: Fri, 18 Sep 2020 15:29:28 +1200 Subject: [PATCH 1/2] Use integer formatting where possible when interpreting signal. Numbers are formatted as floating point numbers, which looks a bit crap for things like e.g. serial numbers (we don't need mantissa and exponent). This change checks the scaling factor, and if it is integer representable, the resulting number is formatted as an integer number as that's most likely what's intended, particularly if the factor is 1. --- dbc/dbc_classes.cpp | 11 +++++++---- dbc/dbc_classes.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dbc/dbc_classes.cpp b/dbc/dbc_classes.cpp index f692f7e..e9a1e3e 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.data, 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 5d32b67..6b56e02 100644 --- a/dbc/dbc_classes.h +++ b/dbc/dbc_classes.h @@ -104,7 +104,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); }; From 6eebfd7089728f7ad352bf420a0b6a3d3504c9a1 Mon Sep 17 00:00:00 2001 From: Kanstantsin Shautsou Date: Sat, 26 Sep 2020 03:40:05 +0300 Subject: [PATCH 2/2] Add CARBUS Analyzer file load. - Generic CSV parses file without failures, so CARBUS must be higher. - File may be loaded but will have no packets. Adjusted UI message for this case. - Obviously if we got CANHACKER header, then file is already detected and there is no need to parse. --- examples/CarBusAnalyzer.trc | 1 + framefileio.cpp | 154 ++++++++++++++++++++++++++++++------ framefileio.h | 2 + mainwindow.cpp | 2 +- 4 files changed, 136 insertions(+), 23 deletions(-) create mode 100644 examples/CarBusAnalyzer.trc 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 3944744..e704099 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)"))); 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(); @@ -312,6 +315,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)) { @@ -704,6 +717,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) { @@ -720,7 +826,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++; @@ -2968,6 +3074,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 7cee894..641ea32 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -804,7 +804,7 @@ void MainWindow::updateFileStatus() QString output; if (model->rowCount() == 0) { - output = tr("No file loaded"); + output = tr("No packets loaded"); } else {