#include "framefileio.h" #include #include #include 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)"))); 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); } 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)"))); 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); 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; 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.timestamp = tokens[1].toDouble() * 1000000.0; 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) { return true; } //CRTD format from Mark Webb-Johnson / OVMS project /* Sample data in CRTD format 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() > 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); 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; for (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 (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; } //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; 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(); if (line.length() > 2) { QList tokens = line.split(','); if (tokens.length() >= 6) { if (tokens[0].length() > 3) { long long temp = tokens[0].right(10).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; if (fileVersion == 1) { thisFrame.isReceived = true; thisFrame.bus = tokens[3].toInt(); thisFrame.len = tokens[4].toInt(); for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; for (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].toInt(); 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] = 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 (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::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; 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 += 5; thisFrame.timestamp = timeStamp; thisFrame.ID = tokens[0].toInt(NULL, 16); if (thisFrame.ID > 0x7FF) thisFrame.extended = true; else thisFrame.extended = false; thisFrame.bus = 0; 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] = 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 (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, I believe x = extended) 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)*** ***