diff --git a/SavvyCAN.pro b/SavvyCAN.pro index b8f445b..be58289 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -20,6 +20,7 @@ QMAKE_INFO_PLIST = Info.plist.template ICON = icons/SavvyIcon.icns SOURCES += main.cpp\ + connections/lawicel_serial.cpp \ connections/mqtt_bus.cpp \ dbc/dbcnodeduplicateeditor.cpp \ mqtt/qmqtt_client.cpp \ @@ -100,6 +101,7 @@ SOURCES += main.cpp\ HEADERS += mainwindow.h \ can_structs.h \ canframemodel.h \ + connections/lawicel_serial.h \ connections/socketcand.h \ connections/mqtt_bus.h \ dbc/dbcnodeduplicateeditor.h \ diff --git a/connections/canconconst.h b/connections/canconconst.h index 7f479c1..2cf53cc 100644 --- a/connections/canconconst.h +++ b/connections/canconconst.h @@ -20,6 +20,7 @@ namespace CANCon { REMOTE, KAYAK, MQTT, + LAWICEL, NONE }; } diff --git a/connections/canconfactory.cpp b/connections/canconfactory.cpp index 0adb9b8..43264dd 100644 --- a/connections/canconfactory.cpp +++ b/connections/canconfactory.cpp @@ -4,10 +4,11 @@ #include "gvretserial.h" #include "mqtt_bus.h" #include "socketcand.h" +#include "lawicel_serial.h" using namespace CANCon; -CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDriverName) +CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDriverName, int pSerialSpeed, int pBusSpeed) { switch(pType) { case SERIALBUS: @@ -19,6 +20,8 @@ CANConnection* CanConFactory::create(type pType, QString pPortName, QString pDri return new GVRetSerial(pPortName, false); case REMOTE: return new GVRetSerial(pPortName, true); //it's a special case of GVRET connected over TCP/IP so it uses the same class + case LAWICEL: + return new LAWICELSerial(pPortName, pSerialSpeed, pBusSpeed); case KAYAK: return new SocketCANd(pPortName); case MQTT: diff --git a/connections/canconfactory.h b/connections/canconfactory.h index 668568e..c23ce8b 100644 --- a/connections/canconfactory.h +++ b/connections/canconfactory.h @@ -7,7 +7,7 @@ class CanConFactory { public: - static CANConnection* create(CANCon::type, QString pPortName, QString pDriverName); + static CANConnection* create(CANCon::type, QString pPortName, QString pDriverName, int pSerialSpeed, int pBusSpeed); }; #endif // CANCONFACTORY_H diff --git a/connections/canconnection.cpp b/connections/canconnection.cpp index ba145ee..dc9d775 100644 --- a/connections/canconnection.cpp +++ b/connections/canconnection.cpp @@ -5,6 +5,8 @@ CANConnection::CANConnection(QString pPort, QString pDriver, CANCon::type pType, + int pSerialSpeed, + int pBusSpeed, int pNumBuses, int pQueueLen, bool pUseThread) : @@ -13,6 +15,7 @@ CANConnection::CANConnection(QString pPort, mPort(pPort), mDriver(pDriver), mType(pType), + mSerialSpeed(pSerialSpeed), mIsCapSuspended(false), mStatus(CANCon::NOT_CONNECTED), mStarted(false), @@ -34,6 +37,8 @@ CANConnection::CANConnection(QString pPort, mBusData[i].mConfigured = false; } + if (pBusSpeed > 0) mBusData[0].mBus.setSpeed(pBusSpeed); + /* if needed, create a thread and move ourself into it */ if(pUseThread) { mThread_p = new QThread(); diff --git a/connections/canconnection.h b/connections/canconnection.h index 38d1160..ab53fc8 100644 --- a/connections/canconnection.h +++ b/connections/canconnection.h @@ -21,13 +21,17 @@ protected: * @param pPort: string containing port name * @param pDriver: string containing driver name - Really only used for SerialBus connections * @param pType: the type of connection @ref CANCon::type + * @param pSerialSpeed: for devices with variable serial speed this is that speed. + * @param pBusSpeed: set an initial speed when opening this connection * @param pNumBuses: the number of buses the device has * @param pQueueLen: the length of the lock free queue to use * @param pUseThread: if set to true, object will be execute in a dedicated thread */ CANConnection(QString pPort, QString pDriver, - CANCon::type pType, + CANCon::type pType, + int pSerialSpeed, + int pBusSpeed, int pNumBuses, int pQueueLen, bool pUseThread); @@ -201,6 +205,7 @@ protected: int mNumBuses; //protected to allow connected device to figure out how many buses are available QVector mBusData; bool mConsoleOutput; //send debugging info to the console? + int mSerialSpeed; //determine if the passed frame is part of a filter or not. void checkTargettedFrame(CANFrame &frame); diff --git a/connections/canconnectionmodel.cpp b/connections/canconnectionmodel.cpp index aba9c7c..32a277f 100644 --- a/connections/canconnectionmodel.cpp +++ b/connections/canconnectionmodel.cpp @@ -86,6 +86,7 @@ QVariant CANConnectionModel::data(const QModelIndex &index, int role) const case CANCon::SERIALBUS: return "SerialBus"; case CANCon::GVRET_SERIAL: return "GVRET"; case CANCon::KAYAK: return "socketcand"; + case CANCon::LAWICEL: return "LAWICEL"; default: {} } else qDebug() << "Tried to show connection type but connection was nullptr"; diff --git a/connections/connectionwindow.cpp b/connections/connectionwindow.cpp index e277e58..210fcdd 100644 --- a/connections/connectionwindow.cpp +++ b/connections/connectionwindow.cpp @@ -250,6 +250,8 @@ void ConnectionWindow::handleNewConn() CANCon::type newType; QString newPort; QString newDriver; + int newSerialSpeed; + int newBusSpeed; CANConnection *conn; if (thisDialog->exec() == QDialog::Accepted) @@ -257,7 +259,9 @@ void ConnectionWindow::handleNewConn() newType = thisDialog->getConnectionType(); newPort = thisDialog->getPortName(); newDriver = thisDialog->getDriverName(); - conn = create(newType, newPort, newDriver); + newSerialSpeed = thisDialog->getSerialSpeed(); + newBusSpeed = thisDialog->getBusSpeed(); + conn = create(newType, newPort, newDriver, newSerialSpeed, newBusSpeed); if (conn) { connModel->add(conn); @@ -292,6 +296,7 @@ void ConnectionWindow::handleResetConn() { QString port, driver; CANCon::type type; + int serSpeed, busSpeed; int selIdx = ui->tableConnections->selectionModel()->currentIndex().row(); if (selIdx <0) return; @@ -304,13 +309,15 @@ void ConnectionWindow::handleResetConn() type = conn_p->getType(); port = conn_p->getPort(); driver = conn_p->getDriver(); + serSpeed = 0; //TODO: implement these + busSpeed = 0; /* stop and delete connection */ conn_p->stop(); conn_p = nullptr; - conn_p = create(type, port, driver); + conn_p = create(type, port, driver, serSpeed, busSpeed); if (conn_p) connModel->replace(selIdx, conn_p); } @@ -476,12 +483,12 @@ void ConnectionWindow::handleSendText() { emit sendDebugData(bytes); } -CANConnection* ConnectionWindow::create(CANCon::type pTye, QString pPortName, QString pDriver) +CANConnection* ConnectionWindow::create(CANCon::type pTye, QString pPortName, QString pDriver, int pSerialSpeed, int pBusSpeed) { CANConnection* conn_p; /* create connection */ - conn_p = CanConFactory::create(pTye, pPortName, pDriver); + conn_p = CanConFactory::create(pTye, pPortName, pDriver, pSerialSpeed, pBusSpeed); if(conn_p) { /* connect signal */ @@ -518,7 +525,8 @@ void ConnectionWindow::loadConnections() for(int i = 0 ; i < portNames.count() ; i++) { - CANConnection* conn_p = create((CANCon::type)devTypes[i], portNames[i], driverNames[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); /* add connection to model */ connModel->add(conn_p); } @@ -536,6 +544,8 @@ void ConnectionWindow::saveConnections() QVector portNames; QVector devTypes; QVector driverNames; + QVector serialSpeeds; + QVector busSpeeds; /* save connections */ foreach(CANConnection* conn_p, conns) diff --git a/connections/connectionwindow.h b/connections/connectionwindow.h index b13f31f..9bc3076 100644 --- a/connections/connectionwindow.h +++ b/connections/connectionwindow.h @@ -65,7 +65,7 @@ private: QVector remoteDeviceIPGVRET; QVector remoteDeviceKayak; - CANConnection* create(CANCon::type pTye, QString pPortName, QString pDriver); + CANConnection* create(CANCon::type pTye, QString pPortName, QString pDriver, int pSerialSpeed, int pBusSpeed); void populateBusDetails(int offset); void loadConnections(); void saveConnections(); diff --git a/connections/gvretserial.cpp b/connections/gvretserial.cpp index d6a122c..fb2eaed 100644 --- a/connections/gvretserial.cpp +++ b/connections/gvretserial.cpp @@ -9,7 +9,7 @@ #include "gvretserial.h" GVRetSerial::GVRetSerial(QString portName, bool useTcp) : - CANConnection(portName, "gvret", CANCon::GVRET_SERIAL, 3, 4000, true), + CANConnection(portName, "gvret", CANCon::GVRET_SERIAL, 0, 0, 3, 4000, true), mTimer(this), /*NB: set this as parent of timer to manage it from working thread */ useTcp(useTcp) { diff --git a/connections/lawicel_serial.cpp b/connections/lawicel_serial.cpp new file mode 100644 index 0000000..b312bf7 --- /dev/null +++ b/connections/lawicel_serial.cpp @@ -0,0 +1,496 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "lawicel_serial.h" +#include "utility.h" + +LAWICELSerial::LAWICELSerial(QString portName, int serialSpeed, int lawicelSpeed) : + CANConnection(portName, "LAWICEL", CANCon::LAWICEL,serialSpeed, lawicelSpeed, 3, 4000, true), + mTimer(this) /*NB: set this as parent of timer to manage it from working thread */ +{ + sendDebug("LAWICELSerial()"); + + serial = nullptr; + isAutoRestart = false; + + readSettings(); +} + + +LAWICELSerial::~LAWICELSerial() +{ + stop(); + sendDebug("~LAWICELSerial()"); +} + +void LAWICELSerial::sendDebug(const QString debugText) +{ + qDebug() << debugText; + debugOutput(debugText); +} + +void LAWICELSerial::sendToSerial(const QByteArray &bytes) +{ + if (serial == nullptr) + { + sendDebug("Attempt to write to serial port when it has not been initialized!"); + return; + } + + if (serial && !serial->isOpen()) + { + sendDebug("Attempt to write to serial port when it is not open!"); + return; + } + + QString buildDebug; + buildDebug = "Write to serial -> "; + foreach (int byt, bytes) { + byt = (unsigned char)byt; + buildDebug = buildDebug % QString::number(byt, 16) % " "; + } + sendDebug(buildDebug); + + if (serial) serial->write(bytes); +} + +void LAWICELSerial::piStarted() +{ + connectDevice(); +} + + +void LAWICELSerial::piSuspend(bool pSuspend) +{ + /* update capSuspended */ + setCapSuspended(pSuspend); + + /* flush queue if we are suspended */ + if(isCapSuspended()) + getQueue().flush(); +} + + +void LAWICELSerial::piStop() +{ + mTimer.stop(); + disconnectDevice(); +} + + +bool LAWICELSerial::piGetBusSettings(int pBusIdx, CANBus& pBus) +{ + return getBusConfig(pBusIdx, pBus); +} + + +void LAWICELSerial::piSetBusSettings(int pBusIdx, CANBus bus) +{ + /* sanity checks */ + if( (pBusIdx < 0) || pBusIdx >= getNumBuses()) + return; + + /* copy bus config */ + setBusConfig(pBusIdx, bus); +/* + qDebug() << "About to update bus " << pBusIdx << " on GVRET"; + if (pBusIdx == 0) + { + can0Baud = bus.getSpeed(); + can0Baud |= 0x80000000; + if (bus.isActive()) + { + can0Baud |= 0x40000000; + can0Enabled = true; + } + else can0Enabled = false; + + if (bus.isListenOnly()) + { + can0Baud |= 0x20000000; + can0ListenOnly = true; + } + else can0ListenOnly = false; + } +*/ + if (pBusIdx < 2) { + /* update baud rates */ + QByteArray buffer; + //sendDebug("Got signal to update bauds. 1: " + QString::number((can0Baud & 0xFFFFFFF))); + buffer[0] = (char)0xF1; //start of a command over serial + //sendToSerial(buffer); + } + +} + + +bool LAWICELSerial::piSendFrame(const CANFrame& frame) +{ + QByteArray buffer; + int c; + quint32 ID; + + //qDebug() << "Sending out lawicel frame with id " << frame.ID << " on bus " << frame.bus; + + framesRapid++; + + if (serial == nullptr) return false; + if (serial && !serial->isOpen()) return false; + //if (!isConnected) return false; + + // Doesn't make sense to send an error frame + // to an adapter + if (frame.frameId() & 0x20000000) { + return true; + } + + ID = frame.frameId(); + if (frame.hasExtendedFrameFormat()) ID |= 1u << 31; + + int idx = 0; + QString buildStr; + if (frame.hasExtendedFrameFormat()) + { + buildStr = QString::asprintf("T%08X%u", ID, frame.payload().length()); + } + else + { + buildStr = QString::asprintf("t%03X%u", ID, frame.payload().length()); + } + foreach (QChar chr, buildStr) + { + buffer[idx] = chr.toLatin1(); + idx++; + } + + for (c = 0; c < frame.payload().length(); c++) + { + QString byt = Utility::formatByteAsHex(frame.payload()[c]); + buffer[idx + (c * 2)] = byt[0].toLatin1(); + buffer[idx + (c * 2) + 1] = byt[1].toLatin1(); + } + buffer[idx + (frame.payload().length() * 2)] = 13; //CR + + sendToSerial(buffer); + + return true; +} + + + +/****************************************************************/ + +void LAWICELSerial::readSettings() +{ + QSettings settings; +} + + +void LAWICELSerial::connectDevice() +{ + QSettings settings; + + /* disconnect device */ + if(serial) + disconnectDevice(); + + /* open new device */ + + qDebug() << "Serial port: " << getPort(); + + serial = new QSerialPort(QSerialPortInfo(getPort())); + if(!serial) { + sendDebug("can't open serial port " + getPort()); + return; + } + sendDebug("Created Serial Port Object"); + + /* connect reading event */ + connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData())); + connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(serialError(QSerialPort::SerialPortError))); + + /* configure */ + serial->setBaudRate(mSerialSpeed); + serial->setDataBits(serial->Data8); + + serial->setFlowControl(serial->HardwareControl); + //serial->setFlowControl(serial->NoFlowControl); + if (!serial->open(QIODevice::ReadWrite)) + { + //sendDebug("Error returned during port opening: " + serial->errorString()); + } + else + { + //serial->setDataTerminalReady(true); //Seemingly these two lines used to be needed + //serial->setRequestToSend(true); //But, really both ends should automatically handle these + deviceConnected(); + } +} + +void LAWICELSerial::deviceConnected() +{ + sendDebug("Connecting to LAWICEL Device!"); + + QByteArray output; + + output.clear(); + output.append('C'); //close the bus in case it was already up + output.append(13); + sendToSerial(output); + + output.clear(); + output.append('S'); //configure speed of bus + switch (this->mBusData[0].mBus.getSpeed()) + { + case 10000: + output.append('0'); + break; + case 20000: + output.append('1'); + break; + case 50000: + output.append('2'); + break; + case 100000: + output.append('3'); + break; + case 125000: + output.append('4'); + break; + case 250000: + output.append('5'); + break; + case 500000: + output.append('6'); + break; + case 800000: + output.append('7'); + break; + case 1000000: + output.append('8'); + break; + default: + output.append('6'); + break; + } + output.append('\x0D'); + + sendToSerial(output); + + output.clear(); + output.append('O'); //open bus now that we set the speed + output.append(13); + + sendToSerial(output); + + mNumBuses = 1; + setStatus(CANCon::CONNECTED); + CANConStatus stats; + stats.conStatus = getStatus(); + stats.numHardwareBuses = mNumBuses; + mBusData[0].mConfigured = true; + mBusData[0].mBus.setActive(true); + //mBusData[0].mBus.setSpeed(); + emit status(stats); +} + +void LAWICELSerial::disconnectDevice() { + if (serial != nullptr) + { + if (serial->isOpen()) + { + //serial->clear(); + serial->close(); + + } + serial->disconnect(); //disconnect all signals + delete serial; + serial = nullptr; + } + + setStatus(CANCon::NOT_CONNECTED); + CANConStatus stats; + stats.conStatus = getStatus(); + stats.numHardwareBuses = mNumBuses; + emit status(stats); +} + +void LAWICELSerial::serialError(QSerialPort::SerialPortError err) +{ + QString errMessage; + bool killConnection = false; + switch (err) + { + case QSerialPort::NoError: + return; + case QSerialPort::DeviceNotFoundError: + errMessage = "Device not found error on serial"; + killConnection = true; + piStop(); + break; + case QSerialPort::PermissionError: + errMessage = "Permission error on serial port"; + killConnection = true; + piStop(); + break; + case QSerialPort::OpenError: + errMessage = "Open error on serial port"; + killConnection = true; + piStop(); + break; + case QSerialPort::ParityError: + errMessage = "Parity error on serial port"; + break; + case QSerialPort::FramingError: + errMessage = "Framing error on serial port"; + break; + case QSerialPort::BreakConditionError: + errMessage = "Break error on serial port"; + break; + case QSerialPort::WriteError: + errMessage = "Write error on serial port"; + piStop(); + break; + case QSerialPort::ReadError: + errMessage = "Read error on serial port"; + piStop(); + break; + case QSerialPort::ResourceError: + errMessage = "Serial port seems to have disappeared."; + killConnection = true; + piStop(); + break; + case QSerialPort::UnsupportedOperationError: + errMessage = "Unsupported operation on serial port"; + killConnection = true; + break; + case QSerialPort::UnknownError: + errMessage = "Beats me what happened to the serial port."; + killConnection = true; + piStop(); + break; + case QSerialPort::TimeoutError: + errMessage = "Timeout error on serial port"; + killConnection = true; + break; + case QSerialPort::NotOpenError: + errMessage = "The serial port isn't open"; + killConnection = true; + piStop(); + break; + } + /* + if (serial) + { + serial->clearError(); + serial->flush(); + serial->close(); + }*/ + if (errMessage.length() > 1) + { + sendDebug(errMessage); + } + if (killConnection) + { + qDebug() << "Shooting the serial object in the head. It deserves it."; + disconnectDevice(); + } +} + + +void LAWICELSerial::connectionTimeout() +{ + //one second after trying to connect are we actually connected? + if (CANCon::NOT_CONNECTED==getStatus()) //no? + { + //then emit the the failure signal and see if anyone cares + sendDebug("Failed to connect to LAWICEL at that com port"); + + disconnectDevice(); + connectDevice(); + } + else + { + /* start timer */ + connect(&mTimer, SIGNAL(timeout()), this, SLOT(handleTick())); + mTimer.setInterval(250); //tick four times per second + mTimer.setSingleShot(false); //keep ticking + mTimer.start(); + } +} + +void LAWICELSerial::readSerialData() +{ + QByteArray data; + unsigned char c; + QString debugBuild; + CANFrame buildFrame; + QByteArray buildData; + + if (serial) data = serial->readAll(); + + sendDebug("Got data from serial. Len = " % QString::number(data.length())); + for (int i = 0; i < data.length(); i++) + { + c = data.at(i); + //qDebug() << c << " " << QString::number(c, 16) << " " << QString(c); + debugBuild = debugBuild % QString::number(c, 16).rightJustified(2,'0') % " "; + //procRXChar(c); + mBuildLine.append(c); + if (c == 13) //all lawicel commands end in CR + { + qDebug() << "Got CR!"; + switch (mBuildLine[0].toLatin1()) + { + case 't': //standard frame + //tIIILDD + buildFrame.setFrameId(mBuildLine.mid(1, 3).toInt(nullptr, 16)); + buildFrame.isReceived = true; + buildFrame.setFrameType(QCanBusFrame::FrameType::DataFrame); + buildData.resize(mBuildLine.mid(4, 1).toInt()); + for (int c = 0; c < buildData.size(); c++) + { + buildData[c] = mBuildLine.mid(5 + (c*2), 2).toInt(nullptr, 16); + } + buildFrame.setPayload(buildData); + if (!isCapSuspended()) + { + /* get frame from queue */ + CANFrame* frame_p = getQueue().get(); + if(frame_p) { + //qDebug() << "Lawicel got frame on bus " << frame_p->bus; + /* copy frame */ + *frame_p = buildFrame; + checkTargettedFrame(buildFrame); + /* enqueue frame */ + getQueue().queue(); + } + else + qDebug() << "can't get a frame, ERROR"; + } + break; + case 'T': //extended frame + break; + } + mBuildLine.clear(); + } + } + debugOutput(debugBuild); + //qDebug() << debugBuild; +} + +//Debugging data sent from connection window. Inject it into Comm traffic. +void LAWICELSerial::debugInput(QByteArray bytes) { + sendToSerial(bytes); +} + +void LAWICELSerial::handleTick() +{ + //qDebug() << "Tick!"; +} + diff --git a/connections/lawicel_serial.h b/connections/lawicel_serial.h new file mode 100644 index 0000000..555d089 --- /dev/null +++ b/connections/lawicel_serial.h @@ -0,0 +1,66 @@ +#ifndef LAWICELSERIAL_H +#define LAWICELSERIAL_H + +#include +#include +#include +#include + +/*************/ +#include +/*************/ + +#include "canframemodel.h" +#include "canconnection.h" +#include "canconmanager.h" + +class LAWICELSerial : public CANConnection +{ + Q_OBJECT + +public: + LAWICELSerial(QString portName, int serialSpeed, int lawicelSpeed); + virtual ~LAWICELSerial(); + +protected: + + virtual void piStarted(); + virtual void piStop(); + virtual void piSetBusSettings(int pBusIdx, CANBus pBus); + virtual bool piGetBusSettings(int pBusIdx, CANBus& pBus); + virtual void piSuspend(bool pSuspend); + virtual bool piSendFrame(const CANFrame&) ; + + void disconnectDevice(); + +public slots: + void debugInput(QByteArray bytes); + +private slots: + void connectDevice(); + void connectionTimeout(); + void readSerialData(); + void serialError(QSerialPort::SerialPortError err); + void deviceConnected(); + void handleTick(); + +private: + void readSettings(); + void rebuildLocalTimeBasis(); + void sendToSerial(const QByteArray &bytes); + void sendDebug(const QString debugText); + +protected: + QTimer mTimer; + QThread mThread; + QString mBuildLine; + + bool isAutoRestart; + QSerialPort *serial; + int framesRapid; + CANFrame buildFrame; + bool can0Enabled; + bool can0ListenOnly; +}; + +#endif // LAWICELSERIAL_H diff --git a/connections/mqtt_bus.cpp b/connections/mqtt_bus.cpp index 6b05be5..bfaa04b 100644 --- a/connections/mqtt_bus.cpp +++ b/connections/mqtt_bus.cpp @@ -9,7 +9,7 @@ #include "mqtt_bus.h" MQTT_BUS::MQTT_BUS(QString topicName) : - CANConnection(topicName, "mqtt_client", CANCon::MQTT, 1, 4000, true), + CANConnection(topicName, "mqtt_client", CANCon::MQTT, 0, 0, 1, 4000, true), mTimer(this) /*NB: set this as parent of timer to manage it from working thread */ { diff --git a/connections/newconnectiondialog.cpp b/connections/newconnectiondialog.cpp index db4a721..88383b0 100644 --- a/connections/newconnectiondialog.cpp +++ b/connections/newconnectiondialog.cpp @@ -27,12 +27,17 @@ NewConnectionDialog::NewConnectionDialog(QVector* gvretips, QVectorrbRemote, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged); connect(ui->rbKayak, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged); connect(ui->rbMQTT, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged); + connect(ui->rbLawicel, &QAbstractButton::clicked, this, &NewConnectionDialog::handleConnTypeChanged); connect(ui->cbDeviceType, QOverload::of(&QComboBox::currentIndexChanged), this, &NewConnectionDialog::handleDeviceTypeChanged); connect(ui->btnOK, &QPushButton::clicked, this, &NewConnectionDialog::handleCreateButton); ui->lblDeviceType->setHidden(true); ui->cbDeviceType->setHidden(true); + ui->cbCANSpeed->setHidden(true); + ui->cbSerialSpeed->setHidden(true); + ui->lblCANSpeed->setHidden(true); + ui->lblSerialSpeed->setHidden(true); selectSerial(); qDebug() << "Was passed " << remoteDeviceIPGVRET->count() << " remote GVRET IPs"; @@ -53,6 +58,7 @@ void NewConnectionDialog::handleConnTypeChanged() { if (ui->rbGVRET->isChecked()) selectSerial(); if (ui->rbSocketCAN->isChecked()) selectSocketCan(); + if (ui->rbLawicel->isChecked()) selectLawicel(); if (ui->rbRemote->isChecked()) selectRemote(); if (ui->rbKayak->isChecked()) selectKayak(); if (ui->rbMQTT->isChecked()) selectMQTT(); @@ -68,12 +74,57 @@ void NewConnectionDialog::handleDeviceTypeChanged() ui->cbPort->addItem(canDevices[i].name()); } +void NewConnectionDialog::selectLawicel() +{ + ui->lPort->setText("Serial Port:"); + + ui->lblDeviceType->setHidden(true); + ui->cbDeviceType->setHidden(true); + + ui->cbCANSpeed->setHidden(false); + ui->cbSerialSpeed->setHidden(false); + ui->lblCANSpeed->setHidden(false); + ui->lblSerialSpeed->setHidden(false); + + ui->cbPort->clear(); + ports = QSerialPortInfo::availablePorts(); + + for (int i = 0; i < ports.count(); i++) + ui->cbPort->addItem(ports[i].portName()); + + if (ui->cbCANSpeed->count() == 0) + { + ui->cbCANSpeed->addItem("10000"); + ui->cbCANSpeed->addItem("20000"); + ui->cbCANSpeed->addItem("50000"); + ui->cbCANSpeed->addItem("100000"); + ui->cbCANSpeed->addItem("125000"); + ui->cbCANSpeed->addItem("250000"); + ui->cbCANSpeed->addItem("500000"); + ui->cbCANSpeed->addItem("1000000"); + } + if (ui->cbSerialSpeed->count() == 0) + { + ui->cbSerialSpeed->addItem("115200"); + ui->cbSerialSpeed->addItem("150000"); + ui->cbSerialSpeed->addItem("250000"); + ui->cbSerialSpeed->addItem("500000"); + ui->cbSerialSpeed->addItem("1000000"); + ui->cbSerialSpeed->addItem("2000000"); + } + +} + void NewConnectionDialog::selectSerial() { ui->lPort->setText("Serial Port:"); ui->lblDeviceType->setHidden(true); ui->cbDeviceType->setHidden(true); + ui->cbCANSpeed->setHidden(true); + ui->cbSerialSpeed->setHidden(true); + ui->lblCANSpeed->setHidden(true); + ui->lblSerialSpeed->setHidden(true); ui->cbPort->clear(); ports = QSerialPortInfo::availablePorts(); @@ -87,6 +138,10 @@ void NewConnectionDialog::selectSocketCan() ui->lPort->setText("Port:"); ui->lblDeviceType->setHidden(false); ui->cbDeviceType->setHidden(false); + ui->cbCANSpeed->setHidden(true); + ui->cbSerialSpeed->setHidden(true); + ui->lblCANSpeed->setHidden(true); + ui->lblSerialSpeed->setHidden(true); ui->cbDeviceType->clear(); QStringList plugins; @@ -99,8 +154,14 @@ void NewConnectionDialog::selectSocketCan() void NewConnectionDialog::selectRemote() { ui->lPort->setText("IP Address:"); + ui->lblDeviceType->setHidden(true); ui->cbDeviceType->setHidden(true); + ui->cbCANSpeed->setHidden(true); + ui->cbSerialSpeed->setHidden(true); + ui->lblCANSpeed->setHidden(true); + ui->lblSerialSpeed->setHidden(true); + ui->cbPort->clear(); foreach(QString pName, *remoteDeviceIPGVRET) { @@ -111,8 +172,14 @@ void NewConnectionDialog::selectRemote() void NewConnectionDialog::selectKayak() { ui->lPort->setText("Available Bus(ses):"); + ui->lblDeviceType->setHidden(true); ui->cbDeviceType->setHidden(true); + ui->cbCANSpeed->setHidden(true); + ui->cbSerialSpeed->setHidden(true); + ui->lblCANSpeed->setHidden(true); + ui->lblSerialSpeed->setHidden(true); + ui->cbPort->clear(); foreach(QString pName, *remoteBusKayak) { @@ -123,8 +190,14 @@ void NewConnectionDialog::selectKayak() void NewConnectionDialog::selectMQTT() { ui->lPort->setText("Topic Name:"); + ui->lblDeviceType->setHidden(true); ui->cbDeviceType->setHidden(true); + ui->cbCANSpeed->setHidden(true); + ui->cbSerialSpeed->setHidden(true); + ui->lblCANSpeed->setHidden(true); + ui->lblSerialSpeed->setHidden(true); + ui->cbPort->clear(); } @@ -148,6 +221,8 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt case CANCon::MQTT: ui->rbMQTT->setChecked(true); break; + case CANCon::LAWICEL: + ui->rbLawicel->setChecked(true); default: {} } @@ -157,6 +232,7 @@ void NewConnectionDialog::setPortName(CANCon::type pType, QString pPortName, QSt switch(pType) { case CANCon::GVRET_SERIAL: + case CANCon::LAWICEL: { int idx = ui->cbPort->findText(pPortName); if( idx<0 ) idx=0; @@ -201,6 +277,7 @@ QString NewConnectionDialog::getPortName() case CANCon::SERIALBUS: case CANCon::REMOTE: case CANCon::MQTT: + case CANCon::LAWICEL: return ui->cbPort->currentText(); case CANCon::KAYAK: return ui->cbPort->currentText(); @@ -220,6 +297,24 @@ QString NewConnectionDialog::getDriverName() return "N/A"; } +int NewConnectionDialog::getSerialSpeed() +{ + if (getConnectionType() == CANCon::LAWICEL) + { + return ui->cbSerialSpeed->currentText().toInt(); + } + else return 0; +} + +int NewConnectionDialog::getBusSpeed() +{ + if (getConnectionType() == CANCon::LAWICEL) + { + return ui->cbCANSpeed->currentText().toInt(); + } + else return 0; +} + CANCon::type NewConnectionDialog::getConnectionType() { if (ui->rbGVRET->isChecked()) return CANCon::GVRET_SERIAL; @@ -227,6 +322,7 @@ CANCon::type NewConnectionDialog::getConnectionType() if (ui->rbRemote->isChecked()) return CANCon::REMOTE; if (ui->rbKayak->isChecked()) return CANCon::KAYAK; if (ui->rbMQTT->isChecked()) return CANCon::MQTT; + if (ui->rbLawicel->isChecked()) return CANCon::LAWICEL; qDebug() << "getConnectionType: error"; return CANCon::NONE; diff --git a/connections/newconnectiondialog.h b/connections/newconnectiondialog.h index 1410b42..938d2d2 100644 --- a/connections/newconnectiondialog.h +++ b/connections/newconnectiondialog.h @@ -24,6 +24,8 @@ public: CANCon::type getConnectionType(); QString getPortName(); QString getDriverName(); + int getSerialSpeed(); + int getBusSpeed(); public slots: void handleConnTypeChanged(); @@ -43,6 +45,7 @@ private: void selectRemote(); void selectKayak(); void selectMQTT(); + void selectLawicel(); bool isSerialBusAvailable(); void setPortName(CANCon::type pType, QString pPortName, QString pDriver); }; diff --git a/connections/serialbusconnection.cpp b/connections/serialbusconnection.cpp index 6ea71c3..b1a2f6e 100644 --- a/connections/serialbusconnection.cpp +++ b/connections/serialbusconnection.cpp @@ -12,7 +12,7 @@ /***********************************/ SerialBusConnection::SerialBusConnection(QString portName, QString driverName) : - CANConnection(portName, driverName, CANCon::SERIALBUS, 1, 4000, true), + CANConnection(portName, driverName, CANCon::SERIALBUS,0 ,0 ,1, 4000, true), mTimer(this) /*NB: set connection as parent of timer to manage it from working thread */ { } diff --git a/connections/socketcand.cpp b/connections/socketcand.cpp index 8a6e5da..df1157d 100644 --- a/connections/socketcand.cpp +++ b/connections/socketcand.cpp @@ -10,7 +10,7 @@ #include "socketcand.h" SocketCANd::SocketCANd(QString portName) : - CANConnection(portName, "kayak", CANCon::KAYAK, 1, 4000, true), + CANConnection(portName, "kayak", CANCon::KAYAK, 0, 0, 1, 4000, true), mTimer(this) /*NB: set this as parent of timer to manage it from working thread */ { diff --git a/framefileio.cpp b/framefileio.cpp index e350671..0c11672 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -1599,10 +1599,13 @@ bool FrameFileIO::isCanalyzerASC(QString filename) //47.971842 2 248 Rx d 8 FF FF FF FF FF FF FF FF //version 8.1 //47244640.194244 CANFD 1 Rx 122 0 0 6 6 00 00 18 12 D2 00 0 0 200000 0 0 0 0 0 +//Version 9.0 +//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! @@ -1660,104 +1663,74 @@ bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) { tokens = line.simplified().split(' '); + if (tokens[0].contains("Begin")) continue; //probably begin triggerblock but we're ignoring that. + //try to do some investigating to see if this line is a CAN frame or not. The file format has many other potential line types it seems... if (tokens.length() > 5) { - if (tokens[5].at(0) >= '0' && tokens[5].at(0) <= '9') + if (tokens[3].toUpper().startsWith("RX") || tokens[3].toUpper().startsWith("TX")) { - if (tokens[3].toUpper().startsWith("RX") || tokens[3].toUpper().startsWith("TX")) + thisFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, static_cast(tokens[0].toDouble() * 1000000.0))); + if (tokens[1].contains("CAN")) //the different format I haven't seen a whole lot of, seems to support CANFD in this format { - int payloadLen = tokens[5].toInt(); - thisFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, static_cast(tokens[0].toDouble() * 1000000.0))); - - //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) - if (tokens[1].contains("CAN")) //the different format I haven't seen a whole lot of, seems to support CANFD in this format + if (tokens[4].endsWith('x')) { - thisFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, static_cast(tokens[0].toDouble() * 1000000.0))); - if (tokens[4].endsWith('x')) - { - QByteArray copied_id = tokens[4]; - copied_id.chop(1); - thisFrame.setFrameId(copied_id.toUInt(nullptr, 16)); - thisFrame.setExtendedFrameFormat(true); - } - else - { - thisFrame.setFrameId(tokens[4].toUInt(nullptr, 16)); - thisFrame.setExtendedFrameFormat(thisFrame.frameId() > 0x7FF); //some .asc files have extended IDs without 'x' - } - - int payloadLen = tokens[8].toInt(); - QByteArray bytes(payloadLen, 0); - - if (payloadLen > 8) - { - qDebug() << "Payload length too long. Original line: " << line; - return false; - } - if (payloadLen < 0) - { - qDebug() << "Payload length negative! Original line: " << line; - return false; - } - thisFrame.isReceived = tokens[3].toUpper().contains("RX"); - thisFrame.bus = tokens[2].toInt(); - if (tokens[5] == "r") thisFrame.setFrameType(QCanBusFrame::RemoteRequestFrame); - for (int d = 9; d < (9 + payloadLen); d++) - { - if (tokens.count() > d) - { - bytes[d - 9] = static_cast(tokens[d].toInt(nullptr, 16)); - } - else //expected byte wasn't there to read. Set it zero and set error flag - { - bytes[d - 9] = 0; - foundErrors = true; - qDebug() << "D:" << d << " Count:" << tokens.count(); - qDebug() << "Expected byte missing! Original line: " << line; - } - } - thisFrame.setPayload(bytes); + QByteArray copied_id = tokens[4]; + copied_id.chop(1); + thisFrame.setFrameId(copied_id.toUInt(nullptr, 16)); + thisFrame.setExtendedFrameFormat(true); } else { - if (tokens[2].endsWith('x')) - { - QByteArray copied_id = tokens[2]; - copied_id.chop(1); - thisFrame.setFrameId(copied_id.toUInt(nullptr, 16)); - thisFrame.setExtendedFrameFormat(true); - } - else - { - thisFrame.setFrameId(tokens[2].toUInt(nullptr, 16)); - thisFrame.setExtendedFrameFormat(thisFrame.frameId() > 0x7FF); //some .asc files have extended IDs without 'x' - } - QByteArray bytes(payloadLen, 0); + thisFrame.setFrameId(tokens[4].toUInt(nullptr, 16)); + thisFrame.setExtendedFrameFormat(thisFrame.frameId() > 0x7FF); //some .asc files have extended IDs without 'x' + } - if (payloadLen > 8) - { - qDebug() << "Payload length too long. Original line: " << line; - return false; - } - if (payloadLen < 0) - { - qDebug() << "Payload length negative! Original line: " << line; - return false; - } - thisFrame.isReceived = tokens[3].toUpper().contains("RX"); - thisFrame.bus = tokens[1].toInt(); - if (tokens[4] == "r") thisFrame.setFrameType(QCanBusFrame::RemoteRequestFrame); - for (int d = 6; d < (6 + payloadLen); d++) + int payloadLen = tokens[8].toInt(); + qDebug() << "PayloadLen: " << payloadLen << " Tokens: " << tokens; + QByteArray bytes(payloadLen, 0); + if (payloadLen > 8) + { + qDebug() << "Payload length too long. Original line: " << line; + return false; + } + if (payloadLen < 0) + { + qDebug() << "Payload length negative! Original line: " << line; + return false; + } + thisFrame.isReceived = tokens[3].toUpper().contains("RX"); + thisFrame.bus = tokens[2].toInt(); + + if (tokens[5].at(0) >= '0' && tokens[5].at(0) <= '9') + { + for (int d = 9; d < (9 + payloadLen); d++) { if (tokens.count() > d) { - bytes[d - 6] = static_cast(tokens[d].toInt(nullptr, 16)); + bytes[d - 9] = static_cast(tokens[d].toInt(nullptr, 16)); } else //expected byte wasn't there to read. Set it zero and set error flag { - bytes[d - 6] = 0; + bytes[d - 9] = 0; + foundErrors = true; + qDebug() << "D:" << d << " Count:" << tokens.count(); + qDebug() << "Expected byte missing! Original line: " << line; + } + } + thisFrame.setPayload(bytes); + } + else + { + for (int d = 10; d < (10 + payloadLen); d++) + { + if (tokens.count() > d) + { + bytes[d - 10] = static_cast(tokens[d].toInt(nullptr, 16)); + } + else //expected byte wasn't there to read. Set it zero and set error flag + { + bytes[d - 10] = 0; foundErrors = true; qDebug() << "D:" << d << " Count:" << tokens.count(); qDebug() << "Expected byte missing! Original line: " << line; @@ -1765,8 +1738,55 @@ bool FrameFileIO::loadCanalyzerASC(QString filename, QVector* frames) } thisFrame.setPayload(bytes); } - frames->append(thisFrame); } + else + { + int payloadLen = tokens[5].toInt(); + thisFrame.setTimeStamp(QCanBusFrame::TimeStamp(0, static_cast(tokens[0].toDouble() * 1000000.0))); + if (tokens[2].endsWith('x')) + { + QByteArray copied_id = tokens[2]; + copied_id.chop(1); + thisFrame.setFrameId(copied_id.toUInt(nullptr, 16)); + thisFrame.setExtendedFrameFormat(true); + } + else + { + thisFrame.setFrameId(tokens[2].toUInt(nullptr, 16)); + thisFrame.setExtendedFrameFormat(thisFrame.frameId() > 0x7FF); //some .asc files have extended IDs without 'x' + } + QByteArray bytes(payloadLen, 0); + + if (payloadLen > 8) + { + qDebug() << "Payload length too long. Original line: " << line; + return false; + } + if (payloadLen < 0) + { + qDebug() << "Payload length negative! Original line: " << line; + return false; + } + thisFrame.isReceived = tokens[3].toUpper().contains("RX"); + thisFrame.bus = tokens[1].toInt(); + if (tokens[4] == "r") thisFrame.setFrameType(QCanBusFrame::RemoteRequestFrame); + for (int d = 6; d < (6 + payloadLen); d++) + { + if (tokens.count() > d) + { + bytes[d - 6] = static_cast(tokens[d].toInt(nullptr, 16)); + } + else //expected byte wasn't there to read. Set it zero and set error flag + { + bytes[d - 6] = 0; + foundErrors = true; + qDebug() << "D:" << d << " Count:" << tokens.count(); + qDebug() << "Expected byte missing! Original line: " << line; + } + } + thisFrame.setPayload(bytes); + } + frames->append(thisFrame); } } } diff --git a/re/udsscanwindow.cpp b/re/udsscanwindow.cpp index 630ccab..69d6d51 100644 --- a/re/udsscanwindow.cpp +++ b/re/udsscanwindow.cpp @@ -6,6 +6,23 @@ #include "utility.h" #include "helpwindow.h" + +static QVector SCANTYPE_NAMES = { + QString("Tester Present"), + QString("Session Control"), + QString("Communication Control"), + QString("ECU Reset"), + QString("Clear DTCs"), + QString("Read DTCs"), + QString("Security Access"), + QString("Read By ID"), + QString("Read By Address"), + QString("Read Scaling Data By ID"), + QString("IO Control"), + QString("Routine Control"), + QString("Custom UDS"), +}; + UDSScanWindow::UDSScanWindow(const QVector *frames, QWidget *parent) : QDialog(parent), ui(new Ui::UDSScanWindow) @@ -21,15 +38,23 @@ UDSScanWindow::UDSScanWindow(const QVector *frames, QWidget *parent) : waitTimer->setInterval(100); udsHandler = new UDS_HANDLER; + inhibitUpdates = false; + + for (int i = 0; i < 13; i++) ui->cbScanType->addItem(SCANTYPE_NAMES[i]); + + ui->cbSessType->addItem("No Change"); + ui->cbSessType->addItem("Default"); + ui->cbSessType->addItem("Programming"); + ui->cbSessType->addItem("Extended Diag"); + ui->cbSessType->addItem("Safety Sys Diag"); connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int))); connect(udsHandler, &UDS_HANDLER::newUDSMessage, this, &UDSScanWindow::gotUDSReply); - connect(ui->btnScan, &QPushButton::clicked, this, &UDSScanWindow::scanUDS); + connect(ui->btnScanAll, &QPushButton::clicked, this, &UDSScanWindow::scanAll); + connect(ui->btnScanSelected, &QPushButton::clicked, this, &UDSScanWindow::scanSelected); connect(waitTimer, &QTimer::timeout, this, &UDSScanWindow::timeOut); connect(ui->btnSaveResults, &QPushButton::clicked, this, &UDSScanWindow::saveResults); - connect(ui->ckWildcard, &QCheckBox::toggled, this, &UDSScanWindow::wildcardToggled); - connect(ui->ckReadByAddr, &QCheckBox::toggled, this, &UDSScanWindow::readByToggled); - connect(ui->ckReadByID, &QCheckBox::toggled, this, &UDSScanWindow::readByToggled); + connect(ui->cbScanType, &QComboBox::currentTextChanged, this, &UDSScanWindow::changedScanType); connect(ui->cbAllowAdaptiveOffset, &QCheckBox::toggled, this, &UDSScanWindow::adaptiveToggled); connect(ui->spinNumBytes, SIGNAL(valueChanged(int)), this, SLOT(numBytesChanged())); connect(ui->spinLowerService, SIGNAL(valueChanged(int)), this, SLOT(checkServiceRange())); @@ -38,10 +63,23 @@ UDSScanWindow::UDSScanWindow(const QVector *frames, QWidget *parent) : connect(ui->spinUpperSubfunc, SIGNAL(valueChanged(int)), this, SLOT(checkSubFuncRange())); connect(ui->spinStartID, SIGNAL(valueChanged(int)), this, SLOT(checkIDRange())); connect(ui->spinEndID, SIGNAL(valueChanged(int)), this, SLOT(checkIDRange())); + connect(ui->btnAdd, &QPushButton::clicked, this, &UDSScanWindow::addNewScan); + connect(ui->btnDelete, &QPushButton::clicked, this, &UDSScanWindow::deleteSelectedScan); + connect(ui->btnSave, &QPushButton::clicked, this, &UDSScanWindow::saveScans); + connect(ui->btnLoad, &QPushButton::clicked, this, &UDSScanWindow::loadScans); + connect(ui->listScansToRun, &QListWidget::currentRowChanged, this, &UDSScanWindow::displayScanEntry); + connect(ui->ckShowNoReply, &QCheckBox::toggled, this, &UDSScanWindow::setNoReplyVal); + connect(ui->spinDelay, SIGNAL(valueChanged(int)), this, SLOT(setMaxDelayVal())); + connect(ui->spinIncrement, SIGNAL(valueChanged(int)), this, SLOT(setIncrementVal())); + connect(ui->spinReplyOffset, SIGNAL(valueChanged(int)), this, SLOT(setReplyOffset())); + connect(ui->cbSessType, &QComboBox::currentTextChanged, this, &UDSScanWindow::setSessType); +//not handling show no reply, max reply delay, reply offset, increment int numBuses = CANConManager::getInstance()->getNumBuses(); for (int n = 0; n < numBuses; n++) ui->cbBuses->addItem(QString::number(n)); installEventFilter(this); + + addNewScan(); } UDSScanWindow::~UDSScanWindow() @@ -71,74 +109,368 @@ bool UDSScanWindow::eventFilter(QObject *obj, QEvent *event) return false; } +void UDSScanWindow::displayScanEntry(int idx) +{ + if (inhibitUpdates) return; + if (idx == -1) return; + currEditEntry = &scanEntries.data()[idx]; + ui->spinStartID->setValue(currEditEntry->startID); + ui->spinEndID->setValue(currEditEntry->endID); + ui->spinReplyOffset->setValue(currEditEntry->idOffset); + ui->cbAllowAdaptiveOffset->setChecked(currEditEntry->bAdaptiveOffset); + ui->ckShowNoReply->setChecked(currEditEntry->bShowNoReplies); + ui->cbBuses->setCurrentIndex(currEditEntry->busToScan); + ui->spinDelay->setValue(currEditEntry->maxWaitTime); + ui->cbScanType->setCurrentIndex((int)currEditEntry->scanType); + ui->cbSessType->setCurrentIndex(currEditEntry->sessType); + ui->spinNumBytes->setValue(currEditEntry->subfunctLen); + ui->spinLowerService->setValue(currEditEntry->serviceLower); + ui->spinUpperService->setValue(currEditEntry->serviceUpper); + ui->spinLowerSubfunc->setValue(currEditEntry->subfunctLower); + ui->spinUpperSubfunc->setValue(currEditEntry->subfunctUpper); + ui->spinIncrement->setValue(currEditEntry->subfunctIncrement); +} + +void UDSScanWindow::deleteSelectedScan() +{ + int idx = ui->listScansToRun->currentRow(); + if (idx == -1) return; + scanEntries.removeAt(idx); + + QListWidgetItem *item = ui->listScansToRun->takeItem(idx); + delete item; + int rows = ui->listScansToRun->count(); + ui->listScansToRun->setCurrentRow(rows - 1); + + for (int i = 0; i < rows; i++) + { + QListWidgetItem *item = ui->listScansToRun->item(i); + item->setText(generateListDesc(i)); + } +} + +void UDSScanWindow::addNewScan() +{ + ScanEntry newEntry; + newEntry.busToScan = 0; + newEntry.scanType = SCAN_TYPE::ST_TESTER_PRESENT; + newEntry.sessType = 0; + newEntry.bAdaptiveOffset = false; + newEntry.bShowNoReplies = false; + newEntry.idOffset = 8; + newEntry.startID = 0x7E0; + newEntry.endID = 0x7E7; + newEntry.maxWaitTime = 100; + newEntry.subfunctLen = 1; + newEntry.subfunctIncrement = 1; + newEntry.subfunctLower = 0; + newEntry.subfunctUpper = 0; + newEntry.serviceLower = 1; + newEntry.serviceUpper = 1; + scanEntries.append(newEntry); + displayScanEntry(scanEntries.length() - 1); + ui->listScansToRun->addItem(generateListDesc(scanEntries.length() - 1)); + ui->listScansToRun->setCurrentRow(ui->listScansToRun->count() - 1); +} + +void UDSScanWindow::loadScans() +{ + QString filename; + QFileDialog dialog(qApp->activeWindow()); + QSettings settings; + bool result = false; + + QStringList filters; + filters.append(QString(tr("UDS Test Specification (*.uds *.UDS)"))); + + dialog.setDirectory(settings.value("FileIO/LoadSaveDirectory", dialog.directory().path()).toString()); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setNameFilters(filters); + dialog.setViewMode(QFileDialog::Detail); + dialog.setAcceptMode(QFileDialog::AcceptOpen); + + if (dialog.exec() == QDialog::Accepted) + { + filename = dialog.selectedFiles()[0]; + QFile inFile(filename); + inFile.open(QIODevice::ReadOnly); + QDataStream load(&inFile); + int fileVersion; + load >> fileVersion; + if (fileVersion != 1) + { + QMessageBox::warning(this, "Cannot Load File", "File is not a supported version.\nCannot load it!"); + return; + } + inhibitUpdates = true;; + scanEntries.clear(); + ui->listScansToRun->clear(); + int numEntries; + load >> numEntries; + for (int i = 0; i < numEntries; i++) + { + ScanEntry entry; + load >> entry.startID >> entry.endID; + load >> entry.idOffset >> entry.bAdaptiveOffset >> entry.bShowNoReplies; + load >> entry.busToScan >> entry.maxWaitTime >> entry.scanType; + load >> entry.sessType >> entry.subfunctLen >> entry.subfunctLower; + load >> entry.subfunctUpper >> entry.subfunctIncrement; + load >> entry.serviceLower >> entry.serviceUpper; + scanEntries.append(entry); + ui->listScansToRun->addItem(generateListDesc(scanEntries.length() - 1)); + } + inFile.close(); + inhibitUpdates = false; + ui->listScansToRun->setCurrentRow(ui->listScansToRun->count() - 1); + displayScanEntry(scanEntries.length() - 1); + } +} + +void UDSScanWindow::saveScans() +{ + QString filename; + QFileDialog dialog(qApp->activeWindow()); + QSettings settings; + bool result = false; + + QStringList filters; + filters.append(QString(tr("UDS Test Specification (*.uds *.UDS)"))); + + dialog.setDirectory(settings.value("FileIO/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]; + QFile outFile(filename); + outFile.open(QIODevice::WriteOnly); + QDataStream save(&outFile); + save << 1; //file version + save << scanEntries.count(); + for (int i = 0; i < scanEntries.count(); i++) + { + save << scanEntries[i].startID; + save << scanEntries[i].endID; + save << scanEntries[i].idOffset; + save << scanEntries[i].bAdaptiveOffset; + save << scanEntries[i].bShowNoReplies; + save << scanEntries[i].busToScan; + save << scanEntries[i].maxWaitTime; + save << scanEntries[i].scanType; + save << scanEntries[i].sessType; + save << scanEntries[i].subfunctLen; + save << scanEntries[i].subfunctLower; + save << scanEntries[i].subfunctUpper; + save << scanEntries[i].subfunctIncrement; + save << scanEntries[i].serviceLower; + save << scanEntries[i].serviceUpper; + } + outFile.close(); + } +} + +QString UDSScanWindow::generateListDesc(int idx) +{ + QString builder; + ScanEntry entry = scanEntries[idx]; + builder = "(0x" + QString::number(entry.startID, 16) + " - 0x" + QString::number(entry.endID, 16) + ") "; + switch (entry.scanType) + { + case ST_TESTER_PRESENT: + builder += "TP"; + break; + case ST_SESS_CTRL: + builder += "SESS"; + break; + case ST_COMM_CTRL: + builder += "COMM"; + break; + case ST_ECU_RESET: + builder += "ECU"; + break; + case ST_CLEAR_DTC: + builder += "CDTC"; + break; + case ST_READ_DTC: + builder += "RDTC"; + break; + case ST_SEC_ACCESS: + builder += "SECU"; + break; + case ST_READ_ID: + builder += "RID"; + break; + case ST_READ_ADDR: + builder += "RADD"; + break; + case ST_READ_SCALING: + builder += "RSCAL"; + break; + case ST_IO_CTRL: + builder += "IO"; + break; + case ST_ROUTINE_CTRL: + builder += "ROUT"; + break; + case ST_CUSTOM: + builder += "CUST"; + break; + } + return builder; +} + void UDSScanWindow::adaptiveToggled() { + if (inhibitUpdates) return; if (ui->cbAllowAdaptiveOffset->isChecked()) ui->spinReplyOffset->setEnabled(false); else ui->spinReplyOffset->setEnabled(true); + if (currEditEntry) currEditEntry->bAdaptiveOffset = ui->cbAllowAdaptiveOffset->isChecked(); } -void UDSScanWindow::wildcardToggled() +void UDSScanWindow::setSessType() { - bool state = false; - if (ui->ckWildcard->isChecked()) state = false; - else state = true; - - ui->ckReset->setEnabled(state); - ui->ckSecurity->setEnabled(state); - ui->ckSession->setEnabled(state); - ui->ckTester->setEnabled(state); - ui->ckReadByAddr->setEnabled(state); - ui->ckReadByID->setEnabled(state); - ui->ckReset->setChecked(false); - ui->ckSecurity->setChecked(false); - ui->ckSession->setChecked(false); - ui->ckTester->setChecked(false); - ui->ckReadByAddr->setChecked(false); - ui->ckReadByID->setChecked(false); - - ui->spinLowerService->setEnabled(!state); - ui->spinLowerSubfunc->setEnabled(!state); - ui->spinNumBytes->setEnabled(!state); - ui->spinUpperService->setEnabled(!state); - ui->spinUpperSubfunc->setEnabled(!state); + if (inhibitUpdates) return; + if (currEditEntry) currEditEntry->sessType = ui->cbSessType->currentIndex(); } -void UDSScanWindow::readByToggled() +void UDSScanWindow::setNoReplyVal() { - bool state = false; - if (ui->ckReadByAddr->isChecked() || ui->ckReadByID->isChecked()) state = true; - else state = false; + if (inhibitUpdates) return; + if (currEditEntry) currEditEntry->bShowNoReplies = ui->ckShowNoReply->isChecked(); +} - ui->spinLowerService->setEnabled(false); - ui->spinLowerSubfunc->setEnabled(state); - ui->spinNumBytes->setEnabled(state); - ui->spinUpperService->setEnabled(false); - ui->spinUpperSubfunc->setEnabled(state); +void UDSScanWindow::setMaxDelayVal() +{ + if (inhibitUpdates) return; + if (currEditEntry) currEditEntry->maxWaitTime = ui->spinDelay->value(); +} + +void UDSScanWindow::setIncrementVal() +{ + if (inhibitUpdates) return; + if (currEditEntry) currEditEntry->subfunctIncrement = ui->spinIncrement->value(); +} + +void UDSScanWindow::setReplyOffset() +{ + if (inhibitUpdates) return; + if (currEditEntry) currEditEntry->idOffset = ui->spinReplyOffset->value(); +} + +void UDSScanWindow::changedScanType() +{ + if (inhibitUpdates) return; + int idx = ui->cbScanType->currentIndex(); + if (idx == -1) + { + ui->cbScanType->setCurrentIndex(0); + idx = 0; + } + + if (currEditEntry) currEditEntry->scanType = (SCAN_TYPE)idx; + + QListWidgetItem* item = ui->listScansToRun->currentItem(); + item->setText(generateListDesc(ui->listScansToRun->currentRow())); + + switch (idx) + { + case ST_TESTER_PRESENT: + case ST_SESS_CTRL: + ui->cbSessType->setEnabled(false); + ui->spinLowerService->setEnabled(false); + ui->spinLowerSubfunc->setEnabled(false); + ui->spinUpperService->setEnabled(false); + ui->spinUpperSubfunc->setEnabled(false); + ui->spinNumBytes->setEnabled(false); + ui->spinIncrement->setEnabled(false); + ui->cbSessType->setCurrentIndex(0); + break; + case ST_COMM_CTRL: + case ST_ECU_RESET: + case ST_CLEAR_DTC: + case ST_SEC_ACCESS: + case ST_READ_DTC: + ui->cbSessType->setEnabled(true); + ui->spinLowerService->setEnabled(false); + ui->spinUpperService->setEnabled(false); + ui->spinLowerSubfunc->setEnabled(false); + ui->spinUpperSubfunc->setEnabled(false); + ui->spinNumBytes->setEnabled(false); + ui->spinIncrement->setEnabled(false); + break; + case ST_IO_CTRL: + case ST_ROUTINE_CTRL: + case ST_READ_SCALING: + ui->cbSessType->setEnabled(true); + ui->spinLowerService->setEnabled(false); + ui->spinUpperService->setEnabled(false); + ui->spinLowerSubfunc->setEnabled(true); + ui->spinUpperSubfunc->setEnabled(true); + ui->spinNumBytes->setEnabled(false); + ui->spinNumBytes->setValue(2); + ui->spinIncrement->setEnabled(true); + break; + case ST_READ_ID: + case ST_READ_ADDR: + ui->cbSessType->setEnabled(true); + ui->spinLowerService->setEnabled(false); + ui->spinUpperService->setEnabled(false); + ui->spinLowerSubfunc->setEnabled(true); + ui->spinUpperSubfunc->setEnabled(true); + ui->spinNumBytes->setEnabled(true); + ui->spinIncrement->setEnabled(true); + break; + case ST_CUSTOM: + ui->cbSessType->setEnabled(true); + ui->spinLowerService->setEnabled(true); + ui->spinUpperService->setEnabled(true); + ui->spinLowerSubfunc->setEnabled(true); + ui->spinUpperSubfunc->setEnabled(true); + ui->spinNumBytes->setEnabled(true); + ui->spinIncrement->setEnabled(true); + break; + } } void UDSScanWindow::numBytesChanged() { + if (inhibitUpdates) return; uint64_t upperBound = (1ull << (8ull * ui->spinNumBytes->value())) - 1; if (upperBound > 0x7FFFFFFF) upperBound = 0x7FFFFFFF; ui->spinUpperSubfunc->setMaximum(upperBound); + if (currEditEntry) currEditEntry->subfunctLen = ui->spinNumBytes->value(); } void UDSScanWindow::checkIDRange() { + if (inhibitUpdates) return; ui->spinStartID->setMaximum(ui->spinEndID->value()); ui->spinEndID->setMinimum(ui->spinStartID->value()); + if (currEditEntry) currEditEntry->startID = ui->spinStartID->value(); + if (currEditEntry) currEditEntry->endID = ui->spinEndID->value(); + QListWidgetItem* item = ui->listScansToRun->currentItem(); + item->setText(generateListDesc(ui->listScansToRun->currentRow())); } void UDSScanWindow::checkServiceRange() { + if (inhibitUpdates) return; ui->spinLowerService->setMaximum(ui->spinUpperService->value()); ui->spinUpperService->setMinimum(ui->spinLowerService->value()); + if (currEditEntry) currEditEntry->serviceLower = ui->spinLowerService->value(); + if (currEditEntry) currEditEntry->serviceUpper = ui->spinUpperService->value(); } void UDSScanWindow::checkSubFuncRange() { + if (inhibitUpdates) return; ui->spinLowerSubfunc->setMaximum(ui->spinUpperSubfunc->value()); ui->spinUpperSubfunc->setMinimum(ui->spinLowerSubfunc->value()); + if (currEditEntry) currEditEntry->subfunctLower = ui->spinLowerSubfunc->value(); + if (currEditEntry) currEditEntry->subfunctUpper = ui->spinUpperSubfunc->value(); } void UDSScanWindow::saveResults() @@ -196,20 +528,27 @@ void UDSScanWindow::sendOnBuses(UDS_MESSAGE test, int buses) sendingFrames.append(test); } -void UDSScanWindow::scanUDS() +void UDSScanWindow::scanAll() { - if (currentlyRunning) + sendingFrames.clear(); + for (int i = 0; i < scanEntries.count(); i++) { - waitTimer->stop(); - sendingFrames.clear(); - udsHandler->setReception(false); - udsHandler->setProcessAllIDs(false); - udsHandler->setFlowCtrl(false); - currentlyRunning = false; - ui->btnScan->setText("Start Scan"); - return; + setupScan(i); } + startScan(); +} +void UDSScanWindow::scanSelected() +{ + sendingFrames.clear(); + int idx = ui->listScansToRun->currentRow(); + if (idx < 0) return; + setupScan(idx); + startScan(); +} + +void UDSScanWindow::startScan() +{ udsHandler->setReception(true); udsHandler->setProcessAllIDs(true); udsHandler->setFlowCtrl(true); @@ -217,121 +556,173 @@ void UDSScanWindow::scanUDS() waitTimer->setInterval(ui->spinDelay->value()); ui->treeResults->clear(); - sendingFrames.clear(); nodeService = nullptr; nodeID = nullptr; nodeSubFunc = nullptr; - UDS_MESSAGE test; - int typ, id; - int startID, endID; - startID = ui->spinStartID->value(); - endID = ui->spinEndID->value(); - if (endID < startID) { - int temp = startID; - startID = endID; - endID = temp; - } - - int buses = ui->cbBuses->currentIndex(); - - for (id = startID; id <= endID; id++) - { - test.setFrameId( id ); - test.payload().clear(); - - if (ui->ckTester->isChecked()) - { - test.service = UDS_SERVICES::TESTER_PRESENT; - test.subFunc = 0; - sendOnBuses(test, buses); - } - - if (ui->ckSession->isChecked()) - { - for (typ = 1; typ < 4; typ++) //try each type of session access - { - test.service = UDS_SERVICES::DIAG_CONTROL; - test.subFunc = typ; - sendOnBuses(test, buses); - } - } - - if (ui->ckReset->isChecked()) //try to command a reset of the ECU. You're likely to know if it works. ;) - { - for (typ = 1; typ < 4; typ++) //try each type of session access - { - test.service = UDS_SERVICES::ECU_RESET; - test.subFunc = typ; - sendOnBuses(test, buses); - } - } - - if (ui->ckSecurity->isChecked()) //try to enter security mode - very likely to get a response if an ECU exists. - { - for (typ = 1; typ < 0x42; typ = typ + 2) //try each type of session access. In practice only the first 1-3 are likely to work - { - test.service = UDS_SERVICES::SECURITY_ACCESS; - test.subFunc = typ; - sendOnBuses(test, buses); - } - } - - if (ui->ckReadByAddr->isChecked()) - { - test.subFuncLen = ui->spinNumBytes->value(); - test.service = UDS_SERVICES::READ_BY_ADDR; - for (int subf = ui->spinLowerSubfunc->value(); subf <= ui->spinUpperSubfunc->value(); subf++) - { - test.subFunc = subf; - sendOnBuses(test, buses); - } - } - - if (ui->ckReadByID->isChecked()) - { - test.subFuncLen = ui->spinNumBytes->value(); - test.service = UDS_SERVICES::READ_BY_ID; - for (int subf = ui->spinLowerSubfunc->value(); subf <= ui->spinUpperSubfunc->value(); subf++) - { - test.subFunc = subf; - sendOnBuses(test, buses); - } - } - - if (ui->ckWildcard->isChecked()) - { - //preallocate the whole buffer so we don't have to keep updating the size and moving as we go. - //TODO: this shows a downside to the current method - it might potentially need to create a huge - //number of frames here. Of course, the rest of the system will do the same so I guess it's a bad idea - //any way you go to generate a 1 billion frame test. - int size = (endID - startID) * (ui->spinUpperService->value() - ui->spinLowerService->value()); - size *= (ui->spinUpperSubfunc->value() - ui->spinLowerSubfunc->value()); - sendingFrames.reserve(size); - - test.subFuncLen = ui->spinNumBytes->value(); - - for (typ = ui->spinLowerService->value(); typ <= ui->spinUpperService->value(); typ++) - { - test.service = typ; - for (int subTyp = ui->spinLowerSubfunc->value(); subTyp <= ui->spinUpperSubfunc->value(); subTyp++) - { - test.subFunc = subTyp; - sendOnBuses(test, buses); - } - } - } - } - waitTimer->start(); currIdx = -1; currentlyRunning = true; - ui->btnScan->setText("Abort Scan"); + //ui->btnScan->setText("Abort Scan"); ui->progressBar->setValue(0); ui->progressBar->setMaximum(sendingFrames.length()); + qDebug() << "Number of operations: " << sendingFrames.length(); sendNextMsg(); } +void UDSScanWindow::stopScan() +{ + waitTimer->stop(); + sendingFrames.clear(); + udsHandler->setReception(false); + udsHandler->setProcessAllIDs(false); + udsHandler->setFlowCtrl(false); + currentlyRunning = false; + //ui->btnScan->setText("Start Scan"); +} + +void UDSScanWindow::setupScan(int idx) +{ + UDS_MESSAGE test; + + qDebug() << "Generating scan id: " << idx; + + for (uint32_t id = scanEntries[idx].startID; id <= scanEntries[idx].endID; id++) + { + test.setFrameId( id ); + + if (scanEntries[idx].sessType > 0) + { + test.payload().clear(); + test.service = UDS_SERVICES::DIAG_CONTROL; + test.subFuncLen = 1; + test.subFunc = scanEntries[ idx].sessType; + sendOnBuses(test, scanEntries[idx].busToScan); + } + + test.payload().clear(); + + qDebug() << "Generating scan on ID " << QString::number(id, 16) << " of type " << scanEntries[idx].scanType; + + switch (scanEntries[idx].scanType) + { + case ST_TESTER_PRESENT: + test.service = UDS_SERVICES::TESTER_PRESENT; + test.subFuncLen = 1; + test.subFunc = 0; + sendOnBuses(test, scanEntries[idx].busToScan); + break; + case ST_SESS_CTRL: + for (int typ = 1; typ < 4; typ++) //try each type of session access + { + test.service = UDS_SERVICES::DIAG_CONTROL; + test.subFuncLen = 1; + test.subFunc = typ; + sendOnBuses(test, scanEntries[idx].busToScan); + } + break; + case ST_COMM_CTRL: + test.service = UDS_SERVICES::COMM_CTRL; + test.subFuncLen = 2; //need two bytes for this one + test.subFunc = 0x100; //00 01 on the bus = enable Rx/Tx + sendOnBuses(test, scanEntries[idx].busToScan); + break; + case ST_ECU_RESET: + for (int typ = 1; typ < 4; typ++) //try each type of session access + { + test.service = UDS_SERVICES::ECU_RESET; + test.subFuncLen = 1; + test.subFunc = typ; + sendOnBuses(test, scanEntries[idx].busToScan); + } + break; + case ST_CLEAR_DTC: + test.service = UDS_SERVICES::CLEAR_DIAG; + test.subFuncLen = 3; //DTC groups are sent as 3 bytes + test.subFunc = 0xFFFFFF; //clear everything! + sendOnBuses(test, scanEntries[idx].busToScan); + break; + case ST_READ_DTC: + test.service = UDS_SERVICES::READ_DTC; + test.subFuncLen = 2; + test.subFunc = 0x8702; //get DTCs by mask (87 is the mask) + sendOnBuses(test, scanEntries[idx].busToScan); + break; + case ST_SEC_ACCESS: + for (int typ = 1; typ < 0x42; typ = typ + 2) //try each type of session access. In practice only the first 1-3 are likely to work + { + test.service = UDS_SERVICES::SECURITY_ACCESS; + test.subFuncLen = 1; + test.subFunc = typ; + sendOnBuses(test, scanEntries[idx].busToScan); + } + break; + case ST_READ_ID: + test.subFuncLen = scanEntries[idx].subfunctLen; + test.service = UDS_SERVICES::READ_BY_ID; + for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement) + { + test.subFunc = subf; + sendOnBuses(test, scanEntries[idx].busToScan); + } + break; + case ST_READ_ADDR: + test.subFuncLen = scanEntries[idx].subfunctLen; + test.service = UDS_SERVICES::READ_BY_ADDR; + for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement) + { + test.subFunc = subf; + sendOnBuses(test, scanEntries[idx].busToScan); + } + break; + case ST_READ_SCALING: + //sub function should always be a length of 2 though. Should conform to this in the GUI + test.subFuncLen = 2; + test.service = UDS_SERVICES::READ_SCALING_ID; + for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement) + { + test.subFunc = subf; + sendOnBuses(test, scanEntries[idx].busToScan); + } + break; + case ST_IO_CTRL: + test.subFuncLen = 3; + test.service = UDS_SERVICES::IO_CTRL; + for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement) + { + test.subFunc = subf; //the upper byte will be 0 which is what we want. 0 = Return control to ECU + sendOnBuses(test, scanEntries[idx].busToScan); + } + break; + case ST_ROUTINE_CTRL: + test.subFuncLen = 3; + test.service = UDS_SERVICES::ROUTINE_CTRL; + for (int subf = scanEntries[idx].subfunctLower; subf <= scanEntries[idx].subfunctUpper; subf += scanEntries[idx].subfunctIncrement) + { + //request results of routine for all the addresses we're testing. This is the safest thing to do. + //starting or stopping arbitrary routines is super dangerous. Don't do that unless you really know + //what the hell you're doing or something really crazy might happen. + test.subFunc = (subf << 8) + 3; + sendOnBuses(test, scanEntries[idx].busToScan); + } + break; + case ST_CUSTOM: + test.subFuncLen = scanEntries[idx].subfunctLen; + + for (uint32_t typ = scanEntries[idx].serviceLower; typ <= scanEntries[idx].serviceUpper; typ++) + { + test.service = typ; + for (int subTyp = scanEntries[idx].subfunctLower; subTyp <= scanEntries[idx].subfunctUpper; subTyp += scanEntries[idx].subfunctIncrement) + { + test.subFunc = subTyp; + sendOnBuses(test, scanEntries[idx].busToScan); + } + } + break; + } + } +} + //Updates here are sent about every 1/4 second. That's fine for most windows but not this one. void UDSScanWindow::updatedFrames(int numFrames) { @@ -491,7 +882,7 @@ void UDSScanWindow::sendNextMsg() udsHandler->setReception(false); udsHandler->setProcessAllIDs(false); udsHandler->setFlowCtrl(false); - ui->btnScan->setText("Start Scan"); + //ui->btnScan->setText("Start Scan"); currentlyRunning = false; } ui->progressBar->setValue(currIdx); diff --git a/re/udsscanwindow.h b/re/udsscanwindow.h index 9ec8745..ed50e01 100644 --- a/re/udsscanwindow.h +++ b/re/udsscanwindow.h @@ -9,6 +9,44 @@ #include #include + +enum SCAN_TYPE +{ + ST_TESTER_PRESENT, + ST_SESS_CTRL, + ST_COMM_CTRL, + ST_ECU_RESET, + ST_CLEAR_DTC, + ST_READ_DTC, + ST_SEC_ACCESS, + ST_READ_ID, + ST_READ_ADDR, + ST_READ_SCALING, + ST_IO_CTRL, + ST_ROUTINE_CTRL, + ST_CUSTOM, +}; + +//stores the parameters for one scan +class ScanEntry +{ +public: + uint32_t startID, endID; + int32_t idOffset; + bool bAdaptiveOffset; + bool bShowNoReplies; + uint32_t busToScan; + uint32_t maxWaitTime; //in milliseconds + SCAN_TYPE scanType; + uint32_t sessType; + uint32_t subfunctLen; + qint64 subfunctLower; + qint64 subfunctUpper; + uint32_t subfunctIncrement; + uint32_t serviceLower; + uint32_t serviceUpper; +}; + namespace Ui { class UDSScanWindow; } @@ -24,16 +62,25 @@ public: private slots: void updatedFrames(int numFrames); void gotUDSReply(UDS_MESSAGE msg); - void scanUDS(); + void scanAll(); + void scanSelected(); void saveResults(); void timeOut(); void adaptiveToggled(); - void wildcardToggled(); - void readByToggled(); + void changedScanType(); void numBytesChanged(); void checkIDRange(); void checkServiceRange(); void checkSubFuncRange(); + void deleteSelectedScan(); + void addNewScan(); + void loadScans(); + void saveScans(); + void setNoReplyVal(); + void setMaxDelayVal(); + void setIncrementVal(); + void setReplyOffset(); + void setSessType(); private: Ui::UDSScanWindow *ui; @@ -44,9 +91,17 @@ private: QTreeWidgetItem *nodeID; QTreeWidgetItem *nodeService; QTreeWidgetItem *nodeSubFunc; + QVector scanEntries; + ScanEntry *currEditEntry; int currIdx = 0; bool currentlyRunning; + bool inhibitUpdates; + void displayScanEntry(int idx); + QString generateListDesc(int idx); + void setupScan(int idx); + void startScan(); + void stopScan(); void sendNextMsg(); void sendOnBuses(UDS_MESSAGE frame, int buses); void setupNodes(uint32_t replyID); diff --git a/ui/newconnectiondialog.ui b/ui/newconnectiondialog.ui index faa847a..a257907 100644 --- a/ui/newconnectiondialog.ui +++ b/ui/newconnectiondialog.ui @@ -6,8 +6,8 @@ 0 0 - 356 - 282 + 426 + 540 @@ -23,6 +23,30 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + socketcand (SocketCAN over Ethernet) + + + + + + + false + + + QT SerialBus Devices (SocketCAN, PeakCAN, etc) + + + + + + + MQTT Connection + + + @@ -40,27 +64,10 @@ - - - - false - + + - QT SerialBus Devices (SocketCAN, PeakCAN, etc) - - - - - - - socketcand (SocketCAN over Ethernet) - - - - - - - MQTT Connection + LAWICEL / SLCAN Serial @@ -109,6 +116,39 @@ + + + + Serial Port Speed: + + + + + + + + + + CAN Bus Speed: + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/ui/udsscanwindow.ui b/ui/udsscanwindow.ui index cae150e..b9ad262 100644 --- a/ui/udsscanwindow.ui +++ b/ui/udsscanwindow.ui @@ -6,61 +6,346 @@ 0 0 - 742 - 628 + 1053 + 624 UDS Scanner - + - - - Results - - - - - - - false - - - - 1 - - - - - - - - Save Scan Results - - - - - + - + + + Scans To Run + + + + + + + + - + - Starting ID + Add - + + + Delete + + + + + + + + + + + Load + + + + + + + Save + + + + + + + + + + + + + + + + + Starting ID + + + + + + + 0x + + + 536870911 + + + 2016 + + + 16 + + + + + + + + + + + Ending ID + + + + + + + 0x + + + 536870911 + + + 2023 + + + 16 + + + + + + + + + + + Show "No Reply" + + + true + + + + + + + Allow adaptive reply offset + + + true + + + + + + + + + + + + + Bus to scan + + + + + + + + + + Maximum reply delay (ms) + + + + + + + 50 + + + 10000 + + + 10 + + + 100 + + + + + + + Reply Offset: + + + + + + + false + + + -128 + + + 128 + + + 8 + + + + + + + + + + + + 0 + 0 + + + + Scan Type: + + + + + + + + 0 + 0 + + + + + + + + Run test in session type: + + + + + + + false + + + + + + + + + + + + + + Subfunct Len: + + + + + + + false + + + 1 + + + 4 + + + 1 + + + + + + + Lower Bound: + + + + + + + false + 0x - 536870911 + 255 + + + 16 + + + + + + + Upper Bound: + + + + + + + false + + + 0x + + + 255 - 2016 + 255 + + + 16 + + + + + + + Increment + + + + + + + false + + + 0x + + + 1024 + + + 8 16 @@ -70,24 +355,50 @@ - + - + - Ending ID + Lower Service: - + + + false + 0x - 536870911 + 255 + + + 16 + + + + + + + Upper Service: + + + + + + + false + + + 0x + + + 255 - 2023 + 255 16 @@ -97,323 +408,101 @@ - - - Show Tests with no Replies + + + + 0 + 20 + - - true + + QFrame::Plain - - - - - - - - - - Reply Offset: - - - - - - - false - - - -128 - - - 128 - - - 8 - - - - - - - Allow adaptive reply offset - - - true - - - - - - - - - - - Bus to scan - - - - - - - - - - Maximum reply delay (ms) - - - - - - - 50 - - - 10000 - - - 10 - - - 100 - - - - - - - - - - - Scan Types: - - - - - - - Tester Present - - - - - - - Session Ctrl - - - - - - - ECU Reset - - - - - - - Security Access - - - - - - - - - - - Read By ID - - - - - - - Read By Addr - - - - - - - Wildcard - - - - - - - - - - - Lower Service: - - - - - - - false - - - 0x - - - 255 - - - 16 - - - - - - - Upper Service: - - - - - - - false - - - 0x - - - 255 - - - 255 - - - 16 - - - - - - - - - - - Subfunction Bytes: - - - - - - - false - - - 1 - - + 4 - - 1 + + Qt::Horizontal - + - Lower Bound: + Results + + + Qt::AlignCenter - + + + false + + + + 1 + + + + + + false - - 0x + + 0 - - 255 + + true - - 16 - - - - - - - Upper Bound: - - - - - - + false - - 0x - - - 255 - - - 255 - - - 16 + + + + + + + + Run All Scans + + + + + + + Run Selected Scan + + + + + + + + + Save Scan Results - - - - 0 - - - - - - - Start Scan - - - spinStartID spinEndID - ckShowNoReply - spinReplyOffset - cbAllowAdaptiveOffset cbBuses spinDelay - ckTester - ckSession - ckReset - ckSecurity - ckReadByID - ckReadByAddr - ckWildcard spinLowerService spinUpperService spinNumBytes spinLowerSubfunc spinUpperSubfunc - btnScan - treeResults - btnSaveResults diff --git a/utility.h b/utility.h index 0f31ca3..47a6e96 100644 --- a/utility.h +++ b/utility.h @@ -146,6 +146,11 @@ public: return output; } + static QString formatByteAsHex(uint8_t value) + { + return QString::number(value, 16).toUpper().rightJustified(2,'0'); + } + static QVariant formatTimestamp(uint64_t timestamp) { if (!sysTimeMode) { @@ -200,17 +205,44 @@ public: return (value1 * (1.0 - samplePoint)) + (value2 * samplePoint); } + /* A unified function that can extract a signal from the (up to) 64 bits of data bytes in a CAN frame + * handles both little and big endian signals (and floats too but that's untested). + */ static int64_t processIntegerSignal(const QByteArray data, int startBit, int sigSize, bool littleEndian, bool isSigned) { - int64_t result = 0; - int bit; + uint64_t result = 0; + int bit = 0; int maxBytes = (startBit + sigSize) / 8; - if (data.size() < maxBytes) return 0; + if (data.size() < maxBytes) return 0; //if signal extends past the end of data then abort if (littleEndian) { +/* + int currByte = (startBit) / 8; + int currOffset = startBit - (currByte * 8); + int remainingBits = qMax(0, (sigSize - (8 - currOffset)) ); + int prevBits = qMin((8 - currOffset), sigSize); + result = data[currByte] >> currOffset; + result &= ( (1 << sigSize) - 1); //doesn't hurt to do this even if sigSize is way larger than the # of bits we've got so far + + while (remainingBits > 0) + { + currByte++; + if (remainingBits >= 8) //use this entire byte, its easy + { + result += data[currByte] << prevBits; + remainingBits -= 8; + prevBits += 8; + } + else //use only part of this byte. We're going to need to mask it + { + result += ((data[currByte] & ((1 << remainingBits) - 1) ) << prevBits); + remainingBits = 0; + } + }*/ + bit = startBit; for (int bitpos = 0; bitpos < sigSize; bitpos++) { @@ -222,6 +254,7 @@ public: } bit++; } + } else //motorola / big endian mode { @@ -266,6 +299,7 @@ public: */ uint64_t signedMask = ~((1ULL << sigSize) - 1); result = (-1LL & signedMask) | result; + return (int64_t)(result); } }