#include "mainwindow.h" #include "ui_mainwindow.h" #include "can_structs.h" #include #include #include #include "connections/canconmanager.h" #include "connections/connectionwindow.h" #include "helpwindow.h" #include "utility.h" #include "filterutility.h" #include /* 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>(); qRegisterMetaTypeStreamOperators>(); #endif useHex = true; useColorsByCanId = false; selfRef = this; this->setWindowTitle("GiesCAN 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))); ui->tableSimpleSender->setVisible(false); 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(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", 400).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) { if (settings.value("Main/UseStartDate", false).toBool()) { ts = TS_START_DATE; } else { ts = TS_CLOCK; } } tempBool = settings.value("Main/TimeMillis", false).toBool(); if (tempBool) ts = TS_MILLIS; model->setTimeStyle(ts); Utility::startDate = QDateTime::fromString(settings.value("Main/StartDate", "2025-01-01T00:00:00.000000").toString(), Qt::ISODateWithMs); 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 *filters = model->getFiltersReference(); const QMap *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::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 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 *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 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 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 *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> 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(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; mID) found = true; } if(found == false) msgsAndColumns.append(QPair(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(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; iID) { 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 *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(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))); } if (playbackWindow) { connect(lastGraphingWindow, SIGNAL(sendCenterTimeID(uint32_t,double)), playbackWindow, SLOT(gotCenterTimeID(uint32_t,double))); connect(playbackWindow, SIGNAL(sendCurrentTimestamp(double)), lastGraphingWindow, SLOT(gotPlaybackTimestamp(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 *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()); connect(this, SIGNAL(sendCenterTimeID(uint32_t, double)), playbackWindow, SLOT(gotCenterTimeID(uint32_t, double))); for (auto &&graphWindow : graphWindows) { connect(graphWindow, SIGNAL(sendCenterTimeID(uint32_t, double)), playbackWindow, SLOT(gotCenterTimeID(uint32_t, double))); connect(playbackWindow, SIGNAL(sendCurrentTimestamp(double)), graphWindow, SLOT(gotPlaybackTimestamp(double))); } } 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*)), worker, SLOT(sendFrameBatch(const QList*))); } 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(); }