diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b16305..5961338 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,16 +4,15 @@ on: push: branches: - "master" - jobs: buildlinux: name: Linux x64 - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Prepare Environment run: | - sudo apt-get install libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-randr0 libxkbcommon-x11-0 + sudo apt-get install libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-randr0 libxkbcommon-x11-0 libfuse2 - name: Prepare Qt Libraries uses: jurplel/install-qt-action@v3 @@ -33,14 +32,14 @@ jobs: chmod a+x linuxdeployqt-continuous-x86_64.AppImage ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/SavvyCAN.desktop -appimage -extra-plugins=iconengines,platformthemes/libqgtk3.so,canbus - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: SavvyCAN-Linux_x64.AppImage path: SavvyCAN-*x86_64.AppImage - buildmacos: + buildmacos86: name: macOS x64 - runs-on: macos-12 + runs-on: macos-13 steps: - name: Prepare macOS Environment @@ -61,11 +60,42 @@ jobs: mkdir -p SavvyCAN.app/Contents/MacOS/help cp -R help/* SavvyCAN.app/Contents/MacOS/help macdeployqt SavvyCAN.app -dmg + mv SavvyCAN.dmg SavvyCAN_x64.dmg - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: SavvyCAN-macOS_x64.dmg - path: SavvyCAN.dmg + path: SavvyCAN_x64.dmg + + buildmacos-arm64: + name: macOS ARM64 + runs-on: macos-14 + + steps: + - name: Prepare macOS Environment + run: | + brew install qt5 + brew link qt5 --force + + - name: Clone + uses: actions/checkout@v3 + + - name: Compile + run: | + qmake CONFIG+=release CONFIG+=sdk_no_version_check SavvyCAN.pro + make -j`sysctl kern.aioprocmax | awk '{print $2}'` + + - name: Package + run: | + mkdir -p SavvyCAN.app/Contents/MacOS/help + cp -R help/* SavvyCAN.app/Contents/MacOS/help + macdeployqt SavvyCAN.app -dmg + mv SavvyCAN.dmg SavvyCAN_arm64.dmg + + - uses: actions/upload-artifact@v4 + with: + name: SavvyCAN-macOS_ARM64.dmg + path: SavvyCAN_arm64.dmg buildwindows: name: Windows x64 @@ -113,7 +143,7 @@ jobs: mkdir package/canbus copy "${Env:Qt5_Dir}/plugins/canbus/*.*" package/canbus/ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: SavvyCAN-Windows_x64 path: package @@ -121,12 +151,12 @@ jobs: pre-release: name: "pre-release" runs-on: "ubuntu-latest" - needs: [buildwindows, buildmacos, buildlinux] + needs: [buildwindows, buildmacos86, buildmacos-arm64, buildlinux] steps: - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4.1.7 - name: Display structure of downloaded files run: zip -r SavvyCAN-Windows_x64_CIBuild.zip SavvyCAN-Windows_x64 @@ -139,12 +169,13 @@ jobs: files: | SavvyCAN-Linux_x64.AppImage/SavvyCAN-*x86_64.AppImage SavvyCAN-Windows_x64_CIBuild.zip - SavvyCAN-macOS_x64.dmg/SavvyCAN.dmg + SavvyCAN-macOS_x64.dmg/SavvyCAN_x64.dmg + SavvyCAN-macOS_ARM64.dmg/SavvyCAN_arm64.dmg notify: name: Notify Discord runs-on: ubuntu-latest - needs: [buildwindows, buildmacos, buildlinux] + needs: [buildwindows, buildmacos86, buildmacos-arm64, buildlinux] steps: - id: msg_var name: remove newlines diff --git a/.gitignore b/.gitignore index 6cb0bf6..13068cd 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ Makefile SavvyCAN.pro.qtds .xcode SavvyCAN.xcodeproj +.vscode diff --git a/README.md b/README.md index e4cc5ca..e6f6aa1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SavvyCAN Qt based cross platform canbus tool -(C) 2015-2021 EVTV and Collin Kidder +(C) 2015-2024 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 @@ -17,9 +17,6 @@ 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 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. It should, however, be noted that use of a capture device is not required to make use of this program. It can load and save in several formats: @@ -35,6 +32,7 @@ of this program. It can load and save in several formats: 9. Vehicle Spy log files 10. CANDump / Kayak (Read only) 11. PCAN Viewer (Read Only) +12. Wireshark socketcan PCAP file (Read only) ## Dependencies @@ -50,8 +48,9 @@ to download it separately. This project requires 5.14.0 or higher because of a dependency on QtSerialBus and other new additions to Qt. -NOTE: Qt6 currently lacks support for QtSerialBus and many other Qt sub-features. At this time you cannot -use Qt6 to compile SavvyCAN. Support for Qt6 should be possible around Qt6.2. +NOTE: As the code in this master branch sits, it does compile with QT6. Support for QT6 is approximately "beta" quality. Most all functions should work, please send issues if found. + +It appears that the current binary build for MacOS requires at least MacOS 10.15 ## Instructions for compiling: diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 3b782a9..d608800 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -4,11 +4,15 @@ # #------------------------------------------------- +!versionAtLeast(QT_VERSION, 5.14.0) { + error("Current version of Qt ($${QT_VERSION}) is too old, this project requires Qt 5.14 or newer") +} + QT = core gui printsupport qml serialbus serialport widgets help network opengl CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT -CONFIG += c++11 +CONFIG += c++17 CONFIG += NO_UNIT_TESTS DEFINES += QCUSTOMPLOT_USE_OPENGL @@ -26,6 +30,7 @@ SOURCES += main.cpp\ connections/lawicel_serial.cpp \ connections/mqtt_bus.cpp \ dbc/dbcnodeduplicateeditor.cpp \ + framesenderobject.cpp \ mqtt/qmqtt_client.cpp \ mqtt/qmqtt_client_p.cpp \ mqtt/qmqtt_frame.cpp \ @@ -113,6 +118,7 @@ HEADERS += mainwindow.h \ connections/mqtt_bus.h \ dbc/dbcnodeduplicateeditor.h \ dbc/dbcnoderebaseeditor.h \ + framesenderobject.h \ mqtt/qmqtt.h \ mqtt/qmqtt_client.h \ mqtt/qmqtt_client_p.h \ @@ -255,12 +261,16 @@ unix { DISTFILES += SavvyCAN.desktop } +windows { +RC_ICONS=icons/SavvyIcon.ico +} + examplefiles.files=examples examplefiles.path = $$PREFIX/share/savvycan/examples INSTALLS += examplefiles iconfiles.files=icons -iconfiles.path = $$PREFIX/share/icons +iconfiles.path = $$PREFIX/share INSTALLS += iconfiles helpfiles.files=help/* diff --git a/bus_protocols/uds_handler.cpp b/bus_protocols/uds_handler.cpp index 697f758..f39b5bb 100644 --- a/bus_protocols/uds_handler.cpp +++ b/bus_protocols/uds_handler.cpp @@ -182,11 +182,11 @@ UDS_HANDLER::~UDS_HANDLER() delete isoHandler; } -void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) +UDS_MESSAGE UDS_HANDLER::tryISOtoUDS(ISOTP_MESSAGE msg, bool *result) { - qDebug() << "UDS handler got ISOTP frame"; const unsigned char *data = reinterpret_cast(msg.payload().constData()); int dataLen = msg.payload().count(); + *result = true; UDS_MESSAGE udsMsg; udsMsg.bus = msg.bus; udsMsg.setExtendedFrameFormat(msg.hasExtendedFrameFormat()); @@ -208,9 +208,17 @@ void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) { udsMsg.service = data[1]; if (dataLen > 2) udsMsg.subFunc = data[2]; - else return; + else + { + *result = false; + return udsMsg; + }; } - else return; + else + { + *result = false; + return udsMsg; + }; udsMsg.payload().remove(0, 2); } else @@ -220,9 +228,22 @@ void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) udsMsg.payload().remove(0, 1); } } - else return; + else + { + *result = false; + }; + return udsMsg; +} - emit newUDSMessage(udsMsg); +void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE msg) +{ + qDebug() << "UDS handler got ISOTP frame"; + UDS_MESSAGE udsMsg; + + bool result; + udsMsg = tryISOtoUDS(msg, &result); + + if (result) emit newUDSMessage(udsMsg); } void UDS_HANDLER::setFlowCtrl(bool state) diff --git a/bus_protocols/uds_handler.h b/bus_protocols/uds_handler.h index 5c5ea4f..b1578ed 100644 --- a/bus_protocols/uds_handler.h +++ b/bus_protocols/uds_handler.h @@ -105,6 +105,7 @@ public: QString getShortDesc(QVector &codeVector, int code); QString getLongDesc(QVector &codeVector, int code); QString getDetailedMessageAnalysis(const UDS_MESSAGE &msg); + UDS_MESSAGE tryISOtoUDS(ISOTP_MESSAGE msg, bool *result); public slots: void gotISOTPFrame(ISOTP_MESSAGE msg); diff --git a/can_trigger_structs.h b/can_trigger_structs.h index 32ef0ef..177df4f 100644 --- a/can_trigger_structs.h +++ b/can_trigger_structs.h @@ -4,6 +4,7 @@ #include "can_structs.h" #include +#include enum TriggerMask { @@ -38,6 +39,7 @@ public: //If ID is -1 then this is the temporary storage register. This is a shadow //register used to accumulate the results of a multi operation modifier. //if ID is -2 then this is a look up of our own data bytes stored in the class data. +//Of course, if the ID is positive then we grab bytes or signals from newest message with that ID class ModifierOperand { public: @@ -45,6 +47,7 @@ public: int bus; int databyte; bool notOper; //should a bitwise NOT be applied to this prior to doing the actual calculation? + QString signalName; //if ID is positive and there is text in here then we'll look up the signal and use its value }; //list of operations that can be done between the two operands @@ -73,7 +76,8 @@ public: class Modifier { public: - int destByte; + int destByte; //if -1 then target one of this ID's signals instead + QString signalName; QList operations; }; diff --git a/connections/canbus.cpp b/connections/canbus.cpp index 3779e86..0623baa 100644 --- a/connections/canbus.cpp +++ b/connections/canbus.cpp @@ -13,21 +13,13 @@ CANBus::CANBus() } -CANBus::CANBus(const CANBus& pBus) : - speed(pBus.speed), - listenOnly(pBus.listenOnly), - singleWire(pBus.singleWire), - active(pBus.active), - canFD(pBus.canFD), - dataRate(pBus.dataRate){} - - bool CANBus::operator==(const CANBus& bus) const{ return speed == bus.speed && listenOnly == bus.listenOnly && singleWire == bus.singleWire && active == bus.active && - canFD == bus.canFD; + canFD == bus.canFD && + dataRate == bus.dataRate; } void CANBus::setSpeed(int newSpeed){ @@ -55,11 +47,11 @@ void CANBus::setCanFD(bool mode){ canFD = mode; } -int CANBus::getSpeed(){ +int CANBus::getSpeed() const { return speed; } -int CANBus::getDataRate(){ +int CANBus::getDataRate() const { return dataRate; } @@ -68,29 +60,30 @@ void CANBus::setDataRate(int newSpeed){ dataRate = newSpeed; } -bool CANBus::isListenOnly(){ +bool CANBus::isListenOnly() const { return listenOnly; } -bool CANBus::isSingleWire(){ +bool CANBus::isSingleWire() const { return singleWire; } -bool CANBus::isActive(){ +bool CANBus::isActive() const { return active; } -bool CANBus::isCanFD(){ +bool CANBus::isCanFD() const { return canFD; } -QDataStream& operator<<( QDataStream & pStream, const CANBus& pCanBus ) +QDataStream& operator<<(QDataStream & pStream, const CANBus& pCanBus) { pStream << pCanBus.speed; pStream << pCanBus.listenOnly; pStream << pCanBus.singleWire; pStream << pCanBus.active; + // FIXME CANFD settings missing return pStream; } diff --git a/connections/canbus.h b/connections/canbus.h index ff3d887..61fc122 100644 --- a/connections/canbus.h +++ b/connections/canbus.h @@ -5,13 +5,6 @@ class CANBus { -public: - CANBus(); - CANBus(const CANBus&); - bool operator==(const CANBus&) const; - CANBus& operator=(const CANBus& other) = default; - //virtual ~CANBus(){} - int speed; bool listenOnly; bool singleWire; @@ -19,28 +12,36 @@ public: bool canFD; int dataRate; + friend QDataStream& operator<<(QDataStream & pStream, const CANBus& pCanBus); + friend QDataStream& operator>>(QDataStream & pStream, CANBus& pCanBus); +public: + CANBus(); + + bool operator==(const CANBus&) const; + void setSpeed(int); // new speed void setListenOnly(bool); //bool for whether to only listen void setSingleWire(bool); //bool for whether to use single wire mode void setActive(bool); //whether this bus should be enabled or not. void setCanFD(bool); // enable or disable CANFD support - int getSpeed(); - int getDataRate(); void setDataRate(int newSpeed); - bool isListenOnly(); - bool isSingleWire(); - bool isActive(); - bool isCanFD(); + + int getSpeed() const; + int getDataRate() const; + bool isListenOnly() const; + bool isSingleWire() const; + bool isActive() const; + bool isCanFD() const; }; -QDataStream& operator<<( QDataStream & pStream, const CANBus& pCanBus ); -QDataStream & operator>>(QDataStream & pStream, CANBus& pCanBus); +QDataStream& operator<<(QDataStream & pStream, const CANBus& pCanBus); +QDataStream& operator>>(QDataStream & pStream, CANBus& pCanBus); Q_DECLARE_METATYPE(CANBus); struct BusData { CANBus mBus; - bool mConfigured; + bool mConfigured = {}; QVector mTargettedFrames; }; diff --git a/connections/canconfactory.cpp b/connections/canconfactory.cpp index ae2bc6b..774c92c 100644 --- a/connections/canconfactory.cpp +++ b/connections/canconfactory.cpp @@ -14,7 +14,7 @@ CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDri { switch(pType) { case SERIALBUS: - return new SerialBusConnection(pPortName, pDriverName); + return new SerialBusConnection(pPortName, pDriverName, pBusSpeed, pDataRate, pCanFd); case GVRET_SERIAL: if(pPortName.contains(".") && !pPortName.contains("tty") && !pPortName.contains("serial")) return new GVRetSerial(pPortName, true); diff --git a/connections/canconmanager.cpp b/connections/canconmanager.cpp index 9496b9e..9ac931f 100644 --- a/connections/canconmanager.cpp +++ b/connections/canconmanager.cpp @@ -222,7 +222,7 @@ bool CANConManager::sendFrame(const CANFrame& pFrame) workingFrame.isReceived = false; if (useSystemTime) { - workingFrame.setTimeStamp(QCanBusFrame::TimeStamp(0,QDateTime::currentMSecsSinceEpoch() * 1000)); + workingFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(QDateTime::currentMSecsSinceEpoch() * 1000ul)); } else { diff --git a/connections/canconnection.cpp b/connections/canconnection.cpp index 74e4fb8..e6f0228 100644 --- a/connections/canconnection.cpp +++ b/connections/canconnection.cpp @@ -13,11 +13,11 @@ CANConnection::CANConnection(QString pPort, int pQueueLen, bool pUseThread) : mNumBuses(pNumBuses), + mSerialSpeed(pSerialSpeed), mQueue(), mPort(pPort), mDriver(pDriver), mType(pType), - mSerialSpeed(pSerialSpeed), mIsCapSuspended(false), mStatus(CANCon::NOT_CONNECTED), mStarted(false), @@ -41,8 +41,13 @@ CANConnection::CANConnection(QString pPort, if (pBusSpeed > 0) mBusData[0].mBus.setSpeed(pBusSpeed); mBusData[0].mBus.setCanFD(pCanFd); - if (pDataRate > 0) mBusData[0].mBus.setDataRate(pDataRate); - + if (pDataRate > 0) { + mBusData[0].mBus.setDataRate(pDataRate); + if (pCanFd) { + mBusData[0].mBus.setCanFD(pCanFd); + } + } + /* if needed, create a thread and move ourself into it */ if(pUseThread) { mThread_p = new QThread(); @@ -210,6 +215,10 @@ int CANConnection::getNumBuses() const{ return mNumBuses; } +int CANConnection::getSerialSpeed() const{ + return mSerialSpeed; +} + bool CANConnection::isConfigured(int pBusId) { if( pBusId < 0 || pBusId >= getNumBuses()) diff --git a/connections/canconnection.h b/connections/canconnection.h index fde8296..b92b229 100644 --- a/connections/canconnection.h +++ b/connections/canconnection.h @@ -52,6 +52,12 @@ public: */ int getNumBuses() const; + /** + * @brief getserialSpeed + * @return returns the serial speed of the device + */ + int getSerialSpeed() const; + /** * @brief getPort * @return returns the port name of the device diff --git a/connections/canserver.cpp b/connections/canserver.cpp index a8ad145..22daf59 100644 --- a/connections/canserver.cpp +++ b/connections/canserver.cpp @@ -214,7 +214,7 @@ void CANserver::readNetworkData() frame_p->setFrameType(QCanBusFrame::DataFrame); frame_p->isReceived = true; - frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, QDateTime::currentMSecsSinceEpoch() * 1000ul)); + frame_p->setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(QDateTime::currentMSecsSinceEpoch() * 1000ul)); frame_p->setPayload(datagram.mid(dataByteLocation, length)); diff --git a/connections/connectionwindow.cpp b/connections/connectionwindow.cpp index 639d531..aa0caf3 100644 --- a/connections/connectionwindow.cpp +++ b/connections/connectionwindow.cpp @@ -79,12 +79,12 @@ ConnectionWindow::ConnectionWindow(QWidget *parent) : //Need to make sure it tries to share the address in case there are //multiple instances of SavvyCAN running. rxBroadcastGVRET->bind(QHostAddress::AnyIPv4, 17222, QAbstractSocket::ShareAddress); - connect(rxBroadcastGVRET, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); + connect(rxBroadcastGVRET, &QUdpSocket::readyRead, this, &ConnectionWindow::readPendingDatagrams); //Doing the same for socketcand/kayak hosts: rxBroadcastKayak = new QUdpSocket(this); rxBroadcastKayak->bind(QHostAddress::AnyIPv4, 42000, QAbstractSocket::ShareAddress); - connect(rxBroadcastKayak, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); + connect(rxBroadcastKayak, &QUdpSocket::readyRead, this, &ConnectionWindow::readPendingDatagrams); } @@ -236,12 +236,12 @@ void ConnectionWindow::consoleEnableChanged(bool checked) { CANConnection* conn_p = connModel->getAtIdx(selIdx); if (checked) { //enable console - connect(conn_p, SIGNAL(debugOutput(QString)), this, SLOT(getDebugText(QString))); - connect(this, SIGNAL(sendDebugData(QByteArray)), conn_p, SLOT(debugInput(QByteArray))); + connect(conn_p, &CANConnection::debugOutput, this, &ConnectionWindow::getDebugText, Qt::UniqueConnection); + connect(this, &ConnectionWindow::sendDebugData, conn_p, &CANConnection::debugInput, Qt::UniqueConnection); } else { //turn it off - disconnect(conn_p, SIGNAL(debugOutput(QString)), nullptr, nullptr); - disconnect(this, SIGNAL(sendDebugData(QByteArray)), conn_p, SLOT(debugInput(QByteArray))); + disconnect(conn_p, &CANConnection::debugOutput, nullptr, nullptr); + disconnect(this, &ConnectionWindow::sendDebugData, conn_p, &CANConnection::debugInput); } } @@ -336,7 +336,9 @@ void ConnectionWindow::connectionStatus(CANConStatus pStatus) Q_UNUSED(pStatus); qDebug() << "Connectionstatus changed"; + int selIdx = ui->tableConnections->selectionModel()->currentIndex().row(); connModel->refresh(); + ui->tableConnections->selectRow(selIdx); } void ConnectionWindow::setSuspendAll(bool pSuspend) @@ -454,8 +456,8 @@ void ConnectionWindow::currentRowChanged(const QModelIndex ¤t, const QMode int selIdx = current.row(); CANConnection* prevConn = connModel->getAtIdx(previous.row()); if(prevConn != nullptr) - disconnect(prevConn, SIGNAL(debugOutput(QString)), nullptr, nullptr); - disconnect(this, SIGNAL(sendDebugData(QByteArray)), nullptr, nullptr); + disconnect(prevConn, &CANConnection::debugOutput, nullptr, nullptr); + disconnect(this, &ConnectionWindow::sendDebugData, nullptr, nullptr); /* set parameters */ if (selIdx == -1) { @@ -472,7 +474,7 @@ void ConnectionWindow::currentRowChanged(const QModelIndex ¤t, const QMode if(!conn_p) return; //because this might have already been setup during the initial setup so tear that one down and then create the normal one. - //disconnect(conn_p, SIGNAL(debugOutput(QString)), 0, 0); + //disconnect(conn_p, &CANConnection::debugOutput, 0, 0); numBuses = conn_p->getNumBuses(); int numB = ui->tabBuses->count(); @@ -485,8 +487,8 @@ void ConnectionWindow::currentRowChanged(const QModelIndex ¤t, const QMode populateBusDetails(0); if (ui->ckEnableConsole->isChecked()) { - connect(conn_p, SIGNAL(debugOutput(QString)), this, SLOT(getDebugText(QString))); - connect(this, SIGNAL(sendDebugData(QByteArray)), conn_p, SLOT(debugInput(QByteArray))); + connect(conn_p, &CANConnection::debugOutput, this, &ConnectionWindow::getDebugText, Qt::UniqueConnection); + connect(this, &ConnectionWindow::sendDebugData, conn_p, &CANConnection::debugInput, Qt::UniqueConnection); } } } @@ -524,12 +526,11 @@ CANConnection* ConnectionWindow::create(CANCon::type pTye, QString pPortName, QS if(conn_p) { /* connect signal */ - connect(conn_p, SIGNAL(status(CANConStatus)), - this, SLOT(connectionStatus(CANConStatus))); + connect(conn_p, &CANConnection::status, this, &ConnectionWindow::connectionStatus); if (ui->ckEnableConsole->isChecked()) { //set up the debug console to operate if we've selected it. Doing so here allows debugging right away during set up - connect(conn_p, SIGNAL(debugOutput(QString)), this, SLOT(getDebugText(QString))); + connect(conn_p, &CANConnection::debugOutput, this, &ConnectionWindow::getDebugText, Qt::UniqueConnection); } /*TODO add return value and checks */ conn_p->start(); @@ -552,13 +553,17 @@ void ConnectionWindow::loadConnections() QVector driverNames = settings.value("connections/driverNames").value>(); QVector devTypes = settings.value("connections/types").value>(); + QVector busSpeeds = settings.value("connections/busSpeeds_0").value>(); + QVector DataRates = settings.value("connections/DataRates_0").value>(); + QVector isCanFds = settings.value("connections/isCanFds_0").value>(); + QVector serialSpeeds = settings.value("connections/serialSpeeds").value>(); //don't load the connections if the three setting arrays above aren't all the same size. - if (portNames.count() != driverNames.count() || devTypes.count() != driverNames.count()) return; + if (portNames.count() != driverNames.count() || devTypes.count() != driverNames.count() || busSpeeds.count() != driverNames.count() || isCanFds.count() != driverNames.count() || + DataRates.count() != driverNames.count() || serialSpeeds.count() != driverNames.count() ) return; for(int i = 0 ; i < portNames.count() ; i++) { - //TODO: add serial speed and bus speed to this properly. - CANConnection* conn_p = create((CANCon::type)devTypes[i], portNames[i], driverNames[i], 0, 0, false, 0); + CANConnection* conn_p = create((CANCon::type)devTypes[i], portNames[i], driverNames[i], serialSpeeds[i], busSpeeds[i], isCanFds[i] ? true : false, DataRates[i]); /* add connection to model */ connModel->add(conn_p); } @@ -578,10 +583,19 @@ void ConnectionWindow::saveConnections() QVector driverNames; QVector serialSpeeds; QVector busSpeeds; - + QVector DataRates; + QVector CanFds; + /* save connections */ foreach(CANConnection* conn_p, conns) - { + { CANBus bus; + + if (conn_p->getBusSettings(0, bus)) { + busSpeeds.append(bus.getSpeed()); + CanFds.append(bus.isCanFD() ? 1 : 0); + DataRates.append(bus.getDataRate()); + } + serialSpeeds.append(conn_p->getSerialSpeed()); portNames.append(conn_p->getPort()); devTypes.append(conn_p->getType()); driverNames.append(conn_p->getDriver()); @@ -590,6 +604,10 @@ void ConnectionWindow::saveConnections() settings.setValue("connections/portNames", QVariant::fromValue(portNames)); settings.setValue("connections/types", QVariant::fromValue(devTypes)); settings.setValue("connections/driverNames", QVariant::fromValue(driverNames)); + settings.setValue("connections/busSpeeds_0", QVariant::fromValue(busSpeeds)); + settings.setValue("connections/isCanFds_0", QVariant::fromValue(CanFds)); + settings.setValue("connections/DataRates_0", QVariant::fromValue(DataRates)); + settings.setValue("connections/serialSpeeds", QVariant::fromValue(serialSpeeds)); } void ConnectionWindow::moveConnUp() diff --git a/connections/gvretserial.cpp b/connections/gvretserial.cpp index dc63ae0..72eb637 100644 --- a/connections/gvretserial.cpp +++ b/connections/gvretserial.cpp @@ -685,9 +685,10 @@ void GVRetSerial::procRXChar(unsigned char c) buildTimestamp += timeBasis; if (useSystemTime) { - buildTimestamp = QDateTime::currentMSecsSinceEpoch() * 1000l; + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(QDateTime::currentMSecsSinceEpoch() * 1000ul)); + } else { + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, buildTimestamp)); } - buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, buildTimestamp)); break; case 4: buildId = c; @@ -770,9 +771,10 @@ void GVRetSerial::procRXChar(unsigned char c) buildTimestamp += timeBasis; if (useSystemTime) { - buildTimestamp = QDateTime::currentMSecsSinceEpoch() * 1000l; + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(QDateTime::currentMSecsSinceEpoch() * 1000ul)); + } else { + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, buildTimestamp)); } - buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, buildTimestamp)); break; case 4: buildId = c; diff --git a/connections/lawicel_serial.cpp b/connections/lawicel_serial.cpp index 0b502f8..e45067d 100644 --- a/connections/lawicel_serial.cpp +++ b/connections/lawicel_serial.cpp @@ -491,7 +491,26 @@ void LAWICELSerial::readSerialData() { qDebug() << "Got CR!"; - buildFrame.setTimeStamp(QDateTime::currentMSecsSinceEpoch() * 1000l); + if (useSystemTime) + { + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(QDateTime::currentMSecsSinceEpoch() * 1000ul)); + } + else + { + //If total length is greater than command, header and data, timestamps must be enabled. + if (data.length() > (5 + mBuildLine.mid(4, 1).toInt() * 2 + 1)) + { + //Four bytes after the end of the data bytes. + buildTimestamp = mBuildLine.mid(5 + mBuildLine.mid(4, 1).toInt() * 2, 4).toInt(nullptr, 16) * 1000l; + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, buildTimestamp)); + } + else + { + //Default to system time if timestamps are disabled. + buildFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(QDateTime::currentMSecsSinceEpoch() * 1000ul)); + } + } + switch (mBuildLine[0].toLatin1()) { case 't': //standard frame @@ -551,6 +570,7 @@ void LAWICELSerial::readSerialData() break; case 'b': buildFrame.setBitrateSwitch(true); //BRS enabled + [[fallthrough]]; case 'd': //standard fd frame, BRS disabled //tIIILDD buildFrame.setFlexibleDataRateFormat(true); @@ -581,6 +601,7 @@ void LAWICELSerial::readSerialData() break; case 'B': buildFrame.setBitrateSwitch(true); //BRS enabled + [[fallthrough]]; case 'D': //extended fd frame //TIIIIIIIILDD. buildFrame.setFlexibleDataRateFormat(true); diff --git a/connections/lawicel_serial.h b/connections/lawicel_serial.h index 3d32a88..cfee96d 100644 --- a/connections/lawicel_serial.h +++ b/connections/lawicel_serial.h @@ -61,6 +61,7 @@ protected: QSerialPort *serial; int framesRapid; CANFrame buildFrame; + qint64 buildTimestamp; bool can0Enabled; bool can0ListenOnly; bool canFd; diff --git a/connections/mqtt_bus.cpp b/connections/mqtt_bus.cpp index 4220e34..a2e5b66 100644 --- a/connections/mqtt_bus.cpp +++ b/connections/mqtt_bus.cpp @@ -302,7 +302,7 @@ void MQTT_BUS::clientMessageReceived(const QMQTT::Message& message) frame_p->isReceived = true; if (useSystemTime) { - frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, QDateTime::currentMSecsSinceEpoch() * 1000ul)); + frame_p->setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(QDateTime::currentMSecsSinceEpoch() * 1000ul)); } else frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, timeStamp)); diff --git a/connections/serialbusconnection.cpp b/connections/serialbusconnection.cpp index a5ec860..14ec9b3 100644 --- a/connections/serialbusconnection.cpp +++ b/connections/serialbusconnection.cpp @@ -11,8 +11,8 @@ /**** class definition ****/ /***********************************/ -SerialBusConnection::SerialBusConnection(QString portName, QString driverName) : - CANConnection(portName, driverName, CANCon::SERIALBUS,0 ,0, false, 0 ,1, 4000, true), +SerialBusConnection::SerialBusConnection(QString portName, QString driverName, int pBusSpeed, int pDataRate, bool pCanFd) : + CANConnection(portName, driverName, CANCon::SERIALBUS,0 ,pBusSpeed, pCanFd, pDataRate ,1, 4000, true), mTimer(this) /*NB: set connection as parent of timer to manage it from working thread */ { } @@ -94,7 +94,7 @@ void SerialBusConnection::piSetBusSettings(int pBusIdx, CANBus bus) setBusConfig(0, bus); /* if bus is not active we are done */ - if(!bus.active) + if(!bus.isActive()) return; /* set configuration */ @@ -105,10 +105,10 @@ void SerialBusConnection::piSetBusSettings(int pBusIdx, CANBus bus) //You cannot set the speed of a socketcan interface, it has to be set with console commands. //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); - mDev_p->setConfigurationParameter(QCanBusDevice::CanFdKey, bus.canFD); + mDev_p->setConfigurationParameter(QCanBusDevice::BitRateKey, bus.getSpeed()); + mDev_p->setConfigurationParameter(QCanBusDevice::CanFdKey, bus.isCanFD()); - if(bus.listenOnly) + if(bus.isListenOnly()) sbusconfig |= EN_SILENT_MODE; mDev_p->setConfigurationParameter(QCanBusDevice::UserKey, sbusconfig); @@ -188,6 +188,7 @@ void SerialBusConnection::framesReceived() /* check frame */ //if (recFrame.payload().length() <= 8) { + if (true) { CANFrame* frame_p = getQueue().get(); if(frame_p) { frame_p->setPayload(recFrame.payload()); @@ -206,11 +207,11 @@ void SerialBusConnection::framesReceived() frame_p->setTimeStamp(recFrame.timeStamp()); frame_p->setFrameType(recFrame.frameType()); frame_p->setError(recFrame.error()); - /* If recorded frame has a local echo, it is a Tx message, and thus should not be marked as Rx */ + /* If recorded frame has a local echo, it is a Tx message, and thus should not be marked as Rx */ frame_p->isReceived = !recFrame.hasLocalEcho(); if (useSystemTime) { - frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, QDateTime::currentMSecsSinceEpoch() * 1000ul)); + frame_p->setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(QDateTime::currentMSecsSinceEpoch() * 1000ul)); } else frame_p->setTimeStamp(QCanBusFrame::TimeStamp(0, (recFrame.timeStamp().seconds() * 1000000ul + recFrame.timeStamp().microSeconds()) - timeBasis)); @@ -218,11 +219,9 @@ void SerialBusConnection::framesReceived() /* enqueue frame */ getQueue().queue(); - //} -#if 0 + } else qDebug() << "can't get a frame, ERROR"; -#endif } } } diff --git a/connections/serialbusconnection.h b/connections/serialbusconnection.h index f65be43..10c1331 100644 --- a/connections/serialbusconnection.h +++ b/connections/serialbusconnection.h @@ -27,7 +27,8 @@ class SerialBusConnection : public CANConnection Q_OBJECT public: - SerialBusConnection(QString portName, QString driverName); + SerialBusConnection(QString portName, QString driverName, int pBusSpeed, + int pDataRate, bool pCanFd); virtual ~SerialBusConnection(); protected: diff --git a/connections/socketcand.cpp b/connections/socketcand.cpp index 83fb144..6ef8975 100644 --- a/connections/socketcand.cpp +++ b/connections/socketcand.cpp @@ -219,8 +219,8 @@ void SocketCANd::connectDevice() void SocketCANd::deviceConnected(int busNum) { sendDebug("Opening CAN on Kayak Device!"); - const char* openCanCmd = ("< open " + hostCanIDs[busNum] + " >").toStdString().c_str(); - sendStringToTCP(openCanCmd, busNum); + QString openCanCmd("< open " % hostCanIDs[busNum] % " >"); + sendStringToTCP(openCanCmd.toUtf8().data(), busNum); QCoreApplication::processEvents(); } @@ -383,8 +383,6 @@ void SocketCANd::procRXData(QString data, int busNum) mTimer.stop(); mTimer.start(); } - QByteArray output; - switch (rx_state.at(busNum)) { case IDLE: diff --git a/dbc/dbc_classes.cpp b/dbc/dbc_classes.cpp index 04fec40..c177e0b 100644 --- a/dbc/dbc_classes.cpp +++ b/dbc/dbc_classes.cpp @@ -166,12 +166,16 @@ QString DBC_SIGNAL::processSignalTree(const CANFrame &frame) if (sig->processAsText(frame, sigString)) { qDebug() << "Returned value: " << sigString; + if (!build.isEmpty() && !sigString.isEmpty()) + build.append("\n"); build.append(sigString); - build.append("\n"); if (sig->isMultiplexor) { qDebug() << "Spelunkin!"; - build.append(sig->processSignalTree(frame)); + auto subTreeString = sig->processSignalTree(frame); + if (!build.isEmpty() && !subTreeString.isEmpty()) + build.append("\n"); + build.append(subTreeString); } } } @@ -300,12 +304,12 @@ QString DBC_SIGNAL::makePrettyOutput(double floatVal, int64_t intVal, bool outpu } } if (!foundVal) outputString += QString::number(intVal); - if (outputUnit) outputString += unitName; + if (outputUnit) outputString += " " + unitName; } else //otherwise display the actual number and unit (if it exists) { outputString += (isInteger ? QString::number(intVal) : QString::number(floatVal)); - if (outputUnit) outputString += unitName; + if (outputUnit) outputString += " " + unitName; } return outputString; } diff --git a/dbc/dbchandler.cpp b/dbc/dbchandler.cpp index d2a7d57..b32e2f6 100644 --- a/dbc/dbchandler.cpp +++ b/dbc/dbchandler.cpp @@ -674,6 +674,44 @@ bool DBCFile::parseSignalMultiplexValueLine(QString line) return false; } +bool DBCFile::parseSignalValueTypeLine(QString line) +{ + QRegularExpression regex; + QRegularExpressionMatch match; + qDebug() << "Found a signal valtype line"; + regex.setPattern("^SIG\\_VALTYPE\\_ *(\\d+) *([-\\w]+) *: *(\\d+);"); + match = regex.match(line); + + // captured 1 is the message id + // captured 2 is the signal name + // captured 3 is the valtype + if (!match.hasMatch()) { return false; } + uint32_t id = match.captured(1).toULong() & 0x1FFFFFFFUL; + + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toULong() & 0x1FFFFFFFUL); + if (msg == nullptr) { return false; } + + DBC_SIGNAL *thisSignal = msg->sigHandler->findSignalByName(match.captured(2)); + if (thisSignal == nullptr) { return false; } + int valType = match.captured(3).toInt(); + + switch (valType) { + case 1: { + thisSignal->valType = SP_FLOAT; + break; + } + case 2: { + thisSignal->valType = DP_FLOAT; + break; + } + default: { + return false; + } + } + return true; +} + + bool DBCFile::parseValueLine(QString line) { QRegularExpression regex; @@ -737,7 +775,7 @@ bool DBCFile::parseAttributeLine(QString line) if (foundAttr) { qDebug() << "That message attribute does exist"; - DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toUInt() & 0x1FFFFFFFul); + DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toULong() & 0x1FFFFFFFul); if (foundMsg) { qDebug() << "It references a valid, registered message"; @@ -769,7 +807,7 @@ bool DBCFile::parseAttributeLine(QString line) if (foundAttr) { qDebug() << "That signal attribute does exist"; - DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toUInt() & 0x1FFFFFFFUL); + DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toULong() & 0x1FFFFFFFUL); if (foundMsg) { qDebug() << "It references a valid, registered message"; @@ -941,6 +979,11 @@ bool DBCFile::loadFile(QString fileName) if (!parseSignalMultiplexValueLine(line)) numSigFaults++; } + if (line.startsWith("SIG_VALTYPE_ ")) //defines a signal value type + { + if (!parseSignalValueTypeLine(line)) numSigFaults++; + } + if (line.startsWith("BU_:")) //line specifies the nodes on this canbus { qDebug() << "Found a BU line"; @@ -976,7 +1019,7 @@ bool DBCFile::loadFile(QString fileName) if (match.hasMatch()) { //qDebug() << "Comment was: " << match.captured(3); - DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toUInt()); + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toULong() & 0x1FFFFFFFul); if (msg != nullptr) { DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2)); @@ -997,7 +1040,7 @@ bool DBCFile::loadFile(QString fileName) if (match.hasMatch()) { //qDebug() << "Comment was: " << match.captured(2); - DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toUInt()); + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toULong() & 0x1FFFFFFFul); if (msg != nullptr) { msg->comment = match.captured(2); diff --git a/dbc/dbchandler.h b/dbc/dbchandler.h index 3327c18..14f19f8 100644 --- a/dbc/dbchandler.h +++ b/dbc/dbchandler.h @@ -106,6 +106,7 @@ private: bool parseSignalMultiplexValueLine(QString line); DBC_MESSAGE* parseMessageLine(QString line); bool parseValueLine(QString line); + bool parseSignalValueTypeLine(QString line); bool parseAttributeLine(QString line); bool parseDefaultAttrLine(QString line); }; diff --git a/firmwareuploaderwindow.cpp b/firmwareuploaderwindow.cpp index 05028be..bc234e2 100644 --- a/firmwareuploaderwindow.cpp +++ b/firmwareuploaderwindow.cpp @@ -133,38 +133,38 @@ void FirmwareUploaderWindow::timerElapsed() void FirmwareUploaderWindow::sendFirmwareChunk() { - CANFrame *output = new CANFrame; + CANFrame output; int firmwareLocation = currentSendingPosition * 4; int xorByte = 0; - output->setExtendedFrameFormat(false); + output.setExtendedFrameFormat(false); QByteArray bytes(7,0); - output->bus = bus; - output->setFrameId(baseAddress + 0x16); - output->payload()[0] = currentSendingPosition & 0xFF; - output->payload()[1] = (currentSendingPosition >> 8) & 0xFF; - output->payload()[2] = firmwareData[firmwareLocation++]; - output->payload()[3] = firmwareData[firmwareLocation++]; - output->payload()[4] = firmwareData[firmwareLocation++]; - output->payload()[5] = firmwareData[firmwareLocation++]; - for (int i = 0; i < 6; i++) xorByte = xorByte ^ static_cast(output->payload()[i]); - output->payload()[6] = xorByte; - output->setPayload(bytes); - CANConManager::getInstance()->sendFrame(*output); + output.bus = bus; + output.setFrameId(baseAddress + 0x16); + bytes[0] = currentSendingPosition & 0xFF; + bytes[1] = (currentSendingPosition >> 8) & 0xFF; + bytes[2] = firmwareData[firmwareLocation++]; + bytes[3] = firmwareData[firmwareLocation++]; + bytes[4] = firmwareData[firmwareLocation++]; + bytes[5] = firmwareData[firmwareLocation++]; + for (int i = 0; i < 6; i++) xorByte ^= static_cast(bytes[i]); + bytes[6] = xorByte; + output.setPayload(bytes); + CANConManager::getInstance()->sendFrame(output); timer->start(); } void FirmwareUploaderWindow::sendFirmwareEnding() { - CANFrame *output = new CANFrame; - output->setExtendedFrameFormat(false); - output->bus = bus; + CANFrame output; + output.setExtendedFrameFormat(false); + output.bus = bus; QByteArray bytes(4,0); - output->setFrameId(baseAddress + 0x30); - output->payload()[3] = 0xC0; - output->payload()[2] = 0xDE; - output->payload()[1] = 0xFA; - output->payload()[0] = 0xDE; - output->setPayload(bytes); + output.setFrameId(baseAddress + 0x30); + bytes[3] = (char)0xC0; + bytes[2] = (char)0xDE; + bytes[1] = (char)0xFA; + bytes[0] = (char)0xDE; + output.setPayload(bytes); //sendCANFrame(output, bus); } @@ -182,23 +182,23 @@ void FirmwareUploaderWindow::handleStartStopTransfer() qDebug() << "Base address: " + QString::number(baseAddress); CANConManager::getInstance()->addTargettedFrame(bus, baseAddress + 0x10, 0x7FF, this); CANConManager::getInstance()->addTargettedFrame(bus, baseAddress + 0x20, 0x7FF, this); - CANFrame *output = new CANFrame; - output->setExtendedFrameFormat(false); + CANFrame output; + output.setExtendedFrameFormat(false); QByteArray bytes(8,0); - output->bus = bus; - output->setFrameId(baseAddress); - output->setFrameType(QCanBusFrame::DataFrame); + output.bus = bus; + output.setFrameId(baseAddress); + output.setFrameType(QCanBusFrame::DataFrame); - bytes[0] = 0xEF; - bytes[1] = 0xBE; - bytes[2] = 0xAD; - bytes[3] = 0xDE; + bytes[0] = (char)0xEF; + bytes[1] = (char)0xBE; + bytes[2] = (char)0xAD; + bytes[3] = (char)0xDE; bytes[4] = token & 0xFF; bytes[5] = (token >> 8) & 0xFF; bytes[6] = (token >> 16) & 0xFF; bytes[7] = (token >> 24) & 0xFF; - output->setPayload(bytes); - CANConManager::getInstance()->sendFrame(*output); + output.setPayload(bytes); + CANConManager::getInstance()->sendFrame(output); } else //stop anything in process { @@ -209,7 +209,6 @@ void FirmwareUploaderWindow::handleStartStopTransfer() void FirmwareUploaderWindow::handleLoadFile() { - QString filename; QFileDialog dialog; QStringList filters; @@ -221,7 +220,7 @@ void FirmwareUploaderWindow::handleLoadFile() if (dialog.exec() == QDialog::Accepted) { - filename = dialog.selectedFiles()[0]; + QString filename = dialog.selectedFiles().constFirst(); loadBinaryFile(filename); } @@ -232,22 +231,20 @@ void FirmwareUploaderWindow::loadBinaryFile(QString filename) if (transferInProgress) handleStartStopTransfer(); - QFile *inFile = new QFile(filename); + QFile inFile(filename); - if (!inFile->open(QIODevice::ReadOnly)) + if (!inFile.open(QIODevice::ReadOnly)) { - delete inFile; return; } - firmwareData = inFile->readAll(); + firmwareData = inFile.readAll(); currentSendingPosition = 0; firmwareSize = firmwareData.length(); updateProgress(); - inFile->close(); - delete inFile; + inFile.close(); } diff --git a/framefileio.cpp b/framefileio.cpp index 5ea46ca..24090d8 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -191,6 +191,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache filters.append(QString(tr("CLX000 (*.txt *.TXT)"))); filters.append(QString(tr("CANServer Binary Log (*.log *.LOG)"))); filters.append(QString(tr("Wireshark (*.pcap *.PCAP *.pcapng *.PCAPNG)"))); + filters.append(QString(tr("Wireshark SocketCAN (*.pcap *.PCAP"))); dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString()); dialog.setFileMode(QFileDialog::ExistingFile); @@ -237,6 +238,7 @@ bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache if (selectedNameFilter == filters[22]) result = loadCLX000File(filename, frameCache); if (selectedNameFilter == filters[23]) result = loadCANServerFile(filename, frameCache); if (selectedNameFilter == filters[24]) result = loadWiresharkFile(filename, frameCache); + if (selectedNameFilter == filters[25]) result = loadWiresharkSocketCANFile(filename, frameCache); progress.cancel(); @@ -288,6 +290,28 @@ bool FrameFileIO::autoDetectLoadFile(QString filename, QVector* frames } } + // Attempt to load socket CAN first to avoid generic wireshark logic catching it + qDebug() << "Attempting Wireshark Socket CAN Log"; + if (isWiresharkSocketCANFile(filename)) + { + if (loadWiresharkSocketCANFile(filename, frames)) + { + qDebug() << "Loaded as Wireshark SocketCAN Log successfully!"; + return true; + } + } + + // This and the decoder above were both moved above TeslaAPFile as they match based on magic numbers and sometimes these files were falling into the TeslaAP decoder + qDebug() << "Attempting Wireshark Log"; + if (isWiresharkFile(filename)) + { + if (loadWiresharkFile(filename, frames)) + { + qDebug() << "Loaded as Wireshark Log successfully!"; + return true; + } + } + qDebug() << "Attempting Tesla AP Snapshot"; if (isTeslaAPFile(filename)) { @@ -308,16 +332,6 @@ bool FrameFileIO::autoDetectLoadFile(QString filename, QVector* frames } } - qDebug() << "Attempting Wireshark Log"; - if (isWiresharkFile(filename)) - { - if (loadWiresharkFile(filename, frames)) - { - qDebug() << "Loaded as Wireshark Log successfully!"; - return true; - } - } - qDebug() << "Attempting canalyzer ASC"; if (isCanalyzerASC(filename)) { @@ -1440,7 +1454,8 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) QByteArray bytes(numBytes, 0); thisFrame.isReceived = true; thisFrame.bus = 0; - if (thisFrame.frameId() > 0x10000000) + if ((thisFrame.frameId() > 0x10000000) || + (tokens[3].length() >= 8)) { thisFrame.setExtendedFrameFormat(true); } @@ -1492,7 +1507,8 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) //qDebug() << thisFrame.payload().length(); thisFrame.isReceived = true; thisFrame.bus = tokens[2].toInt(); - if (thisFrame.frameId() > 0x10000000) + if ((thisFrame.frameId() > 0x10000000) || + (tokens[4].length() >= 8)) { thisFrame.setExtendedFrameFormat(true); } @@ -1534,7 +1550,8 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) //qDebug() << thisFrame.payload().length(); thisFrame.isReceived = true; thisFrame.bus = 0; - if (thisFrame.frameId() > 0x10000000) + if ((thisFrame.frameId() > 0x10000000) || + (tokens[3].length() >= 8)) { thisFrame.setExtendedFrameFormat(true); } @@ -1586,7 +1603,8 @@ bool FrameFileIO::loadPCANFile(QString filename, QVector* frames) //qDebug() << thisFrame.payload().length(); thisFrame.isReceived = true; thisFrame.bus = tokens[3].toInt(); - if (thisFrame.frameId() > 0x10000000) + if ((thisFrame.frameId() > 0x10000000) || + (tokens[4].length() >= 8)) { thisFrame.setExtendedFrameFormat(true); } @@ -1680,12 +1698,16 @@ bool FrameFileIO::isCanalyzerASC(QString filename) //0.001371 CANFD 1 Rx 171 A0066_Unknown 0 0 8 8 00 00 07 76 23 00 00 00 230199 119 220000 62e7 46500250 460a0250 20011736 20010205 //Version 16.0.0 //0.008600 1 358 Rx d 8 04 03 50 01 F0 40 54 2C Length = 233910 BitCount = 121 ID = 856 -//Time bus id dir ? len databytes (Ver 8.0) -//Time type bus dir ID ? ? length length bytes then many values of unknown type (ver 8.1) -//Time type bus dir ID SignalName ? ? length length bytes then many values of unknown type (ver 9.0) -//Time bus id dir ? len databytes additional crap (ver 16.0) -//So it seems the file format is not entirely different based on version but rather on some other -//settings... Fun! +//Version 17.3.0 +//0.001684 CANFD 1 Rx 65b 1 0 d 32 f1 01 00 00 50 00 00 00 00 00 00 00 00 00 00 40 00 88 f5 77 17 78 00 00 c0 03 00 00 00 00 00 00 213797 356 303000 11729 46500250 4b140250 20011736 2001040d + +//Time bus id dir ? len databytes (Ver 8.0) +//Time bus id dir ? len databytes (addl info) (ver 16.0) +//Time type bus dir ID ? ? length length (bytes) (many values of unknown type) (ver 8.1) +//Time type bus dir ID SignalName ? ? length length bytes (then many values of unknown type)(ver 9.0) +//Time Type Bus Dir ID ? ? (length) (Real Length) (bytes) (many values of unknown type) (Ver 17.3) +//0 1 2 3 4 5 6 7 8 9 10 +//This seems like a rather eclectic mix. It's almost arbitrary! bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) { QFile *inFile = new QFile(filename); @@ -1736,7 +1758,7 @@ bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) } } if (inHeader) continue; - if (line.length() > 2) + if (line.length() > 2 && !line.startsWith("//")) { tokens = line.simplified().split(' '); @@ -1766,7 +1788,7 @@ bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) int payloadLen = tokens[8].toInt(); qDebug() << "PayloadLen: " << payloadLen << " Tokens: " << tokens; QByteArray bytes(payloadLen, 0); - if (payloadLen > 8) + if (payloadLen > 64) { qDebug() << "Payload length too long. Original line: " << line; return false; @@ -3766,12 +3788,17 @@ bool FrameFileIO::loadCanDumpFile(QString filename, QVector* frames) if (line.contains('[')) //the expanded format (second one from the above list) { //(1551774790.942758) can1 7A8 [8] F4 DC D1 83 0E 02 00 00 + //(1551774790.942758) can1 7A8 [08] F4 DC D1 83 0E 02 00 00 // 0 1 2 3 4 5 6 7 8 9 10 11 thisFrame.setFrameId(tokens[2].toLong(nullptr, 16)); if (thisFrame.frameId() > 0x7FF) thisFrame.setExtendedFrameFormat(true); else thisFrame.setExtendedFrameFormat(false); thisFrame.setFrameType(QCanBusFrame::DataFrame); - int numBytes = tokens[3].at(1) - '0'; + int numBytes; + if (tokens[3].at(2) == ']') + numBytes = tokens[3].at(1) - '0'; + else + numBytes = (tokens[3].at(1) - '0')*10 + (tokens[3].at(2) - '0'); QByteArray bytes(numBytes, 0); for (int c = 0; c < numBytes; c++) { @@ -5019,7 +5046,7 @@ bool FrameFileIO::loadWiresharkFile(QString filename, QVector* frames) QByteArray ba = filename.toLocal8Bit(); - pcap_data_file = pcap_open_offline(ba.data(), errbuf); + pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_ANY); if (!pcap_data_file) { return false; } @@ -5046,9 +5073,13 @@ bool FrameFileIO::loadWiresharkFile(QString filename, QVector* frames) thisFrame.isReceived = true; // TODO: check if tx detection is possible thisFrame.setFrameType(QCanBusFrame::DataFrame); - thisFrame.setFrameId((0xff & *(packetData+17)) << 8 | (0xff & *(packetData+16))); - if (thisFrame.frameId() <= 0x7FF) thisFrame.setExtendedFrameFormat(false); - else thisFrame.setExtendedFrameFormat(true); + if ((0x80 & *(packetData+19))) { + thisFrame.setExtendedFrameFormat(true); + thisFrame.setFrameId((0x3f & *(packetData+19))<<24 | (0xff & *(packetData+18)) << 16 | (0xff & *(packetData+17)) << 8 | (0xff & *(packetData+16))); + } else { + thisFrame.setExtendedFrameFormat(false); + thisFrame.setFrameId((0xff & *(packetData+17)) << 8 | (0xff & *(packetData+16))); + } thisFrame.bus = 0; int numBytes = *(packetData+20); QByteArray bytes(numBytes, 0); @@ -5075,7 +5106,7 @@ bool FrameFileIO::isWiresharkFile(QString filename) char errbuf[PCAP_ERRBUF_SIZE]; QByteArray ba = filename.toLocal8Bit(); - pcap_data_file = pcap_open_offline(ba.data(), errbuf); + pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_ANY); if (!pcap_data_file) { return false; } @@ -5085,3 +5116,96 @@ bool FrameFileIO::isWiresharkFile(QString filename) return true; } + +bool FrameFileIO::loadWiresharkSocketCANFile(QString filename, QVector* frames) +{ + pcap_t *pcap_data_file; + CANFrame thisFrame; + long long startTimestamp = 0; + long long timeStamp; + int lineCounter = 0; + bool foundErrors = false; + pcap_pkthdr packetHeader; + const char *packetData = NULL; + char errbuf[PCAP_ERRBUF_SIZE]; + + QByteArray ba = filename.toLocal8Bit(); + + pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_SOCKETCAN); + if (!pcap_data_file) { + return false; + } + + packetData = (const char*)pcap_next(pcap_data_file, &packetHeader); + while (packetData) { + lineCounter++; + if (lineCounter > 100) { + qApp->processEvents(); + lineCounter = 0; + } + thisFrame.bus = 0; + + // Timestamp + timeStamp = packetHeader.ts.tv_sec * 1000000 + packetHeader.ts.tv_usec; + if (0 == startTimestamp) { + startTimestamp = timeStamp; + } + timeStamp -= startTimestamp; + thisFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, timeStamp)); + + // ID and extended frame format + const quint32 can_id = qFromBigEndian(packetData); + if (can_id & 0x80000000) { + thisFrame.setExtendedFrameFormat(true); + thisFrame.setFrameId(0x1fffffff & can_id); + } else { + thisFrame.setExtendedFrameFormat(false); + thisFrame.setFrameId(0x7ff & can_id); + } + + // Frame type + if (can_id & 0x20000000U) { + thisFrame.setFrameType(QCanBusFrame::ErrorFrame); + } else if (can_id & 0x40000000U) { + thisFrame.setFrameType(QCanBusFrame::RemoteRequestFrame); + } else { + thisFrame.setFrameType(QCanBusFrame::DataFrame); + } + + // Direction - This isn't actually officially supported, but CAN Bus Debugger device logs set this byte to 1 to indicate a TX frame and 0 for RX + quint8 direction = (quint8) *(packetData + 6); + thisFrame.isReceived = (direction != 1); + + // Data + quint8 numBytes = (quint8) *(packetData + 4); + if (numBytes > 8) { + numBytes = 8; + } + QByteArray bytes(numBytes, 0); + for (int d = 0; d < numBytes; d++) { + bytes[d] = *(packetData + 8 + d); + } + thisFrame.setPayload(bytes); + frames->append(thisFrame); + + packetData = (const char*) pcap_next(pcap_data_file, &packetHeader); + } + pcap_close(pcap_data_file); + pcap_data_file = NULL; + return !foundErrors; +} + +bool FrameFileIO::isWiresharkSocketCANFile(QString filename) +{ + pcap_t *pcap_data_file; + char errbuf[PCAP_ERRBUF_SIZE]; + QByteArray ba = filename.toLocal8Bit(); + + pcap_data_file = pcap_open_offline(ba.data(), errbuf, PCAP_LINKTYPE_SOCKETCAN); + if (!pcap_data_file) { + return false; + } + pcap_close(pcap_data_file); + pcap_data_file = NULL; + return true; +} diff --git a/framefileio.h b/framefileio.h index 3d52517..7171591 100644 --- a/framefileio.h +++ b/framefileio.h @@ -54,6 +54,7 @@ public: static bool loadCLX000File(QString filename, QVector* frames); static bool loadCANServerFile(QString filename, QVector* frames); static bool loadWiresharkFile(QString filename, QVector* frames); + static bool loadWiresharkSocketCANFile(QString filename, QVector* frames); //functions that pre-scan a file to try to figure out if they could read it. Used to automatically determine //file type and load it. @@ -80,6 +81,7 @@ public: static bool isCLX000File(QString filename); static bool isCANServerFile(QString filename); static bool isWiresharkFile(QString filename); + static bool isWiresharkSocketCANFile(QString filename); static bool saveCRTDFile(QString, const QVector*); static bool saveNativeCSVFile(QString, const QVector*); diff --git a/framesenderobject.cpp b/framesenderobject.cpp new file mode 100644 index 0000000..f9eb468 --- /dev/null +++ b/framesenderobject.cpp @@ -0,0 +1,457 @@ +#include "framesenderobject.h" +#include "mainwindow.h" + +FrameSenderObject::FrameSenderObject(const QVector *frames) +{ + mThread_p = new QThread(); + + statusCounter = 0; + modelFrames = frames; + dbcHandler = DBCHandler::getReference(); +} + +FrameSenderObject::~FrameSenderObject() +{ + mThread_p->quit(); + mThread_p->wait(); + delete mThread_p; +} + +void FrameSenderObject::piStart() +{ + sendingTimer = new QTimer(); + sendingTimer->setTimerType(Qt::PreciseTimer); + sendingTimer->setInterval(1); + + connect(sendingTimer, &QTimer::timeout, this, &FrameSenderObject::timerTriggered); +} + +void FrameSenderObject::piStop() +{ + sendingTimer->stop(); + delete sendingTimer; +} + +void FrameSenderObject::initialize() +{ + if( mThread_p && (mThread_p != QThread::currentThread()) ) + { + /* move ourself to the thread */ + moveToThread(mThread_p); /*TODO handle errors */ + /* connect started() */ + connect(mThread_p, SIGNAL(started()), this, SLOT(initialize())); + /* start the thread */ + mThread_p->start(QThread::HighPriority); + return; + } + + /* set started flag */ + //mStarted = true; + + connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int))); + + /* in multithread case, this will be called before entering thread event loop */ + return piStart(); + +} + +void FrameSenderObject::finalize() +{ + /* 1) execute in mThread_p context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) + { + /* if thread is finished, it means we call this function for the second time so we can leave */ + if( !mThread_p->isFinished() ) + { + /* we need to call piStop() */ + QMetaObject::invokeMethod(this, "finalize", + Qt::BlockingQueuedConnection); + /* 3) stop thread */ + mThread_p->quit(); + if(!mThread_p->wait()) { + qDebug() << "can't stop thread"; + } + } + return; + } + + /* 2) call piStop in mThread context */ + return piStop(); +} + +void FrameSenderObject::startSending() +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "startSending", + Qt::BlockingQueuedConnection); + return; + } + + sendingElapsed.start(); + sendingTimer->start(); +} + +void FrameSenderObject::stopSending() +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "stopPlayback", + Qt::BlockingQueuedConnection); + return; + } + + sendingTimer->stop(); //pushing this button halts automatic playback + //emit statusUpdate(currentPosition); +} + +void FrameSenderObject::addSendRecord(FrameSendData record) +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "addSendRecord", + Qt::BlockingQueuedConnection, + Q_ARG(FrameSendData, record)); + return; + } + sendingData.append(record); +} + +void FrameSenderObject::removeSendRecord(int idx) +{ + /* make sure we execute in mThread context */ + if( mThread_p && (mThread_p != QThread::currentThread()) ) { + QMetaObject::invokeMethod(this, "removeSendRecord", + Qt::BlockingQueuedConnection, + Q_ARG(int, idx)); + return; + } + sendingData.removeAt(idx); +} + +/* + * Note: Getting a reference to an item in a QList is a dangerous thing. If anyone + * or anything happens to reallocate the list you're screwed. So, call this, do the + * work, and drop the reference as soon as possible. +*/ +FrameSendData *FrameSenderObject::getSendRecordRef(int idx) +{ + if (idx < 0 || idx >= sendingData.count()) return nullptr; + return &sendingData[idx]; +} + +/// +/// Called every millisecond to set the system update figures and send frames if necessary. +/// +/// +/// +/// +void FrameSenderObject::timerTriggered() +{ + FrameSendData *sendData; + Trigger *trigger; + + sendingList.clear(); + if(mutex.tryLock()) + { + /* + * Requested tick interval was 1ms but the actual interval could be wildly different. So, we track + * by counting microseconds and accumulating. This creates decent stability even for long intervals + */ + quint64 elapsed = sendingElapsed.nsecsElapsed() / 1000ul; + if (elapsed == 0) elapsed = 1; + sendingElapsed.start(); + sendingLastTimeStamp += elapsed; + //qDebug() << sendingLastTimeStamp; + //qDebug() << "El: " << elapsed; + statusCounter++; + for (int i = 0; i < sendingData.count(); i++) + { + sendData = &sendingData[i]; + if (!sendData->enabled) + { + if (sendData->triggers.count() > 0) + { + for (int j = 0; j < sendData->triggers.count(); j++) //resetting currCount when line is disabled + { + sendData->triggers[j].currCount = 0; + } + } + continue; //abort any processing on this if it is not enabled. + } + if (sendData->triggers.count() == 0) + { + //qDebug() << "No triggers to process"; + break; + } + for (int j = 0; j < sendData->triggers.count(); j++) + { + trigger = &sendData->triggers[j]; + //if ( (trigger->currCount >= trigger->maxCount) || (trigger->maxCount == -1) ) continue; //don't process if we've sent max frames we were supposed to + if (!trigger->readyCount) continue; //don't tick if not ready to tick + //is it time to fire? + trigger->msCounter += elapsed; //gives proper tracking even if timer doesn't fire as fast as it should + //qDebug() << trigger->msCounter; + if (trigger->msCounter >= (trigger->milliseconds * 1000)) + { + trigger->msCounter -= (trigger->milliseconds * 1000); + sendData->count++; + trigger->currCount++; + doModifiers(i); + //updateGridRow(i); + //qDebug() << "About to try to send a frame"; + //CANConManager::getInstance()->sendFrame(sendingData[i]); + sendingList.append(sendingData[i]); //queue it instead of immediate sending + if (trigger->ID > 0) trigger->readyCount = false; //reset flag if this is a timed ID trigger + } + } + } + + //if we have any frames to send after the above then send as a batch + if (sendingList.count() > 0) CANConManager::getInstance()->sendFrames(sendingList); + mutex.unlock(); + } + else + { + qDebug() << "framesenderobject::handleTick() couldn't get mutex, elapsed is: " << sendingElapsed.elapsed(); + } +} + +void FrameSenderObject::buildFrameCache() +{ + CANFrame thisFrame; + frameCache.clear(); + for (int i = 0; i < modelFrames->count(); i++) + { + thisFrame = modelFrames->at(i); + if (!frameCache.contains(thisFrame.frameId())) + { + frameCache.insert(thisFrame.frameId(), thisFrame); + } + else + { + frameCache[thisFrame.frameId()] = thisFrame; + } + } +} + +//remember, negative numbers are special -1 = all frames deleted, -2 = totally new set of frames. +void FrameSenderObject::updatedFrames(int numFrames) +{ + CANFrame thisFrame; + if (numFrames == -1) //all frames deleted. + { + } + else if (numFrames == -2) //all new set of frames. + { + buildFrameCache(); + } + else //just got some new frames. See if they are relevant. + { + if (numFrames > modelFrames->count()) return; + qDebug() << "New frames in sender window"; + //run through the supposedly new frames in order + for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + { + thisFrame = modelFrames->at(i); + if (!frameCache.contains(thisFrame.frameId())) + { + frameCache.insert(thisFrame.frameId(), thisFrame); + } + else + { + frameCache[thisFrame.frameId()] = thisFrame; + } + processIncomingFrame(&thisFrame); + } + } +} + +void FrameSenderObject::processIncomingFrame(CANFrame *frame) +{ + for (int sd = 0; sd < sendingData.count(); sd++) + { + if (sendingData[sd].triggers.count() == 0) continue; + bool passedChecks = true; + for (int trig = 0; trig < sendingData[sd].triggers.count(); trig++) + { + Trigger *thisTrigger = &sendingData[sd].triggers[trig]; + //need to ensure that this trigger is actually related to frames incoming. + //Otherwise ignore it here. Only frames that have BUS and/or ID set as trigger will work here. + if (!(thisTrigger->triggerMask & (TriggerMask::TRG_BUS | TriggerMask::TRG_ID) ) ) continue; + + passedChecks = true; + //qDebug() << "Trigger ID: " << thisTrigger->ID; + //qDebug() << "Frame ID: " << frame->frameId(); + + //Check to see if we have a bus trigger condition and if so does it match + if (thisTrigger->bus != frame->bus && (thisTrigger->triggerMask & TriggerMask::TRG_BUS) ) + passedChecks = false; + + //check to see if we have an ID trigger condition and if so does it match + if ((thisTrigger->triggerMask & TriggerMask::TRG_ID) && (uint32_t)thisTrigger->ID != frame->frameId() ) + passedChecks = false; + + //check to see if we're limiting the trigger by max count and have we reached that count? + if ( (thisTrigger->triggerMask & TriggerMask::TRG_COUNT) && (thisTrigger->currCount >= thisTrigger->maxCount) ) + passedChecks = false; + + //if the above passed then are we triggering not only on ID but also signal? + if (passedChecks && (thisTrigger->triggerMask & (TriggerMask::TRG_SIGNAL | TriggerMask::TRG_ID) ) ) + { + bool sigCheckPassed = false; + DBC_MESSAGE *msg = dbcHandler->findMessage(thisTrigger->ID); + if (msg) + { + DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(thisTrigger->sigName); + if (sig) + { + //first of all, is this signal really in this message we got? + if (sig->isSignalInMessage(*frame)) sigCheckPassed = true; + //if it was and we're also filtering on value then try that next + if (sigCheckPassed && (thisTrigger->triggerMask & TriggerMask::TRG_SIGVAL)) + { + double sigval = 0.0; + if (sig->processAsDouble(*frame, sigval)) + { + if (abs(sigval - thisTrigger->sigValueDbl) > 0.001) + { + sigCheckPassed = false; + } + } + else sigCheckPassed = false; + } + } + else sigCheckPassed = false; + } + else sigCheckPassed = false; + passedChecks &= sigCheckPassed; //passedChecks can only be true if both are + } + + //if all the above says it's OK then we'll go ahead and send that message. + //If a message that comes through here has a MS value then we use it as a delay + //after the check passes. This allows for delaying the sending of the frame if that + //is required. Otherwise, just send it immediately. + if (passedChecks) + { + if (thisTrigger->milliseconds == 0) //immediate reply + { + thisTrigger->currCount++; + sendingData[sd].count++; + doModifiers(sd); + //updateGridRow(sd); + CANConManager::getInstance()->sendFrame(sendingData[sd]); + } + else //delayed sending frame + { + thisTrigger->readyCount = true; + } + } + } + } +} + + +/// +/// given an index into the sendingData list we run the modifiers that it has set up +/// +/// The index into the sendingData list +void FrameSenderObject::doModifiers(int idx) +{ + int shadowReg = 0; //shadow register we use to accumulate results + int first=0, second=0; + + FrameSendData *sendData = &sendingData[idx]; + Modifier *mod; + ModifierOp op; + + if (sendData->modifiers.count() == 0) return; //if no modifiers just leave right now + + //qDebug() << "Executing mods"; + + for (int i = 0; i < sendData->modifiers.count(); i++) + { + mod = &sendData->modifiers[i]; + for (int j = 0; j < mod->operations.count(); j++) + { + op = mod->operations.at(j); + if (op.first.ID == -1) + { + first = shadowReg; + } + else first = fetchOperand(idx, op.first); + second = fetchOperand(idx, op.second); + switch (op.operation) + { + case ADDITION: + shadowReg = first + second; + break; + case AND: + shadowReg = first & second; + break; + case DIVISION: + shadowReg = first / second; + break; + case MULTIPLICATION: + shadowReg = first * second; + break; + case OR: + shadowReg = first | second; + break; + case SUBTRACTION: + shadowReg = first - second; + break; + case XOR: + shadowReg = first ^ second; + break; + case MOD: + shadowReg = first % second; + } + } + //Finally, drop the result into the proper data byte + QByteArray newArr(sendData->payload()); + newArr[mod->destByte] = (char) shadowReg; + sendData->setPayload(newArr); + } +} + +int FrameSenderObject::fetchOperand(int idx, ModifierOperand op) +{ + CANFrame *tempFrame = nullptr; + if (op.ID == 0) //numeric constant + { + if (op.notOper) return ~op.databyte; + else return op.databyte; + } + else if (op.ID == -2) //fetch data from a data byte within the output frame + { + if (op.notOper) return ~((unsigned char)sendingData.at(idx).payload()[op.databyte]); + else return (unsigned char)sendingData.at(idx).payload()[op.databyte]; + } + else //look up external data byte + { + tempFrame = lookupFrame(op.ID, op.bus); + if (tempFrame != nullptr) + { + if (op.notOper) return ~((unsigned char)tempFrame->payload()[op.databyte]); + else return (unsigned char)tempFrame->payload()[op.databyte]; + } + else return 0; + } +} + +/// +/// Try to find the most recent frame given the input criteria +/// +/// The ID to find +/// Which bus to look on (-1 if you don't care) +/// +CANFrame* FrameSenderObject::lookupFrame(int ID, int bus) +{ + if (!frameCache.contains(ID)) return nullptr; + + if (bus == -1 || frameCache[ID].bus == bus) return &frameCache[ID]; + + return nullptr; +} + diff --git a/framesenderobject.h b/framesenderobject.h new file mode 100644 index 0000000..47a5800 --- /dev/null +++ b/framesenderobject.h @@ -0,0 +1,81 @@ +#ifndef FRAMESENDEROBJECT_H +#define FRAMESENDEROBJECT_H + +#include +#include +#include +#include +#include +#include +#include "can_structs.h" +#include "connections/canconmanager.h" +#include "can_trigger_structs.h" +#include "dbc/dbchandler.h" + +class FrameSenderObject : public QObject +{ + Q_OBJECT + +public: + FrameSenderObject(const QVector *frames); + ~FrameSenderObject(); + +public slots: + /** + * @brief start the device. This calls piStarted + * @note starts the working thread if required (piStart is called in the working thread context) + */ + void initialize(); + + /** + * @brief stop the device. This calls piStop + * @note if a working thread is used, piStop is called before exiting the working thread + */ + void finalize(); + + void startSending(); + void stopSending(); + + void addSendRecord(FrameSendData record); + void removeSendRecord(int idx); + FrameSendData *getSendRecordRef(int idx); + +signals: + +private slots: + void timerTriggered(); + void updatedFrames(int); + +private: + QList sendingList; + int currentPosition; + QTimer *sendingTimer; + QElapsedTimer sendingElapsed; + quint64 sendingLastTimeStamp; + int statusCounter; + QList sendingData; + QThread* mThread_p; + QHash frameCache; //hash with frame ID as the key and the most recent frame as the value + const QVector *modelFrames; + bool inhibitChanged = false; + QMutex mutex; + DBCHandler *dbcHandler; + + void doModifiers(int); + int fetchOperand(int, ModifierOperand); + CANFrame* lookupFrame(int, int); + void buildFrameCache(); + void processIncomingFrame(CANFrame *frame); + + /** + * @brief starts the device + */ + void piStart(); + + /** + * @brief stops the device + */ + void piStop(); +}; + +#endif // FRAMESENDEROBJECT_H diff --git a/framesenderwindow.cpp b/framesenderwindow.cpp index 73162e5..cd4b32f 100644 --- a/framesenderwindow.cpp +++ b/framesenderwindow.cpp @@ -63,8 +63,8 @@ void FrameSenderWindow::setupGrid() ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_EXT, 50); ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_REM, 50); ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_DATA, 220); - ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_TRIGGER, 220); - ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_MODS, 220); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_TRIGGER, 270); + ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_MODS, 270); ui->tableSender->setColumnWidth(ST_COLS::SENDTAB_COL_COUNT, 80); ui->tableSender->setHorizontalHeaderLabels(headers); } @@ -693,6 +693,12 @@ void FrameSenderWindow::processModifierText(int line) //Example line: //d0 = D0 + 1,d1 = id:0x200:d3 + id:0x200:d4 AND 0xF0 - Original version //D0=D0+1,D1=ID:0x200:D3+ID:0x200:D4&0xF0 + + //[BMS_TargetVoltage]=45 would set the value of signal BMS_TargetVoltage to 45 + + //[BMS_TargetVoltage]=[0x234:BMS_CurrentVoltage] + 4 would instead grab the value + //of BMS_CurrentVoltage from ID 0x234, add 4 to it, and set BMS_TargetVoltage to that value. + //This is certainly much harder to parse than the trigger definitions. //the left side of the = has to be D0 to D7. After that there is a string of //data. Spaces used to be required but no longer are. This makes parsing harder but data entry easier @@ -710,22 +716,33 @@ void FrameSenderWindow::processModifierText(int line) Modifier thisMod; thisMod.destByte = 0; - QString leftSide = Utility::grabAlphaNumeric(mods[i]); - if (leftSide.startsWith("D") && leftSide.length() == 2) + QRegularExpression regex; + QRegularExpressionMatch match; + + regex.setPattern("^\\[(\\w+)]=(\\w+)"); + match = regex.match(mods[i]); + if (match.hasMatch()) { - thisMod.destByte = leftSide.right(1).toInt(); + thisMod.destByte = -1; + thisMod.signalName = match.captured(1); + mods[i] = match.captured(2); thisMod.operations.clear(); } else { - qDebug() << "Something wrong with lefthand val"; - continue; - } - if (!(Utility::grabOperation(mods[i]) == "=")) - { - qDebug() << "Err: No = after lefthand val"; - continue; + QString leftSide = Utility::grabAlphaNumeric(mods[i]); + if (leftSide.startsWith("D") && leftSide.length() == 2) + { + thisMod.destByte = leftSide.right(1).toInt(); + thisMod.operations.clear(); + if (!(Utility::grabOperation(mods[i]) == "=")) + { + qDebug() << "Err: No = after lefthand val"; + continue; + } + } } + abort = false; token = Utility::grabAlphaNumeric(mods[i]); @@ -1054,8 +1071,6 @@ void FrameSenderWindow::processCellChange(int line, int col) } break; case ST_COLS::SENDTAB_COL_DATA: //Data bytes - for (int i = 0; i < 8; i++) sendingData[line].payload().data()[i] = 0; - #if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) tokens = ui->tableSender->item(line, ST_COLS::SENDTAB_COL_DATA)->text().split(" ", Qt::SkipEmptyParts); #else diff --git a/icons/SavvyIcon.ico b/icons/SavvyIcon.ico new file mode 100644 index 0000000..c47683c Binary files /dev/null and b/icons/SavvyIcon.ico differ diff --git a/install-linux.sh b/install-linux.sh new file mode 100755 index 0000000..20b5ff9 --- /dev/null +++ b/install-linux.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +APP_PATH="/usr/bin/SavvyCAN" +DESKTOP_PATH="/usr/share/applications/savvycan.desktop" +ICON_PATH="/usr/share/pixmaps/SavvyCAN.png" + +require_root() { + if [ "$(id -u)" -ne 0 ]; then + echo "This script must be run as root." + exit 1 + fi +} + +show_help() { + echo "Usage: $0 [option]" + echo "" + echo "Options:" + echo " --help Show this help message" + echo " --uninstall Remove SavvyCAN from the system" + echo " (no option) Install or reinstall SavvyCAN" +} + +install_error() { + echo "SavvyCAN installation failed." + exit 1 +} + +uninstall_error() { + echo "SavvyCAN uninstallation failed." + exit 1 +} + +if [ "$1" = "--help" ]; then + show_help + exit 0 +fi + +require_root + +# Uninstall +if [ "$1" = "--uninstall" ]; then + echo "Uninstalling SavvyCAN..." + trap uninstall_error ERR + rm -f "$APP_PATH" "$DESKTOP_PATH" "$ICON_PATH" + trap - ERR + echo "SavvyCAN has been uninstalled." + exit 0 +fi + +# Check required files +if [ ! -f "SavvyCAN" ]; then + echo "Missing file \"SavvyCAN\". You need to build first." + install_error +fi + +if [ ! -f "SavvyCAN.desktop" ]; then + echo "Missing file \"SavvyCAN.desktop\"." + install_error +fi + +if [ ! -f "icons/SavvyIcon.png" ]; then + echo "Missing file \"icons/SavvyIcon.png\"." + install_error +fi + +# Install +if [ -f "$APP_PATH" ]; then + echo "Re-installing SavvyCAN..." +else + echo "Installing SavvyCAN..." +fi + +trap install_error ERR +install -Dm755 SavvyCAN "$APP_PATH" +install -Dm644 SavvyCAN.desktop "$DESKTOP_PATH" +install -Dm644 icons/SavvyIcon.png "$ICON_PATH" +trap - ERR + +echo "SavvyCAN is installed." diff --git a/mainwindow.cpp b/mainwindow.cpp index 8b14149..9e6fea3 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -164,6 +164,8 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->btnExpandAll, &QAbstractButton::clicked, this, &MainWindow::expandAllRows); connect(ui->btnCollapseAll, &QAbstractButton::clicked, this, &MainWindow::collapseAllRows); + connect(ui->tableSimpleSender, SIGNAL(cellChanged(int,int)), this, SLOT(onSenderCellChanged(int,int))); + lbStatusConnected.setText(tr("Connected to 0 buses")); lbHelp.setText(tr("Press F1 on any screen for help")); lbHelp.setAlignment(Qt::AlignCenter); @@ -227,12 +229,34 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionMotorControlConfig->setVisible(false); ui->actionSingle_Multi_State_2->setVisible(false); + QStringList headers; + headers << "En" << "Bus" << "ID" << "Ext" << "Rem" << "Data" + << "Interval" << "Count"; + ui->tableSimpleSender->setColumnCount(8); + ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_EN, 70); + ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_BUS, 70); + ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_ID, 70); + ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_EXT, 70); + ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_REM, 70); + ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_DATA, 300); + ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_INTERVAL, 100); + ui->tableSimpleSender->setColumnWidth(SIMP_COL::SC_COL_COUNT, 100); + ui->tableSimpleSender->setHorizontalHeaderLabels(headers); + + createSenderRow(); + + frameSender = new FrameSenderObject(model->getListReference()); + + frameSender->initialize(); //creates the thread and sets things up + frameSender->startSending(); //start the timer in the object so enabled things can send + installEventFilter(this); } MainWindow::~MainWindow() { updateTimer.stop(); + frameSender->stopSending(); killEmAll(); //Ride the lightning delete ui; delete model; @@ -438,6 +462,171 @@ void MainWindow::writeSettings() } } +void MainWindow::onSenderCellChanged(int row, int col) +{ + if (inhibitSenderChanged) return; + qDebug() << "onCellChanged"; + if (row == ui->tableSimpleSender->rowCount() - 1) + { + createSenderRow(); + } + + processSenderCellChange(row, col); +} + +void MainWindow::processSenderCellChange(int line, int col) +{ + qDebug() << "processSenderCellChange"; + FrameSendData *tempData; + QStringList tokens; + int tempVal; + + int numBuses = CANConManager::getInstance()->getNumBuses(); + QByteArray arr; + + tempData = frameSender->getSendRecordRef(line); + + if (!tempData) + { + qDebug() << "Need to set up a new entry in senders"; + FrameSendData dat; + dat.enabled = false; + dat.count = 0; + dat.frameCount = 0; + dat.bus = 0; + frameSender->addSendRecord(dat); + tempData = frameSender->getSendRecordRef(line); + } + + if (!tempData) + { + qDebug() << "No data to modify in processSenderCellChange. This is a bug!"; + return; + } + + switch (col) + { + case SIMP_COL::SC_COL_EN: //Enable check box + if (ui->tableSimpleSender->item(line, 0)->checkState() == Qt::Checked) + { + tempData->enabled = true; + } + else tempData->enabled = false; + qDebug() << "Setting enabled to " << tempData->enabled; + break; + case SIMP_COL::SC_COL_BUS: //Bus designation + tempVal = Utility::ParseStringToNum(ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_BUS)->text()); + if (tempVal < -1) tempVal = -1; + if (tempVal >= numBuses) tempVal = numBuses - 1; + tempData->bus = tempVal; + qDebug() << "Setting bus to " << tempVal; + break; + case SIMP_COL::SC_COL_ID: //ID field + tempVal = Utility::ParseStringToNum(ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_ID)->text()); + if (tempVal < 0) tempVal = 0; + if (tempVal > 0x7FFFFFFF) tempVal = 0x7FFFFFFF; + tempData->setFrameId(tempVal); + if (tempData->frameId() > 0x7FF) { + tempData->setExtendedFrameFormat(true); + ui->tableSimpleSender->blockSignals(true); + ui->tableSimpleSender->item(line, ST_COLS::SENDTAB_COL_EXT)->setCheckState(Qt::Checked); + ui->tableSimpleSender->blockSignals(false); + } + qDebug() << "setting ID to " << tempVal; + break; + case SIMP_COL::SC_COL_EXT: + if (ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_EXT)->checkState() == Qt::Checked) { + tempData->setExtendedFrameFormat(true); + } else { + tempData->setExtendedFrameFormat(false); + } + break; + case SIMP_COL::SC_COL_REM: + if (ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_REM)->checkState() == Qt::Checked) { + tempData->setFrameType(QCanBusFrame::RemoteRequestFrame); + } else { + tempData->setFrameType(QCanBusFrame::DataFrame); + } + break; + case SIMP_COL::SC_COL_DATA: //Data bytes + for (int i = 0; i < 8; i++) tempData->payload().data()[i] = 0; + +#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) + tokens = ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_DATA)->text().split(" ", Qt::SkipEmptyParts); +#else + tokens = ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_DATA)->text().split(" ", QString::SkipEmptyParts); +#endif + arr.clear(); + arr.reserve(tokens.count()); + for (int j = 0; j < tokens.count(); j++) + { + arr.append((uint8_t)Utility::ParseStringToNum(tokens[j])); + } + tempData->setPayload(arr); + break; + case SIMP_COL::SC_COL_INTERVAL: //interval in ms + + QString trigger = ui->tableSimpleSender->item(line, SIMP_COL::SC_COL_INTERVAL)->text().toUpper(); + + Trigger thisTrigger; + thisTrigger.bus = -1; //-1 means we don't care which + thisTrigger.ID = -1; //the rest of these being -1 means nothing has changed it + thisTrigger.maxCount = -1; + thisTrigger.milliseconds = -1; + thisTrigger.currCount = 0; + thisTrigger.msCounter = 0; + thisTrigger.triggerMask = 0; + thisTrigger.readyCount = true; + + tempData->triggers.clear(); + tempData->triggers.reserve(1); + + if (trigger != "") + { + thisTrigger.milliseconds = Utility::ParseStringToNum(trigger); + thisTrigger.triggerMask |= TriggerMask::TRG_MS; + } + + if (thisTrigger.milliseconds < 1) thisTrigger.milliseconds = 1; + + tempData->triggers.append(thisTrigger); + + break; + } +} + +void MainWindow::createSenderRow() +{ + int row = ui->tableSimpleSender->rowCount(); + ui->tableSimpleSender->insertRow(row); + + QTableWidgetItem *item = new QTableWidgetItem(); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Unchecked); + inhibitSenderChanged = true; + ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_EN, item); + + item = new QTableWidgetItem(); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Unchecked); + ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_EXT, item); + + item = new QTableWidgetItem(); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Unchecked); + ui->tableSimpleSender->setItem(row, SIMP_COL::SC_COL_REM, item); + + for (int i = 1; i <= SIMP_COL::SC_COL_COUNT; i++) + { + if (i != SIMP_COL::SC_COL_EXT && i != SIMP_COL::SC_COL_REM) { + item = new QTableWidgetItem(""); + ui->tableSimpleSender->setItem(row, i, item); + } + } + + inhibitSenderChanged = false; +} + void MainWindow::updateConnectionSettings(QString connectionType, QString port, int speed0, int speed1) { Q_UNUSED(connectionType); @@ -825,6 +1014,18 @@ void MainWindow::tickGUIUpdate() } } + //refresh the count for all the frame senders + FrameSendData *tempData; + int numRows = ui->tableSimpleSender->rowCount(); + for (int i = 0; i < numRows; i++) + { + tempData = frameSender->getSendRecordRef(i); + if (tempData) + { + ui->tableSimpleSender->item(i, SIMP_COL::SC_COL_COUNT)->setText(QString::number( tempData->count )); + } + } + rxFrames = 0; //} } @@ -1371,6 +1572,11 @@ CANFrameModel* MainWindow::getCANFrameModel() return model; } + +/* + * All functions past this point set up the various other windows that can be opened +*/ + void MainWindow::showSettingsDialog() { if (!settingsDialog) diff --git a/mainwindow.h b/mainwindow.h index dbc7cdf..31df02c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -10,7 +10,7 @@ #include "framefileio.h" #include "dbc/dbchandler.h" #include "bus_protocols/isotp_handler.h" - +#include "framesenderobject.h" #include "re/graphingwindow.h" #include "re/frameinfowindow.h" #include "frameplaybackwindow.h" @@ -40,6 +40,18 @@ class ConnectionWindow; class ISOTP_InterpreterWindow; class ScriptingWindow; +enum SIMP_COL +{ + SC_COL_EN = 0, + SC_COL_BUS = 1, + SC_COL_ID = 2, + SC_COL_EXT = 3, + SC_COL_REM = 4, + SC_COL_DATA = 5, + SC_COL_INTERVAL = 6, + SC_COL_COUNT = 7, +}; + namespace Ui { class MainWindow; } @@ -114,6 +126,7 @@ private slots: void filterClearAll(); void headerClicked (int logicalIndex); void DBCSettingsUpdated(); + void onSenderCellChanged(int, int); public slots: void gotFrames(int); @@ -142,6 +155,7 @@ private: QByteArray inputBuffer; QTimer updateTimer; QElapsedTimer *elapsedTime; + FrameSenderObject *frameSender; int framesPerSec; int rxFrames; bool inhibitFilterUpdate; @@ -151,6 +165,7 @@ private: bool CSVAbsTime; bool bDirty; //have frames been added or subtracted since the last save/load? bool useFiltered; //should sub-windows use the unfiltered or filtered frames list? + bool inhibitSenderChanged; bool continuousLogging; int continuousLogFlushCounter; @@ -211,6 +226,8 @@ private: bool eventFilter(QObject *obj, QEvent *event); void manageRowExpansion(); void disableAutoRowExpansion(); + void createSenderRow(); + void processSenderCellChange(int line, int col); }; #endif // MAINWINDOW_H diff --git a/pcaplite.cpp b/pcaplite.cpp index 189b2ce..a0f6c90 100644 --- a/pcaplite.cpp +++ b/pcaplite.cpp @@ -19,7 +19,7 @@ static unsigned char pcap_buffer[MAX_CAN_PACKET_SIZE]; static pcap_t p; -pcap *pcap_open_offline(const char *filename, char *error_text) { +pcap *pcap_open_offline(const char *filename, char *error_text, int expected_link_type) { FILE *file; snprintf(error_text, PCAP_ERRBUF_SIZE, "OK"); @@ -48,6 +48,23 @@ pcap *pcap_open_offline(const char *filename, char *error_text) { return (NULL); } + unsigned int link_type; + fseek(file, PCAP_FILE_HEADER_LENGTH - 4, SEEK_SET); + bytes_read = fread(&link_type, 1, sizeof(link_type), file); + if (bytes_read != sizeof(link_type)) { + snprintf(error_text, PCAP_ERRBUF_SIZE, "Cannot read linktype word"); + fclose(file); + return (NULL); + } + if (expected_link_type >= 0) { + // Check the link type + if ((int) link_type != expected_link_type) { + snprintf(error_text, PCAP_ERRBUF_SIZE, "This link type is not supported by this decoder"); + fclose(file); + return (NULL); + } + } + // set the format // and seek past file header if (MAGIC_NG == magic) { @@ -61,14 +78,12 @@ pcap *pcap_open_offline(const char *filename, char *error_text) { return (NULL); } fseek(file, section_length, SEEK_SET); - } else { p.is_ng = 0; fseek(file, PCAP_FILE_HEADER_LENGTH, SEEK_SET); } p.file = file; - return(&p); } diff --git a/pcaplite.h b/pcaplite.h index 8a48153..02fbb77 100644 --- a/pcaplite.h +++ b/pcaplite.h @@ -8,7 +8,9 @@ #include #endif -#define PCAP_ERRBUF_SIZE 256 +#define PCAP_ERRBUF_SIZE (256) +#define PCAP_LINKTYPE_SOCKETCAN (227) +#define PCAP_LINKTYPE_ANY (-1) struct pcap_pkthdr { struct timeval ts; /* time stamp */ @@ -23,7 +25,7 @@ struct pcap { typedef struct pcap pcap_t; -pcap *pcap_open_offline(const char *, char *); +pcap *pcap_open_offline(const char *, char *, int); const unsigned char *pcap_next(pcap_t *, struct pcap_pkthdr *); diff --git a/re/dbccomparatorwindow.cpp b/re/dbccomparatorwindow.cpp index e9b87b2..f8eb0bd 100644 --- a/re/dbccomparatorwindow.cpp +++ b/re/dbccomparatorwindow.cpp @@ -222,8 +222,8 @@ void DBCComparatorWindow::calculateDetails() ui->treeDetails->addTopLevelItem(msgDiffRoot); - QTreeWidgetItem *msgItem; - QTreeWidgetItem *sigTemp; + QTreeWidgetItem *msgItem {}; + QTreeWidgetItem *sigTemp {}; for (int i = 0; i < firstDBC->messageHandler->getCount(); i++) { @@ -257,7 +257,8 @@ void DBCComparatorWindow::calculateDetails() msgItem->setText(0, msgName + " (" + Utility::formatCANID(thisMsg->ID) + ")"); sigDiffTwo->addChild(msgItem); } - msgItem->addChild(missingSigItem); + if (msgItem) + msgItem->addChild(missingSigItem); } else //signal exists on both sides. See if it as changed position or length { @@ -301,7 +302,8 @@ void DBCComparatorWindow::calculateDetails() sigTemp->setText(0, msgName + " (" + Utility::formatCANID(thisMsg->ID) + ")"); sigModifiedRoot->addChild(sigTemp); } - sigTemp->addChild(sigItem); + if (sigTemp) + sigTemp->addChild(sigItem); } } } diff --git a/re/flowviewwindow.cpp b/re/flowviewwindow.cpp index c51960b..77c088f 100644 --- a/re/flowviewwindow.cpp +++ b/re/flowviewwindow.cpp @@ -851,7 +851,7 @@ void FlowViewWindow::updatePosition(bool forward) int maxVal = qMin(chunk * 8 + 8, frameCache.at(currentPosition).payload().length()); for (int i = chunk * 8; i < maxVal; i++) { - unsigned char thisByte = static_cast(frameCache.at(currentPosition).payload().data()[i]); + unsigned char thisByte = static_cast(frameCache.at(currentPosition).payload()[i]); cngByte = currBytes[i] ^ thisByte; changedBits |= (uint64_t)cngByte << (8ull * (i & 7)); } diff --git a/re/fuzzingwindow.cpp b/re/fuzzingwindow.cpp index d7c84a4..9a75c3e 100644 --- a/re/fuzzingwindow.cpp +++ b/re/fuzzingwindow.cpp @@ -19,6 +19,9 @@ FuzzingWindow::FuzzingWindow(const QVector *frames, QWidget *parent) : fuzzTimer = new QTimer(); + connect(ui->btnMarkAllHigh, &QPushButton::clicked, this, &FuzzingWindow::markAllHigh); + connect(ui->btnMarkAllLow, &QPushButton::clicked, this, &FuzzingWindow::markAllLow); + connect(ui->btnMarkAllAuto, &QPushButton::clicked, this, &FuzzingWindow::markAllAuto); connect(ui->btnStartStop, &QPushButton::clicked, this, &FuzzingWindow::toggleFuzzing); connect(ui->btnAllFilters, &QPushButton::clicked, this, &FuzzingWindow::setAllFilters); connect(ui->btnNoFilters, &QPushButton::clicked, this, &FuzzingWindow::clearAllFilters); @@ -508,3 +511,30 @@ void FuzzingWindow::redrawGrid() ui->bitfield->setReference(refBytes, false); ui->bitfield->updateData(dataBytes, true); } + +void FuzzingWindow::markAllHigh() +{ + for (int i = 0; i < 512; i++) + { + bitGrid[i] = 2; + } + redrawGrid(); +} + +void FuzzingWindow::markAllLow() +{ + for (int i = 0; i < 512; i++) + { + bitGrid[i] = 0; + } + redrawGrid(); +} + +void FuzzingWindow::markAllAuto() +{ + for (int i = 0; i < 512; i++) + { + bitGrid[i] = 1; + } + redrawGrid(); +} diff --git a/re/fuzzingwindow.h b/re/fuzzingwindow.h index 6b52da4..58cf378 100644 --- a/re/fuzzingwindow.h +++ b/re/fuzzingwindow.h @@ -42,6 +42,9 @@ private slots: void bitfieldClicked(int); void changedNumDataBytes(int newVal); void updatedFrames(int numFrames); + void markAllHigh(); + void markAllLow(); + void markAllAuto(); private: Ui::FuzzingWindow *ui; diff --git a/re/graphingwindow.cpp b/re/graphingwindow.cpp index f2a41e7..80b32d5 100644 --- a/re/graphingwindow.cpp +++ b/re/graphingwindow.cpp @@ -39,7 +39,7 @@ GraphingWindow::GraphingWindow(const QVector *frames, QWidget *parent) if (Utility::timeStyle == TS_CLOCK) { - QSharedPointer timeTicker(new QCPAxisTickerTime); + QSharedPointer timeTicker = QSharedPointer::create(); timeTicker->setTimeFormat("%h:%m:%s.%z"); ui->graphingView->xAxis->setTicker(timeTicker); } @@ -571,7 +571,7 @@ void GraphingWindow::removeSelectedGraph() int idx = -1; for (int i = 0; i < graphParams.count(); i++) { - if (graphParams[i].ref == ui->graphingView->selectedGraphs().first()) + if (graphParams[i].ref == ui->graphingView->selectedGraphs().constFirst()) { idx = i; break; @@ -590,7 +590,7 @@ void GraphingWindow::removeSelectedGraph() graphParams.removeAt(idx); - ui->graphingView->removeGraph(ui->graphingView->selectedGraphs().first()); + ui->graphingView->removeGraph(ui->graphingView->selectedGraphs().constFirst()); if (graphParams.count() == 0) needScaleSetup = true; @@ -605,7 +605,7 @@ void GraphingWindow::editSelectedGraph() int idx = -1; for (int i = 0; i < graphParams.count(); i++) { - if (graphParams[i].ref == ui->graphingView->selectedGraphs().first()) + if (graphParams[i].ref == ui->graphingView->selectedGraphs().constFirst()) { idx = i; break; @@ -701,7 +701,6 @@ void GraphingWindow::contextMenuRequest(QPoint pos) void GraphingWindow::saveGraphs() { - QString filename; QFileDialog dialog(this); QSettings settings; @@ -718,7 +717,7 @@ void GraphingWindow::saveGraphs() if (dialog.exec() == QDialog::Accepted) { - filename = dialog.selectedFiles()[0]; + QString filename = dialog.selectedFiles().constFirst(); settings.setValue("Graphing/LoadSaveDirectory", dialog.directory().path()); if (dialog.selectedNameFilter() == filters[0]) @@ -741,7 +740,6 @@ void GraphingWindow::saveGraphs() void GraphingWindow::saveSpreadsheet() { - QString filename; QFileDialog dialog(this); QSettings settings; @@ -756,14 +754,14 @@ void GraphingWindow::saveSpreadsheet() if (dialog.exec() == QDialog::Accepted) { - filename = dialog.selectedFiles()[0]; + QString filename = dialog.selectedFiles().constFirst(); settings.setValue("Graphing/LoadSaveDirectory", dialog.directory().path()); if (!filename.contains('.')) filename += ".csv"; - QFile *outFile = new QFile(filename); + QFile outFile(filename); - if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text)) return; /* @@ -779,12 +777,12 @@ void GraphingWindow::saveSpreadsheet() QList::iterator iter; double xMin = std::numeric_limits::max(), xMax = std::numeric_limits::min(); - size_t maxCount = 0; - size_t numGraphs = graphParams.length(); + int maxCount = 0; + int numGraphs = graphParams.length(); for (auto && graph : graphParams) { xMin = std::min(xMin, graph.x[0]); xMax = std::max(xMax, graph.x[graph.x.count() - 1]); - maxCount = std::max(maxCount, static_cast(graph.x.count())); + maxCount = std::max(maxCount, graph.x.count()); } qDebug() << "xMin: " << xMin; qDebug() << "xMax: " << xMax; @@ -795,27 +793,25 @@ void GraphingWindow::saveSpreadsheet() double xSize = xMax - xMin; double sliceSize = xSize / ((double)maxCount); double equivValue = sliceSize / 100.0; - double currentX; - double value; QList indices; indices.reserve(numGraphs); - outFile->write("TimeStamp"); + outFile.write("TimeStamp"); for (auto && graph : graphParams) { indices.append(0); - outFile->putChar(','); - outFile->write(graph.graphName.toUtf8()); + outFile.putChar(','); + outFile.write(graph.graphName.toUtf8()); } - outFile->write("\n"); + outFile.write("\n"); - for (size_t j = 1; j < (maxCount - 1); j++) + for (int j = 1; j < (maxCount - 1); j++) { - currentX = xMin + (j * sliceSize); + double currentX = xMin + (j * sliceSize); qDebug() << "X: " << currentX; - outFile->write(QString::number(currentX, 'f').toUtf8()); - for (size_t k = 0; k < numGraphs; k++) + outFile.write(QString::number(currentX, 'f').toUtf8()); + for (int k = 0; k < numGraphs; k++) { - value = 0.0; + double value = 0.0; // move cursor to last sample before currentX while (graphParams[k].x[indices[k]+1] < currentX) @@ -849,27 +845,26 @@ void GraphingWindow::saveSpreadsheet() else { // find index, where x >= currentX - size_t cursor = indices[k]; + int cursor = indices[k]; double span = graphParams[k].x[cursor+1] - graphParams[k].x[cursor]; double progress = (currentX - graphParams[k].x[cursor]) / span; Q_ASSERT(progress >= 0.0 && progress <= 1.0); value = Utility::Lerp(graphParams[k].y[cursor], graphParams[k].y[cursor+1], progress); qDebug() << "Span: " << span << " Prog: " << progress << " Value: " << value; } - outFile->putChar(','); - outFile->write(QString::number(value).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(value).toUtf8()); } - outFile->write("\n"); + outFile.write("\n"); } - outFile->close(); + outFile.close(); } } void GraphingWindow::saveDefinitions() { - QString filename; QFileDialog dialog(this); QSettings settings; @@ -884,79 +879,78 @@ void GraphingWindow::saveDefinitions() if (dialog.exec() == QDialog::Accepted) { - filename = dialog.selectedFiles()[0]; + QString filename = dialog.selectedFiles().constFirst(); settings.setValue("Graphing/LoadSaveDirectory", dialog.directory().path()); if (!filename.contains('.')) filename += ".gdf"; - QFile *outFile = new QFile(filename); + QFile outFile(filename); - if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text)) return; QList::iterator iter; for (iter = graphParams.begin(); iter != graphParams.end(); ++iter) { - outFile->write("Z,"); - outFile->write(QString::number(iter->ID, 16).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->mask, 16).toUtf8()); - outFile->putChar(','); - if (iter->intelFormat) outFile->write(QString::number(iter->startBit).toUtf8()); - else outFile->write(QString::number(iter->startBit * -1).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->numBits).toUtf8()); - outFile->putChar(','); - if (iter->isSigned) outFile->putChar('Y'); - else outFile->putChar('N'); - outFile->putChar(','); - outFile->write(QString::number(iter->bias).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->scale).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->stride).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->bus).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->lineColor.red()).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->lineColor.green()).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->lineColor.blue()).toUtf8()); - outFile->putChar(','); - outFile->write(iter->graphName.toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->fillColor.red()).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->fillColor.green()).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->fillColor.blue()).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->fillColor.alpha()).toUtf8()); - outFile->putChar(','); - if (iter->drawOnlyPoints) outFile->putChar('Y'); - else outFile->putChar('N'); - outFile->putChar(','); - outFile->write(QString::number(iter->pointType).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->lineWidth).toUtf8()); + outFile.write("Z,"); + outFile.write(QString::number(iter->ID, 16).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->mask, 16).toUtf8()); + outFile.putChar(','); + if (iter->intelFormat) outFile.write(QString::number(iter->startBit).toUtf8()); + else outFile.write(QString::number(iter->startBit * -1).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->numBits).toUtf8()); + outFile.putChar(','); + if (iter->isSigned) outFile.putChar('Y'); + else outFile.putChar('N'); + outFile.putChar(','); + outFile.write(QString::number(iter->bias).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->scale).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->stride).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->bus).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->lineColor.red()).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->lineColor.green()).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->lineColor.blue()).toUtf8()); + outFile.putChar(','); + outFile.write(iter->graphName.toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->fillColor.red()).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->fillColor.green()).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->fillColor.blue()).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->fillColor.alpha()).toUtf8()); + outFile.putChar(','); + if (iter->drawOnlyPoints) outFile.putChar('Y'); + else outFile.putChar('N'); + outFile.putChar(','); + outFile.write(QString::number(iter->pointType).toUtf8()); + outFile.putChar(','); + outFile.write(QString::number(iter->lineWidth).toUtf8()); if (iter->associatedSignal) { - outFile->putChar(','); - outFile->write(iter->associatedSignal->parentMessage->name.toUtf8()); - outFile->putChar(','); - outFile->write(iter->associatedSignal->name.toUtf8()); + outFile.putChar(','); + outFile.write(iter->associatedSignal->parentMessage->name.toUtf8()); + outFile.putChar(','); + outFile.write(iter->associatedSignal->name.toUtf8()); } - outFile->write("\n"); + outFile.write("\n"); } - outFile->close(); + outFile.close(); } } void GraphingWindow::loadDefinitions() { - QString filename; QFileDialog dialog; QSettings settings; @@ -973,17 +967,17 @@ void GraphingWindow::loadDefinitions() if (dialog.exec() == QDialog::Accepted) { - filename = dialog.selectedFiles()[0]; + QString filename = dialog.selectedFiles().constFirst(); settings.setValue("Graphing/LoadSaveDirectory", dialog.directory().path()); - QFile *inFile = new QFile(filename); + QFile inFile(filename); QByteArray line; - if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + if (!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) return; - while (!inFile->atEnd()) { - line = inFile->readLine().simplified(); + while (!inFile.atEnd()) { + line = inFile.readLine().simplified(); if (line.length() > 2) { GraphParams gp; @@ -1205,7 +1199,7 @@ void GraphingWindow::loadDefinitions() } } } - inFile->close(); + inFile.close(); } } @@ -1325,10 +1319,8 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) double yminval=10000000.0, ymaxval = -1000000.0; double xminval=10000000000.0, xmaxval = -10000000000.0; GraphParams *refParam = ¶ms; - int sBit, bits; - bool intelFormat, isSigned; QString tempStr; - double x,y; + double x{}, y{}; qDebug() << "New Graph ID: " << params.ID; qDebug() << "Start bit: " << params.startBit; @@ -1341,7 +1333,8 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) for (int i = 0; i < modelFrames->count(); i++) { CANFrame thisFrame = modelFrames->at(i); - if (thisFrame.frameId() == params.ID && thisFrame.frameType() == QCanBusFrame::DataFrame && ( ( params.bus == -1) || (params.bus == thisFrame.bus) ) ) frameCache.append(thisFrame); + if ( (thisFrame.frameId() == params.ID) && (thisFrame.frameType() == QCanBusFrame::DataFrame) + && ( ( params.bus == -1) || (params.bus == thisFrame.bus) ) ) frameCache.append(thisFrame); } //to fix weirdness where a graph that has no data won't be able to be edited, selected, or deleted properly @@ -1360,15 +1353,17 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) int numEntries = frameCache.count() / params.stride; if (numEntries < 1) numEntries = 1; //could happen if stride is larger than frame count + params.x.clear(); + params.y.clear(); params.x.reserve(numEntries); params.y.reserve(numEntries); //params.x.fill(0, numEntries); //params.y.fill(0, numEntries); - sBit = params.startBit; - bits = params.numBits; - intelFormat = params.intelFormat; - isSigned = params.isSigned; + int sBit = params.startBit; + int bits = params.numBits; + bool intelFormat = params.intelFormat; + bool isSigned = params.isSigned; for (int j = 0; j < numEntries; j++) { @@ -1558,16 +1553,16 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) void GraphingWindow::moveLegend() { qDebug() << "moveLegend"; - if (QAction* contextAction = qobject_cast(sender())) // make sure this slot is really called by a context menu action, so it carries the data we need - { - bool ok; - int dataInt = contextAction->data().toInt(&ok); - if (ok) + if (QAction* contextAction = qobject_cast(sender())) // make sure this slot is really called by a context menu action, so it carries the data we need { - ui->graphingView->axisRect()->insetLayout()->setInsetAlignment(0, (Qt::Alignment)dataInt); - ui->graphingView->replot(); + bool ok; + int dataInt = contextAction->data().toInt(&ok); + if (ok) + { + ui->graphingView->axisRect()->insetLayout()->setInsetAlignment(0, (Qt::Alignment)dataInt); + ui->graphingView->replot(); + } } - } } GraphParams::GraphParams() diff --git a/re/isotp_interpreterwindow.cpp b/re/isotp_interpreterwindow.cpp index 5f8bb10..045d608 100644 --- a/re/isotp_interpreterwindow.cpp +++ b/re/isotp_interpreterwindow.cpp @@ -31,6 +31,7 @@ ISOTP_InterpreterWindow::ISOTP_InterpreterWindow(const QVector *frames connect(ui->tableIsoFrames, &QTableWidget::itemSelectionChanged, this, &ISOTP_InterpreterWindow::showDetailView); connect(ui->btnClearList, &QPushButton::clicked, this, &ISOTP_InterpreterWindow::clearList); + connect(ui->btnSaveList, &QPushButton::clicked, this, &ISOTP_InterpreterWindow::saveList); connect(ui->cbUseExtendedAddressing, SIGNAL(toggled(bool)), this, SLOT(useExtendedAddressing(bool))); QStringList headers; @@ -172,6 +173,80 @@ void ISOTP_InterpreterWindow::clearList() //idFilters.clear(); } +/* + * A bit complicated as the list doesn't have the detailed analysis in it. Have to take each list entry + * and process it to get the details then save those details to the file as well. + */ +void ISOTP_InterpreterWindow::saveList() +{ + QString buildString; + QString filename; + QFileDialog dialog(this); + QSettings settings; + + QStringList filters; + filters.append(QString(tr("Text File (*.txt)"))); + + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setNameFilters(filters); + dialog.setViewMode(QFileDialog::Detail); + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setDirectory(settings.value("FrameInfo/LoadSaveDirectory", dialog.directory().path()).toString()); + + if (dialog.exec() == QDialog::Accepted) + { + settings.setValue("FrameInfo/LoadSaveDirectory", dialog.directory().path()); + filename = dialog.selectedFiles()[0]; + if (!filename.contains('.')) filename += ".txt"; + if (dialog.selectedNameFilter() == filters[0]) + { + QFile *outFile = new QFile(filename); + + if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + { + delete outFile; + return; + } + + int rows = messages.count(); + for (int r = 0 ; r < rows; r++) + { + ISOTP_MESSAGE msg = messages.at(r); + const unsigned char *data = reinterpret_cast(msg.payload().constData()); + int dataLen = msg.payload().length(); + + if (msg.reportedLength != dataLen) + { + continue; + } + + buildString.append(QString::number(msg.timeStamp().microSeconds()) + " " + QString::number(msg.frameId(), 16) + " "); + + //buildString.append(tr("Raw Payload: ")); + + for (int i = 0; i < dataLen; i++) + { + buildString.append(Utility::formatNumber(data[i])); + buildString.append(" "); + } + buildString.append("\n\n"); + + UDS_MESSAGE udsMsg; + bool result; + udsMsg = udsDecoder->tryISOtoUDS(msg, &result); + if (result) + buildString.append(udsDecoder->getDetailedMessageAnalysis(udsMsg)); + buildString.append("\n*********************************************************\n"); + outFile->write(buildString.toUtf8()); + } + + outFile->close(); + delete outFile; + } + } +} + + void ISOTP_InterpreterWindow::useExtendedAddressing(bool checked) { decoder->setExtendedAddressing(checked); diff --git a/re/isotp_interpreterwindow.h b/re/isotp_interpreterwindow.h index 14167de..3b3a6b2 100644 --- a/re/isotp_interpreterwindow.h +++ b/re/isotp_interpreterwindow.h @@ -26,6 +26,7 @@ private slots: void showDetailView(); void updatedFrames(int); void clearList(); + void saveList(); void listFilterItemChanged(QListWidgetItem *item); void filterAll(); void filterNone(); diff --git a/re/newgraphdialog.cpp b/re/newgraphdialog.cpp index 6b759f4..62f73c6 100644 --- a/re/newgraphdialog.cpp +++ b/re/newgraphdialog.cpp @@ -361,7 +361,6 @@ void NewGraphDialog::loadNodes() void NewGraphDialog::loadMessages(int idx) { int numFiles = 0; - DBC_MESSAGE *msg; ui->cbMessages->clear(); if (dbcHandler == nullptr) return; if ((numFiles = dbcHandler->getFileCount()) == 0) return; diff --git a/re/sniffer/snifferwindow.cpp b/re/sniffer/snifferwindow.cpp index e0ecfb8..f8145b1 100644 --- a/re/sniffer/snifferwindow.cpp +++ b/re/sniffer/snifferwindow.cpp @@ -181,7 +181,7 @@ void SnifferWindow::notchTick() } else { - ui->lblNotch->setBackgroundRole(QPalette::Background); + ui->lblNotch->setBackgroundRole(QPalette::Window); ui->lblNotch->repaint(); //qDebug() << "Tock"; } diff --git a/scriptingwindow.cpp b/scriptingwindow.cpp index 1f24dee..ea254c3 100644 --- a/scriptingwindow.cpp +++ b/scriptingwindow.cpp @@ -41,8 +41,11 @@ ScriptingWindow::ScriptingWindow(const QVector *frames, QWidget *paren connect(ui->btnRecompile, &QAbstractButton::pressed, this, &ScriptingWindow::recompileScript); connect(ui->btnRemoveScript, &QAbstractButton::pressed, this, &ScriptingWindow::deleteCurrentScript); connect(ui->btnRevertScript, &QAbstractButton::pressed, this, &ScriptingWindow::revertScript); + connect(ui->btnReloadScript, &QAbstractButton::pressed, this, &ScriptingWindow::reloadScript); connect(ui->btnSaveScript, &QAbstractButton::pressed, this, &ScriptingWindow::saveScript); + connect(ui->btnSaveAsScript, &QAbstractButton::pressed, this, &ScriptingWindow::saveAsScript); connect(ui->btnClearLog, &QAbstractButton::pressed, this, &ScriptingWindow::clickedLogClear); + connect(ui->btnSaveLog, &QAbstractButton::pressed, this, &ScriptingWindow::saveLog); connect(ui->listLoadedScripts, &QListWidget::currentRowChanged, this, &ScriptingWindow::changeCurrentScript); connect(ui->tableVariables, SIGNAL(cellChanged(int,int)), this, SLOT(updatedValue(int, int))); @@ -301,6 +304,21 @@ void ScriptingWindow::refreshSourceWindow() } void ScriptingWindow::saveScript() +{ + QFile *outFile = new QFile(currentScript->filePath); + + if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + { + delete outFile; + return; + } + outFile->write(editor->toPlainText().toUtf8()); + currentScript->scriptText = editor->toPlainText(); + outFile->close(); + delete outFile; +} + +void ScriptingWindow::saveAsScript() { QString filename; QFileDialog dialog(this); @@ -358,6 +376,38 @@ void ScriptingWindow::revertScript() } } +void ScriptingWindow::reloadScript() +{ + QMessageBox msgBox; + msgBox.setText("Are you sure you'd like to reload from disk?"); + msgBox.setInformativeText("Really do it?"); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Cancel); + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Yes: + { + // get the latest version from disk and set in the editor/state + QFile scriptFile(currentScript->filePath); + if (scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QString contents = scriptFile.readAll(); + scriptFile.close(); + editor->setPlainText(contents); + currentScript->scriptText = contents; + currentScript->compileScript(); + } + break; + } + case QMessageBox::No: + break; + default: + // should never be reached + break; + } +} + void ScriptingWindow::recompileScript() { if (currentScript) @@ -373,6 +423,45 @@ void ScriptingWindow::clickedLogClear() elapsedTime.start(); } +void ScriptingWindow::saveLog() +{ + QString filename; + QFileDialog dialog(this); + QSettings settings; + + QStringList filters; + filters.append(QString(tr("Log File (*.log)"))); + + dialog.setDirectory(settings.value("ScriptingWindow/LoadSaveDirectory", dialog.directory().path()).toString()); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setNameFilters(filters); + dialog.setViewMode(QFileDialog::Detail); + dialog.setAcceptMode(QFileDialog::AcceptSave); + + if (dialog.exec() == QDialog::Accepted) + { + filename = dialog.selectedFiles()[0]; + if (!filename.contains('.')) filename += ".log"; + if (dialog.selectedNameFilter() == filters[0]) + { + QFile *outFile = new QFile(filename); + + if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + { + delete outFile; + return; + } + int c = ui->listLog ->count(); + for (int row = 0; row < c; row++) { + outFile->write(ui->listLog->item(row)->data(Qt::DisplayRole).toString().toUtf8() + "\n"); + } + outFile->close(); + delete outFile; + settings.setValue("ScriptingWindow/LoadSaveDirectory", dialog.directory().path()); + } + } +} + void ScriptingWindow::log(QString text) { ScriptContainer *cont = qobject_cast(sender()); diff --git a/scriptingwindow.h b/scriptingwindow.h index 156a72e..5577099 100644 --- a/scriptingwindow.h +++ b/scriptingwindow.h @@ -37,7 +37,9 @@ private slots: void deleteCurrentScript(); void refreshSourceWindow(); void saveScript(); + void saveAsScript(); void revertScript(); + void reloadScript(); void recompileScript(); void changeCurrentScript(); void newFrames(const CANConnection*, const QVector&); @@ -49,6 +51,7 @@ private: void closeEvent(QCloseEvent *event); void readSettings(); void writeSettings(); + void saveLog(); bool eventFilter(QObject *obj, QEvent *event); Ui::ScriptingWindow *ui; diff --git a/signalviewerwindow.cpp b/signalviewerwindow.cpp index f21c7f9..c31f0fb 100644 --- a/signalviewerwindow.cpp +++ b/signalviewerwindow.cpp @@ -98,15 +98,18 @@ void SignalViewerWindow::processFrame(CANFrame &frame) if (!sig) return; if (sig->parentMessage->ID == frame.frameId()) { - if (sig->processAsText(frame, sigString, false)) //if true we could interpret the signal so update it in the list + if (sig->isSignalInMessage(frame)) //filter out multiplexed signals that aren't in this message. { - QTableWidgetItem *item = ui->tableViewer->item(i, VALUE_COL); - if (!item) + if (sig->processAsText(frame, sigString, false)) //if true we could interpret the signal so update it in the list { - item = new QTableWidgetItem(sigString); - ui->tableViewer->setItem(i, VALUE_COL, item); + QTableWidgetItem *item = ui->tableViewer->item(i, VALUE_COL); + if (!item) + { + item = new QTableWidgetItem(sigString); + ui->tableViewer->setItem(i, VALUE_COL, item); + } + else item->setText(sigString); } - else item->setText(sigString); } } } diff --git a/ui/frameinfowindow.ui b/ui/frameinfowindow.ui index 0b61394..375c63e 100644 --- a/ui/frameinfowindow.ui +++ b/ui/frameinfowindow.ui @@ -120,7 +120,7 @@ - + diff --git a/ui/framesenderwindow.ui b/ui/framesenderwindow.ui index 4e027bd..0f0bebf 100644 --- a/ui/framesenderwindow.ui +++ b/ui/framesenderwindow.ui @@ -6,7 +6,7 @@ 0 0 - 1008 + 1350 611 diff --git a/ui/fuzzingwindow.ui b/ui/fuzzingwindow.ui index 1600b97..813e9a4 100644 --- a/ui/fuzzingwindow.ui +++ b/ui/fuzzingwindow.ui @@ -487,6 +487,27 @@ + + + + All High + + + + + + + All Low + + + + + + + All Auto + + + @@ -534,6 +555,9 @@ listID btnAllFilters btnNoFilters + btnMarkAllHigh + btnMarkAllLow + btnMarkAllAuto btnStartStop diff --git a/ui/isotp_interpreterwindow.ui b/ui/isotp_interpreterwindow.ui index ec1ed3b..6f7f67d 100644 --- a/ui/isotp_interpreterwindow.ui +++ b/ui/isotp_interpreterwindow.ui @@ -40,6 +40,13 @@ + + + + Save List + + + diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 3b71991..72d9cec 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 800 - 682 + 1177 + 717 @@ -26,336 +26,350 @@ SavvyCAN - + - - - - 11 - false - - - - true - - - QAbstractItemView::ScrollPerPixel - - - false - - - - - + - - - true - - - Remote Connection Active: - - - Qt::AlignCenter - - - - - - - true - - - - 1000 - 50 - - - - - 150 - 50 - - - - - false - - - - 8 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - true - - - - - - - - - - - - 255 - 0 - 0 - - - - - - - - - 255 - 0 - 0 - - - - - - - - - 120 - 120 - 120 - - - - - - - - - 11 - true - true - - - - - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - Total Frames Captured: - - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - Frames Per Second: - - - - - - - - 0 - 0 - - - - - - - - - - - Suspend Capturing - - - false - - - - - - - Normalize Frame Timing - - - - - - - Clear Frames - - - - - - - Keep Filters When Clearing - - - - - - - Auto Scroll Window - - - - - - - true - - - Overwrite Mode - - - - - - - Qt::Horizontal - - - - - - - Interpret Frames - - - - - - - Expand All Rows - - - - - - - Collapse All Rows - - - - - - - Bus Filtering: - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 175 - 40 - - - - QListView::LeftToRight - - - - - - - Frame Filtering: - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 0 - 160 - - - - - 175 - 16777215 - - - - false - - - - - + - + + + + 11 + false + + + + true + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + + + + + + + + + + + + true + - All + Remote Connection Active: + + + Qt::AlignCenter - - - None + + + true + + + + 1000 + 50 + + + + + 150 + 50 + + + + + false + + + + 8 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 120 + 120 + 120 + + + + + + + + + 11 + true + true + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Total Frames Captured: + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Frames Per Second: + + + + + + + + 0 + 0 + + + + + + + + + + + Suspend Capturing + + + false + + + + + + + Normalize Frame Timing + + + + + + + Clear Frames + + + + + + + Keep Filters When Clearing + + + + + + + Auto Scroll Window + + + + + + + true + + + Overwrite Mode + + + + + + + Qt::Horizontal + + + + + + + Interpret Frames + + + + + + + Expand All Rows + + + + + + + Collapse All Rows + + + + + + + Bus Filtering: + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 175 + 40 + + + + QListView::LeftToRight + + + + + + + Frame Filtering: + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 300 + 160 + + + + + 1000 + 16777215 + + + + false + + + + + + + + + All + + + + + + + None + + + + + @@ -367,7 +381,7 @@ 0 0 - 800 + 1177 26 diff --git a/ui/newgraphdialog.ui b/ui/newgraphdialog.ui index 9b2f036..46d7684 100644 --- a/ui/newgraphdialog.ui +++ b/ui/newgraphdialog.ui @@ -7,7 +7,7 @@ 0 0 681 - 828 + 797 @@ -15,7 +15,7 @@ - + @@ -49,216 +49,27 @@ - 300 - 300 + 280 + 280 - 300 - 300 + 280 + 280 - - - - Data Len: - - - - - - - - - - Little Endian - - - - - - - (LSB First) - - - - - - - Signed: - - - - - - - - - - - - - - QFrame::NoFrame - - - Mask: - - - - - - - - - - Bias: - - - - - - - - - - Scale - - - - - - - - - - Stride - - - - - - - - - - Only Points - - - - - - - - - - - - - - Point Style - - - - - - - - - - Line Thickness - - - - - - - 15 - - - 1 - - - - - - - Line Color - - - - - - - true - - - CHANGE - - - true - - - - - - - Fill Color - - - - - - - true - - - CHANGE - - - true - - - - - - - Add this graph - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Associated Bus - - - - - - - -1 - - - + + + + Qt::Vertical + + + @@ -356,7 +167,7 @@ 20 - 40 + 30 @@ -365,6 +176,215 @@ + + + + Qt::Horizontal + + + + + + + + + + + Bit Len: + + + + + + + + + + Little Endian + + + + + + + (LSB First) + + + + + + + Signed: + + + + + + + + + + + + + + QFrame::NoFrame + + + Mask: + + + + + + + + + + Bias: + + + + + + + + + + Scale + + + + + + + + + + Stride + + + + + + + + + + Associated Bus + + + + + + + -1 + + + + + + + + + Qt::Vertical + + + + + + + + + Only Points + + + + + + + + + + + + + + Point Style + + + + + + + + + + Line Thickness + + + + + + + 15 + + + 1 + + + + + + + Line Color + + + + + + + true + + + CHANGE + + + true + + + + + + + Fill Color + + + + + + + true + + + CHANGE + + + true + + + + + + + + + + + Add this graph + + + @@ -378,15 +398,6 @@ txtName txtID - txtDataLen - cbIntel - cbSigned - txtMask - txtBias - txtScale - txtStride - colorSwatch - btnAddGraph cbMessages cbSignals btnCopySignal diff --git a/ui/scriptingwindow.ui b/ui/scriptingwindow.ui index 35c0b9e..5623804 100644 --- a/ui/scriptingwindow.ui +++ b/ui/scriptingwindow.ui @@ -108,6 +108,13 @@ + + + + Sa&ve As + + + @@ -115,6 +122,13 @@ + + + + R&eload + + + @@ -151,11 +165,26 @@ - - - Clear Log &Window - - + + + Qt::AlignCenter + + 10, 0, 10, 0 + + + + Clear Log &Window + + + + + + + Save L&og + + + + @@ -169,10 +198,13 @@ listLoadedScripts tableVariables btnSaveScript + btnSaveAsScript btnRevertScript + btnReloadScript btnRecompile cbAutoScroll btnClearLog + btnSaveLog