that were sent within the same window as received frames. Added a new scripting window that can be used to create scripts that send and receive messages at runtime - not done but sort of working.
1108 lines
36 KiB
C++
1108 lines
36 KiB
C++
#include "mainwindow.h"
|
|
#include "ui_mainwindow.h"
|
|
#include "can_structs.h"
|
|
#include <QDateTime>
|
|
#include <QFileDialog>
|
|
#include <QtSerialPort/QSerialPortInfo>
|
|
#include "canframemodel.h"
|
|
#include "utility.h"
|
|
#include "serialworker.h"
|
|
|
|
/*
|
|
Things that were planned for the GVRET-PC project but never completed.
|
|
|
|
Single / Multi state - The goal is to find bits that change based on toggles or discrete state items (shifters, etc)
|
|
|
|
Range state - Find things that range like accelerator pedal inputs, road speed, tach, etc
|
|
|
|
fuzzy scope - Try to find potential places where a given value might be stored - offer guesses and the program tries to find candidates for you
|
|
or, try to find things that appear to be multi-byte integers
|
|
*/
|
|
|
|
QString MainWindow::loadedFileName = "";
|
|
MainWindow *MainWindow::selfRef = NULL;
|
|
|
|
MainWindow *MainWindow::getReference()
|
|
{
|
|
return selfRef;
|
|
}
|
|
|
|
MainWindow::MainWindow(QWidget *parent) :
|
|
QMainWindow(parent),
|
|
ui(new Ui::MainWindow)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
useHex = true;
|
|
|
|
//These things are used by QSettings to set up setting storage
|
|
QCoreApplication::setOrganizationName("EVTV");
|
|
QCoreApplication::setOrganizationDomain("evtv.me");
|
|
QCoreApplication::setApplicationName("SavvyCAN");
|
|
|
|
selfRef = this;
|
|
|
|
this->setWindowTitle("Savvy CAN V" + QString::number(VERSION));
|
|
|
|
model = new CANFrameModel();
|
|
ui->canFramesView->setModel(model);
|
|
|
|
readSettings();
|
|
|
|
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, 275);
|
|
QHeaderView *HorzHdr = ui->canFramesView->horizontalHeader();
|
|
HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview
|
|
//enabling the below line kills performance in every way imaginable. Left here as a warning. Do not do this.
|
|
//ui->canFramesView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
|
|
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"));
|
|
|
|
worker = new SerialWorker(model);
|
|
worker->moveToThread(&serialWorkerThread);
|
|
connect(&serialWorkerThread, &QThread::finished, worker, &QObject::deleteLater);
|
|
connect(&serialWorkerThread, SIGNAL(started()), worker, SLOT(run())); //setup timers within the proper thread
|
|
connect(this, &MainWindow::sendSerialPort, worker, &SerialWorker::setSerialPort, Qt::QueuedConnection);
|
|
connect(worker, &SerialWorker::frameUpdateTick, this, &MainWindow::gotFrames, Qt::QueuedConnection);
|
|
connect(this, &MainWindow::updateBaudRates, worker, &SerialWorker::updateBaudRates, Qt::QueuedConnection);
|
|
connect(this, &MainWindow::sendCANFrame, worker, &SerialWorker::sendFrame, Qt::QueuedConnection);
|
|
connect(worker, &SerialWorker::connectionSuccess, this, &MainWindow::connectionSucceeded, Qt::QueuedConnection);
|
|
connect(worker, &SerialWorker::connectionFailure, this, &MainWindow::connectionFailed, Qt::QueuedConnection);
|
|
connect(worker, &SerialWorker::deviceInfo, this, &MainWindow::gotDeviceInfo, Qt::QueuedConnection);
|
|
connect(this, &MainWindow::closeSerialPort, worker, &SerialWorker::closeSerialPort, Qt::QueuedConnection);
|
|
connect(this, &MainWindow::startFrameCapturing, worker, &SerialWorker::startFrameCapture);
|
|
connect(this, &MainWindow::stopFrameCapturing, worker, &SerialWorker::stopFrameCapture);
|
|
connect(this, &MainWindow::settingsUpdated, worker, &SerialWorker::readSettings);
|
|
serialWorkerThread.start();
|
|
serialWorkerThread.setPriority(QThread::HighPriority);
|
|
|
|
graphingWindow = NULL;
|
|
frameInfoWindow = NULL;
|
|
playbackWindow = NULL;
|
|
flowViewWindow = NULL;
|
|
frameSenderWindow = NULL;
|
|
dbcMainEditor = NULL;
|
|
comparatorWindow = NULL;
|
|
settingsDialog = NULL;
|
|
firmwareUploaderWindow = NULL;
|
|
discreteStateWindow = NULL;
|
|
scriptingWindow = NULL;
|
|
dbcHandler = new DBCHandler;
|
|
bDirty = false;
|
|
inhibitFilterUpdate = false;
|
|
|
|
model->setDBCHandler(dbcHandler);
|
|
|
|
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->actionSave_Filtered_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveFilteredFile()));
|
|
connect(ui->actionLoad_Filter_Definition, SIGNAL(triggered(bool)), this, SLOT(handleLoadFilters()));
|
|
connect(ui->actionSave_Filter_Definition, SIGNAL(triggered(bool)), this, SLOT(handleSaveFilters()));
|
|
connect(ui->action_Playback, SIGNAL(triggered(bool)), this, SLOT(showPlaybackWindow()));
|
|
connect(ui->btnBaudSet, SIGNAL(clicked(bool)), this, SLOT(changeBaudRates()));
|
|
connect(ui->actionFlow_View, SIGNAL(triggered(bool)), this, SLOT(showFlowViewWindow()));
|
|
connect(ui->action_Custom, SIGNAL(triggered(bool)), this, SLOT(showFrameSenderWindow()));
|
|
connect(ui->actionLoad_DBC_File, SIGNAL(triggered(bool)), this, SLOT(handleLoadDBC()));
|
|
connect(ui->actionSave_DBC_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveDBC()));
|
|
connect(ui->canFramesView, SIGNAL(clicked(QModelIndex)), this, SLOT(gridClicked(QModelIndex)));
|
|
connect(ui->canFramesView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(gridDoubleClicked(QModelIndex)));
|
|
connect(ui->cbInterpret, SIGNAL(toggled(bool)), this, SLOT(interpretToggled(bool)));
|
|
connect(ui->cbOverwrite, SIGNAL(toggled(bool)), this, SLOT(overwriteToggled(bool)));
|
|
connect(ui->actionEdit_Messages_Signals, SIGNAL(triggered(bool)), this, SLOT(showDBCEditor()));
|
|
connect(ui->btnCaptureToggle, SIGNAL(clicked(bool)), this, SLOT(toggleCapture()));
|
|
connect(ui->actionExit_Application, SIGNAL(triggered(bool)), this, SLOT(exitApp()));
|
|
connect(ui->actionFuzzy_Scope, SIGNAL(triggered(bool)), this, SLOT(showFuzzyScopeWindow()));
|
|
connect(ui->actionRange_State, SIGNAL(triggered(bool)), this, SLOT(showRangeWindow()));
|
|
connect(ui->actionSave_Decoded_Frames, SIGNAL(triggered(bool)), this, SLOT(handleSaveDecoded()));
|
|
connect(ui->actionSingle_Multi_State, SIGNAL(triggered(bool)), this, SLOT(showSingleMultiWindow()));
|
|
connect(ui->actionFile_Comparison, SIGNAL(triggered(bool)), this, SLOT(showComparisonWindow()));
|
|
connect(ui->actionScripting_INterface, SIGNAL(triggered(bool)), this, SLOT(showScriptingWindow()));
|
|
connect(ui->btnNormalize, SIGNAL(clicked(bool)), this, SLOT(normalizeTiming()));
|
|
connect(ui->actionPreferences, SIGNAL(triggered(bool)), this, SLOT(showSettingsDialog()));
|
|
connect(model, SIGNAL(updatedFiltersList()), this, SLOT(updateFilterList()));
|
|
connect(ui->listFilters, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(filterListItemChanged(QListWidgetItem*)));
|
|
connect(ui->btnFilterAll, SIGNAL(clicked(bool)), this, SLOT(filterSetAll()));
|
|
connect(ui->btnFilterNone, SIGNAL(clicked(bool)), this, SLOT(filterClearAll()));
|
|
connect(ui->actionFirmware_Uploader, SIGNAL(triggered(bool)), this, SLOT(showFirmwareUploaderWindow()));
|
|
|
|
lbStatusConnected.setText(tr("Not connected"));
|
|
updateFileStatus();
|
|
lbStatusDatabase.setText(tr("No DBC database loaded"));
|
|
ui->statusBar->addWidget(&lbStatusConnected);
|
|
ui->statusBar->addWidget(&lbStatusFilename);
|
|
ui->statusBar->addWidget(&lbStatusDatabase);
|
|
|
|
ui->lbFPS->setText("0");
|
|
ui->lbNumFrames->setText("0");
|
|
|
|
isConnected = false;
|
|
allowCapture = true;
|
|
|
|
//create a temporary frame to be able to capture the correct
|
|
//default height of an item in the table. Need to do this in case
|
|
//of scaling or font differences between different computers.
|
|
CANFrame temp;
|
|
temp.bus = 0;
|
|
temp.ID = 0x100;
|
|
temp.len = 0;
|
|
model->addFrame(temp, true);
|
|
normalRowHeight = ui->canFramesView->rowHeight(0);
|
|
qDebug() << "normal row height = " << normalRowHeight;
|
|
model->clearFrames();
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
serialWorkerThread.quit();
|
|
serialWorkerThread.wait();
|
|
//delete worker;
|
|
|
|
if (graphingWindow)
|
|
{
|
|
graphingWindow->close();
|
|
delete graphingWindow;
|
|
}
|
|
|
|
if (frameInfoWindow)
|
|
{
|
|
frameInfoWindow->close();
|
|
delete frameInfoWindow;
|
|
}
|
|
|
|
if (playbackWindow)
|
|
{
|
|
playbackWindow->close();
|
|
delete playbackWindow;
|
|
}
|
|
|
|
if (flowViewWindow)
|
|
{
|
|
flowViewWindow->close();
|
|
delete flowViewWindow;
|
|
}
|
|
|
|
if (frameSenderWindow)
|
|
{
|
|
frameSenderWindow->close();
|
|
delete frameSenderWindow;
|
|
}
|
|
|
|
if (comparatorWindow)
|
|
{
|
|
comparatorWindow->close();
|
|
delete comparatorWindow;
|
|
}
|
|
|
|
if (dbcMainEditor)
|
|
{
|
|
dbcMainEditor->close();
|
|
delete dbcMainEditor;
|
|
}
|
|
|
|
if (settingsDialog)
|
|
{
|
|
settingsDialog->close();
|
|
delete settingsDialog;
|
|
}
|
|
|
|
if (discreteStateWindow)
|
|
{
|
|
discreteStateWindow->close();
|
|
delete discreteStateWindow;
|
|
}
|
|
|
|
if (scriptingWindow)
|
|
{
|
|
scriptingWindow->close();
|
|
delete scriptingWindow;
|
|
}
|
|
|
|
delete ui;
|
|
delete dbcHandler;
|
|
model->clearFrames();
|
|
delete model;
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
{
|
|
Q_UNUSED(event);
|
|
writeSettings();
|
|
exitApp();
|
|
}
|
|
|
|
void MainWindow::updateSettings()
|
|
{
|
|
readUpdateableSettings();
|
|
emit settingsUpdated();
|
|
}
|
|
|
|
void MainWindow::readSettings()
|
|
{
|
|
QSettings settings;
|
|
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
|
{
|
|
resize(settings.value("Main/WindowSize", QSize(800, 750)).toSize());
|
|
move(settings.value("Main/WindowPos", QPoint(100, 100)).toPoint());
|
|
}
|
|
if (settings.value("Main/AutoScroll", false).toBool())
|
|
{
|
|
ui->cbAutoScroll->setChecked(true);
|
|
}
|
|
readUpdateableSettings();
|
|
}
|
|
|
|
void MainWindow::readUpdateableSettings()
|
|
{
|
|
QSettings settings;
|
|
useHex = settings.value("Main/UseHex", true).toBool();
|
|
model->setHexMode(useHex);
|
|
Utility::decimalMode = !useHex;
|
|
secondsMode = settings.value("Main/TimeSeconds", false).toBool();
|
|
model->setSecondsMode(secondsMode);
|
|
useFiltered = settings.value("Main/UseFiltered", false).toBool();
|
|
}
|
|
|
|
void MainWindow::writeSettings()
|
|
{
|
|
QSettings settings;
|
|
|
|
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
|
{
|
|
settings.setValue("Main/WindowSize", size());
|
|
settings.setValue("Main/WindowPos", pos());
|
|
}
|
|
}
|
|
|
|
void MainWindow::gridClicked(QModelIndex idx)
|
|
{
|
|
if (ui->canFramesView->rowHeight(idx.row()) > normalRowHeight)
|
|
{
|
|
ui->canFramesView->setRowHeight(idx.row(), normalRowHeight);
|
|
}
|
|
else {
|
|
ui->canFramesView->resizeRowToContents(idx.row());
|
|
}
|
|
}
|
|
|
|
void MainWindow::gridDoubleClicked(QModelIndex idx)
|
|
{
|
|
//grab ID and timestamp and send them away
|
|
CANFrame frame = model->getListReference()->at(idx.row());
|
|
emit sendCenterTimeID(frame.ID, frame.timestamp / 1000000.0);
|
|
}
|
|
|
|
void MainWindow::interpretToggled(bool state)
|
|
{
|
|
model->setInterpetMode(state);
|
|
}
|
|
|
|
void MainWindow::overwriteToggled(bool state)
|
|
{
|
|
model->setOverwriteMode(state);
|
|
}
|
|
|
|
void MainWindow::updateFilterList()
|
|
{
|
|
if (model == NULL) return;
|
|
const QMap<int, bool> *filters = model->getFiltersReference();
|
|
if (filters == NULL) return;
|
|
|
|
qDebug() << "updateFilterList called on MainWindow";
|
|
|
|
ui->listFilters->clear();
|
|
|
|
if (filters->isEmpty()) return;
|
|
|
|
QMap<int, bool>::const_iterator filterIter;
|
|
for (filterIter = filters->begin(); filterIter != filters->end(); ++filterIter)
|
|
{
|
|
QListWidgetItem *thisItem = new QListWidgetItem();
|
|
thisItem->setText(Utility::formatNumber(filterIter.key()));
|
|
thisItem->setFlags(thisItem->flags() | Qt::ItemIsUserCheckable);
|
|
if (filterIter.value()) thisItem->setCheckState(Qt::Checked);
|
|
else thisItem->setCheckState(Qt::Unchecked);
|
|
ui->listFilters->addItem(thisItem);
|
|
}
|
|
}
|
|
|
|
void MainWindow::filterListItemChanged(QListWidgetItem *item)
|
|
{
|
|
if (inhibitFilterUpdate) return;
|
|
//qDebug() << item->text();
|
|
int ID;
|
|
bool isSet = false;
|
|
ID = Utility::ParseStringToNum(item->text());
|
|
if (item->checkState() == Qt::Checked) isSet = true;
|
|
|
|
model->setFilterState(ID, isSet);
|
|
}
|
|
|
|
void MainWindow::filterSetAll()
|
|
{
|
|
inhibitFilterUpdate = true;
|
|
for (int i = 0; i < ui->listFilters->count(); i++)
|
|
{
|
|
ui->listFilters->item(i)->setCheckState(Qt::Checked);
|
|
}
|
|
inhibitFilterUpdate = false;
|
|
model->setAllFilters(true);
|
|
}
|
|
|
|
void MainWindow::filterClearAll()
|
|
{
|
|
inhibitFilterUpdate = true;
|
|
for (int i = 0; i < ui->listFilters->count(); i++)
|
|
{
|
|
ui->listFilters->item(i)->setCheckState(Qt::Unchecked);
|
|
}
|
|
inhibitFilterUpdate = false;
|
|
model->setAllFilters(false);
|
|
}
|
|
|
|
//most of the work is handled elsewhere. Need only to update the # of frames
|
|
//and maybe auto scroll
|
|
void MainWindow::gotFrames(int FPS, int framesSinceLastUpdate)
|
|
{
|
|
ui->lbNumFrames->setText(QString::number(model->rowCount()));
|
|
if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom();
|
|
ui->lbFPS->setText(QString::number(FPS));
|
|
if (framesSinceLastUpdate > 0)
|
|
{
|
|
bDirty = true;
|
|
emit framesUpdated(framesSinceLastUpdate); //anyone care that frames were updated?
|
|
}
|
|
|
|
if (model->needsFilterRefresh()) updateFilterList();
|
|
}
|
|
|
|
void MainWindow::addFrameToDisplay(CANFrame &frame, bool autoRefresh = false)
|
|
{
|
|
model->addFrame(frame, autoRefresh);
|
|
if (autoRefresh)
|
|
{
|
|
if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom();
|
|
ui->lbNumFrames->setText(QString::number(model->rowCount()));
|
|
}
|
|
}
|
|
|
|
//A sub-window is sending us a center on timestamp and ID signal
|
|
//try to find the relevant frame in the list and focus on it.
|
|
void MainWindow::gotCenterTimeID(int32_t ID, double timestamp)
|
|
{
|
|
int idx = model->getIndexFromTimeID(ID, timestamp);
|
|
if (idx > -1)
|
|
{
|
|
ui->canFramesView->selectRow(idx);
|
|
}
|
|
}
|
|
|
|
void MainWindow::clearFrames()
|
|
{
|
|
ui->canFramesView->scrollToTop();
|
|
model->clearFrames();
|
|
ui->lbNumFrames->setText(QString::number(model->rowCount()));
|
|
bDirty = false;
|
|
loadedFileName = "";
|
|
updateFileStatus();
|
|
emit framesUpdated(-1);
|
|
}
|
|
|
|
void MainWindow::normalizeTiming()
|
|
{
|
|
model->normalizeTiming();
|
|
emit framesUpdated(-2); //claim an all new set of frames because every frame was updated.
|
|
}
|
|
|
|
void MainWindow::changeBaudRates()
|
|
{
|
|
int Speed1 = 0, Speed2 = 0;
|
|
|
|
Speed1 = ui->cbSpeed1->currentText().toInt();
|
|
Speed2 = ui->cbSpeed2->currentText().toInt();
|
|
|
|
emit updateBaudRates(Speed1, Speed2);
|
|
}
|
|
|
|
void MainWindow::handleLoadFile()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
bool result = false;
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("CRTD Logs (*.txt)")));
|
|
filters.append(QString(tr("GVRET Logs (*.csv)")));
|
|
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
|
|
filters.append(QString(tr("BusMaster Log (*.log)")));
|
|
filters.append(QString(tr("Microchip Log (*.can)")));
|
|
filters.append(QString(tr("Vector Trace Files (*.trace)")));
|
|
|
|
dialog.setFileMode(QFileDialog::ExistingFile);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
filename = dialog.selectedFiles()[0];
|
|
ui->canFramesView->scrollToTop();
|
|
model->clearFrames();
|
|
|
|
QVector<CANFrame> tempFrames;
|
|
|
|
QProgressDialog progress(this);
|
|
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 = FrameFileIO::loadCRTDFile(filename, &tempFrames);
|
|
if (dialog.selectedNameFilter() == filters[1]) result = FrameFileIO::loadNativeCSVFile(filename, &tempFrames);
|
|
if (dialog.selectedNameFilter() == filters[2]) result = FrameFileIO::loadGenericCSVFile(filename, &tempFrames);
|
|
if (dialog.selectedNameFilter() == filters[3]) result = FrameFileIO::loadLogFile(filename, &tempFrames);
|
|
if (dialog.selectedNameFilter() == filters[4]) result = FrameFileIO::loadMicrochipFile(filename, &tempFrames);
|
|
if (dialog.selectedNameFilter() == filters[5]) result = FrameFileIO::loadTraceFile(filename, &tempFrames);
|
|
|
|
progress.cancel();
|
|
|
|
if (result)
|
|
{
|
|
model->insertFrames(tempFrames);
|
|
|
|
QStringList fileList = filename.split('/');
|
|
loadedFileName = fileList[fileList.length() - 1];
|
|
|
|
model->recalcOverwrite();
|
|
ui->lbNumFrames->setText(QString::number(model->rowCount()));
|
|
if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom();
|
|
|
|
updateFileStatus();
|
|
emit framesUpdated(-2);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::handleSaveFile()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
bool result = false;
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("CRTD Logs (*.txt)")));
|
|
filters.append(QString(tr("GVRET Logs (*.csv)")));
|
|
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
|
|
filters.append(QString(tr("BusMaster Log (*.log)")));
|
|
filters.append(QString(tr("Microchip Log (*.can)")));
|
|
filters.append(QString(tr("Vector Trace Files (*.trace)")));
|
|
|
|
dialog.setFileMode(QFileDialog::AnyFile);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
const QVector<CANFrame> *frames = model->getListReference();
|
|
filename = dialog.selectedFiles()[0];
|
|
|
|
QProgressDialog progress(this);
|
|
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 += ".txt";
|
|
result = FrameFileIO::saveCRTDFile(filename, frames);
|
|
}
|
|
if (dialog.selectedNameFilter() == filters[1])
|
|
{
|
|
if (!filename.contains('.')) filename += ".csv";
|
|
result = FrameFileIO::saveNativeCSVFile(filename, frames);
|
|
}
|
|
if (dialog.selectedNameFilter() == filters[2])
|
|
{
|
|
if (!filename.contains('.')) filename += ".csv";
|
|
result = FrameFileIO::saveGenericCSVFile(filename, frames);
|
|
}
|
|
if (dialog.selectedNameFilter() == filters[3])
|
|
{
|
|
if (!filename.contains('.')) filename += ".log";
|
|
result = FrameFileIO::saveLogFile(filename, frames);
|
|
}
|
|
if (dialog.selectedNameFilter() == filters[4])
|
|
{
|
|
if (!filename.contains('.')) filename += ".log";
|
|
result = FrameFileIO::saveMicrochipFile(filename, frames);
|
|
}
|
|
|
|
if (dialog.selectedNameFilter() == filters[5])
|
|
{
|
|
if (!filename.contains('.')) filename += ".trace";
|
|
result = FrameFileIO::saveTraceFile(filename, frames);
|
|
}
|
|
|
|
progress.cancel();
|
|
|
|
if (result)
|
|
{
|
|
QStringList fileList = filename.split('/');
|
|
loadedFileName = fileList[fileList.length() - 1];
|
|
updateFileStatus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::handleSaveFilteredFile()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
bool result = false;
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("CRTD Logs (*.txt)")));
|
|
filters.append(QString(tr("GVRET Logs (*.csv)")));
|
|
filters.append(QString(tr("Generic ID/Data CSV (*.csv)")));
|
|
filters.append(QString(tr("BusMaster Log (*.log)")));
|
|
filters.append(QString(tr("Microchip Log (*.log)")));
|
|
filters.append(QString(tr("Vector Trace Files (*.trace)")));
|
|
|
|
dialog.setFileMode(QFileDialog::AnyFile);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
const QVector<CANFrame> *frames = model->getFilteredListReference();
|
|
filename = dialog.selectedFiles()[0];
|
|
|
|
QProgressDialog progress(this);
|
|
progress.setWindowModality(Qt::WindowModal);
|
|
progress.setLabelText("Saving filtered file...");
|
|
progress.setCancelButton(0);
|
|
progress.setRange(0,0);
|
|
progress.setMinimumDuration(0);
|
|
progress.show();
|
|
|
|
qApp->processEvents();
|
|
|
|
if (dialog.selectedNameFilter() == filters[0])
|
|
{
|
|
if (!filename.contains('.')) filename += ".txt";
|
|
result = FrameFileIO::saveCRTDFile(filename, frames);
|
|
}
|
|
if (dialog.selectedNameFilter() == filters[1])
|
|
{
|
|
if (!filename.contains('.')) filename += ".csv";
|
|
result = FrameFileIO::saveNativeCSVFile(filename, frames);
|
|
}
|
|
if (dialog.selectedNameFilter() == filters[2])
|
|
{
|
|
if (!filename.contains('.')) filename += ".csv";
|
|
result = FrameFileIO::saveGenericCSVFile(filename, frames);
|
|
}
|
|
if (dialog.selectedNameFilter() == filters[3])
|
|
{
|
|
if (!filename.contains('.')) filename += ".log";
|
|
result = FrameFileIO::saveLogFile(filename, frames);
|
|
}
|
|
if (dialog.selectedNameFilter() == filters[4])
|
|
{
|
|
if (!filename.contains('.')) filename += ".log";
|
|
result = FrameFileIO::saveMicrochipFile(filename, frames);
|
|
}
|
|
|
|
if (dialog.selectedNameFilter() == filters[5])
|
|
{
|
|
if (!filename.contains('.')) filename += ".trace";
|
|
result = FrameFileIO::saveTraceFile(filename, frames);
|
|
}
|
|
|
|
progress.cancel();
|
|
|
|
if (result)
|
|
{
|
|
QStringList fileList = filename.split('/');
|
|
loadedFileName = fileList[fileList.length() - 1];
|
|
updateFileStatus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::handleSaveFilters()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("Filter list (*.ftl)")));
|
|
|
|
dialog.setFileMode(QFileDialog::AnyFile);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
filename = dialog.selectedFiles()[0];
|
|
if (!filename.contains('.')) filename += ".ftl";
|
|
if (dialog.selectedNameFilter() == filters[0]) model->saveFilterFile(filename);
|
|
}
|
|
}
|
|
|
|
void MainWindow::handleLoadFilters()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("Filter List (*.ftl)")));
|
|
|
|
dialog.setFileMode(QFileDialog::ExistingFile);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
filename = dialog.selectedFiles()[0];
|
|
//right now there is only one file type that can be loaded here so just do it.
|
|
model->loadFilterFile(filename);
|
|
}
|
|
}
|
|
|
|
void MainWindow::handleLoadDBC()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("DBC File (*.dbc)")));
|
|
|
|
dialog.setFileMode(QFileDialog::ExistingFile);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
filename = dialog.selectedFiles()[0];
|
|
//right now there is only one file type that can be loaded here so just do it.
|
|
dbcHandler->loadDBCFile(filename);
|
|
//dbcHandler->listDebugging();
|
|
QStringList fileList = filename.split('/');
|
|
lbStatusDatabase.setText(fileList[fileList.length() - 1] + tr(" loaded."));
|
|
}
|
|
}
|
|
|
|
void MainWindow::handleSaveDBC()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("DBC File (*.dbc)")));
|
|
|
|
dialog.setFileMode(QFileDialog::AnyFile);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
filename = dialog.selectedFiles()[0];
|
|
if (!filename.contains('.')) filename += ".dbc";
|
|
dbcHandler->saveDBCFile(filename);
|
|
QStringList fileList = filename.split('/');
|
|
lbStatusDatabase.setText(fileList[fileList.length() - 1] + tr(" loaded."));
|
|
}
|
|
|
|
}
|
|
|
|
void MainWindow::handleSaveDecoded()
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("Text File (*.txt)")));
|
|
|
|
dialog.setFileMode(QFileDialog::AnyFile);
|
|
dialog.setNameFilters(filters);
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
filename = dialog.selectedFiles()[0];
|
|
if (!filename.contains('.')) filename += ".txt";
|
|
saveDecodedTextFile(filename);
|
|
}
|
|
}
|
|
|
|
void MainWindow::saveDecodedTextFile(QString filename)
|
|
{
|
|
QFile *outFile = new QFile(filename);
|
|
const QVector<CANFrame> *frames = model->getListReference();
|
|
|
|
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
|
|
return;
|
|
/*
|
|
Time: 205.173000 ID: 0x20E Std Bus: 0 Len: 8
|
|
Data Bytes: 88 10 00 13 BB 00 06 00
|
|
SignalName Value
|
|
*/
|
|
for (int c = 0; c < frames->count(); c++)
|
|
{
|
|
CANFrame thisFrame = frames->at(c);
|
|
QString builderString;
|
|
builderString += tr("Time: ") + QString::number((thisFrame.timestamp / 1000000.0), 'f', 6);
|
|
builderString += tr(" ID: ") + Utility::formatNumber(thisFrame.ID);
|
|
if (thisFrame.extended) builderString += tr(" Ext ");
|
|
else builderString += tr(" Std ");
|
|
builderString += tr("Bus: ") + QString::number(thisFrame.bus);
|
|
builderString += " Len: " + QString::number(thisFrame.len) + "\n";
|
|
outFile->write(builderString.toUtf8());
|
|
|
|
builderString = tr("Data Bytes: ");
|
|
for (int temp = 0; temp < thisFrame.len; temp++)
|
|
{
|
|
builderString += Utility::formatNumber(thisFrame.data[temp]) + " ";
|
|
}
|
|
builderString += "\n";
|
|
outFile->write(builderString.toUtf8());
|
|
|
|
builderString = "";
|
|
if (dbcHandler != NULL)
|
|
{
|
|
DBC_MESSAGE *msg = dbcHandler->findMsgByID(thisFrame.ID);
|
|
if (msg != NULL)
|
|
{
|
|
for (int j = 0; j < msg->msgSignals.length(); j++)
|
|
{
|
|
|
|
builderString.append("\t" + dbcHandler->processSignal(thisFrame, msg->msgSignals.at(j)));
|
|
builderString.append("\n");
|
|
}
|
|
}
|
|
builderString.append("\n");
|
|
outFile->write(builderString.toUtf8());
|
|
}
|
|
}
|
|
outFile->close();
|
|
}
|
|
|
|
void MainWindow::connButtonPress()
|
|
{
|
|
if (!isConnected)
|
|
{
|
|
for (int x = 0; x < ports.count(); x++)
|
|
{
|
|
if (ports.at(x).portName() == ui->cbSerialPorts->currentText())
|
|
{
|
|
emit sendSerialPort(&ports[x]);
|
|
lbStatusConnected.setText(tr("Attempting to connect to port ") + ports[x].portName());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
emit closeSerialPort();
|
|
isConnected = false;
|
|
lbStatusConnected.setText(tr("Not Connected"));
|
|
ui->btnConnect->setText(tr("Connect to GVRET"));
|
|
}
|
|
}
|
|
|
|
void MainWindow::toggleCapture()
|
|
{
|
|
allowCapture = !allowCapture;
|
|
if (allowCapture)
|
|
{
|
|
ui->btnCaptureToggle->setText("Suspend Capturing");
|
|
emit startFrameCapturing();
|
|
}
|
|
else
|
|
{
|
|
ui->btnCaptureToggle->setText("Restart Capturing");
|
|
emit stopFrameCapturing();
|
|
}
|
|
}
|
|
|
|
void MainWindow::connectionSucceeded(int baud0, int baud1)
|
|
{
|
|
lbStatusConnected.setText(tr("Connected to GVRET"));
|
|
//finally, find the baud rate in the list of rates or
|
|
//add it to the bottom if needed (that'll be weird though...
|
|
|
|
int idx = ui->cbSpeed1->findText(QString::number(baud0));
|
|
if (idx > -1) ui->cbSpeed1->setCurrentIndex(idx);
|
|
else
|
|
{
|
|
ui->cbSpeed1->addItem(QString::number(baud0));
|
|
ui->cbSpeed1->setCurrentIndex(ui->cbSpeed1->count() - 1);
|
|
}
|
|
|
|
idx = ui->cbSpeed2->findText(QString::number(baud1));
|
|
if (idx > -1) ui->cbSpeed2->setCurrentIndex(idx);
|
|
else
|
|
{
|
|
ui->cbSpeed2->addItem(QString::number(baud1));
|
|
ui->cbSpeed2->setCurrentIndex(ui->cbSpeed2->count() - 1);
|
|
}
|
|
//ui->btnConnect->setEnabled(false);
|
|
ui->btnConnect->setText(tr("Disconnect from GVRET"));
|
|
isConnected = true;
|
|
}
|
|
|
|
void MainWindow::connectionFailed()
|
|
{
|
|
lbStatusConnected.setText(tr("Failed to connect!"));
|
|
|
|
QMessageBox msgBox;
|
|
msgBox.setText("Connection to the GVRET firmware failed.\nYou might have an old version of GVRET\nor may have chosen the wrong serial port.");
|
|
msgBox.exec();
|
|
}
|
|
|
|
void MainWindow::updateFileStatus()
|
|
{
|
|
QString output;
|
|
if (model->rowCount() == 0)
|
|
{
|
|
output = tr("No file loaded");
|
|
}
|
|
else
|
|
{
|
|
if (loadedFileName.length() > 2)
|
|
{
|
|
output = loadedFileName + " loaded";
|
|
}
|
|
else
|
|
{
|
|
output = tr("No file loaded");
|
|
}
|
|
|
|
if (bDirty)
|
|
{
|
|
output += " (X)";
|
|
}
|
|
}
|
|
lbStatusFilename.setText(output);
|
|
}
|
|
|
|
void MainWindow::gotDeviceInfo(int build, int swCAN)
|
|
{
|
|
QString str = tr("Connected to GVRET ") + QString::number(build);
|
|
if (swCAN == 1) str += "(SW)";
|
|
lbStatusConnected.setText(str);
|
|
|
|
if (build < CURRENT_GVRET_VER)
|
|
{
|
|
QMessageBox msgBox;
|
|
msgBox.setText("It appears that you are not running\nan up-to-date version of GVRET\n\nYour version: "
|
|
+ QString::number(build) +"\nCurrent Version: " + QString::number(CURRENT_GVRET_VER)
|
|
+ "\n\nPlease upgrade your firmware version.\nOtherwise, you may experience difficulties.");
|
|
msgBox.exec();
|
|
}
|
|
}
|
|
|
|
void MainWindow::setTargettedID(int id)
|
|
{
|
|
worker->targetFrameID(id);
|
|
}
|
|
|
|
void MainWindow::showSettingsDialog()
|
|
{
|
|
if (!settingsDialog)
|
|
{
|
|
settingsDialog = new MainSettingsDialog();
|
|
connect(settingsDialog, SIGNAL(finished(int)), this, SLOT(updateSettings()));
|
|
}
|
|
settingsDialog->show();
|
|
}
|
|
|
|
//always gets unfiltered list. You ask for the graphs so there is no need to send filtered frames
|
|
void MainWindow::showGraphingWindow()
|
|
{
|
|
if (!graphingWindow) {
|
|
graphingWindow = new GraphingWindow(dbcHandler, model->getListReference());
|
|
connect(graphingWindow, SIGNAL(sendCenterTimeID(int32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double)));
|
|
connect(this, SIGNAL(sendCenterTimeID(int32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
}
|
|
|
|
if (flowViewWindow) //connect the two external windows together
|
|
{
|
|
connect(graphingWindow, SIGNAL(sendCenterTimeID(int32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
connect(flowViewWindow, SIGNAL(sendCenterTimeID(int32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
}
|
|
graphingWindow->show();
|
|
}
|
|
|
|
void MainWindow::showFrameDataAnalysis()
|
|
{
|
|
//only create an instance of the object if we dont have one. Otherwise just display the existing one.
|
|
if (!frameInfoWindow)
|
|
{
|
|
if (!useFiltered)
|
|
frameInfoWindow = new FrameInfoWindow(model->getListReference());
|
|
else
|
|
frameInfoWindow = new FrameInfoWindow(model->getFilteredListReference());
|
|
}
|
|
frameInfoWindow->show();
|
|
}
|
|
|
|
void MainWindow::showFrameSenderWindow()
|
|
{
|
|
if (!frameSenderWindow)
|
|
{
|
|
if (!useFiltered)
|
|
frameSenderWindow = new FrameSenderWindow(model->getListReference());
|
|
else
|
|
frameSenderWindow = new FrameSenderWindow(model->getFilteredListReference());
|
|
|
|
connect(frameSenderWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int)));
|
|
}
|
|
frameSenderWindow->show();
|
|
}
|
|
|
|
void MainWindow::showPlaybackWindow()
|
|
{
|
|
if (!playbackWindow)
|
|
{
|
|
if (!useFiltered)
|
|
playbackWindow = new FramePlaybackWindow(model->getListReference(), worker);
|
|
else
|
|
playbackWindow = new FramePlaybackWindow(model->getFilteredListReference(), worker);
|
|
}
|
|
playbackWindow->show();
|
|
}
|
|
|
|
void MainWindow::showFirmwareUploaderWindow()
|
|
{
|
|
if (!firmwareUploaderWindow)
|
|
{
|
|
firmwareUploaderWindow = new FirmwareUploaderWindow(model->getListReference());
|
|
connect(firmwareUploaderWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int)));
|
|
connect(worker, SIGNAL(gotTargettedFrame(int)), firmwareUploaderWindow, SLOT(gotTargettedFrame(int)));
|
|
}
|
|
firmwareUploaderWindow->show();
|
|
}
|
|
|
|
void MainWindow::showComparisonWindow()
|
|
{
|
|
if (!comparatorWindow)
|
|
{
|
|
comparatorWindow = new FileComparatorWindow();
|
|
}
|
|
comparatorWindow->show();
|
|
}
|
|
|
|
void MainWindow::showSingleMultiWindow()
|
|
{
|
|
if (!discreteStateWindow)
|
|
{
|
|
discreteStateWindow = new DiscreteStateWindow(model->getListReference());
|
|
}
|
|
discreteStateWindow->show();
|
|
}
|
|
|
|
void MainWindow::showScriptingWindow()
|
|
{
|
|
if (!scriptingWindow)
|
|
{
|
|
scriptingWindow = new ScriptingWindow(model->getListReference());
|
|
}
|
|
scriptingWindow->show();
|
|
}
|
|
|
|
void MainWindow::showRangeWindow()
|
|
{
|
|
//not done yet
|
|
}
|
|
|
|
void MainWindow::showFuzzyScopeWindow()
|
|
{
|
|
//not done yet
|
|
}
|
|
|
|
void MainWindow::exitApp()
|
|
{
|
|
if (graphingWindow) graphingWindow->close();
|
|
if (frameInfoWindow) frameInfoWindow->close();
|
|
if (playbackWindow) playbackWindow->close();
|
|
if (flowViewWindow) flowViewWindow->close();
|
|
if (frameSenderWindow) frameSenderWindow->close();
|
|
if (dbcMainEditor) dbcMainEditor->close();
|
|
if (comparatorWindow) comparatorWindow->close();
|
|
this->close();
|
|
}
|
|
|
|
void MainWindow::showFlowViewWindow()
|
|
{
|
|
if (!flowViewWindow)
|
|
{
|
|
if (!useFiltered)
|
|
flowViewWindow = new FlowViewWindow(model->getListReference());
|
|
else
|
|
flowViewWindow = new FlowViewWindow(model->getFilteredListReference());
|
|
connect(flowViewWindow, SIGNAL(sendCenterTimeID(int32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double)));
|
|
connect(this, SIGNAL(sendCenterTimeID(int32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
}
|
|
|
|
if (graphingWindow)
|
|
{
|
|
connect(graphingWindow, SIGNAL(sendCenterTimeID(int32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
connect(flowViewWindow, SIGNAL(sendCenterTimeID(int32_t,double)), graphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
}
|
|
|
|
flowViewWindow->show();
|
|
}
|
|
|
|
//this one always gets the unfiltered list intentionally.
|
|
void MainWindow::showDBCEditor()
|
|
{
|
|
if (!dbcMainEditor)
|
|
{
|
|
dbcMainEditor = new DBCMainEditor(dbcHandler, model->getListReference());
|
|
}
|
|
dbcMainEditor->show();
|
|
}
|