#include "framefileio.h" #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()); bool result = false; QStringList filters; filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); filters.append(QString(tr("CRTD Logs (*.txt *.TXT)"))); 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.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(0); 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]; return true; } return false; } return false; } bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache) { QString filename; QFileDialog dialog; bool result = false; QStringList filters; filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); filters.append(QString(tr("CRTD Logs (*.txt *.TXT)"))); 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)"))); 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(0); progress.setRange(0,0); progress.setMinimumDuration(0); progress.show(); qApp->processEvents(); if (dialog.selectedNameFilter() == filters[0]) result = loadNativeCSVFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[1]) result = loadCRTDFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[2]) result = loadGenericCSVFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[3]) result = loadLogFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[4]) result = loadMicrochipFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[5]) result = loadTraceFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[6]) result = loadIXXATFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[7]) result = loadCANDOFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[8]) result = loadVehicleSpyFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[9]) result = loadCanDumpFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[10]) result = loadPCANFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[11]) result = loadKvaserFile(filename, frameCache, false); if (dialog.selectedNameFilter() == filters[12]) result = loadKvaserFile(filename, frameCache, true); if (dialog.selectedNameFilter() == filters[13]) result = loadCanalyzerASC(filename, frameCache); if (dialog.selectedNameFilter() == filters[14]) result = loadCanalyzerBLF(filename, frameCache); if (dialog.selectedNameFilter() == filters[15]) result = loadCANHackerFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[16]) result = loadCabanaFile(filename, frameCache); progress.cancel(); if (result) { QStringList fileList = filename.split('/'); fileName = fileList[fileList.length() - 1]; 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; } //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.addMSecs(tokens[1].toDouble() * 1000.0); thisFrame.timestamp = tempTime.toMSecsSinceEpoch() * 1000ul; if (tokens[5].startsWith("T")) thisFrame.isReceived = false; else thisFrame.isReceived = true; thisFrame.ID = tokens[9].toInt(NULL, 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] = tokens[12 + i].toInt(NULL, 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; } //CRTD format from Mark Webb-Johnson / OVMS project /* 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 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 = (int64_t)(tokens[0].toDouble() * multiplier); char firstChar = tokens[1].left(1)[0]; if (firstChar == 'R' || firstChar == 'T') { thisFrame.ID = tokens[2].toInt(NULL, 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.bus = 0; thisFrame.len = tokens.length() - 3; thisFrame.remote = false; for (unsigned int d = 0; d < thisFrame.len; d++) { if (tokens[d + 3] != "") { thisFrame.data[d] = tokens[d + 3].toInt(NULL, 16); } else thisFrame.data[d] = 0; } frames->append(thisFrame); } } else foundErrors = true; } } inFile->close(); delete inFile; return !foundErrors; } // 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 = (int64_t)(tokens[0].toDouble() * multiplier); thisFrame.ID = tokens[1].toInt(NULL, 16); thisFrame.extended = (thisFrame.ID > 0x7FF); thisFrame.isReceived = true; thisFrame.remote = false; thisFrame.bus = 0; thisFrame.len = tokens[2].toInt(NULL, 16); for (unsigned int d = 0; d < thisFrame.len; d++) { if (tokens[d + 3] != "") { thisFrame.data[d] = tokens[d + 3].toInt(NULL, 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(' '); 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 (unsigned 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; } /*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() > 2) { if (fileVersion == 1) { thisFrame.timestamp = (uint64_t)(line.mid(10, 8).simplified().toDouble() * 1000.0); thisFrame.ID = line.mid(28, 8).simplified().toUInt(NULL, 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 (unsigned int d = 0; d < thisFrame.len; d++) { if (tokens[d] != "") { thisFrame.data[d] = tokens[d].toInt(NULL, 16); } else thisFrame.data[d] = 0; } } frames->append(thisFrame); } } else if (fileVersion == 2) { thisFrame.timestamp = (uint64_t)(line.mid(8, 13).simplified().toDouble() * 1000.0); thisFrame.ID = line.mid(25, 8).simplified().toUInt(NULL, 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 (unsigned int d = 0; d < thisFrame.len; d++) { if (tokens[d] != "") { thisFrame.data[d] = tokens[d].toInt(NULL, 16); } else thisFrame.data[d] = 0; } } frames->append(thisFrame); } } } //else foundErrors = true; } inFile->close(); delete inFile; return !foundErrors; } //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; 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("//")) { inHeader = false; continue; } if (inHeader) continue; if (line.length() > 2) { QList tokens = line.simplified().split(' '); if (tokens.length() > 4) { thisFrame.timestamp = (uint64_t)(tokens[0].toDouble() * (double)1000000.0); thisFrame.ID = tokens[2].toUInt(NULL, 16); thisFrame.len = tokens[5].toUInt(); thisFrame.isReceived = tokens[3].toUpper().contains("RX"); thisFrame.bus = tokens[1].toUInt(); thisFrame.extended = (thisFrame.ID > 0x7FF); thisFrame.remote = false; for (unsigned int d = 6; d < (6 + thisFrame.len); d++) { if (tokens.count() > d) { thisFrame.data[d - 6] = tokens[d].toInt(NULL, 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; } outFile->write(QString::number((frames->at(c).timestamp - offsetTime) / 1000000.0, 'f', 5).toUtf8()); outFile->putChar(' '); outFile->write(QString::number(frames->at(c).bus).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->putChar(' '); for (unsigned 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; } //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); } //The "native" file format for this program bool FrameFileIO::loadNativeCSVFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; int fileVersion = 1; long long 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) { long long temp = tokens[0].toLongLong(); thisFrame.timestamp = temp; } else { timeStamp += 5; thisFrame.timestamp = timeStamp; } thisFrame.ID = tokens[1].toInt(NULL, 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].toUInt(); for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; for (unsigned int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = tokens[5 + d].toInt(NULL, 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].toUInt(); if (thisFrame.len + 6 > (unsigned int) tokens.length()) thisFrame.len = tokens.length() - 6; for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; for (unsigned int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = tokens[6 + d].toInt(NULL, 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 (unsigned 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()); QStringList filters; filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); 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"); 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 (unsigned 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::loadGenericCSVFile(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; long long 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].toInt(NULL, 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 (unsigned int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = dataTok[d].toInt(NULL, 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 (unsigned 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; } //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)*** ***