diff --git a/README.md b/README.md index 3b7f7cc..e479a14 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # SavvyCAN Qt based cross platform canbus tool -(C) 2015-2017 EVTV and Collin Kidder +(C) 2015-2018 EVTV and Collin Kidder A Qt5 based cross platform tool which can be used to load, save, and capture canbus frames. This tool is designed to help with visualization, reverse engineering, debugging, and capturing of canbus frames. -Really requires at a resolution of at least 1024x768. Fully multi-monitor capable. +Really requires at a resolution of at least 1024x768. Fully multi-monitor capable. Works on 4K monitors as well. You are highly recommended to use the [CANDue board from EVTV](http://store.evtv.me/proddetail.php?prod=ArduinoDueCANBUS&cat=23). @@ -14,8 +14,8 @@ You are highly recommended to use the The CANDue board must be running the GVRET firmware which can also be found within the collin80 repos. -It is now possible to use any SocketCAN compatible device -under LINUX. There may, however, be some loss of some functionality as +It is now possible to use any QT SerialBus driver (socketcan, Vector, PeakCAN, TinyCAN). +There may, however, be some loss of some functionality as some functions of SavvyCAN are designed for use directly with the EVTVDue and CANDue 2.0 boards. @@ -46,7 +46,8 @@ http://www.qcustomplot.com/ However, this source code is integrated into the source for SavvyCAN and one isn't required to download it separately. -This project requires 5.8.0 or higher because of a dependency on QSerialBus. +This project requires 5.8.0 or higher because of a dependency on QSerialBus. However, you will get +even more SerialBus goodness if you use QT 5.11 or 5.12 ## Instructions for compiling: diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 84b6308..0ef31a1 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -65,7 +65,8 @@ SOURCES += main.cpp\ jsedit.cpp \ frameplaybackobject.cpp \ helpwindow.cpp \ - blfhandler.cpp + blfhandler.cpp \ + re/sniffer/SnifferDelegate.cpp HEADERS += mainwindow.h \ can_structs.h \ @@ -121,7 +122,8 @@ HEADERS += mainwindow.h \ jsedit.h \ frameplaybackobject.h \ helpwindow.h \ - blfhandler.h + blfhandler.h \ + re/sniffer/SnifferDelegate.h FORMS += ui/candatagrid.ui \ ui/connectionwindow.ui \ diff --git a/SavvyCAN.qch b/SavvyCAN.qch index 90f027b..997519a 100644 Binary files a/SavvyCAN.qch and b/SavvyCAN.qch differ diff --git a/SavvyCAN.qhc b/SavvyCAN.qhc index 13690f1..e218082 100644 Binary files a/SavvyCAN.qhc and b/SavvyCAN.qhc differ diff --git a/SavvyCAN.qhcp b/SavvyCAN.qhcp index 602be63..fad8e16 100644 --- a/SavvyCAN.qhcp +++ b/SavvyCAN.qhcp @@ -1,9 +1,9 @@ - SavvyCAN 181 documentation - qthelp://org.sphinx.savvycan.181/doc/index.html - qthelp://org.sphinx.savvycan.181/doc/index.html + SavvyCAN 189 documentation + qthelp://org.sphinx.savvycan.189/doc/index.html + qthelp://org.sphinx.savvycan.189/doc/index.html @@ -16,4 +16,4 @@ SavvyCAN.qch - + \ No newline at end of file diff --git a/SavvyCAN.qhp b/SavvyCAN.qhp index 00786c3..c5acc7a 100644 --- a/SavvyCAN.qhp +++ b/SavvyCAN.qhp @@ -1,16 +1,16 @@ - org.sphinx.savvycan.181 + org.sphinx.savvycan.189 doc - + SavvyCAN - 181 + 189 SavvyCAN - 181 + 189 -
+
@@ -58,6 +58,11 @@
+
+
+
+
+
@@ -101,63 +106,65 @@ - flowview.html - dbc_manager.html - graphwindow.html - framedetails.html - rangestate.html - filecomparison.html - connectionwindow.html - fuzzingwindow.html - mainscreen.html - signaleditor.html - uds_scanner.html - customsender.html - graphsetup.html - playbackwindow.html - index.html - scriptingwindow.html bisector.html - firmwareuploader.html - genindex.html - sniffer.html - isotp_decoder.html - preferences.html + connectionwindow.html + customsender.html dbc_editor.html - _static/file.png - _static/down.png + dbc_manager.html + filecomparison.html + firmwareuploader.html + flowview.html + framedetails.html + fuzzingwindow.html + genindex.html + graphsetup.html + graphwindow.html + index.html + isotp_decoder.html + mainscreen.html + playbackwindow.html + preferences.html + rangestate.html + scriptingwindow.html + search.html + signaleditor.html + sniffer.html + uds_scanner.html _static/ajax-loader.gif - _static/comment-close.png - _static/minus.png - _static/comment-bright.png - _static/up-pressed.png - _static/pygments.css - _static/plus.png - _static/nonav.css + _static/alabaster.css _static/basic.css - _static/down-pressed.png - _static/up.png + _static/comment-bright.png + _static/comment-close.png _static/comment.png - _images/SignalEditor.png - _images/FrameInfoWindow.png - _images/DBCEditor.png - _images/UDS_Scanner.png - _images/Sniffer.png + _static/down-pressed.png + _static/down.png + _static/file.png + _static/minus.png + _static/nonav.css + _static/plus.png + _static/pygments.css + _static/up-pressed.png + _static/up.png _images/Bisector.png - _images/FuzzingWindow.png - _images/GraphingView.png - _images/ScriptingWindow.png - _images/DBCManager.png - _images/GraphSetup.png - _images/RangeState.png - _images/Preferences.png _images/ConnectionWindow.png + _images/CustomSender.png + _images/DBCEditor.png + _images/DBCManager.png _images/FileComparator.png + _images/FlowView.png + _images/FrameInfoWindow.png + _images/FuzzingWindow.png + _images/GraphSetup.png + _images/GraphingView.png _images/ISOTPDecoder.png _images/MainScreen.png - _images/FlowView.png - _images/CustomSender.png _images/Playback.png + _images/Preferences.png + _images/RangeState.png + _images/ScriptingWindow.png + _images/SignalEditor.png + _images/Sniffer.png + _images/UDS_Scanner.png - + \ No newline at end of file diff --git a/canframemodel.cpp b/canframemodel.cpp index 94f0e0a..eea4ae0 100644 --- a/canframemodel.cpp +++ b/canframemodel.cpp @@ -160,6 +160,7 @@ void CANFrameModel::setOverwriteMode(bool mode) { beginResetModel(); overwriteDups = mode; + recalcOverwrite(); endResetModel(); } @@ -191,26 +192,20 @@ void CANFrameModel::recalcOverwrite() mutex.lock(); beginResetModel(); - for (int i = 1; i < frames.count(); i++) - { - found = false; - for (int j = 0; j <= lastUnique; j++) - { - if (frames[i].ID == frames[j].ID) - { - frames.replace(j, frames[i]); - found = true; - break; - } - } - if (!found) - { - lastUnique++; - frames.replace(lastUnique, frames[i]); - } - } - while (frames.count() > lastUnique) frames.removeLast(); + //Look at the current list of frames and turn it into just a list of unique IDs + QHash overWriteFrames; + foreach(CANFrame frame, frames) + { + if (!overWriteFrames.contains(frame.ID)) + { + overWriteFrames.insert(frame.ID, frame); + } + else overWriteFrames[frame.ID] = frame; + } + //Then replace the old list of frames with just the unique list + frames.clear(); + frames.append(overWriteFrames.values().toVector()); filteredFrames.clear(); filteredFrames.reserve(preallocSize); @@ -255,6 +250,11 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const return QColor(Qt::white); } + if (role == Qt::TextAlignmentRole) + { + return Qt::AlignLeft; + } + if (role == Qt::TextColorRole) { if (dbcHandler != NULL && interpretFrames) @@ -300,7 +300,8 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const for (int i = 0; i < dLen; i++) { quint8 byt = thisFrame.data[i]; - if (byt < 0x20) byt = 0x2E; //A dot + //0x20 through 0x7E are printable characters. Outside of that range they aren't. So use dots instead + if (byt < 0x20) byt = 0x2E; //dot character if (byt > 0x7E) byt = 0x2E; tempString.append(QString::fromUtf8((char *)&byt, 1)); } @@ -326,7 +327,7 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame); if (msg != NULL) { - tempString.append("\n"); + tempString.append(" \n"); tempString.append(msg->name + "\n" + msg->comment + "\n"); for (int j = 0; j < msg->sigHandler->getCount(); j++) { @@ -410,7 +411,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) frames.append(tempFrame); if (filters[tempFrame.ID]) { - if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + 1); + if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count()); filteredFrames.append(tempFrame); if (autoRefresh) endInsertRows(); } @@ -432,7 +433,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) frames.append(tempFrame); if (filters[tempFrame.ID]) { - if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + 1); + if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count(), filteredFrames.count()); filteredFrames.append(tempFrame); if (autoRefresh) endInsertRows(); } diff --git a/config.h b/config.h index e277117..5febcc8 100644 --- a/config.h +++ b/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG #define CONFIG -#define VERSION 187 +#define VERSION 190 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision diff --git a/connections/canconconst.h b/connections/canconconst.h index fa117b3..f70ebd9 100644 --- a/connections/canconconst.h +++ b/connections/canconconst.h @@ -16,7 +16,7 @@ namespace CANCon { { GVRET_SERIAL, KVASER, - SOCKETCAN, + SERIALBUS, REMOTE, NONE }; diff --git a/connections/canconfactory.cpp b/connections/canconfactory.cpp index 55e1727..d40dd98 100644 --- a/connections/canconfactory.cpp +++ b/connections/canconfactory.cpp @@ -5,11 +5,11 @@ using namespace CANCon; -CANConnection* CanConFactory::create(type pType, QString pPortName) +CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDriverName) { switch(pType) { - case SOCKETCAN: - return new SerialBusConnection(pPortName); + case SERIALBUS: + return new SerialBusConnection(pPortName, pDriverName); case GVRET_SERIAL: return new GVRetSerial(pPortName, false); case REMOTE: diff --git a/connections/canconfactory.h b/connections/canconfactory.h index 2b482f5..668568e 100644 --- a/connections/canconfactory.h +++ b/connections/canconfactory.h @@ -7,7 +7,7 @@ class CanConFactory { public: - static CANConnection* create(CANCon::type, QString pPortName); + static CANConnection* create(CANCon::type, QString pPortName, QString pDriverName); }; #endif // CANCONFACTORY_H diff --git a/connections/canconnection.cpp b/connections/canconnection.cpp index 8617ac9..ec99099 100644 --- a/connections/canconnection.cpp +++ b/connections/canconnection.cpp @@ -3,6 +3,7 @@ #include "canconnection.h" CANConnection::CANConnection(QString pPort, + QString pDriver, CANCon::type pType, int pNumBuses, int pQueueLen, @@ -10,6 +11,7 @@ CANConnection::CANConnection(QString pPort, mQueue(), mNumBuses(pNumBuses), mPort(pPort), + mDriver(pDriver), mType(pType), mIsCapSuspended(false), mStatus(CANCon::NOT_CONNECTED), @@ -227,6 +229,10 @@ QString CANConnection::getPort() { return mPort; } +QString CANConnection::getDriver() +{ + return mDriver; +} LFQueue& CANConnection::getQueue() { return mQueue; diff --git a/connections/canconnection.h b/connections/canconnection.h index 0966ea2..a450134 100644 --- a/connections/canconnection.h +++ b/connections/canconnection.h @@ -19,13 +19,15 @@ protected: /** * @brief CANConnection constructor * @param pPort: string containing port name + * @param pDriver: string containing driver name - Really only used for SerialBus connections * @param pType: the type of connection @ref CANCon::type * @param pNumBuses: the number of buses the device has * @param pQueueLen: the length of the lock free queue to use * @param pUseThread: if set to true, object will be execute in a dedicated thread */ CANConnection(QString pPort, - CANCon::type pType, + QString pDriver, + CANCon::type pType, int pNumBuses, int pQueueLen, bool pUseThread); @@ -50,6 +52,13 @@ public: */ QString getPort(); + /** + * @brief getDriver + * @return returns the name of the driver used for this device + */ + QString getDriver(); + + /** * @brief getQueue * @return the lock free queue of the device @@ -294,6 +303,7 @@ protected: private: LFQueue mQueue; const QString mPort; + const QString mDriver; const CANCon::type mType; bool mIsCapSuspended; QAtomicInt mStatus; diff --git a/connections/canconnectionmodel.cpp b/connections/canconnectionmodel.cpp index 719ca38..ac07450 100644 --- a/connections/canconnectionmodel.cpp +++ b/connections/canconnectionmodel.cpp @@ -87,10 +87,11 @@ Qt::ItemFlags CANConnectionModel::flags(const QModelIndex &index) const CANConnection *conn_p = getAtIdx(index.row(), busId); if (!conn_p) return Qt::ItemFlag::NoItemFlags; - //you can't set speed, single wire, or listen only on socketcan devices so - //detect if we're using GVRET where you can and turn that functionality on - bool editParams = false; - if (conn_p->getType() == CANCon::GVRET_SERIAL) editParams = true; + //socketcan is limited for what you can set but the other serialbus + //devices should be able to set the stuff. For now let anyone try + //and the underlying drivers can do with them as they will + bool editParams = true; + //if (conn_p->getType() == CANCon::GVRET_SERIAL) editParams = true; switch (Column(index.column())) { @@ -152,7 +153,7 @@ QVariant CANConnectionModel::data(const QModelIndex &index, int role) const bool ret; if (!conn_p) return QVariant(); ret = conn_p->getBusSettings(busId, bus); - bool isSocketCAN = (conn_p->getType() == CANCon::SOCKETCAN) ? true: false; + bool isSocketCAN = (conn_p->getType() == CANCon::SERIALBUS) ? true: false; //qDebug() << "ConnP: " << conn_p << " ret " << ret; @@ -167,7 +168,7 @@ QVariant CANConnectionModel::data(const QModelIndex &index, int role) const if (conn_p) switch (conn_p->getType()) { case CANCon::KVASER: return "KVASER"; - case CANCon::SOCKETCAN: return "SocketCAN"; + case CANCon::SERIALBUS: return "SerialBus"; case CANCon::GVRET_SERIAL: return "GVRET"; default: {} } diff --git a/connections/connectionwindow.cpp b/connections/connectionwindow.cpp index a355b5a..b1512ba 100644 --- a/connections/connectionwindow.cpp +++ b/connections/connectionwindow.cpp @@ -44,16 +44,13 @@ ConnectionWindow::ConnectionWindow(QWidget *parent) : /* load connection configuration */ loadConnections(); - ui->rbSocketCAN->setEnabled(isSocketCanAvailable()); -#ifdef Q_OS_WIN - ui->rbKvaser->setEnabled(true); -#endif + ui->rbSocketCAN->setEnabled(isSerialBusAvailable()); connect(ui->btnOK, &QAbstractButton::clicked, this, &ConnectionWindow::handleOKButton); connect(ui->rbGVRET, &QAbstractButton::clicked, this, &ConnectionWindow::handleConnTypeChanged); - connect(ui->rbKvaser, &QAbstractButton::clicked, this, &ConnectionWindow::handleConnTypeChanged); connect(ui->rbSocketCAN, &QAbstractButton::clicked, this, &ConnectionWindow::handleConnTypeChanged); connect(ui->rbRemote, &QAbstractButton::clicked, this, &ConnectionWindow::handleConnTypeChanged); + connect(ui->cbDeviceType, QOverload::of(&QComboBox::currentIndexChanged), this, &ConnectionWindow::handleDeviceTypeChanged); connect(ui->tableConnections->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &ConnectionWindow::currentRowChanged); connect(ui->btnActivateAll, &QPushButton::clicked, this, &ConnectionWindow::handleEnableAll); connect(ui->btnDeactivateAll, &QPushButton::clicked, this, &ConnectionWindow::handleDisableAll); @@ -63,6 +60,9 @@ ConnectionWindow::ConnectionWindow(QWidget *parent) : connect(ui->btnSendHex, &QPushButton::clicked, this, &ConnectionWindow::handleSendHex); connect(ui->btnSendText, &QPushButton::clicked, this, &ConnectionWindow::handleSendText); connect(ui->ckEnableConsole, &QCheckBox::toggled, this, &ConnectionWindow::consoleEnableChanged); + + ui->lblDeviceType->setHidden(true); + ui->cbDeviceType->setHidden(true); } ConnectionWindow::~ConnectionWindow() @@ -118,7 +118,7 @@ bool ConnectionWindow::eventFilter(QObject *obj, QEvent *event) // standard event processing return QObject::eventFilter(obj, event); } - return false; + //return false; } void ConnectionWindow::readSettings() @@ -200,7 +200,7 @@ void ConnectionWindow::consoleEnableChanged(bool checked) { connect(this, SIGNAL(sendDebugData(QByteArray)), conn_p, SLOT(debugInput(QByteArray))); } else { //turn it off - disconnect(conn_p, SIGNAL(debugOutput(QString)), 0, 0); + disconnect(conn_p, SIGNAL(debugOutput(QString)), nullptr, nullptr); disconnect(this, SIGNAL(sendDebugData(QByteArray)), conn_p, SLOT(debugInput(QByteArray))); } } @@ -226,11 +226,18 @@ void ConnectionWindow::handleDisableAll() void ConnectionWindow::handleConnTypeChanged() { if (ui->rbGVRET->isChecked()) selectSerial(); - if (ui->rbKvaser->isChecked()) selectKvaser(); if (ui->rbSocketCAN->isChecked()) selectSocketCan(); if (ui->rbRemote->isChecked()) selectRemote(); } +void ConnectionWindow::handleDeviceTypeChanged() +{ + ui->cbPort->clear(); + canDevices = QCanBus::instance()->availableDevices(ui->cbDeviceType->currentText()); + + for (int i = 0; i < canDevices.count(); i++) + ui->cbPort->addItem(canDevices[i].name()); +} /* status */ void ConnectionWindow::connectionStatus(CANConStatus pStatus) @@ -244,12 +251,12 @@ void ConnectionWindow::connectionStatus(CANConStatus pStatus) void ConnectionWindow::handleOKButton() { - CANConnection* conn_p = NULL; + CANConnection* conn_p = nullptr; if( ! CANConManager::getInstance()->getByName(getPortName()) ) { /* create connection */ - conn_p = create(getConnectionType(), getPortName()); + conn_p = create(getConnectionType(), getPortName(), getDriverName()); if(!conn_p) return; /* add connection to model */ @@ -281,7 +288,7 @@ return; ui->btnOK->setText(tr("Create New Connection")); ui->rbGVRET->setChecked(true); setSpeed(0); - setPortName(CANCon::GVRET_SERIAL, ""); + setPortName(CANCon::GVRET_SERIAL, "", ""); } else { @@ -300,7 +307,7 @@ return; ui->btnOK->setText(tr("Update Connection Settings")); setSpeed(bus.getSpeed()); - setPortName(conn_p->getType(), conn_p->getPort()); + setPortName(conn_p->getType(), conn_p->getPort(), conn_p->getDriver()); } } @@ -333,6 +340,8 @@ void ConnectionWindow::selectSerial() ui->lPort->setText("Port:"); /* set combobox page visible */ ui->stPort->setCurrentWidget(ui->cbPage); + ui->lblDeviceType->setHidden(true); + ui->cbDeviceType->setHidden(true); ui->cbPort->clear(); ports = QSerialPortInfo::availablePorts(); @@ -341,41 +350,42 @@ void ConnectionWindow::selectSerial() ui->cbPort->addItem(ports[i].portName()); } -void ConnectionWindow::selectKvaser() -{ - ui->lPort->setText("Port:"); - /* set combobox page visible */ - ui->stPort->setCurrentWidget(ui->cbPage); -} - void ConnectionWindow::selectSocketCan() { ui->lPort->setText("Port:"); /* set edit text page visible */ - ui->stPort->setCurrentWidget(ui->etPage); + ui->stPort->setCurrentWidget(ui->cbPage); + ui->lblDeviceType->setHidden(false); + ui->cbDeviceType->setHidden(false); + + ui->cbDeviceType->clear(); + QStringList plugins; + plugins = QCanBus::instance()->plugins(); + for (int i = 0; i < plugins.count(); i++) + ui->cbDeviceType->addItem(plugins[i]); } void ConnectionWindow::selectRemote() { ui->lPort->setText("IP Address:"); ui->stPort->setCurrentWidget(ui->etPage); + ui->lblDeviceType->setHidden(true); + ui->cbDeviceType->setHidden(true); } void ConnectionWindow::setSpeed(int speed0) { + Q_UNUSED(speed0); } -void ConnectionWindow::setPortName(CANCon::type pType, QString pPortName) +void ConnectionWindow::setPortName(CANCon::type pType, QString pPortName, QString pDriver) { switch(pType) { case CANCon::GVRET_SERIAL: ui->rbGVRET->setChecked(true); break; - case CANCon::KVASER: - ui->rbKvaser->setChecked(true); - break; - case CANCon::SOCKETCAN: + case CANCon::SERIALBUS: ui->rbSocketCAN->setChecked(true); //you can't configure any of the below three with socketcan so dim them out break; @@ -394,7 +404,16 @@ void ConnectionWindow::setPortName(CANCon::type pType, QString pPortName) ui->cbPort->setCurrentIndex(idx); break; } - case CANCon::SOCKETCAN: + case CANCon::SERIALBUS: + { + int idx = ui->cbDeviceType->findText(pDriver); + if (idx < 0) idx = 0; + ui->cbDeviceType->setCurrentIndex(idx); + idx = ui->cbPort->findText(pPortName); + if( idx < 0 ) idx = 0; + ui->cbPort->setCurrentIndex(idx); + break; + } case CANCon::REMOTE: { ui->lePort->setText(pPortName); @@ -415,9 +434,8 @@ QString ConnectionWindow::getPortName() { switch( getConnectionType() ) { case CANCon::GVRET_SERIAL: - case CANCon::KVASER: + case CANCon::SERIALBUS: return ui->cbPort->currentText(); - case CANCon::SOCKETCAN: case CANCon::REMOTE: return ui->lePort->text(); default: @@ -427,11 +445,20 @@ QString ConnectionWindow::getPortName() return ""; } +QString ConnectionWindow::getDriverName() +{ + if (getConnectionType() == CANCon::SERIALBUS) + { + return ui->cbDeviceType->currentText(); + } + + return ""; +} + CANCon::type ConnectionWindow::getConnectionType() { if (ui->rbGVRET->isChecked()) return CANCon::GVRET_SERIAL; - if (ui->rbKvaser->isChecked()) return CANCon::KVASER; - if (ui->rbSocketCAN->isChecked()) return CANCon::SOCKETCAN; + if (ui->rbSocketCAN->isChecked()) return CANCon::SERIALBUS; if (ui->rbRemote->isChecked()) return CANCon::REMOTE; qDebug() << "getConnectionType: error"; return CANCon::NONE; @@ -440,6 +467,7 @@ CANCon::type ConnectionWindow::getConnectionType() void ConnectionWindow::setSWMode(bool mode) { + Q_UNUSED(mode); } bool ConnectionWindow::getSWMode() @@ -475,21 +503,19 @@ void ConnectionWindow::handleRevert() } -bool ConnectionWindow::isSocketCanAvailable() +bool ConnectionWindow::isSerialBusAvailable() { -#ifdef Q_OS_LINUX - if (QCanBus::instance()->plugins().contains(QStringLiteral("socketcan"))) return true; -#endif + if (QCanBus::instance()->plugins().count() > 0) return true; return false; } -CANConnection* ConnectionWindow::create(CANCon::type pTye, QString pPortName) +CANConnection* ConnectionWindow::create(CANCon::type pTye, QString pPortName, QString pDriver) { CANConnection* conn_p; /* create connection */ - conn_p = CanConFactory::create(pTye, pPortName); + conn_p = CanConFactory::create(pTye, pPortName, pDriver); if(conn_p) { /* connect signal */ @@ -512,11 +538,15 @@ void ConnectionWindow::loadConnections() /* fill connection list */ QVector portNames = settings.value("connections/portNames").value>(); + QVector driverNames = settings.value("connections/driverNames").value>(); QVector devTypes = settings.value("connections/types").value>(); - for(int i=0 ; iadd(conn_p); } @@ -533,14 +563,17 @@ void ConnectionWindow::saveConnections() QSettings settings; QVector portNames; QVector devTypes; + QVector driverNames; /* save connections */ foreach(CANConnection* conn_p, conns) { portNames.append(conn_p->getPort()); devTypes.append(conn_p->getType()); + driverNames.append(conn_p->getDriver()); } settings.setValue("connections/portNames", QVariant::fromValue(portNames)); settings.setValue("connections/types", QVariant::fromValue(devTypes)); + settings.setValue("connections/driverNames", QVariant::fromValue(driverNames)); } diff --git a/connections/connectionwindow.h b/connections/connectionwindow.h index 3d000ba..7b44a1d 100644 --- a/connections/connectionwindow.h +++ b/connections/connectionwindow.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "canconnectionmodel.h" #include "connections/canconnection.h" @@ -47,6 +48,7 @@ public slots: private slots: void handleOKButton(); void handleConnTypeChanged(); + void handleDeviceTypeChanged(); void currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); void consoleEnableChanged(bool checked); void handleRemoveConn(); @@ -63,6 +65,7 @@ private slots: private: Ui::ConnectionWindow *ui; QList ports; + QList canDevices; QSettings *settings; CANConnectionModel *connModel; @@ -70,13 +73,14 @@ private: void selectKvaser(); void selectSocketCan(); void selectRemote(); - bool isSocketCanAvailable(); + bool isSerialBusAvailable(); int getSpeed(); QString getPortName(); - void setPortName(CANCon::type pType, QString pPortName); + QString getDriverName(); + void setPortName(CANCon::type pType, QString pPortName, QString pDriver); void setActiveAll(bool pActive); - CANConnection* create(CANCon::type pTye, QString pPortName); + CANConnection* create(CANCon::type pTye, QString pPortName, QString pDriver); void loadConnections(); void saveConnections(); void showEvent(QShowEvent *); diff --git a/connections/gvretserial.cpp b/connections/gvretserial.cpp index efee5f3..86d1b0f 100644 --- a/connections/gvretserial.cpp +++ b/connections/gvretserial.cpp @@ -9,19 +9,20 @@ #include "gvretserial.h" GVRetSerial::GVRetSerial(QString portName, bool useTcp) : - CANConnection(portName, CANCon::GVRET_SERIAL, 3, 4000, true), + CANConnection(portName, "gvret", CANCon::GVRET_SERIAL, 3, 4000, true), useTcp(useTcp), mTimer(this) /*NB: set this as parent of timer to manage it from working thread */ { - qDebug() << "GVRetSerial()"; - debugOutput("GVRetSerial()"); + sendDebug("GVRetSerial()"); serial = NULL; tcpClient = NULL; + udpClient = NULL; rx_state = IDLE; rx_step = 0; - gotValidated = true; + validationCounter = 10; //how many times we can miss validation before we die isAutoRestart = false; + espSerialMode = true; timeBasis = 0; lastSystemTimeBasis = 0; @@ -34,27 +35,38 @@ GVRetSerial::GVRetSerial(QString portName, bool useTcp) : GVRetSerial::~GVRetSerial() { stop(); - qDebug() << "~GVRetSerial()"; - debugOutput("~GVRetSerial()"); + sendDebug("~GVRetSerial()"); +} + +void GVRetSerial::sendDebug(const QString debugText) +{ + qDebug() << debugText; + debugOutput(debugText); } void GVRetSerial::sendToSerial(const QByteArray &bytes) { - if (serial == NULL && tcpClient == NULL) + if (serial == NULL && tcpClient == NULL && udpClient == NULL) { - debugOutput("Attempt to write to serial port when it has not been initialized!"); + sendDebug("Attempt to write to serial port when it has not been initialized!"); return; } if (serial && !serial->isOpen()) { - debugOutput("Attempt to write to serial port when it is not open!"); + sendDebug("Attempt to write to serial port when it is not open!"); return; } if (tcpClient && !tcpClient->isOpen()) { - debugOutput("Attempt to write to TCP/IP port when it is not open!"); + sendDebug("Attempt to write to TCP/IP port when it is not open!"); + return; + } + + if (udpClient && !udpClient->isOpen()) + { + sendDebug("Attempt to write to UDP Socket when it is not open!"); return; } @@ -64,10 +76,11 @@ void GVRetSerial::sendToSerial(const QByteArray &bytes) byt = (unsigned char)byt; buildDebug = buildDebug % QString::number(byt, 16) % " "; } - debugOutput(buildDebug); + sendDebug(buildDebug); if (serial) serial->write(bytes); if (tcpClient) tcpClient->write(bytes); + if (udpClient) udpClient->write(bytes); } void GVRetSerial::piStarted() @@ -176,8 +189,7 @@ void GVRetSerial::piSetBusSettings(int pBusIdx, CANBus bus) if (pBusIdx < 2) { /* update baud rates */ QByteArray buffer; - qDebug() << "Got signal to update bauds. 1: " << (can0Baud & 0xFFFFFFF) <<" 2: " << (can1Baud & 0xFFFFFFF); - debugOutput("Got signal to update bauds. 1: " + QString::number((can0Baud & 0xFFFFFFF)) + " 2: " + QString::number((can1Baud & 0xFFFFFFF))); + sendDebug("Got signal to update bauds. 1: " + QString::number((can0Baud & 0xFFFFFFF)) + " 2: " + QString::number((can1Baud & 0xFFFFFFF))); buffer[0] = (char)0xF1; //start of a command over serial buffer[1] = 5; //setup canbus buffer[2] = (unsigned char)(can0Baud & 0xFF); //four bytes of ID LSB first @@ -195,8 +207,7 @@ void GVRetSerial::piSetBusSettings(int pBusIdx, CANBus bus) { /* update baud rates */ QByteArray buffer; - qDebug() << "Got signal to update extended bus speeds SWCAN: " << swcanBaud <<" LIN1: " << lin1Baud << " LIN2: " << lin2Baud; - debugOutput("Got signal to update extended bus speeds SWCAN: " + QString::number(swcanBaud) + " LIN1: " + QString::number(lin1Baud) + " LIN2: " + QString::number(lin2Baud)); + sendDebug("Got signal to update extended bus speeds SWCAN: " + QString::number(swcanBaud) + " LIN1: " + QString::number(lin1Baud) + " LIN2: " + QString::number(lin2Baud)); buffer[0] = (char)0xF1; //start of a command over serial buffer[1] = 14; //setup extended buses buffer[2] = (unsigned char)(swcanBaud & 0xFF); //four bytes of ID LSB first @@ -227,8 +238,10 @@ bool GVRetSerial::piSendFrame(const CANFrame& frame) framesRapid++; - if (serial == NULL) return false; - if (!serial->isOpen()) return false; + if (serial == NULL && tcpClient == NULL && udpClient == NULL) return false; + if (serial && !serial->isOpen()) return false; + if (tcpClient && !tcpClient->isOpen()) return false; + if (udpClient && !udpClient->isOpen()) return false; //if (!isConnected) return false; // Doesn't make sense to send an error frame @@ -283,55 +296,84 @@ void GVRetSerial::connectDevice() disconnectDevice(); if(tcpClient) disconnectDevice(); + if (udpClient) + disconnectDevice(); /* open new device */ - // this does the wrong thing if the serial port has a '.' in the name - // if (getPort().contains('.')) //TCP/IP mode then since it looks like an IP address if (useTcp) { - qDebug() << "TCP Connection to a GVRET device"; + // /* + sendDebug("TCP Connection to a GVRET device"); tcpClient = new QTcpSocket(); tcpClient->connectToHost(getPort(), 23); connect(tcpClient, SIGNAL(readyRead()), this, SLOT(readSerialData())); - connect(tcpClient, SIGNAL(connected()), this, SLOT(tcpConnected())); + connect(tcpClient, SIGNAL(connected()), this, SLOT(deviceConnected())); + sendDebug("Created TCP Socket"); + // */ + /* + qDebug() << "UDP Connection to a GVRET device"; + udpClient = new QUdpSocket(); + udpClient->connectToHost(getPort(), 17222); + connect(udpClient, SIGNAL(readyRead()), this, SLOT(readSerialData())); + //connect(udpClient, SIGNAL(connected()), this, SLOT(tcpConnected())); + debugOutput("Created UDP Socket"); + tcpConnected(); + */ } else { - qDebug() << "Serial connection to a GVRET device"; + sendDebug("Serial connection to a GVRET device"); serial = new QSerialPort(QSerialPortInfo(getPort())); if(!serial) { - qDebug() << "can't open serial port " << getPort(); - debugOutput("can't open serial port " + getPort()); + sendDebug("can't open serial port " + getPort()); return; } - debugOutput("Created Serial Port Object"); + sendDebug("Created Serial Port Object"); - /* configure */ - serial->setDataBits(serial->Data8); - serial->setFlowControl(serial->HardwareControl); //this is important though - //serial->setFlowControl(serial->NoFlowControl); - serial->setBaudRate(1000000); - if (!serial->open(QIODevice::ReadWrite)) - { - qDebug() << serial->errorString(); - } - //serial->setDataTerminalReady(false); //you do need to set these or the fan gets dirty - //serial->setRequestToSend(false); - serial->setDataTerminalReady(true); //you do need to set these or the fan gets dirty - serial->setRequestToSend(true); - - debugOutput("Opened Serial Port"); /* connect reading event */ connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData())); connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(serialError(QSerialPort::SerialPortError))); - } - if (serial) tcpConnected(); //well, not a proper function name I guess... + /* configure */ + serial->setBaudRate(1000000); //most GVRET devices ignore baud, ESP32 needs it set explicitly to the proper value + serial->setDataBits(serial->Data8); + + if (espSerialMode) + { + sendDebug("Trying ESP32 Serial Mode"); + serial->setFlowControl(serial->NoFlowControl); + if (!serial->open(QIODevice::ReadWrite)) + { + //sendDebug("Error returned during port opening: " + serial->errorString()); + } + else + { + serial->setDataTerminalReady(false); //ESP32 uses these for bootloader selection and reset so turn them off + serial->setRequestToSend(false); + QTimer::singleShot(3000, this, SLOT(deviceConnected())); //give ESP32 some time as it could have rebooted + } + } + else + { + sendDebug("Trying Standard Serial Mode"); + serial->setFlowControl(serial->HardwareControl); //Most GVRET style devices use hardware flow control + if (!serial->open(QIODevice::ReadWrite)) + { + //sendDebug("Error returned during port opening: " + serial->errorString()); + } + else + { + //serial->setDataTerminalReady(true); //Seemingly these two lines used to be needed + //serial->setRequestToSend(true); //But, really both ends should automatically handle these + deviceConnected(); + } + } + } } -void GVRetSerial::tcpConnected() +void GVRetSerial::deviceConnected() { - qDebug() << "Connected to GVRET Device!"; + sendDebug("Connecting to GVRET Device!"); QByteArray output; output.append((unsigned char)0xE7); //this puts the device into binary comm mode output.append((unsigned char)0xE7); @@ -367,14 +409,8 @@ void GVRetSerial::tcpConnected() sendToSerial(output); - /* start timer */ - connect(&mTimer, SIGNAL(timeout()), this, SLOT(handleTick())); - mTimer.setInterval(250); //tick four times per second - mTimer.setSingleShot(false); //keep ticking - mTimer.start(); - if(doValidation) { - QTimer::singleShot(1000, this, SLOT(connectionTimeout())); + QTimer::singleShot(5000, this, SLOT(connectionTimeout())); } else { setStatus(CANCon::CONNECTED); @@ -390,7 +426,7 @@ void GVRetSerial::disconnectDevice() { { if (serial->isOpen()) { - serial->clear(); + //serial->clear(); serial->close(); } @@ -408,6 +444,17 @@ void GVRetSerial::disconnectDevice() { delete tcpClient; tcpClient = NULL; } + if (udpClient != NULL) + { + if (udpClient->isOpen()) + { + udpClient->close(); + } + udpClient->disconnect(); + delete udpClient; + udpClient = NULL; + } + setStatus(CANCon::NOT_CONNECTED); CANConStatus stats; stats.conStatus = getStatus(); @@ -426,14 +473,17 @@ void GVRetSerial::serialError(QSerialPort::SerialPortError err) case QSerialPort::DeviceNotFoundError: errMessage = "Device not found error on serial"; killConnection = true; + piStop(); break; case QSerialPort::PermissionError: errMessage = "Permission error on serial port"; killConnection = true; + piStop(); break; case QSerialPort::OpenError: errMessage = "Open error on serial port"; killConnection = true; + piStop(); break; case QSerialPort::ParityError: errMessage = "Parity error on serial port"; @@ -446,13 +496,16 @@ void GVRetSerial::serialError(QSerialPort::SerialPortError err) break; case QSerialPort::WriteError: errMessage = "Write error on serial port"; + piStop(); break; case QSerialPort::ReadError: errMessage = "Read error on serial port"; + piStop(); break; case QSerialPort::ResourceError: errMessage = "Serial port seems to have disappeared."; killConnection = true; + piStop(); break; case QSerialPort::UnsupportedOperationError: errMessage = "Unsupported operation on serial port"; @@ -461,23 +514,28 @@ void GVRetSerial::serialError(QSerialPort::SerialPortError err) case QSerialPort::UnknownError: errMessage = "Beats me what happened to the serial port."; killConnection = true; + piStop(); break; case QSerialPort::TimeoutError: errMessage = "Timeout error on serial port"; killConnection = true; break; case QSerialPort::NotOpenError: - errMessage = "The serial port isn't open dummy"; + errMessage = "The serial port isn't open"; killConnection = true; + piStop(); break; } - serial->clearError(); - serial->flush(); - serial->close(); + /* + if (serial) + { + serial->clearError(); + serial->flush(); + serial->close(); + }*/ if (errMessage.length() > 1) { - qDebug() << errMessage; - debugOutput(errMessage); + sendDebug(errMessage); } if (killConnection) { @@ -490,13 +548,23 @@ void GVRetSerial::serialError(QSerialPort::SerialPortError err) void GVRetSerial::connectionTimeout() { //one second after trying to connect are we actually connected? - //if (CANCon::NOT_CONNECTED==getStatus()) //no? - if (!gotValidated) + if (CANCon::NOT_CONNECTED==getStatus()) //no? { //then emit the the failure signal and see if anyone cares - qDebug() << "Failed to connect to GVRET at that com port"; + sendDebug("Failed to connect to GVRET at that com port"); + //toggle the serial mode and try again + espSerialMode = !espSerialMode; disconnectDevice(); + connectDevice(); + } + else + { + /* start timer */ + connect(&mTimer, SIGNAL(timeout()), this, SLOT(handleTick())); + mTimer.setInterval(250); //tick four times per second + mTimer.setSingleShot(false); //keep ticking + mTimer.start(); } } @@ -509,9 +577,9 @@ void GVRetSerial::readSerialData() if (serial) data = serial->readAll(); if (tcpClient) data = tcpClient->readAll(); + if (udpClient) data = udpClient->readAll(); - debugOutput("Got data from serial. Len = " % QString::number(data.length())); - //qDebug() << (tr("Got data from serial. Len = %0").arg(data.length())); + sendDebug("Got data from serial. Len = " % QString::number(data.length())); for (int i = 0; i < data.length(); i++) { c = data.at(i); @@ -571,7 +639,7 @@ void GVRetSerial::procRXChar(unsigned char c) rx_step = 0; break; case 9: - gotValidated = true; + validationCounter = 10; //qDebug() << "Got validated"; rx_state = IDLE; break; @@ -932,12 +1000,14 @@ void GVRetSerial::handleTick() if( CANCon::CONNECTED == getStatus() ) { - if (!gotValidated && doValidation) + if (doValidation) validationCounter--; + //qDebug() << validationCounter; + if (validationCounter == 0 && doValidation) { if (serial == NULL && tcpClient == NULL) return; - if ( (serial && serial->isOpen()) || (tcpClient && tcpClient->isOpen())) //if it's still false we have a problem... + if ( (serial && serial->isOpen()) || (tcpClient && tcpClient->isOpen()) || (udpClient && udpClient->isOpen())) //if it's still false we have a problem... { - qDebug() << "Comm validation failed. "; + sendDebug("Comm validation failed."); setStatus(CANCon::NOT_CONNECTED); //emit status(getStatus()); @@ -952,6 +1022,7 @@ void GVRetSerial::handleTick() } if (doValidation && serial && serial->isOpen()) sendCommValidation(); if (doValidation && tcpClient && tcpClient->isOpen()) sendCommValidation(); + if (doValidation && udpClient && udpClient->isOpen()) sendCommValidation(); } @@ -959,7 +1030,6 @@ void GVRetSerial::sendCommValidation() { QByteArray output; - gotValidated = false; output.append((unsigned char)0xF1); //another command to the GVRET output.append((unsigned char)0x09); //request a reply to get validation diff --git a/connections/gvretserial.h b/connections/gvretserial.h index efff041..dba664f 100644 --- a/connections/gvretserial.h +++ b/connections/gvretserial.h @@ -6,6 +6,7 @@ #include #include #include +#include /*************/ #include @@ -64,7 +65,7 @@ private slots: void connectionTimeout(); void readSerialData(); void serialError(QSerialPort::SerialPortError err); - void tcpConnected(); + void deviceConnected(); void handleTick(); private: @@ -73,18 +74,21 @@ private: void sendCommValidation(); void rebuildLocalTimeBasis(); void sendToSerial(const QByteArray &bytes); + void sendDebug(const QString debugText); protected: QTimer mTimer; QThread mThread; bool doValidation; - bool gotValidated; + int validationCounter; bool isAutoRestart; bool continuousTimeSync; bool useTcp; + bool espSerialMode; //special serial mode for ESP32 based boards - no flow control and much slower serial baud speed QSerialPort *serial; QTcpSocket *tcpClient; + QUdpSocket *udpClient; int framesRapid; STATE rx_state; uint32_t rx_step; diff --git a/connections/serialbusconnection.cpp b/connections/serialbusconnection.cpp index a8dc36b..af67b9e 100644 --- a/connections/serialbusconnection.cpp +++ b/connections/serialbusconnection.cpp @@ -11,8 +11,8 @@ /**** class definition ****/ /***********************************/ -SerialBusConnection::SerialBusConnection(QString portName) : - CANConnection(portName, CANCon::SOCKETCAN, 1, 4000, true), +SerialBusConnection::SerialBusConnection(QString portName, QString driverName) : + CANConnection(portName, driverName, CANCon::SERIALBUS, 1, 4000, true), mTimer(this) /*NB: set connection as parent of timer to manage it from working thread */ { } @@ -26,6 +26,22 @@ SerialBusConnection::~SerialBusConnection() void SerialBusConnection::piStarted() { + qDebug() << "piStarted()"; + /* create device */ + QString errorString; + qDebug() << "Creating device instance"; + mDev_p = QCanBus::instance()->createDevice(getDriver(), getPort(), &errorString); + if (!mDev_p) { + disconnectDevice(); + qDebug() << "Error: createDevice(" << getType() << getDriver() << getPort() << "):" << errorString; + return; + } + + /* connect slots */ + connect(mDev_p, &QCanBusDevice::errorOccurred, this, &SerialBusConnection::errorReceived); + connect(mDev_p, &QCanBusDevice::framesWritten, this, &SerialBusConnection::framesWritten); + connect(mDev_p, &QCanBusDevice::framesReceived, this, &SerialBusConnection::framesReceived); + connect(&mTimer, SIGNAL(timeout()), this, SLOT(testConnection())); mTimer.setInterval(1000); mTimer.setSingleShot(false); //keep ticking @@ -47,6 +63,7 @@ void SerialBusConnection::piSuspend(bool pSuspend) void SerialBusConnection::piStop() { + qDebug() << "piStop()"; mTimer.stop(); disconnectDevice(); } @@ -65,6 +82,8 @@ void SerialBusConnection::piSetBusSettings(int pBusIdx, CANBus bus) if(0 != pBusIdx) return; + if (!mDev_p) return; + /* disconnect device if we have one connected */ disconnectDevice(); @@ -75,20 +94,6 @@ void SerialBusConnection::piSetBusSettings(int pBusIdx, CANBus bus) if(!bus.active) return; - /* create device */ - QString errorString; - mDev_p = QCanBus::instance()->createDevice("socketcan", getPort(), &errorString); - if (!mDev_p) { - disconnectDevice(); - qDebug() << "Error: createDevice(" << getType() << getPort() << "):" << errorString; - return; - } - - /* connect slots */ - connect(mDev_p, &QCanBusDevice::errorOccurred, this, &SerialBusConnection::errorReceived); - connect(mDev_p, &QCanBusDevice::framesWritten, this, &SerialBusConnection::framesWritten); - connect(mDev_p, &QCanBusDevice::framesReceived, this, &SerialBusConnection::framesReceived); - /* set configuration */ /*if (p.useConfigurationEnabled) { foreach (const SettingsDialog::ConfigurationItem &item, p.configurations) @@ -96,7 +101,8 @@ void SerialBusConnection::piSetBusSettings(int pBusIdx, CANBus bus) }*/ //You cannot set the speed of a socketcan interface, it has to be set with console commands. - //mDev_p->setConfigurationParameter(QCanBusDevice::BitRateKey, bus.speed); + //But, you can probabaly set the speed of many of the other serialbus devices so go ahead and try + mDev_p->setConfigurationParameter(QCanBusDevice::BitRateKey, bus.speed); /* connect device */ if (!mDev_p->connectDevice()) { @@ -115,8 +121,8 @@ bool SerialBusConnection::piSendFrame(const CANFrame& pFrame) /* fill frame */ QCanBusFrame frame; - frame.setFrameId(pFrame.ID); frame.setExtendedFrameFormat(pFrame.extended); + frame.setFrameId(pFrame.ID); if (pFrame.remote) { frame.setFrameType(QCanBusFrame::FrameType::RemoteRequestFrame); } else { @@ -137,8 +143,6 @@ bool SerialBusConnection::piSendFrame(const CANFrame& pFrame) void SerialBusConnection::disconnectDevice() { if(mDev_p) { mDev_p->disconnectDevice(); - delete mDev_p; - mDev_p = nullptr; } } @@ -255,13 +259,12 @@ void SerialBusConnection::framesReceived() void SerialBusConnection::testConnection() { - QCanBusDevice* dev_p = QCanBus::instance()->createDevice("socketcan", getPort()); CANConStatus stats; switch(getStatus()) { case CANCon::CONNECTED: - if (!dev_p || !dev_p->connectDevice()) { + if (!mDev_p || mDev_p->state() == QCanBusDevice::UnconnectedState) { /* we have lost connectivity */ disconnectDevice(); @@ -269,21 +272,18 @@ void SerialBusConnection::testConnection() { stats.conStatus = getStatus(); stats.numHardwareBuses = mNumBuses; emit status(stats); + piStop(); } break; case CANCon::NOT_CONNECTED: - if (dev_p && dev_p->connectDevice()) { - if(!mDev_p) { - /* try to reconnect */ - CANBus bus; - if(getBusConfig(0, bus)) - { - bus.setEnabled(true); - setBusSettings(0, bus); - } + if (mDev_p && mDev_p->state() == QCanBusDevice::UnconnectedState) { + /* try to reconnect */ + CANBus bus; + if(getBusConfig(0, bus)) + { + bus.setEnabled(true); + setBusSettings(0, bus); } - /* disconnect test instance */ - dev_p->disconnectDevice(); setStatus(CANCon::CONNECTED); stats.conStatus = getStatus(); @@ -293,7 +293,4 @@ void SerialBusConnection::testConnection() { break; default: {} } - - if(dev_p) - delete dev_p; } diff --git a/connections/serialbusconnection.h b/connections/serialbusconnection.h index 2c9d357..e430ebd 100644 --- a/connections/serialbusconnection.h +++ b/connections/serialbusconnection.h @@ -12,7 +12,7 @@ class SerialBusConnection : public CANConnection Q_OBJECT public: - SerialBusConnection(QString portName); + SerialBusConnection(QString portName, QString driverName); virtual ~SerialBusConnection(); protected: diff --git a/dbc/dbchandler.cpp b/dbc/dbchandler.cpp index 7e76550..dbd33ac 100644 --- a/dbc/dbchandler.cpp +++ b/dbc/dbchandler.cpp @@ -8,21 +8,22 @@ #include #include #include "utility.h" +#include "connections/canconmanager.h" -DBCHandler* DBCHandler::instance = NULL; +DBCHandler* DBCHandler::instance = nullptr; DBC_SIGNAL* DBCSignalHandler::findSignalByIdx(int idx) { - if (sigs.count() == 0) return NULL; - if (idx < 0) return NULL; - if (idx >= sigs.count()) return NULL; + if (sigs.count() == 0) return nullptr; + if (idx < 0) return nullptr; + if (idx >= sigs.count()) return nullptr; return &sigs[idx]; } DBC_SIGNAL* DBCSignalHandler::findSignalByName(QString name) { - if (sigs.count() == 0) return NULL; + if (sigs.count() == 0) return nullptr; for (int i = 0; i < sigs.count(); i++) { if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0) @@ -30,7 +31,7 @@ DBC_SIGNAL* DBCSignalHandler::findSignalByName(QString name) return &sigs[i]; } } - return NULL; + return nullptr; } bool DBCSignalHandler::addSignal(DBC_SIGNAL &sig) @@ -82,7 +83,7 @@ int DBCSignalHandler::getCount() DBC_MESSAGE* DBCMessageHandler::findMsgByID(uint32_t id) { - if (messages.count() == 0) return NULL; + if (messages.count() == 0) return nullptr; for (int i = 0; i < messages.count(); i++) { if (isJ1939Handler) @@ -115,20 +116,20 @@ DBC_MESSAGE* DBCMessageHandler::findMsgByID(uint32_t id) } } } - return NULL; + return nullptr; } DBC_MESSAGE* DBCMessageHandler::findMsgByIdx(int idx) { - if (messages.count() == 0) return NULL; - if (idx < 0) return NULL; - if (idx >= messages.count()) return NULL; + if (messages.count() == 0) return nullptr; + if (idx < 0) return nullptr; + if (idx >= messages.count()) return nullptr; return &messages[idx]; } DBC_MESSAGE* DBCMessageHandler::findMsgByName(QString name) { - if (messages.count() == 0) return NULL; + if (messages.count() == 0) return nullptr; for (int i = 0; i < messages.count(); i++) { if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0) @@ -136,7 +137,7 @@ DBC_MESSAGE* DBCMessageHandler::findMsgByName(QString name) return &messages[i]; } } - return NULL; + return nullptr; } bool DBCMessageHandler::addMessage(DBC_MESSAGE &msg) @@ -251,14 +252,14 @@ DBCFile& DBCFile::operator=(const DBCFile& cpy) DBC_NODE* DBCFile::findNodeByIdx(int idx) { - if (idx < 0) return NULL; - if (idx >= dbc_nodes.count()) return NULL; + if (idx < 0) return nullptr; + if (idx >= dbc_nodes.count()) return nullptr; return &dbc_nodes[idx]; } DBC_NODE* DBCFile::findNodeByName(QString name) { - if (dbc_nodes.length() == 0) return NULL; + if (dbc_nodes.length() == 0) return nullptr; for (int i = 0; i < dbc_nodes.length(); i++) { if (dbc_nodes[i].name.compare(name, Qt::CaseInsensitive) == 0) @@ -266,7 +267,7 @@ DBC_NODE* DBCFile::findNodeByName(QString name) return &dbc_nodes[i]; } } - return NULL; + return nullptr; } QString DBCFile::getFullFilename() @@ -291,14 +292,15 @@ int DBCFile::getAssocBus() void DBCFile::setAssocBus(int bus) { + int numBuses = CANConManager::getInstance()->getNumBuses(); if (bus < -1) return; - if (bus > 1) return; + if (bus >= numBuses) return; assocBuses = bus; } DBC_ATTRIBUTE *DBCFile::findAttributeByName(QString name) { - if (dbc_attributes.length() == 0) return NULL; + if (dbc_attributes.length() == 0) return nullptr; for (int i = 0; i < dbc_attributes.length(); i++) { if (dbc_attributes[i].name.compare(name, Qt::CaseInsensitive) == 0) @@ -306,13 +308,13 @@ DBC_ATTRIBUTE *DBCFile::findAttributeByName(QString name) return &dbc_attributes[i]; } } - return NULL; + return nullptr; } DBC_ATTRIBUTE *DBCFile::findAttributeByIdx(int idx) { - if (idx < 0) return NULL; - if (idx >= dbc_attributes.count()) return NULL; + if (idx < 0) return nullptr; + if (idx >= dbc_attributes.count()) return nullptr; return &dbc_attributes[idx]; } @@ -345,8 +347,9 @@ DBC_MESSAGE* DBCFile::parseMessageLine(QString line) DBC_MESSAGE msg; msg.ID = match.captured(1).toULong() & 0x7FFFFFFFul; //the ID is always stored in decimal format msg.name = match.captured(2); - msg.len = match.captured(3).toInt(); + msg.len = match.captured(3).toUInt(); msg.sender = findNodeByName(match.captured(4)); + if (!msg.sender) msg.sender = findNodeByIdx(0); messageHandler->addMessage(msg); msgPtr = messageHandler->findMsgByID(msg.ID); } @@ -450,9 +453,12 @@ DBC_SIGNAL* DBCFile::parseSignalLine(QString line, DBC_MESSAGE *msg) if (match.captured(11 + offset).contains(',')) { QString tmp = match.captured(11 + offset).split(',')[0]; - sig.receiver = findNodeByName(tmp); + sig.receiver = findNodeByName(tmp); } else sig.receiver = findNodeByName(match.captured(11 + offset)); + + if (!sig.receiver) sig.receiver = findNodeByIdx(0); //apply default if there was no match + sig.parentMessage = msg; msg->sigHandler->addSignal(sig); if (isMultiplexor) msg->multiplexorSignal = msg->sigHandler->findSignalByName(sig.name); @@ -476,11 +482,11 @@ bool DBCFile::parseValueLine(QString line) if (match.hasMatch()) { //qDebug() << "Data was: " << match.captured(3); - DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); - if (msg != NULL) + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toUInt()); + if (msg != nullptr) { DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2)); - if (sig != NULL) + if (sig != nullptr) { QString tokenString = match.captured(3); DBC_VAL_ENUM_ENTRY val; @@ -525,7 +531,7 @@ bool DBCFile::parseAttributeLine(QString line) if (foundAttr) { qDebug() << "That attribute does exist"; - DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toInt()); + DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toUInt()); if (foundMsg) { qDebug() << "It references a valid, registered message"; @@ -557,7 +563,7 @@ bool DBCFile::parseAttributeLine(QString line) if (foundAttr) { qDebug() << "That attribute does exist"; - DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toInt()); + DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toUInt()); if (foundMsg) { qDebug() << "It references a valid, registered message"; @@ -662,7 +668,7 @@ void DBCFile::loadFile(QString fileName) QString line, rawLine; QRegularExpression regex; QRegularExpressionMatch match; - DBC_MESSAGE *currentMessage = NULL; + DBC_MESSAGE *currentMessage = nullptr; DBC_ATTRIBUTE attr; int numSigFaults = 0, numMsgFaults = 0; @@ -746,11 +752,11 @@ void DBCFile::loadFile(QString fileName) if (match.hasMatch()) { //qDebug() << "Comment was: " << match.captured(3); - DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); - if (msg != NULL) + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toUInt()); + if (msg != nullptr) { DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2)); - if (sig != NULL) + if (sig != nullptr) { sig->comment = match.captured(3); } @@ -767,8 +773,8 @@ void DBCFile::loadFile(QString fileName) if (match.hasMatch()) { //qDebug() << "Comment was: " << match.captured(2); - DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); - if (msg != NULL) + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toUInt()); + if (msg != nullptr) { msg->comment = match.captured(2); } @@ -785,7 +791,7 @@ void DBCFile::loadFile(QString fileName) { //qDebug() << "Comment was: " << match.captured(2); DBC_NODE *node = findNodeByName(match.captured(1)); - if (node != NULL) + if (node != nullptr) { node->comment = match.captured(2); } @@ -1036,6 +1042,9 @@ bool DBCFile::parseAttribute(QString inpString, DBC_ATTRIBUTE &attr) void DBCFile::saveFile(QString fileName) { + int nodeNumber = 1; + int msgNumber = 1; + int sigNumber = 1; QFile *outFile = new QFile(fileName); QString nodesOutput, msgOutput, commentsOutput, valuesOutput; QString defaultsOutput, attrValOutput; @@ -1089,6 +1098,11 @@ void DBCFile::saveFile(QString fileName) DBC_NODE node = dbc_nodes[x]; if (node.name.compare("Vector__XXX", Qt::CaseInsensitive) != 0) { + if (node.name.length() < 1) //detect an empty string and fill it out with something + { + node.name = "NODE" + QString::number(nodeNumber); + nodeNumber++; + } nodesOutput.append(node.name + " "); if (node.comment.length() > 0) { @@ -1119,6 +1133,12 @@ void DBCFile::saveFile(QString fileName) { DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x); + if (msg->name.length() < 1) //detect an empty string and fill it out with something + { + msg->name = "MSG" + QString::number(msgNumber); + msgNumber++; + } + msgOutput.append("BO_ " + QString::number(msg->ID) + " " + msg->name + ": " + QString::number(msg->len) + " " + msg->sender->name + "\n"); if (msg->comment.length() > 0) @@ -1146,6 +1166,13 @@ void DBCFile::saveFile(QString fileName) for (int s = 0; s < msg->sigHandler->getCount(); s++) { DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s); + + if (sig->name.length() < 1) //detect an empty string and fill it out with something + { + sig->name = "SIG" + QString::number(sigNumber); + sigNumber++; + } + msgOutput.append(" SG_ " + sig->name); if (sig->isMultiplexor) msgOutput.append(" M"); @@ -1399,7 +1426,7 @@ DBCFile* DBCHandler::loadDBCFile(int idx) return &loadedFiles.last(); } - return NULL; + return nullptr; } void DBCHandler::removeDBCFile(int idx) @@ -1439,10 +1466,10 @@ DBC_MESSAGE* DBCHandler::findMessage(const CANFrame &frame) if (loadedFiles[i].getAssocBus() == -1 || frame.bus == (unsigned int)loadedFiles[i].getAssocBus()) { DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(frame.ID); - if (msg != NULL) return msg; + if (msg != nullptr) return msg; } } - return NULL; + return nullptr; } int DBCHandler::getFileCount() @@ -1452,15 +1479,15 @@ int DBCHandler::getFileCount() DBCFile* DBCHandler::getFileByIdx(int idx) { - if (loadedFiles.count() == 0) return NULL; - if (idx < 0) return NULL; - if (idx >= loadedFiles.count()) return NULL; + if (loadedFiles.count() == 0) return nullptr; + if (idx < 0) return nullptr; + if (idx >= loadedFiles.count()) return nullptr; return &loadedFiles[idx]; } DBCFile* DBCHandler::getFileByName(QString name) { - if (loadedFiles.count() == 0) return NULL; + if (loadedFiles.count() == 0) return nullptr; for (int i = 0; i < loadedFiles.count(); i++) { if (loadedFiles[i].getFilename().compare(name, Qt::CaseInsensitive) == 0) @@ -1468,7 +1495,7 @@ DBCFile* DBCHandler::getFileByName(QString name) return &loadedFiles[i]; } } - return NULL; + return nullptr; } DBCHandler::DBCHandler() diff --git a/dbc/dbcloadsavewindow.cpp b/dbc/dbcloadsavewindow.cpp index 7bc5a58..e4dcb5c 100644 --- a/dbc/dbcloadsavewindow.cpp +++ b/dbc/dbcloadsavewindow.cpp @@ -2,6 +2,7 @@ #include "ui_dbcloadsavewindow.h" #include #include "helpwindow.h" +#include "connections/canconmanager.h" DBCLoadSaveWindow::DBCLoadSaveWindow(const QVector *frames, QWidget *parent) : QDialog(parent), @@ -34,6 +35,7 @@ DBCLoadSaveWindow::DBCLoadSaveWindow(const QVector *frames, QWidget *p connect(ui->tableFiles, &QTableWidget::cellDoubleClicked, this, &DBCLoadSaveWindow::cellDoubleClicked); editorWindow = new DBCMainEditor(frames); + currentlyEditingFile = nullptr; installEventFilter(this); } @@ -108,10 +110,25 @@ void DBCLoadSaveWindow::saveFile() void DBCLoadSaveWindow::removeFile() { + bool bContinue = true; int idx = ui->tableFiles->currentRow(); if (idx < 0) return; - dbcHandler->removeDBCFile(idx); - ui->tableFiles->removeRow(idx); + + if (currentlyEditingFile == dbcHandler->getFileByIdx(idx)) + { + bContinue = false; + QMessageBox::StandardButton confirmDialog; + confirmDialog = QMessageBox::question(this, "Confirm Deletion", "This DBC is currently open for editing.\nMake sure you've saved any changes!\nAre you sure you want to remove this DBC?", + QMessageBox::Yes|QMessageBox::No); + if (confirmDialog == QMessageBox::Yes) bContinue = true; + } + + if (bContinue) + { + editorWindow->close(); + dbcHandler->removeDBCFile(idx); + ui->tableFiles->removeRow(idx); + } } void DBCLoadSaveWindow::moveUp() @@ -146,7 +163,8 @@ void DBCLoadSaveWindow::cellChanged(int row, int col) { DBCFile *file = dbcHandler->getFileByIdx(row); int bus = ui->tableFiles->item(row, col)->text().toInt(); - if (bus > -2 && bus < 2) + int numBuses = CANConManager::getInstance()->getNumBuses(); + if (bus > -2 && bus < numBuses) { file->setAssocBus(bus); } @@ -185,6 +203,7 @@ void DBCLoadSaveWindow::cellChanged(int row, int col) void DBCLoadSaveWindow::cellDoubleClicked(int row, int col) { Q_UNUSED(col) + currentlyEditingFile = dbcHandler->getFileByIdx(row); editorWindow->setFileIdx(row); editorWindow->show(); } diff --git a/dbc/dbcloadsavewindow.h b/dbc/dbcloadsavewindow.h index 6e41e0c..a307a86 100644 --- a/dbc/dbcloadsavewindow.h +++ b/dbc/dbcloadsavewindow.h @@ -32,6 +32,7 @@ private slots: private: Ui::DBCLoadSaveWindow *ui; DBCHandler *dbcHandler; + DBCFile *currentlyEditingFile; const QVector *referenceFrames; DBCMainEditor *editorWindow; diff --git a/dbc/dbcmaineditor.cpp b/dbc/dbcmaineditor.cpp index 2afe9cc..56a9899 100644 --- a/dbc/dbcmaineditor.cpp +++ b/dbc/dbcmaineditor.cpp @@ -103,6 +103,7 @@ void DBCMainEditor::closeEvent(QCloseEvent *event) { Q_UNUSED(event); writeSettings(); + sigEditor->close(); } void DBCMainEditor::readSettings() @@ -153,7 +154,7 @@ void DBCMainEditor::deleteCurrentNode() int thisRow = ui->NodesTable->currentRow(); QString nodeName = ui->NodesTable->item(thisRow, 0)->text(); if (nodeName.length() > 0 && nodeName.compare("Vector__XXX", Qt::CaseInsensitive) != 0) - { + { ui->NodesTable->removeRow(thisRow); dbcFile->dbc_nodes.removeAt(thisRow); inhibitCellChanged = true; @@ -283,6 +284,9 @@ void DBCMainEditor::onCellChangedMessage(int row,int col) newMsg.name = ""; newMsg.sender = node; newMsg.len = 0; + newMsg.fgColor = ui->MessagesTable->item(row, 4)->backgroundColor(); + newMsg.bgColor = ui->MessagesTable->item(row, 5)->backgroundColor(); + for (int i = 0; i < referenceFrames->length(); i++) { if ((uint) referenceFrames->at(i).ID == msgID) @@ -547,7 +551,7 @@ void DBCMainEditor::insertBlankRow() { QTableWidgetItem *item = new QTableWidgetItem(""); if (i == 4) item->setBackgroundColor(QApplication::palette().color(QPalette::WindowText)); //foreground color - //if (i == 5) item->setBackgroundColor(ui->MessagesTable->palette().color(ui->MessagesTable->backgroundRole())); //background color + if (i == 5) item->setBackgroundColor(QApplication::palette().color(QPalette::Base)); item->setFlags(item->flags() & ~Qt::ItemIsEditable); ui->MessagesTable->setItem(rowIdx, i, item); } diff --git a/dbc/dbcsignaleditor.cpp b/dbc/dbcsignaleditor.cpp index 1eb4977..5e47d0a 100644 --- a/dbc/dbcsignaleditor.cpp +++ b/dbc/dbcsignaleditor.cpp @@ -156,7 +156,8 @@ DBCSignalEditor::DBCSignalEditor(QWidget *parent) : [=]() { if (currentSignal == NULL) return; - currentSignal->name = ui->txtName->text().simplified().replace(' ', '_'); + QString tempNameStr = ui->txtName->text().simplified().replace(' ', '_'); + if (tempNameStr.length() > 0) currentSignal->name = tempNameStr; //need to update the list too. ui->signalsList->currentItem()->setText(currentSignal->name); }); @@ -256,7 +257,7 @@ void DBCSignalEditor::readSettings() QSettings settings; if (settings.value("Main/SaveRestorePositions", false).toBool()) { - resize(settings.value("DBCSignalEditor/WindowSize", QSize(800, 572)).toSize()); + resize(settings.value("DBCSignalEditor/WindowSize", QSize(1000, 600)).toSize()); move(settings.value("DBCSignalEditor/WindowPos", QPoint(100, 100)).toPoint()); } } @@ -371,8 +372,8 @@ void DBCSignalEditor::deleteCurrentSignal() { int currIdx = ui->signalsList->currentRow(); - if(currIdx==ui->signalsList->count()-1) - return; + //if(currIdx==ui->signalsList->count()-1) + //return; if (currIdx > -1) { diff --git a/docs/build.sh b/docs/build.sh index 2d567b1..db03507 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -1,4 +1,4 @@ make qthelp -~/Qt/5.9.1/gcc_64/bin/qcollectiongenerator build/qthelp/SavvyCAN.qhcp -~/Qt/5.9.1/gcc_64/bin/assistant -collectionFile build/qthelp/SavvyCAN.qhc +~/Qt/5.12.1/gcc_64/bin/qcollectiongenerator build/qthelp/SavvyCAN.qhcp +~/Qt/5.12.1/gcc_64/bin/assistant -collectionFile build/qthelp/SavvyCAN.qhc diff --git a/docs/source/conf.py b/docs/source/conf.py index 5502388..07dd908 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -47,7 +47,7 @@ master_doc = 'index' # General information about the project. project = u'SavvyCAN' -copyright = u'2017, EVTV' +copyright = u'2018, EVTV' author = u'Collin Kidder' # The version info for the project you're documenting, acts as replacement for @@ -55,9 +55,9 @@ author = u'Collin Kidder' # built documents. # # The short X.Y version. -version = '181' +version = '189' # The full version, including alpha/beta/rc tags. -release = '181' +release = '189' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/connectionwindow.rst b/docs/source/connectionwindow.rst index b6f60b1..6a25846 100644 --- a/docs/source/connectionwindow.rst +++ b/docs/source/connectionwindow.rst @@ -5,32 +5,60 @@ Connection Window .. image:: ./images/ConnectionWindow.png -The connection window is used to add, remove, and modify connections. At the moment it is possible to use any SocketCAN compatible device (in LINUX) and any GVRET compatible device in any of the supported operating systems. -At this time GVRET compatible devices are: EVTVDue, EVTV CANDue (1.3/2/2.1/2.2), Teensy 3.1-3.6, Macchina M2. +The connection window is used to add, remove, and modify connections. At the moment it is possible +to use any QT SerialBus compatible device and any GVRET compatible device in any of the supported +operating systems. SerialBus supports socketcan on linux, passthrough on Linux and Windows 32 +bit, and Vector, PeakCAN, and TinyCAN on supported OS's. + +At this time GVRET compatible devices are: EVTVDue, EVTV CANDue (1.3/2/2.1/2.2), +Teensy 3.1-3.6, Macchina M2, EVTV ESP32 Due. Connecting To A Dongle ============================== -SavvyCAN is able to connect to GVRET compatible devices to capture new traffic. These devices will present as serial ports on the connected PC. -To connect to a dongle select the proper serial port and click "Create New Connection". If a valid device is found on that serial port the first -statusbar section will update and the currently set canbus speeds will show in the table at the left of the window. These speeds can then -be changed by clicking on the speed (or otherwise selecting the cell in the table) and typing in a new value. Leaving the cell will update the speed to the new value. GVRET devices also support -setting "listen only" on each bus. This mode causes the device to not acknowledge any traffic or try to modify the bus at all. It is as it says, a mode where you can only listen to whatever traffic -is found on the bus. Some older GVRET devices supported a mode where you could change the second bus between single wire CAN and normal CAN. This is deprecated. However, newer GVRET devices have -dedicated single wire CAN buses and the relevant bus will show the checkbox. +SavvyCAN is able to connect to GVRET compatible devices to capture new traffic. These +devices will present as serial ports on the connected PC. +To connect to a dongle select the proper serial port and click "Create New Connection". +If a valid device is found on that serial port the first statusbar section will update +and the currently set canbus speeds will show in the table at the left of the window. +These speeds can then be changed by clicking on the speed (or otherwise selecting the cell +in the table) and typing in a new value. Leaving the cell will update the speed to the new +value. GVRET devices also support setting "listen only" on each bus. This mode causes the +device to not acknowledge any traffic or try to modify the bus at all. It is as it says, +a mode where you can only listen to whatever traffic is found on the bus. Some older GVRET +devices supported a mode where you could change the second bus between single wire CAN +and normal CAN. This is deprecated. However, newer GVRET devices have dedicated single +wire CAN buses and the relevant bus will show the checkbox. -SavvyCAN can also connect to SocketCAN devices in LINUX. Select "SocketCAN" as the connection type and then type in the device name. This should be just the same as you would provide to the can-utils -programs. That is, do not enter /dev/can0 but rather can0. Then push "Create New Connection" and you should see the new connection in the table on the left of the window. Note that SocketCAN devices don't -support changing the baud rate within a program. You must do this when you set up the connection via console commands. This is outside the scope of this documentation. Consult the SocketCAN documentation -for details on configuring such devices. +SavvyCAN can also connect to a wide variety of CAN hardware through the built-in QT +SerialBus drivers. These drivers vary by operating system but support socketcan on LINUX +and Vector tools on both LINUX and Windows. When you select "QT SerialBus Devices" you will +get a list of device types supported. Select a device type and for most devices you should see +the Port list fill out with all registered and valid ports for that driver. Socketcan devices, for +instance, are automatically detected now. Then push "Create New Connection" and you should +see the new connection in the table on the left of the window. Note that SocketCAN devices +don't support changing the baud rate within a program. You must do this when you set up +the connection via console commands. This is outside the scope of this documentation. +Consult the SocketCAN documentation for details on configuring such devices. +The last connection option is "Remote Host." If you select this option then Port will change +to a textbox. Enter the IP address of the remote (but still local to your LAN) IP address. Currently +this works with EVTV ESP32 boards and M2 boards. Debugging Connection Problems ============================== -GVRET devices present as serial ports and have significant configuration options. However, the ability to configure so many things and the ability to compile the firmware yourself both come as a -double edged sword. They present many opportunities for things to go wrong. Because of this there is a debugging console present on the connection window. Click a bus in the table then click "Enable Console" -to cause it to start logging serial traffic. From this console you can see what is going on. It shows what SavvyCAN is sending and what it is getting back. It has extended status messages that might help to narrow down -what is going wrong. Additionally, if you're feeling adventurous you can send traffic to the serial device from the Send line. "Send Hex" accepts a set of hex values separated by spaces. "Send Text" will send the raw -text you type on the line. GVRET traffic is ordinarily binary so "Send Text" won't work very well for that. But, there is also a text console possible on GVRET devices. If you connect to them with a serial program you can -configure things via a text console. Type ? and follow it up with some form of line ending (Cr, Lf, CrLf, any will work). +GVRET devices present as serial ports and have significant configuration options. +However, the ability to configure so many things and the ability to compile the firmware +yourself both come as a double edged sword. They present many opportunities for things +to go wrong. Because of this there is a debugging console present on the connection window. +Click a bus in the table then click "Enable Console" to cause it to start logging serial +traffic. From this console you can see what is going on. It shows what SavvyCAN is sending +and what it is getting back. It has extended status messages that might help to narrow down +what is going wrong. Additionally, if you're feeling adventurous you can send traffic to +the serial device from the Send line. "Send Hex" accepts a set of hex values separated +by spaces. "Send Text" will send the raw text you type on the line. GVRET traffic is +ordinarily binary so "Send Text" won't work very well for that. But, there is also a +text console possible on GVRET devices. If you connect to them with a serial program you can +configure things via a text console. Type ? and follow it up with some form of line +ending (Cr, Lf, CrLf, any will work). diff --git a/docs/source/framedetails.rst b/docs/source/framedetails.rst index 9309f12..3fb92d8 100644 --- a/docs/source/framedetails.rst +++ b/docs/source/framedetails.rst @@ -8,4 +8,12 @@ Frame Details Window The Purpose of Frame Details Window =================================== -This window is used to get detailed statistics about frames. It provides information about a given frame ID across all frames with that ID. You can get such information as the number of frames, the number of data bytes that frame ID has, the average interval between frames with that ID, and the minimum and maximum interval. Also listed are detailed statistics for each data byte in that frame. Each byte has listed which bits changed, the range of values found, and a histogram both graphically (at the bottom of the window) and textually. The textual representation shows the number of times a specific value occurred. Also listed at the bottom is a histogram of each bit and how many times it was set. The graph is a histogram of all the bits and the number of times each bit was set. This can be used to quickly visually see where data has changed. All information can be saved to a text file for later analysis. +This window is used to get detailed statistics about frames. + +It provides information about a given frame ID across all frames with that ID. You can get such information as the number of frames, the number of data bytes that frame ID has, the average interval between frames with that ID, and the minimum and maximum interval. + +Also listed are detailed statistics for each data byte in that frame. Each byte has listed which bits changed, the range of values found, and a histogram both graphically (at the righthand side of the window) and textually. The textual representation shows the number of times a specific value occurred. + +The top right graph is a histogram of all the bits and the number of times each bit was set. This can be used to quickly visually see where data has changed. + +The bottom right graph is of each individual byte as its value varies over time. All textual information can be saved to a text file for later analysis. diff --git a/docs/source/images/ConnectionWindow.png b/docs/source/images/ConnectionWindow.png index 9c64a6e..07132aa 100644 Binary files a/docs/source/images/ConnectionWindow.png and b/docs/source/images/ConnectionWindow.png differ diff --git a/docs/source/images/FrameInfoWindow.png b/docs/source/images/FrameInfoWindow.png index e613f81..4f7cb1b 100644 Binary files a/docs/source/images/FrameInfoWindow.png and b/docs/source/images/FrameInfoWindow.png differ diff --git a/docs/source/images/SignalEditor.png b/docs/source/images/SignalEditor.png index 4dfb227..1ae6589 100644 Binary files a/docs/source/images/SignalEditor.png and b/docs/source/images/SignalEditor.png differ diff --git a/docs/source/images/Sniffer.png b/docs/source/images/Sniffer.png index 1c9e4d5..c473ce8 100644 Binary files a/docs/source/images/Sniffer.png and b/docs/source/images/Sniffer.png differ diff --git a/docs/source/index.rst b/docs/source/index.rst index 75efd02..15516b8 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,5 +1,5 @@ -Welcome to SavvyCAN V181 documentation! +Welcome to SavvyCAN V189 documentation! ========================================= Contents: diff --git a/docs/source/sniffer.rst b/docs/source/sniffer.rst index a5668e1..a349b23 100644 --- a/docs/source/sniffer.rst +++ b/docs/source/sniffer.rst @@ -8,11 +8,64 @@ Sniffer Window Using the Sniffer Window ========================= -This window is essentially a graphical version of the linux can_utils. The general idea here is to display a list of frames such that you only see frames that are actively updating. If a given ID has not been seen in 4 seconds the ID portion will turn RED and then disappear from the list. In this way only frames that are updating are in the list. They are ordered by last update time. Bytes that have deincremented will be red and bytes that have incremented will be green. You can use the "Filters" area to mask away some IDs so that they never show up. This can help to declutter the list. +This window is essentially a turbo charged graphical version of the linux +can_utils program "cansniffer". The general idea here is to display a list +of frames such that you only see frames that are actively updating. If a given +ID has not been seen in 5 seconds the ID portion will turn RED and then disappear +from the list. In this way only frames that are updating are in the list. They are +ordered by ID. Bytes that have deincremented will be red and bytes that have incremented +will be green. You can use the "Filters" area to mask away some IDs so that they never +show up. This can help to declutter the list. + +This window updates with a 200ms interval. Notching and Unnotching ======================== -Honestly, I don't know. It seems that notching means to store the current value of the data bytes for each frame and then use that notch data for the comparison for increment / deincrement to color the bytes. Unnotching would then be clearing that out so that it uses the previous value from the last time the frame ID was seen. But, don't quote me on that. This should be figured out definitively and corrected. +While the window is running it keeps a running list each 200ms cycle of all the bits that +changed in that timespan. Each 200ms this list is backed up and reset. If you push the +notch button the system will remember all the bits that were set in the last 200ms window +and will not color the output if those bits are toggled in the future. They will thus somewhat +be ignored except that you can visually still see them updating. If you click the Notch +button repeatedly it will add any new changed bits to the old changed bits. In this way you +can build up a set of bits to ignore. Un-notching causes all notched (ignored) bits to be +reset and thus all changes will be colored once again. +Advanced Options +================== +All of the above was valid if you do not check any of the four checkboxes. With the checkboxes +unchecked this window is very close to cansniffer on the command line. But, what fun is that? +These checkboxes modify the way the window works in a variety of ways. + +Never Expire IDs +================== + +This checkbox will do exactly what it says on the tin. Instead of frame ID's expiring after +5 seconds of inactivity they will stick around forever. This can be useful so that the ID you +are watching doesn't jump up and down as IDs expire and potentially come back later on or new IDs +are seen. With no expiration you will get a more consistent view of the IDs. You are still free +to filter away IDs you are not interested in. + +Mute notched bits +=================== + +This checkbox might sound a bit strange. With this checked any bytes that you have notched will +ignore any notched bits and not even change the display to update if only notched bits were changed. +This completely hides all notched data. The view of the frames will then NOT perfectly or correctly +represent the actual most up to date data for each ID. So, use this option with caution. But, it +is handy when you are looking for a needle in a haystack and you don't want things changing if you've +already told the program to notch them away. + +Fade inactive bytes +==================== + +This can be used with mute or without but has a similar purpose. When this is checked any bytes +that haven't updated recently will begin to fade away to white. They never quite get all the way +to disappearing but will fade to be very light. In this way only data which is actively changing will +be very visible. This drastically aids in helping you to ignore any bytes that are not changing. + +View Bits +========== + +This option changes the view very starkly. This is the view shown in the picture associated with this topic. When View Bits is selected the display will change to show each bit within the bytes as separate blocks that each can separately be black when set and unchanged, white when unset and unchanged, red when freshly unset, and green when freshly set. This allows for a very fine grained view. Fade inactive, and never expire still work as usual. This mode might be a bit "busy" and lowers the number of IDs you can see at once. But, the choice is yours. You give up some density in exchange for verbosity. diff --git a/framefileio.cpp b/framefileio.cpp index d0be115..dd8e2a6 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -555,9 +555,9 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) thisFrame.isReceived = true; thisFrame.bus = 0; if (line.at(28) == ' ') { - thisFrame.extended = true; - } else { thisFrame.extended = false; + } else { + thisFrame.extended = true; } if (line.at(41) == 'R') { @@ -655,25 +655,28 @@ bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) } if (inHeader) continue; if (line.length() > 2) - { + { QList tokens = line.simplified().split(' '); - thisFrame.timestamp = (uint32_t)(tokens[0].toFloat() * 1000000.0); - thisFrame.ID = tokens[2].toUInt(NULL, 16); - thisFrame.len = tokens[5].toUInt(); - thisFrame.isReceived = tokens[3].toUpper().contains("RX"); - thisFrame.bus = tokens[1].toUInt(); - thisFrame.extended = (thisFrame.ID > 0x7FF); - thisFrame.remote = false; - for (unsigned int d = 6; d < (6 + thisFrame.len); d++) + if (tokens.length() > 4) { - if (tokens.count() > d) - { - thisFrame.data[d - 6] = tokens[d].toInt(NULL, 16); - } - else //expected byte wasn't there to read. Set it zero and set error flag + thisFrame.timestamp = (uint32_t)(tokens[0].toFloat() * 1000000.0); + thisFrame.ID = tokens[2].toUInt(NULL, 16); + thisFrame.len = tokens[5].toUInt(); + thisFrame.isReceived = tokens[3].toUpper().contains("RX"); + thisFrame.bus = tokens[1].toUInt(); + thisFrame.extended = (thisFrame.ID > 0x7FF); + thisFrame.remote = false; + for (unsigned int d = 6; d < (6 + thisFrame.len); d++) { + if (tokens.count() > d) + { + thisFrame.data[d - 6] = tokens[d].toInt(NULL, 16); + } + else //expected byte wasn't there to read. Set it zero and set error flag + { thisFrame.data[d - 6] = 0; foundErrors = true; + } } } frames->append(thisFrame); diff --git a/frameplaybackobject.cpp b/frameplaybackobject.cpp index 5d41774..e990efe 100644 --- a/frameplaybackobject.cpp +++ b/frameplaybackobject.cpp @@ -12,7 +12,8 @@ FramePlaybackObject::FramePlaybackObject() playbackActive = false; playbackForward = true; useOrigTiming = false; - whichBusSend = -1; + whichBusSend = 0; + currentSeqItem = nullptr; } FramePlaybackObject::~FramePlaybackObject() @@ -25,6 +26,12 @@ FramePlaybackObject::~FramePlaybackObject() quint64 FramePlaybackObject::updatePosition(bool forward) { //qDebug() << "updatePosition"; + if (!currentSeqItem) { + playbackTimer->stop(); //pushing this button halts automatic playback + playbackActive = false; + currentPosition = 0; + return 0; + } if (forward) { if (currentPosition < (currentSeqItem->data.count() - 1)) currentPosition++; //still in same file so keep going @@ -231,6 +238,7 @@ void FramePlaybackObject::stepPlaybackForward() playbackActive = false; updatePosition(true); CANConManager::getInstance()->sendFrames(sendingBuffer); + emit statusUpdate(currentPosition); } void FramePlaybackObject::stepPlaybackBackward() @@ -248,6 +256,7 @@ void FramePlaybackObject::stepPlaybackBackward() updatePosition(false); CANConManager::getInstance()->sendFrames(sendingBuffer); + emit statusUpdate(currentPosition); } void FramePlaybackObject::stopPlayback() @@ -262,6 +271,7 @@ void FramePlaybackObject::stopPlayback() playbackTimer->stop(); //pushing this button halts automatic playback playbackActive = false; currentPosition = 0; + emit statusUpdate(currentPosition); } void FramePlaybackObject::pausePlayback() @@ -275,6 +285,7 @@ void FramePlaybackObject::pausePlayback() playbackActive = false; playbackTimer->stop(); + emit statusUpdate(currentPosition); } void FramePlaybackObject::setSequenceObject(SequenceItem *item) @@ -289,6 +300,7 @@ void FramePlaybackObject::setUseOriginalTiming(bool state) void FramePlaybackObject::setSendingBus(int bus) { + qDebug() << "Setting sending bus to " << bus; whichBusSend = bus; } diff --git a/frameplaybackwindow.cpp b/frameplaybackwindow.cpp index 7c6be20..d200e0c 100644 --- a/frameplaybackwindow.cpp +++ b/frameplaybackwindow.cpp @@ -296,9 +296,14 @@ void FramePlaybackWindow::calculateWhichBus() { int idx = ui->comboCANBus->currentIndex(); int maxIdx = ui->comboCANBus->count() - 1; + if (maxIdx == 0) maxIdx = 2; int out = idx; + + qDebug() << idx << "***" << maxIdx; + if (idx == (maxIdx - 1) ) out = -1; if (idx == maxIdx) out = -2; + if (idx < 0) out = 0; playbackObject.setSendingBus(out); } @@ -534,6 +539,7 @@ void FramePlaybackWindow::btnReverseClick() forward = false; isPlaying = true; playbackObject.startPlaybackBackward(); + updateFrameLabel(); } void FramePlaybackWindow::btnStopClick() @@ -554,7 +560,6 @@ void FramePlaybackWindow::btnStopClick() ui->tblSequence->setCurrentCell(0, 0); refreshIDList(); } - updateFrameLabel(); } void FramePlaybackWindow::btnPlayClick() diff --git a/framesenderwindow.cpp b/framesenderwindow.cpp index 3b0c783..54ab529 100644 --- a/framesenderwindow.cpp +++ b/framesenderwindow.cpp @@ -820,6 +820,8 @@ void FrameSenderWindow::processCellChange(int line, int col) sendingData[line].count = 0; + int numBuses = CANConManager::getInstance()->getNumBuses(); + switch (col) { case 0: //Enable check box @@ -831,9 +833,9 @@ void FrameSenderWindow::processCellChange(int line, int col) qDebug() << "Setting enabled to " << sendingData[line].enabled; break; case 1: //Bus designation - tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, 1)->text()); - if (tempVal < 0) tempVal = 0; - if (tempVal > 1) tempVal = 1; + tempVal = Utility::ParseStringToNum(ui->tableSender->item(line, 1)->text()); + if (tempVal < -1) tempVal = -1; + if (tempVal >= numBuses) tempVal = numBuses - 1; sendingData[line].bus = tempVal; qDebug() << "Setting bus to " << tempVal; break; diff --git a/helpwindow.cpp b/helpwindow.cpp index 56186d5..42fcc1b 100644 --- a/helpwindow.cpp +++ b/helpwindow.cpp @@ -67,7 +67,7 @@ HelpWindow* HelpWindow::getRef() void HelpWindow::showHelp(QString help) { if (m_helpEngine) { - QString url = "qthelp://org.sphinx.savvycan.181/doc/" + help; + QString url = "qthelp://org.sphinx.savvycan.189/doc/" + help; qDebug() << "Searching for " << url; QByteArray helpData = m_helpEngine->fileData(QUrl(url)); qDebug() << "Help file size: " << helpData.length(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 6a34cc2..1d6f896 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -55,31 +55,34 @@ MainWindow::MainWindow(QWidget *parent) : readSettings(); + QHeaderView *verticalHeader = ui->canFramesView->verticalHeader(); + verticalHeader->setSectionResizeMode(QHeaderView::Fixed); + verticalHeader->setDefaultSectionSize(10); QHeaderView *HorzHdr = ui->canFramesView->horizontalHeader(); HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview connect(HorzHdr, SIGNAL(sectionClicked(int)), this, SLOT(headerClicked(int))); - graphingWindow = NULL; - frameInfoWindow = NULL; - playbackWindow = NULL; - flowViewWindow = NULL; - frameSenderWindow = NULL; - dbcMainEditor = NULL; - comparatorWindow = NULL; - settingsDialog = NULL; - firmwareUploaderWindow = NULL; - discreteStateWindow = NULL; - connectionWindow = NULL; - scriptingWindow = NULL; - rangeWindow = NULL; - dbcFileWindow = NULL; - fuzzingWindow = NULL; - udsScanWindow = NULL; - motorctrlConfigWindow = NULL; - isoWindow = NULL; - snifferWindow = NULL; - bisectWindow = NULL; - signalViewerWindow = NULL; + graphingWindow = nullptr; + frameInfoWindow = nullptr; + playbackWindow = nullptr; + flowViewWindow = nullptr; + frameSenderWindow = nullptr; + dbcMainEditor = nullptr; + comparatorWindow = nullptr; + settingsDialog = nullptr; + firmwareUploaderWindow = 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; dbcHandler = DBCHandler::getReference(); bDirty = false; inhibitFilterUpdate = false; @@ -160,6 +163,10 @@ MainWindow::MainWindow(QWidget *parent) : temp.bus = 0; temp.ID = 0x100; temp.len = 0; + temp.extended = false; + temp.isReceived = true; + temp.remote = false; + temp.timestamp = 100000000; model->addFrame(temp, true); qApp->processEvents(); tickGUIUpdate(); //force a GUI refresh so that the row exists to measure @@ -180,7 +187,7 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionFirmware_Update->setVisible(false); ui->actionMotorControlConfig->setVisible(false); ui->actionSignal_Viewer->setVisible(false); - //ui->actionSingle_Multi_State_2->setVisible(false); + ui->actionSingle_Multi_State_2->setVisible(false); installEventFilter(this); } @@ -293,7 +300,7 @@ void MainWindow::readSettings() ui->canFramesView->setColumnWidth(5, settings.value("Main/BusColumn", 40).toUInt()); //bus ui->canFramesView->setColumnWidth(6, settings.value("Main/LengthColumn", 40).toUInt()); //length ui->canFramesView->setColumnWidth(7, settings.value("Main/AsciiColumn", 50).toUInt()); //ascii - ui->canFramesView->setColumnWidth(8, settings.value("Main/DataColumn", 225).toUInt()); //data + //ui->canFramesView->setColumnWidth(8, settings.value("Main/DataColumn", 225).toUInt()); //data } if (settings.value("Main/AutoScroll", false).toBool()) { @@ -338,7 +345,7 @@ void MainWindow::writeSettings() 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)); + //settings.setValue("Main/DataColumn", ui->canFramesView->columnWidth(8)); } } @@ -387,11 +394,22 @@ void MainWindow::gridDoubleClicked(QModelIndex idx) void MainWindow::interpretToggled(bool state) { model->setInterpetMode(state); + //ui->canFramesView->resizeRowsToContents(); //a VERY costly operation! } void MainWindow::overwriteToggled(bool state) { - model->setOverwriteMode(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(state); + } + else ui->cbOverwrite->setCheckState(Qt::Unchecked); + } } void MainWindow::updateFilterList() @@ -466,7 +484,7 @@ void MainWindow::tickGUIUpdate() framesPerSec = 0; ui->lbNumFrames->setText(QString::number(model->rowCount())); - if (allowCapture && ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); + if (rxFrames > 0 && /*allowCapture && */ ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); ui->lbFPS->setText(QString::number(framesPerSec)); if (rxFrames > 0) { diff --git a/re/frameinfowindow.cpp b/re/frameinfowindow.cpp index 2769b8d..76710e8 100644 --- a/re/frameinfowindow.cpp +++ b/re/frameinfowindow.cpp @@ -4,6 +4,10 @@ #include "helpwindow.h" #include +const QColor FrameInfoWindow::byteGraphColors[8] = {Qt::blue, Qt::green, Qt::black, Qt::red, //0 1 2 3 + Qt::gray, Qt::yellow, Qt::cyan, Qt::darkMagenta}; //4 5 6 7 +QPen FrameInfoWindow::bytePens[8]; + FrameInfoWindow::FrameInfoWindow(const QVector *frames, QWidget *parent) : QDialog(parent), ui(new Ui::FrameInfoWindow) @@ -32,19 +36,40 @@ FrameInfoWindow::FrameInfoWindow(const QVector *frames, QWidget *paren ui->graphHistogram->legend->setVisible(false); + ui->graphBytes->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes | + QCP::iSelectLegend | QCP::iSelectPlottables); + + ui->graphBytes->xAxis->setRange(0, 63); + ui->graphBytes->yAxis->setRange(0, 265); + ui->graphBytes->axisRect()->setupFullAxesBox(); + + ui->graphBytes->xAxis->setLabel("Time"); + ui->graphBytes->yAxis->setLabel("Value"); + + ui->graphBytes->legend->setVisible(false); + if (useOpenGL) { ui->graphHistogram->setAntialiasedElements(QCP::aeAll); - //ui->graphingView->setNoAntialiasingOnDrag(true); ui->graphHistogram->setOpenGl(true); + ui->graphBytes->setAntialiasedElements(QCP::aeAll); + ui->graphBytes->setOpenGl(true); } else { ui->graphHistogram->setOpenGl(false); ui->graphHistogram->setAntialiasedElements(QCP::aeNone); + ui->graphBytes->setOpenGl(false); + ui->graphBytes->setAntialiasedElements(QCP::aeNone); } installEventFilter(this); + + for (int i = 0; i < 8; i++) + { + bytePens[i].setColor(byteGraphColors[i]); + bytePens[i].setWidth(1); + } } void FrameInfoWindow::showEvent(QShowEvent* event) @@ -94,7 +119,7 @@ void FrameInfoWindow::readSettings() QSettings settings; if (settings.value("Main/SaveRestorePositions", false).toBool()) { - resize(settings.value("FrameInfo/WindowSize", QSize(794, 494)).toSize()); + resize(settings.value("FrameInfo/WindowSize", QSize(794, 694)).toSize()); move(settings.value("FrameInfo/WindowPos", QPoint(50, 50)).toPoint()); } useOpenGL = settings.value("Main/UseOpenGL", false).toBool(); @@ -188,7 +213,8 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) int maxData[8]; int dataHistogram[256][8]; int bitfieldHistogram[64]; - QVector graphX, graphY; + QVector histGraphX, histGraphY; + QVector byteGraphX, byteGraphY[8]; double maxY = -1000.0; uint8_t changedBits[8]; uint8_t referenceBits[8]; @@ -248,7 +274,7 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) baseNode->addChild(tempItem); tempItem = new QTreeWidgetItem(); - tempItem->setText(0, tr("PGN: ") + Utility::formatNumber(jid.pgn)); + tempItem->setText(0, tr("PGN: ") + Utility::formatNumber(jid.pgn) + "(" + QString::number(jid.pgn) + ")"); baseNode->addChild(tempItem); tempItem = new QTreeWidgetItem(); @@ -287,6 +313,12 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) //then find all data points for (int j = 0; j < frameCache.count(); j++) { + byteGraphX.append(j); + for (int bytcnt = 0; bytcnt < frameCache[j].len; bytcnt++) + { + byteGraphY[bytcnt].append(frameCache[j].data[bytcnt]); + } + if (j != 0) { thisInterval = (frameCache[j].timestamp - frameCache[j-1].timestamp); @@ -381,16 +413,17 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) + QString::number(c % 8) + ") :" + QString::number(bitfieldHistogram[c])); dataBase->addChild(tempItem); - graphX.append(c); - graphY.append(bitfieldHistogram[c]); + histGraphX.append(c); + histGraphY.append(bitfieldHistogram[c]); if (bitfieldHistogram[c] > maxY) maxY = bitfieldHistogram[c]; } baseNode->addChild(dataBase); ui->treeDetails->insertTopLevelItem(0, baseNode); + ui->graphHistogram->clearGraphs(); ui->graphHistogram->addGraph(); - ui->graphHistogram->graph()->setData(graphX, graphY); + ui->graphHistogram->graph()->setData(histGraphX, histGraphY); ui->graphHistogram->graph()->setLineStyle(QCPGraph::lsStepLeft); //connect points with lines QBrush graphBrush; graphBrush.setColor(Qt::red); @@ -400,6 +433,16 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) ui->graphHistogram->yAxis->setRange(0, maxY * 1.02); ui->graphHistogram->axisRect()->setupFullAxesBox(); ui->graphHistogram->replot(); + + ui->graphBytes->clearGraphs(); + for (int graphs = 0; graphs < 8; graphs++) + { + ui->graphBytes->addGraph(); + ui->graphBytes->graph()->setData(byteGraphX, byteGraphY[graphs]); + ui->graphBytes->graph()->setPen(bytePens[graphs]); + } + ui->graphBytes->xAxis->setRange(0, byteGraphX.count()); + ui->graphBytes->replot(); } else { diff --git a/re/frameinfowindow.h b/re/frameinfowindow.h index 0f38edc..abf53a8 100644 --- a/re/frameinfowindow.h +++ b/re/frameinfowindow.h @@ -33,6 +33,8 @@ private: QList frameCache; const QVector *modelFrames; bool useOpenGL; + static const QColor byteGraphColors[8]; + static QPen bytePens[8]; void refreshIDList(); void closeEvent(QCloseEvent *event); diff --git a/re/graphingwindow.cpp b/re/graphingwindow.cpp index f999bbb..0cc5121 100644 --- a/re/graphingwindow.cpp +++ b/re/graphingwindow.cpp @@ -696,7 +696,7 @@ void GraphingWindow::saveSpreadsheet() */ QList::iterator iter; - double xMin = 1000000000, xMax=-1000000000; + double xMin = 10000000000000, xMax=-10000000000000; int maxCount = 0; int numGraphs = 0; for (iter = graphParams.begin(); iter != graphParams.end(); ++iter) @@ -732,6 +732,7 @@ void GraphingWindow::saveSpreadsheet() for (int j = 1; j < (maxCount - 1); j++) { currentX = xMin + (j * sliceSize); + qDebug() << "X: " << currentX; outFile->write(QString::number(currentX).toUtf8()); for (int k = 0; k < graphParams.count(); k++) { @@ -764,9 +765,10 @@ void GraphingWindow::saveSpreadsheet() double span = graphParams[k].x[indices[k] + 1] - graphParams[k].x[indices[k]]; double progress = (currentX - graphParams[k].x[indices[k]]) / span; value = Utility::Lerp(graphParams[k].y[indices[k]], graphParams[k].y[indices[k] + 1], progress); + qDebug() << "Span: " << span << " Prog: " << progress << " Value: " << value; } - if (currentX >= graphParams[k].x[indices[k] + 1]) indices[k]++; + if (currentX >= graphParams[k].x[indices[k]]) indices[k]++; outFile->putChar(','); outFile->write(QString::number(value).toUtf8()); @@ -845,7 +847,7 @@ void GraphingWindow::loadDefinitions() QStringList filters; filters.append(QString(tr("Graph definition (*.gdf)"))); - if (dbcHandler == NULL) return; + if (dbcHandler == nullptr) return; if (dbcHandler->getFileCount() == 0) dbcHandler->createBlankFile(); dialog.setFileMode(QFileDialog::ExistingFile); @@ -871,11 +873,14 @@ void GraphingWindow::loadDefinitions() if (tokens[0] == "X") //newest format based around signals { - gp.ID = tokens[1].toInt(NULL, 16); - gp.mask = tokens[2].toULongLong(NULL, 16); + gp.ID = tokens[1].toUInt(nullptr, 16); + gp.mask = tokens[2].toULongLong(nullptr, 16); gp.startBit = tokens[3].toInt(); - if (gp.startBit < 0) gp.intelFormat = false; - else gp.intelFormat = true; + if (gp.startBit < 0) { + gp.intelFormat = false; + gp.startBit *= -1; + } + else gp.intelFormat = true; gp.numBits = tokens[4].toInt(); if (tokens[5] == "Y") gp.isSigned = true; else gp.isSigned = false; @@ -894,18 +899,18 @@ void GraphingWindow::loadDefinitions() } else //one of the two older formats then { - gp.ID = tokens[0].toInt(NULL, 16); + gp.ID = tokens[0].toUInt(nullptr, 16); if (tokens[1] == "S") //old signal based graph definition { //tokens[2] is the signal name. Need to use the message ID and this name to look it up DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(gp.ID); - if (msg != NULL) + if (msg != nullptr) { DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(tokens[2]); if (sig) { gp.mask = 0xFFFFFFFF; - gp.bias = sig->bias; + gp.bias = (float)sig->bias; gp.color.setRed(tokens[3].toInt()); gp.color.setGreen(tokens[4].toInt()); gp.color.setBlue(tokens[5].toInt()); @@ -914,7 +919,7 @@ void GraphingWindow::loadDefinitions() if (sig->valType == SIGNED_INT) gp.isSigned = true; else gp.isSigned = false; gp.numBits = sig->signalSize; - gp.scale = sig->factor; + gp.scale = (float)sig->factor; gp.startBit = sig->startBit; gp.stride = 1; createGraph(gp, true); @@ -926,7 +931,7 @@ void GraphingWindow::loadDefinitions() //hard part - this all changed drastically //the difference between intel and motorola format is whether //start is larger than end byte or not. - uint64_t oldMask = tokens[1].toULongLong(NULL, 16); + uint64_t oldMask = tokens[1].toULongLong(nullptr, 16); int oldStart = tokens[2].toInt(); int oldEnd = tokens[3].toInt(); diff --git a/re/isotp_interpreterwindow.cpp b/re/isotp_interpreterwindow.cpp index d0ad623..bb98076 100644 --- a/re/isotp_interpreterwindow.cpp +++ b/re/isotp_interpreterwindow.cpp @@ -179,6 +179,7 @@ void ISOTP_InterpreterWindow::updatedFrames(int numFrames) void ISOTP_InterpreterWindow::headerClicked(int logicalIndex) { + ui->tableIsoFrames->setSortingEnabled(false); ui->tableIsoFrames->sortByColumn(logicalIndex); } @@ -261,9 +262,13 @@ void ISOTP_InterpreterWindow::newISOMessage(ISOTP_MESSAGE msg) messages.append(msg); rowNum = ui->tableIsoFrames->rowCount(); + ui->tableIsoFrames->insertRow(rowNum); - ui->tableIsoFrames->setItem(rowNum, 0, new QTableWidgetItem(Utility::formatTimestamp(msg.timestamp))); + QTableWidgetItem *item = new QTableWidgetItem; + item->setData(Qt::EditRole, Utility::formatTimestamp(msg.timestamp)); + //ui->tableIsoFrames->setItem(rowNum, 0, (double)msg.timestamp, Utility::formatTimestamp(msg.timestamp))); + ui->tableIsoFrames->setItem(rowNum, 0, item); ui->tableIsoFrames->setItem(rowNum, 1, new QTableWidgetItem(QString::number(msg.ID, 16))); ui->tableIsoFrames->setItem(rowNum, 2, new QTableWidgetItem(QString::number(msg.bus))); if (msg.isReceived) ui->tableIsoFrames->setItem(rowNum, 3, new QTableWidgetItem("Rx")); diff --git a/re/sniffer/SnifferDelegate.cpp b/re/sniffer/SnifferDelegate.cpp new file mode 100644 index 0000000..82b2fdd --- /dev/null +++ b/re/sniffer/SnifferDelegate.cpp @@ -0,0 +1,116 @@ +#include "SnifferDelegate.h" +#include +#include +#include "utility.h" +#include "re/sniffer/snifferitem.h" + +SnifferDelegate::SnifferDelegate(QWidget *parent) : QItemDelegate(parent) +{ + blackBrush = QBrush(Qt::black); + whiteBrush = QBrush(Qt::white); + redBrush = QBrush(Qt::red); + greenBrush = QBrush(Qt::green); + grayBrush = QBrush(QColor(230,230,230)); + mainFont.setPointSize(10); + mainFontInfo = new QFontInfo(mainFont); + mFadeInactive = false; +} + +bool SnifferDelegate::getFadeInactive() +{ + return mFadeInactive; +} + +void SnifferDelegate::setFadeInactive(bool val) +{ + mFadeInactive = val; +} + +void SnifferDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + //qDebug() << "SnifferDelegate Paint Event"; + + if (index.column() < 2) //allow default handling of the first two columns + { + QItemDelegate::paint(painter, option, index); + return; + } + + if (index.column() > 9) return; + + int x, y; + SnifferItem *item = static_cast(index.internalPointer()); + int idx = index.column() - 2; + int val = item->getData(idx); + int prevVal = item->getLastData(idx); + int notchPattern = item->getNotchPattern(idx); + int maskPattern; + if (val < 0) return; + + QRect viewport = option.rect; + + int xSpan = viewport.right() - viewport.left(); + int ySpan = viewport.bottom() - viewport.top(); + int yOffset = (ySpan - (xSpan / 4)) / 2; + if (yOffset < 0) yOffset = 0; + + //qDebug() << "XSpan" << xSpan << " YSpan " << ySpan; + + int xSector = xSpan / 8; + int v = item->getSeqInterval(index.column() - 2) * 10; + if (v > 225) v = 225; + if (v < 0) v = 0; + + for (x = 0; x < 8; x++) + { + maskPattern = 1 << (7-x); + //We look to see if each bit has changed since the last update or not. If not we + //straight draw it black if set or white if unset. If it changed then draw it green if it is newly set + //and red if newly unset + //But also, if a bit is notched we just plain draw it gray no matter what it's doing + if (notchPattern & maskPattern) + { + painter->setBrush(grayBrush); + } + else if ( (val & maskPattern) == (prevVal & maskPattern) ) //wasn't notched so has it changed? No? Then... + { + if (val & maskPattern) + { + painter->setBrush(blackBrush); + } + else + { + painter->setBrush(whiteBrush); + } + } + else //wasn't notched and did change since last time. + { + if (val & maskPattern) + { + painter->setBrush(greenBrush); + } + else + { + painter->setBrush(redBrush); + } + } + if (mFadeInactive) painter->setOpacity((255 - v) / 255.0); + else painter->setOpacity(1.0); + painter->drawRect(viewport.left() + x * xSector, viewport.top() + yOffset, xSector, xSector); + } + + //painter->setPen(QPen(QColor(v,v,v,255))); + painter->setOpacity(1.0); + painter->setPen(Qt::black); + painter->setFont(mainFont); + painter->drawText(QRect(viewport.left(), viewport.top() + xSector + yOffset, xSpan, mainFontInfo->pixelSize()), Qt::AlignCenter, Utility::formatNumber(val)); +} + +QSize SnifferDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QSize origSize = QItemDelegate::sizeHint(option, index); + origSize.setHeight(origSize.height() * 2.0); + return origSize; +} + + diff --git a/re/sniffer/SnifferDelegate.h b/re/sniffer/SnifferDelegate.h new file mode 100644 index 0000000..693c259 --- /dev/null +++ b/re/sniffer/SnifferDelegate.h @@ -0,0 +1,27 @@ +#ifndef SNIFFERDELEGATE_H +#define SNIFFERDELEGATE_H + +#include + +class SnifferDelegate : public QItemDelegate +{ + Q_OBJECT +public: + explicit SnifferDelegate(QWidget *parent = nullptr); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + bool getFadeInactive(); + void setFadeInactive(bool val); + +signals: + +public slots: + +private: + QBrush blackBrush, whiteBrush, redBrush, greenBrush, grayBrush; + QFont mainFont; + QFontInfo* mainFontInfo; + bool mFadeInactive; +}; + +#endif diff --git a/re/sniffer/snifferitem.cpp b/re/sniffer/snifferitem.cpp index 494fda1..b86462b 100644 --- a/re/sniffer/snifferitem.cpp +++ b/re/sniffer/snifferitem.cpp @@ -3,15 +3,21 @@ #include "snifferitem.h" -SnifferItem::SnifferItem(const CANFrame& pFrame): - mNotch(0), +SnifferItem::SnifferItem(const CANFrame& pFrame, quint32 seq): mID(pFrame.ID) { - mMarker = {0,0}; - mLastMarker = {0,0}; + for (int i = 0; i < 8; i++) { + mNotch[i] =0; + mMarker.data[i] = 0; + mMarker.dataTimestamp[i] = 0; + mCurrent.data[i] = pFrame.data[i]; + mCurrent.dataTimestamp[i] = seq; + } + mLastMarker = mMarker; + mCurrent.len = pFrame.len; /* that's dirty */ - update(pFrame); - update(pFrame); + update(pFrame, seq, false); + update(pFrame, seq, false); } @@ -30,57 +36,124 @@ float SnifferItem::getDelta() const return ((float)(mCurrentTime-mLastTime))/1000000; } +//Get a data byte by index 0-7 (but not more than the length of the actual frame) int SnifferItem::getData(uchar i) const { - return (i>=mCurrent.len) ? -1 : ((uchar*) &mCurrent.data)[i]; + return (i >= mCurrent.len) ? -1 : mCurrent.data[i]; } +quint8 SnifferItem::getNotchPattern(uchar i) const +{ + return (i >= mCurrent.len) ? -1 : mNotch[i]; +} + +quint8 SnifferItem::getLastData(uchar i) const +{ + return (i >= mLast.len) ? -1 : mLast.data[i]; +} + +quint32 SnifferItem::getDataTimestamp(uchar i) const +{ + return (i >= mCurrent.len) ? 0 : mCurrent.dataTimestamp[i]; +} + +quint32 SnifferItem::getSeqInterval(uchar i) const +{ + return mCurrSeqVal - getDataTimestamp(i); +} + +//Return whether a given data byte (by index 0-7) has incremented, deincremented, or stayed the same +//since the last message +//The If checks first that we aren't past the actual data length +// then checks whether lastMarker shows that some bits have changed in the previous 200ms cycle +// then we check if the byte in mNotch has bits set and if it does we say nothing changed (notched out) dc SnifferItem::dataChange(uchar i) const { - if( i= mCurrent.len) return dc::NO; + + uchar notch = mNotch[i]; + uchar byt = mCurrent.data[i]; + uchar last = mLast.data[i]; + uchar lastMark = mLastMarker.data[i]; + if( lastMark ) { - return ((uchar*) &mCurrent.data)[i] >= ((uchar*) &mLast.data)[i] ? dc::INC : dc::DEINC; + if (!notch) //if no notching is set + return (byt >= last ? dc::INC : dc::DEINC); + else //mNotch contained a bit pattern so use masks and do things more complicated + { + byt &= ~notch; //mask off bits that were notched + if (byt == 0) return dc::NO; //and if result is null then nothing changed (other than maybe notched bits and they don't count) + last &= ~notch; //need to mask last too + if (last == byt) return dc::NO; + return (byt >= last ? dc::INC : dc::DEINC); //then compare the masked copies to see which way the bit(s) went + } } return dc::NO; } + + int SnifferItem::elapsed() const { return mTime.elapsed(); } -void SnifferItem::update(const CANFrame& pFrame) +//called when a new frame comes in that matches our same ID +//timeSeq is stored so we can figure out the last time a specific byte was updated +//mute is used to specify whether to mask the byte against the notching filter +//in order to hide any updates of the notched bits. This is toggleable +void SnifferItem::update(const CANFrame& pFrame, quint32 timeSeq, bool mute) { + unsigned char maskedCurr, maskedData; + //qDebug() << "update with ts: " << timeSeq; /* copy current to last */ mLast = mCurrent; mLastTime = mCurrentTime; + mCurrSeqVal = timeSeq; /* copy new value */ - memcpy(&mCurrent.data, pFrame.data, 8); + for (int i = 0; i < 8; i++) + { + maskedData = pFrame.data[i]; + if (mute) maskedData &= ~mNotch[i]; + maskedCurr = mCurrent.data[i]; + if (mute) maskedCurr &= ~mNotch[i]; + if (maskedCurr != maskedData) + { + mCurrent.data[i] = pFrame.data[i]; + mCurrent.dataTimestamp[i] = timeSeq; + } + } mCurrent.len = pFrame.len; mCurrentTime = pFrame.timestamp; /* update marker */ - mMarker.data |= mLast.data ^ mCurrent.data; + //We "OR" our stored marker with the changed bits. + //this accumulates changed bits into the marker + for (int i = 0 ; i < 8; i++) mMarker.data[i] |= mLast.data[i] ^ mCurrent.data[i]; //XOR causes only changed bits to be 1's mMarker.len |= mLast.len ^ mCurrent.len; /* restart timeout */ mTime.restart(); } +//Called in refresh from the model. Interval about 200ms currently. +//So, this means the marker only accumulates for 200ms then resets void SnifferItem::updateMarker() { mLastMarker = mMarker; - mMarker = {0, 0}; + for (int i = 0; i < 8; i++) mMarker.data[i] = 0; } +//Notch or un-notch this snifferitem / frame void SnifferItem::notch(bool pNotch) { if(pNotch) - mNotch |= mLastMarker.data; + { + for (int i = 0; i < 8; i++) mNotch[i] |= mLastMarker.data[i]; //add changed bits to notch value + } + else - mNotch = 0; + for (int i = 0; i < 8; i++) mNotch[i] = 0; } diff --git a/re/sniffer/snifferitem.h b/re/sniffer/snifferitem.h index 18d836e..0995a10 100644 --- a/re/sniffer/snifferitem.h +++ b/re/sniffer/snifferitem.h @@ -7,7 +7,8 @@ struct fstCan { - quint64 data; + quint8 data[8]; + quint32 dataTimestamp[8]; int len; }; @@ -22,26 +23,32 @@ enum dc class SnifferItem { public: - explicit SnifferItem(const CANFrame& pFrame); + explicit SnifferItem(const CANFrame& pFrame, quint32 seq); virtual ~SnifferItem(); quint64 getId() const; float getDelta() const; int getData(uchar i) const; + quint8 getNotchPattern(uchar i) const; + quint8 getLastData(uchar i) const; + quint32 getDataTimestamp(uchar i) const; + quint32 getSeqInterval(uchar i) const; dc dataChange(uchar) const; int elapsed() const; - void update(const CANFrame& pFrame); + void update(const CANFrame& pFrame, quint32 timeSeq, bool mute); void updateMarker(); void notch(bool); + private: - quint64 mID; + quint32 mID; struct fstCan mLast; struct fstCan mCurrent; struct fstCan mLastMarker; struct fstCan mMarker; - quint64 mNotch; + quint8 mNotch[8]; quint64 mLastTime; quint64 mCurrentTime; + quint64 mCurrSeqVal; QTime mTime; }; diff --git a/re/sniffer/sniffermodel.cpp b/re/sniffer/sniffermodel.cpp index 2a3bbed..c55163c 100644 --- a/re/sniffer/sniffermodel.cpp +++ b/re/sniffer/sniffermodel.cpp @@ -1,11 +1,15 @@ #include #include "sniffermodel.h" #include "snifferwindow.h" - +#include "SnifferDelegate.h" SnifferModel::SnifferModel(QObject *parent) : QAbstractItemModel(parent), - mFilter(false) + mFilter(false), + mNeverExpire(false), + mFadeInactive(false), + mMuteNotched(false), + mTimeSequence(0) { } @@ -56,16 +60,29 @@ QVariant SnifferModel::data(const QModelIndex &index, int role) const if(tc::DATA_0<=col && col <=tc::DATA_7) { int data = item->getData(col-tc::DATA_0); - if(data>=0) + if(data >= 0) + { return QString("%1").arg(data, 2, 16, QLatin1Char('0')).toUpper(); + } } break; } + case Qt::ForegroundRole: + { + if (!mFadeInactive || col < 2) return QBrush(Qt::black); + int v = item->getSeqInterval(col - 2) * 10; + //qDebug() << "mTS: " << mTimeSequence << " gDT(" << (col - 2) << ") " << item->getDataTimestamp(col - 2); + if (v > 225) v = 225; + if (v < 0) v = 0; + return QBrush(QColor(v,v,v,255)); + break; + } + case Qt::BackgroundRole: { if(tc::ID==col) { - if(item->elapsed()>4000) + if(item->elapsed() > 4000) return QBrush(Qt::red); } else if(tc::DATA_0<=col && col<=tc::DATA_7) @@ -143,6 +160,35 @@ QModelIndex SnifferModel::parent(const QModelIndex &) const return QModelIndex(); } +bool SnifferModel::getNeverExpire() +{ + return mNeverExpire; +} + +bool SnifferModel::getFadeInactive() +{ + return mFadeInactive; +} + +bool SnifferModel::getMuteNotched() +{ + return mMuteNotched; +} + +void SnifferModel::setNeverExpire(bool val) +{ + mNeverExpire = val; +} + +void SnifferModel::setFadeInactive(bool val) +{ + mFadeInactive = val; +} + +void SnifferModel::setMuteNotched(bool val) +{ + mMuteNotched = val; +} void SnifferModel::clear() { @@ -154,20 +200,22 @@ void SnifferModel::clear() endResetModel(); } - +//Called from window with a timer (currently 200ms) void SnifferModel::refresh() { QMap::iterator i; QVector toRemove; SnifferItem* item; + mTimeSequence++; + /* update markers */ for (i = mMap.begin(); i != mMap.end(); ++i) { i.value()->updateMarker(); - if(i.value()->elapsed()>5000) + if(i.value()->elapsed()>5000 && !mNeverExpire) toRemove.append(i.key()); } @@ -237,14 +285,15 @@ void SnifferModel::update(CANConnection*, QVector& pFrames) int index = std::distance(mMap.begin(), mMap.lowerBound(frame.ID)); /* add the frame */ beginInsertRows(QModelIndex(), index, index); - mMap[frame.ID] = new SnifferItem(frame); + mMap[frame.ID] = new SnifferItem(frame, mTimeSequence); + mMap[frame.ID]->update(frame, mTimeSequence, mMuteNotched); endInsertRows(); emit idChange(frame.ID, true); } else //updateData - mMap[frame.ID]->update(frame); + mMap[frame.ID]->update(frame, mTimeSequence, mMuteNotched); } } diff --git a/re/sniffer/sniffermodel.h b/re/sniffer/sniffermodel.h index 7c3fc19..4945cd2 100644 --- a/re/sniffer/sniffermodel.h +++ b/re/sniffer/sniffermodel.h @@ -40,6 +40,13 @@ public: void refresh(); void clear(); void filter(fltType pType, int pId=0); + bool getNeverExpire(); + bool getFadeInactive(); + bool getMuteNotched(); + void setNeverExpire(bool val); + void setFadeInactive(bool val); + void setMuteNotched(bool val); + public slots: void update(CANConnection*, QVector&); @@ -53,6 +60,10 @@ private: QMap mMap; QMap mFilters; bool mFilter; + bool mNeverExpire; + bool mFadeInactive; + bool mMuteNotched; + quint32 mTimeSequence; }; #endif // SNIFFERMODEL_H diff --git a/re/sniffer/snifferwindow.cpp b/re/sniffer/snifferwindow.cpp index 19f142b..f6a4505 100644 --- a/re/sniffer/snifferwindow.cpp +++ b/re/sniffer/snifferwindow.cpp @@ -4,6 +4,7 @@ #include "ui_snifferwindow.h" #include "helpwindow.h" #include "connections/canconmanager.h" +#include "SnifferDelegate.h" SnifferWindow::SnifferWindow(QWidget *parent) : QDialog(parent), @@ -16,12 +17,17 @@ SnifferWindow::SnifferWindow(QWidget *parent) : setWindowFlags(Qt::Window); ui->treeView->setModel(&mModel); + sniffDel = new SnifferDelegate(); + defaultDel = ui->treeView->itemDelegate(); + /* set column width */ - ui->treeView->setColumnWidth(tc::ID, 50); + ui->treeView->setColumnWidth(tc::ID, 80); ui->treeView->setColumnWidth(tc::LAST, 1); for(int i=tc::DATA_0 ; i<=tc::DATA_7 ; i++) - ui->treeView->setColumnWidth(i, 30); + ui->treeView->setColumnWidth(i, 92); + ui->treeView->setUniformRowHeights(true); ui->treeView->header()->setDefaultAlignment(Qt::AlignCenter); + //ui->treeView->setItemDelegate(new SnifferDelegate()); /* activate sorting */ ui->listWidget->setSortingEnabled(true); @@ -38,20 +44,75 @@ SnifferWindow::SnifferWindow(QWidget *parent) : connect(&mModel, &SnifferModel::idChange, this, &SnifferWindow::idChange); connect(ui->listWidget, &QListWidget::itemChanged, this, &SnifferWindow::itemChanged); + connect(ui->cbFadeInactive, &QCheckBox::stateChanged, this, [this](int val){mModel.setFadeInactive(val);sniffDel->setFadeInactive(val);}); + connect(ui->cbMuteNotched, &QCheckBox::stateChanged, this, [this](int val){mModel.setMuteNotched(val);}); + connect(ui->cbNoExpire, &QCheckBox::stateChanged, this, [this](int val){mModel.setNeverExpire(val);}); + connect(ui->cbViewBits, &QCheckBox::stateChanged, this, + [this](int val) + { + if (val) ui->treeView->setItemDelegate(sniffDel); + else + { + ui->treeView->setItemDelegate(defaultDel); + } + } + ); } SnifferWindow::~SnifferWindow() { closeEvent(NULL); + delete sniffDel; delete ui; } +void SnifferWindow::readSettings() +{ + QSettings settings; + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + resize(settings.value("Sniffer/WindowSize", QSize(1100, 750)).toSize()); + move(settings.value("Sniffer/WindowPos", QPoint(50, 50)).toPoint()); + ui->treeView->setColumnWidth(0, settings.value("Sniffer/DeltaColumn", 110).toUInt()); + ui->treeView->setColumnWidth(1, settings.value("Sniffer/IDColumn", 70).toUInt()); + ui->treeView->setColumnWidth(2, settings.value("Sniffer/Data0Column", 92).toUInt()); + ui->treeView->setColumnWidth(3, settings.value("Sniffer/Data1Column", 92).toUInt()); + ui->treeView->setColumnWidth(4, settings.value("Sniffer/Data2Column", 92).toUInt()); + ui->treeView->setColumnWidth(5, settings.value("Sniffer/Data3Column", 92).toUInt()); + ui->treeView->setColumnWidth(6, settings.value("Sniffer/Data4Column", 92).toUInt()); + ui->treeView->setColumnWidth(7, settings.value("Sniffer/Data5Column", 92).toUInt()); + ui->treeView->setColumnWidth(8, settings.value("Sniffer/Data6Column", 92).toUInt()); + ui->treeView->setColumnWidth(9, settings.value("Sniffer/Data7Column", 92).toUInt()); + } +} + +void SnifferWindow::writeSettings() +{ + QSettings settings; + + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + settings.setValue("Sniffer/WindowSize", size()); + settings.setValue("Sniffer/WindowPos", pos()); + settings.setValue("Sniffer/DeltaColumn", ui->treeView->columnWidth(0)); + settings.setValue("Sniffer/IDColumn", ui->treeView->columnWidth(1)); + settings.setValue("Sniffer/Data0Column", ui->treeView->columnWidth(2)); + settings.setValue("Sniffer/Data1Column", ui->treeView->columnWidth(3)); + settings.setValue("Sniffer/Data2Column", ui->treeView->columnWidth(4)); + settings.setValue("Sniffer/Data3Column", ui->treeView->columnWidth(5)); + settings.setValue("Sniffer/Data4Column", ui->treeView->columnWidth(6)); + settings.setValue("Sniffer/Data5Column", ui->treeView->columnWidth(7)); + settings.setValue("Sniffer/Data6Column", ui->treeView->columnWidth(8)); + settings.setValue("Sniffer/Data7Column", ui->treeView->columnWidth(9)); + } +} void SnifferWindow::showEvent(QShowEvent* event) { QDialog::showEvent(event); connect(CANConManager::getInstance(), &CANConManager::framesReceived, &mModel, &SnifferModel::update); mTimer.start(); + readSettings(); installEventFilter(this); qDebug() << "show"; } @@ -64,6 +125,7 @@ void SnifferWindow::closeEvent(QCloseEvent *event) mTimer.stop(); /* disconnect reception of frames */ disconnect(CANConManager::getInstance(), 0, this, 0); + writeSettings(); /* clear model */ mModel.clear(); /* clean list */ @@ -127,7 +189,10 @@ void SnifferWindow::idChange(int pId, bool pAdd) QString text = QString("0x") + QString("%1").arg(pId, 3, 16, QLatin1Char('0')).toUpper(); item = new QListWidgetItem(text); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); - item->setCheckState(mFilter ? Qt::Unchecked : Qt::Checked); + //item->setCheckState(mFilter ? Qt::Unchecked : Qt::Checked); + //always check new IDs now. Otherwise any that expire then come back will not be selected + //and that might be a bigger issue than defaulting them unselected. + item->setCheckState(Qt::Checked); ui->listWidget->addItem(item); mMap[pId] = item; } diff --git a/re/sniffer/snifferwindow.h b/re/sniffer/snifferwindow.h index cebd059..87d9338 100644 --- a/re/sniffer/snifferwindow.h +++ b/re/sniffer/snifferwindow.h @@ -4,6 +4,7 @@ #include #include #include "sniffermodel.h" +#include "SnifferDelegate.h" namespace Ui { class snifferWindow; @@ -46,12 +47,16 @@ public slots: private: void filter(bool pFilter); bool eventFilter(QObject *obj, QEvent *event); + void readSettings(); + void writeSettings(); Ui::snifferWindow* ui; SnifferModel mModel; QTimer mTimer; QMap mMap; bool mFilter; + SnifferDelegate *sniffDel; + QAbstractItemDelegate *defaultDel; }; #endif // SNIFFER_H diff --git a/ui/connectionwindow.ui b/ui/connectionwindow.ui index 197703f..b679a1b 100644 --- a/ui/connectionwindow.ui +++ b/ui/connectionwindow.ui @@ -81,16 +81,6 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - false - - - Kvaser - - - @@ -101,17 +91,17 @@ - + false - SocketCAN + QT SerialBus Devices (SocketCAN, PeakCAN, etc) - + Remote Host @@ -121,6 +111,16 @@ + + + + Device Type: + + + + + + diff --git a/ui/dbcsignaleditor.ui b/ui/dbcsignaleditor.ui index f49da05..d2ea6aa 100644 --- a/ui/dbcsignaleditor.ui +++ b/ui/dbcsignaleditor.ui @@ -6,16 +6,16 @@ 0 0 - 884 - 915 + 1239 + 644 Signal Editor - + - + @@ -52,155 +52,159 @@ - - - - Bit Length: - - - - - - - Byte Order - - - - - - - - - - LSB First (Little Endian) - - - - - - - Type: - - - - - - - Scale: - - - - - - - Bias: - - - - - - - Min Value: - - - - - - - Max Value: - - - - - - - Receiving Node: - - - - - - - Comment: - - - - - - - Units Name: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Multiplexing - - - - - - - - - Not multiplexed - - - - - - - Multiplexed - - - - - - - Multiplexor - - - - - - - - - Multiplex Value - - - - - - + + + + + + Bit Length: + + + + + + + + + + Byte Order + + + + + + + LSB First (Little Endian) + + + + + + + Type: + + + + + + + + + + Scale: + + + + + + + + + + Bias: + + + + + + + + + + Min Value: + + + + + + + + + + Max Value: + + + + + + + + + + Units Name: + + + + + + + + + + Receiving Node: + + + + + + + + + + Multiplexing + + + + + + + + + Not multiplexed + + + + + + + Multiplexed + + + + + + + Multiplexor + + + + + + + + + Multiplex Value + + + + + + + + + + Comment: + + + + + + + + @@ -226,20 +230,9 @@ signalsList txtName - txtBitLength - cbIntelFormat - comboType - txtScale - txtBias - txtMinVal - txtMaxVal - txtUnitName - comboReceiver rbNotMulti rbMultiplexed rbMultiplexor - txtMultiplexValue - txtComment valuesTable diff --git a/ui/frameinfowindow.ui b/ui/frameinfowindow.ui index f696115..5fb1fdc 100644 --- a/ui/frameinfowindow.ui +++ b/ui/frameinfowindow.ui @@ -13,67 +13,473 @@ Detailed Frame Information - + - + - + - - - Frame IDs: - - + + + + + Frame IDs: + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + - - - - 0 - 0 - - - - - 120 - 0 - - - - - - - - - - - - Details: - - - - - - - - 1 - - - + + + + + Details: + + + + + + + + 1 + + + + + + + + Save details to file + + + + - - - Save details to file - - - - - + + + + + Bit Histograph + + + Qt::AlignCenter + + + + + + + + + + Bytes Graph + + + Qt::AlignCenter + + + + + + + + + + + + + + + + + 52 + 101 + 164 + + + + + + + + + 52 + 101 + 164 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + 13 + 75 + true + + + + 0 + + + + + + + + + + + + 78 + 154 + 6 + + + + + + + + + 78 + 154 + 6 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + 13 + 75 + true + + + + 1 + + + + + + + + 13 + 75 + true + + + + 2 + + + + + + + + + + + + 204 + 0 + 0 + + + + + + + + + 204 + 0 + 0 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + 13 + 75 + true + + + + 3 + + + + + + + + + + + + 136 + 138 + 133 + + + + + + + + + 136 + 138 + 133 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + 13 + 75 + true + + + + 4 + + + + + + + + + + + + 252 + 233 + 79 + + + + + + + + + 252 + 233 + 79 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + 13 + 75 + true + + + + 5 + + + + + + + + + + + + 114 + 159 + 207 + + + + + + + + + 114 + 159 + 207 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + 13 + 75 + true + + + + 6 + + + + + + + + + + + + 173 + 127 + 168 + + + + + + + + + 173 + 127 + 168 + + + + + + + + + 190 + 190 + 190 + + + + + + + + + 13 + 75 + true + + + + 7 + + + + + + @@ -88,7 +494,6 @@ listFrameID treeDetails - btnSave diff --git a/ui/isotp_interpreterwindow.ui b/ui/isotp_interpreterwindow.ui index 77e398e..ec1ed3b 100644 --- a/ui/isotp_interpreterwindow.ui +++ b/ui/isotp_interpreterwindow.ui @@ -89,7 +89,11 @@ - + + + true + + diff --git a/ui/snifferwindow.ui b/ui/snifferwindow.ui index 44d36a0..9adc67f 100644 --- a/ui/snifferwindow.ui +++ b/ui/snifferwindow.ui @@ -6,7 +6,7 @@ 0 0 - 602 + 1097 752 @@ -82,6 +82,34 @@ + + + + Never Expire IDs + + + + + + + Mute notched bits + + + + + + + Fade inactive bytes + + + + + + + View Bits + + + diff --git a/utility.h b/utility.h index ec62b7e..5036ba8 100644 --- a/utility.h +++ b/utility.h @@ -128,13 +128,13 @@ public: return output; } - static QString formatTimestamp(uint64_t timestamp) + static QVariant formatTimestamp(uint64_t timestamp) { if (!sysTimeMode) { - if (!secondsMode) return QString::number(timestamp); - else return QString::number((double)timestamp / 1000000.0, 'f', 6); + if (!secondsMode) return (unsigned long long)(timestamp); + else return (double)timestamp / 1000000.0; } - else return QDateTime::fromMSecsSinceEpoch(timestamp / 1000).toString(timeFormat); + else return QDateTime::fromMSecsSinceEpoch(timestamp / 1000); } //parses the input string to grab as much of it as possible while staying alpha numeric