From dda3109f604339a43a375cdb83bf688a2ecf6338 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Sun, 24 Sep 2023 21:38:04 -0400 Subject: [PATCH] Added a new dialog that helps to construct triggers for custom frame sender. Reworked custom sender window in anticipation of new functionality for triggers and modifiers --- SavvyCAN.pro | 3 + can_trigger_structs.h | 14 ++ framesenderwindow.cpp | 185 +++++++++++++++-------- framesenderwindow.h | 22 +++ triggerdialog.cpp | 341 ++++++++++++++++++++++++++++++++++++++++++ triggerdialog.h | 40 +++++ triggerdialog.ui | 191 +++++++++++++++++++++++ 7 files changed, 731 insertions(+), 65 deletions(-) create mode 100644 triggerdialog.cpp create mode 100644 triggerdialog.h create mode 100644 triggerdialog.ui diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 64cbc55..f9c49be 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -42,6 +42,7 @@ SOURCES += main.cpp\ mainwindow.cpp \ canframemodel.cpp \ simplecrypt.cpp \ + triggerdialog.cpp \ utility.cpp \ qcustomplot.cpp \ frameplaybackwindow.cpp \ @@ -132,6 +133,7 @@ HEADERS += mainwindow.h \ qcpaxistickerhex.h \ re/dbccomparatorwindow.h \ simplecrypt.h \ + triggerdialog.h \ utility.h \ qcustomplot.h \ frameplaybackwindow.h \ @@ -193,6 +195,7 @@ HEADERS += mainwindow.h \ pcaplite.h FORMS += ui/candatagrid.ui \ + triggerdialog.ui \ ui/canbridgewindow.ui \ ui/dbcnodeduplicateeditor.ui \ ui/dbccomparatorwindow.ui \ diff --git a/can_trigger_structs.h b/can_trigger_structs.h index ce58d91..32ef0ef 100644 --- a/can_trigger_structs.h +++ b/can_trigger_structs.h @@ -5,6 +5,16 @@ #include +enum TriggerMask +{ + TRG_ID = 1, + TRG_MS = 2, + TRG_COUNT = 4, + TRG_BUS = 8, + TRG_SIGNAL = 16, + TRG_SIGVAL = 32, +}; + //Stores a single trigger. class Trigger { @@ -16,6 +26,10 @@ public: int maxCount; //max # of these frames to trigger for int currCount; //how many we have triggered for so far. int bus; //which bus to monitor (-1 if we aren't picky) + QString sigName; //which signal to trigger on + int64_t sigValueInt; //value to trigger on (integer) + double sigValueDbl; //value to trigger on (floating point) + uint32_t triggerMask; }; //referece for a source location for a single modifier. diff --git a/framesenderwindow.cpp b/framesenderwindow.cpp index dd09216..70127a4 100644 --- a/framesenderwindow.cpp +++ b/framesenderwindow.cpp @@ -7,6 +7,7 @@ #include "mainwindow.h" #include "helpwindow.h" #include "connections/canconmanager.h" +#include "triggerdialog.h" /* * notes: need to ensure that you grab pointers when modifying data structures and dont @@ -24,6 +25,8 @@ FrameSenderWindow::FrameSenderWindow(const QVector *frames, QWidget *p modelFrames = frames; + dbcHandler = DBCHandler::getReference(); + intervalTimer = new QTimer(); intervalTimer->setTimerType(Qt::PreciseTimer); intervalTimer->setInterval(1); @@ -32,6 +35,7 @@ FrameSenderWindow::FrameSenderWindow(const QVector *frames, QWidget *p createBlankRow(); connect(ui->tableSender, SIGNAL(cellChanged(int,int)), this, SLOT(onCellChanged(int,int))); + connect(ui->tableSender, SIGNAL(cellDoubleClicked(int, int)), SLOT(onCellDoubleTap(int, int))); connect(intervalTimer, SIGNAL(timeout()), this, SLOT(handleTick())); connect(ui->btnClearGrid, SIGNAL(clicked(bool)), this, SLOT(clearGrid())); connect(ui->btnDisableAll, SIGNAL(clicked(bool)), this, SLOT(disableAll())); @@ -48,19 +52,20 @@ FrameSenderWindow::FrameSenderWindow(const QVector *frames, QWidget *p void FrameSenderWindow::setupGrid() { QStringList headers; - headers << "En" << "Bus" << "ID" << "Len" << "Ext" << "Rem" << "Data" + headers << "En" << "Bus" << "ID" << "MsgName" << "Len" << "Ext" << "Rem" << "Data" << "Trigger" << "Modifications" << "Count"; - ui->tableSender->setColumnCount(10); - ui->tableSender->setColumnWidth(0, 50); - ui->tableSender->setColumnWidth(1, 50); - ui->tableSender->setColumnWidth(2, 50); - ui->tableSender->setColumnWidth(3, 50); - ui->tableSender->setColumnWidth(4, 50); - ui->tableSender->setColumnWidth(5, 50); - ui->tableSender->setColumnWidth(6, 220); - ui->tableSender->setColumnWidth(7, 220); - ui->tableSender->setColumnWidth(8, 220); - ui->tableSender->setColumnWidth(9, 80); + ui->tableSender->setColumnCount(11); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_EN, 50); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_BUS, 50); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_ID, 50); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_MSGNAME, 150); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_LEN, 50); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_EXT, 50); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_REM, 50); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_DATA, 220); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_TRIGGER, 220); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_MODS, 220); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_COUNT, 80); ui->tableSender->setHorizontalHeaderLabels(headers); } @@ -100,22 +105,26 @@ void FrameSenderWindow::createBlankRow() item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(Qt::Unchecked); inhibitChanged = true; - ui->tableSender->setItem(row, 0, item); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_EN, item); item = new QTableWidgetItem(); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(Qt::Unchecked); - ui->tableSender->setItem(row, 4, item); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_EXT, item); item = new QTableWidgetItem(); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(Qt::Unchecked); - ui->tableSender->setItem(row, 5, item); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_REM, item); - for (int i = 1; i < 10; i++) + for (int i = 1; i <= ST_COLS::SENDTAB_COL_COUNT; i++) { - if (i != 4 && i != 5) { + if (i != ST_COLS::SENDTAB_COL_EXT && i != ST_COLS::SENDTAB_COL_REM) { item = new QTableWidgetItem(""); + //msgname is looked up via DBC interface and not able to be edited. + //Though, it might be perhaps interesting to allow renaming it to + //a shorter name in some cases? Jury is still out on this one... + if (i == ST_COLS::SENDTAB_COL_MSGNAME) item->setFlags(item->flags() & ~Qt::ItemIsEditable); ui->tableSender->setItem(row, i, item); } } @@ -212,7 +221,7 @@ void FrameSenderWindow::enableAll() { for (int i = 0; i < ui->tableSender->rowCount() - 1; i++) { - ui->tableSender->item(i, 0)->setCheckState(Qt::Checked); + ui->tableSender->item(i, ST_COLS::SENDTAB_COL_EN)->setCheckState(Qt::Checked); sendingData[i].enabled = true; } } @@ -221,7 +230,7 @@ void FrameSenderWindow::disableAll() { for (int i = 0; i < ui->tableSender->rowCount() - 1; i++) { - ui->tableSender->item(i, 0)->setCheckState(Qt::Unchecked); + ui->tableSender->item(i, ST_COLS::SENDTAB_COL_EN)->setCheckState(Qt::Unchecked); sendingData[i].enabled = false; } } @@ -309,14 +318,14 @@ void FrameSenderWindow::saveSenderFile(QString filename) for (int c = 0; c < sendingData.count(); c++) { outString.clear(); - if (ui->tableSender->item(c, 0)->checkState() == Qt::Checked) + if (ui->tableSender->item(c, ST_COLS::SENDTAB_COL_EN)->checkState() == Qt::Checked) { outString = "T#"; } else outString = "F#"; - for (int i = 1; i < 9; i++) + for (int i = 1; i < ST_COLS::SENDTAB_COL_COUNT; i++) { - if (i == 4 || i == 5) { + if (i == ST_COLS::SENDTAB_COL_EXT || i == ST_COLS::SENDTAB_COL_REM) { if (ui->tableSender->item(c, i)->checkState() == Qt::Checked) { outString.append("T"); } else { @@ -359,28 +368,28 @@ void FrameSenderWindow::loadSenderFile(QString filename) int row = ui->tableSender->rowCount(); ui->tableSender->insertRow(row); QTableWidgetItem *item = new QTableWidgetItem(); - ui->tableSender->setItem(row, 0, item); - ui->tableSender->item(row, 0)->setFlags(item->flags() | Qt::ItemIsUserCheckable); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_EN, item); + ui->tableSender->item(row, ST_COLS::SENDTAB_COL_EN)->setFlags(item->flags() | Qt::ItemIsUserCheckable); item = new QTableWidgetItem(); - ui->tableSender->setItem(row, 4, item); - ui->tableSender->item(row, 4)->setFlags(item->flags() | Qt::ItemIsUserCheckable); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_EXT, item); + ui->tableSender->item(row, ST_COLS::SENDTAB_COL_EN)->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(Qt::Unchecked); item = new QTableWidgetItem(); - ui->tableSender->setItem(row, 5, item); - ui->tableSender->item(row, 5)->setFlags(item->flags() | Qt::ItemIsUserCheckable); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_REM, item); + ui->tableSender->item(row, ST_COLS::SENDTAB_COL_REM)->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(Qt::Unchecked); if (tokens[0] == "T") { - ui->tableSender->item(row, 0)->setCheckState(Qt::Checked); + ui->tableSender->item(row, ST_COLS::SENDTAB_COL_EN)->setCheckState(Qt::Checked); } - else ui->tableSender->item(row, 0)->setCheckState(Qt::Unchecked); + else ui->tableSender->item(row, ST_COLS::SENDTAB_COL_EN)->setCheckState(Qt::Unchecked); if (tokens.length() >= 9) { - for (int i = 1; i < 9; i++) + for (int i = 1; i < ST_COLS::SENDTAB_COL_COUNT; i++) { - if (i != 4 && i != 5) { + if (i != ST_COLS::SENDTAB_COL_EXT && i != ST_COLS::SENDTAB_COL_REM) { ui->tableSender->setItem(row, i, new QTableWidgetItem(QString(tokens[i]))); } else { if (tokens[i] == "T") { @@ -389,15 +398,15 @@ void FrameSenderWindow::loadSenderFile(QString filename) } } } else { - ui->tableSender->setItem(row, 1, new QTableWidgetItem(QString(tokens[1]))); - ui->tableSender->setItem(row, 2, new QTableWidgetItem(QString(tokens[2]))); - ui->tableSender->setItem(row, 3, new QTableWidgetItem(QString(tokens[3]))); - ui->tableSender->setItem(row, 6, new QTableWidgetItem(QString(tokens[4]))); - ui->tableSender->setItem(row, 7, new QTableWidgetItem(QString(tokens[5]))); - ui->tableSender->setItem(row, 8, new QTableWidgetItem(QString(tokens[6]))); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_BUS, new QTableWidgetItem(QString(tokens[1]))); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_ID, new QTableWidgetItem(QString(tokens[2]))); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_LEN, new QTableWidgetItem(QString(tokens[3]))); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_DATA, new QTableWidgetItem(QString(tokens[4]))); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_TRIGGER, new QTableWidgetItem(QString(tokens[5]))); + ui->tableSender->setItem(row, ST_COLS::SENDTAB_COL_MODS, new QTableWidgetItem(QString(tokens[6]))); } inhibitChanged = false; - for (int k = 0; k < 9; k++) processCellChange(row, k); + for (int k = 0; k < ST_COLS::SENDTAB_COL_COUNT; k++) processCellChange(row, k); } } @@ -405,6 +414,43 @@ void FrameSenderWindow::loadSenderFile(QString filename) delete inFile; } +void FrameSenderWindow::onCellDoubleTap(int row, int column) +{ + if (column == ST_COLS::SENDTAB_COL_TRIGGER) + { + if (row == ui->tableSender->rowCount() - 1) + { + createBlankRow(); + } + if (row >= sendingData.count()) + { + FrameSendData tempData; + tempData.enabled = false; + tempData.setFrameType(QCanBusFrame::DataFrame); + tempData.setExtendedFrameFormat(false); + sendingData.append(tempData); + } + sendData = nullptr; + sendData = &sendingData[row]; + td = new TriggerDialog(sendData->triggers); + if (td->exec() == QDialog::Accepted) + { + sendData->triggers.clear(); + sendData->triggers.append(td->getUpdatedTriggers()); + //now have to generate the actual trigger text + QString output; + foreach (Trigger trig, sendData->triggers) + { + output += td->buildEntry(trig) + ","; + } + output.chop(1); //don't want the trailing , + ui->tableSender->item(row, ST_COLS::SENDTAB_COL_TRIGGER)->setText(output); + } + delete td; + td = nullptr; + } +} + void FrameSenderWindow::onCellChanged(int row, int col) { if (inhibitChanged) return; @@ -692,7 +738,7 @@ void FrameSenderWindow::processTriggerText(int line) //trigger has two levels of syntactic parsing. First you split by comma to get each //actual trigger. Then you split by spaces to get the tokens within each trigger //trigger = ui->tableSender->item(line, 5)->text().toUpper().trimmed().replace(" ", ""); - trigger = ui->tableSender->item(line, 7)->text().toUpper(); + trigger = ui->tableSender->item(line, ST_COLS::SENDTAB_COL_TRIGGER)->text().toUpper(); if (trigger != "") { QStringList triggers = trigger.split(','); @@ -717,7 +763,7 @@ void FrameSenderWindow::processTriggerText(int line) if (tok.left(2) == "ID") { thisTrigger.ID = Utility::ParseStringToNum(tok.right(tok.length() - 2)); - if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 10000000; + //if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 0; if (thisTrigger.milliseconds == -1) thisTrigger.milliseconds = 0; //by default don't count, just send it upon trigger thisTrigger.readyCount = false; //won't try counting until trigger hits @@ -725,13 +771,13 @@ void FrameSenderWindow::processTriggerText(int line) else if (tok.endsWith("MS")) { thisTrigger.milliseconds = Utility::ParseStringToNum(tok.left(tok.length()-2)); - if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 10000000; - if (thisTrigger.ID == -1) thisTrigger.ID = 0; + //if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 0; + //if (thisTrigger.ID == -1) thisTrigger.ID = 0; } else if (tok.endsWith("X")) { thisTrigger.maxCount = Utility::ParseStringToNum(tok.left(tok.length() - 1)); - if (thisTrigger.ID == -1) thisTrigger.ID = 0; + //if (thisTrigger.ID == -1) thisTrigger.ID = 0; if (thisTrigger.milliseconds == -1) thisTrigger.milliseconds = 10; } else if (tok.startsWith("BUS")) @@ -740,9 +786,9 @@ void FrameSenderWindow::processTriggerText(int line) } } //now, find anything that wasn't set and set it to defaults - if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 10000000; - if (thisTrigger.milliseconds == -1) thisTrigger.milliseconds = 100; - if (thisTrigger.ID == -1) thisTrigger.ID = 0; + //if (thisTrigger.maxCount == -1) thisTrigger.maxCount = 0; + //if (thisTrigger.milliseconds == -1) thisTrigger.milliseconds = 100; + //if (thisTrigger.ID == -1) thisTrigger.ID = 0; sendingData[line].triggers.append(thisTrigger); } } @@ -815,7 +861,7 @@ void FrameSenderWindow::updateGridRow(int idx) FrameSendData *temp = &sendingData[idx]; int gridLine = idx; QString dataString; - QTableWidgetItem *item = ui->tableSender->item(gridLine, 9); + QTableWidgetItem *item = ui->tableSender->item(gridLine, ST_COLS::SENDTAB_COL_COUNT); const unsigned char *data = reinterpret_cast(temp->payload().constData()); int dataLen = temp->payload().length(); @@ -823,7 +869,7 @@ void FrameSenderWindow::updateGridRow(int idx) { item = new QTableWidgetItem(); item->setText(QString::number(temp->count)); - ui->tableSender->setItem(gridLine, 9, item); + ui->tableSender->setItem(gridLine, ST_COLS::SENDTAB_COL_COUNT, item); } else { @@ -837,11 +883,11 @@ void FrameSenderWindow::updateGridRow(int idx) dataString.append(Utility::formatNumber(data[i])); dataString.append(" "); } - ui->tableSender->item(gridLine, 6)->setText(dataString); + ui->tableSender->item(gridLine, ST_COLS::SENDTAB_COL_DATA)->setText(dataString); } else { - ui->tableSender->item(gridLine, 6)->setText(""); + ui->tableSender->item(gridLine, ST_COLS::SENDTAB_COL_DATA)->setText(""); } inhibitChanged = false; } @@ -852,6 +898,7 @@ void FrameSenderWindow::processCellChange(int line, int col) FrameSendData tempData; QStringList tokens; int tempVal; + DBC_MESSAGE *msg; //if this is a new line then create the base object for the line if (line >= sendingData.count()) @@ -870,7 +917,7 @@ void FrameSenderWindow::processCellChange(int line, int col) switch (col) { - case 0: //Enable check box + case ST_COLS::SENDTAB_COL_EN: //Enable check box if (ui->tableSender->item(line, 0)->checkState() == Qt::Checked) { sendingData[line].enabled = true; @@ -878,54 +925,62 @@ void FrameSenderWindow::processCellChange(int line, int col) else sendingData[line].enabled = false; qDebug() << "Setting enabled to " << sendingData[line].enabled; break; - case 1: //Bus designation - tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, 1)->text()); + case ST_COLS::SENDTAB_COL_BUS: //Bus designation + tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, ST_COLS::SENDTAB_COL_BUS)->text()); if (tempVal < -1) tempVal = -1; if (tempVal >= numBuses) tempVal = numBuses - 1; sendingData[line].bus = tempVal; qDebug() << "Setting bus to " << tempVal; break; - case 2: //ID field - tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, 2)->text()); + case ST_COLS::SENDTAB_COL_ID: //ID field + tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, ST_COLS::SENDTAB_COL_ID)->text()); if (tempVal < 0) tempVal = 0; if (tempVal > 0x7FFFFFFF) tempVal = 0x7FFFFFFF; sendingData[line].setFrameId(tempVal); if (sendingData[line].frameId() > 0x7FF) { sendingData[line].setExtendedFrameFormat(true); ui->tableSender->blockSignals(true); - ui->tableSender->item(line, 4)->setCheckState(Qt::Checked); + ui->tableSender->item(line, ST_COLS::SENDTAB_COL_EXT)->setCheckState(Qt::Checked); + ui->tableSender->blockSignals(false); + } + msg = dbcHandler->findMessage(sendingData[line].frameId()); + if (msg) + { + ui->tableSender->blockSignals(true); + ui->tableSender->item(line, ST_COLS::SENDTAB_COL_MSGNAME)->setText(msg->name); + ui->tableSender->item(line, ST_COLS::SENDTAB_COL_LEN)->setText(QString::number(msg->len)); ui->tableSender->blockSignals(false); } qDebug() << "setting ID to " << tempVal; break; - case 3: //length field + case ST_COLS::SENDTAB_COL_LEN: tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, 3)->text()); if (tempVal < 0) tempVal = 0; if (tempVal > 8) tempVal = 8; arr.resize(tempVal); sendingData[line].setPayload(arr); break; - case 4: // Ext + case ST_COLS::SENDTAB_COL_EXT: if (ui->tableSender->item(line, 4)->checkState() == Qt::Checked) { sendingData[line].setExtendedFrameFormat(true); } else { sendingData[line].setExtendedFrameFormat(false); } break; - case 5: // Rem + case ST_COLS::SENDTAB_COL_REM: if (ui->tableSender->item(line, 5)->checkState() == Qt::Checked) { sendingData[line].setFrameType(QCanBusFrame::RemoteRequestFrame); } else { sendingData[line].setFrameType(QCanBusFrame::DataFrame); } break; - case 6: //Data bytes + case ST_COLS::SENDTAB_COL_DATA: //Data bytes for (int i = 0; i < 8; i++) sendingData[line].payload().data()[i] = 0; #if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) - tokens = ui->tableSender->item(line, 6)->text().split(" ", Qt::SkipEmptyParts); + tokens = ui->tableSender->item(line, ST_COLS::SENDTAB_COL_DATA)->text().split(" ", Qt::SkipEmptyParts); #else - tokens = ui->tableSender->item(line, 6)->text().split(" ", QString::SkipEmptyParts); + tokens = ui->tableSender->item(line, ST_COLS::SENDTAB_COL_DATA)->text().split(" ", QString::SkipEmptyParts); #endif arr.clear(); arr.reserve(tokens.count()); @@ -935,10 +990,10 @@ void FrameSenderWindow::processCellChange(int line, int col) } sendingData[line].setPayload(arr); break; - case 7: //triggers + case ST_COLS::SENDTAB_COL_TRIGGER: //triggers processTriggerText(line); break; - case 8: //modifiers + case ST_COLS::SENDTAB_COL_MODS: //modifiers processModifierText(line); break; } diff --git a/framesenderwindow.h b/framesenderwindow.h index c07531d..e8ed6e8 100644 --- a/framesenderwindow.h +++ b/framesenderwindow.h @@ -8,11 +8,28 @@ #include #include "can_structs.h" #include "can_trigger_structs.h" +#include "dbc/dbchandler.h" +#include "triggerdialog.h" namespace Ui { class FrameSenderWindow; } +enum ST_COLS +{ + SENDTAB_COL_EN = 0, + SENDTAB_COL_BUS = 1, + SENDTAB_COL_ID = 2, + SENDTAB_COL_MSGNAME = 3, + SENDTAB_COL_LEN = 4, + SENDTAB_COL_EXT = 5, + SENDTAB_COL_REM = 6, + SENDTAB_COL_DATA = 7, + SENDTAB_COL_TRIGGER = 8, + SENDTAB_COL_MODS = 9, + SENDTAB_COL_COUNT = 10, +}; + class FrameSenderWindow : public QDialog { Q_OBJECT @@ -23,6 +40,7 @@ public: private slots: void onCellChanged(int, int); + void onCellDoubleTap(int, int); void handleTick(); void enableAll(); void disableAll(); @@ -40,6 +58,9 @@ private: QElapsedTimer elapsedTimer; bool inhibitChanged = false; QMutex mutex; + DBCHandler *dbcHandler; + TriggerDialog *td; + FrameSendData *sendData; void createBlankRow(); void doModifiers(int); @@ -57,6 +78,7 @@ private: void processIncomingFrame(CANFrame *frame); bool eventFilter(QObject *obj, QEvent *event); void setupGrid(); + void finishedTriggerDialog(int result); }; #endif // FRAMESENDERWINDOW_H diff --git a/triggerdialog.cpp b/triggerdialog.cpp new file mode 100644 index 0000000..6221dce --- /dev/null +++ b/triggerdialog.cpp @@ -0,0 +1,341 @@ +#include "triggerdialog.h" +#include "ui_triggerdialog.h" +#include + +TriggerDialog::TriggerDialog(QList trigs, QWidget *parent) : + QDialog(parent), + ui(new Ui::TriggerDialog) +{ + ui->setupUi(this); + triggers.clear(); + triggers.append(trigs); //make sure it's just a clone of the existing triggers + + //if there are no triggers then create a default one + if (triggers.count() == 0) + { + Trigger trig; + trig.ID = 0; + trig.bus = 0; + trig.maxCount = 0; + trig.milliseconds = 100; + trig.sigName = ""; + trig.sigValueDbl = 0.0; + trig.sigValueInt = 0; + trig.triggerMask = TriggerMask::TRG_MS; + triggers.append(trig); + } + lastMsgID = 0xFFFFFFFF; + inhibitUpdates = false; + + dbcHandler = DBCHandler::getReference(); + /* + getting the messages possible is complicated by the fact that multiple DBC files + can be loaded and each one can be associated with a different bus. So, this is + tough. At first I suppose we need to list all possible messages but if the user + sets a bus then we should recalculate and only show messages possible on that bus. + */ + ui->cmMsgID->clear(); + QStringList entries; + int numFiles = dbcHandler->getFileCount(); + for (int i = 0; i < numFiles; i++) + { + DBCFile *file = dbcHandler->getFileByIdx(i); + int numMsgs = file->messageHandler->getCount(); + for (int j = 0; j < numMsgs; j++) + { + DBC_MESSAGE *msg = file->messageHandler->findMsgByIdx(j); + entries.append(("0x" + QString::number(msg->ID, 16) + " (" + msg->name + ")")); + } + } + entries.sort(); + ui->cmMsgID->addItems(entries); + + connect(ui->listTriggers, &QListWidget::currentRowChanged, this, &TriggerDialog::FillDetails); + connect(ui->btnDelete, &QPushButton::clicked, this, &TriggerDialog::deleteSelectedTrigger); + connect(ui->btnNew, &QPushButton::clicked, this, &TriggerDialog::addNewTrigger); + connect(ui->btnSave, &QPushButton::clicked, this, &TriggerDialog::saveAndExit); + connect(ui->cbBus, &QCheckBox::stateChanged, this, &TriggerDialog::handleCheckboxes); + connect(ui->cbMillis, &QCheckBox::stateChanged, this, &TriggerDialog::handleCheckboxes); + connect(ui->cbMaxCount, &QCheckBox::stateChanged, this, &TriggerDialog::handleCheckboxes); + connect(ui->cbMsgID, &QCheckBox::stateChanged, this, &TriggerDialog::handleCheckboxes); + connect(ui->cbSigValue, &QCheckBox::stateChanged, this, &TriggerDialog::handleCheckboxes); + connect(ui->cbSignal, &QCheckBox::stateChanged, this, &TriggerDialog::handleCheckboxes); + connect(ui->txtMaxCount, &QLineEdit::textEdited, this, &TriggerDialog::regenerateCurrentListItem); + connect(ui->cmMsgID, &QComboBox::currentTextChanged, this, &TriggerDialog::regenerateCurrentListItem); + connect(ui->cmSignal, &QComboBox::currentTextChanged, this, &TriggerDialog::regenerateCurrentListItem); + connect(ui->txtBus, &QLineEdit::textEdited, this, &TriggerDialog::regenerateCurrentListItem); + connect(ui->txtDelay, &QLineEdit::textEdited, this, &TriggerDialog::regenerateCurrentListItem); + connect(ui->txtSigValue, &QLineEdit::textEdited, this, &TriggerDialog::regenerateCurrentListItem); +} + +TriggerDialog::~TriggerDialog() +{ + delete ui; +} + +QList TriggerDialog::getUpdatedTriggers() +{ + return triggers; +} + +void TriggerDialog::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + regenerateList(); +} + +void TriggerDialog::regenerateList() +{ + ui->listTriggers->clear(); + //iterate over the QList of triggers, show them in the list, + //load the first item in the list, and display it broken down on the right. + foreach(Trigger trig, triggers) + { + ui->listTriggers->addItem(buildEntry(trig)); + } + ui->listTriggers->setCurrentRow(0); + FillDetails(); +} + +QString TriggerDialog::buildEntry(Trigger trig) +{ + QString build; + if (trig.triggerMask & TriggerMask::TRG_BUS) + { + build = build + "BUS" + QString::number(trig.bus) + " "; + } + + if (trig.triggerMask & TriggerMask::TRG_ID) + { + build = build + "ID0x" + QString::number(trig.ID, 16) + " "; + } + + if (trig.triggerMask & TriggerMask::TRG_SIGNAL) + { + build = build + "SIG[" + trig.sigName; + if (trig.triggerMask & TriggerMask::TRG_SIGVAL) + { + build = build + ";" + QString::number(trig.sigValueDbl); + } + build = build + "] "; + } + + if (trig.triggerMask & TriggerMask::TRG_COUNT) + { + build = build + QString::number(trig.maxCount) + "X "; + } + + if (trig.triggerMask & TriggerMask::TRG_MS) + { + build = build + QString::number(trig.milliseconds) + "MS "; + } + + return build; +} + +void TriggerDialog::handleCheckboxes() +{ + ui->txtBus->setEnabled(ui->cbBus->checkState()); + ui->txtDelay->setEnabled(ui->cbMillis->checkState()); + ui->txtMaxCount->setEnabled(ui->cbMaxCount->checkState()); + ui->cmMsgID->setEnabled(ui->cbMsgID->checkState()); + ui->cmSignal->setEnabled(ui->cbSignal->checkState()); + ui->txtSigValue->setEnabled(ui->cbSigValue->checkState()); + + if (ui->cbBus->checkState() == Qt::Unchecked) ui->txtBus->setText(""); + if (ui->cbMillis->checkState() == Qt::Unchecked) ui->txtDelay->setText(""); + if (ui->cbMaxCount->checkState() == Qt::Unchecked) ui->txtMaxCount->setText(""); + if (ui->cbMsgID->checkState() == Qt::Unchecked) ui->cmMsgID->setCurrentText(""); + if (ui->cbSignal->checkState() == Qt::Unchecked) ui->cmSignal->setCurrentText(""); + if (ui->cbSigValue->checkState() == Qt::Unchecked) ui->txtSigValue->setText(""); + + regenerateCurrentListItem(); +} + +//load all the values from the editing screen and place them into the structure +//also, redo the list entry so it shows the proper text +void TriggerDialog::regenerateCurrentListItem() +{ + if (inhibitUpdates) return; + int row = ui->listTriggers->currentRow(); + Trigger trig; + trig.ID = 0; + trig.bus = 0; + trig.maxCount = 0; + trig.milliseconds = 0; + trig.sigName = ""; + trig.sigValueDbl = 0.0; + trig.triggerMask = 0; + + if (ui->cbMsgID->isChecked() && ui->cmMsgID->currentText().length() > 0) + { + trig.triggerMask |= TriggerMask::TRG_ID; + trig.ID = ui->cmMsgID->currentText() + .replace("0x", "") + .split("(")[0] + .toInt(nullptr, 16); + if (trig.ID != lastMsgID) + { + lastMsgID = trig.ID; + //now, reload signals combobox + ui->cmSignal->clear(); + QStringList entries; + //danger - this isn't doing a great job of making sure we're using + //the right message. Should at least search by message name instead + //if possible. + DBC_MESSAGE *msg = dbcHandler->findMessage(trig.ID); + int numSigs = msg->sigHandler->getCount(); + for (int s = 0; s < numSigs; s++) + { + DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s); + + entries.append(sig->name); + } + entries.sort(); + ui->cmSignal->addItems(entries); + } + } + + if (ui->cbBus->isChecked() && ui->txtBus ->text().length() > 0) + { + trig.triggerMask |= TriggerMask::TRG_BUS; + trig.bus = ui->txtBus->text().toInt(); + } + + if (ui->cbMillis->isChecked() && ui->txtDelay->text().length() > 0) + { + trig.triggerMask |= TriggerMask::TRG_MS; + trig.milliseconds = ui->txtDelay->text().toInt(); + } + + if (ui->cbMaxCount->isChecked() && ui->txtMaxCount->text().length() > 0) + { + trig.triggerMask |= TriggerMask::TRG_COUNT; + trig.maxCount = ui->txtMaxCount->text().toInt(); + } + + if (ui->cbSigValue->isChecked() && ui->txtSigValue->text().length() > 0) + { + trig.triggerMask |= TriggerMask::TRG_SIGVAL; + trig.sigValueDbl = ui->txtSigValue->text().toDouble(); + } + + if (ui->cbSignal->isChecked() && ui->cmSignal->currentText().length() > 0) + { + trig.triggerMask |= TriggerMask::TRG_SIGNAL; + trig.sigName = ui->cmSignal->currentText().trimmed(); + } + + triggers[row] = trig; + ui->listTriggers->item(row)->setText(buildEntry(trig)); +} + +void TriggerDialog::FillDetails() +{ + int selectedRow = ui->listTriggers->currentRow(); + if (selectedRow < 0) return; + inhibitUpdates = true; //this function makes a lot of changes and they should not trigger the normal updates + + if (triggers[selectedRow].triggerMask & TriggerMask::TRG_BUS) + { + ui->txtBus->setEnabled(true); + ui->txtBus->setText(QString::number(triggers[selectedRow].bus)); + ui->cbBus->setCheckState(Qt::Checked); + } + else + { + ui->txtBus->setEnabled(false); + ui->cbBus->setCheckState(Qt::Unchecked); + } + + if (triggers[selectedRow].triggerMask & TriggerMask::TRG_ID) + { + ui->cmMsgID->setEnabled(true); + ui->cmMsgID->setCurrentText(QString::number(triggers[selectedRow].ID, 16)); + ui->cbMsgID->setCheckState(Qt::Checked); + } + else + { + ui->cmMsgID->setEnabled(false); + ui->cbMsgID->setCheckState(Qt::Unchecked); + } + + if (triggers[selectedRow].triggerMask & TriggerMask::TRG_COUNT) + { + ui->txtMaxCount->setEnabled(true); + ui->txtMaxCount->setText(QString::number(triggers[selectedRow].maxCount)); + ui->cbMaxCount->setCheckState(Qt::Checked); + } + else + { + ui->txtMaxCount->setEnabled(false); + ui->cbMaxCount->setCheckState(Qt::Unchecked); + } + + if (triggers[selectedRow].triggerMask & TriggerMask::TRG_MS) + { + ui->txtDelay->setEnabled(true); + ui->txtDelay->setText(QString::number(triggers[selectedRow].milliseconds)); + ui->cbMillis->setCheckState(Qt::Checked); + } + else + { + ui->txtDelay->setEnabled(false); + ui->cbMillis->setCheckState(Qt::Unchecked); + } + + if (triggers[selectedRow].triggerMask & TriggerMask::TRG_SIGNAL) + { + ui->cmSignal->setEnabled(true); + ui->cmSignal->setCurrentText(triggers[selectedRow].sigName); + ui->cbSignal->setCheckState(Qt::Checked); + } + else + { + ui->cmSignal->setEnabled(false); + ui->cbSignal->setCheckState(Qt::Unchecked); + } + + if (triggers[selectedRow].triggerMask & TriggerMask::TRG_SIGVAL) + { + ui->txtSigValue->setEnabled(true); + ui->txtSigValue->setText(QString::number(triggers[selectedRow].sigValueDbl)); + ui->cbSigValue->setCheckState(Qt::Checked); + } + else + { + ui->txtSigValue->setEnabled(false); + ui->cbSigValue->setCheckState(Qt::Unchecked); + } + + inhibitUpdates = false; +} + +void TriggerDialog::addNewTrigger() +{ + Trigger newTrig; + newTrig.ID = 0; + newTrig.bus = 0; + newTrig.maxCount = 0; + newTrig.milliseconds = 100; + newTrig.sigName = ""; + newTrig.sigValueDbl = 0.0; + newTrig.sigValueInt = 0; + newTrig.triggerMask = TriggerMask::TRG_MS; + triggers.append(newTrig); + ui->listTriggers->addItem(buildEntry(newTrig)); +} + +void TriggerDialog::deleteSelectedTrigger() +{ + if (triggers.count() == 1) + { + QMessageBox::information(this, "Error", "Cannot delete the last trigger"); + return; + } +} + +void TriggerDialog::saveAndExit() +{ + accept(); +} diff --git a/triggerdialog.h b/triggerdialog.h new file mode 100644 index 0000000..09f6a1c --- /dev/null +++ b/triggerdialog.h @@ -0,0 +1,40 @@ +#ifndef TRIGGERDIALOG_H +#define TRIGGERDIALOG_H + +#include +#include "can_trigger_structs.h" +#include "dbc/dbchandler.h" + +namespace Ui { +class TriggerDialog; +} + +class TriggerDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TriggerDialog(QList triggers, QWidget *parent = nullptr); + QList getUpdatedTriggers(); + void showEvent(QShowEvent*); + ~TriggerDialog(); + QString buildEntry(Trigger trig); + +private: + Ui::TriggerDialog *ui; + DBCHandler *dbcHandler; + int lastMsgID; + bool inhibitUpdates; + + QList triggers; + + void FillDetails(); + void addNewTrigger(); + void deleteSelectedTrigger(); + void saveAndExit(); + void regenerateList(); + void handleCheckboxes(); + void regenerateCurrentListItem(); +}; + +#endif // TRIGGERDIALOG_H diff --git a/triggerdialog.ui b/triggerdialog.ui new file mode 100644 index 0000000..95477e0 --- /dev/null +++ b/triggerdialog.ui @@ -0,0 +1,191 @@ + + + TriggerDialog + + + + 0 + 0 + 771 + 303 + + + + Trigger Builder + + + + + + + + + + + + + Add New + + + + + + + Delete + + + + + + + + + QFrame::Raised + + + 7 + + + 3 + + + Qt::Vertical + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + Bus + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + + + + + Msg ID + + + + + + + Signal + + + + + + + false + + + + 0 + 0 + + + + true + + + + + + + MS Delay + + + + + + + false + + + + + + + Max Count + + + + + + + false + + + + + + + false + + + + 0 + 0 + + + + true + + + + + + + Signal Value + + + + + + + false + + + + + + + + + + + Save And Update + + + + + + + +