#include "mainwindow.h" #include "ui_mainwindow.h" #include "can_structs.h" #include #include #include #include "canframemodel.h" #include "utility.h" #include "serialworker.h" /* This first order of business is to attempt to gain feature parity (roughly) with GVRET-PC so that this can replace it as a cross platform solution. Here are things yet to do - Add flow view - ability to see bits change over time - Ability to send frames via playback mechanism (people use this so get it working!) - Ability to use programmatic frame sending interface (only I use it and it's complicated but helpful) */ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); model = new CANFrameModel(); ui->canFramesView->setModel(model); QStringList headers; headers << "Timestamp" << "ID" << "Ext" << "Bus" << "Len" << "Data"; //model->setHorizontalHeaderLabels(headers); ui->canFramesView->setColumnWidth(0, 110); ui->canFramesView->setColumnWidth(1, 70); ui->canFramesView->setColumnWidth(2, 40); ui->canFramesView->setColumnWidth(3, 40); ui->canFramesView->setColumnWidth(4, 40); ui->canFramesView->setColumnWidth(5, 300); QList ports = QSerialPortInfo::availablePorts(); for (int i = 0; i < ports.count(); i++) { ui->cbSerialPorts->addItem(ports[i].portName()); } ui->cbSpeed1->addItem(tr("Disabled")); ui->cbSpeed1->addItem(tr("125000")); ui->cbSpeed1->addItem(tr("250000")); ui->cbSpeed1->addItem(tr("500000")); ui->cbSpeed1->addItem(tr("1000000")); ui->cbSpeed1->addItem(tr("33333")); ui->cbSpeed2->addItem(tr("Disabled")); ui->cbSpeed2->addItem(tr("125000")); ui->cbSpeed2->addItem(tr("250000")); ui->cbSpeed2->addItem(tr("500000")); ui->cbSpeed2->addItem(tr("1000000")); ui->cbSpeed2->addItem(tr("33333")); SerialWorker *worker = new SerialWorker(); worker->moveToThread(&serialWorkerThread); connect(&serialWorkerThread, &QThread::finished, worker, &QObject::deleteLater); connect(this, &MainWindow::sendSerialPort, worker, &SerialWorker::setSerialPort, Qt::QueuedConnection); connect(worker, &SerialWorker::receivedFrame, this, &MainWindow::gotFrame, Qt::QueuedConnection); connect(this, &MainWindow::updateBaudRates, worker, &SerialWorker::updateBaudRates, Qt::QueuedConnection); connect(this, &MainWindow::sendCANFrame, worker, &SerialWorker::sendFrame, Qt::QueuedConnection); serialWorkerThread.start(); graphingWindow = NULL; frameInfoWindow = NULL; playbackWindow = NULL; connect(ui->btnConnect, SIGNAL(clicked(bool)), this, SLOT(connButtonPress())); connect(ui->actionOpen_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleLoadFile())); connect(ui->actionGraph_Dta, SIGNAL(triggered(bool)), this, SLOT(showGraphingWindow())); connect(ui->actionFrame_Data_Analysis, SIGNAL(triggered(bool)), this, SLOT(showFrameDataAnalysis())); connect(ui->btnClearFrames, SIGNAL(clicked(bool)), this, SLOT(clearFrames())); connect(ui->actionSave_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveFile())); connect(ui->action_Playback, SIGNAL(triggered(bool)), this, SLOT(showPlaybackWindow())); connect(ui->btnBaudSet, SIGNAL(clicked(bool)), this, SLOT(changeBaudRates())); } MainWindow::~MainWindow() { delete ui; if (graphingWindow) delete graphingWindow; serialWorkerThread.quit(); serialWorkerThread.wait(); } void MainWindow::gotFrame(CANFrame *frame) { qDebug() << "got frame from serial side. ID was " << frame->ID; addFrameToDisplay(*frame, true); ui->lbNumFrames->setText(QString::number(model->rowCount())); } void MainWindow::addFrameToDisplay(CANFrame &frame, bool autoRefresh = false) { model->addFrame(frame, autoRefresh); if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); if (autoRefresh) { ui->lbNumFrames->setText(QString::number(model->rowCount())); } } void MainWindow::clearFrames() { ui->canFramesView->scrollToTop(); model->clearFrames(); ui->lbNumFrames->setText(QString::number(model->rowCount())); } void MainWindow::changeBaudRates() { int Speed1 = 0, Speed2 = 0; switch (ui->cbSpeed1->currentIndex()) { case 0: //disable break; case 1: Speed1 = 125000; break; case 2: Speed1 = 250000; break; case 3: Speed1 = 500000; break; case 4: Speed1 = 1000000; break; case 5: Speed1 = 33333; break; } switch (ui->cbSpeed2->currentIndex()) { case 0: //disable break; case 1: Speed2 = 125000; break; case 2: Speed2 = 250000; break; case 3: Speed2 = 500000; break; case 4: Speed2 = 1000000; break; case 5: Speed2 = 33333; break; } emit updateBaudRates(Speed1, Speed2); } //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 */ void MainWindow::loadCRTDFile(QString filename) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) return; line = inFile->readLine(); //read out the header first and discard it. while (!inFile->atEnd()) { line = inFile->readLine(); if (line.length() > 2) { QList tokens = line.split(' '); thisFrame.timestamp = (int64_t)(tokens[0].toDouble() * 1000); if (tokens[1] == "R11" || tokens[1] == "R29") { thisFrame.ID = tokens[2].toInt(NULL, 16); if (tokens[1] == "R29") thisFrame.extended = true; else thisFrame.extended = false; 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; } addFrameToDisplay(thisFrame); } } } inFile->close(); model->sendRefresh(); ui->lbNumFrames->setText(QString::number(model->rowCount())); if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); } void MainWindow::saveCRTDFile(QString filename) { QFile *outFile = new QFile(filename); QList *frames = model->getListReference(); if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) return; outFile->write(QString::number(frames->at(0).timestamp / 1000.0).toUtf8() + tr(" CXX GVRET-PC Reverse Engineering Tool Output").toUtf8()); outFile->write("\n"); for (int c = 0; c < frames->count(); c++) { outFile->write(QString::number(frames->at(c).timestamp).toUtf8()); outFile->putChar(' '); if (frames->at(c).extended) { outFile->write("R29 "); } else outFile->write("R11 "); 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(); } //The "native" file format for this program void MainWindow::loadNativeCSVFile(QString filename) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; long long timeStamp = Utility::GetTimeMS(); if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) return; line = inFile->readLine(); //read out the header first and discard it. while (!inFile->atEnd()) { line = inFile->readLine(); if (line.length() > 2) { QList tokens = line.split(','); if (tokens[0].length() > 10) { 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].contains("True")) thisFrame.extended = 1; else thisFrame.extended = 0; 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); addFrameToDisplay(thisFrame); } } inFile->close(); model->sendRefresh(); ui->lbNumFrames->setText(QString::number(model->rowCount())); if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); } void MainWindow::saveNativeCSVFile(QString filename) { QFile *outFile = new QFile(filename); QList *frames = model->getListReference(); if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) return; outFile->write("Time Stamp,ID,Extended,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8"); outFile->write("\n"); for (int c = 0; c < frames->count(); c++) { 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,"); 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++) { outFile->write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8()); outFile->putChar(44); } outFile->write("\n"); } outFile->close(); } void MainWindow::loadGenericCSVFile(QString filename) { QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; long long timeStamp = Utility::GetTimeMS(); if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) return; line = inFile->readLine(); //read out the header first and discard it. while (!inFile->atEnd()) { 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); addFrameToDisplay(thisFrame); } } inFile->close(); model->sendRefresh(); ui->lbNumFrames->setText(QString::number(model->rowCount())); if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); } void MainWindow::saveGenericCSVFile(QString filename) { } //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)*** ***