Files
giesCANne/mainwindow.cpp
T
Kanstantsin Shautsou 6eebfd7089 Add CARBUS Analyzer file load.
- Generic CSV parses file without failures, so CARBUS must be higher.
- File may be loaded but will have no packets. Adjusted UI message for this case.
- Obviously if we got CANHACKER header, then file is already detected and there is no need to parse.
2020-09-26 03:45:19 +03:00

1075 lines
35 KiB
C++

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "can_structs.h"
#include <QDateTime>
#include <QFileDialog>
#include <QtSerialPort/QSerialPortInfo>
#include "connections/canconmanager.h"
#include "connections/connectionwindow.h"
#include "helpwindow.h"
#include "utility.h"
#include "filterutility.h"
/*
Some notes on things I'd like to put into the program but haven't put on github (yet)
Allow scripts to read/write signals from DBC files
allow scripts to load DBC files in support of the script - maybe the graphing system too.
*/
QString MainWindow::loadedFileName = "";
MainWindow *MainWindow::selfRef = nullptr;
MainWindow *MainWindow::getReference()
{
return selfRef;
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qRegisterMetaTypeStreamOperators<QVector<QString>>();
qRegisterMetaTypeStreamOperators<QVector<int>>();
useHex = true;
selfRef = this;
this->setWindowTitle("Savvy CAN V" + QString::number(VERSION));
model = new CANFrameModel(this); // set parent to mainwindow to prevent canframemodel to change thread (might be done by setModel but just in case)
QSortFilterProxyModel* proxyModel = new QSortFilterProxyModel;
proxyModel->setSourceModel(model);
ui->canFramesView->setModel(proxyModel);
settingsDialog = new MainSettingsDialog(); //instantiate the settings dialog so it can initialize settings if this is the first run or the config file was deleted.
settingsDialog->updateSettings(); //write out all the settings. If this is the first run it'll write defaults out.
readSettings();
//QHeaderView *verticalHeader = ui->canFramesView->verticalHeader();
//verticalHeader->setSectionResizeMode(QHeaderView::Fixed);
//verticalHeader->setDefaultSectionSize(10);
QHeaderView *HorzHdr = ui->canFramesView->horizontalHeader();
HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview
connect(HorzHdr, SIGNAL(sectionClicked(int)), this, SLOT(headerClicked(int)));
graphingWindow = nullptr;
frameInfoWindow = nullptr;
playbackWindow = nullptr;
flowViewWindow = nullptr;
frameSenderWindow = nullptr;
dbcMainEditor = nullptr;
comparatorWindow = nullptr;
settingsDialog = nullptr;
firmwareUploaderWindow = nullptr;
discreteStateWindow = nullptr;
connectionWindow = nullptr;
scriptingWindow = nullptr;
rangeWindow = nullptr;
dbcFileWindow = nullptr;
fuzzingWindow = nullptr;
udsScanWindow = nullptr;
motorctrlConfigWindow = nullptr;
isoWindow = nullptr;
snifferWindow = nullptr;
bisectWindow = nullptr;
signalViewerWindow = nullptr;
temporalGraphWindow = nullptr;
dbcHandler = DBCHandler::getReference();
bDirty = false;
inhibitFilterUpdate = false;
rxFrames = 0;
framesPerSec = 0;
continuousLogging = false;
continuousLogFlushCounter = 0;
connect(ui->actionSetup, SIGNAL(triggered(bool)), SLOT(showConnectionSettingsWindow()));
connect(ui->actionOpen_Log_File, &QAction::triggered, this, &MainWindow::handleLoadFile);
connect(ui->actionGraph_Dta, &QAction::triggered, this, &MainWindow::showGraphingWindow);
connect(ui->actionFrame_Data_Analysis, &QAction::triggered, this, &MainWindow::showFrameDataAnalysis);
connect(ui->btnClearFrames, &QAbstractButton::clicked, this, &MainWindow::clearFrames);
connect(ui->actionSave_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFile);
connect(ui->actionSave_Filtered_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFilteredFile);
connect(ui->actionLoad_Filter_Definition, &QAction::triggered, this, &MainWindow::handleLoadFilters);
connect(ui->actionSave_Filter_Definition, &QAction::triggered, this, &MainWindow::handleSaveFilters);
connect(ui->action_Playback, &QAction::triggered, this, &MainWindow::showPlaybackWindow);
connect(ui->actionFlow_View, &QAction::triggered, this, &MainWindow::showFlowViewWindow);
connect(ui->action_Custom, &QAction::triggered, this, &MainWindow::showFrameSenderWindow);
connect(ui->canFramesView, &QAbstractItemView::pressed, this, &MainWindow::gridClicked);
connect(ui->canFramesView, &QAbstractItemView::doubleClicked, this, &MainWindow::gridDoubleClicked);
connect(ui->cbInterpret, &QAbstractButton::toggled, this, &MainWindow::interpretToggled);
connect(ui->cbOverwrite, &QAbstractButton::toggled, this, &MainWindow::overwriteToggled);
connect(ui->btnCaptureToggle, &QAbstractButton::clicked, this, &MainWindow::toggleCapture);
connect(ui->actionExit_Application, &QAction::triggered, this, &MainWindow::exitApp);
connect(ui->actionFuzzy_Scope, &QAction::triggered, this, &MainWindow::showFuzzyScopeWindow);
connect(ui->actionRange_State_2, &QAction::triggered, this, &MainWindow::showRangeWindow);
connect(ui->actionSave_Decoded_Frames, &QAction::triggered, this, &MainWindow::handleSaveDecoded);
connect(ui->actionSingle_Multi_State_2, &QAction::triggered, this, &MainWindow::showSingleMultiWindow);
connect(ui->actionFile_Comparison, &QAction::triggered, this, &MainWindow::showComparisonWindow);
connect(ui->actionScripting_INterface, &QAction::triggered, this, &MainWindow::showScriptingWindow);
connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming);
connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::showSettingsDialog);
connect(model, &CANFrameModel::updatedFiltersList, this, &MainWindow::updateFilterList);
connect(ui->listFilters, &QListWidget::itemChanged, this, &MainWindow::filterListItemChanged);
connect(ui->btnFilterAll, &QAbstractButton::clicked, this, &MainWindow::filterSetAll);
connect(ui->btnFilterNone, &QAbstractButton::clicked, this, &MainWindow::filterClearAll);
connect(ui->actionFirmware_Update, &QAction::triggered, this, &MainWindow::showFirmwareUploaderWindow);
connect(ui->actionDBC_File_Manager, &QAction::triggered, this, &MainWindow::showDBCFileWindow);
connect(ui->actionFuzzing, &QAction::triggered, this, &MainWindow::showFuzzingWindow);
connect(ui->actionUDS_Scanner, &QAction::triggered, this, &MainWindow::showUDSScanWindow);
connect(ui->actionISO_TP_Decoder, &QAction::triggered, this, &MainWindow::showISOInterpreterWindow);
connect(ui->actionSniffer, &QAction::triggered, this, &MainWindow::showSnifferWindow);
connect(ui->actionMotorControlConfig, &QAction::triggered, this, &MainWindow::showMCConfigWindow);
connect(ui->actionCapture_Bisector, &QAction::triggered, this, &MainWindow::showBisectWindow);
connect(ui->actionSignal_Viewer, &QAction::triggered, this, &MainWindow::showSignalViewer);
connect(ui->actionSave_Continuous_Logfile, &QAction::triggered, this, &MainWindow::handleContinousLogging);
connect(ui->actionTemporal_Graph, &QAction::triggered, this, &MainWindow::showTemporalGraphWindow);
connect(CANConManager::getInstance(), &CANConManager::framesReceived, model, &CANFrameModel::addFrames);
lbStatusConnected.setText(tr("Connected to 0 buses"));
lbHelp.setText(tr("Press F1 on any screen for help"));
lbHelp.setAlignment(Qt::AlignCenter);
QFont boldFont;
boldFont.setBold(true);
lbHelp.setFont(boldFont);
updateFileStatus();
//lbStatusDatabase.setText(tr("No DBC database loaded"));
ui->statusBar->insertWidget(0, &lbStatusConnected, 1);
ui->statusBar->insertWidget(1, &lbStatusFilename, 1);
ui->statusBar->insertWidget(2, &lbHelp, 1);
//ui->statusBar->addWidget(&lbStatusDatabase);
ui->lblRemoteConn->setVisible(false);
ui->lineRemoteKey->setVisible(false);
ui->lbFPS->setText("0");
ui->lbNumFrames->setText("0");
// Prevent annoying accidental horizontal scrolling when filter list is populated with long interpreted message names
ui->listFilters->horizontalScrollBar()->setEnabled(false);
connect(&updateTimer, &QTimer::timeout, this, &MainWindow::tickGUIUpdate);
updateTimer.setInterval(250);
updateTimer.start();
elapsedTime = new QTime;
elapsedTime->start();
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;
temp.extended = false;
temp.isReceived = true;
temp.remote = false;
temp.timestamp = 100000000;
model->addFrame(temp, true);
qApp->processEvents();
tickGUIUpdate(); //force a GUI refresh so that the row exists to measure
normalRowHeight = ui->canFramesView->rowHeight(0);
if (normalRowHeight == 0) normalRowHeight = 30; //should not be necessary but provides a sane number if something stupid happened.
qDebug() << "normal row height = " << normalRowHeight;
model->clearFrames();
//connect(CANConManager::getInstance(), CANConManager::connectionStatusUpdated, this, MainWindow::connectionStatusUpdated);
connect(CANConManager::getInstance(), SIGNAL(connectionStatusUpdated(int)), this, SLOT(connectionStatusUpdated(int)));
//Automatically create the connection window so it can be updated even if we never opened it.
connectionWindow = new ConnectionWindow();
connect(this, SIGNAL(suspendCapturing(bool)), connectionWindow, SLOT(setSuspendAll(bool)));
//these either are unfinished/not working or are not for general use. But,they exist
//so if you want to enable them and play with them then go for it.
ui->actionFirmware_Update->setVisible(false);
ui->actionMotorControlConfig->setVisible(false);
ui->actionSingle_Multi_State_2->setVisible(false);
installEventFilter(this);
}
MainWindow::~MainWindow()
{
updateTimer.stop();
killEmAll(); //Ride the lightning
delete ui;
delete model;
delete elapsedTime;
delete dbcHandler;
}
//kill every sub window that could be open. At the moment a hard coded list
//but eventually each window should be registered and be able to be iterated.
void MainWindow::killEmAll()
{
killWindow(graphingWindow);
killWindow(frameInfoWindow);
killWindow(playbackWindow);
killWindow(flowViewWindow);
killWindow(frameSenderWindow);
killWindow(comparatorWindow);
killWindow(dbcMainEditor);
killWindow(settingsDialog);
killWindow(discreteStateWindow);
killWindow(scriptingWindow);
killWindow(rangeWindow);
killWindow(dbcFileWindow);
killWindow(fuzzingWindow);
killWindow(udsScanWindow);
killWindow(isoWindow);
killWindow(snifferWindow);
killWindow(bisectWindow);
killWindow(firmwareUploaderWindow);
killWindow(motorctrlConfigWindow);
killWindow(signalViewerWindow);
killWindow(connectionWindow);
killWindow(temporalGraphWindow);
}
//forcefully close the window, kill it, and salt the earth
//note, for some stupid reason this function causes a seg fault
//it seems that when it runs just before the program closes it'll
//fault out when trying to close the connection window. I assume
//this could be because that window has long running threads open and doesn't
//close quickly or maybe cleanly. Investigate.
void MainWindow::killWindow(QDialog *win)
{
if (win)
{
win->close();
delete win;
win = nullptr;
}
}
void MainWindow::exitApp()
{
this->close();
QApplication::quit(); //forces the whole application to terminate when the main window is closed
}
void MainWindow::closeEvent(QCloseEvent *event)
{
Q_UNUSED(event);
removeEventFilter(this);
writeSettings();
exitApp();
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyRelease) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch (keyEvent->key())
{
case Qt::Key_F1:
HelpWindow::getRef()->showHelp("mainscreen.html");
return true;
break;
}
return false;
} else {
// standard event processing
return QObject::eventFilter(obj, event);
}
return false;
}
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());
ui->canFramesView->setColumnWidth(0, settings.value("Main/TimeColumn", 150).toUInt()); //time stamp
ui->canFramesView->setColumnWidth(1, settings.value("Main/IDColumn", 70).toUInt()); //frame ID
ui->canFramesView->setColumnWidth(2, settings.value("Main/ExtColumn", 40).toUInt()); //extended
ui->canFramesView->setColumnWidth(3, settings.value("Main/RemColumn", 40).toUInt()); //remote
ui->canFramesView->setColumnWidth(4, settings.value("Main/DirColumn", 40).toUInt()); //direction
ui->canFramesView->setColumnWidth(5, settings.value("Main/BusColumn", 40).toUInt()); //bus
ui->canFramesView->setColumnWidth(6, settings.value("Main/LengthColumn", 40).toUInt()); //length
ui->canFramesView->setColumnWidth(7, settings.value("Main/AsciiColumn", 50).toUInt()); //ascii
//ui->canFramesView->setColumnWidth(8, settings.value("Main/DataColumn", 225).toUInt()); //data
}
if (settings.value("Main/AutoScroll", false).toBool())
{
ui->cbAutoScroll->setChecked(true);
}
int fontSize = settings.value("Main/FontSize", 9).toUInt();
QFont newFont = QFont(ui->canFramesView->font());
newFont.setPointSize(fontSize);
ui->canFramesView->setFont(newFont);
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);
useSystemClock = settings.value("Main/TimeClock", false).toBool();
model->setSysTimeMode(useSystemClock);
useFiltered = settings.value("Main/UseFiltered", false).toBool();
model->setTimeFormat(settings.value("Main/TimeFormat", "MMM-dd HH:mm:ss.zzz").toString());
if (settings.value("Main/FilterLabeling", false).toBool())
ui->listFilters->setMaximumWidth(250);
else
ui->listFilters->setMaximumWidth(175);
updateFilterList();
}
void MainWindow::writeSettings()
{
QSettings settings;
if (settings.value("Main/SaveRestorePositions", false).toBool())
{
settings.setValue("Main/WindowSize", size());
settings.setValue("Main/WindowPos", pos());
settings.setValue("Main/TimeColumn", ui->canFramesView->columnWidth(0));
settings.setValue("Main/IDColumn", ui->canFramesView->columnWidth(1));
settings.setValue("Main/ExtColumn", ui->canFramesView->columnWidth(2));
settings.setValue("Main/RemColumn", ui->canFramesView->columnWidth(3));
settings.setValue("Main/DirColumn", ui->canFramesView->columnWidth(4));
settings.setValue("Main/BusColumn", ui->canFramesView->columnWidth(5));
settings.setValue("Main/LengthColumn", ui->canFramesView->columnWidth(6));
settings.setValue("Main/AsciiColumn", ui->canFramesView->columnWidth(7));
//settings.setValue("Main/DataColumn", ui->canFramesView->columnWidth(8));
}
}
void MainWindow::updateConnectionSettings(QString connectionType, QString port, int speed0, int speed1)
{
Q_UNUSED(connectionType);
Q_UNUSED(port);
Q_UNUSED(speed0);
Q_UNUSED(speed1);
//connType = connectionType;
//portName = port;
//canSpeed0 = speed0;
//canSpeed1 = speed1;
if (isConnected)
{
//emit updateBaudRates(speed0, speed1);
}
}
void MainWindow::headerClicked(int logicalIndex)
{
//ui->canFramesView->sortByColumn(logicalIndex);
model->sortByColumn(logicalIndex);
}
void MainWindow::gridClicked(QModelIndex idx)
{
//qDebug() << "Grid Clicked";
if (ui->canFramesView->rowHeight(idx.row()) > normalRowHeight)
{
ui->canFramesView->setRowHeight(idx.row(), normalRowHeight);
}
else {
ui->canFramesView->resizeRowToContents(idx.row());
}
}
void MainWindow::gridDoubleClicked(QModelIndex idx)
{
//qDebug() << "Grid double clicked";
//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->setInterpretMode(state);
//ui->canFramesView->resizeRowsToContents(); //a VERY costly operation!
}
void MainWindow::overwriteToggled(bool state)
{
if (state)
{
QMessageBox::StandardButton confirmDialog;
confirmDialog = QMessageBox::question(this, "Danger Will Robinson", "Enabling Overwrite mode will\ndelete your captured frames\nand replace them with one\nframe per ID.\n\nAre you ready to do that?",
QMessageBox::Yes|QMessageBox::No);
if (confirmDialog == QMessageBox::Yes)
{
model->setOverwriteMode(true);
}
else ui->cbOverwrite->setCheckState(Qt::Unchecked);
}
else
{
model->setOverwriteMode(false);
}
}
void MainWindow::updateFilterList()
{
if (model == nullptr) return;
const QMap<int, bool> *filters = model->getFiltersReference();
if (filters == nullptr) return;
qDebug() << "updateFilterList called on MainWindow";
inhibitFilterUpdate = true;
ui->listFilters->clear();
if (filters->isEmpty()) return;
QMap<int, bool>::const_iterator filterIter;
for (filterIter = filters->begin(); filterIter != filters->end(); ++filterIter)
{
QListWidgetItem *thisItem = FilterUtility::createCheckableFilterItem(filterIter.key(), filterIter.value(), ui->listFilters);
}
inhibitFilterUpdate = false;
}
void MainWindow::filterListItemChanged(QListWidgetItem *item)
{
if (inhibitFilterUpdate) return;
//qDebug() << item->text();
// strip away possible filter label
int ID = FilterUtility::getIdAsInt(item);
bool isSet = false;
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);
}
void MainWindow::tickGUIUpdate()
{
rxFrames = model->sendBulkRefresh();
//if(rxFrames>0)
//{
int elapsed = elapsedTime->elapsed();
if(elapsed) {
framesPerSec = (framesPerSec + (rxFrames * 1000 / elapsed)) / 2;
elapsedTime->restart();
}
else
framesPerSec = 0;
ui->lbNumFrames->setText(QString::number(model->rowCount()));
if (rxFrames > 0 && /*allowCapture && */ ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom();
ui->lbFPS->setText(QString::number(framesPerSec));
if (rxFrames > 0)
{
bDirty = true;
emit framesUpdated(rxFrames); //anyone care that frames were updated?
}
if (model->needsFilterRefresh()) updateFilterList();
if (continuousLogging)
{
const QVector<CANFrame> *modelFrames = model->getListReference();
FrameFileIO::writeContinuousNative(modelFrames, modelFrames->count() - rxFrames);
continuousLogFlushCounter++;
if ((continuousLogFlushCounter % 3) == 0)
{
if (ui->lblContMsg->text().length() > 2)
{
ui->lblContMsg->setText("");
}
else
{
ui->lblContMsg->setText("CONTINUOUS LOGGING");
}
}
if (continuousLogFlushCounter > 8)
{
continuousLogFlushCounter = 0;
FrameFileIO::flushContinuousNative();
}
}
rxFrames = 0;
//}
}
void MainWindow::gotFrames(int framesSinceLastUpdate)
{
rxFrames += framesSinceLastUpdate;
emit frameUpdateRapid(framesSinceLastUpdate);
}
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();
CANConManager::getInstance()->resetTimeBasis();
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::handleLoadFile()
{
QString filename;
QVector<CANFrame> tempFrames;
QMessageBox::StandardButton confirmDialog;
bool loadResult = FrameFileIO::loadFrameFile(filename, &tempFrames);
if (!loadResult)
{
confirmDialog = QMessageBox::question(this, "Error Loading", "Do you want to salvage what could be loaded?",
QMessageBox::Yes|QMessageBox::No);
if (confirmDialog == QMessageBox::Yes) {
loadResult = true;
}
}
if (loadResult)
{
ui->canFramesView->scrollToTop();
model->clearFrames();
model->insertFrames(tempFrames);
loadedFileName = filename;
model->recalcOverwrite();
ui->lbNumFrames->setText(QString::number(model->rowCount()));
if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom();
updateFileStatus();
emit framesUpdated(-1);
}
}
void MainWindow::handleSaveFile()
{
QString filename;
if (FrameFileIO::saveFrameFile(filename, model->getListReference()))
{
loadedFileName = filename;
updateFileStatus();
}
}
void MainWindow::handleContinousLogging()
{
continuousLogging = !continuousLogging;
if (continuousLogging)
{
ui->actionSave_Continuous_Logfile->setText(tr("Cease Continuous Logging"));
FrameFileIO::openContinuousNative();
}
else
{
ui->actionSave_Continuous_Logfile->setText(tr("Start Continuous Logging"));
ui->lblContMsg->setText("");
FrameFileIO::closeContinuousNative();
}
}
void MainWindow::handleSaveFilteredFile()
{
QString filename;
if (FrameFileIO::saveFrameFile(filename, model->getFilteredListReference()))
{
loadedFileName = filename;
updateFileStatus();
}
}
void MainWindow::handleSaveFilters()
{
QString filename;
QFileDialog dialog(this);
QSettings settings;
QStringList filters;
filters.append(QString(tr("Filter list (*.ftl)")));
dialog.setDirectory(settings.value("Filters/LoadSaveDirectory", dialog.directory().path()).toString());
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
if (!filename.contains('.')) filename += ".ftl";
if (dialog.selectedNameFilter() == filters[0]) model->saveFilterFile(filename);
settings.setValue("Filters/LoadSaveDirectory", dialog.directory().path());
}
}
void MainWindow::handleLoadFilters()
{
QString filename;
QFileDialog dialog(this);
QSettings settings;
QStringList filters;
filters.append(QString(tr("Filter List (*.ftl)")));
dialog.setDirectory(settings.value("Filters/LoadSaveDirectory", dialog.directory().path()).toString());
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
//right now there is only one file type that can be loaded here so just do it.
model->loadFilterFile(filename);
settings.setValue("Filters/LoadSaveDirectory", dialog.directory().path());
}
}
void MainWindow::handleSaveDecoded()
{
QString filename;
QFileDialog dialog(this);
QSettings settings;
QStringList filters;
filters.append(QString(tr("Text File (*.txt)")));
dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString());
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilters(filters);
dialog.setViewMode(QFileDialog::Detail);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec() == QDialog::Accepted)
{
filename = dialog.selectedFiles()[0];
if (!filename.contains('.')) filename += ".txt";
saveDecodedTextFile(filename);
settings.setValue("FileIO/LoadSaveDirectory", dialog.directory().path());
}
}
void MainWindow::saveDecodedTextFile(QString filename)
{
QFile *outFile = new QFile(filename);
const QVector<CANFrame> *frames = model->getFilteredListReference();
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::formatCANID(thisFrame.ID, thisFrame.extended);
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 (unsigned int temp = 0; temp < thisFrame.len; temp++)
{
builderString += Utility::formatNumber(thisFrame.data[temp]) + " ";
}
builderString += "\n";
outFile->write(builderString.toUtf8());
builderString = "";
if (dbcHandler != nullptr)
{
DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame);
if (msg != nullptr)
{
for (int j = 0; j < msg->sigHandler->getCount(); j++)
{
QString temp;
if (msg->sigHandler->findSignalByIdx(j)->processAsText(thisFrame, temp))
{
builderString.append("\t" + temp);
builderString.append("\n");
}
}
}
builderString.append("\n");
outFile->write(builderString.toUtf8());
}
}
outFile->close();
}
void MainWindow::toggleCapture()
{
allowCapture = !allowCapture;
if (allowCapture)
ui->btnCaptureToggle->setText("Suspend Capturing");
else
ui->btnCaptureToggle->setText("Restart Capturing");
emit suspendCapturing(!allowCapture);
}
void MainWindow::connectionStatusUpdated(int conns)
{
lbStatusConnected.setText(tr("Connected to ") + QString::number(conns) + tr(" buses"));
}
void MainWindow::updateFileStatus()
{
QString output;
if (model->rowCount() == 0)
{
output = tr("No packets loaded");
}
else
{
if (loadedFileName.length() > 2)
{
output = loadedFileName + " loaded";
}
else
{
output = tr("No file loaded");
}
if (bDirty)
{
output += " (X)";
}
}
lbStatusFilename.setText(output);
}
CANFrameModel* MainWindow::getCANFrameModel()
{
return model;
}
void MainWindow::showSettingsDialog()
{
if (!settingsDialog)
{
settingsDialog = new MainSettingsDialog();
connect (settingsDialog, SIGNAL(updatedSettings()), this, SLOT(readUpdateableSettings()));
}
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(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::showTemporalGraphWindow()
{
//only create an instance of the object if we dont have one. Otherwise just display the existing one.
if (!temporalGraphWindow)
{
if (!useFiltered)
temporalGraphWindow = new TemporalGraphWindow(model->getListReference());
else
temporalGraphWindow = new TemporalGraphWindow(model->getFilteredListReference());
}
temporalGraphWindow->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::showISOInterpreterWindow()
{
if (!isoWindow)
{
if (!useFiltered)
isoWindow = new ISOTP_InterpreterWindow(model->getListReference());
else
isoWindow = new ISOTP_InterpreterWindow(model->getFilteredListReference());
}
isoWindow->show();
}
void MainWindow::showSnifferWindow()
{
if (!snifferWindow)
snifferWindow = new SnifferWindow(this);
snifferWindow->show();
}
void MainWindow::showBisectWindow()
{
if (!bisectWindow)
{
bisectWindow = new BisectWindow(model->getListReference());
}
bisectWindow->show();
}
void MainWindow::showFrameSenderWindow()
{
if (!frameSenderWindow)
{
if (!useFiltered)
frameSenderWindow = new FrameSenderWindow(model->getListReference());
else
frameSenderWindow = new FrameSenderWindow(model->getFilteredListReference());
}
frameSenderWindow->show();
}
void MainWindow::showPlaybackWindow()
{
if (!playbackWindow)
{
if (!useFiltered)
playbackWindow = new FramePlaybackWindow(model->getListReference());
else
playbackWindow = new FramePlaybackWindow(model->getFilteredListReference());
}
playbackWindow->show();
}
void MainWindow::showFirmwareUploaderWindow()
{
if (!firmwareUploaderWindow)
{
firmwareUploaderWindow = new FirmwareUploaderWindow(model->getListReference());
}
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::showFuzzingWindow()
{
if (!fuzzingWindow)
{
fuzzingWindow = new FuzzingWindow(model->getListReference());
}
fuzzingWindow->show();
}
void MainWindow::showMCConfigWindow()
{
if (!motorctrlConfigWindow)
{
motorctrlConfigWindow = new MotorControllerConfigWindow(model->getListReference());
//connect(motorctrlConfigWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int)));
//connect(motorctrlConfigWindow, SIGNAL(sendFrameBatch(const QList<CANFrame>*)), worker, SLOT(sendFrameBatch(const QList<CANFrame>*)));
}
motorctrlConfigWindow->show();
}
void MainWindow::showUDSScanWindow()
{
if (!udsScanWindow)
{
udsScanWindow = new UDSScanWindow(model->getListReference());
}
udsScanWindow->show();
}
void MainWindow::showScriptingWindow()
{
if (!scriptingWindow)
{
scriptingWindow = new ScriptingWindow(model->getListReference());
}
scriptingWindow->show();
}
void MainWindow::showRangeWindow()
{
if (!rangeWindow)
{
rangeWindow = new RangeStateWindow(model->getListReference());
}
rangeWindow->show();
}
void MainWindow::showFuzzyScopeWindow()
{
//not done yet
}
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();
}
void MainWindow::DBCSettingsUpdated()
{
updateFilterList();
model->sendRefresh();
}
void MainWindow::showDBCFileWindow()
{
if (!dbcFileWindow)
{
dbcFileWindow = new DBCLoadSaveWindow(model->getListReference());
connect(dbcFileWindow, &DBCLoadSaveWindow::updatedDBCSettings, this, &MainWindow::DBCSettingsUpdated);
}
dbcFileWindow->show();
}
void MainWindow::showSignalViewer()
{
if (!signalViewerWindow)
{
if (!useFiltered)
signalViewerWindow = new SignalViewerWindow(model->getListReference());
else
signalViewerWindow = new SignalViewerWindow(model->getFilteredListReference());
}
signalViewerWindow->show();
}
void MainWindow::showConnectionSettingsWindow()
{
if (!connectionWindow)
{
connectionWindow = new ConnectionWindow();
}
connectionWindow->show();
}