#include "framefileio.h" #include #include #include #include #include #include #include "utility.h" #include "blfhandler.h" QFile FrameFileIO::continuousFile; FrameFileIO::FrameFileIO() { } bool FrameFileIO::saveFrameFile(QString &fileName, const QVector* frameCache) { QString filename; QFileDialog dialog(qApp->activeWindow()); QSettings settings; bool result = false; QStringList filters; 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)"))); filters.append(QString(tr("IXXAT MiniLog (*.csv *.CSV)"))); filters.append(QString(tr("CAN-DO Log (*.can *.avc *.evc *.qcc *.CAN *.AVC *.EVC *.QCC)"))); filters.append(QString(tr("Vehicle Spy (*.csv *.CSV)"))); filters.append(QString(tr("Candump/Kayak(*.log)"))); filters.append(QString(tr("Cabana Log(*.csv *.CSV)"))); filters.append(QString(tr("CANalyzer Ascii Log (*.asc *.ASC)"))); dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString()); dialog.setFileMode(QFileDialog::AnyFile); dialog.setNameFilters(filters); dialog.setViewMode(QFileDialog::Detail); dialog.setAcceptMode(QFileDialog::AcceptSave); if (dialog.exec() == QDialog::Accepted) { filename = dialog.selectedFiles()[0]; QProgressDialog progress(qApp->activeWindow()); progress.setWindowModality(Qt::WindowModal); progress.setLabelText("Saving file..."); progress.setCancelButton(nullptr); progress.setRange(0,0); progress.setMinimumDuration(0); progress.show(); qApp->processEvents(); if (dialog.selectedNameFilter() == filters[0]) { if (!filename.contains('.')) filename += ".csv"; result = saveNativeCSVFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[1]) { if (!filename.contains('.')) filename += ".txt"; result = saveCRTDFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[2]) { if (!filename.contains('.')) filename += ".csv"; result = saveGenericCSVFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[3]) { if (!filename.contains('.')) filename += ".log"; result = saveLogFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[4]) { if (!filename.contains('.')) filename += ".log"; result = saveMicrochipFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[5]) { if (!filename.contains('.')) filename += ".trace"; result = saveTraceFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[6]) { if (!filename.contains('.')) filename += ".csv"; result = saveIXXATFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[7]) { if (!filename.contains('.')) filename += ".can"; result = saveCANDOFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[8]) { if (!filename.contains('.')) filename += ".csv"; result = saveVehicleSpyFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[9]) { if (!filename.contains('.')) filename += ".log"; saveCanDumpFile(filename,frameCache); } if (dialog.selectedNameFilter() == filters[10]) { if (!filename.contains('.')) filename += ".csv"; result = saveCabanaFile(filename, frameCache); } if (dialog.selectedNameFilter() == filters[11]) { if (!filename.contains('.')) filename += ".asc"; result = saveCanalyzerASC(filename, frameCache); } progress.cancel(); if (result) { QStringList fileList = filename.split('/'); fileName = fileList[fileList.length() - 1]; settings.setValue("FileIO/LoadSaveDirectory", dialog.directory().path()); return true; } return false; } return false; } bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache) { QString filename; QFileDialog dialog; QSettings settings; bool result = false; QStringList filters; 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)"))); filters.append(QString(tr("IXXAT MiniLog (*.csv *.CSV)"))); filters.append(QString(tr("CAN-DO Log (*.avc *.can *.evc *.qcc *.AVC *.CAN *.EVC *.QCC)"))); filters.append(QString(tr("Vehicle Spy (*.csv *.CSV)"))); filters.append(QString(tr("Candump/Kayak (*.log *.LOG)"))); filters.append(QString(tr("PCAN Viewer (*.trc *.TRC)"))); filters.append(QString(tr("Kvaser Log Decimal (*.txt *.TXT)"))); 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("CANHacker Trace Files (*.trc *.TRC)"))); filters.append(QString(tr("Cabana Log (*.csv *.CSV)"))); filters.append(QString(tr("CANOpen Magic (*.csv *.CSV)"))); dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString()); dialog.setFileMode(QFileDialog::ExistingFile); dialog.setNameFilters(filters); dialog.setViewMode(QFileDialog::Detail); if (dialog.exec() == QDialog::Accepted) { filename = dialog.selectedFiles()[0]; QProgressDialog progress(qApp->activeWindow()); progress.setWindowModality(Qt::WindowModal); progress.setLabelText("Loading file..."); progress.setCancelButton(nullptr); progress.setRange(0,0); progress.setMinimumDuration(0); progress.show(); 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 = loadPCANFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[12]) result = loadKvaserFile(filename, frameCache, false); if (dialog.selectedNameFilter() == filters[13]) result = loadKvaserFile(filename, frameCache, true); if (dialog.selectedNameFilter() == filters[14]) result = loadCanalyzerASC(filename, frameCache); if (dialog.selectedNameFilter() == filters[15]) result = loadCanalyzerBLF(filename, frameCache); if (dialog.selectedNameFilter() == filters[16]) result = loadCANHackerFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[17]) result = loadCabanaFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[18]) result = loadCANOpenFile(filename, frameCache); progress.cancel(); if (result) { QStringList fileList = filename.split('/'); fileName = fileList[fileList.length() - 1]; settings.setValue("FileIO/LoadSaveDirectory", dialog.directory().path()); return true; } else { QMessageBox msgBox; msgBox.setText("File load completed with errors.\r\nPerhaps you selected the wrong file type?"); msgBox.exec(); return false; } } return false; } //Try every format by first uses the "is" functions which try to detect whether a given file is a good match to that //file format or not. Those functions are much less tolerant than the load functions and so should help to discriminate //whether a file could be loaded or not by a given loader. The loader return is still used in case the guess was wrong. bool FrameFileIO::autoDetectLoadFile(QString filename, QVector* frames) { if (isCanalyzerBLF(filename)) { if (loadCanalyzerBLF(filename, frames)) { qDebug() << "Loaded as Canalyzer BLF successfully!"; return true; } } if (isNativeCSVFile(filename)) { if (loadNativeCSVFile(filename, frames)) { qDebug() << "Loaded as native CSV successfully!"; return true; } } if (isCanalyzerASC(filename)) { if (loadCanalyzerASC(filename, frames)) { qDebug() << "Loaded as Canalyzer ASC successfully!"; return true; } } if (isCRTDFile(filename)) { if (loadCRTDFile(filename, frames)) { qDebug() << "Loaded as CRTD successfully!"; return true; } } if (isTraceFile(filename)) { if (loadTraceFile(filename, frames)) { qDebug() << "Loaded as trace successfully!"; return true; } } if (isVehicleSpyFile(filename)) { if (loadVehicleSpyFile(filename, frames)) { qDebug() << "Loaded as vehicle spy successfully!"; return true; } } if (isCanDumpFile(filename)) { if (loadCanDumpFile(filename, frames)) { qDebug() << "Loaded as candump successfully!"; return true; } } if (isCANHackerFile(filename)) { if (loadCANHackerFile(filename, frames)) { qDebug() << "Loaded as CANHacker successfully!"; return true; } } if (isCabanaFile(filename)) { if (loadCabanaFile(filename, frames)) { qDebug() << "Loaded as Cabana successfully!"; return true; } } if (isCANOpenFile(filename)) { if (loadCANOpenFile(filename, frames)) { qDebug() << "Loaded as CANOpen Magic successfully!"; return true; } } if (isLogFile(filename)) { if (loadLogFile(filename, frames)) { qDebug() << "Loaded as Busmaster Log successfully!"; return true; } } if (isPCANFile(filename)) { if (loadPCANFile(filename, frames)) { qDebug() << "Loaded as PCAN successfully!"; return true; } } if (isIXXATFile(filename)) { if (loadIXXATFile(filename, frames)) { qDebug() << "Loaded as IXXAT successfully!"; return true; } } if (isMicrochipFile(filename)) { if (loadMicrochipFile(filename, frames)) { qDebug() << "Loaded as microchip successfully!"; return true; } } if (isCANDOFile(filename)) { if (loadCANDOFile(filename, frames)) { qDebug() << "Loaded as CANDO successfully!"; return true; } } if (isKvaserFile(filename)) { if (loadKvaserFile(filename, frames,true)) { qDebug() << "Loaded as Kvaser HEX successfully!"; return true; } if (loadKvaserFile(filename, frames,false)) { qDebug() << "Loaded as KVaser Decimal successfully!"; return true; } } if (isGenericCSVFile(filename)) { if (loadGenericCSVFile(filename, frames)) { qDebug() << "Loaded as generic CSV successfully!"; return true; } } return false; } bool FrameFileIO::isVehicleSpyFile(QString filename) { QFile *inFile = new QFile(filename); QByteArray line; bool foundProbableHeader = false; bool isMatch = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { for (int i = 0; i < 10; i++) { if (!inFile->atEnd()) { line = inFile->readLine().simplified().toUpper(); if (line.startsWith("LINE") && line.contains("TIME") && line.contains("B1")) { foundProbableHeader = true; } } } if (foundProbableHeader) { if (!inFile->atEnd()) { line = inFile->readLine().simplified().toUpper(); inFile->close(); QList tokens = line.split(','); if (tokens.length() > 20) { if (tokens[9].toInt(nullptr, 16) > 0) isMatch = true; } } } } catch (...) { isMatch = false; } inFile->close(); return isMatch; } //2,2550.368293675,0.003818174999651092,67371008,F,F,HS CAN $119,HS CAN,,119,F,F,00,00,00,00,00,00,0D,8B,,, //Line,Abs Time(Sec),Rel Time (Sec),Status,Er,Tx,Description,Network,Node,Arb ID,Remote,Xtd,B1,B2,B3,B4,B5,B6,B7,B8,Value,Trigger,Signals // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 bool FrameFileIO::loadVehicleSpyFile(QString filename, QVector *frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; int lineCounter = 0; bool pastHeader = false; bool foundErrors = false; QDateTime now = QDateTime::currentDateTime(); QDateTime tempTime; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } while (!inFile->atEnd() && !pastHeader) { line = inFile->readLine().simplified().toUpper(); if (line.startsWith("LINE")) lineCounter++; if (lineCounter == 2) pastHeader = true; } if (inFile->atEnd()) foundErrors = true; while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine().simplified().toUpper(); QList tokens = line.split(','); if (tokens.length() > 20) { thisFrame.bus = 0; thisFrame.remote = false; tempTime = now; tempTime = tempTime.addMSecs(static_cast(tokens[1].toDouble() * 1000.0)); thisFrame.timestamp = static_cast(tempTime.toMSecsSinceEpoch() * 1000); if (tokens[5].startsWith("T")) thisFrame.isReceived = false; else thisFrame.isReceived = true; thisFrame.ID = static_cast(tokens[9].toInt(nullptr, 16)); if (tokens[11].startsWith("T")) thisFrame.extended = true; else thisFrame.extended = false; thisFrame.len = 0; for (int i = 0; i < 8; i++) { if (tokens[12 + i].length() > 0) { thisFrame.data[i] = static_cast(tokens[12 + i].toInt(nullptr, 16)); thisFrame.len++; } else break; } frames->append(thisFrame); } else foundErrors = true; } inFile->close(); delete inFile; return !foundErrors; } bool FrameFileIO::saveVehicleSpyFile(QString filename, const QVector *frames) { Q_UNUSED(filename); Q_UNUSED(frames); return true; } bool FrameFileIO::isCRTDFile(QString filename) { QFile *inFile = new QFile(filename); QByteArray line; int lineCounter = 0; bool isMatch = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { line = inFile->readLine().toUpper(); //read out the header first and discard it. while (!inFile->atEnd() && lineCounter < 100) { lineCounter++; line = inFile->readLine().simplified(); if (line.length() > 2) { QList tokens = line.split(' '); if (tokens.length() > 2) { char firstChar = tokens[1].left(1)[0]; if (firstChar >= '1' && firstChar <= '9') { tokens[1].remove(0,1); // Remove leading digit (bus number) firstChar = tokens[1].left(1)[0]; } if (firstChar == 'R' || firstChar == 'T') { if (tokens[1] == "R29" || tokens[1] == "T29") isMatch = true; if (tokens[1] == "R11" || tokens[1] == "T11") isMatch = true; } } else isMatch = false; } if (lineCounter > 10) break; } } catch (...) { isMatch = false; } inFile->close(); delete inFile; return isMatch; } //CRTD format from Mark Webb-Johnson / OVMS project // Specification at: https://docs.openvehicles.com/en/latest/crtd/ /* Sample data in CRTD format Timestamp appears to be seconds since the epoch 1320745424.000 CXX OVMS Tesla Roadster cando2crtd converted log 1320745424.000 CXX OVMS Tesla roadster log: charge.20111108.csv 1320745424.002 R11 402 FA 01 C3 A0 96 00 07 01 1320745424.015 R11 400 02 9E 01 80 AB 80 55 00 1320745424.066 R11 400 01 01 00 00 00 00 4C 1D 1320745424.105 R11 100 A4 53 46 5A 52 45 38 42 1320745424.106 R11 100 A5 31 35 42 33 30 30 30 1320745424.106 R11 100 A6 35 36 39 1320745424.106 CEV Open charge port door 1320745424.106 R11 100 9B 97 A6 31 03 15 05 06 1320745424.107 R11 100 07 64 123.431 2R11 27E 00 00 00 00 00 00 00 00 123.441 2R11 7C8 00 00 00 00 00 00 00 00 123.451 2R11 402 FD 04 04 0F 10 00 00 07 123.461 2R11 318 92 08 33 0D 07 00 00 00 tokens: 0 = timestamp 1 = line type 2 = ID 3-x = The data bytes */ bool FrameFileIO::loadCRTDFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; int lineCounter = 0; bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } line = inFile->readLine().toUpper(); //read out the header first and discard it. while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine().simplified(); if (line.length() > 2) { QList tokens = line.split(' '); int multiplier; if (tokens.length() > 2) { int idxOfDecimal = tokens[0].indexOf('.'); if (idxOfDecimal > -1) { //int decimalPlaces = tokens[0].length() - tokens[0].indexOf('.') - 1; //the result of the above is the # of digits after the decimal. //This program deals in microsecond so turn the value into microseconds multiplier = 1000000; //turn the decimal into full microseconds } else { multiplier = 1; //special case. Assume no decimal means microseconds } //qDebug() << "decimal places " << decimalPlaces; thisFrame.timestamp = static_cast((tokens[0].toDouble() * multiplier)); thisFrame.bus = 0; char firstChar = tokens[1].left(1)[0]; if (firstChar >= '1' && firstChar <= '9') { thisFrame.bus = tokens[1].left(1)[0] - '1'; tokens[1].remove(0,1); // Remove leading digit (bus number) firstChar = tokens[1].left(1)[0]; } if (firstChar == 'R' || firstChar == 'T') { thisFrame.ID = static_cast(tokens[2].toInt(nullptr, 16)); if (tokens[1] == "R29" || tokens[1] == "T29") thisFrame.extended = true; else thisFrame.extended = false; if (firstChar == 'T') thisFrame.isReceived = false; else thisFrame.isReceived = true; thisFrame.len = tokens.length() - 3; thisFrame.remote = false; for (int d = 0; d < thisFrame.len; d++) { if (tokens[d + 3] != "") { thisFrame.data[d] = static_cast(tokens[d + 3].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) { QFile *inFile = new QFile(filename); QByteArray line; int lineCounter = 0; bool isMatch = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { line = inFile->readLine().toUpper(); //read out the header first and discard it. if (!line.contains("CANHACKER")) return false; while (!inFile->atEnd()) { lineCounter++; line = inFile->readLine().simplified(); if (line.length() > 2) { QList tokens = line.split(' '); if (tokens.length() > 3) { if (tokens[1].toInt(nullptr, 16) > 0) { int len = tokens[2].toInt(); if (len > -1 && len < 9) { isMatch = true; } } } else isMatch = false; } if (lineCounter > 10) break; } } catch (...) { isMatch = false; } inFile->close(); delete inFile; return isMatch; } // CANHacker trace format // Time ID DLC Data Comment // 00.000 00004000 8 36 47 19 43 01 00 00 80 bool FrameFileIO::loadCANHackerFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; int lineCounter = 0; bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } line = inFile->readLine().toUpper(); //read out the header first and discard it. while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine().simplified(); if (line.length() > 2) { QList tokens = line.split(' '); int multiplier; if (tokens.length() > 3) { int idxOfDecimal = tokens[0].indexOf('.'); if (idxOfDecimal > -1) { //int decimalPlaces = tokens[0].length() - tokens[0].indexOf('.') - 1; //the result of the above is the # of digits after the decimal. //This program deals in microsecond so turn the value into microseconds multiplier = 1000000; //turn the decimal into full microseconds } else { multiplier = 1; //special case. Assume no decimal means microseconds } //qDebug() << "decimal places " << decimalPlaces; thisFrame.timestamp = static_cast(tokens[0].toDouble() * multiplier); thisFrame.ID = static_cast(tokens[1].toInt(nullptr, 16)); thisFrame.extended = (thisFrame.ID > 0x7FF); thisFrame.isReceived = true; thisFrame.remote = false; thisFrame.bus = 0; thisFrame.len = tokens[2].toInt(nullptr, 16); for (int d = 0; d < thisFrame.len; d++) { if (tokens[d + 3] != "") { thisFrame.data[d] = static_cast(tokens[d + 3].toInt(nullptr, 16)); } else thisFrame.data[d] = 0; } frames->append(thisFrame); } else foundErrors = true; } } inFile->close(); delete inFile; return !foundErrors; } bool FrameFileIO::isCANOpenFile(QString filename) { QFile *inFile = new QFile(filename); QByteArray line; int lineCounter = 0; bool isMatch = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { line = inFile->readLine().toUpper(); if (!line.contains("CANOPEN MAGIC")) return false; line = inFile->readLine(); line = inFile->readLine(); line = inFile->readLine(); line = inFile->readLine(); while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 10) { break; } line = inFile->readLine().replace('\"', ' ').simplified(); if (line.length() > 2) { QList tokens = line.split(','); if (tokens.length() > 11) { if (Utility::ParseStringToNum(tokens[5].simplified()) > 0) { QList dataTok = tokens[11].simplified().split(' '); if ( dataTok.length() > -1 && dataTok.length() < 9) isMatch = true; } } else isMatch = false; } } } catch (...) { isMatch = false; } inFile->close(); delete inFile; return isMatch; } //"Message Number","Time (ms)","Time","Excel Time","Count","ID","Flags","Message Type","Node","Details","Process Data","Data (Hex)","Data (Text)","Data (Decimal)","Length","Raw Message" //"0","0.000","8:09:42:48.7953090'",43447.7100146116,"","0x2E1","","Default: PDO","","Default: TPDO 2 of Node 0x61 (97)","","10 21 04 00 00 00 00 00 ",". ! . . . . . . ","U:0 S:0","8","10 21 04 00 00 00 00 00" bool FrameFileIO::loadCANOpenFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; int lineCounter = 0; bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; qDebug() << "Could not open the file!"; return false; } line = inFile->readLine(); //read out the header first and discard it. line = inFile->readLine(); line = inFile->readLine(); line = inFile->readLine(); line = inFile->readLine(); while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine().replace('\"', ' ').simplified(); if (line.length() > 2) { QList tokens = line.split(','); if (tokens.length() > 11) { thisFrame.timestamp = static_cast(tokens[1].simplified().toDouble() * 1000.0); thisFrame.ID = static_cast(Utility::ParseStringToNum(tokens[5].simplified())); thisFrame.extended = (thisFrame.ID > 0x7FF); thisFrame.isReceived = true; thisFrame.remote = false; thisFrame.bus = 0; QList dataTok = tokens[11].simplified().split(' '); thisFrame.len = dataTok.length(); for (int d = 0; d < thisFrame.len; d++) { if (dataTok[d] != "") { thisFrame.data[d] = static_cast(dataTok[d].simplified().toInt(nullptr, 16)); } else thisFrame.data[d] = 0; } frames->append(thisFrame); } else foundErrors = true; } } inFile->close(); delete inFile; return !foundErrors; } bool FrameFileIO::saveCRTDFile(QString filename, const QVector* frames) { QFile *outFile = new QFile(filename); int lineCounter = 0; if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) { delete outFile; return false; } //write in float format with 6 digits after the decimal point outFile->write(QString::number(frames->at(0).timestamp / 1000000.0, 'f', 6).toUtf8() + tr(" CXX GVRET-PC Reverse Engineering Tool Output V").toUtf8() + QString::number(VERSION).toUtf8()); outFile->write("\n"); for (int c = 0; c < frames->count(); c++) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } outFile->write(QString::number(frames->at(c).timestamp / 1000000.0, 'f', 6).toUtf8()); outFile->putChar(' '); outFile->write(QString::number(frames->at(c).bus + 1).toUtf8()); if (frames->at(c).isReceived) outFile->putChar('R'); else outFile->putChar('T'); if (frames->at(c).extended) { outFile->write("29 "); } else outFile->write("11 "); outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8()); outFile->putChar(' '); for (int temp = 0; temp < frames->at(c).len; temp++) { outFile->write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8()); outFile->putChar(' '); } outFile->write("\n"); } outFile->close(); delete outFile; return true; } bool FrameFileIO::isPCANFile(QString filename) { QFile *inFile = new QFile(filename); QByteArray line; int lineCounter = 0; int fileVersion = 1; bool isMatch = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 10) { break; } line = inFile->readLine(); if (line.startsWith(";$FILEVERSION=2.0")) fileVersion = 2; if (line.startsWith(';')) continue; if (line.length() > 41) { if (fileVersion == 1) { uint32_t id = line.mid(28, 8).simplified().toUInt(nullptr, 16); if (id > 0 && id < 0x1FFFFFFF) { int len = line.mid(38,1).toInt(); if (len > - 1 && len < 9) isMatch = true; } else isMatch = false; } else if (fileVersion == 2) { uint32_t id = line.mid(25, 8).simplified().toUInt(nullptr, 16); if (id > 0 && id < 0x1FFFFFFF) { int len = line.mid(37,2).trimmed().toInt(); if (len > 0 && len < 9) { QList tokens = line.mid(40, len * 3).split(' '); if (tokens.length() == len) isMatch = true; } else isMatch = false; } } } //else foundErrors = true; } } catch (...) { isMatch = false; } inFile->close(); delete inFile; return isMatch; } /*Fixed length lines Version 1.1 //; Message Number //; | Time Offset (ms) //; | | Type //; | | | ID (hex) //; | | | | Data Length Code //; | | | | | Data Bytes (hex) ... //; | | | | | | //;---+-- ----+---- --+-- ----+--- + -+ -- -- -- -- -- -- -- // 0-6 10-18 21-25 28-35 38 41-? two chars each + space Version 2.0 ; Message Time Type ID Rx/Tx ; Number Offset | [hex] | Data Length ; | [ms] | | | | Data [hex] ... ; | | | | | | | ;---+-- ------+------ +- --+----- +- +- +- +- -- -- -- -- -- -- -- 0-6 8-20 22-23 25-32 34-35 37-38 40-? two chars each + space Both versions are supported now but rather lazily. Very many aspects of the file are ignored. It seems as if Version 2 files might be able to store other protocols like ISO-TP or J1939 */ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; int lineCounter = 0; int fileVersion = 1; bool foundErrors = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine(); if (line.startsWith(";$FILEVERSION=2.0")) fileVersion = 2; if (line.startsWith(';')) continue; if (line.length() > 41) { if (fileVersion == 1) { thisFrame.timestamp = (uint64_t)(line.mid(10, 8).simplified().toDouble() * 1000.0); thisFrame.ID = line.mid(28, 8).simplified().toUInt(nullptr, 16); if (thisFrame.ID < 0x1FFFFFFF) { thisFrame.len = line.mid(38,1).toInt(); thisFrame.isReceived = true; thisFrame.bus = 0; if (line.at(28) == ' ') { thisFrame.extended = false; } else { thisFrame.extended = true; } if (line.at(41) == 'R') { thisFrame.remote = true; } else { QList tokens = line.mid(41, thisFrame.len * 3).split(' '); thisFrame.remote = false; for (int d = 0; d < thisFrame.len; d++) { if (tokens[d] != "") { thisFrame.data[d] = static_cast(tokens[d].toInt(nullptr, 16)); } else thisFrame.data[d] = 0; } } frames->append(thisFrame); } } else if (fileVersion == 2) { thisFrame.timestamp = static_cast(line.mid(8, 13).simplified().toDouble() * 1000.0); thisFrame.ID = line.mid(25, 8).simplified().toUInt(nullptr, 16); if (thisFrame.ID < 0x1FFFFFFF) { thisFrame.len = line.mid(37,2).trimmed().toInt(); qDebug() << thisFrame.len; thisFrame.isReceived = true; thisFrame.bus = 0; if (line.at(25) == ' ') { thisFrame.extended = false; } else { thisFrame.extended = true; } if (line.at(40) == 'R') { thisFrame.remote = true; } else { QList tokens = line.mid(40, thisFrame.len * 3).split(' '); thisFrame.remote = false; for (int d = 0; d < thisFrame.len; d++) { if (tokens[d] != "") { thisFrame.data[d] = static_cast(tokens[d].toInt(nullptr, 16)); } else thisFrame.data[d] = 0; } } frames->append(thisFrame); } } } //else foundErrors = true; } inFile->close(); delete inFile; return !foundErrors; } bool FrameFileIO::isCanalyzerASC(QString filename) { QFile *inFile = new QFile(filename); QByteArray line; int lineCounter = 0; bool isMatch = true; bool inHeader = true; QList tokens; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { while (!inFile->atEnd() && lineCounter < 10) { lineCounter++; if (lineCounter > 10) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine(); if (inHeader) { if (line.startsWith("//") || lineCounter > 4) { inHeader = false; continue; } } if (inHeader) continue; if (line.length() > 2) { tokens = line.simplified().split(' '); if (tokens.length() > 5) { uint64_t time = static_cast(tokens[0].toDouble() * 1000000.0); if (time <= 0) isMatch = false; uint32_t id = static_cast(tokens[2].toUInt(nullptr, 16)); if (id < 1 || id > 0x1FFFFFFF) isMatch = false; int len = tokens[5].toInt(); if (len < 0 || len > 8) isMatch = false; if (tokens.count() < (len + 6)) isMatch = false; } else isMatch = false; } else isMatch = false; } } catch (...) { isMatch = false; } inFile->close(); delete inFile; return isMatch; } //There tends to be four lines of header first. The last of which starts with // so first burn off lines //until a line starting with // is seen, then the rest are formatted like this: // 47.971842 2 248 Rx d 8 FF FF FF FF FF FF FF FF //That can be simplified to look like this: //47.971842 2 248 Rx d 8 FF FF FF FF FF FF FF FF //Which is then easy to parse by splitting on the spaces //Time bus id dir ? len databytes bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; int lineCounter = 0; bool foundErrors = false; bool inHeader = true; thisFrame.remote = false; QList tokens; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine(); if (inHeader) { if (line.startsWith("//") || lineCounter > 4) { inHeader = false; continue; } } if (inHeader) continue; if (line.length() > 2) { tokens = line.simplified().split(' '); if (tokens.length() > 5) { thisFrame.timestamp = static_cast(tokens[0].toDouble() * 1000000.0); thisFrame.ID = tokens[2].toUInt(nullptr, 16); thisFrame.len = tokens[5].toInt(); if (thisFrame.len > 8) return false; if (thisFrame.len < 0) return false; thisFrame.isReceived = tokens[3].toUpper().contains("RX"); thisFrame.bus = tokens[1].toInt(); thisFrame.extended = (thisFrame.ID > 0x7FF); thisFrame.remote = false; for (int d = 6; d < (6 + static_cast(thisFrame.len)); d++) { if (tokens.count() > d) { thisFrame.data[d - 6] = static_cast(tokens[d].toInt(nullptr, 16)); } else //expected byte wasn't there to read. Set it zero and set error flag { thisFrame.data[d - 6] = 0; foundErrors = true; } } } frames->append(thisFrame); } //else foundErrors = true; } inFile->close(); delete inFile; return !foundErrors; } bool FrameFileIO::saveCanalyzerASC(QString filename, const QVector* frames) { QFile *outFile = new QFile(filename); int lineCounter = 0; uint64_t offsetTime = frames->at(0).timestamp; for (int c = 0; c < frames->count(); c++) { if (frames->at(c).timestamp < offsetTime) offsetTime = frames->at(c).timestamp; } if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) { delete outFile; return false; } QDateTime now; now = QDateTime::currentDateTime(); if (offsetTime > 10000000000) //chances are the input file had times as system time so load it { now.setMSecsSinceEpoch(offsetTime / 1000); //offsetTime was in microseconds } outFile->write("date " + now.toString("ddd MMM dd h:mm:ss.zzz a yyyy").toUtf8()); outFile->write("\nbase hex timestamps absolute\n"); outFile->write("no internal event logging\n"); outFile->write("// version 11.0.0\n"); for (int c = 0; c < frames->count(); c++) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } uint64_t timeStamp = (frames->at(c).timestamp - offsetTime) / 1000000ull; int tsLen = QString::number(timeStamp).length(); int precision = 6; //vector seems to keep 10 bytes at the start of the line for the timestamp. It should never exceed this //and there should never be a precision over 6 digits after the decimal if (tsLen > 3) precision = 9 - tsLen; outFile->write(QString::number((frames->at(c).timestamp - offsetTime) / 1000000.0, 'f', precision).rightJustified(10, ' ').toUtf8()); outFile->putChar(' '); outFile->write(QString::number(frames->at(c).bus + 1).toUtf8()); outFile->write(" "); if (frames->at(c).extended) outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8()); else { outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(3, '0').toUtf8()); outFile->write(" "); } outFile->write(" "); if (frames->at(c).isReceived) outFile->write("Rx D "); else outFile->write("Tx D "); outFile->write(QString::number(frames->at(c).len).toUtf8()); outFile->write(" "); for (int temp = 0; temp < frames->at(c).len; temp++) { outFile->write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8()); outFile->write(" "); } outFile->write("\n"); } outFile->close(); delete outFile; return true; } bool FrameFileIO::isCanalyzerBLF(QString filename) { BLF_FILE_HEADER header; QFile *inFile = new QFile(filename); bool isMatch = false; if (!inFile->open(QIODevice::ReadOnly)) { delete inFile; return false; } inFile->read(reinterpret_cast(&header), sizeof(header)); if (qFromLittleEndian(header.sig) == 0x47474F4C) { qDebug() << "Proper BLF file header token"; isMatch = true; } else isMatch = false; inFile->close(); delete inFile; return isMatch; } //this one is pretty complicated and handled by it's own class bool FrameFileIO::loadCanalyzerBLF(QString filename, QVector *frames) { BLFHandler blf; return blf.loadBLF(filename, frames); } bool FrameFileIO::isNativeCSVFile(QString filename) { QFile *inFile = new QFile(filename); QByteArray line; int fileVersion = 1; bool isMatch = true; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { line = inFile->readLine().toUpper(); //read out the header first and discard it. if (line.length() < 23) isMatch = false; else if (line.at(23) == 'D') fileVersion = 2; //Dir is found starting at position 23 if this is a V2 file if (!line.contains("TIME STAMP")) isMatch = false; if (!line.contains("EXTENDED")) isMatch = false; if (!line.contains("D1")) isMatch = false; if (!inFile->atEnd()) { line = inFile->readLine().simplified(); if (line.length() > 2) { QList tokens = line.split(','); if (tokens.length() >= 5) { if (fileVersion == 1) { if (tokens[4].toUInt() > 8) isMatch = false; } else if (fileVersion == 2) { if ( tokens[5].toUInt() > 8) isMatch = false; } } else isMatch = false; } } } catch (...) { isMatch = false; } inFile->close(); delete inFile; return isMatch; } //The "native" file format for this program //Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8 //39747828,000005EB,false,Rx,0,8,E8,45,85,4B,4A,28,36,69, bool FrameFileIO::loadNativeCSVFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; int fileVersion = 1; uint64_t timeStamp = Utility::GetTimeMS(); int lineCounter = 0; bool foundErrors = false; thisFrame.remote = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } line = inFile->readLine().toUpper(); //read out the header first and discard it. if (line.at(23) == 'D') fileVersion = 2; //Dir is found starting at position 23 if this is a V2 file while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine().simplified(); if (line.length() > 2) { QList tokens = line.split(','); if (tokens.length() >= 5) { if (tokens[0].length() > 3) { thisFrame.timestamp = tokens[0].toULongLong(); } else { timeStamp += 5; thisFrame.timestamp = timeStamp; } thisFrame.ID = tokens[1].toUInt(nullptr, 16); if (tokens[2].toUpper().contains("TRUE")) thisFrame.extended = 1; else thisFrame.extended = 0; thisFrame.remote = false; if (fileVersion == 1) { thisFrame.isReceived = true; thisFrame.bus = tokens[3].toInt(); thisFrame.len = tokens[4].toInt(); if (thisFrame.len > 8) thisFrame.len = 8; if (thisFrame.len < 0) thisFrame.len = 0; for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; if (thisFrame.len + 5 > tokens.length()) thisFrame.len = tokens.length() - 5; for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = static_cast(tokens[5 + d].toInt(nullptr, 16)); } else if (fileVersion == 2) { if (tokens[3].at(0) == 'R') thisFrame.isReceived = true; else thisFrame.isReceived = false; thisFrame.bus = tokens[4].toInt(); thisFrame.len = tokens[5].toInt(); if (thisFrame.len > 8) thisFrame.len = 8; if (thisFrame.len < 0) thisFrame.len = 0; if (thisFrame.len + 6 > tokens.length()) thisFrame.len = tokens.length() - 6; for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = static_cast(tokens[6 + d].toInt(nullptr, 16)); } frames->append(thisFrame); } else foundErrors = true; } } inFile->close(); delete inFile; return !foundErrors; } bool FrameFileIO::saveNativeCSVFile(QString filename, const QVector* frames) { QFile *outFile = new QFile(filename); int lineCounter = 0; if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) { delete outFile; return false; } outFile->write("Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8"); outFile->write("\n"); for (int c = 0; c < frames->count(); c++) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } outFile->write(QString::number(frames->at(c).timestamp).toUtf8()); outFile->putChar(44); outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8()); outFile->putChar(44); if (frames->at(c).extended) outFile->write("true,"); else outFile->write("false,"); if (frames->at(c).isReceived) outFile->write("Rx,"); else outFile->write("Tx,"); outFile->write(QString::number(frames->at(c).bus).toUtf8()); outFile->putChar(44); outFile->write(QString::number(frames->at(c).len).toUtf8()); outFile->putChar(44); for (int temp = 0; temp < 8; temp++) { if (temp < frames->at(c).len) outFile->write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8()); else outFile->write("00"); outFile->putChar(44); } outFile->write("\n"); } outFile->close(); delete outFile; return true; } bool FrameFileIO::openContinuousNative() { QString filename; QFileDialog dialog(qApp->activeWindow()); QSettings settings; QStringList filters; filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString()); dialog.setFileMode(QFileDialog::AnyFile); dialog.setNameFilters(filters); dialog.setViewMode(QFileDialog::Detail); dialog.setAcceptMode(QFileDialog::AcceptSave); if (dialog.exec() == QDialog::Accepted) { filename = dialog.selectedFiles()[0]; continuousFile.setFileName(filename); if (!continuousFile.open(QIODevice::WriteOnly | QIODevice::Text)) { return false; } continuousFile.write("Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8"); continuousFile.write("\n"); settings.setValue("FileIO/LoadSaveDirectory", dialog.directory().path()); return true; } return false; } bool FrameFileIO::closeContinuousNative() { if (continuousFile.isOpen()) { continuousFile.close(); return true; } return false; } bool FrameFileIO::writeContinuousNative(const QVector* frames, int beginningFrame) { if (!continuousFile.isOpen()) return false; qDebug() << "Bgn: " << beginningFrame << " Count: " << frames->count(); for (int c = beginningFrame; c < frames->count(); c++) { continuousFile.write(QString::number(frames->at(c).timestamp).toUtf8()); continuousFile.putChar(44); continuousFile.write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8()); continuousFile.putChar(44); if (frames->at(c).extended) continuousFile.write("true,"); else continuousFile.write("false,"); if (frames->at(c).isReceived) continuousFile.write("Rx,"); else continuousFile.write("Tx,"); continuousFile.write(QString::number(frames->at(c).bus).toUtf8()); continuousFile.putChar(44); continuousFile.write(QString::number(frames->at(c).len).toUtf8()); continuousFile.putChar(44); for (int temp = 0; temp < 8; temp++) { if (temp < frames->at(c).len) continuousFile.write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8()); else continuousFile.write("00"); continuousFile.putChar(44); } continuousFile.write("\n"); } return true; } bool FrameFileIO::flushContinuousNative() { if (continuousFile.isOpen()) { return continuousFile.flush(); } return false; } bool FrameFileIO::isGenericCSVFile(QString filename) { QFile *inFile = new QFile(filename); QByteArray line; bool isMatch = true; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { line = inFile->readLine(); //read out the header first and discard it. if (!inFile->atEnd()) { line = inFile->readLine(); if (line.length() > 2) { QList tokens = line.split(','); int ID = tokens[0].toInt(nullptr, 16); if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; if (tokens.count() < 2) { isMatch = false; } if (isMatch) { QList dataTok = tokens[1].split(' '); int len = dataTok.length(); if (len > 8) isMatch = false; if (len < 2) isMatch = false; } } else isMatch = false; } } catch (...) { isMatch = false; } inFile->close(); delete inFile; return isMatch; } bool FrameFileIO::loadGenericCSVFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; uint64_t timeStamp = Utility::GetTimeMS(); int lineCounter = 0; bool foundErrors = false; thisFrame.remote = false; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } line = inFile->readLine(); //read out the header first and discard it. while (!inFile->atEnd()) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } line = inFile->readLine(); if (line.length() > 2) { QList tokens = line.split(','); timeStamp += 5000; thisFrame.timestamp = timeStamp; thisFrame.ID = tokens[0].toUInt(nullptr, 16); if (thisFrame.ID > 0x7FF) thisFrame.extended = true; else thisFrame.extended = false; thisFrame.bus = 0; thisFrame.remote = false; QList dataTok = tokens[1].split(' '); thisFrame.len = dataTok.length(); if (thisFrame.len > 8) thisFrame.len = 8; for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = static_cast(dataTok[d].toInt(nullptr, 16)); frames->append(thisFrame); } else foundErrors = true; } inFile->close(); delete inFile; return !foundErrors; } //4f5,ff 34 23 45 24 e4 bool FrameFileIO::saveGenericCSVFile(QString filename, const QVector* frames) { QFile *outFile = new QFile(filename); int lineCounter = 0; if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) { delete outFile; return false; } outFile->write("ID,Data Bytes"); outFile->write("\n"); for (int c = 0; c < frames->count(); c++) { lineCounter++; if (lineCounter > 100) { qApp->processEvents(); lineCounter = 0; } outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8()); outFile->putChar(44); for (int temp = 0; temp < frames->at(c).len; temp++) { outFile->write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8()); outFile->putChar(' '); } outFile->write("\n"); } outFile->close(); delete outFile; return true; } bool FrameFileIO::isLogFile(QString filename) { QFile *inFile = new QFile(filename); QByteArray line; bool isMatch = true; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { delete inFile; return false; } try { line = inFile->readLine().toUpper(); if (!line.contains("BUSMASTER")) isMatch = false; while (!inFile->atEnd() && line.startsWith("***")) { line = inFile->readLine().toUpper(); } if (!inFile->atEnd()) { line = inFile->readLine().toUpper(); if (line.length() > 1) { QList tokens = line.split(' '); if (tokens.length() >= 6) { QList timeToks = tokens[0].split(':'); if (timeToks.count() != 4) isMatch = false; int ID = tokens[3].right(tokens[3].length() - 2).toInt(nullptr, 16); if (ID < 1 || ID > 0x1FFFFFFF) isMatch = false; if (tokens[4] != "S" && tokens[4] != "X" && tokens[4] != "SR" && tokens[4] != "XR") isMatch = false; int len = tokens[5].toInt(); if (len > 8) isMatch = false; } else isMatch = false; } } } catch (...) { isMatch = false; } inFile->close(); delete inFile; return isMatch; } //busmaster log file /* tokens: 0 = timestamp 1 = Transmission direction 2 = Channel 3 = ID 4 = Type (s = standard, x = extended, sr = standard remote, xr = extended remote) 5 = Data byte length 6-x = The data bytes Sample chunk of a busmaster log: ***BUSMASTER Ver 2.4.0*** ***PROTOCOL CAN*** ***NOTE: PLEASE DO NOT EDIT THIS DOCUMENT*** ***[START LOGGING SESSION]*** ***START DATE AND TIME 8:8:2014 11:49:7:965*** ***HEX*** ***SYSTEM MODE*** ***START CHANNEL BAUD RATE*** ***CHANNEL 1 - Kvaser - Kvaser Leaf Light HS #0 (Channel 0), Serial Number- 0, Firmware- 0x00000037 0x00020000 - 500000 bps*** ***END CHANNEL BAUD RATE*** ***START DATABASE FILES (DBF/DBC)*** ***END OF DATABASE FILES (DBF/DBC)*** ***