diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 0ad1aad..a42ad47 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -15,7 +15,8 @@ DEFINES += QCUSTOMPLOT_USE_OPENGL TARGET = SavvyCAN TEMPLATE = app -SOURCES += main.cpp\ +SOURCES += main.cpp\ + re/dbccomparatorwindow.cpp \ mainwindow.cpp \ canframemodel.cpp \ utility.cpp \ @@ -75,7 +76,8 @@ SOURCES += main.cpp\ HEADERS += mainwindow.h \ can_structs.h \ - canframemodel.h \ + canframemodel.h \ \ + re/dbccomparatorwindow.h \ utility.h \ qcustomplot.h \ frameplaybackwindow.h \ @@ -136,6 +138,7 @@ HEADERS += mainwindow.h \ filterutility.h FORMS += ui/candatagrid.ui \ + ui/dbccomparatorwindow.ui \ ui/dbcmessageeditor.ui \ ui/connectionwindow.ui \ ui/dbcloadsavewindow.ui \ diff --git a/dbc/dbc_classes.h b/dbc/dbc_classes.h index 5d32b67..ac9279e 100644 --- a/dbc/dbc_classes.h +++ b/dbc/dbc_classes.h @@ -74,6 +74,11 @@ public: DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name); DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx); + + friend bool operator<(const DBC_NODE& l, const DBC_NODE& r) + { + return (l.name.toLower() < r.name.toLower()); + } }; class DBC_MESSAGE; //forward reference so that DBC_SIGNAL can compile before we get to real definition of DBC_MESSAGE @@ -107,6 +112,11 @@ public: //TODO: this is sloppy. It shouldn't all be public! QString makePrettyOutput(double floatVal, int64_t intVal, bool outputName = true); DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name); DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx); + + friend bool operator<(const DBC_SIGNAL& l, const DBC_SIGNAL& r) + { + return (l.name.toLower() < r.name.toLower()); + } }; class DBCSignalHandler; //forward declaration to keep from having to include dbchandler.h in this file and thus create a loop @@ -129,6 +139,11 @@ public: DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name); DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx); + + friend bool operator<(const DBC_MESSAGE& l, const DBC_MESSAGE& r) + { + return (l.name.toLower() < r.name.toLower()); + } }; diff --git a/dbc/dbchandler.cpp b/dbc/dbchandler.cpp index c1db554..e88a658 100644 --- a/dbc/dbchandler.cpp +++ b/dbc/dbchandler.cpp @@ -92,6 +92,11 @@ int DBCSignalHandler::getCount() return sigs.count(); } +void DBCSignalHandler::sort() +{ + std::sort(sigs.begin(), sigs.end()); +} + DBC_MESSAGE* DBCMessageHandler::findMsgByID(uint32_t id) { if (messages.count() == 0) return nullptr; @@ -241,6 +246,15 @@ int DBCMessageHandler::getCount() return messages.count(); } +void DBCMessageHandler::sort() +{ + std::sort(messages.begin(), messages.end()); + for (int i = 0; i < messages.count(); i++) + { + messages[i].sigHandler->sort(); + } +} + bool DBCMessageHandler::filterLabeling() { return filterLabelingEnabled; @@ -266,6 +280,7 @@ DBCFile::DBCFile() messageHandler = new DBCMessageHandler; messageHandler->setMatchingCriteria(EXACT); messageHandler->setFilterLabeling(false); + isDirty = false; } DBCFile::DBCFile(const DBCFile& cpy) : QObject() @@ -283,6 +298,7 @@ DBCFile::DBCFile(const DBCFile& cpy) : QObject() dbc_nodes.append(cpy.dbc_nodes); dbc_attributes.clear(); dbc_attributes.append(cpy.dbc_attributes); + isDirty = cpy.isDirty; } DBCFile& DBCFile::operator=(const DBCFile& cpy) @@ -301,6 +317,12 @@ DBCFile& DBCFile::operator=(const DBCFile& cpy) return *this; } +void DBCFile::sort() +{ + std::sort(dbc_nodes.begin(), dbc_nodes.end()); //sort node names + messageHandler->sort(); //sort messages, each of which sorts its signals too +} + DBC_NODE* DBCFile::findNodeByIdx(int idx) { if (idx < 0) return nullptr; @@ -380,6 +402,17 @@ void DBCFile::findAttributesByType(DBC_ATTRIBUTE_TYPE typ, QList } } +//there's no external way to clear the flag. It is only cleared when the file is saved by this object. +void DBCFile::setDirtyFlag() +{ + isDirty = true; +} + +bool DBCFile::getDirtyFlag() +{ + return isDirty; +} + DBC_MESSAGE* DBCFile::parseMessageLine(QString line) { QRegularExpression regex; @@ -735,6 +768,7 @@ void DBCFile::loadFile(QString fileName) DBC_MESSAGE *currentMessage = nullptr; DBC_ATTRIBUTE attr; int numSigFaults = 0, numMsgFaults = 0; + int linesSinceYield = 0; bool inMultilineBU = false; @@ -761,6 +795,12 @@ void DBCFile::loadFile(QString fileName) rawLine = QString(inFile->readLine()); line = rawLine.simplified(); + linesSinceYield++; + if (linesSinceYield > 100) + { + qApp->processEvents(); + } + if (inMultilineBU) { if (rawLine.startsWith("\t") || rawLine.startsWith(" ")) @@ -998,6 +1038,7 @@ void DBCFile::loadFile(QString fileName) this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function. filePath = fileName.left(fileName.length() - this->fileName.length()); assocBuses = -1; + isDirty = false; } QVariant DBCFile::processAttributeVal(QString input, DBC_ATTRIBUTE_VAL_TYPE typ) @@ -1214,7 +1255,10 @@ void DBCFile::saveFile(QString fileName) msgNumber++; } - msgOutput.append("BO_ " + QString::number(msg->ID) + " " + msg->name + ": " + QString::number(msg->len) + + uint32_t ID = msg->ID; + if (msg->ID > 0x7FF) msg->ID += 0x80000000ul; //set bit 31 if this ID is extended. + + msgOutput.append("BO_ " + QString::number(ID) + " " + msg->name + ": " + QString::number(msg->len) + " " + msg->sender->name + "\n"); if (msg->comment.length() > 0) { @@ -1405,6 +1449,8 @@ void DBCFile::saveFile(QString fileName) outFile->close(); delete outFile; + isDirty = false; + QStringList fileList = fileName.split('/'); this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function. filePath = fileName.left(fileName.length() - this->fileName.length()); @@ -1681,6 +1727,7 @@ DBCFile* DBCHandler::loadJSONFile(QString filename) } } } + thisFile->setDirtyFlag(); return thisFile; } diff --git a/dbc/dbchandler.h b/dbc/dbchandler.h index 5e83525..f2341eb 100644 --- a/dbc/dbchandler.h +++ b/dbc/dbchandler.h @@ -29,6 +29,8 @@ public: bool removeSignal(QString name); void removeAllSignals(); int getCount(); + void sort(); + private: QList sigs; //signals is a reserved word or I'd have used that }; @@ -52,6 +54,8 @@ public: void setMatchingCriteria(MatchingCriteria_t mc); void setFilterLabeling( bool labelFiltering ); bool filterLabeling(); + void sort(); + private: QList messages; MatchingCriteria_t matchingCriteria; @@ -79,6 +83,9 @@ public: QString getPath(); int getAssocBus(); void setAssocBus(int bus); + void setDirtyFlag(); + bool getDirtyFlag(); + void sort(); DBCMessageHandler *messageHandler; QList dbc_nodes; @@ -87,6 +94,7 @@ private: QString fileName; QString filePath; int assocBuses; //-1 = all buses, 0 = first bus, 1 = second bus, etc. + bool isDirty; //has the file been modified? bool parseAttribute(QString inpString, DBC_ATTRIBUTE &attr); QVariant processAttributeVal(QString input, DBC_ATTRIBUTE_VAL_TYPE typ); diff --git a/dbc/dbcmaineditor.cpp b/dbc/dbcmaineditor.cpp index 721b576..e3ea24e 100644 --- a/dbc/dbcmaineditor.cpp +++ b/dbc/dbcmaineditor.cpp @@ -399,6 +399,7 @@ void DBCMainEditor::newNode() itemToNode.insert(nodeItem, nodePtr); ui->treeDBC->addTopLevelItem(nodeItem); ui->treeDBC->setCurrentItem(nodeItem); + dbcFile->setDirtyFlag(); } @@ -465,6 +466,7 @@ void DBCMainEditor::newMessage() itemToMessage.insert(newMsgItem, msgPtr); nodeItem->addChild(newMsgItem); ui->treeDBC->setCurrentItem(newMsgItem); + dbcFile->setDirtyFlag(); } void DBCMainEditor::newSignal() @@ -519,7 +521,7 @@ void DBCMainEditor::newSignal() itemToSignal.insert(newSigItem, sigPtr); msgItem->addChild(newSigItem); ui->treeDBC->setCurrentItem(newSigItem); - + dbcFile->setDirtyFlag(); } //gets confirmation before calling the real routines that delete things @@ -628,6 +630,7 @@ void DBCMainEditor::deleteNode(DBC_NODE *node) itemToNode.remove(currItem); ui->treeDBC->removeItemWidget(currItem, 0); delete currItem; + dbcFile->setDirtyFlag(); } void DBCMainEditor::deleteMessage(DBC_MESSAGE *msg) @@ -648,7 +651,7 @@ void DBCMainEditor::deleteMessage(DBC_MESSAGE *msg) messageToItem.remove(msg); ui->treeDBC->removeItemWidget(currItem, 0); delete currItem; - + dbcFile->setDirtyFlag(); } void DBCMainEditor::deleteSignal(DBC_SIGNAL *sig) @@ -662,4 +665,5 @@ void DBCMainEditor::deleteSignal(DBC_SIGNAL *sig) signalToItem.remove(sig); ui->treeDBC->removeItemWidget(currItem, 0); delete currItem; + dbcFile->setDirtyFlag(); } diff --git a/dbc/dbcmessageeditor.cpp b/dbc/dbcmessageeditor.cpp index c388c17..8b3a426 100644 --- a/dbc/dbcmessageeditor.cpp +++ b/dbc/dbcmessageeditor.cpp @@ -22,6 +22,7 @@ DBCMessageEditor::DBCMessageEditor(QWidget *parent) : [=]() { if (dbcMessage == nullptr) return; + if (dbcMessage->comment != ui->lineComment->text()) dbcFile->setDirtyFlag(); dbcMessage->comment = ui->lineComment->text(); emit updatedTreeInfo(dbcMessage); }); @@ -30,6 +31,7 @@ DBCMessageEditor::DBCMessageEditor(QWidget *parent) : [=]() { if (dbcMessage == nullptr) return; + if (dbcMessage->ID != Utility::ParseStringToNum(ui->lineFrameID->text())) dbcFile->setDirtyFlag(); dbcMessage->ID = Utility::ParseStringToNum(ui->lineFrameID->text()); emit updatedTreeInfo(dbcMessage); }); @@ -38,6 +40,7 @@ DBCMessageEditor::DBCMessageEditor(QWidget *parent) : [=]() { if (dbcMessage == nullptr) return; + if (dbcMessage->name != ui->lineMsgName->text().simplified().replace(' ', '_')) dbcFile->setDirtyFlag(); dbcMessage->name = ui->lineMsgName->text().simplified().replace(' ', '_'); emit updatedTreeInfo(dbcMessage); }); @@ -46,6 +49,7 @@ DBCMessageEditor::DBCMessageEditor(QWidget *parent) : [=]() { if (dbcMessage == nullptr) return; + if (dbcMessage->len != Utility::ParseStringToNum(ui->lineFrameLen->text())) dbcFile->setDirtyFlag(); dbcMessage->len = Utility::ParseStringToNum(ui->lineFrameLen->text()); }); @@ -53,7 +57,7 @@ DBCMessageEditor::DBCMessageEditor(QWidget *parent) : [=]() { QColor newColor = QColorDialog::getColor(dbcMessage->fgColor); - + if (dbcMessage->fgColor != newColor) dbcFile->setDirtyFlag(); dbcMessage->fgColor = newColor; DBC_ATTRIBUTE_VALUE *val = dbcMessage->findAttrValByName("GenMsgForegroundColor"); if (val) @@ -74,7 +78,7 @@ DBCMessageEditor::DBCMessageEditor(QWidget *parent) : [=]() { QColor newColor = QColorDialog::getColor(dbcMessage->bgColor); - + if (dbcMessage->bgColor != newColor) dbcFile->setDirtyFlag(); dbcMessage->bgColor = newColor; DBC_ATTRIBUTE_VALUE *val = dbcMessage->findAttrValByName("GenMsgBackgroundColor"); if (val) diff --git a/dbc/dbcnodeeditor.cpp b/dbc/dbcnodeeditor.cpp index e321fe3..0f25543 100644 --- a/dbc/dbcnodeeditor.cpp +++ b/dbc/dbcnodeeditor.cpp @@ -22,6 +22,7 @@ DBCNodeEditor::DBCNodeEditor(QWidget *parent) : [=]() { if (dbcNode == nullptr) return; + if (dbcNode->comment != ui->lineComment->text()) dbcFile->setDirtyFlag(); dbcNode->comment = ui->lineComment->text(); emit updatedTreeInfo(dbcNode); }); @@ -30,6 +31,7 @@ DBCNodeEditor::DBCNodeEditor(QWidget *parent) : [=]() { if (dbcNode == nullptr) return; + if (dbcNode->name != ui->lineMsgName->text()) dbcFile->setDirtyFlag(); dbcNode->name = ui->lineMsgName->text(); emit updatedTreeInfo(dbcNode); }); diff --git a/dbc/dbcsignaleditor.cpp b/dbc/dbcsignaleditor.cpp index 7fd4016..ce69871 100644 --- a/dbc/dbcsignaleditor.cpp +++ b/dbc/dbcsignaleditor.cpp @@ -44,6 +44,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : [=]() { if (currentSignal == nullptr) return; + if (currentSignal->intelByteOrder != ui->cbIntelFormat->isChecked()) dbcFile->setDirtyFlag(); currentSignal->intelByteOrder = ui->cbIntelFormat->isChecked(); fillSignalForm(currentSignal); }); @@ -52,7 +53,9 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : [=]() { if (currentSignal == nullptr) return; - currentSignal->receiver = dbcFile->findNodeByName(ui->comboReceiver->currentText()); + DBC_NODE *node = dbcFile->findNodeByName(ui->comboReceiver->currentText()); + if (currentSignal->receiver != node) dbcFile->setDirtyFlag(); + currentSignal->receiver = node; }); connect(ui->comboType, &QComboBox::currentTextChanged, [=]() @@ -80,6 +83,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : currentSignal->valType = STRING; break; } + dbcFile->setDirtyFlag(); fillSignalForm(currentSignal); }); connect(ui->txtBias, &QLineEdit::editingFinished, @@ -89,7 +93,11 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : double temp; bool result; temp = ui->txtBias->text().toDouble(&result); - if (result) currentSignal->bias = temp; + if (result) + { + if (currentSignal->bias != temp) dbcFile->setDirtyFlag(); + currentSignal->bias = temp; + } }); connect(ui->txtMaxVal, &QLineEdit::editingFinished, @@ -99,7 +107,11 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : double temp; bool result; temp = ui->txtMaxVal->text().toDouble(&result); - if (result) currentSignal->max = temp; + if (result) + { + if (currentSignal->max != temp) dbcFile->setDirtyFlag(); + currentSignal->max = temp; + } }); connect(ui->txtMinVal, &QLineEdit::editingFinished, @@ -109,7 +121,11 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : double temp; bool result; temp = ui->txtMinVal->text().toDouble(&result); - if (result) currentSignal->min = temp; + if (result) + { + if (currentSignal->min != temp) dbcFile->setDirtyFlag(); + currentSignal->min = temp; + } }); connect(ui->txtScale, &QLineEdit::editingFinished, [=]() @@ -118,12 +134,17 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : double temp; bool result; temp = ui->txtScale->text().toDouble(&result); - if (result) currentSignal->factor = temp; + if (result) + { + if (currentSignal->factor != temp) dbcFile->setDirtyFlag(); + currentSignal->factor = temp; + } }); connect(ui->txtComment, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; + if (currentSignal->comment != ui->txtComment->text().simplified().replace(' ','_')) dbcFile->setDirtyFlag(); currentSignal->comment = ui->txtComment->text().simplified().replace(' ', '_'); emit updatedTreeInfo(currentSignal); }); @@ -132,6 +153,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : [=]() { if (currentSignal == nullptr) return; + if (currentSignal->unitName != ui->txtUnitName->text().simplified().replace(' ','_')) dbcFile->setDirtyFlag(); currentSignal->unitName = ui->txtUnitName->text().simplified().replace(' ', '_'); }); connect(ui->txtBitLength, &QLineEdit::textChanged, @@ -142,6 +164,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : temp = Utility::ParseStringToNum(ui->txtBitLength->text()); if (temp < 1) return; if (temp > 64) return; + if (currentSignal->signalSize != temp) dbcFile->setDirtyFlag(); if (currentSignal->valType != SP_FLOAT && currentSignal->valType != DP_FLOAT) currentSignal->signalSize = temp; fillSignalForm(currentSignal); @@ -151,6 +174,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : { if (currentSignal == nullptr) return; QString tempNameStr = ui->txtName->text().simplified().replace(' ', '_'); + if (currentSignal->name != tempNameStr) dbcFile->setDirtyFlag(); if (tempNameStr.length() > 0) currentSignal->name = tempNameStr; //need to update the tree too. emit updatedTreeInfo(currentSignal); @@ -162,6 +186,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : if (currentSignal == nullptr) return; int temp; temp = Utility::ParseStringToNum(ui->txtMultiplexValue->text()); + if (currentSignal->multiplexValue != temp) dbcFile->setDirtyFlag(); //TODO: could look up the multiplexor and ensure that the value is within a range that the multiplexor could return currentSignal->multiplexValue = temp; }); @@ -175,6 +200,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : //if the set multiplexor for the message was this signal then clear it if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = nullptr; } + dbcFile->setDirtyFlag(); }); connect(ui->rbMultiplexor, &QRadioButton::toggled, @@ -189,6 +215,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : //we just set that this is the multiplexor so update the message to show that as well. dbcMessage->multiplexorSignal = currentSignal; } + dbcFile->setDirtyFlag(); }); connect(ui->rbNotMulti, &QRadioButton::toggled, @@ -200,6 +227,7 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : currentSignal->isMultiplexor = false; if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = nullptr; } + dbcFile->setDirtyFlag(); }); installEventFilter(this); diff --git a/mainwindow.cpp b/mainwindow.cpp index ab8e7c5..4b32a54 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -80,6 +80,7 @@ MainWindow::MainWindow(QWidget *parent) : bisectWindow = nullptr; signalViewerWindow = nullptr; temporalGraphWindow = nullptr; + dbcComparatorWindow = nullptr; dbcHandler = DBCHandler::getReference(); bDirty = false; inhibitFilterUpdate = false; @@ -111,6 +112,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->actionSave_Decoded_Frames, &QAction::triggered, this, &MainWindow::handleSaveDecoded); connect(ui->actionSingle_Multi_State_2, &QAction::triggered, this, &MainWindow::showSingleMultiWindow); connect(ui->actionFile_Comparison, &QAction::triggered, this, &MainWindow::showComparisonWindow); + connect(ui->actionDBC_Comparison, &QAction::triggered, this, &MainWindow::showDBCComparisonWindow); connect(ui->actionScripting_INterface, &QAction::triggered, this, &MainWindow::showScriptingWindow); connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming); connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::showSettingsDialog); @@ -257,12 +259,31 @@ void MainWindow::exitApp() 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) { - Q_UNUSED(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) @@ -1001,6 +1022,15 @@ void MainWindow::showComparisonWindow() comparatorWindow->show(); } +void MainWindow::showDBCComparisonWindow() +{ + if (!dbcComparatorWindow) + { + dbcComparatorWindow = new DBCComparatorWindow(); + } + dbcComparatorWindow->show(); +} + void MainWindow::showSingleMultiWindow() { if (!discreteStateWindow) diff --git a/mainwindow.h b/mainwindow.h index d3a45dd..68b8cec 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -32,6 +32,7 @@ #include "motorcontrollerconfigwindow.h" #include "signalviewerwindow.h" #include "re/temporalgraphwindow.h" +#include "re/dbccomparatorwindow.h" class CANConnection; class ConnectionWindow; @@ -85,6 +86,7 @@ private slots: void showBisectWindow(); void showSignalViewer(); void showTemporalGraphWindow(); + void showDBCComparisonWindow(); void exitApp(); void handleSaveDecoded(); void connectionStatusUpdated(int conns); @@ -165,6 +167,7 @@ private: BisectWindow* bisectWindow; SignalViewerWindow *signalViewerWindow; TemporalGraphWindow *temporalGraphWindow; + DBCComparatorWindow *dbcComparatorWindow; //various private storage QLabel lbStatusConnected; diff --git a/re/dbccomparatorwindow.cpp b/re/dbccomparatorwindow.cpp new file mode 100644 index 0000000..b4f2cc1 --- /dev/null +++ b/re/dbccomparatorwindow.cpp @@ -0,0 +1,399 @@ +#include "dbccomparatorwindow.h" +#include "ui_dbccomparatorwindow.h" +#include "helpwindow.h" +#include +#include +#include + +DBCComparatorWindow::DBCComparatorWindow(QWidget *parent) : + QDialog(parent), + ui(new Ui::DBCComparatorWindow) +{ + ui->setupUi(this); + setWindowFlags(Qt::Window); + + connect(ui->btnDBCFile1, SIGNAL(clicked(bool)), this, SLOT(loadFirstFile())); + connect(ui->btnDBCFile2, SIGNAL(clicked(bool)), this, SLOT(loadSecondFile())); + connect(ui->btnSaveDetails, SIGNAL(clicked(bool)), this, SLOT(saveDetails())); + + ui->lblFirstFile->setText(""); + ui->lblSecondFile->setText(""); + + firstDBC = nullptr; + secondDBC = nullptr; + + installEventFilter(this); +} + +DBCComparatorWindow::~DBCComparatorWindow() +{ + removeEventFilter(this); + delete ui; +} + +void DBCComparatorWindow::showEvent(QShowEvent *) +{ + readSettings(); +} + +void DBCComparatorWindow::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event) + writeSettings(); +} + +bool DBCComparatorWindow::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("filecomparison.html"); + break; + } + return true; + } else { + // standard event processing + return QObject::eventFilter(obj, event); + } + return false; +} + +void DBCComparatorWindow::readSettings() +{ + QSettings settings; + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + resize(settings.value("DBCComparator/WindowSize", QSize(720, 631)).toSize()); + move(settings.value("DBCComparator/WindowPos", QPoint(50, 50)).toPoint()); + } +} + +void DBCComparatorWindow::writeSettings() +{ + QSettings settings; + + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + settings.setValue("DBCComparator/WindowSize", size()); + settings.setValue("DBCComparator/WindowPos", pos()); + } +} + +QString DBCComparatorWindow::loadDBC(DBCFile **file) +{ + QString filename; + QFileDialog dialog; + QSettings settings; + + QStringList filters; + filters.append(QString(tr("DBC Files (*.dbc *.DBC)"))); + + dialog.setDirectory(settings.value("FileIO/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]; + + QProgressDialog progress(qApp->activeWindow()); + progress.setWindowModality(Qt::WindowModal); + progress.setLabelText("Loading file..."); + progress.setCancelButton(nullptr); + progress.setRange(0,0); + progress.setMinimumDuration(0); + progress.show(); + + qApp->processEvents(); + + if (dialog.selectedNameFilter() == filters[0]) + { + if (file[0]) delete file[0]; + file[0] = new DBCFile; + file[0]->loadFile(filename); + qDebug() << "Loaded the DBC file into first slot"; + } + + progress.cancel(); + return filename; + } + return QString(); +} + +void DBCComparatorWindow::loadFirstFile() +{ + QString filename; + filename = loadDBC(&firstDBC); + ui->lblFirstFile->setText(filename); + if (firstDBC && secondDBC) calculateDetails(); +} + +void DBCComparatorWindow::loadSecondFile() +{ + QString filename; + filename = loadDBC(&secondDBC); + ui->lblSecondFile->setText(filename); + if (firstDBC && secondDBC) calculateDetails(); +} + +void DBCComparatorWindow::calculateDetails() +{ + QProgressDialog progress(this); + progress.setWindowModality(Qt::WindowModal); + progress.setLabelText("Calculating differences"); + progress.setCancelButton(nullptr); + progress.setRange(0,0); + progress.setMinimumDuration(0); + progress.show(); + + qApp->processEvents(); + + firstDBC->sort(); + secondDBC->sort(); + + + ui->treeDetails->clear(); + + //Find nodes missing in one of the DBC files + + QTreeWidgetItem *nodeDiffRoot = new QTreeWidgetItem(); + nodeDiffRoot->setText(0, "Node Differences"); + QTreeWidgetItem *nodesMissingDBCFirst = new QTreeWidgetItem(); + nodesMissingDBCFirst->setText(0, "Nodes not found in first DBC"); + QTreeWidgetItem *nodesMissingDBCSecond = new QTreeWidgetItem(); + nodesMissingDBCSecond->setText(0, "Nodes not found in second DBC"); + nodeDiffRoot->addChild(nodesMissingDBCFirst); + nodeDiffRoot->addChild(nodesMissingDBCSecond); + ui->treeDetails->addTopLevelItem(nodeDiffRoot); + + for (int i = 0; i < firstDBC->dbc_nodes.count(); i++) + { + QString nodeName = firstDBC->dbc_nodes[i].name; + DBC_NODE *node = secondDBC->findNodeByName(nodeName); + if (!node) + { + QTreeWidgetItem *missingNodeItem = new QTreeWidgetItem(); + missingNodeItem->setText(0, nodeName); + nodesMissingDBCSecond->addChild(missingNodeItem); + } + } + + for (int i = 0; i < secondDBC->dbc_nodes.count(); i++) + { + QString nodeName = secondDBC->dbc_nodes[i].name; + DBC_NODE *node = firstDBC->findNodeByName(nodeName); + if (!node) + { + QTreeWidgetItem *missingNodeItem = new QTreeWidgetItem(); + missingNodeItem->setText(0, nodeName); + nodesMissingDBCFirst->addChild(missingNodeItem); + } + } + + + + //Find messages that are missing in one of the DBC files + //If a message exists in both then check for missing signals + + QTreeWidgetItem *msgDiffRoot = new QTreeWidgetItem(); + msgDiffRoot->setText(0, "Message Differences"); + QTreeWidgetItem *msgMissingDBCFirst = new QTreeWidgetItem(); + msgMissingDBCFirst->setText(0, "Messages not found in first DBC"); + QTreeWidgetItem *msgMissingDBCSecond = new QTreeWidgetItem(); + msgMissingDBCSecond->setText(0, "Messages not found in second DBC"); + QTreeWidgetItem *msgSignalsDiff = new QTreeWidgetItem(); + msgSignalsDiff->setText(0, "Messages with missing signals"); + QTreeWidgetItem *sigDiffOne = new QTreeWidgetItem(); + sigDiffOne->setText(0, "Missing from first DBC"); + QTreeWidgetItem *sigDiffTwo = new QTreeWidgetItem(); + sigDiffTwo->setText(0, "Missing from second DBC"); + QTreeWidgetItem *sigModifiedRoot = new QTreeWidgetItem(); + sigModifiedRoot->setText(0, "Modified Signals"); + msgSignalsDiff->addChild(sigDiffOne); + msgSignalsDiff->addChild(sigDiffTwo); + + msgDiffRoot->addChild(msgMissingDBCFirst); + msgDiffRoot->addChild(msgMissingDBCSecond); + msgDiffRoot->addChild(msgSignalsDiff); + msgDiffRoot->addChild(sigModifiedRoot); + + ui->treeDetails->addTopLevelItem(msgDiffRoot); + + QTreeWidgetItem *msgItem; + QTreeWidgetItem *sigTemp; + + for (int i = 0; i < firstDBC->messageHandler->getCount(); i++) + { + DBC_MESSAGE *thisMsg = firstDBC->messageHandler->findMsgByIdx(i); + QString msgName = thisMsg->name; + DBC_MESSAGE *otherMsg = secondDBC->messageHandler->findMsgByName(msgName); + if (!otherMsg) + { + QTreeWidgetItem *missingMsgItem = new QTreeWidgetItem(); + missingMsgItem->setText(0, msgName); + msgMissingDBCSecond->addChild(missingMsgItem); + } + else //both have Msg. Check sigs for missing + { + bool thisMsgHasMissing = false; + bool thisMsgHasMods = false; + + for (int i = 0; i < thisMsg->sigHandler->getCount(); i++) + { + DBC_SIGNAL *thisSig = thisMsg->sigHandler->findSignalByIdx(i); + QString sigName = thisSig->name; + DBC_SIGNAL *otherSig = otherMsg->sigHandler->findSignalByName(sigName); + if (!otherSig) + { + QTreeWidgetItem *missingSigItem = new QTreeWidgetItem(); + missingSigItem->setText(0, sigName); + if (!thisMsgHasMissing) + { + thisMsgHasMissing = true; + msgItem = new QTreeWidgetItem(); + msgItem->setText(0, msgName); + sigDiffTwo->addChild(msgItem); + } + msgItem->addChild(missingSigItem); + } + else //signal exists on both sides. See if it as changed position or length + { + bool didChange = false; + QTreeWidgetItem *sigItem = new QTreeWidgetItem(); + sigItem->setText(0, sigName); + if (thisSig->startBit != otherSig->startBit) + { + didChange = true; + QTreeWidgetItem *sigStartBit = new QTreeWidgetItem(); + sigStartBit->setText(0, "Start Bit First DBC: " + QString::number(thisSig->startBit) + " Second: " + QString::number(otherSig->startBit)); + sigItem->addChild(sigStartBit); + } + if (thisSig->signalSize != otherSig->signalSize) + { + didChange = true; + QTreeWidgetItem *sigSize = new QTreeWidgetItem(); + sigSize->setText(0, "Size First DBC: " + QString::number(thisSig->signalSize) + " Second: " + QString::number(otherSig->signalSize)); + sigItem->addChild(sigSize); + } + if (thisSig->bias != otherSig->bias) + { + didChange = true; + QTreeWidgetItem *sigBias = new QTreeWidgetItem(); + sigBias->setText(0, "Bias First DBC: " + QString::number(thisSig->bias) + " Second: " + QString::number(otherSig->bias)); + sigItem->addChild(sigBias); + } + if (thisSig->factor != otherSig->factor) + { + didChange = true; + QTreeWidgetItem *sigFactor = new QTreeWidgetItem(); + sigFactor->setText(0, "Factor First DBC: " + QString::number(thisSig->factor) + " Second: " + QString::number(otherSig->factor)); + sigItem->addChild(sigFactor); + } + if (didChange) + { + if (!thisMsgHasMods) + { + thisMsgHasMods = true; + sigTemp = new QTreeWidgetItem(); + sigTemp->setText(0, msgName); + sigModifiedRoot->addChild(sigTemp); + } + sigTemp->addChild(sigItem); + } + } + } + thisMsgHasMissing = false; + for (int i = 0; i < otherMsg->sigHandler->getCount(); i++) + { + DBC_SIGNAL *thisSig = otherMsg->sigHandler->findSignalByIdx(i); + QString sigName = thisSig->name; + DBC_SIGNAL *otherSig = thisMsg->sigHandler->findSignalByName(sigName); + if (!otherSig) + { + QTreeWidgetItem *missingSigItem = new QTreeWidgetItem(); + missingSigItem->setText(0, sigName); + if (!thisMsgHasMissing) + { + thisMsgHasMissing = true; + msgItem = new QTreeWidgetItem(); + msgItem->setText(0, msgName); + sigDiffOne->addChild(msgItem); + } + msgItem->addChild(missingSigItem); + } + } + } + } + + for (int i = 0; i < secondDBC->messageHandler->getCount(); i++) + { + QString msgName = secondDBC->messageHandler->findMsgByIdx(i)->name; + DBC_MESSAGE *msg = firstDBC->messageHandler->findMsgByName(msgName); + if (!msg) + { + QTreeWidgetItem *missingMsgItem = new QTreeWidgetItem(); + missingMsgItem->setText(0, msgName); + msgMissingDBCFirst->addChild(missingMsgItem); + } + } + + QSettings settings; + if (settings.value("InfoCompare/AutoExpand", false).toBool()) + { + ui->treeDetails->expandAll(); + } + + progress.cancel(); + + qApp->processEvents(); +} + +void DBCComparatorWindow::saveDetails() +{ + QString filename; + QFileDialog dialog(this); + QSettings settings; + + QStringList filters; + filters.append(QString(tr("Text File (*.txt)"))); + + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setNameFilters(filters); + dialog.setViewMode(QFileDialog::Detail); + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setDirectory(settings.value("FileComparator/LoadSaveDirectory", dialog.directory().path()).toString()); + + if (dialog.exec() == QDialog::Accepted) + { + filename = dialog.selectedFiles()[0]; + settings.setValue("FileComparator/LoadSaveDirectory", dialog.directory().path()); + if (!filename.contains('.')) filename += ".txt"; + QFile *outFile = new QFile(filename); + + if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + return; + + QTreeWidget *tree = ui->treeDetails; + + + QTreeWidgetItemIterator it(tree); + while (*it) { + QTreeWidgetItem *item = *it; + QString itemText = item->text(0); + while (item->parent()) + { + outFile->write(" "); + item = item->parent(); + } + outFile->write(itemText.toUtf8() + "\n"); + ++it; + } + + outFile->close(); + + } +} + diff --git a/re/dbccomparatorwindow.h b/re/dbccomparatorwindow.h new file mode 100644 index 0000000..b05f155 --- /dev/null +++ b/re/dbccomparatorwindow.h @@ -0,0 +1,45 @@ +#ifndef DBCCOMPARATORWINDOW_H +#define DBCCOMPARATORWINDOW_H + +#include +#include +#include +#include "framefileio.h" +#include "dbc/dbc_classes.h" +#include "dbc/dbchandler.h" +#include "utility.h" + +namespace Ui { +class DBCComparatorWindow; +} + +class DBCComparatorWindow : public QDialog +{ + Q_OBJECT + +public: + explicit DBCComparatorWindow(QWidget *parent = 0); + ~DBCComparatorWindow(); + +private slots: + void loadFirstFile(); + void loadSecondFile(); + void saveDetails(); + +private: + Ui::DBCComparatorWindow *ui; + DBCFile *firstDBC; + DBCFile *secondDBC; + QString firstDBCFilename; + QString secondDBCFilename; + + void calculateDetails(); + void showEvent(QShowEvent *); + void closeEvent(QCloseEvent *event); + bool eventFilter(QObject *obj, QEvent *event); + QString loadDBC(DBCFile **file); + void readSettings(); + void writeSettings(); +}; + +#endif //DBCCOMPARATORWINDOW_H diff --git a/ui/dbccomparatorwindow.ui b/ui/dbccomparatorwindow.ui new file mode 100644 index 0000000..6f736d6 --- /dev/null +++ b/ui/dbccomparatorwindow.ui @@ -0,0 +1,131 @@ + + + DBCComparatorWindow + + + + 0 + 0 + 720 + 631 + + + + DBC Comparator + + + + + + + + + + + 16 + + + + Side 1 + + + Qt::AlignCenter + + + + + + + TextLabel + + + + + + + Load New File + + + + + + + + + Qt::Vertical + + + + + + + + + + 16 + + + + Side 2 + + + Qt::AlignCenter + + + + + + + TextLabel + + + + + + + + + Load A File + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + 1 + + + + + + + + Save Details to File + + + + + + + btnDBCFile1 + btnDBCFile2 + treeDetails + btnSaveDetails + + + + diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 46acf44..c50a4ae 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -304,6 +304,7 @@ + @@ -531,6 +532,11 @@ Temporal Graph + + + DBC Comparison + +