1939 lines
65 KiB
C++
1939 lines
65 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"
|
|
|
|
#include <QClipboard>
|
|
/*
|
|
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);
|
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
|
qRegisterMetaTypeStreamOperators<QVector<QString>>();
|
|
qRegisterMetaTypeStreamOperators<QVector<int>>();
|
|
#endif
|
|
|
|
useHex = true;
|
|
useColorsByCanId = false;
|
|
selfRef = this;
|
|
|
|
this->setWindowTitle("Savvy CAN V" + QString::number(VERSION) + " [Built " + QString(__DATE__) +"]");
|
|
|
|
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);
|
|
ui->canFramesView->setSelectionMode(QAbstractItemView::NoSelection);
|
|
|
|
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);
|
|
QSettings settings;
|
|
int fontSize = settings.value("Main/FontSize", 9).toUInt();
|
|
QFont sysFont;
|
|
if(settings.value("Main/FontFixedWidth", false).toBool())
|
|
sysFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); //get default fixed width font
|
|
else
|
|
sysFont = QFont(); //get default font
|
|
sysFont.setPointSize(fontSize);
|
|
verticalHeader->setDefaultSectionSize(sysFont.pixelSize());
|
|
verticalHeader->setFont(QFont());
|
|
ui->canFramesView->setFont(sysFont);
|
|
|
|
QHeaderView *HorzHdr = ui->canFramesView->horizontalHeader();
|
|
HorzHdr->setFont(QFont());
|
|
HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview
|
|
connect(HorzHdr, SIGNAL(sectionClicked(int)), this, SLOT(headerClicked(int)));
|
|
|
|
lastGraphingWindow = nullptr;
|
|
frameInfoWindow = nullptr;
|
|
playbackWindow = nullptr;
|
|
flowViewWindow = nullptr;
|
|
frameSenderWindow = nullptr;
|
|
dbcMainEditor = nullptr;
|
|
comparatorWindow = nullptr;
|
|
settingsDialog = nullptr;
|
|
udsFirmwareUploaderWindow = 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;
|
|
dbcComparatorWindow = nullptr;
|
|
canBridgeWindow = nullptr;
|
|
dbcHandler = DBCHandler::getReference();
|
|
bDirty = false;
|
|
inhibitFilterUpdate = false;
|
|
rxFrames = 0;
|
|
framesPerSec = 0;
|
|
continuousLogging = false;
|
|
continuousLogFlushCounter = 0;
|
|
|
|
//handlers for all menu entries
|
|
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->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->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->actionSave_Decoded_Frames_CSV, &QAction::triggered, this, &MainWindow::handleSaveDecodedCsv);
|
|
connect(ui->actionSingle_Multi_State_2, &QAction::triggered, this, &MainWindow::showSingleMultiWindow);
|
|
connect(ui->actionFile_Comparison, &QAction::triggered, this, &MainWindow::showComparisonWindow);
|
|
connect(ui->actionDBC_Comparison, &QAction::triggered, this, &MainWindow::showDBCComparisonWindow);
|
|
connect(ui->actionScripting_INterface, &QAction::triggered, this, &MainWindow::showScriptingWindow);
|
|
connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::showSettingsDialog);
|
|
connect(ui->actionUDS_Firmware_Update, &QAction::triggered, this, &MainWindow::showUDSFirmwareUploaderWindow);
|
|
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(ui->actionCAN_Bridge, &QAction::triggered, this, &MainWindow::showCANBridgeWindow);
|
|
|
|
//handlers fror interactions with the main can frame view table
|
|
connect(ui->canFramesView, &QAbstractItemView::pressed, this, &MainWindow::gridClicked);
|
|
connect(ui->canFramesView, &QAbstractItemView::doubleClicked, this, &MainWindow::gridDoubleClicked);
|
|
ui->canFramesView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
copyAct = new QAction(tr("Copy"), this);
|
|
copyAct->setShortcut(QKeySequence::Copy);
|
|
connect(copyAct, &QAction::triggered, this, &MainWindow::copyFromTable);
|
|
ui->canFramesView->addAction(copyAct);
|
|
connect(ui->canFramesView, &QAbstractItemView::customContextMenuRequested, this, &MainWindow::gridContextMenuRequest);
|
|
|
|
connect(model, &CANFrameModel::updatedFiltersList, this, &MainWindow::updateFilterList);
|
|
connect(CANConManager::getInstance(), &CANConManager::framesReceived, model, &CANFrameModel::addFrames);
|
|
//new implementation for continuous logging
|
|
connect(CANConManager::getInstance(), &CANConManager::framesReceived, this, &MainWindow::logReceivedFrame);
|
|
|
|
connect(ui->cbInterpret, &QAbstractButton::toggled, this, &MainWindow::interpretToggled);
|
|
connect(ui->cbOverwrite, &QAbstractButton::toggled, this, &MainWindow::overwriteToggled);
|
|
connect(ui->cbPersistentFilters, &QAbstractButton::toggled, this, &MainWindow::presistentFiltersToggled);
|
|
connect(ui->listFilters, &QListWidget::itemChanged, this, &MainWindow::filterListItemChanged);
|
|
connect(ui->listBusFilters, &QListWidget::itemChanged, this, &MainWindow::busFilterListItemChanged);
|
|
|
|
connect(ui->btnCaptureToggle, &QAbstractButton::clicked, this, &MainWindow::toggleCapture);
|
|
connect(ui->btnClearFrames, &QAbstractButton::clicked, this, &MainWindow::clearFrames);
|
|
connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming);
|
|
connect(ui->btnFilterAll, &QAbstractButton::clicked, this, &MainWindow::filterSetAll);
|
|
connect(ui->btnFilterNone, &QAbstractButton::clicked, this, &MainWindow::filterClearAll);
|
|
connect(ui->btnExpandAll, &QAbstractButton::clicked, this, &MainWindow::expandAllRows);
|
|
connect(ui->btnCollapseAll, &QAbstractButton::clicked, this, &MainWindow::collapseAllRows);
|
|
|
|
connect(ui->tableSimpleSender, SIGNAL(cellChanged(int,int)), this, SLOT(onSenderCellChanged(int,int)));
|
|
|
|
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 QElapsedTimer;
|
|
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.setFrameId(0x100);
|
|
temp.isReceived = true;
|
|
temp.setTimeStamp(QCanBusFrame::TimeStamp(0, 100000000));
|
|
model->addFrame(temp, true);
|
|
ui->canFramesView->resizeRowToContents(0); // Resize the row to fit the contents so we get a proper height value
|
|
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(true);
|
|
|
|
ui->canFramesView->verticalHeader()->setDefaultSectionSize(normalRowHeight); // Set the default height for all rows to the height that was calculated
|
|
|
|
//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);
|
|
|
|
QStringList headers;
|
|
headers << "En" << "Bus" << "ID" << "Ext" << "Rem" << "Data"
|
|
<< "Interval" << "Count";
|
|
ui->tableSimpleSender->setColumnCount(8);
|
|
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_EN, 70);
|
|
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_BUS, 70);
|
|
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_ID, 70);
|
|
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_EXT, 70);
|
|
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_REM, 70);
|
|
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_DATA, 300);
|
|
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_INTERVAL, 100);
|
|
ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_COUNT, 100);
|
|
ui->tableSimpleSender->setHorizontalHeaderLabels(headers);
|
|
|
|
createSenderRow();
|
|
|
|
frameSender = new FrameSenderObject(model->getListReference());
|
|
|
|
frameSender->initialize(); //creates the thread and sets things up
|
|
frameSender->startSending(); //start the timer in the object so enabled things can send
|
|
|
|
installEventFilter(this);
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
updateTimer.stop();
|
|
frameSender->stopSending();
|
|
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()
|
|
{
|
|
foreach (GraphingWindow *win, graphWindows)
|
|
{
|
|
killWindow(win);
|
|
}
|
|
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(udsFirmwareUploaderWindow);
|
|
killWindow(motorctrlConfigWindow);
|
|
killWindow(signalViewerWindow);
|
|
killWindow(temporalGraphWindow);
|
|
killWindow(canBridgeWindow);
|
|
|
|
//trying to kill this window can cause a fault to happen. It's closed last just in case.
|
|
killWindow(connectionWindow);
|
|
}
|
|
|
|
//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
|
|
}
|
|
|
|
|
|
//the close event can be trapped and ignored so put unsaved warnings in here so the user can abort the program closing if they forgot to save things.
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
{
|
|
|
|
QMessageBox::StandardButton confirmDialog;
|
|
|
|
for (int i = 0; i < dbcHandler->getFileCount(); i++)
|
|
{
|
|
DBCFile *file = dbcHandler->getFileByIdx(i);
|
|
if (file->getDirtyFlag())
|
|
{
|
|
confirmDialog = QMessageBox::question(this, "Unsaved DBC", "DBC File:\n" + file->getFilename() + "\nAppears to have unsaved changes\nReally close without saving?", QMessageBox::Yes|QMessageBox::No);
|
|
if (confirmDialog != QMessageBox::Yes)
|
|
{
|
|
event->ignore();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
removeEventFilter(this);
|
|
writeSettings();
|
|
exitApp();
|
|
event->accept();
|
|
}
|
|
|
|
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.md");
|
|
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(Utility::constrainedWindowPos(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();
|
|
}
|
|
|
|
|
|
/*
|
|
* TODO: The way the frame timing mode is specified is DEAD STUPID. There shouldn't be three boolean values
|
|
* for this. Instead switch it all to an ENUM or something sane.
|
|
*/
|
|
void MainWindow::readUpdateableSettings()
|
|
{
|
|
QSettings settings;
|
|
useHex = settings.value("Main/UseHex", true).toBool();
|
|
model->setHexMode(useHex);
|
|
Utility::decimalMode = !useHex;
|
|
|
|
useColorsByCanId = settings.value("Main/ColorsByCanId", false).toBool();
|
|
model->setUseColorsByCanId(useColorsByCanId);
|
|
|
|
bool tempBool;
|
|
TimeStyle ts = TS_MICROS;
|
|
tempBool = settings.value("Main/TimeSeconds", false).toBool();
|
|
if (tempBool) ts = TS_SECONDS;
|
|
tempBool = settings.value("Main/TimeClock", false).toBool();
|
|
if (tempBool) ts = TS_CLOCK;
|
|
tempBool = settings.value("Main/TimeMillis", false).toBool();
|
|
if (tempBool) ts = TS_MILLIS;
|
|
model->setTimeStyle(ts);
|
|
|
|
useFiltered = settings.value("Main/UseFiltered", false).toBool();
|
|
model->setTimeFormat(settings.value("Main/TimeFormat", "MMM-dd HH:mm:ss.zzz").toString());
|
|
ignoreDBCColors = settings.value("Main/IgnoreDBCColors", false).toBool();
|
|
model->setIgnoreDBCColors(ignoreDBCColors);
|
|
int bpl = settings.value("Main/BytesPerLine", 8).toInt();
|
|
model->setBytesPerLine(bpl);
|
|
|
|
CSVAbsTime = settings.value("Main/CSVAbsTime", false).toBool();
|
|
|
|
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::onSenderCellChanged(int row, int col)
|
|
{
|
|
if (inhibitSenderChanged) return;
|
|
qDebug() << "onCellChanged";
|
|
if (row == ui->tableSimpleSender->rowCount() - 1)
|
|
{
|
|
createSenderRow();
|
|
}
|
|
|
|
processSenderCellChange(row, col);
|
|
}
|
|
|
|
void MainWindow::processSenderCellChange(int line, int col)
|
|
{
|
|
qDebug() << "processSenderCellChange";
|
|
FrameSendData *tempData;
|
|
QStringList tokens;
|
|
int tempVal;
|
|
|
|
int numBuses = CANConManager::getInstance()->getNumBuses();
|
|
QByteArray arr;
|
|
|
|
tempData = frameSender->getSendRecordRef(line);
|
|
|
|
if (!tempData)
|
|
{
|
|
qDebug() << "Need to set up a new entry in senders";
|
|
FrameSendData dat;
|
|
dat.enabled = false;
|
|
dat.count = 0;
|
|
dat.frameCount = 0;
|
|
dat.bus = 0;
|
|
frameSender->addSendRecord(dat);
|
|
tempData = frameSender->getSendRecordRef(line);
|
|
}
|
|
|
|
if (!tempData)
|
|
{
|
|
qDebug() << "No data to modify in processSenderCellChange. This is a bug!";
|
|
return;
|
|
}
|
|
|
|
switch (col)
|
|
{
|
|
case SIMP_COL::SC_COL_EN: //Enable check box
|
|
if (ui->tableSimpleSender->item(line, 0)->checkState() == Qt::Checked)
|
|
{
|
|
tempData->enabled = true;
|
|
}
|
|
else tempData->enabled = false;
|
|
qDebug() << "Setting enabled to " << tempData->enabled;
|
|
break;
|
|
case SIMP_COL::SC_COL_BUS: //Bus designation
|
|
tempVal = Utility::ParseStringToNum(ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_BUS)->text());
|
|
if (tempVal < -1) tempVal = -1;
|
|
if (tempVal >= numBuses) tempVal = numBuses - 1;
|
|
tempData->bus = tempVal;
|
|
qDebug() << "Setting bus to " << tempVal;
|
|
break;
|
|
case SIMP_COL::SC_COL_ID: //ID field
|
|
tempVal = Utility::ParseStringToNum(ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_ID)->text());
|
|
if (tempVal < 0) tempVal = 0;
|
|
if (tempVal > 0x7FFFFFFF) tempVal = 0x7FFFFFFF;
|
|
tempData->setFrameId(tempVal);
|
|
if (tempData->frameId() > 0x7FF) {
|
|
tempData->setExtendedFrameFormat(true);
|
|
ui->tableSimpleSender->blockSignals(true);
|
|
ui->tableSimpleSender->item(line, ST_COLS::SENDTAB_COL_EXT)->setCheckState(Qt::Checked);
|
|
ui->tableSimpleSender->blockSignals(false);
|
|
}
|
|
qDebug() << "setting ID to " << tempVal;
|
|
break;
|
|
case SIMP_COL::SC_COL_EXT:
|
|
if (ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_EXT)->checkState() == Qt::Checked) {
|
|
tempData->setExtendedFrameFormat(true);
|
|
} else {
|
|
tempData->setExtendedFrameFormat(false);
|
|
}
|
|
break;
|
|
case SIMP_COL::SC_COL_REM:
|
|
if (ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_REM)->checkState() == Qt::Checked) {
|
|
tempData->setFrameType(QCanBusFrame::RemoteRequestFrame);
|
|
} else {
|
|
tempData->setFrameType(QCanBusFrame::DataFrame);
|
|
}
|
|
break;
|
|
case SIMP_COL::SC_COL_DATA: //Data bytes
|
|
for (int i = 0; i < 8; i++) tempData->payload().data()[i] = 0;
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 )
|
|
tokens = ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_DATA)->text().split(" ", Qt::SkipEmptyParts);
|
|
#else
|
|
tokens = ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_DATA)->text().split(" ", QString::SkipEmptyParts);
|
|
#endif
|
|
arr.clear();
|
|
arr.reserve(tokens.count());
|
|
for (int j = 0; j < tokens.count(); j++)
|
|
{
|
|
arr.append((uint8_t)Utility::ParseStringToNum(tokens[j]));
|
|
}
|
|
tempData->setPayload(arr);
|
|
break;
|
|
case SIMP_COL::SC_COL_INTERVAL: //interval in ms
|
|
|
|
QString trigger = ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_INTERVAL)->text().toUpper();
|
|
|
|
Trigger thisTrigger;
|
|
thisTrigger.bus = -1; //-1 means we don't care which
|
|
thisTrigger.ID = -1; //the rest of these being -1 means nothing has changed it
|
|
thisTrigger.maxCount = -1;
|
|
thisTrigger.milliseconds = -1;
|
|
thisTrigger.currCount = 0;
|
|
thisTrigger.msCounter = 0;
|
|
thisTrigger.triggerMask = 0;
|
|
thisTrigger.readyCount = true;
|
|
|
|
tempData->triggers.clear();
|
|
tempData->triggers.reserve(1);
|
|
|
|
if (trigger != "")
|
|
{
|
|
thisTrigger.milliseconds = Utility::ParseStringToNum(trigger);
|
|
thisTrigger.triggerMask |= TriggerMask::TRG_MS;
|
|
}
|
|
|
|
if (thisTrigger.milliseconds < 1) thisTrigger.milliseconds = 1;
|
|
|
|
tempData->triggers.append(thisTrigger);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MainWindow::createSenderRow()
|
|
{
|
|
int row = ui->tableSimpleSender->rowCount();
|
|
ui->tableSimpleSender->insertRow(row);
|
|
|
|
QTableWidgetItem *item = new QTableWidgetItem();
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Unchecked);
|
|
inhibitSenderChanged = true;
|
|
ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_EN, item);
|
|
|
|
item = new QTableWidgetItem();
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Unchecked);
|
|
ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_EXT, item);
|
|
|
|
item = new QTableWidgetItem();
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Unchecked);
|
|
ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_REM, item);
|
|
|
|
for (int i = 1; i <= SIMP_COL::SC_COL_COUNT; i++)
|
|
{
|
|
if (i != SIMP_COL::SC_COL_EXT && i != SIMP_COL::SC_COL_REM) {
|
|
item = new QTableWidgetItem("");
|
|
ui->tableSimpleSender->setItem(row, i, item);
|
|
}
|
|
}
|
|
|
|
inhibitSenderChanged = false;
|
|
}
|
|
|
|
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);
|
|
|
|
manageRowExpansion();
|
|
}
|
|
|
|
void MainWindow::expandAllRows()
|
|
{
|
|
bool goAhead = false;
|
|
int numRows = ui->canFramesView->model()->rowCount();
|
|
|
|
if (numRows > 20000)
|
|
{
|
|
QMessageBox::StandardButton confirmDialog;
|
|
confirmDialog = QMessageBox::question(this, "Really?", "It's not recommended to use this\non more than 20000 frames.\nIt can take a long time.\n\nYou have been warned!\nStill do it?",
|
|
QMessageBox::Yes|QMessageBox::No);
|
|
|
|
if (confirmDialog == QMessageBox::Yes) goAhead = true;
|
|
}
|
|
else goAhead = true;
|
|
|
|
if (goAhead)
|
|
{
|
|
ui->canFramesView->resizeRowsToContents();
|
|
|
|
rowExpansionActive = true;
|
|
}
|
|
}
|
|
|
|
void MainWindow::manageRowExpansion()
|
|
{
|
|
int numRows = ui->canFramesView->model()->rowCount();
|
|
if(numRows < 20000)
|
|
{
|
|
if(rowExpansionActive && model->getInterpretMode())
|
|
ui->canFramesView->resizeRowsToContents();
|
|
}
|
|
else
|
|
{
|
|
disableAutoRowExpansion();
|
|
}
|
|
}
|
|
|
|
void MainWindow::disableAutoRowExpansion()
|
|
{
|
|
rowExpansionActive = false;
|
|
}
|
|
|
|
void MainWindow::collapseAllRows()
|
|
{
|
|
bool goAhead = false;
|
|
int numRows = ui->canFramesView->model()->rowCount();
|
|
|
|
if (numRows > 50000)
|
|
{
|
|
QMessageBox::StandardButton confirmDialog;
|
|
confirmDialog = QMessageBox::question(this, "Really?", "It's not recommended to use this\non more than 50000 frames.\nIt can take a long time.\n\nYou have been warned!\nStill do it?",
|
|
QMessageBox::Yes|QMessageBox::No);
|
|
|
|
if (confirmDialog == QMessageBox::Yes) goAhead = true;
|
|
}
|
|
else goAhead = true;
|
|
|
|
if (goAhead)
|
|
{
|
|
for (int i = 0; i < numRows; i++) ui->canFramesView->setRowHeight(i, normalRowHeight);
|
|
|
|
rowExpansionActive = false;
|
|
}
|
|
}
|
|
|
|
void MainWindow::gridClicked(QModelIndex idx)
|
|
{
|
|
// do not expand or collapse row if press was a right mouse button press to be able to use the right click menu
|
|
if (QApplication::mouseButtons() & Qt::RightButton) {
|
|
return;
|
|
}
|
|
//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.frameId(), frame.timeStamp().microSeconds() / 1000000.0);
|
|
}
|
|
|
|
void MainWindow::gridContextMenuRequest(QPoint pos)
|
|
{
|
|
QModelIndex idx = ui->canFramesView->indexAt(pos); //figure out where in the view we clicked (row, column)
|
|
qDebug() << "Pos: " << pos << " Row :" << idx.row() << " Col: " << idx.column();
|
|
|
|
if (!idx.isValid()) return;
|
|
|
|
QMenu *menu = new QMenu(this);
|
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
menu->addAction(copyAct);
|
|
|
|
if (idx.column() == 8) //we're over the DATA column
|
|
{
|
|
contextMenuPosition = pos;
|
|
menu->addSeparator();
|
|
menu->addAction(tr("Add to a new graphing window"), this, SLOT(setupAddToNewGraph()));
|
|
menu->addAction(tr("Add to latest graphing window"), this, SLOT(setupSendToLatestGraphWindow()));
|
|
}
|
|
|
|
menu->popup(ui->canFramesView->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
void MainWindow::copyFromTable()
|
|
{
|
|
copySelection();
|
|
}
|
|
|
|
void MainWindow::copySelection()
|
|
{
|
|
QItemSelectionModel *selectionModel = ui->canFramesView->selectionModel();
|
|
QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
|
|
|
|
if(selectedIndexes.isEmpty())
|
|
return;
|
|
|
|
// QModelIndex::operator< sorts by row and then by column.
|
|
std::sort(selectedIndexes.begin(), selectedIndexes.end());
|
|
|
|
QString selectedText;
|
|
int currentRow = -1;
|
|
int lastRow = selectedIndexes.last().row();
|
|
|
|
for(const QModelIndex ¤t : selectedIndexes)
|
|
{
|
|
if (currentRow != -1 && current.row() != currentRow)
|
|
{
|
|
// remove last tab
|
|
if (selectedText.endsWith(QLatin1Char('\t')))
|
|
selectedText.chop(1);
|
|
selectedText.append(QLatin1Char('\n'));
|
|
}
|
|
currentRow = current.row();
|
|
|
|
QString cellText = current.data(Qt::DisplayRole).toString();
|
|
|
|
// Replace newlines within a cell to avoid breaking the table structure in Excel
|
|
cellText.replace(QLatin1Char('\n'), QLatin1String(" "));
|
|
|
|
selectedText.append(cellText);
|
|
|
|
if (current.row() != lastRow || current != selectedIndexes.last())
|
|
{
|
|
selectedText.append(QLatin1Char('\t'));
|
|
}
|
|
}
|
|
|
|
// remove last tab if it exists
|
|
if (selectedText.endsWith(QLatin1Char('\t')))
|
|
selectedText.chop(1);
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(selectedText);
|
|
}
|
|
|
|
QString MainWindow::getSignalNameFromPosition(QPoint pos)
|
|
{
|
|
//there's a bit of an issue to solve. The data column is one big string even if there are a number
|
|
//of signals in there. So, the basic idea is to find out how tall the font is and where the user
|
|
//clicked within the cell. Then find out which line that puts us over.
|
|
QModelIndex idx = ui->canFramesView->indexAt(pos); //figure out where in the view we clicked (row, column)
|
|
int fontHeight = ui->canFramesView->fontMetrics().height();
|
|
int cellBaseY = ui->canFramesView->rowViewportPosition(idx.row());
|
|
int lineOffset = (pos.y() - cellBaseY) / fontHeight;
|
|
qDebug() << "Offset: " << lineOffset;
|
|
QString lineText = idx.data().toString().split("\n")[lineOffset];
|
|
qDebug() << "Line Text: " << lineText;
|
|
return lineText.split(":")[0];
|
|
}
|
|
|
|
uint32_t MainWindow::getMessageIDFromPosition(QPoint pos)
|
|
{
|
|
QModelIndex idx = ui->canFramesView->indexAt(pos); //figure out where in the view we clicked (row, column)
|
|
QString idText = ui->canFramesView->model()->index(idx.row(), 1).data().toString();
|
|
return Utility::ParseStringToNum(idText);
|
|
}
|
|
|
|
void MainWindow::setupAddToNewGraph()
|
|
{
|
|
showGraphingWindow(); //creates a new window and sets it as latest
|
|
setupSendToLatestGraphWindow(); //then call the other function to finish
|
|
}
|
|
|
|
|
|
void MainWindow::setupSendToLatestGraphWindow()
|
|
{
|
|
if (!lastGraphingWindow) showGraphingWindow();
|
|
GraphParams param;
|
|
QString signalName = getSignalNameFromPosition(contextMenuPosition);
|
|
param.ID = getMessageIDFromPosition(contextMenuPosition);
|
|
DBC_MESSAGE *msg = dbcHandler->findMessage(param.ID);
|
|
if(msg)
|
|
{
|
|
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(signalName);
|
|
if(sig)
|
|
{
|
|
param.associatedSignal = sig;
|
|
param.bias = sig->bias;
|
|
param.intelFormat = sig->intelByteOrder;
|
|
param.isSigned = sig->valType == SIGNED_INT ? true : false;
|
|
param.numBits = sig->signalSize;
|
|
param.scale = sig->factor;
|
|
param.startBit = sig->startBit;
|
|
param.stride = 1;
|
|
param.graphName = sig->name;
|
|
param.lineColor = QColor(QRandomGenerator::global()->bounded(160), QRandomGenerator::global()->bounded(160), QRandomGenerator::global()->bounded(160));
|
|
param.lineWidth = 1;
|
|
param.fillColor = QColor(128, 128, 128, 0);
|
|
param.mask = 0xFFFFFFFFFFFFFFFFull;
|
|
param.drawOnlyPoints = false;
|
|
param.pointType = 0;
|
|
|
|
lastGraphingWindow->createGraph(param); //add the new graph to the window
|
|
}
|
|
else
|
|
{
|
|
QMessageBox msgbox;
|
|
QString boxmsg = "Cannot find ID 0x" + QStringLiteral("%1").arg(param.ID, 3, 16, QLatin1Char('0')) + " in DBC message " + msg->name + ". Not adding graph.";
|
|
msgbox.setText(boxmsg);
|
|
msgbox.exec();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QMessageBox msgbox;
|
|
QString boxmsg = "Cannot find ID 0x" + QStringLiteral("%1").arg(param.ID, 3, 16, QLatin1Char('0')) + " in DBC file(s). Not adding graph.";
|
|
msgbox.setText(boxmsg);
|
|
msgbox.exec();
|
|
}
|
|
}
|
|
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
|
|
{
|
|
rowExpansionActive = false;
|
|
model->resetFilteredFrames();
|
|
model->setOverwriteMode(false);
|
|
}
|
|
}
|
|
|
|
void MainWindow::presistentFiltersToggled(bool state)
|
|
{
|
|
if (state)
|
|
{
|
|
model->setClearMode(true);
|
|
}
|
|
else
|
|
{
|
|
model->setClearMode(false);
|
|
}
|
|
}
|
|
|
|
void MainWindow::updateFilterList()
|
|
{
|
|
if (model == nullptr) return;
|
|
const QMap<int, bool> *filters = model->getFiltersReference();
|
|
const QMap<int, bool> *busFilters = model->getBusFiltersReference();
|
|
if (filters == nullptr || busFilters == nullptr) return;
|
|
|
|
qDebug() << "updateFilterList called on MainWindow";
|
|
|
|
inhibitFilterUpdate = true;
|
|
|
|
ui->listFilters->clear();
|
|
ui->listBusFilters->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);
|
|
}
|
|
|
|
if (busFilters->isEmpty()) return;
|
|
|
|
for (filterIter = busFilters->begin(); filterIter != busFilters->end(); ++filterIter)
|
|
{
|
|
/*QListWidgetItem *thisItem = */ FilterUtility::createCheckableBusFilterItem(filterIter.key(), filterIter.value(), ui->listBusFilters);
|
|
}
|
|
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);
|
|
|
|
manageRowExpansion();
|
|
}
|
|
|
|
void MainWindow::busFilterListItemChanged(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->setBusFilterState(ID, isSet);
|
|
|
|
manageRowExpansion();
|
|
}
|
|
|
|
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);
|
|
|
|
manageRowExpansion();
|
|
}
|
|
|
|
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::logReceivedFrame(CANConnection* conn, QVector<CANFrame> frames)
|
|
{
|
|
Q_UNUSED(conn);
|
|
if (continuousLogging)
|
|
{
|
|
FrameFileIO::writeContinuousNative(&frames, 0);
|
|
}
|
|
}
|
|
|
|
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?
|
|
manageRowExpansion();
|
|
}
|
|
|
|
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("LOGGING");
|
|
}
|
|
}
|
|
if (continuousLogFlushCounter > 8)
|
|
{
|
|
continuousLogFlushCounter = 0;
|
|
FrameFileIO::flushContinuousNative();
|
|
}
|
|
}
|
|
|
|
//refresh the count for all the frame senders
|
|
FrameSendData *tempData;
|
|
int numRows = ui->tableSimpleSender->rowCount();
|
|
for (int i = 0; i < numRows; i++)
|
|
{
|
|
tempData = frameSender->getSendRecordRef(i);
|
|
if (tempData)
|
|
{
|
|
ui->tableSimpleSender->item(i, SIMP_COL::SC_COL_COUNT)->setText(QString::number( tempData->count ));
|
|
}
|
|
}
|
|
|
|
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(uint32_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)
|
|
{
|
|
if (tempFrames.count() > 0) //only ask if at least one frame was decoded.
|
|
{
|
|
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)
|
|
{
|
|
disableAutoRowExpansion();
|
|
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::handleDroppedFile(const QString &filename)
|
|
{
|
|
QProgressDialog progress(qApp->activeWindow());
|
|
progress.setWindowModality(Qt::WindowModal);
|
|
progress.setLabelText("Loading file...");
|
|
progress.setCancelButton(nullptr);
|
|
progress.setRange(0,0);
|
|
progress.setMinimumDuration(0);
|
|
progress.show();
|
|
|
|
QVector<CANFrame> loadedFrames;
|
|
bool loadResult = FrameFileIO::autoDetectLoadFile(filename, &loadedFrames);
|
|
|
|
progress.cancel();
|
|
|
|
if (!loadResult)
|
|
{
|
|
if (loadedFrames.count() > 0) //only ask if at least one frame was decoded.
|
|
{
|
|
QMessageBox::StandardButton 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)
|
|
{
|
|
disableAutoRowExpansion();
|
|
ui->canFramesView->scrollToTop();
|
|
model->clearFrames();
|
|
model->insertFrames(loadedFrames);
|
|
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()
|
|
{
|
|
handleSaveDecodedMethod(false);
|
|
}
|
|
|
|
void MainWindow::handleSaveDecodedCsv()
|
|
{
|
|
handleSaveDecodedMethod(true);
|
|
}
|
|
|
|
void MainWindow::handleSaveDecodedMethod(bool csv)
|
|
{
|
|
QString filename;
|
|
QFileDialog dialog(this);
|
|
QSettings settings;
|
|
|
|
QStringList filters;
|
|
if (!csv) filters.append(QString(tr("Text File (*.txt *.TXT)")));
|
|
else filters.append(QString(tr("CSV File (*.csv *.CSV)")));
|
|
|
|
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('.'))
|
|
{
|
|
if (!csv) filename += ".txt";
|
|
else filename += ".csv";
|
|
}
|
|
|
|
if(csv)
|
|
saveDecodedTextFileAsColumns(filename);
|
|
else
|
|
saveDecodedTextFile(filename);
|
|
|
|
settings.setValue("FileIO/LoadSaveDirectory", dialog.directory().path());
|
|
}
|
|
}
|
|
|
|
void MainWindow::saveDecodedTextFileAsColumns(QString filename)
|
|
{
|
|
QFile *outFile = new QFile(filename);
|
|
const QVector<CANFrame> *frames = model->getFilteredListReference();
|
|
|
|
//const unsigned char *data;
|
|
int dataLen;
|
|
const CANFrame *frame;
|
|
|
|
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
|
|
*/
|
|
QList<QPair<uint32_t, int>> msgsAndColumns;
|
|
int columnsAdded = 0;
|
|
int dataStartCol = 0;
|
|
|
|
QString builderString;
|
|
if (CSVAbsTime)
|
|
{
|
|
builderString += tr("Year") + "," + tr("Month") + "," + tr("Day") + "," + tr("Hour") + "," + tr("Minute") + "," + tr("Second") + "," + tr("Ms") + ",";
|
|
dataStartCol += 7;
|
|
}
|
|
else
|
|
{
|
|
//time
|
|
builderString += tr("Time") + ",";
|
|
dataStartCol++;
|
|
}
|
|
//id
|
|
builderString += tr("ID") + ",";
|
|
dataStartCol++;
|
|
//if (frame->hasExtendedFrameFormat()) builderString += tr(" Ext ");
|
|
//else builderString += tr(" Std ");
|
|
//bus
|
|
builderString += tr("Bus") + ",";
|
|
dataStartCol++;
|
|
//len
|
|
builderString += tr("DataLen") + ",";
|
|
dataStartCol++;
|
|
|
|
columnsAdded = dataStartCol;
|
|
|
|
//loop through all the frames and the message data therein
|
|
for (int c = 0; c < frames->count(); c++)
|
|
{
|
|
frame = &frames->at(c);
|
|
//data = reinterpret_cast<const unsigned char *>(frame->payload().constData());
|
|
dataLen = frame->payload().count();
|
|
|
|
//add all column names
|
|
if (dbcHandler != nullptr)
|
|
{
|
|
DBC_MESSAGE *msg = dbcHandler->findMessage(*frame);
|
|
if (msg != nullptr)
|
|
{
|
|
bool found = false;
|
|
for (int j = 0; j < msg->sigHandler->getCount(); j++)
|
|
{
|
|
if(j==0)
|
|
{
|
|
for(int m=0; m<msgsAndColumns.count(); m++)
|
|
{
|
|
if(msgsAndColumns[m].first == msg->ID)
|
|
found = true;
|
|
}
|
|
if(found == false)
|
|
msgsAndColumns.append(QPair<uint32_t,int>(msg->ID, columnsAdded));
|
|
}
|
|
|
|
if(found == false)
|
|
{
|
|
QString temp;
|
|
if (msg->sigHandler->findSignalByIdx(j)->processAsText(*frame, temp))
|
|
{
|
|
builderString.append(msg->sigHandler->findSignalByIdx(j)->name);
|
|
builderString.append(",");
|
|
columnsAdded++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//add EOL
|
|
builderString += "\n";
|
|
//write out the header row
|
|
outFile->write(builderString.toUtf8());
|
|
|
|
//builderString = tr("Data Bytes: ");
|
|
//for (int temp = 0; temp < dataLen; temp++)
|
|
//{
|
|
// builderString += Utility::formatNumber(data[temp]) + " ";
|
|
//}
|
|
//builderString += "\n";
|
|
//outFile->write(builderString.toUtf8());
|
|
|
|
int dataColumnsAdded = 0;
|
|
builderString = "";
|
|
for (int c = 0; c < frames->count(); c++)
|
|
{
|
|
dataColumnsAdded = 0;
|
|
frame = &frames->at(c);
|
|
//data = reinterpret_cast<const unsigned char *>(frame->payload().constData());
|
|
dataLen = frame->payload().count();
|
|
|
|
QString builderString;
|
|
if (CSVAbsTime)
|
|
{
|
|
QDateTime dt = QDateTime::fromMSecsSinceEpoch(frame->timeStamp().microSeconds() / 1000);
|
|
builderString += QString::number(dt.date().year()) + "," + QString::number(dt.date().month()) + ",";
|
|
builderString += QString::number(dt.date().day()) + "," + QString::number(dt.time().hour()) + ",";
|
|
builderString += QString::number(dt.time().minute()) + "," + QString::number(dt.time().second()) + ",";
|
|
builderString += QString::number(dt.time().msec()) + ",";
|
|
dataColumnsAdded += 7;
|
|
}
|
|
else {
|
|
builderString += QString::number((frame->timeStamp().microSeconds() / 1000000.0), 'f', 6) + ",";
|
|
dataColumnsAdded++;
|
|
}
|
|
//id
|
|
builderString += Utility::formatCANID(frame->frameId(), frame->hasExtendedFrameFormat()) + ",";
|
|
dataColumnsAdded++;
|
|
//if (frame->hasExtendedFrameFormat()) builderString += tr(" Ext ");
|
|
//else builderString += tr(" Std ");
|
|
//bus
|
|
builderString += QString::number(frame->bus) + ",";
|
|
dataColumnsAdded++;
|
|
//len
|
|
builderString += QString::number(dataLen) + ",";
|
|
dataColumnsAdded++;
|
|
|
|
if (dbcHandler != nullptr)
|
|
{
|
|
DBC_MESSAGE *msg = dbcHandler->findMessage(*frame);
|
|
if (msg != nullptr)
|
|
{
|
|
for (int j = 0; j < msg->sigHandler->getCount(); j++)
|
|
{
|
|
if(j==0)
|
|
{
|
|
for(int i = 0; i<msgsAndColumns.count(); i++)
|
|
{
|
|
if(msgsAndColumns[i].first == msg->ID)
|
|
{
|
|
int startCol = msgsAndColumns[i].second;
|
|
while(dataColumnsAdded < startCol)
|
|
{
|
|
builderString += ",";
|
|
dataColumnsAdded++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QString temp;
|
|
if (msg->sigHandler->findSignalByIdx(j)->processAsText(*frame, temp, false, false))
|
|
{
|
|
builderString.append(temp);
|
|
builderString.append(",");
|
|
dataColumnsAdded++;
|
|
}
|
|
}
|
|
}
|
|
builderString.append("\n");
|
|
outFile->write(builderString.toUtf8());
|
|
}
|
|
}
|
|
outFile->close();
|
|
}
|
|
|
|
void MainWindow::saveDecodedTextFile(QString filename)
|
|
{
|
|
QFile *outFile = new QFile(filename);
|
|
const QVector<CANFrame> *frames = model->getFilteredListReference();
|
|
|
|
const unsigned char *data;
|
|
int dataLen;
|
|
const CANFrame *frame;
|
|
|
|
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++)
|
|
{
|
|
frame = &frames->at(c);
|
|
data = reinterpret_cast<const unsigned char *>(frame->payload().constData());
|
|
dataLen = frame->payload().count();
|
|
|
|
QString builderString;
|
|
builderString += tr("Time: ") + QString::number((frame->timeStamp().microSeconds() / 1000000.0), 'f', 6);
|
|
builderString += tr(" ID: ") + Utility::formatCANID(frame->frameId(), frame->hasExtendedFrameFormat());
|
|
if (frame->hasExtendedFrameFormat()) builderString += tr(" Ext ");
|
|
else builderString += tr(" Std ");
|
|
builderString += tr("Bus: ") + QString::number(frame->bus);
|
|
builderString += " Len: " + QString::number(dataLen) + "\n";
|
|
outFile->write(builderString.toUtf8());
|
|
|
|
builderString = tr("Data Bytes: ");
|
|
for (int temp = 0; temp < dataLen; temp++)
|
|
{
|
|
builderString += Utility::formatNumber(data[temp]) + " ";
|
|
}
|
|
builderString += "\n";
|
|
outFile->write(builderString.toUtf8());
|
|
|
|
builderString = "";
|
|
if (dbcHandler != nullptr)
|
|
{
|
|
DBC_MESSAGE *msg = dbcHandler->findMessage(*frame);
|
|
if (msg != nullptr)
|
|
{
|
|
for (int j = 0; j < msg->sigHandler->getCount(); j++)
|
|
{
|
|
|
|
QString temp;
|
|
if (msg->sigHandler->findSignalByIdx(j)->processAsText(*frame, 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* All functions past this point set up the various other windows that can be opened
|
|
*/
|
|
|
|
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
|
|
//now always creates a new window. This allows for multiple independent graphing windows
|
|
void MainWindow::showGraphingWindow()
|
|
{
|
|
/* could only allow the latest window to have these centering signals.
|
|
if (lastGraphingWindow)
|
|
{
|
|
disconnect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double)));
|
|
disconnect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
if (flowViewWindow)
|
|
{
|
|
disconnect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
disconnect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
}
|
|
}
|
|
*/
|
|
lastGraphingWindow = new GraphingWindow(model->getListReference());
|
|
graphWindows.append(lastGraphingWindow);
|
|
|
|
connect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), this, SLOT(gotCenterTimeID(uint32_t,double)));
|
|
connect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(uint32_t,double)));
|
|
|
|
if (flowViewWindow) //connect the two external windows together
|
|
{
|
|
connect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(uint32_t,double)));
|
|
connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, SLOT(gotCenterTimeID(uint32_t,double)));
|
|
}
|
|
|
|
lastGraphingWindow->show();
|
|
}
|
|
|
|
void MainWindow::showTemporalGraphWindow()
|
|
{
|
|
//only create an instance of the object if we dont have one. Otherwise just display the existing one.
|
|
if (!temporalGraphWindow)
|
|
{
|
|
const QVector<CANFrame> *frames;
|
|
if (!useFiltered)
|
|
frames = model->getListReference();
|
|
else
|
|
frames = model->getFilteredListReference();
|
|
|
|
if(frames->count() > 2000)
|
|
{
|
|
QMessageBox::StandardButton confirmDialog;
|
|
confirmDialog = QMessageBox::question(this, "Danger Will Robinson", "There are a lot of frames (>2000) to plot, this may take a while or crash the app. Crash likely with more than 10k frames. Continue?",
|
|
QMessageBox::Yes|QMessageBox::No);
|
|
if (confirmDialog == QMessageBox::No)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
temporalGraphWindow = new TemporalGraphWindow(frames);
|
|
}
|
|
|
|
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::showCANBridgeWindow()
|
|
{
|
|
if (!canBridgeWindow)
|
|
{
|
|
canBridgeWindow = new CANBridgeWindow(model->getListReference());
|
|
}
|
|
canBridgeWindow->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::showUDSFirmwareUploaderWindow()
|
|
{
|
|
if (!udsFirmwareUploaderWindow)
|
|
{
|
|
udsFirmwareUploaderWindow = new UDSFirmwareUploaderWindow(model->getListReference());
|
|
}
|
|
udsFirmwareUploaderWindow->show();
|
|
}
|
|
|
|
void MainWindow::showComparisonWindow()
|
|
{
|
|
if (!comparatorWindow)
|
|
{
|
|
comparatorWindow = new FileComparatorWindow();
|
|
}
|
|
comparatorWindow->show();
|
|
}
|
|
|
|
void MainWindow::showDBCComparisonWindow()
|
|
{
|
|
if (!dbcComparatorWindow)
|
|
{
|
|
dbcComparatorWindow = new DBCComparatorWindow();
|
|
}
|
|
dbcComparatorWindow->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(uint32_t,double)), this, SLOT(gotCenterTimeID(int32_t,double)));
|
|
connect(this, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
}
|
|
|
|
if (lastGraphingWindow)
|
|
{
|
|
connect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), flowViewWindow, SLOT(gotCenterTimeID(int32_t,double)));
|
|
connect(flowViewWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), lastGraphingWindow, 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();
|
|
}
|