From 51ba7a2e765f556b8e37dd7f146b92551c93a2e0 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Wed, 22 Feb 2023 21:06:23 -0500 Subject: [PATCH] Add a new window that can be used to bridge two CAN buses with selective ID forwarding. --- SavvyCAN.pro | 5 +- canbridgewindow.cpp | 143 ++++++++++++++++++++++++++++++ canbridgewindow.h | 38 ++++++++ help/canbridge.md | 13 +++ help/images/CANBridge.png | Bin 0 -> 43114 bytes mainwindow.cpp | 16 +++- mainwindow.h | 3 + ui/canbridgewindow.ui | 89 +++++++++++++++++++ helpwindow.ui => ui/helpwindow.ui | 0 ui/mainwindow.ui | 14 ++- 10 files changed, 315 insertions(+), 6 deletions(-) create mode 100644 canbridgewindow.cpp create mode 100644 canbridgewindow.h create mode 100644 help/canbridge.md create mode 100644 help/images/CANBridge.png create mode 100644 ui/canbridgewindow.ui rename helpwindow.ui => ui/helpwindow.ui (100%) diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 57d34d2..64cbc55 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -20,6 +20,7 @@ QMAKE_INFO_PLIST = Info.plist.template ICON = icons/SavvyIcon.icns SOURCES += main.cpp\ + canbridgewindow.cpp \ connections/canserver.cpp \ connections/lawicel_serial.cpp \ connections/mqtt_bus.cpp \ @@ -101,6 +102,7 @@ SOURCES += main.cpp\ HEADERS += mainwindow.h \ can_structs.h \ + canbridgewindow.h \ canframemodel.h \ connections/canserver.h \ connections/lawicel_serial.h \ @@ -191,6 +193,7 @@ HEADERS += mainwindow.h \ pcaplite.h FORMS += ui/candatagrid.ui \ + ui/canbridgewindow.ui \ ui/dbcnodeduplicateeditor.ui \ ui/dbccomparatorwindow.ui \ ui/dbcmessageeditor.ui \ @@ -220,7 +223,7 @@ FORMS += ui/candatagrid.ui \ ui/udsscanwindow.ui \ ui/bisectwindow.ui \ ui/signalviewerwindow.ui \ - helpwindow.ui \ + ui/helpwindow.ui \ ui/newconnectiondialog.ui \ ui/temporalgraphwindow.ui diff --git a/canbridgewindow.cpp b/canbridgewindow.cpp new file mode 100644 index 0000000..cb4ffb4 --- /dev/null +++ b/canbridgewindow.cpp @@ -0,0 +1,143 @@ +#include "canbridgewindow.h" +#include "helpwindow.h" +#include "qevent.h" +#include "ui_canbridgewindow.h" +#include "filterutility.h" +#include "mainwindow.h" + +CANBridgeWindow::CANBridgeWindow(const QVector *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::CANBridgeWindow) +{ + ui->setupUi(this); + + modelFrames = frames; + + side1BusNum = 0; + side2BusNum = 0; + + connect(ui->cbSide1, &QComboBox::currentTextChanged, this, &CANBridgeWindow::recalcSides); + connect(ui->cbSide2, &QComboBox::currentTextChanged, this, &CANBridgeWindow::recalcSides); + connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &CANBridgeWindow::updatedFrames); + connect(ui->listSide1, &QListWidget::itemChanged, + [this] (QListWidgetItem *item) + { + bool checked = item->checkState() == Qt::Checked ? true:false; + int IDval = FilterUtility::getIdAsInt(item); + qDebug() << "ID " << IDval << " set to " << checked; + foundIDSide1[IDval] = checked; + }); + connect(ui->listSide2, &QListWidget::itemChanged, + [this] (QListWidgetItem *item) + { + bool checked = item->checkState() == Qt::Checked ? true:false; + int IDval = FilterUtility::getIdAsInt(item); + foundIDSide2[IDval] = checked; + }); +} + +CANBridgeWindow::~CANBridgeWindow() +{ + delete ui; +} + + +void CANBridgeWindow::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + //stuff to do every time this form is shown. + ui->cbSide1->clear(); + ui->cbSide2->clear(); + int numBuses = CANConManager::getInstance()->getNumBuses(); + for (int i = 0; i < numBuses; i++) + { + ui->cbSide1->addItem(QString::number(i)); + ui->cbSide2->addItem(QString::number(i)); + } +} + +bool CANBridgeWindow::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("canbridge.md"); + break; + } + return true; + } else { + // standard event processing + return QObject::eventFilter(obj, event); + } +} + +void CANBridgeWindow::recalcSides() +{ + side1BusNum = ui->cbSide1->currentText().toInt(); + side2BusNum = ui->cbSide2->currentText().toInt(); +} + + +void CANBridgeWindow::updatedFrames(int numFrames) +{ + bool addedSide1 = false; + bool addedSide2 = false; + + if (numFrames == -1) //all frames deleted. Kill the display + { + } + else if (numFrames == -2) //all new set of frames. Reset + { + } + else //just got some new frames. See if they are relevant. + { + if (numFrames > modelFrames->count()) return; + + for (int x = modelFrames->count() - numFrames; x < modelFrames->count(); x++) + { + CANFrame thisFrame = modelFrames->at(x); + int32_t id = static_cast(thisFrame.frameId()); + + if (thisFrame.bus == side1BusNum) + { + if (!foundIDSide1.contains(id)) + { + foundIDSide1.insert(id, true); + FilterUtility::createCheckableFilterItem(id, true, ui->listSide1); + addedSide1 = true; + } + if (ui->ckEnableSide1->isChecked()) //if we're enabled to forward traffic on this bus to the other one + { + if (foundIDSide1[id]) //and the checkbox for this particular ID is checked + { + thisFrame.bus = side2BusNum; + CANConManager::getInstance()->sendFrame(thisFrame); + } + } + } + else if (thisFrame.bus == side2BusNum) + { + if (!foundIDSide2.contains(id)) + { + foundIDSide2.insert(id, true); + FilterUtility::createCheckableFilterItem(id, true, ui->listSide2); + addedSide2 = true; + } + if (ui->ckEnableSide2->isChecked()) //if we're enabled to forward traffic on this bus to the other one + { + if (foundIDSide2[id]) //and the checkbox for this particular ID is checked + { + thisFrame.bus = side1BusNum; + CANConManager::getInstance()->sendFrame(thisFrame); + } + } + } + } + //default is to sort in ascending order + if (addedSide1) ui->listSide1->sortItems(); + if (addedSide2) ui->listSide2->sortItems(); + } +} + diff --git a/canbridgewindow.h b/canbridgewindow.h new file mode 100644 index 0000000..40304a2 --- /dev/null +++ b/canbridgewindow.h @@ -0,0 +1,38 @@ +#ifndef CANBRIDGEWINDOW_H +#define CANBRIDGEWINDOW_H + +#include +#include "connections/canconmanager.h" + +namespace Ui { +class CANBridgeWindow; +} + +class CANBridgeWindow : public QDialog +{ + Q_OBJECT + +public: + explicit CANBridgeWindow(const QVector *frames, QWidget *parent = nullptr); + ~CANBridgeWindow(); + void showEvent(QShowEvent*); + + +private slots: + void updatedFrames(int); + void recalcSides(); + +private: + Ui::CANBridgeWindow *ui; + const QVector *modelFrames; + QMap foundIDSide1; + QMap foundIDSide2; + int side1BusNum; + int side2BusNum; + + void processIncomingFrame(CANFrame *frame); + bool eventFilter(QObject *obj, QEvent *event); + +}; + +#endif // CANBRIDGEWINDOW_H diff --git a/help/canbridge.md b/help/canbridge.md new file mode 100644 index 0000000..5055d02 --- /dev/null +++ b/help/canbridge.md @@ -0,0 +1,13 @@ +CAN Bridge Window +================= + +![Bridge Window](images/CANBridge.png) + +Using the Bridge Window +========================== + +The purpose of this window is simple, to allow one to forward traffic from one bus to another. Any two buses you have connected could be bridged in this way, even if they originate on different hardware adapters. By default neither side will forward to the other. You must select different buses for Side 1 and Side 2 then enable forwarding from one to the other. You can enable bi-directional forwarding. However, this can be problematic. If you are not careful you can create an infinite loop where traffic from one side gets forwarded to the other side which then forwards to the first side, and so on. + +If you'd like to only forward some traffic then uncheck the boxes next to IDs you do not want to forward. + +At the moment this window is in the preliminary stages and, as mentioned above, could be prone to allowing infinite loops in some circumstances. \ No newline at end of file diff --git a/help/images/CANBridge.png b/help/images/CANBridge.png new file mode 100644 index 0000000000000000000000000000000000000000..91207e094d27194bd8fcb2c9045c1c7d934914a0 GIT binary patch literal 43114 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, &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::clicked, this, &MainWindow::gridClicked); @@ -262,8 +264,11 @@ void MainWindow::killEmAll() killWindow(firmwareUploaderWindow); killWindow(motorctrlConfigWindow); killWindow(signalViewerWindow); - killWindow(connectionWindow); 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 @@ -1442,6 +1447,15 @@ void MainWindow::showBisectWindow() bisectWindow->show(); } +void MainWindow::showCANBridgeWindow() +{ + if (!canBridgeWindow) + { + canBridgeWindow = new CANBridgeWindow(model->getListReference()); + } + canBridgeWindow->show(); +} + void MainWindow::showFrameSenderWindow() { if (!frameSenderWindow) diff --git a/mainwindow.h b/mainwindow.h index e76ec3f..24eb2da 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -33,6 +33,7 @@ #include "signalviewerwindow.h" #include "re/temporalgraphwindow.h" #include "re/dbccomparatorwindow.h" +#include "canbridgewindow.h" class CANConnection; class ConnectionWindow; @@ -89,6 +90,7 @@ private slots: void showSignalViewer(); void showTemporalGraphWindow(); void showDBCComparisonWindow(); + void showCANBridgeWindow(); void exitApp(); void handleSaveDecoded(); void handleSaveDecodedCsv(); @@ -183,6 +185,7 @@ private: SignalViewerWindow *signalViewerWindow; TemporalGraphWindow *temporalGraphWindow; DBCComparatorWindow *dbcComparatorWindow; + CANBridgeWindow *canBridgeWindow; //various private storage QLabel lbStatusConnected; diff --git a/ui/canbridgewindow.ui b/ui/canbridgewindow.ui new file mode 100644 index 0000000..3818efa --- /dev/null +++ b/ui/canbridgewindow.ui @@ -0,0 +1,89 @@ + + + CANBridgeWindow + + + + 0 + 0 + 640 + 480 + + + + CAN Bridger + + + false + + + + + + + + Side 1 + + + + + + + + + + Enable forwarding + + + false + + + + + + + IDs to forward to side 2 + + + + + + + + + + + + + + Side 2 + + + + + + + + + + Enable forwarding + + + + + + + IDs to forward to side 1 + + + + + + + + + + + + + diff --git a/helpwindow.ui b/ui/helpwindow.ui similarity index 100% rename from helpwindow.ui rename to ui/helpwindow.ui diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 0136c80..3b71991 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -32,7 +32,6 @@ 11 - 50 false @@ -81,7 +80,6 @@ - 50 false @@ -138,7 +136,6 @@ 11 - 75 true true @@ -338,6 +335,9 @@ 16777215 + + false + @@ -368,7 +368,7 @@ 0 0 800 - 21 + 26 @@ -399,6 +399,7 @@ + @@ -621,6 +622,11 @@ Save Decoded Frames CSV + + + CAN Bridge + +