diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 507299d..a6492a3 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -50,7 +50,6 @@ SOURCES += main.cpp\ re/flowviewwindow.cpp \ re/frameinfowindow.cpp \ re/fuzzingwindow.cpp \ - re/isotp_handler.cpp \ re/isotp_interpreterwindow.cpp \ re/rangestatewindow.cpp \ re/udsscanwindow.cpp \ @@ -60,7 +59,10 @@ SOURCES += main.cpp\ re/graphingwindow.cpp \ re/newgraphdialog.cpp \ bisectwindow.cpp \ - signalviewerwindow.cpp + signalviewerwindow.cpp \ + bus_protocols/isotp_handler.cpp \ + bus_protocols/j1939_handler.cpp \ + bus_protocols/uds_handler.cpp HEADERS += mainwindow.h \ can_structs.h \ @@ -99,7 +101,6 @@ HEADERS += mainwindow.h \ re/flowviewwindow.h \ re/frameinfowindow.h \ re/fuzzingwindow.h \ - re/isotp_handler.h \ re/isotp_interpreterwindow.h \ re/rangestatewindow.h \ re/udsscanwindow.h \ @@ -109,7 +110,11 @@ HEADERS += mainwindow.h \ re/graphingwindow.h \ re/newgraphdialog.h \ bisectwindow.h \ - signalviewerwindow.h + signalviewerwindow.h \ + bus_protocols/isotp_handler.h \ + bus_protocols/j1939_handler.h \ + bus_protocols/uds_handler.h \ + bus_protocols/isotp_message.h FORMS += ui/candatagrid.ui \ ui/connectionwindow.ui \ diff --git a/bus_protocols/isotp_handler.cpp b/bus_protocols/isotp_handler.cpp new file mode 100644 index 0000000..67a6148 --- /dev/null +++ b/bus_protocols/isotp_handler.cpp @@ -0,0 +1,246 @@ +#include "isotp_handler.h" +#include "connections/canconmanager.h" + +ISOTP_HANDLER* ISOTP_HANDLER::mInstance = NULL; + +ISOTP_HANDLER* ISOTP_HANDLER::getInstance() +{ + if(!mInstance) + { + mInstance = new ISOTP_HANDLER(); + mInstance->modelFrames = MainWindow::getReference()->getCANFrameModel()->getListReference(); + } + + return mInstance; +} + +ISOTP_HANDLER::ISOTP_HANDLER() +{ + useExtendedAddressing = false; + isReceiving = false; +} + +void ISOTP_HANDLER::setExtendedAddressing(bool mode) +{ + useExtendedAddressing = mode; +} + +void ISOTP_HANDLER::setReception(bool mode) +{ + if (isReceiving == mode) return; + isReceiving = mode; + + if (isReceiving) + { + connect(CANConManager::getInstance(), &CANConManager::framesReceived, this, &ISOTP_HANDLER::rapidFrames); + qDebug() << "Enabling reception in ISOTP handler"; + } + else + { + disconnect(CANConManager::getInstance(), &CANConManager::framesReceived, this, &ISOTP_HANDLER::rapidFrames); + qDebug() << "Disabling reception in ISOTP handler"; + } +} + +void ISOTP_HANDLER::sendISOTPFrame(int bus, int ID, QVector data) +{ + CANFrame frame; + int currByte = 0; + int index = 0; + if (bus < 0) return; + if (bus >= CANConManager::getInstance()->getNumBuses()) return; + + if (data.length() < 8) + { + frame.bus = bus; + frame.extended = false; + frame.ID = ID; + frame.len = 8; + for (int b = 0; b < 8; b++) frame.data[b] = 0xAA; + frame.data[0] = data.length(); + for (int i = 0; i < frame.data[0]; i++) frame.data[i + 1] = data[i]; + CANConManager::getInstance()->sendFrame(frame); + } + else //need to send a multi-part ISO_TP message - no flow control possible right now. TODO - Add flow control + { + frame.bus = bus; + frame.ID = ID; + frame.extended = false; + frame.len = 8; + for (int b = 0; b < 8; b++) frame.data[b] = 0xAA; + frame.data[0] = 0x10 + (data.length() / 256); + frame.data[1] = data.length() & 0xFF; + for (int i = 0; i < 6; i++) frame.data[2 + i] = data[currByte++]; + CANConManager::getInstance()->sendFrame(frame); + while (currByte < data.length()) + { + for (int b = 0; b < 8; b++) frame.data[b] = 0xAA; + frame.data[0] = 0x20 + index; + index = (index + 1) & 0xF; + int bytesToGo = data.length() - currByte; + if (bytesToGo > 7) bytesToGo = 7; + for (int i = 0; i < bytesToGo; i++) frame.data[1 + i] = data[currByte++]; + frame.len = 8; + CANConManager::getInstance()->sendFrame(frame); + } + } +} + +//remember, negative numbers are special -1 = all frames deleted, -2 = totally new set of frames. +void ISOTP_HANDLER::updatedFrames(int numFrames) +{ + if (numFrames == -1) //all frames deleted. Kill the display + { + } + else if (numFrames == -2) //all new set of frames. Reset + { + for (int i = 0; i < modelFrames->length(); i++) processFrame(modelFrames->at(i)); + } + else //just got some new frames. See if they are relevant. + { + for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + { + //processFrame(modelFrames->at(i)); + } + } +} + +void ISOTP_HANDLER::rapidFrames(const CANConnection* conn, const QVector& pFrames) +{ + if (pFrames.length() <= 0) return; + + qDebug() << "received messages in ISOTP handler"; + + foreach(const CANFrame& thisFrame, pFrames) + { + processFrame(thisFrame); + } +} + +void ISOTP_HANDLER::processFrame(const CANFrame &frame) +{ + uint64_t ID = frame.ID; + int frameType; + int frameLen; + int ln; + //int offset; + ISOTP_MESSAGE msg; + ISOTP_MESSAGE *pMsg; + + frameType = 0; + frameLen = 0; + + if (useExtendedAddressing) + { + ID = ID << 8; + ID += frame.data[0]; + frameType = frame.data[1] >> 4; + frameLen = frame.data[1] & 0xF; + } + else + { + frameType = frame.data[0] >> 4; + frameLen = frame.data[0] & 0xF; + } + + switch(frameType) + { + case 0: //single frame message + checkNeedFlush(ID); + + if (frameLen == 0) return; //length of zero isn't valid. + if (frameLen > 6 && useExtendedAddressing) return; //impossible + if (frameLen > 7) return; + + msg.bus = frame.bus; + msg.extended = frame.extended; + msg.ID = ID; + msg.isReceived = frame.isReceived; + msg.len = frameLen; + msg.data.reserve(frameLen); + msg.timestamp = frame.timestamp; + if (useExtendedAddressing) for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+2]); + else for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+1]); + qDebug() << "Emitting single frame ISOTP message"; + emit newISOMessage(msg); + break; + case 1: //first frame of a multi-frame message + checkNeedFlush(ID); + msg.bus = frame.bus; + msg.extended = frame.extended; + msg.ID = ID; + msg.timestamp = frame.timestamp; + msg.isReceived = frame.isReceived; + frameLen = frameLen << 8; + if (useExtendedAddressing) + { + frameLen += frame.data[2]; + frameLen = frameLen & 0xFFF; + msg.len = frameLen; + msg.data.reserve(frameLen); + for (int j = 0; j < 5; j++) msg.data.append(frame.data[3 + j]); + } + else + { + frameLen += frame.data[1]; + frameLen = frameLen & 0xFFF; + msg.len = frameLen; + msg.data.reserve(frameLen); + for (int j = 0; j < 6; j++) msg.data.append(frame.data[2 + j]); + } + messageBuffer.append(msg); + break; + case 2: //subsequent frames for multi-frame messages + pMsg = NULL; + for (int i = 0; i < messageBuffer.length(); i++) + { + if (messageBuffer[i].ID == ID) + { + pMsg = &messageBuffer[i]; + break; + } + } + if (!pMsg) return; + ln = pMsg->len - pMsg->data.count(); + //offset = pMsg->data.count(); + if (useExtendedAddressing) + { + if (ln > 6) ln = 6; + for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+2]); + } + else + { + if (ln > 7) ln = 7; + for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+1]); + } + if (pMsg->len <= pMsg->data.count()) + { + qDebug() << "Emitting multiframe ISOTP message"; + emit newISOMessage(*pMsg); + } + break; + case 3: //flow control messages -ignored for now + break; + } +} + +void ISOTP_HANDLER::checkNeedFlush(uint64_t ID) +{ + for (int i = 0; i < messageBuffer.length(); i++) + { + if (messageBuffer[i].ID == ID) + { + //warning... this code will work for direct signals as emit turns into a function call + //and thus the other side will have time to do its processing before control returns + //and we delete the message on our side. But, if this code were used cross thread the + //emit would be a queued message instead and control would immediately return + //here and then the message would be deleted before being delivered. + //To fix that the message would have to be passed by value instead which I'd like to avoid. + //Thus, don't use this across threads unless you like to debug strange issues. + qDebug() << "Flushing a partial frame"; + emit newISOMessage(messageBuffer[i]); + messageBuffer.removeAt(i); + return; + } + } +} diff --git a/bus_protocols/isotp_handler.h b/bus_protocols/isotp_handler.h new file mode 100644 index 0000000..5d22c21 --- /dev/null +++ b/bus_protocols/isotp_handler.h @@ -0,0 +1,42 @@ +#ifndef ISOTP_HANDLER_H +#define ISOTP_HANDLER_H + +#include +#include +#include +#include "can_structs.h" +#include "mainwindow.h" +#include "canframemodel.h" +#include "isotp_message.h" + +class ISOTP_HANDLER : public QObject +{ + Q_OBJECT + +public: + ISOTP_HANDLER(); + void setExtendedAddressing(bool mode); + static ISOTP_HANDLER* getInstance(); + void setReception(bool mode); //set whether to accept and forward frames or not + void sendISOTPFrame(int bus, int ID, QVector data); + +public slots: + void updatedFrames(int); + void rapidFrames(const CANConnection* conn, const QVector& pFrames); + +signals: + void newISOMessage(ISOTP_MESSAGE &msg); + +private: + QList messageBuffer; + const QVector *modelFrames; + bool useExtendedAddressing; + bool isReceiving; + + void processFrame(const CANFrame &frame); + void checkNeedFlush(uint64_t ID); + + static ISOTP_HANDLER* mInstance; +}; + +#endif // ISOTP_HANDLER_H diff --git a/bus_protocols/isotp_message.h b/bus_protocols/isotp_message.h new file mode 100644 index 0000000..66ed9df --- /dev/null +++ b/bus_protocols/isotp_message.h @@ -0,0 +1,18 @@ +#ifndef ISOTP_MESSAGE_H +#define ISOTP_MESSAGE_H + +//the same as the CANFrame struct but with arbitrary data size. +class ISOTP_MESSAGE +{ +public: + uint32_t ID; + int bus; + bool extended; + bool isReceived; + int len; //# of bytes this message should have (as reported) + int actualSize; //# we actually got + QVector data; + uint64_t timestamp; +}; + +#endif // ISOTP_MESSAGE_H diff --git a/bus_protocols/j1939_handler.cpp b/bus_protocols/j1939_handler.cpp new file mode 100644 index 0000000..8b80ca6 --- /dev/null +++ b/bus_protocols/j1939_handler.cpp @@ -0,0 +1,2 @@ +#include "j1939_handler.h" + diff --git a/bus_protocols/j1939_handler.h b/bus_protocols/j1939_handler.h new file mode 100644 index 0000000..53ff315 --- /dev/null +++ b/bus_protocols/j1939_handler.h @@ -0,0 +1,21 @@ +#ifndef J1939_HANDLER_H +#define J1939_HANDLER_H + +#include +#include +#include +#include "can_structs.h" + +struct J1939ID +{ +public: + int src; + int dest; + int pgn; + int pf; + int ps; + int priority; + bool isBroadcast; +}; + +#endif // J1939_HANDLER_H diff --git a/bus_protocols/uds_handler.cpp b/bus_protocols/uds_handler.cpp new file mode 100644 index 0000000..112eb06 --- /dev/null +++ b/bus_protocols/uds_handler.cpp @@ -0,0 +1,266 @@ +#include "uds_handler.h" +#include "connections/canconmanager.h" +#include "mainwindow.h" +#include + +QVector UDS_SERVICE_DESC = { + {1, "OBDII_SHOW_CURRENT", "OBDII - Show current data"}, + {2, "OBDII_SHOW_FREEZE", "OBDII - Show freeze data"}, + {3, "OBDII_SHOW_STORED_DTC", "OBDII - Show stored DTC codes"}, + {4, "OBDII_CLEAR_DTC", "OBDII - Clear current DTC codes"}, + {5, "OBDII_TEST_O2", "OBDII - O2 sensor testing"}, + {6, "OBDII_TEST_RESULTS", "OBDII - Show emissions testing results"}, + {7, "OBDII_SHOW_PENDING_DTC", "OBDII - Show pending DTC codes"}, + {8, "OBDII_CONTROL_DEVICES", "OBDII - Control vehicle devices"}, + {9, "OBDII_VEH_INFO", "OBDII - Retrieve vehicle information"}, + {0xA, "OBDII_PERM_DTC", "OBDII - Show permanent DTC codes"}, + {0x10, "DIAG_CONTROL", "Diagnostic session control"}, + {0x11, "ECU_RESET", "Reset ECU"}, + {0x12, "GMLAN_READ_FAILURE_RECORD", "GMLAN - Read Fail"}, + {0x14, "CLEAR_DIAG", "Clear diagnostic trouble codes"}, + {0x19, "READ_DTC", "Read diagnostic trouble codes"}, + {0x1A, "GMLAN_READ_DIAGNOSTIC_ID", "GMLAN - Read diagnostics ID"}, + {0x20, "RETURN_TO_NORMAL", "Return to normal mode"}, + {0x22, "READ_BY_ID", "Read data by ID"}, + {0x23, "READ_BY_ADDR", "Read data by address"}, + {0x24, "READ_SCALING_ID", "Read scaling data by ID"}, + {0x27, "SECURITY_ACCESS", "Request security access"}, + {0x28, "COMM_CTRL", "Communication control"}, + {0x2A, "READ_DATA_ID_PERIODIC", "Read data by ID periodically"}, + {0x2C, "DYNAMIC_DATA_DEFINE", "Create dynamic data ID"}, + {0x2D, "DEFINE_PID_BY_ADDR", "Create a PID for a given memory address"}, + {0x2E, "WRITE_BY_ID", "Write data by ID"}, + {0x2F, "IO_CTRL", "Input/Output control (force)"}, + {0x31, "ROUTINE_CTRL", "Call a service routine"}, + {0x34, "REQUEST_DOWNLOAD", "Request data download (from PC to ECU)"}, + {0x35, "REQUEST_UPLOAD", "Request data upload (from ECU to PC)"}, + {0x36, "TRANSFER_DATA", "Transfer data"}, + {0x37, "REQ_TRANS_EXIT", "Request that data transfer cease"}, + {0x38, "REQ_FILE_TRANS", "Request file transfer"}, + {0x3B, "GMLAN_WRITE_DID", "GMLAN - Write DID"}, + {0x3D, "WRITE_BY_ADDR", "Write data by address"}, + {0x3E, "TESTER_PRESENT", "Tester is present"}, + {0x7F, "NEG_RESPONSE","Negative Response"}, + {0x83, "ACCESS_TIMING", "Read or write comm timing parameters"}, + {0x84, "SECURED_DATA_TRANS", "Secured data transmission"}, + {0x85, "CTRL_DTC_SETTINGS", "Control DTC settings"}, + {0x86, "RESPONSE_ON_EVENT", "Request start/stop transmission on event"}, + {0x87, "RESPONSE_LINK_CTRL", "Control comm link"}, + {0xA2, "GMLAN_REPORT_PROG_STATE", "GMLAN - Report programming state"}, + {0xA5, "GMLAN_ENTER_PROG_MODE", "GMLAN - Enter programming mode"}, + {0xA9, "GMLAN_CHECK_CODES", "GMLAN - Check codes"}, + {0xAA, "GMLAN_READ_DPID", "GMLAN - Read dynamic PID"}, + {0xAE, "GMLAN_DEVICE_CTRL", "GMLAN - Device control"}, + {0xFF, "UNKNOWN_CODE", "Unknown, likely proprietary UDS function code"} +}; + +QVector UDS_NEG_RESPONSE = +{ + {0x10, "GENERAL_REJECT", "General rejection (no other codes matched)"}, + {0x11, "SERVICE_NOTSUPP", "ECU does not support this service code"}, + {0x12, "SUBFUNCT_NOTSUPP", "ECU does not support the requested sub function"}, + {0x13, "INVALID_FORMAT", "Invalid request length or format error"}, + {0x14, "RESPONSE_TOOLONG", "Response would be too long to send"}, + {0x21, "BUSY", "ECU is busy. Try again later"}, + {0x22, "COND_INCORR", "A prereq. condition was not met"}, + {0x24, "REQ_SEQ_ERR", "Invalid sequence of requests"}, + {0x25, "SUBNET_NORESP", "ECU tried to gateway request but response timed out"}, + {0x26, "FAILURE", "A failure (indicated in a DTC) is preventing a reply"}, + {0x31, "REQ_OUTOFRANGE", "A parameter is outside of the valid range"}, + {0x33, "SECURITY_DENIED", "Security access was denied. (invalid seq or ECU not unlocked?)"}, + {0x35, "INVALID_KEY", "Key passed was invalid. Failure counter has been incremented."}, + {0x36, "EXCEED_ATTEMPTS", "Key failed too many times. ECU security access locked out"}, + {0x37, "TIMEDELAY", "Security access too soon after last attempt"}, + {0x38, "EXT_SECUR_1", "Extended security failure code 1"}, + {0x39, "EXT_SECUR_2", "Extended security failure code 2"}, + {0x3A, "EXT_SECUR_3", "Extended security failure code 3"}, + {0x3B, "EXT_SECUR_4", "Extended security failure code 4"}, + {0x3C, "EXT_SECUR_5", "Extended security failure code 5"}, + {0x3D, "EXT_SECUR_6", "Extended security failure code 6"}, + {0x3E, "EXT_SECUR_7", "Extended security failure code 7"}, + {0x3F, "EXT_SECUR_8", "Extended security failure code 8"}, + {0x40, "EXT_SECUR_9", "Extended security failure code 9"}, + {0x41, "EXT_SECUR_10", "Extended security failure code 10"}, + {0x42, "EXT_SECUR_11", "Extended security failure code 11"}, + {0x43, "EXT_SECUR_12", "Extended security failure code 12"}, + {0x44, "EXT_SECUR_13", "Extended security failure code 13"}, + {0x45, "EXT_SECUR_14", "Extended security failure code 14"}, + {0x46, "EXT_SECUR_15", "Extended security failure code 15"}, + {0x47, "EXT_SECUR_16", "Extended security failure code 16"}, + {0x48, "EXT_SECUR_17", "Extended security failure code 17"}, + {0x49, "EXT_SECUR_18", "Extended security failure code 18"}, + {0x4A, "EXT_SECUR_19", "Extended security failure code 19"}, + {0x4B, "EXT_SECUR_20", "Extended security failure code 20"}, + {0x4C, "EXT_SECUR_21", "Extended security failure code 21"}, + {0x4D, "EXT_SECUR_22", "Extended security failure code 22"}, + {0x4E, "EXT_SECUR_23", "Extended security failure code 23"}, + {0x4F, "EXT_SECUR_24", "Extended security failure code 24"}, + {0x70, "UPLOAD_DOWNLOAD", "Fault when attempting to start upload/download"}, + {0x71, "TRX_SUSPENDED", "Transfer aborting due to a fault"}, + {0x72, "GEN_PROGRAMMING", "Fault while attempting to write to ECU memory"}, + {0x73, "WRONG_BLOCK_SEQ", "Invalid sequence value detected during transfer"}, + {0x78, "RESP_PENDING", "Request successful but ECU still busy - Response pending"}, + {0x7E, "SUBFUNCT_CURRSESS", "ECU does not support this subfunction in current session type"}, + {0x7F, "SERVICE_CURRSESS", "ECU does not support this service in current session type"}, + {0x81, "RPM_TOOHIGH", "RPM is too high to execute request"}, + {0x82, "RPM_TOOLOW", "RPM is too low to execute request"}, + {0x83, "ENGINE_RUNNING", "Cannot execute request while engine is running"}, + {0x84, "ENGINE_NOTRUNNING", "Cannot execute request while engine is off"}, + {0x85, "ENG_RUNTIME_LOW", "Cannot execute request until engine has run for longer"}, + {0x86, "TEMPERATURE_HIGH", "Cannot execute request until temperature is lower"}, + {0x87, "TEMPERATURE_LOW", "Cannot execute request until temperature is higher"}, + {0x88, "SPEED_HIGH", "Cannot execute request until vehicle slows down"}, + {0x89, "SPEED_LOW", "Cannot execute request until vehicle is going faster"}, + {0x8A, "PEDAL_HIGH", "Cannot execute request until throttle is lower"}, + {0x8B, "PEDAL_LOW", "Cannot execute request until throttle is higher"}, + {0x8C, "NOT_NEUTRAL", "Cannot execute request until transmission is in neutral"}, + {0x8D, "NOT_INGEAR", "Cannot execute request until vehicle is in gear"}, + {0x8F, "BRAKE_NOTPRESSED", "Cannot execute request until brake pedal is pressed (Hold down)"}, + {0x90, "NOT_PARK", "Cannot execute request until vehicle is in park"}, + {0x91, "CLUTCH_LOCKED", "Cannot execute request while clutch is locked"}, + {0x92, "VOLTAGE_HIGH", "Cannot execute request until voltage is lower"}, + {0x93, "VOLTAGE_LOW", "Cannot execute request until voltage is higher"}, +}; + +UDS_MESSAGE::UDS_MESSAGE() +{ + subFunc = 0; + service = 0; + subFuncLen = 1; + extended = false; + isErrorReply = false; +} + +UDS_HANDLER* UDS_HANDLER::mInstance = NULL; + +UDS_HANDLER* UDS_HANDLER::getInstance() +{ + if(!mInstance) + { + mInstance = new UDS_HANDLER(); + mInstance->modelFrames = MainWindow::getReference()->getCANFrameModel()->getListReference(); + } + + return mInstance; +} + +UDS_HANDLER::UDS_HANDLER() +{ + isReceiving = false; + useExtendedAddressing = false; +} + +void UDS_HANDLER::gotISOTPFrame(ISOTP_MESSAGE &msg) +{ + qDebug() << "UDS handler got ISOTP frame"; + UDS_MESSAGE udsMsg; + udsMsg.bus = msg.bus; + udsMsg.extended = msg.extended; + udsMsg.ID = msg.ID; + udsMsg.isReceived = msg.isReceived; + udsMsg.timestamp = msg.timestamp; + udsMsg.actualSize = msg.actualSize; + udsMsg.len = msg.len; + if (msg.data.length() > 0) { + udsMsg.service = msg.data.at(0); + if (udsMsg.service == 0x7F) + { + udsMsg.isErrorReply = true; + if (msg.data.length() > 1) + { + udsMsg.service = msg.data.at(1); + if (msg.data.length() > 2) udsMsg.subFunc = msg.data.at(2); + else return; + } + else return; + udsMsg.data = msg.data.mid(2, -1); //don't copy error byte nor service byte + } + else + { + udsMsg.isErrorReply = false; + if (msg.data.length() > 1) udsMsg.subFunc = msg.data.at(1); + udsMsg.data = msg.data.mid(1, -1); //don't copy service byte + } + } + else return; + + emit newUDSMessage(udsMsg); +} + +void UDS_HANDLER::setReception(bool mode) +{ + if (isReceiving == mode) return; + + isReceiving = mode; + + if (isReceiving) + { + connect(ISOTP_HANDLER::getInstance(), SIGNAL(newISOMessage(ISOTP_MESSAGE&)), this, SLOT(gotISOTPFrame(ISOTP_MESSAGE&))); + ISOTP_HANDLER::getInstance()->setReception(true); //must enable ISOTP reception too. + qDebug() << "Enabling reception of ISO-TP frames in UDS handler"; + } + else + { + disconnect(ISOTP_HANDLER::getInstance(), SIGNAL(newISOMessage(ISOTP_MESSAGE&)), this, SLOT(gotISOTPFrame(ISOTP_MESSAGE&))); + //can't disable ISOTP reception because something else might be using it. + qDebug() << "Disabling reception of ISOTP frames in UDS handler"; + } +} + +void UDS_HANDLER::sendUDSFrame(const UDS_MESSAGE &msg) +{ + QVector data; + if (msg.bus < 0) return; + if (msg.bus >= CANConManager::getInstance()->getNumBuses()) return; + if (msg.service < 0 || msg.service > 0xFF) return; + + data.append(msg.service); + for (int b = msg.subFuncLen - 1; b >= 0; b--) + { + data.append((msg.subFunc >> (8 * b)) & 0xFF); + } + + data.append(msg.data); + ISOTP_HANDLER::getInstance()->sendISOTPFrame(msg.bus, msg.ID, data); + + qDebug() << "Sent UDS service: " << getServiceShortDesc(msg.service) << " on bus " << msg.bus; +} + +QString UDS_HANDLER::getServiceShortDesc(int service) +{ + foreach (CODE_STRUCT code, UDS_SERVICE_DESC) + { + if (code.code == service) return code.shortDesc; + if (code.code == (service + 0x40)) return code.shortDesc; + } + return QString(); +} + +QString UDS_HANDLER::getServiceLongDesc(int service) +{ + foreach (CODE_STRUCT code, UDS_SERVICE_DESC) + { + if (code.code == service) return code.longDesc; + if (code.code == (service + 0x40)) return code.longDesc; + } + return QString(); +} + +QString UDS_HANDLER::getNegativeResponseShort(int respCode) +{ + foreach (CODE_STRUCT code, UDS_NEG_RESPONSE) + { + if (code.code == respCode) return code.shortDesc; + } + return QString(); +} + +QString UDS_HANDLER::getNegativeResponseLong(int respCode) +{ + foreach (CODE_STRUCT code, UDS_NEG_RESPONSE) + { + if (code.code == respCode) return code.longDesc; + } + return QString(); +} + diff --git a/bus_protocols/uds_handler.h b/bus_protocols/uds_handler.h new file mode 100644 index 0000000..8e745a3 --- /dev/null +++ b/bus_protocols/uds_handler.h @@ -0,0 +1,115 @@ +#ifndef UDS_HANDLER_H +#define UDS_HANDLER_H + +#include +#include +#include +#include "can_structs.h" +#include "isotp_message.h" + +namespace UDS_SERVICES +{ + enum + { + OBDII_SHOW_CURRENT = 1, + OBDII_SHOW_FREEZE = 2, + OBDII_SHOW_STORED_DTC = 3, + OBDII_CLEAR_DTC = 4, + OBDII_TEST_O2 = 5, + OBDII_TEST_RESULTS = 6, + OBDII_SHOW_PENDING_DTC = 7, + OBDII_CONTROL_DEVICES = 8, + OBDII_VEH_INFO = 9, + OBDII_PERM_DTC = 0xA, + DIAG_CONTROL = 0x10, + ECU_RESET = 0x11, + GMLAN_READ_FAILURE_RECORD = 0x12, + CLEAR_DIAG = 0x14, + READ_DTC = 0x19, + GMLAN_READ_DIAGNOSTIC_ID = 0x1A, + RETURN_TO_NORMAL = 0x20, + READ_BY_ID = 0x22, + READ_BY_ADDR = 0x23, + READ_SCALING_ID = 0x24, + SECURITY_ACCESS = 0x27, + COMM_CTRL = 0x28, + READ_DATA_ID_PERIODIC = 0x2A, + DYNAMIC_DATA_DEFINE = 0x2C, + DEFINE_PID_BY_ADDR = 0x2D, + WRITE_BY_ID = 0x2E, + IO_CTRL = 0x2F, + ROUTINE_CTRL = 0x31, + REQUEST_DOWNLOAD = 0x34, + REQUEST_UPLOAD = 0x35, + TRANSFER_DATA = 0x36, + REQ_TRANS_EXIT = 0x37, + REQ_FILE_TRANS = 0x38, + GMLAN_WRITE_DID = 0x3B, + WRITE_BY_ADDR = 0x3D, + TESTER_PRESENT = 0x3E, + NEG_RESPONSE = 0x7F, + ACCESS_TIMING = 0x83, + SECURED_DATA_TRANS = 0x84, + CTRL_DTC_SETTINGS = 0x85, + RESPONSE_ON_EVENT = 0x86, + RESPONSE_LINK_CTRL = 0x87, + GMLAN_REPORT_PROG_STATE = 0xA2, + GMLAN_ENTER_PROG_MODE = 0xA5, + GMLAN_CHECK_CODES = 0xA9, + GMLAN_READ_DPID = 0xAA, + GMLAN_DEVICE_CTRL = 0xAE + }; +} + +struct CODE_STRUCT +{ + int code; + QString shortDesc; + QString longDesc; +}; + +class UDS_MESSAGE: public ISOTP_MESSAGE +{ +public: + int service; + int subFunc; + int subFuncLen; + bool isErrorReply; + + UDS_MESSAGE(); +}; + +class UDS_HANDLER : public QObject +{ + Q_OBJECT + +public: + UDS_HANDLER(); + void setExtendedAddressing(bool mode); + static UDS_HANDLER* getInstance(); + void setReception(bool mode); //set whether to accept and forward frames or not + void sendUDSFrame(const UDS_MESSAGE &msg); + QString getServiceShortDesc(int service); + QString getServiceLongDesc(int service); + QString getNegativeResponseShort(int respCode); + QString getNegativeResponseLong(int respCode); + +public slots: + void gotISOTPFrame(ISOTP_MESSAGE &msg); + +signals: + void newUDSMessage(UDS_MESSAGE &msg); + +private: + QList messageBuffer; + const QVector *modelFrames; + bool isReceiving; + bool useExtendedAddressing; + + void processFrame(const CANFrame &frame); + + static UDS_HANDLER* mInstance; +}; + + +#endif // UDS_HANDLER_H diff --git a/can_structs.h b/can_structs.h index 40428b5..ece14b1 100644 --- a/can_structs.h +++ b/can_structs.h @@ -32,31 +32,5 @@ public: } }; -struct J1939ID -{ -public: - int src; - int dest; - int pgn; - int pf; - int ps; - int priority; - bool isBroadcast; -}; - -//the same as the CANFrame struct but with arbitrary data size. -struct ISOTP_MESSAGE -{ -public: - uint32_t ID; - int bus; - bool extended; - bool isReceived; - int len; //# of bytes this message should have (as reported) - int actualSize; //# we actually got - QVector data; - uint64_t timestamp; -}; - #endif // CAN_STRUCTS_H diff --git a/canframemodel.cpp b/canframemodel.cpp index 5d24df6..07e2869 100644 --- a/canframemodel.cpp +++ b/canframemodel.cpp @@ -49,12 +49,12 @@ CANFrameModel::CANFrameModel(QObject *parent) if (QSysInfo::WordSize > 32) { qDebug() << "64 bit OS detected. Requesting a large preallocation"; - preallocSize = 20000000; + preallocSize = 10000000; } else //if compiling for 32 bit you can't ask for gigabytes of preallocation so tone it down. { qDebug() << "32 bit OS detected. Requesting a much restricted prealloc"; - preallocSize = 4000000; + preallocSize = 2000000; } frames.reserve(preallocSize); @@ -106,7 +106,7 @@ void CANFrameModel::setInterpetMode(bool mode) void CANFrameModel::normalizeTiming() { mutex.lock(); - if (frames.count() == 0) return; + if (frames.count() == 0) return; timeOffset = frames[0].timestamp; for (int i = 0; i < frames.count(); i++) { @@ -240,7 +240,7 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const if (!timeSeconds) return QString::number(thisFrame.timestamp); else return QString::number((double)thisFrame.timestamp / 1000000.0, 'f', 6); break; - case 1: //id + case 1: //id return Utility::formatNumber(thisFrame.ID); break; case 2: //ext @@ -281,7 +281,7 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const tempString.append(sigString); tempString.append("\n"); } - } + } } } return tempString; @@ -351,7 +351,7 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) } if (!overwriteDups) - { + { frames.append(tempFrame); if (filters[tempFrame.ID]) { @@ -366,14 +366,14 @@ void CANFrameModel::addFrame(const CANFrame& frame, bool autoRefresh = false) for (int i = 0; i < frames.count(); i++) { if (frames[i].ID == tempFrame.ID) - { + { frames.replace(i, tempFrame); found = true; break; } } if (!found) - { + { frames.append(tempFrame); if (filters[tempFrame.ID]) { @@ -409,9 +409,9 @@ void CANFrameModel::addFrames(const CANConnection*, const QVector& pFr } void CANFrameModel::sendRefresh() -{ +{ qDebug() << "Sending mass refresh"; - QVector tempContainer; + QVector tempContainer; int count = frames.count(); for (int i = 0; i < count; i++) { @@ -423,8 +423,9 @@ void CANFrameModel::sendRefresh() mutex.lock(); beginResetModel(); filteredFrames.clear(); - filteredFrames.append(tempContainer); filteredFrames.reserve(preallocSize); + filteredFrames.append(tempContainer); + lastUpdateNumFrames = filteredFrames.count(); endResetModel(); mutex.unlock(); @@ -479,7 +480,7 @@ void CANFrameModel::clearFrames() * allows for a mass import of frames into the model */ void CANFrameModel::insertFrames(const QVector &newFrames) -{ +{ //not resetting the model here because the serial worker automatically does a bulk refresh every 1/4 second //and that refresh will cause the view to update. If you do both it usually ends up thinking you have //double the number of frames. diff --git a/config.h b/config.h index 39da5a0..5d61a24 100644 --- a/config.h +++ b/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG #define CONFIG -#define VERSION 165 +#define VERSION 168 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision diff --git a/connections/canconmanager.cpp b/connections/canconmanager.cpp index 0403d72..b83f663 100644 --- a/connections/canconmanager.cpp +++ b/connections/canconmanager.cpp @@ -17,7 +17,7 @@ CANConManager* CANConManager::getInstance() CANConManager::CANConManager(QObject *parent): QObject(parent) { connect(&mTimer, SIGNAL(timeout()), this, SLOT(refreshCanList())); - mTimer.setInterval(125); /*tick 8 times a second */ + mTimer.setInterval(20); /*Tick 50 times per second to allow for good resolution in reception where needed. GUI updates *MUCH* more slowly*/ mTimer.setSingleShot(false); mTimer.start(); @@ -39,7 +39,7 @@ CANConManager::~CANConManager() void CANConManager::add(CANConnection* pConn_p) -{ +{ mConns.append(pConn_p); } @@ -196,7 +196,7 @@ bool CANConManager::sendFrames(const QList& pFrames) bool CANConManager::addTargettedFrame(int pBusId, uint32_t ID, uint32_t mask, QObject *receiver) { int tempBusVal; - int busBase = 0; + int busBase = 0; foreach (CANConnection* conn, mConns) { diff --git a/frameplaybackwindow.cpp b/frameplaybackwindow.cpp index 4145803..10e8dd4 100644 --- a/frameplaybackwindow.cpp +++ b/frameplaybackwindow.cpp @@ -64,6 +64,8 @@ FramePlaybackWindow::FramePlaybackWindow(const QVector *frames, QWidge connect(ui->btnLoadLive, SIGNAL(clicked(bool)), this, SLOT(btnLoadLive())); connect(ui->tblSequence, SIGNAL(cellPressed(int,int)), this, SLOT(seqTableCellClicked(int,int))); connect(ui->tblSequence, SIGNAL(cellChanged(int,int)), this, SLOT(seqTableCellChanged(int,int))); + connect(ui->btnLoadFilters, SIGNAL(clicked(bool)), this, SLOT(loadFilters())); + connect(ui->btnSaveFilters, SIGNAL(clicked(bool)), this, SLOT(saveFilters())); ui->listID->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->listID, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuFilters(QPoint))); diff --git a/mainwindow.cpp b/mainwindow.cpp index 9af3409..b4b9778 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -168,7 +168,7 @@ MainWindow::MainWindow(QWidget *parent) : //Automatically create the connection window so it can be updated even if we never opened it. connectionWindow = new ConnectionWindow(); - connect(this, SIGNAL(suspendCapturing(bool)), connectionWindow, SLOT(setSuspendAll(bool))); + connect(this, SIGNAL(suspendCapturing(bool)), connectionWindow, SLOT(setSuspendAll(bool))); } @@ -644,7 +644,7 @@ void MainWindow::toggleCapture() } void MainWindow::connectionStatusUpdated(int conns) -{ +{ lbStatusConnected.setText(tr("Connected to ") + QString::number(conns) + tr(" buses")); } @@ -674,6 +674,11 @@ void MainWindow::updateFileStatus() lbStatusFilename.setText(output); } +CANFrameModel* MainWindow::getCANFrameModel() +{ + return model; +} + void MainWindow::showSettingsDialog() { if (!settingsDialog) diff --git a/mainwindow.h b/mainwindow.h index 8b90e4b..4a03275 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -8,6 +8,9 @@ #include "canframemodel.h" #include "can_structs.h" #include "framefileio.h" +#include "dbc/dbchandler.h" +#include "bus_protocols/isotp_handler.h" + #include "re/graphingwindow.h" #include "re/frameinfowindow.h" #include "frameplaybackwindow.h" @@ -15,7 +18,6 @@ #include "re/flowviewwindow.h" #include "framesenderwindow.h" #include "re/filecomparatorwindow.h" -#include "dbc/dbchandler.h" #include "dbc/dbcmaineditor.h" #include "mainsettingsdialog.h" #include "firmwareuploaderwindow.h" @@ -32,6 +34,7 @@ #include "signalviewerwindow.h" class ConnectionWindow; +class ISOTP_InterpreterWindow; namespace Ui { class MainWindow; @@ -45,6 +48,7 @@ public: explicit MainWindow(QWidget *parent = 0); static QString loadedFileName; static MainWindow *getReference(); + CANFrameModel * getCANFrameModel(); ~MainWindow(); private slots: @@ -101,7 +105,7 @@ signals: void suspendCapturing(bool); //-1 = frames cleared, -2 = a new file has been loaded (so all frames are different), otherwise # of new frames - void framesUpdated(int numFrames); //something has updated the frame list + void framesUpdated(int numFrames); //something has updated the frame list (send at gui update frequency) void frameUpdateRapid(int numFrames); void settingsUpdated(); void sendCenterTimeID(int32_t ID, double timestamp); @@ -112,7 +116,7 @@ private: //canbus related data CANFrameModel *model; - DBCHandler *dbcHandler; + DBCHandler *dbcHandler; QByteArray inputBuffer; QTimer updateTimer; QTime *elapsedTime; diff --git a/re/frameinfowindow.h b/re/frameinfowindow.h index dc3ac57..67bbb44 100644 --- a/re/frameinfowindow.h +++ b/re/frameinfowindow.h @@ -6,6 +6,7 @@ #include #include #include "can_structs.h" +#include "bus_protocols/j1939_handler.h" namespace Ui { class FrameInfoWindow; diff --git a/re/isotp_handler.cpp b/re/isotp_handler.cpp deleted file mode 100644 index f679213..0000000 --- a/re/isotp_handler.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include "isotp_handler.h" - -CODE_STRUCT OBDII_FUNCTS[] = -{ - {1, "UDS_OBDII_SHOW_CURRENT", "OBDII - Show current data"}, - {2, "UDS_OBDII_SHOW_FREEZE", "OBDII - Show freeze data"}, - {3, "UDS_OBDII_SHOW_STORED_DTC", "OBDII - Show stored DTC codes"}, - {4, "UDS_OBDII_CLEAR_DTC", "OBDII - Clear current DTC codes"}, - {5, "UDS_OBDII_TEST_O2", "OBDII - O2 sensor testing"}, - {6, "UDS_OBDII_TEST_RESULTS", "OBDII - Show emissions testing results"}, - {7, "UDS_OBDII_SHOW_PENDING_DTC", "OBDII - Show pending DTC codes"}, - {8, "UDS_OBDII_CONTROL_DEVICES", "OBDII - Control vehicle devices"}, - {9, "UDS_OBDII_VEH_INFO", "OBDII - Retrieve vehicle information"}, - {0xA, "UDS_OBDII_PERM_DTC", "OBDII - Show permanent DTC codes"}, - {0xFF, "UDS_UNKNOWN", "Unknown OBDII code - Likely proprietary"} -}; - -CODE_STRUCT UDS_FUNCS[] = -{ - {0x10, "UDS_DIAG_CONTROL", "Diagnostic session control"}, - {0x11, "UDS_ECU_RESET", "Reset ECU"}, - {0x14, "UDS_CLEAR_DIAG", "Clear diagnostic trouble codes"}, - {0x19, "UDS_READ_DTC", "Read diagnostic trouble codes"}, - {0x22, "UDS_READ_BY_ID", "Read data by ID"}, - {0x23, "UDS_READ_BY_ADDR", "Read data by address"}, - {0x24, "UDS_READ_SCALING_ID", "Read scaling data by ID"}, - {0x27, "UDS_SECURITY_ACCESS", "Request security access"}, - {0x28, "UDS_COMM_CTRL", "Communication control"}, - {0x2A, "UDS_READ_DATA_ID_PERIODIC", "Read data by ID periodically"}, - {0x2C, "UDS_DYNAMIC_DATA_DEFINE", "Create dynamic data ID"}, - {0x2E, "UDS_WRITE_BY_ID", "Write data by ID"}, - {0x2F, "UDS_IO_CTRL", "Input/Output control (force)"}, - {0x31, "UDS_ROUTINE_CTRL", "Call a service routine"}, - {0x34, "UDS_REQUEST_DOWNLOAD", "Request data download (from PC to ECU)"}, - {0x35, "UDS_REQUEST_UPLOAD", "Request data upload (from ECU to PC)"}, - {0x36, "UDS_TRANSFER_DATA", "Transfer data"}, - {0x37, "UDS_REQ_TRANS_EXIT", "Request that data transfer cease"}, - {0x38, "UDS_REQ_FILE_TRANS", "Request file transfer"}, - {0x3D, "UDS_WRITE_BY_ADDR", "Write data by address"}, - {0x3E, "UDS_TESTER_PRESENT", "Tester is present"}, - {0x83, "UDS_ACCESS_TIMING", "Read or write comm timing parameters"}, - {0x84, "UDS_SECURED_DATA_TRANS", "Secured data transmission"}, - {0x85, "UDS_CTRL_DTC_SETTINGS", "Control DTC settings"}, - {0x86, "UDS_RESPONSE_ON_EVENT", "Request start/stop transmission on event"}, - {0x87, "UDS_RESPONSE_LINK_CTRL", "Control comm link"}, - {0xFF, "UDS_UNKNOWN_CODE", "Unknown, likely proprietary UDS function code"} -}; - -CODE_STRUCT UDS_NEG_RESPONSE[] = -{ - {0x10, "UDS_NEG_GENERAL_REJECT", "General rejection (no other codes matched)"}, - {0x11, "UDS_NEG_SERVICE_NOTSUPP", "ECU does not support this service code"}, - {0x12, "UDS_NEG_SUBFUNCT_NOTSUPP", "ECU does not support the requested sub function"}, - {0x13, "UDS_NEG_INVALID_FORMAT", "Invalid request length or format error"}, - {0x14, "UDS_NEG_RESPONSE_TOOLONG", "Response would be too long to send"}, - {0x21, "UDS_NEG_BUSY", "ECU is busy. Try again later"}, - {0x22, "UDS_NEG_COND_INCORR", "A prereq. condition was not met"}, - {0x24, "UDS_NEG_REQ_SEQ_ERR", "Invalid sequence of requests"}, - {0x25, "UDS_NEG_SUBNET_NORESP", "ECU tried to gateway request but response timed out"}, - {0x26, "UDS_NEG_FAILURE", "A failure (indicated in a DTC) is preventing a reply"}, - {0x31, "UDS_NEG_REQ_OUTOFRANGE", "A parameter is outside of the valid range"}, - {0x33, "UDS_NEG_SECURITY_DENIED", "Security access was denied. (invalid seq or ECU not unlocked?)"}, - {0x35, "UDS_NEG_INVALID_KEY", "Key passed was invalid. Failure counter has been incremented."}, - {0x36, "UDS_NEG_EXCEED_ATTEMPTS", "Key failed too many times. ECU security access locked out"}, - {0x37, "UDS_NEG_TIMEDELAY", "Security access too soon after last attempt"}, - {0x38, "UDS_NEG_EXT_SECUR_1", "Extended security failure code 1"}, - {0x39, "UDS_NEG_EXT_SECUR_2", "Extended security failure code 2"}, - {0x3A, "UDS_NEG_EXT_SECUR_3", "Extended security failure code 3"}, - {0x3B, "UDS_NEG_EXT_SECUR_4", "Extended security failure code 4"}, - {0x3C, "UDS_NEG_EXT_SECUR_5", "Extended security failure code 5"}, - {0x3D, "UDS_NEG_EXT_SECUR_6", "Extended security failure code 6"}, - {0x3E, "UDS_NEG_EXT_SECUR_7", "Extended security failure code 7"}, - {0x3F, "UDS_NEG_EXT_SECUR_8", "Extended security failure code 8"}, - {0x40, "UDS_NEG_EXT_SECUR_9", "Extended security failure code 9"}, - {0x41, "UDS_NEG_EXT_SECUR_10", "Extended security failure code 10"}, - {0x42, "UDS_NEG_EXT_SECUR_11", "Extended security failure code 11"}, - {0x43, "UDS_NEG_EXT_SECUR_12", "Extended security failure code 12"}, - {0x44, "UDS_NEG_EXT_SECUR_13", "Extended security failure code 13"}, - {0x45, "UDS_NEG_EXT_SECUR_14", "Extended security failure code 14"}, - {0x46, "UDS_NEG_EXT_SECUR_15", "Extended security failure code 15"}, - {0x47, "UDS_NEG_EXT_SECUR_16", "Extended security failure code 16"}, - {0x48, "UDS_NEG_EXT_SECUR_17", "Extended security failure code 17"}, - {0x49, "UDS_NEG_EXT_SECUR_18", "Extended security failure code 18"}, - {0x4A, "UDS_NEG_EXT_SECUR_19", "Extended security failure code 19"}, - {0x4B, "UDS_NEG_EXT_SECUR_20", "Extended security failure code 20"}, - {0x4C, "UDS_NEG_EXT_SECUR_21", "Extended security failure code 21"}, - {0x4D, "UDS_NEG_EXT_SECUR_22", "Extended security failure code 22"}, - {0x4E, "UDS_NEG_EXT_SECUR_23", "Extended security failure code 23"}, - {0x4F, "UDS_NEG_EXT_SECUR_24", "Extended security failure code 24"}, - {0x70, "UDS_NEG_UPLOAD_DOWNLOAD", "Fault when attempting to start upload/download"}, - {0x71, "UDS_NEG_TRX_SUSPENDED", "Transfer aborting due to a fault"}, - {0x72, "UDS_NEG_GEN_PROGRAMMING", "Fault while attempting to write to ECU memory"}, - {0x73, "UDS_NEG_WRONG_BLOCK_SEQ", "Invalid sequence value detected during transfer"}, - {0x78, "UDS_NEG_RESP_PENDING", "Request successful but ECU still busy - Response pending"}, - {0x7E, "UDS_NEG_SUBFUNCT_CURRSESS", "ECU does not support this subfunction in current session type"}, - {0x7F, "UDS_NEG_SERVICE_CURRSESS", "ECU does not support this service in current session type"}, - {0x81, "UDS_NEG_RPM_TOOHIGH", "RPM is too high to execute request"}, - {0x82, "UDS_NEG_RPM_TOOLOW", "RPM is too low to execute request"}, - {0x83, "UDS_NEG_ENGINE_RUNNING", "Cannot execute request while engine is running"}, - {0x84, "UDS_NEG_ENGINE_NOTRUNNING", "Cannot execute request while engine is off"}, - {0x85, "UDS_NEG_ENG_RUNTIME_LOW", "Cannot execute request until engine has run for longer"}, - {0x86, "UDS_NEG_TEMPERATURE_HIGH", "Cannot execute request until temperature is lower"}, - {0x87, "UDS_NEG_TEMPERATURE_LOW", "Cannot execute request until temperature is higher"}, - {0x88, "UDS_NEG_SPEED_HIGH", "Cannot execute request until vehicle slows down"}, - {0x89, "UDS_NEG_SPEED_LOW", "Cannot execute request until vehicle is going faster"}, - {0x8A, "UDS_NEG_PEDAL_HIGH", "Cannot execute request until throttle is lower"}, - {0x8B, "UDS_NEG_PEDAL_LOW", "Cannot execute request until throttle is higher"}, - {0x8C, "UDS_NEG_NOT_NEUTRAL", "Cannot execute request until transmission is in neutral"}, - {0x8D, "UDS_NEG_NOT_INGEAR", "Cannot execute request until vehicle is in gear"}, - {0x8F, "UDS_NEG_BRAKE_NOTPRESSED", "Cannot execute request until brake pedal is pressed (Hold down)"}, - {0x90, "UDS_NEG_NOT_PARK", "Cannot execute request until vehicle is in park"}, - {0x91, "UDS_NEG_CLUTCH_LOCKED", "Cannot execute request while clutch is locked"}, - {0x92, "UDS_NEG_VOLTAGE_HIGH", "Cannot execute request until voltage is lower"}, - {0x93, "UDS_NEG_VOLTAGE_LOW", "Cannot execute request until voltage is higher"}, -}; - -ISOTP_HANDLER::ISOTP_HANDLER(const QVector *frames, QObject *parent) - : QObject(parent) -{ - modelFrames = frames; - useExtendedAddressing = false; -} - -void ISOTP_HANDLER::setExtendedAddressing(bool mode) -{ - useExtendedAddressing = mode; -} - -//remember, negative numbers are special -1 = all frames deleted, -2 = totally new set of frames. -void ISOTP_HANDLER::updatedFrames(int numFrames) -{ - if (numFrames == -1) //all frames deleted. Kill the display - { - } - else if (numFrames == -2) //all new set of frames. Reset - { - for (int i = 0; i < modelFrames->length(); i++) processFrame(modelFrames->at(i)); - } - else //just got some new frames. See if they are relevant. - { - for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) - { - processFrame(modelFrames->at(i)); - } - } -} - -void ISOTP_HANDLER::processFrame(const CANFrame &frame) -{ - uint64_t ID = frame.ID; - int frameType; - int frameLen; - int ln; - //int offset; - ISOTP_MESSAGE msg; - ISOTP_MESSAGE *pMsg; - - frameType = 0; - frameLen = 0; - - if (useExtendedAddressing) - { - ID = ID << 8; - ID += frame.data[0]; - frameType = frame.data[1] >> 4; - frameLen = frame.data[1] & 0xF; - } - else - { - frameType = frame.data[0] >> 4; - frameLen = frame.data[0] & 0xF; - } - - switch(frameType) - { - case 0: //single frame message - checkNeedFlush(ID); - - if (frameLen == 0) return; //length of zero isn't valid. - if (frameLen > 6 && useExtendedAddressing) return; //impossible - if (frameLen > 7) return; - - msg.bus = frame.bus; - msg.extended = frame.extended; - msg.ID = ID; - msg.isReceived = frame.isReceived; - msg.len = frameLen; - msg.data.reserve(frameLen); - msg.timestamp = frame.timestamp; - if (useExtendedAddressing) for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+2]); - else for (int j = 0; j < frameLen; j++) msg.data.append(frame.data[j+1]); - qDebug() << "Emitting single frame ISOTP message"; - emit newISOMessage(msg); - break; - case 1: //first frame of a multi-frame message - checkNeedFlush(ID); - msg.bus = frame.bus; - msg.extended = frame.extended; - msg.ID = ID; - msg.timestamp = frame.timestamp; - msg.isReceived = frame.isReceived; - frameLen = frameLen << 8; - if (useExtendedAddressing) - { - frameLen += frame.data[2]; - frameLen = frameLen & 0xFFF; - msg.len = frameLen; - msg.data.reserve(frameLen); - for (int j = 0; j < 5; j++) msg.data.append(frame.data[3 + j]); - } - else - { - frameLen += frame.data[1]; - frameLen = frameLen & 0xFFF; - msg.len = frameLen; - msg.data.reserve(frameLen); - for (int j = 0; j < 6; j++) msg.data.append(frame.data[2 + j]); - } - messageBuffer.append(msg); - break; - case 2: //subsequent frames for multi-frame messages - pMsg = NULL; - for (int i = 0; i < messageBuffer.length(); i++) - { - if (messageBuffer[i].ID == ID) - { - pMsg = &messageBuffer[i]; - break; - } - } - if (!pMsg) return; - ln = pMsg->len - pMsg->data.count(); - //offset = pMsg->data.count(); - if (useExtendedAddressing) - { - if (ln > 6) ln = 6; - for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+2]); - } - else - { - if (ln > 7) ln = 7; - for (int j = 0; j < ln; j++) pMsg->data.append(frame.data[j+1]); - } - if (pMsg->len <= pMsg->data.count()) - { - qDebug() << "Emitting multiframe ISOTP message"; - emit newISOMessage(*pMsg); - } - break; - case 3: //flow control messages -ignored for now - break; - } -} - -void ISOTP_HANDLER::checkNeedFlush(uint64_t ID) -{ - for (int i = 0; i < messageBuffer.length(); i++) - { - if (messageBuffer[i].ID == ID) - { - //warning... this code will work for direct signals as emit turns into a function call - //and thus the other side will have time to do its processing before control returns - //and we delete the message on our side. But, if this code were used cross thread the - //emit would be a queued message instead and control would immediately return - //here and then the message would be deleted before being delivered. - //To fix that the message would have to be passed by value instead which I'd like to avoid. - //Thus, don't use this across threads unless you like to debug strange issues. - qDebug() << "Flushing a partial frame"; - emit newISOMessage(messageBuffer[i]); - messageBuffer.removeAt(i); - return; - } - } -} diff --git a/re/isotp_handler.h b/re/isotp_handler.h deleted file mode 100644 index 00c2cb8..0000000 --- a/re/isotp_handler.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef ISOTP_DECODER_H -#define ISOTP_DECODER_H - -#include -#include -#include -#include "can_structs.h" - -struct CODE_STRUCT -{ - int code; - QString shortDesc; - QString longDesc; -}; - -class ISOTP_HANDLER : public QObject -{ - Q_OBJECT - -public: - explicit ISOTP_HANDLER(const QVector *frames, QObject *parent = 0); - void setExtendedAddressing(bool mode); - -public slots: - void updatedFrames(int); - -signals: - void newISOMessage(ISOTP_MESSAGE &msg); - -private: - QList messageBuffer; - const QVector *modelFrames; - bool useExtendedAddressing; - - void processFrame(const CANFrame &frame); - void checkNeedFlush(uint64_t ID); -}; - -#endif // ISOTP_DECODER_H diff --git a/re/isotp_interpreterwindow.cpp b/re/isotp_interpreterwindow.cpp index 778a2d8..54ccd52 100644 --- a/re/isotp_interpreterwindow.cpp +++ b/re/isotp_interpreterwindow.cpp @@ -9,7 +9,7 @@ ISOTP_InterpreterWindow::ISOTP_InterpreterWindow(const QVector *frames ui->setupUi(this); modelFrames = frames; - decoder = new ISOTP_HANDLER(modelFrames); + decoder = ISOTP_HANDLER::getInstance(); connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &ISOTP_InterpreterWindow::updatedFrames); connect(MainWindow::getReference(), &MainWindow::framesUpdated, decoder, &ISOTP_HANDLER::updatedFrames); diff --git a/re/isotp_interpreterwindow.h b/re/isotp_interpreterwindow.h index ff099e1..354cef0 100644 --- a/re/isotp_interpreterwindow.h +++ b/re/isotp_interpreterwindow.h @@ -2,8 +2,10 @@ #define ISOTP_INTERPRETERWINDOW_H #include -#include "can_structs.h" -#include "isotp_handler.h" +#include "bus_protocols/isotp_handler.h" + +class ISOTP_MESSAGE; +class ISOTP_HANDLER; namespace Ui { class ISOTP_InterpreterWindow; diff --git a/re/udsscanwindow.cpp b/re/udsscanwindow.cpp index 9f36c36..8d241fa 100644 --- a/re/udsscanwindow.cpp +++ b/re/udsscanwindow.cpp @@ -2,6 +2,8 @@ #include "ui_udsscanwindow.h" #include "mainwindow.h" #include "connections/canconmanager.h" +#include "bus_protocols/uds_handler.h" +#include "utility.h" UDSScanWindow::UDSScanWindow(const QVector *frames, QWidget *parent) : QDialog(parent), @@ -16,14 +18,27 @@ UDSScanWindow::UDSScanWindow(const QVector *frames, QWidget *parent) : waitTimer = new QTimer; waitTimer->setInterval(100); + UDS_HANDLER::getInstance()->setReception(true); + connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int))); - connect(MainWindow::getReference(), SIGNAL(frameUpdateRapid(int)), this, SLOT(rapidFrames(int))); + connect(UDS_HANDLER::getInstance(), &UDS_HANDLER::newUDSMessage, this, &UDSScanWindow::gotUDSReply); connect(ui->btnScan, &QPushButton::clicked, this, &UDSScanWindow::scanUDS); 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->cbAllowAdaptiveOffset, &QCheckBox::toggled, this, &UDSScanWindow::adaptiveToggled); + connect(ui->spinNumBytes, SIGNAL(valueChanged(int)), this, SLOT(numBytesChanged())); + connect(ui->spinLowerService, SIGNAL(valueChanged(int)), this, SLOT(checkServiceRange())); + connect(ui->spinUpperService, SIGNAL(valueChanged(int)), this, SLOT(checkServiceRange())); + connect(ui->spinLowerSubfunc, SIGNAL(valueChanged(int)), this, SLOT(checkSubFuncRange())); + 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())); int numBuses = CANConManager::getInstance()->getNumBuses(); for (int n = 0; n < numBuses; n++) ui->cbBuses->addItem(QString::number(n)); - ui->cbBuses->addItem(tr("All")); } UDSScanWindow::~UDSScanWindow() @@ -33,6 +48,138 @@ UDSScanWindow::~UDSScanWindow() delete waitTimer; } +void UDSScanWindow::adaptiveToggled() +{ + if (ui->cbAllowAdaptiveOffset->isChecked()) ui->spinReplyOffset->setEnabled(false); + else ui->spinReplyOffset->setEnabled(true); +} + +void UDSScanWindow::wildcardToggled() +{ + 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); +} + +void UDSScanWindow::readByToggled() +{ + bool state = false; + if (ui->ckReadByAddr->isChecked() || ui->ckReadByID->isChecked()) state = true; + else state = false; + + ui->spinLowerService->setEnabled(false); + ui->spinLowerSubfunc->setEnabled(state); + ui->spinNumBytes->setEnabled(state); + ui->spinUpperService->setEnabled(false); + ui->spinUpperSubfunc->setEnabled(state); +} + +void UDSScanWindow::numBytesChanged() +{ + uint64_t upperBound = (1 << (8 * ui->spinNumBytes->value())) - 1; + ui->spinUpperSubfunc->setMaximum(upperBound); +} + +void UDSScanWindow::checkIDRange() +{ + ui->spinStartID->setMaximum(ui->spinEndID->value()); + ui->spinEndID->setMinimum(ui->spinStartID->value()); +} + +void UDSScanWindow::checkServiceRange() +{ + ui->spinLowerService->setMaximum(ui->spinUpperService->value()); + ui->spinUpperService->setMinimum(ui->spinLowerService->value()); +} + +void UDSScanWindow::checkSubFuncRange() +{ + ui->spinLowerSubfunc->setMaximum(ui->spinUpperSubfunc->value()); + ui->spinUpperSubfunc->setMinimum(ui->spinLowerSubfunc->value()); +} + +void UDSScanWindow::saveResults() +{ + QString filename; + QFileDialog dialog(this); + + QStringList filters; + filters.append(QString(tr("Text File (*.txt)"))); + + 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 += ".txt"; + if (dialog.selectedNameFilter() == filters[0]) + { + QFile *outFile = new QFile(filename); + + if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + { + delete outFile; + return; + } + outFile->write("UDS Scan Log:\n\n"); + dumpNode(ui->treeResults->invisibleRootItem(), outFile, 0); + + outFile->close(); + delete outFile; + } + } +} + +void UDSScanWindow::dumpNode(QTreeWidgetItem* item, QFile *file, int indent) +{ + if (indent > 0) for (int i = 0; i < (indent - 1); i++) file->write("\t"); + file->write(item->text(0).toUtf8()); + file->write("\n"); + for( int i = 0; i < item->childCount(); ++i ) + dumpNode( item->child(i), file, indent + 1 ); + if (indent == 1) file->write("\n"); +} + +void UDSScanWindow::sendOnBuses(UDS_MESSAGE test, int buses) +{ + int busList = buses; + //if (busList < ui->cbBuses->count() - 1) + //{ + test.bus = buses; + sendingFrames.append(test); + //} + /*else + { + for (int c = 0; c < ui->cbBuses->count() - 1; c++) + { + test.bus = c; + sendingFrames.append(test); + } + }*/ +} + void UDSScanWindow::scanUDS() { if (currentlyRunning) @@ -41,72 +188,107 @@ void UDSScanWindow::scanUDS() sendingFrames.clear(); currentlyRunning = false; ui->btnScan->setText("Start Scan"); + return; } - ui->listResults->clear(); - sendingFrames.clear(); + waitTimer->setInterval(ui->spinDelay->value()); - CANFrame frame; + ui->treeResults->clear(); + sendingFrames.clear(); + nodeService = NULL; + nodeID = NULL; + nodeSubFunc = NULL; + + UDS_MESSAGE test; int typ, id; int startID, endID; - startID = Utility::ParseStringToNum(ui->txtStartID->text()); - endID = Utility::ParseStringToNum(ui->txtEndID->text()); + startID = ui->spinStartID->value(); + endID = ui->spinEndID->value(); + if (endID < startID) { + int temp = startID; + startID = endID; + endID = temp; + } int buses = ui->cbBuses->currentIndex(); - //start out by sending tester present to every address to see if anyone replies for (id = startID; id <= endID; id++) { - frame.ID = id; - frame.len = 8; - frame.extended = false; - frame.data[0] = 2; - frame.data[1] = 0x3E; //tester present - frame.data[2] = 0; - frame.data[3] = 0;frame.data[4] = 0;frame.data[5] = 0; - frame.data[6] = 0;frame.data[7] = 0; + test.ID = id; + test.data.clear(); - if (buses < ui->cbBuses->count()- 1) + if (ui->ckTester->isChecked()) { - frame.bus = buses; - sendingFrames.append(frame); + test.service = UDS_SERVICES::TESTER_PRESENT; + test.subFunc = 0; + sendOnBuses(test, buses); } - else + + if (ui->ckSession->isChecked()) { - for (int c = 0; c < ui->cbBuses->count() - 1; c++) + for (typ = 1; typ < 4; typ++) //try each type of session access { - frame.bus = c; - sendingFrames.append(frame); + test.service = UDS_SERVICES::DIAG_CONTROL; + test.subFunc = typ; + sendOnBuses(test, buses); } } - } - - //then try asking for the various diagnostic session types - for (typ = 1; typ < 5; typ++) - { - for (id = startID; id <= endID; id++) + if (ui->ckReset->isChecked()) //try to command a reset of the ECU. You're likely to know if it works. ;) { - frame.ID = id; - frame.len = 8; - frame.extended = false; - frame.data[0] = 2; - frame.data[1] = 0x10; - frame.data[2] = typ; - frame.data[3] = 0;frame.data[4] = 0;frame.data[5] = 0; - frame.data[6] = 0;frame.data[7] = 0; + test.service = UDS_SERVICES::ECU_RESET; + test.subFunc = 1; + sendOnBuses(test, buses); + } - if (buses < ui->cbBuses->count()- 1) + if (ui->ckSecurity->isChecked()) //try to enter security mode - very likely to get a response if an ECU exists. + { + test.service = UDS_SERVICES::SECURITY_ACCESS; + test.subFunc = 1; + 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++) { - frame.bus = buses; - sendingFrames.append(frame); + test.subFunc = subf; + sendOnBuses(test, buses); } - else + } + + 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++) { - for (int c = 0; c < ui->cbBuses->count() - 1; c++) + 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++) { - frame.bus = c; - sendingFrames.append(frame); + test.subFunc = subTyp; + sendOnBuses(test, buses); } } } @@ -116,6 +298,8 @@ void UDSScanWindow::scanUDS() currIdx = -1; currentlyRunning = true; ui->btnScan->setText("Abort Scan"); + ui->progressBar->setValue(0); + ui->progressBar->setMaximum(sendingFrames.length()); sendNextMsg(); } @@ -133,80 +317,102 @@ void UDSScanWindow::updatedFrames(int numFrames) } } -//Updates here are nearly once per millisecond if there is heavy traffic. That's more like it! -//TODO: I really doubt this works anymore with the new connection system. This breaks the UDS scanner for now! ;( -void UDSScanWindow::rapidFrames(int numFrames) +void UDSScanWindow::gotUDSReply(UDS_MESSAGE &msg) { - CANFrame thisFrame; QString result; + QString serviceShortName; uint32_t id; int offset = ui->spinReplyOffset->value(); - CANFrame sentFrame; + UDS_MESSAGE sentFrame; bool gotReply = false; - if (numFrames > modelFrames->count()) return; - int numSending = sendingFrames.length(); if (numSending == 0) return; if (currIdx >= numSending) return; sentFrame = sendingFrames[currIdx]; - for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + id = msg.ID; + + qDebug() << "UDS message ID " << msg.ID << " service: " << msg.service << " subfunc: " << msg.subFunc; + + if ((id == (uint32_t)(sentFrame.ID + offset)) || ui->cbAllowAdaptiveOffset->isChecked()) { - if (currIdx >= numSending) return; - thisFrame = modelFrames->at(i); - id = thisFrame.ID; - - if ((id == (uint32_t)(sentFrame.ID + offset)) || ui->cbAllowAdaptiveOffset->isChecked()) + serviceShortName = UDS_HANDLER::getInstance()->getServiceShortDesc(sentFrame.service); + if (serviceShortName.length() < 3) serviceShortName = QString::number(sentFrame.service, 16); + if (msg.service == 0x40 + sendingFrames[currIdx].service) { - int temp = thisFrame.data[0] >> 4; - if (temp == 0) //single frame reply (maybe) - { - if (thisFrame.data[1] == 0x40 + sendingFrames[currIdx].data[1]) - { - result = "Request on bus " + QString::number(sentFrame.bus) + " ID: " + QString::number(sentFrame.ID, 16) + " got response to mode " - + QString::number(sentFrame.data[1], 16) - + " " + QString::number(sentFrame.data[2], 16) + " with affirmation from ID " + QString::number(id, 16) - + " on bus " + QString::number(thisFrame.bus) + "."; - gotReply = true; - } - else if ( thisFrame.data[1] == 0x7F) - { - result = "Request on bus " + QString::number(sentFrame.bus) + " ID: " + QString::number(sentFrame.ID, 16) + " got response to mode " - + QString::number(sentFrame.data[1], 16) - + " " + QString::number(sentFrame.data[2], 16) + " with an error from ID " + QString::number(id, 16) - + " on bus " + QString::number(thisFrame.bus) + "."; - gotReply = true; - } - } + setupNodes(); - if (temp == 1) //start of a multiframe reply + QTreeWidgetItem *nodePositive = new QTreeWidgetItem(); + QString reply = "POSITIVE "; + for (int i = 0; i < msg.data.length(); i++) { - if (thisFrame.data[2] == 0x40 + sendingFrames[currIdx].data[1]) - { - result = "Request on bus " + QString::number(sentFrame.bus) + " ID: " + QString::number(sentFrame.ID, 16) + " got response to mode " - + QString::number(sentFrame.data[1], 16) - + " " + QString::number(sentFrame.data[2], 16) + " with affirmation from ID " + QString::number(id, 16) - + " on bus " + QString::number(thisFrame.bus) + "."; - gotReply = true; - } - //error replies are never multiframe so the check doesn't have to be done here. + reply.append(" "); + reply.append(Utility::formatHexNum(msg.data[i])); + } + nodePositive->setText(0, reply); + nodePositive->setForeground(0, QBrush(Qt::darkGreen)); + nodeSubFunc->addChild(nodePositive); + nodeSubFunc->setForeground(0, QBrush(Qt::darkGreen)); + gotReply = true; + } + else if ( msg.isErrorReply && msg.service == sendingFrames[currIdx].service) + { + if (msg.data.length()) + { + setupNodes(); + QTreeWidgetItem *nodeNegative = new QTreeWidgetItem(); + qDebug() << ui->spinNumBytes->value(); + nodeNegative->setText(0, "NEGATIVE - " + UDS_HANDLER::getInstance()->getNegativeResponseShort(msg.data[0])); + nodeNegative->setForeground(0, QBrush(Qt::darkRed)); + nodeSubFunc->addChild(nodeNegative); + nodeSubFunc->setForeground(0, QBrush(Qt::darkRed)); + gotReply = true; } } } if (gotReply) { - ui->listResults->addItem(result); + //ui->listResults->addItem(result); sendNextMsg(); } } +void UDSScanWindow::setupNodes() +{ + QString serviceShortName = UDS_HANDLER::getInstance()->getServiceShortDesc(sendingFrames[currIdx].service); + if (serviceShortName.length() < 3) serviceShortName = QString::number(sendingFrames[currIdx].service, 16); + + if (!nodeID || nodeID->text(0) != Utility::formatHexNum(sendingFrames[currIdx].ID)) + { + nodeID = new QTreeWidgetItem(); + nodeID->setText(0, Utility::formatHexNum(sendingFrames[currIdx].ID)); + ui->treeResults->addTopLevelItem(nodeID); + nodeService = NULL; + } + if (!nodeService || nodeService->text(0) != serviceShortName) + { + nodeService = new QTreeWidgetItem(); + nodeService->setText(0, serviceShortName); + nodeID->addChild(nodeService); + } + + nodeSubFunc = new QTreeWidgetItem(); + nodeSubFunc->setText(0, Utility::formatHexNum(sendingFrames[currIdx].subFunc)); + nodeService->addChild(nodeSubFunc); +} + void UDSScanWindow::timeOut() { - QString result; - result = "Request on bus " + QString::number(sendingFrames[currIdx].bus) + " ID: " + QString::number(sendingFrames[currIdx].ID, 16) + " got no response to mode " - + QString::number(sendingFrames[currIdx].data[1], 16) + " " + QString::number(sendingFrames[currIdx].data[2], 16); - ui->listResults->addItem(result); + if (ui->ckShowNoReply->isChecked()) + { + setupNodes(); + QTreeWidgetItem *nodeNoReply = new QTreeWidgetItem(); + nodeNoReply->setText(0, "No Reply"); + nodeNoReply->setForeground(0, QBrush(Qt::gray)); + nodeSubFunc->addChild(nodeNoReply); + nodeSubFunc->setForeground(0, QBrush(Qt::gray)); + } sendNextMsg(); } @@ -216,7 +422,7 @@ void UDSScanWindow::sendNextMsg() currIdx++; if (currIdx < sendingFrames.count()) { - CANConManager::getInstance()->sendFrame(sendingFrames[currIdx]); + UDS_HANDLER::getInstance()->sendUDSFrame(sendingFrames[currIdx]); waitTimer->start(); } else @@ -225,4 +431,5 @@ void UDSScanWindow::sendNextMsg() ui->btnScan->setText("Start Scan"); currentlyRunning = false; } + ui->progressBar->setValue(currIdx); } diff --git a/re/udsscanwindow.h b/re/udsscanwindow.h index 4039345..33bea56 100644 --- a/re/udsscanwindow.h +++ b/re/udsscanwindow.h @@ -2,8 +2,12 @@ #define UDSSCANWINDOW_H #include "can_structs.h" +#include "connections/canconnection.h" +#include "bus_protocols/uds_handler.h" #include +#include +#include namespace Ui { class UDSScanWindow; @@ -19,19 +23,32 @@ public: private slots: void updatedFrames(int numFrames); - void rapidFrames(int numFrames); + void gotUDSReply(UDS_MESSAGE &msg); void scanUDS(); + void saveResults(); void timeOut(); + void adaptiveToggled(); + void wildcardToggled(); + void readByToggled(); + void numBytesChanged(); + void checkIDRange(); + void checkServiceRange(); + void checkSubFuncRange(); private: Ui::UDSScanWindow *ui; const QVector *modelFrames; QTimer *waitTimer; - QList sendingFrames; + QList sendingFrames; + QTreeWidgetItem *nodeID; + QTreeWidgetItem *nodeService; + QTreeWidgetItem *nodeSubFunc; int currIdx = 0; bool currentlyRunning; void sendNextMsg(); + void sendOnBuses(UDS_MESSAGE frame, int buses); + void setupNodes(); + void dumpNode(QTreeWidgetItem* item, QFile *file, int indent); }; - #endif // UDSSCANWINDOW_H diff --git a/ui/frameplaybackwindow.ui b/ui/frameplaybackwindow.ui index cff92d6..4055cfd 100644 --- a/ui/frameplaybackwindow.ui +++ b/ui/frameplaybackwindow.ui @@ -22,7 +22,7 @@ - + :/icons/images/NavBack.png:/icons/images/NavBack.png @@ -33,7 +33,7 @@ - + :/icons/images/PauseHS.png:/icons/images/PauseHS.png @@ -44,7 +44,7 @@ - + :/icons/images/ReverseHS.png:/icons/images/ReverseHS.png @@ -55,7 +55,7 @@ - + :/icons/images/StopHS.png:/icons/images/StopHS.png @@ -66,7 +66,7 @@ - + :/icons/images/PlayHS.png:/icons/images/PlayHS.png @@ -77,7 +77,7 @@ - + :/icons/images/NavForward.png:/icons/images/NavForward.png @@ -328,6 +328,24 @@ + + + + + + Load Filters + + + + + + + Save Filters + + + + + @@ -338,7 +356,7 @@ - + diff --git a/ui/udsscanwindow.ui b/ui/udsscanwindow.ui index 0c157bf..c305706 100644 --- a/ui/udsscanwindow.ui +++ b/ui/udsscanwindow.ui @@ -7,7 +7,7 @@ 0 0 742 - 527 + 628 @@ -22,7 +22,23 @@ - + + + false + + + + 1 + + + + + + + + Save Scan Results + + @@ -36,9 +52,18 @@ - - - 0x7E0 + + + 0x + + + 536870911 + + + 2016 + + + 16 @@ -54,53 +79,297 @@ - - - 0x7E7 + + + 0x + + + 536870911 + + + 2023 + + + 16 + + + + Show Tests with no Replies + + + true + + + - - - Reply Offset: - - + + + + + Reply Offset: + + + + + + + false + + + -128 + + + 128 + + + 8 + + + + + + + Allow adaptive reply offset + + + true + + + + - - - -128 - - - 128 - + + + + + Bus to scan + + + + + + + + + + Maximum reply delay + + + + + + + 50 + + + 10000 + + + 10 + + + 100 + + + + + + + + + + + Scan Types: + + + + + + + Tester Present + + + + + + + Session Ctrl + + + + + + + ECU Reset + + + + + + + Security Access + + + + + + + + + + + Read By ID + + + + + + + Read By Addr + + + + + + + Wildcard + + + + + + + + + + + Lower Service: + + + + + + + false + + + 255 + + + 16 + + + + + + + Upper Service: + + + + + + + false + + + 255 + + + 255 + + + 16 + + + + + + + + + + + Subfunction Bytes: + + + + + + + false + + + 1 + + + 4 + + + 1 + + + + + + + Lower Bound: + + + + + + + false + + + 255 + + + 16 + + + + + + + Upper Bound: + + + + + + + false + + + 255 + + + 255 + + + 16 + + + + + + + - 8 + 0 - - - - Allow adaptive reply offset - - - - - - - Buses to scan - - - - - -