diff --git a/README.md b/README.md index 0637898..27b7b88 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,13 @@ of this program. It can load and save in several formats: 5. Generic CSV file (ID,D0 D1 D2 D3 D4 D5 D6 D7) +Requires QScintilla library available at: + +https://www.riverbankcomputing.com/software/qscintilla/download + +Also uses QCustomPlot available at: + +http://www.qcustomplot.com/ + +However, this source code is integrated into the source for SavvyCAN and one isn't required to download it separately. + diff --git a/SavvyCAN.pro b/SavvyCAN.pro index 434e9c1..8d64e4d 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -6,11 +6,11 @@ QT += core gui -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets serialport printsupport qml CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT -CONFIG += c++11 +CONFIG += c++11 qscintilla2 TARGET = SavvyCAN TEMPLATE = app @@ -36,7 +36,17 @@ SOURCES += main.cpp\ filecomparatorwindow.cpp \ mainsettingsdialog.cpp \ firmwareuploaderwindow.cpp \ - discretestatewindow.cpp + discretestatewindow.cpp \ + connectionwindow.cpp \ + scriptingwindow.cpp \ + scriptcontainer.cpp \ + canfilter.cpp \ + rangestatewindow.cpp \ + dbc_classes.cpp \ + dbcloadsavewindow.cpp \ + fuzzingwindow.cpp \ + udsscanwindow.cpp \ + can_structs.cpp HEADERS += mainwindow.h \ can_structs.h \ @@ -61,7 +71,15 @@ HEADERS += mainwindow.h \ filecomparatorwindow.h \ mainsettingsdialog.h \ firmwareuploaderwindow.h \ - discretestatewindow.h + discretestatewindow.h \ + connectionwindow.h \ + scriptingwindow.h \ + scriptcontainer.h \ + canfilter.h \ + rangestatewindow.h \ + dbcloadsavewindow.h \ + fuzzingwindow.h \ + udsscanwindow.h FORMS += mainwindow.ui \ graphingwindow.ui \ @@ -76,7 +94,13 @@ FORMS += mainwindow.ui \ filecomparatorwindow.ui \ mainsettingsdialog.ui \ firmwareuploaderwindow.ui \ - discretestatewindow.ui + discretestatewindow.ui \ + connectionwindow.ui \ + scriptingwindow.ui \ + rangestatewindow.ui \ + dbcloadsavewindow.ui \ + fuzzingwindow.ui \ + udsscanwindow.ui DISTFILES += diff --git a/can_structs.cpp b/can_structs.cpp new file mode 100644 index 0000000..853c83d --- /dev/null +++ b/can_structs.cpp @@ -0,0 +1,2 @@ +#include "can_structs.h" + diff --git a/can_structs.h b/can_structs.h index f230964..e9668bd 100644 --- a/can_structs.h +++ b/can_structs.h @@ -1,14 +1,16 @@ #ifndef CAN_STRUCTS_H #define CAN_STRUCTS_H +#include #include -struct CANFrame +class CANFrame { public: int ID; int bus; bool extended; + bool isReceived; //did we receive this or send it? int len; unsigned char data[8]; uint64_t timestamp; @@ -25,5 +27,19 @@ public: int priority; bool isBroadcast; }; + +//the same as the CANFrame struct but with arbitrary data size. +struct ISOTP_FRAME +{ +public: + int ID; + int bus; + bool extended; + bool isReceived; + int len; + QVector data; + uint64_t timestamp; +}; + #endif // CAN_STRUCTS_H diff --git a/candatagrid.cpp b/candatagrid.cpp index 11e8a8e..12c4675 100644 --- a/candatagrid.cpp +++ b/candatagrid.cpp @@ -13,6 +13,7 @@ CANDataGrid::CANDataGrid(QWidget *parent) : memset(data, 0, 8); memset(refData, 0, 8); + memset(usedData, 0, 8); } CANDataGrid::~CANDataGrid() @@ -45,7 +46,7 @@ void CANDataGrid::paintEvent(QPaintEvent *event) int x, y; unsigned char prevByte, thisByte; bool thisBit, prevBit; - QBrush blackBrush, whiteBrush, redBrush, greenBrush; + QBrush blackBrush, whiteBrush, redBrush, greenBrush, grayBrush; QPainter painter(this); QRect viewport = painter.viewport(); @@ -60,6 +61,7 @@ void CANDataGrid::paintEvent(QPaintEvent *event) whiteBrush = QBrush(Qt::white); redBrush = QBrush(Qt::red); greenBrush = QBrush(Qt::green); + grayBrush = QBrush(QColor(230,230,230)); //the whole thing is broken up into 100 chunks which are allocated as such: //The entirety of the upper row is taken up by "BITS" @@ -109,7 +111,7 @@ void CANDataGrid::paintEvent(QPaintEvent *event) thisBit = false; prevBit = false; if ((thisByte & (1 << (7-x))) == (1 << (7-x))) thisBit = true; - if ((prevByte & (1 << (7-x))) == (1 << (7-x))) prevBit = true; + if ((prevByte & (1 << (7-x))) == (1 << (7-x))) prevBit = true; if (thisBit) { @@ -130,7 +132,8 @@ void CANDataGrid::paintEvent(QPaintEvent *event) } else { - painter.setBrush(whiteBrush); + if ((usedData[y] & (1 << (7-x))) == (1 << (7-x))) painter.setBrush(grayBrush); + else painter.setBrush(whiteBrush); } } @@ -180,3 +183,9 @@ void CANDataGrid::updateData(unsigned char *newData, bool bUpdate = true) memcpy(data, newData, 8); //on a 64 bit processor this is probably optimized to a single instruction if (bUpdate) this->update(); } + +void CANDataGrid::setUsed(unsigned char *newData, bool bUpdate = false) +{ + memcpy(usedData, newData, 8); + if (bUpdate) this->update(); +} diff --git a/candatagrid.h b/candatagrid.h index 126ad5a..f1710fe 100644 --- a/candatagrid.h +++ b/candatagrid.h @@ -17,6 +17,7 @@ public: void paintEvent(QPaintEvent *event); void setReference(unsigned char *, bool); void updateData(unsigned char *, bool); + void setUsed(unsigned char *, bool); void saveImage(QString filename, int width, int height); protected: @@ -29,6 +30,7 @@ private: Ui::CANDataGrid *ui; unsigned char refData[8]; unsigned char data[8]; + unsigned char usedData[8]; QPoint upperLeft, gridSize; }; diff --git a/canfilter.cpp b/canfilter.cpp new file mode 100644 index 0000000..dec357f --- /dev/null +++ b/canfilter.cpp @@ -0,0 +1,27 @@ +#include "canfilter.h" + +CANFilter::CANFilter() +{ + ID = 0; + mask = 0; + bus = -1; +} + +void CANFilter::setFilter(uint32_t id, uint32_t mask, int bus) +{ + this->ID = id; + this->mask = mask; + this->bus = bus; +} + +bool CANFilter::checkFilter(uint32_t id, int bus) +{ + if (bus == -1 || bus == this->bus) + { + uint32_t result = id & this->mask; + if (result == this->ID) return true; + } + return false; + +} + diff --git a/canfilter.h b/canfilter.h new file mode 100644 index 0000000..91a84e3 --- /dev/null +++ b/canfilter.h @@ -0,0 +1,19 @@ +#ifndef CANFILTER_H +#define CANFILTER_H + +#include + +class CANFilter +{ +public: + CANFilter(); + void setFilter(uint32_t id, uint32_t mask, int bus); + bool checkFilter(uint32_t id, int bus); + +private: + uint32_t ID; + uint32_t mask; + int bus; +}; + +#endif // CANFILTER_H diff --git a/canframemodel.cpp b/canframemodel.cpp index e62766c..98385cf 100644 --- a/canframemodel.cpp +++ b/canframemodel.cpp @@ -17,7 +17,7 @@ int CANFrameModel::totalFrameCount() int CANFrameModel::columnCount(const QModelIndex &index) const { Q_UNUSED(index); - return 6; + return 7; } CANFrameModel::CANFrameModel(QObject *parent) @@ -115,6 +115,8 @@ void CANFrameModel::recalcOverwrite() { if (!overwriteDups) return; //no need to do a thing if mode is disabled + qDebug() << "recalcOverwrite called in model"; + int lastUnique = 0; bool found; @@ -159,7 +161,7 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const if (!index.isValid()) return QVariant(); - if (index.row() >= filteredFrames.count()) + if (index.row() >= (filteredFrames.count())) return QVariant(); if (role == Qt::DisplayRole) { @@ -168,7 +170,7 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const { case 0: //timestamp if (!timeSeconds) return QString::number(thisFrame.timestamp); - else return QString::number(thisFrame.timestamp / 1000000.0f); + else return QString::number((double)thisFrame.timestamp / 1000000.0, 'f', 6); break; case 1: //id return Utility::formatNumber(thisFrame.ID); @@ -176,13 +178,17 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const case 2: //ext return QString::number(thisFrame.extended); break; - case 3: //bus + case 3: //direction + if (thisFrame.isReceived) return QString(tr("Rx")); + else return QString(tr("Tx")); + break; + case 4: //bus return QString::number(thisFrame.bus); break; - case 4: //len + case 5: //len return QString::number(thisFrame.len); break; - case 5: //data + case 6: //data for (int i = 0; i < thisFrame.len; i++) { tempString.append(Utility::formatNumber(thisFrame.data[i])); @@ -191,16 +197,19 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const //now, if we're supposed to interpret the data and the DBC handler is loaded then use it if (dbcHandler != NULL && interpretFrames) { - DBC_MESSAGE *msg = dbcHandler->findMsgByID(thisFrame.ID); + DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame); if (msg != NULL) { tempString.append("\r\n"); tempString.append(msg->name + " " + msg->comment + "\r\n"); - for (int j = 0; j < msg->msgSignals.length(); j++) + for (int j = 0; j < msg->sigHandler->getCount(); j++) { - - tempString.append(dbcHandler->processSignal(thisFrame, msg->msgSignals.at(j))); - tempString.append("\r\n"); + QString sigString; + if (msg->sigHandler->findSignalByIdx(j)->processAsText(thisFrame, sigString)) + { + tempString.append(sigString); + tempString.append("\r\n"); + } } } } @@ -234,12 +243,15 @@ QVariant CANFrameModel::headerData(int section, Qt::Orientation orientation, return QString(tr("Ext")); break; case 3: - return QString(tr("Bus")); + return QString(tr("Dir")); break; case 4: - return QString(tr("Len")); + return QString(tr("Bus")); break; case 5: + return QString(tr("Len")); + break; + case 6: return QString(tr("Data")); break; } @@ -251,25 +263,27 @@ QVariant CANFrameModel::headerData(int section, Qt::Orientation orientation, return QVariant(); } -void CANFrameModel::addFrame(CANFrame &frame, bool autoRefresh = false) +void CANFrameModel::addFrame(const CANFrame &frame, bool autoRefresh = false) { mutex.lock(); - frame.timestamp -= timeOffset; + CANFrame tempFrame; + tempFrame = frame; + tempFrame.timestamp -= timeOffset; //if this ID isn't found in the filters list then add it and show it by default - if (!filters.contains(frame.ID)) + if (!filters.contains(tempFrame.ID)) { - filters.insert(frame.ID, true); + filters.insert(tempFrame.ID, true); needFilterRefresh = true; } if (!overwriteDups) { - frames.append(frame); - if (filters[frame.ID]) + frames.append(tempFrame); + if (filters[tempFrame.ID]) { if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + 1); - filteredFrames.append(frame); + filteredFrames.append(tempFrame); if (autoRefresh) endInsertRows(); } } @@ -278,20 +292,20 @@ void CANFrameModel::addFrame(CANFrame &frame, bool autoRefresh = false) bool found = false; for (int i = 0; i < frames.count(); i++) { - if (frames[i].ID == frame.ID) + if (frames[i].ID == tempFrame.ID) { - frames.replace(i, frame); + frames.replace(i, tempFrame); found = true; break; } } if (!found) { - frames.append(frame); - if (filters[frame.ID]) + frames.append(tempFrame); + if (filters[tempFrame.ID]) { if (autoRefresh) beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + 1); - filteredFrames.append(frame); + filteredFrames.append(tempFrame); if (autoRefresh) endInsertRows(); } } @@ -299,10 +313,10 @@ void CANFrameModel::addFrame(CANFrame &frame, bool autoRefresh = false) { for (int j = 0; j < filteredFrames.count(); j++) { - if (filteredFrames[j].ID == frame.ID) + if (filteredFrames[j].ID == tempFrame.ID) { if (autoRefresh) beginResetModel(); - filteredFrames.replace(j, frame); + filteredFrames.replace(j, tempFrame); if (autoRefresh) endResetModel(); } } @@ -314,18 +328,23 @@ void CANFrameModel::addFrame(CANFrame &frame, bool autoRefresh = false) void CANFrameModel::sendRefresh() { + mutex.lock(); qDebug() << "Sending mass refresh"; + QVector tempContainer; beginResetModel(); - filteredFrames.clear(); - for (int i = 0; i < frames.count(); i++) + int count = frames.count(); + for (int i = 0; i < count; i++) { if (filters[frames[i].ID]) { - filteredFrames.append(frames[i]); + tempContainer.append(frames[i]); } } + filteredFrames.clear(); + filteredFrames = tempContainer; lastUpdateNumFrames = filteredFrames.count(); endResetModel(); + mutex.unlock(); } void CANFrameModel::sendRefresh(int pos) @@ -343,23 +362,34 @@ void CANFrameModel::sendBulkRefresh(int num) //of how many by tracking the number we last knew about as opposed to how many rows there //are now. num = filteredFrames.count() - lastUpdateNumFrames; + + if (num < 0) return; + + //qDebug() << "Num: " << num; + + if (num == 0 && !overwriteDups) return; + if (filteredFrames.count() == 0) return; + lastUpdateNumFrames += num; //done this way to avoid asking for filteredFrames.count() again - //qDebug() << "Bulk refresh of " << num; - - if (num == 0) return; - if (filteredFrames.count() == 0) return; + qDebug() << "Bulk refresh of " << num; if (!overwriteDups) { + mutex.lock(); if (num > filteredFrames.count()) num = filteredFrames.count(); - beginInsertRows(QModelIndex(), filteredFrames.count() - num, filteredFrames.count() - 1); - endInsertRows(); + //qDebug() << "From " << (filteredFrames.count() - num) << " to " << (filteredFrames.count() - 1); + //beginInsertRows(QModelIndex(), filteredFrames.count() - num, filteredFrames.count() - 1); + //endInsertRows(); + beginResetModel(); + endResetModel(); + mutex.unlock(); } else { - sendRefresh(); - } + beginResetModel(); + endResetModel(); + } } void CANFrameModel::clearFrames() @@ -383,6 +413,11 @@ void CANFrameModel::clearFrames() */ 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. + //beginResetModel(); + mutex.lock(); int insertedFiltered = 0; for (int i = 0; i < newFrames.count(); i++) { @@ -398,9 +433,10 @@ void CANFrameModel::insertFrames(const QVector &newFrames) filteredFrames.append(newFrames[i]); } } - - beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + insertedFiltered); - endInsertRows(); + mutex.unlock(); + //endResetModel(); + //beginInsertRows(QModelIndex(), filteredFrames.count() + 1, filteredFrames.count() + insertedFiltered); + //endInsertRows(); if (needFilterRefresh) emit updatedFiltersList(); } diff --git a/canframemodel.h b/canframemodel.h index 98a3d5c..764c073 100644 --- a/canframemodel.h +++ b/canframemodel.h @@ -23,7 +23,7 @@ public: int columnCount(const QModelIndex &) const; int totalFrameCount(); - void addFrame(CANFrame &, bool); + void addFrame(const CANFrame &, bool); void sendRefresh(); void sendRefresh(int); void sendBulkRefresh(int); diff --git a/config.h b/config.h index 90a5308..8030d77 100644 --- a/config.h +++ b/config.h @@ -1,12 +1,12 @@ #ifndef CONFIG #define CONFIG -#define VERSION 141 +#define VERSION 151 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision //less than this number. -#define CURRENT_GVRET_VER 325 //330 is the newest but it is WIP +#define CURRENT_GVRET_VER 331 #endif // CONFIG diff --git a/connectionwindow.cpp b/connectionwindow.cpp new file mode 100644 index 0000000..ad46e77 --- /dev/null +++ b/connectionwindow.cpp @@ -0,0 +1,239 @@ +#include "connectionwindow.h" +#include "ui_connectionwindow.h" +#include + +ConnectionWindow::ConnectionWindow(QWidget *parent) : + QDialog(parent), + ui(new Ui::ConnectionWindow) +{ + ui->setupUi(this); + + settings = new QSettings(); + + int temp = settings->value("Main/DefaultConnectionType", 0).toInt(); + + if (temp == 0) currentConnType = ConnectionType::GVRET_SERIAL; + if (temp == 1) currentConnType = ConnectionType::KVASER; + if (temp == 2) currentConnType = ConnectionType::SOCKETCAN; + + currentPortName = settings->value("Main/DefaultConnectionPort", "").toString(); + + currentSpeed1 = -1; + currentSpeed2 = -1; + + ui->ckSingleWire->setChecked(settings->value("Main/SingleWireMode", false).toBool()); + + ui->cbSpeed0->addItem(tr("")); + ui->cbSpeed0->addItem(tr("Disabled")); + ui->cbSpeed0->addItem(tr("125000")); + ui->cbSpeed0->addItem(tr("250000")); + ui->cbSpeed0->addItem(tr("500000")); + ui->cbSpeed0->addItem(tr("1000000")); + ui->cbSpeed0->addItem(tr("33333")); + + ui->cbSpeed1->addItem(tr("")); + ui->cbSpeed1->addItem(tr("Disabled")); + ui->cbSpeed1->addItem(tr("125000")); + ui->cbSpeed1->addItem(tr("250000")); + ui->cbSpeed1->addItem(tr("500000")); + ui->cbSpeed1->addItem(tr("1000000")); + ui->cbSpeed1->addItem(tr("33333")); + +#ifdef Q_OS_LINUX + ui->rbSocketCAN->setEnabled(true); +#endif + +#ifdef Q_OS_WIN + ui->rbKvaser->setEnabled(true); +#endif + + connect(ui->btnOK, SIGNAL(clicked(bool)), this, SLOT(handleOKButton())); +} + +ConnectionWindow::~ConnectionWindow() +{ + delete settings; + delete ui; +} + +void ConnectionWindow::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + qDebug() << "Show connectionwindow"; + if (ui->rbGVRET->isChecked()) getSerialPorts(); + if (ui->rbKvaser->isChecked()) getKvaserPorts(); + if (ui->rbSocketCAN->isChecked()) getSocketcanPorts(); +} + +void ConnectionWindow::handleOKButton() +{ + QString conn; + int connType = 0; + + if (ui->rbGVRET->isChecked()) + { + conn = "GVRET"; + connType = 0; + currentConnType = ConnectionType::GVRET_SERIAL; + } + + if (ui->rbKvaser->isChecked()) + { + conn = "KVASER"; + connType = 1; + currentConnType = ConnectionType::KVASER; + } + + if (ui->rbSocketCAN->isChecked()) + { + conn = "SOCKETCAN"; + connType = 2; + currentConnType = ConnectionType::SOCKETCAN; + } + + currentPortName = getPortName(); + currentSpeed1 = getSpeed0(); + currentSpeed2 = getSpeed1(); + + settings->setValue("Main/DefaultConnectionPort", currentPortName); + settings->setValue("Main/DefaultConnectionType", connType); + settings->setValue("Main/SingleWireMode", ui->ckSingleWire->isChecked()); + + emit updateConnectionSettings(conn, getPortName(), getSpeed0(), getSpeed1()); + + this->close(); +} + +void ConnectionWindow::getSerialPorts() +{ + ui->cbPort->clear(); + ports = QSerialPortInfo::availablePorts(); + + for (int i = 0; i < ports.count(); i++) + { + ui->cbPort->addItem(ports[i].portName()); + if (currentPortName == ports[i].portName()) ui->cbPort->setCurrentIndex(i); + } +} + +void ConnectionWindow::getKvaserPorts() +{ + +} + +void ConnectionWindow::getSocketcanPorts() +{ + QList interfaces = QNetworkInterface::allInterfaces(); + QString interfaceName; + + ui->cbPort->clear(); + + foreach (QNetworkInterface interface, interfaces) + { + interfaceName = interface.name().toLower(); + qDebug() << "Interface: " << interface.name(); + if (interfaceName.contains("can")) + { + ui->cbPort->addItem(interfaceName); + } + } +} + +void ConnectionWindow::setSpeeds(int speed0, int speed1) +{ + bool found = false; + + qDebug() << speed0 << "X" << speed1; + + for (int i = 0; i < ui->cbSpeed0->count(); i++) + { + if (ui->cbSpeed0->itemText(i).toInt() == speed0) + { + ui->cbSpeed0->setCurrentIndex(i); + found = true; + } + } + if (!found) + { + ui->cbSpeed0->addItem(QString::number(speed0)); + ui->cbSpeed0->setCurrentIndex(ui->cbSpeed0->count() - 1); + } + + found = false; + for (int i = 0; i < ui->cbSpeed1->count(); i++) + { + if (ui->cbSpeed1->itemText(i).toInt() == speed1) + { + ui->cbSpeed1->setCurrentIndex(i); + found = true; + } + } + if (!found) + { + ui->cbSpeed1->addItem(QString::number(speed1)); + ui->cbSpeed1->setCurrentIndex(ui->cbSpeed1->count() - 1); + } +} + + +//-1 means leave it at whatever it booted up to. 0 means disable. Otherwise the actual rate we want. +int ConnectionWindow::getSpeed0() +{ + switch (ui->cbSpeed0->currentIndex()) + { + case -1: + return -1; + break; + case 0: + return -1; + break; + case 1: + return 0; + break; + default: + return (ui->cbSpeed0->currentText().toInt()); + break; + } +} + +int ConnectionWindow::getSpeed1() +{ + switch (ui->cbSpeed1->currentIndex()) + { + case -1: + return -1; + break; + case 0: + return -1; + break; + case 1: + return 0; + break; + default: + return (ui->cbSpeed1->currentText().toInt()); + break; + } +} + +QString ConnectionWindow::getPortName() +{ + return (ui->cbPort->currentText()); +} + +ConnectionType::ConnectionType ConnectionWindow::getConnectionType() +{ + if (ui->rbGVRET->isChecked()) return ConnectionType::GVRET_SERIAL; + if (ui->rbKvaser->isChecked()) return ConnectionType::KVASER; + if (ui->rbSocketCAN->isChecked()) return ConnectionType::SOCKETCAN; +} + +void ConnectionWindow::setCAN1SWMode(bool mode) +{ + ui->ckSingleWire->setChecked(mode); +} + +bool ConnectionWindow::getCAN1SWMode() +{ + if (ui->ckSingleWire->checkState() == Qt::Checked) return true; + return false; +} diff --git a/connectionwindow.h b/connectionwindow.h new file mode 100644 index 0000000..3315006 --- /dev/null +++ b/connectionwindow.h @@ -0,0 +1,61 @@ +#ifndef CONNECTIONWINDOW_H +#define CONNECTIONWINDOW_H + +#include +#include +#include +#include + +namespace Ui { +class ConnectionWindow; +} + +namespace ConnectionType +{ + enum ConnectionType + { + GVRET_SERIAL, + KVASER, + SOCKETCAN + }; +} + +class ConnectionWindow : public QDialog +{ + Q_OBJECT + +public: + explicit ConnectionWindow(QWidget *parent = 0); + ~ConnectionWindow(); + void showEvent(QShowEvent *); + int getSpeed0(); + int getSpeed1(); + QString getPortName(); //name of port to connect to + ConnectionType::ConnectionType getConnectionType(); + bool getCAN1SWMode(); + +signals: + void updateConnectionSettings(QString connectionType, QString port, int speed0, int speed1); + +public slots: + void setSpeeds(int speed0, int speed1); + void setCAN1SWMode(bool mode); + +private slots: + void handleOKButton(); + +private: + Ui::ConnectionWindow *ui; + QList ports; + QSettings *settings; + + ConnectionType::ConnectionType currentConnType; + QString currentPortName; + int currentSpeed1, currentSpeed2; + + void getSerialPorts(); + void getKvaserPorts(); + void getSocketcanPorts(); +}; + +#endif // CONNECTIONWINDOW_H diff --git a/connectionwindow.ui b/connectionwindow.ui new file mode 100644 index 0000000..dc48369 --- /dev/null +++ b/connectionwindow.ui @@ -0,0 +1,117 @@ + + + ConnectionWindow + + + + 0 + 0 + 335 + 235 + + + + Connection Settings + + + + + + Connection Type + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + GVRET Serial + + + true + + + + + + + false + + + Kvaser + + + + + + + false + + + SocketCAN + + + + + + + + + + + 0 + 0 + + + + Port: + + + + + + + + + + + + CAN0 Speed + + + + + + + CAN1 Speed + + + + + + + + + + + + + + + Set CAN1 to Single Wire Mode + + + + + + + Update Settings + + + + + + + + diff --git a/dbc_classes.cpp b/dbc_classes.cpp new file mode 100644 index 0000000..8885b3f --- /dev/null +++ b/dbc_classes.cpp @@ -0,0 +1,203 @@ +#include "dbc_classes.h" +#include "dbchandler.h" +#include "utility.h" + +DBC_MESSAGE::DBC_MESSAGE() +{ + sigHandler = new DBCSignalHandler; +} + +/* + The way that the DBC file format works is kind of weird... For intel format signals you count up +from the start bit to the end bit which is (startbit + signallength - 1). At each point +bits are numbered in a sawtooth manner. What that means is that the very first bit is 0 and you count up +from there all of the way to 63 with each byte being 8 bits so bit 0 is the lowest bit in the first byte +and 8 is the lowest bit in the next byte up. The whole thing looks like this: + Bits + 7 6 5 4 3 2 1 0 + + 0 7 6 5 4 3 2 1 0 +b 1 15 14 13 12 11 10 9 8 +y 2 23 22 21 20 19 18 17 16 +t 3 31 30 29 28 27 26 25 24 +e 4 39 38 37 36 35 34 33 32 +s 5 47 46 45 44 43 42 41 40 + 6 55 54 53 52 51 50 49 48 + 7 63 62 61 60 59 58 57 56 + + For intel format you start at the start bit and keep counting up. If you have a signal size of 8 + and start at bit 12 then the bits are 12, 13, 14, 15, 16, 17, 18, 19 which spans across two bytes. + In this format each bit is worth twice as much as the last and you just keep counting up. + Bit 12 is worth 1, 13 is worth 2, 14 is worth 4, etc all of the way to bit 19 is worth 128. + + Motorola format turns most everything on its head. You count backward from the start bit but + only within the current byte. If you are about to exit the current byte you go one higher and then keep + going backward as before. Using the same example as for intel, start bit of 12 and a signal length of 8. + So, the bits are 12, 11, 10, 9, 8, 23, 22, 21. Yes, that's confusing. They now go in reverse value order too. + Bit 12 is worth 128, 11 is worth 64, etc until bit 21 is worth 1. +*/ +bool DBC_SIGNAL::processAsText(const CANFrame &frame, QString &outString) +{ + int64_t result = 0; + bool isSigned = false; + double endResult; + + if (valType == STRING) + { + QString buildString; + int startByte = signalSize / 8; + int bytes = signalSize / 8; + for (int x = 0; x < bytes; x++) buildString.append(frame.data[startByte + x]); + outString = buildString; + return true; + } + + //if this is a multiplexed signal then we have to see if it is even found in the current message + if (isMultiplexed) + { + if (parentMessage->multiplexorSignal != NULL) + { + int val; + if (!parentMessage->multiplexorSignal->processAsInt(frame, val)) return false; + if (val != multiplexValue) return false; //signal not found in this message + } + else return false; + } + + if (valType == SIGNED_INT) isSigned = true; + if (valType == SIGNED_INT || valType == UNSIGNED_INT) + { + result = Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned); + endResult = ((double)result * factor) + bias; + result = (int64_t)endResult; + } + else if (valType == SP_FLOAT) + { + //The theory here is that we force the integer signal code to treat this as + //a 32 bit unsigned integer. This integer is then cast into a float in such a way + //that the bytes that make up the integer are instead treated as having made up + //a 32 bit single precision float. That's evil incarnate but it is very fast and small + //in terms of new code. + result = Utility::processIntegerSignal(frame.data, startBit, 32, false, false); + endResult = (*((float *)(&result)) * factor) + bias; + } + else //double precision float + { + //like the above, this is rotten and evil and wrong in so many ways. Force + //calculation of a 64 bit integer and then cast it into a double. + result = Utility::processIntegerSignal(frame.data, 0, 64, false, false); + endResult = (*((double *)(&result)) * factor) + bias; + } + + QString outputString; + + outputString = name + ": "; + + if (valList.count() > 0) //if this is a value list type then look it up and display the proper string + { + for (int x = 0; x < valList.count(); x++) + { + if (valList.at(x).value == result) outputString += valList.at(x).descript; + } + } + else //otherwise display the actual number and unit (if it exists) + { + outputString += QString::number(endResult) + unitName; + } + + outString = outputString; + return true; +} + +//Works quite a bit like the above version but this one is cut down and only will return int32_t which is perfect for +//uses like calculating a multiplexor value or if you know you are going to get an integer returned +//from a signal and you want to use it as-is and not have to convert back from a string. Use with caution though +//as this basically assumes the signal is an integer. +//The call syntax is different from the more generic processSignal. Instead of returning the value we return +//true or false to show whether the function succeeded. The variable to fill out is passed by reference. +bool DBC_SIGNAL::processAsInt(const CANFrame &frame, int32_t &outValue) +{ + int32_t result = 0; + bool isSigned = false; + if (valType == STRING || valType == SP_FLOAT || valType == DP_FLOAT) + { + return false; + } + + //if this is a multiplexed signal then we have to see if it is even found in the current message + if (isMultiplexed) + { + if (parentMessage->multiplexorSignal != NULL) + { + int val; + if (!parentMessage->multiplexorSignal->processAsInt(frame, val)) return false; + if (val != multiplexValue) return false; //signal not found in this message + } + else return false; + } + + if (valType == SIGNED_INT) isSigned = true; + result = Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned); + + double endResult = ((double)result * factor) + bias; + result = (int32_t)endResult; + + outValue = result; + return true; +} + +//Another cut down version that will only return double precision data. This can be used on any of the types +//except STRING. Useful for when you know you'll need floating point data and don't want to incur a conversion +//back and forth to double or float. Such a use is the graphing window. +//Similar syntax to processSignalInt but with double instead. +bool DBC_SIGNAL::processAsDouble(const CANFrame &frame, double &outValue) +{ + int64_t result = 0; + bool isSigned = false; + double endResult; + + if (valType == STRING) + { + return false; + } + + //if this is a multiplexed signal then we have to see if it is even found in the current message + if (isMultiplexed) + { + if (parentMessage->multiplexorSignal != NULL) + { + int val; + if (!parentMessage->multiplexorSignal->processAsInt(frame, val)) return false; + if (val != multiplexValue) return false; //signal not found in this message + } + else return false; + } + + if (valType == SIGNED_INT) isSigned = true; + if (valType == SIGNED_INT || valType == UNSIGNED_INT) + { + result = Utility::processIntegerSignal(frame.data, startBit, signalSize, intelByteOrder, isSigned); + endResult = ((double)result * factor) + bias; + result = (int64_t)endResult; + } + else if (valType == SP_FLOAT) + { + //The theory here is that we force the integer signal code to treat this as + //a 32 bit unsigned integer. This integer is then cast into a float in such a way + //that the bytes that make up the integer are instead treated as having made up + //a 32 bit single precision float. That's evil incarnate but it is very fast and small + //in terms of new code. + result = Utility::processIntegerSignal(frame.data, startBit, 32, false, false); + endResult = (*((float *)(&result)) * factor) + bias; + } + else //double precision float + { + //like the above, this is rotten and evil and wrong in so many ways. Force + //calculation of a 64 bit integer and then cast it into a double. + result = Utility::processIntegerSignal(frame.data, 0, 64, false, false); + endResult = (*((double *)(&result)) * factor) + bias; + } + + outValue = endResult; + return true; +} diff --git a/dbc_classes.h b/dbc_classes.h index 7cbde33..9318791 100644 --- a/dbc_classes.h +++ b/dbc_classes.h @@ -3,6 +3,7 @@ #include #include +#include "can_structs.h" /*classes to encapsulate data from a DBC file. Really, the stuff of interest are the nodes, messages, signals, attributes, and comments. @@ -54,35 +55,50 @@ public: QList attributes; }; +class DBC_MESSAGE; //forward reference so that DBC_SIGNAL can compile before we get to real definition of DBC_MESSAGE + class DBC_SIGNAL { -public: +public: //TODO: this is sloppy. It shouldn't all be public! QString name; int startBit; int signalSize; bool intelByteOrder; //true is obviously little endian. False is big endian + bool isMultiplexor; + bool isMultiplexed; + int multiplexValue; DBC_SIG_VAL_TYPE valType; double factor; double bias; double min; double max; - DBC_NODE *receiver; + DBC_NODE *receiver; //it is fast to have a pointer but dangerous... Make sure to walk the whole tree and delete everything so nobody has stale references. + DBC_MESSAGE *parentMessage; QString unitName; QString comment; QList attributes; QList valList; + + bool processAsText(const CANFrame &frame, QString &outString); + bool processAsInt(const CANFrame &frame, int32_t &outValue); + bool processAsDouble(const CANFrame &frame, double &outValue); }; +class DBCSignalHandler; //forward declaration to keep from having to include dbchandler.h in this file and thus create a loop + class DBC_MESSAGE { public: + DBC_MESSAGE(); + int ID; QString name; QString comment; int len; DBC_NODE *sender; QList attributes; - QList msgSignals; + DBCSignalHandler *sigHandler; + DBC_SIGNAL* multiplexorSignal; }; diff --git a/dbchandler.cpp b/dbchandler.cpp index db6257e..2851c60 100644 --- a/dbchandler.cpp +++ b/dbchandler.cpp @@ -3,21 +3,262 @@ #include #include #include +#include +#include +#include "utility.h" -DBCHandler::DBCHandler(QObject *parent) : QObject(parent) +DBC_SIGNAL* DBCSignalHandler::findSignalByIdx(int idx) { + if (sigs.count() == 0) return NULL; + if (idx < 0) return NULL; + if (idx >= sigs.count()) return NULL; + return &sigs[idx]; } -void DBCHandler::loadDBCFile(QString filename) +DBC_SIGNAL* DBCSignalHandler::findSignalByName(QString name) { - QFile *inFile = new QFile(filename); + if (sigs.count() == 0) return NULL; + for (int i = 0; i < sigs.count(); i++) + { + if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0) + { + return &sigs[i]; + } + } + return NULL; +} + +bool DBCSignalHandler::addSignal(DBC_SIGNAL &sig) +{ + sigs.append(sig); + return true; +} + +bool DBCSignalHandler::removeSignal(DBC_SIGNAL *sig) +{ + //if (sigs.removeAll(*sig) > 0) return true; + return false; +} + +bool DBCSignalHandler::removeSignal(int idx) +{ + if (sigs.count() == 0) return false; + if (idx < 0) return false; + if (idx >= sigs.count()) return false; + sigs.removeAt(idx); + return true; +} + +bool DBCSignalHandler::removeSignal(QString name) +{ + bool foundSome = false; + if (sigs.count() == 0) return false; + for (int i = sigs.count() - 1; i >= 0; i--) + { + if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0) + { + sigs.removeAt(i); + foundSome = true; + } + } + return foundSome; +} + +void DBCSignalHandler::removeAllSignals() +{ + sigs.clear(); +} + +int DBCSignalHandler::getCount() +{ + return sigs.count(); +} + +DBC_MESSAGE* DBCMessageHandler::findMsgByID(int id) +{ + if (messages.count() == 0) return NULL; + for (int i = 0; i < messages.count(); i++) + { + if (messages[i].ID == id) + { + return &messages[i]; + } + } + return NULL; +} + +DBC_MESSAGE* DBCMessageHandler::findMsgByIdx(int idx) +{ + if (messages.count() == 0) return NULL; + if (idx < 0) return NULL; + if (idx >= messages.count()) return NULL; + return &messages[idx]; +} + +DBC_MESSAGE* DBCMessageHandler::findMsgByName(QString name) +{ + if (messages.count() == 0) return NULL; + for (int i = 0; i < messages.count(); i++) + { + if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0) + { + return &messages[i]; + } + } + return NULL; +} + +bool DBCMessageHandler::addMessage(DBC_MESSAGE &msg) +{ + messages.append(msg); + return true; +} + +bool DBCMessageHandler::removeMessage(DBC_MESSAGE *msg) +{ + //if (messages.removeAll(*msg) > 0) return true; + return false; +} + +bool DBCMessageHandler::removeMessageByIndex(int idx) +{ + if (messages.count() == 0) return false; + if (idx < 0) return false; + if (idx >= messages.count()) return false; + messages.removeAt(idx); + return true; +} + +bool DBCMessageHandler::removeMessage(int ID) +{ + bool foundSome = false; + if (messages.count() == 0) return false; + for (int i = messages.count() - 1; i >= 0; i--) + { + if (messages[i].ID == ID) + { + messages.removeAt(i); + foundSome = true; + } + } + return foundSome; +} + +bool DBCMessageHandler::removeMessage(QString name) +{ + bool foundSome = false; + if (messages.count() == 0) return false; + for (int i = messages.count() - 1; i >= 0; i--) + { + if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0) + { + messages.removeAt(i); + foundSome = true; + } + } + return foundSome; +} + +void DBCMessageHandler::removeAllMessages() +{ + messages.clear(); +} + +int DBCMessageHandler::getCount() +{ + return messages.count(); +} + +DBCFile::DBCFile() +{ + messageHandler = new DBCMessageHandler; +} + +DBCFile::DBCFile(const DBCFile& cpy) +{ + messageHandler = new DBCMessageHandler; + for (int i = 0 ; i < cpy.messageHandler->getCount() ; i++) + messageHandler->addMessage(*cpy.messageHandler->findMsgByIdx(i)); + + fileName = cpy.fileName; + filePath = cpy.filePath; + assocBuses = cpy.assocBuses; + dbc_nodes.clear(); + dbc_nodes.append(cpy.dbc_nodes); +} + +DBCFile& DBCFile::operator=(const DBCFile& cpy) +{ + if (this != &cpy) // protect against invalid self-assignment + { + messageHandler = cpy.messageHandler; + fileName = cpy.fileName; + filePath = cpy.filePath; + assocBuses = cpy.assocBuses; + dbc_nodes.clear(); + dbc_nodes.append(cpy.dbc_nodes); + } + return *this; +} + +DBC_NODE* DBCFile::findNodeByIdx(int idx) +{ + if (idx < 0) return NULL; + if (idx >= dbc_nodes.count()) return NULL; + return &dbc_nodes[idx]; +} + +DBC_NODE* DBCFile::findNodeByName(QString name) +{ + if (dbc_nodes.length() == 0) return NULL; + for (int i = 0; i < dbc_nodes.length(); i++) + { + if (dbc_nodes[i].name.compare(name, Qt::CaseInsensitive) == 0) + { + return &dbc_nodes[i]; + } + } + return NULL; +} + +QString DBCFile::getFullFilename() +{ + return filePath + fileName; +} + +QString DBCFile::getFilename() +{ + return fileName; +} + +QString DBCFile::getPath() +{ + return filePath; +} + +int DBCFile::getAssocBus() +{ + return assocBuses; +} + +void DBCFile::setAssocBus(int bus) +{ + if (bus < -1) return; + if (bus > 1) return; + assocBuses = bus; +} + +void DBCFile::loadFile(QString fileName) +{ + QFile *inFile = new QFile(fileName); QString line; QRegularExpression regex; QRegularExpressionMatch match; DBC_MESSAGE *currentMessage = NULL; + int numSigFaults = 0, numMsgFaults = 0; - qDebug() << "DBC File: " << filename; + qDebug() << "DBC File: " << fileName; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -27,7 +268,7 @@ void DBCHandler::loadDBCFile(QString filename) qDebug() << "Starting DBC load"; dbc_nodes.clear(); - dbc_messages.clear(); + messageHandler->removeAllMessages(); DBC_NODE falseNode; falseNode.name = "Vector__XXX"; @@ -36,9 +277,9 @@ void DBCHandler::loadDBCFile(QString filename) while (!inFile->atEnd()) { line = QString(inFile->readLine().simplified()); - if (line.startsWith("BO_ ")) + if (line.startsWith("BO_ ")) //defines a message { - //qDebug() << "Found a BO line"; + qDebug() << "Found a BO line"; regex.setPattern("^BO\\_ (\\w+) (\\w+) *: (\\w+) (\\w+)"); match = regex.match(line); //captured 1 = the ID in decimal @@ -52,16 +293,55 @@ void DBCHandler::loadDBCFile(QString filename) msg.name = match.captured(2); msg.len = match.captured(3).toInt(); msg.sender = findNodeByName(match.captured(4)); - dbc_messages.append(msg); - currentMessage = &dbc_messages.last(); + messageHandler->addMessage(msg); + currentMessage = messageHandler->findMsgByID(msg.ID); } + else numMsgFaults++; } - if (line.startsWith("SG_ ")) + if (line.startsWith("SG_ ")) //defines a signal { - //qDebug() << "Found a SG line"; - regex.setPattern("^SG\\_ (\\w+) : (\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)"); + int offset = 0; + bool isMultiplexor = false; + bool isMultiplexed = false; + DBC_SIGNAL sig; + + sig.multiplexValue = 0; + sig.isMultiplexed = false; + sig.isMultiplexor = false; + + qDebug() << "Found a SG line"; + regex.setPattern("^SG\\_ *(\\w+) *M *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)"); + match = regex.match(line); + if (match.hasMatch()) + { + qDebug() << "Multiplexor signal"; + isMultiplexor = true; + sig.isMultiplexor = true; + } + else + { + regex.setPattern("^SG\\_ *(\\w+) *m(\\d+) *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)"); + match = regex.match(line); + if (match.hasMatch()) + { + qDebug() << "Multiplexed signal"; + isMultiplexed = true; + sig.isMultiplexed = true; + sig.multiplexValue = match.captured(2).toInt(); + offset = 1; + } + else + { + regex.setPattern("^SG\\_ *(\\w+) *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)"); + match = regex.match(line); + sig.isMultiplexed = false; + sig.isMultiplexor = false; + } + } + //captured 1 is the signal name + //captured 2 would be multiplex value if this is a multiplex signal. Then offset the rest of these by 1 //captured 2 is the starting bit //captured 3 is the length in bits //captured 4 is the byte order / value type @@ -75,14 +355,13 @@ void DBCHandler::loadDBCFile(QString filename) if (match.hasMatch()) { - DBC_SIGNAL sig; sig.name = match.captured(1); - sig.startBit = match.captured(2).toInt(); - sig.signalSize = match.captured(3).toInt(); - int val = match.captured(4).toInt(); + sig.startBit = match.captured(2 + offset).toInt(); + sig.signalSize = match.captured(3 + offset).toInt(); + int val = match.captured(4 + offset).toInt(); if (val < 2) { - if (match.captured(5) == "+") sig.valType = UNSIGNED_INT; + if (match.captured(5 + offset) == "+") sig.valType = UNSIGNED_INT; else sig.valType = SIGNED_INT; } switch (val) @@ -103,23 +382,26 @@ void DBCHandler::loadDBCFile(QString filename) sig.valType = STRING; break; } - sig.factor = match.captured(6).toDouble(); - sig.bias = match.captured(7).toDouble(); - sig.min = match.captured(8).toDouble(); - sig.max = match.captured(9).toDouble(); - sig.unitName = match.captured(10); - if (match.captured(11).contains(',')) + sig.factor = match.captured(6 + offset).toDouble(); + sig.bias = match.captured(7 + offset).toDouble(); + sig.min = match.captured(8 + offset).toDouble(); + sig.max = match.captured(9 + offset).toDouble(); + sig.unitName = match.captured(10 + offset); + if (match.captured(11 + offset).contains(',')) { QString tmp = match.captured(11).split(',')[0]; sig.receiver = findNodeByName(tmp); } - else sig.receiver = findNodeByName(match.captured(11)); - currentMessage->msgSignals.append(sig); + else sig.receiver = findNodeByName(match.captured(11 + offset)); + sig.parentMessage = currentMessage; + currentMessage->sigHandler->addSignal(sig); + if (isMultiplexor) currentMessage->multiplexorSignal = currentMessage->sigHandler->findSignalByName(sig.name); } + else numSigFaults++; } - if (line.startsWith("BU_:")) + if (line.startsWith("BU_:")) //line specifies the nodes on this canbus { - //qDebug() << "Found a BU line"; + qDebug() << "Found a BU line"; regex.setPattern("^BU\\_\\:(.*)"); match = regex.match(line); //captured 1 = a list of node names separated by spaces. No idea how many yet @@ -141,7 +423,7 @@ void DBCHandler::loadDBCFile(QString filename) } if (line.startsWith("CM_ SG_ ")) { - //qDebug() << "Found an SG comment line"; + qDebug() << "Found an SG comment line"; regex.setPattern("^CM\\_ SG\\_ *(\\w+) *(\\w+) *\\\"(.*)\\\";"); match = regex.match(line); //captured 1 is the ID to match against to get to the message @@ -150,10 +432,10 @@ void DBCHandler::loadDBCFile(QString filename) if (match.hasMatch()) { //qDebug() << "Comment was: " << match.captured(3); - DBC_MESSAGE *msg = findMsgByID(match.captured(1).toInt()); + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); if (msg != NULL) { - DBC_SIGNAL *sig = findSignalByName(msg, match.captured(2)); + DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2)); if (sig != NULL) { sig->comment = match.captured(3); @@ -163,7 +445,7 @@ void DBCHandler::loadDBCFile(QString filename) } if (line.startsWith("CM_ BO_ ")) { - //qDebug() << "Found a BO comment line"; + qDebug() << "Found a BO comment line"; regex.setPattern("^CM\\_ BO\\_ *(\\w+) *\\\"(.*)\\\";"); match = regex.match(line); //captured 1 is the ID to match against to get to the message @@ -171,7 +453,7 @@ void DBCHandler::loadDBCFile(QString filename) if (match.hasMatch()) { //qDebug() << "Comment was: " << match.captured(2); - DBC_MESSAGE *msg = findMsgByID(match.captured(1).toInt()); + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); if (msg != NULL) { msg->comment = match.captured(2); @@ -180,7 +462,7 @@ void DBCHandler::loadDBCFile(QString filename) } if (line.startsWith("CM_ BU_ ")) { - //qDebug() << "Found a BU comment line"; + qDebug() << "Found a BU comment line"; regex.setPattern("^CM\\_ BU\\_ *(\\w+) *\\\"(.*)\\\";"); match = regex.match(line); //captured 1 is the Node name @@ -198,7 +480,7 @@ void DBCHandler::loadDBCFile(QString filename) //VAL_ (1090) (VCUPresentParkLightOC) (1 "Error present" 0 "Error not present") ; if (line.startsWith("VAL_ ")) { - //qDebug() << "Found a value definition line"; + qDebug() << "Found a value definition line"; regex.setPattern("^VAL\\_ (\\w+) (\\w+) (.*);"); match = regex.match(line); //captured 1 is the ID to match against @@ -207,10 +489,10 @@ void DBCHandler::loadDBCFile(QString filename) if (match.hasMatch()) { //qDebug() << "Data was: " << match.captured(3); - DBC_MESSAGE *msg = findMsgByID(match.captured(1).toInt()); + DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); if (msg != NULL) { - DBC_SIGNAL *sig = findSignalByName(msg, match.captured(2)); + DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2)); if (sig != NULL) { QString tokenString = match.captured(3); @@ -260,16 +542,28 @@ void DBCHandler::loadDBCFile(QString filename) } */ } + if (numSigFaults > 0 || numMsgFaults > 0) + { + QMessageBox msgBox; + QString msg = "DBC file loaded with errors!\n"; + msg += "Number of faulty message entries: " + QString::number(numMsgFaults) + "\n"; + msg += "Number of faulty signal entries: " + QString::number(numSigFaults) + "\n\n"; + msg += "Faulty entries have not been loaded."; + msgBox.setText(msg); + msgBox.exec(); + } inFile->close(); delete inFile; + QStringList fileList = fileName.split('/'); + this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function. + filePath = fileName.left(fileName.length() - this->fileName.length()); + assocBuses = -1; + } -/*Yes, this is really hard to follow and all of the sections are mixed up in code - * believe it or not I think this is actually the easiest, simplest way to do it. -*/ -void DBCHandler::saveDBCFile(QString filename) +void DBCFile::saveFile(QString fileName) { - QFile *outFile = new QFile(filename); + QFile *outFile = new QFile(fileName); QString nodesOutput, msgOutput, commentsOutput, valuesOutput; if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) @@ -330,27 +624,36 @@ void DBCHandler::saveDBCFile(QString filename) nodesOutput.append("\n"); outFile->write(nodesOutput.toUtf8()); - for (int x = 0; x < dbc_messages.count(); x++) + for (int x = 0; x < messageHandler->getCount(); x++) { - DBC_MESSAGE msg = dbc_messages[x]; - msgOutput.append("BO_ " + QString::number(msg.ID) + " " + msg.name + ": " + QString::number(msg.len) + - " " + msg.sender->name + "\n"); - if (msg.comment.length() > 0) + DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x); + msgOutput.append("BO_ " + QString::number(msg->ID) + " " + msg->name + ": " + QString::number(msg->len) + + " " + msg->sender->name + "\n"); + if (msg->comment.length() > 0) { - commentsOutput.append("CM_ BO_ " + QString::number(msg.ID) + " \"" + msg.comment + "\";\n"); + commentsOutput.append("CM_ BO_ " + QString::number(msg->ID) + " \"" + msg->comment + "\";\n"); } - for (int s = 0; s < msg.msgSignals.count(); s++) + for (int s = 0; s < msg->sigHandler->getCount(); s++) { - DBC_SIGNAL sig = msg.msgSignals[s]; - msgOutput.append(" SG_ " + sig.name + " : " + QString::number(sig.startBit) + "|" + QString::number(sig.signalSize) + "@"); - switch (sig.valType) + DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s); + msgOutput.append(" SG_ " + sig->name); + + if (sig->isMultiplexor) msgOutput.append(" M"); + if (sig->isMultiplexed) + { + msgOutput.append(" m" + QString::number(sig->multiplexValue)); + } + + msgOutput.append(" : " + QString::number(sig->startBit) + "|" + QString::number(sig->signalSize) + "@"); + + switch (sig->valType) { case UNSIGNED_INT: - if (sig.intelByteOrder) msgOutput.append("1+"); + if (sig->intelByteOrder) msgOutput.append("1+"); else msgOutput.append("0+"); break; case SIGNED_INT: - if (sig.intelByteOrder) msgOutput.append("1-"); + if (sig->intelByteOrder) msgOutput.append("1-"); else msgOutput.append("0-"); break; case SP_FLOAT: @@ -366,19 +669,19 @@ void DBCHandler::saveDBCFile(QString filename) msgOutput.append("0-"); break; } - msgOutput.append(" (" + QString::number(sig.factor) + "," + QString::number(sig.bias) + ") [" + - QString::number(sig.min) + "|" + QString::number(sig.max) + "] \"" + sig.unitName - + "\" " + sig.receiver->name + "\n"); - if (sig.comment.length() > 0) + msgOutput.append(" (" + QString::number(sig->factor) + "," + QString::number(sig->bias) + ") [" + + QString::number(sig->min) + "|" + QString::number(sig->max) + "] \"" + sig->unitName + + "\" " + sig->receiver->name + "\n"); + if (sig->comment.length() > 0) { - commentsOutput.append("CM_ SG_ " + QString::number(msg.ID) + " " + sig.name + " \"" + sig.comment + "\";\n"); + commentsOutput.append("CM_ SG_ " + QString::number(msg->ID) + " " + sig->name + " \"" + sig->comment + "\";\n"); } - if (sig.valList.count() > 0) + if (sig->valList.count() > 0) { - valuesOutput.append("VAL_ " + QString::number(msg.ID) + " " + sig.name); - for (int v = 0; v < sig.valList.count(); v++) + valuesOutput.append("VAL_ " + QString::number(msg->ID) + " " + sig->name); + for (int v = 0; v < sig->valList.count(); v++) { - DBC_VAL val = sig.valList[v]; + DBC_VAL val = sig->valList[v]; valuesOutput.append(" " + QString::number(val.value) + " \"" + val.descript +"\""); } valuesOutput.append(";\n"); @@ -396,250 +699,140 @@ void DBCHandler::saveDBCFile(QString filename) outFile->close(); delete outFile; + + QStringList fileList = fileName.split('/'); + this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function. + filePath = fileName.left(fileName.length() - this->fileName.length()); } - -DBC_NODE *DBCHandler::findNodeByName(QString name) +void DBCHandler::saveDBCFile(int idx) { - if (dbc_nodes.length() == 0) return NULL; - for (int i = 0; i < dbc_nodes.length(); i++) + if (loadedFiles.count() == 0) return; + if (idx < 0) return; + if (idx >= loadedFiles.count()) return; + + QString filename; + QFileDialog dialog; + + QStringList filters; + filters.append(QString(tr("DBC File (*.dbc)"))); + + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setNameFilters(filters); + dialog.setViewMode(QFileDialog::Detail); + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.selectFile(loadedFiles[idx].getFullFilename()); + + if (dialog.exec() == QDialog::Accepted) { - if (dbc_nodes[i].name.compare(name, Qt::CaseInsensitive) == 0) - { - return &dbc_nodes[i]; - } - } - return NULL; -} - -DBC_NODE *DBCHandler::findNodeByIdx(int idx) -{ - if (idx < 0) return NULL; - if (idx >= dbc_nodes.count()) return NULL; - return &dbc_nodes[idx]; -} - -DBC_MESSAGE *DBCHandler::findMsgByID(int id) -{ - if (dbc_messages.length() == 0) return NULL; - for (int i = 0; i < dbc_messages.length(); i++) - { - if (dbc_messages[i].ID == id) - { - return &dbc_messages[i]; - } - } - return NULL; -} - -DBC_MESSAGE *DBCHandler::findMsgByIdx(int idx) -{ - if (dbc_messages.length() == 0) return NULL; - if (idx < 0) return NULL; - if (idx >= dbc_messages.count()) return NULL; - return &dbc_messages[idx]; -} - -DBC_MESSAGE *DBCHandler::findMsgByName(QString name) -{ - if (dbc_messages.length() == 0) return NULL; - for (int i = 0; i < dbc_messages.length(); i++) - { - if (dbc_messages[i].name.compare(name, Qt::CaseInsensitive) == 0) - { - return &dbc_messages[i]; - } - } - return NULL; -} - -DBC_SIGNAL *DBCHandler::findSignalByName(DBC_MESSAGE *msg, QString name) -{ - if (msg == NULL) return NULL; - if (msg->msgSignals.length() == 0) return NULL; - for (int i = 0; i < msg->msgSignals.length(); i++) - { - if (msg->msgSignals[i].name.compare(name, Qt::CaseInsensitive) == 0) - { - return &msg->msgSignals[i]; - } - } - return NULL; -} - -DBC_SIGNAL *DBCHandler::findSignalByIdx(DBC_MESSAGE *msg, int idx) -{ - if (msg == NULL) return NULL; - if (msg->msgSignals.length() == 0) return NULL; - if (idx < 0) return NULL; - if (idx >= msg->msgSignals.count()) return NULL; - return &msg->msgSignals[idx]; -} - -//Dumps the messages, signals, values structs out in order to debugging console. Used only for debugging -//not really meant for general consumption. -void DBCHandler::listDebugging() -{ - for (int i = 0; i < dbc_messages.length(); i++) - { - DBC_MESSAGE msg = dbc_messages.at(i); - qDebug() << " "; - qDebug() << "Msg ID: " << msg.ID << " Name: " << msg.name; - - for (int j = 0; j < msg.msgSignals.length(); j++) - { - DBC_SIGNAL sig; - sig = msg.msgSignals.at(j); - qDebug() << " Signal Name: " << sig.name; - qDebug() << " Start bit: " << sig.startBit; - qDebug() << " Bit Length: " << sig.signalSize; - if (sig.valList.length() > 1) qDebug() << " Values: "; - for (int k = 0; k < sig.valList.length(); k++) - { - DBC_VAL val = sig.valList.at(k); - qDebug() << " " << val.value << " Description: " << val.descript; - } - } + filename = dialog.selectedFiles()[0]; + if (!filename.contains('.')) filename += ".dbc"; + loadedFiles[idx].saveFile(filename); } } +int DBCHandler::createBlankFile() +{ + DBCFile newFile; + loadedFiles.append(newFile); + return loadedFiles.count(); +} + +//the only reason to even bother sending the index is to see if +//the user wants to replace an already loaded DBC. +//Otherwise add a new one. Well, always add a new one. +//If a valid index is passed we'll remove that one and then commence +//adding. Otherwise, just go straight to adding. +DBCFile* DBCHandler::loadDBCFile(int idx) +{ + if (idx > -1 && idx < loadedFiles.count()) removeDBCFile(idx); + + QString filename; + QFileDialog dialog; + + QStringList filters; + filters.append(QString(tr("DBC File (*.dbc)"))); + + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setNameFilters(filters); + dialog.setViewMode(QFileDialog::Detail); + + if (dialog.exec() == QDialog::Accepted) + { + filename = dialog.selectedFiles()[0]; + //right now there is only one file type that can be loaded here so just do it. + DBCFile newFile; + newFile.loadFile(filename); + loadedFiles.append(newFile); + } + return &loadedFiles.last(); +} + +void DBCHandler::removeDBCFile(int idx) +{ + if (loadedFiles.count() == 0) return; + if (idx < 0) return; + if (idx >= loadedFiles.count()) return; + loadedFiles.removeAt(idx); +} + +void DBCHandler::removeAllFiles() +{ + loadedFiles.clear(); +} + +void DBCHandler::swapFiles(int pos1, int pos2) +{ + if (loadedFiles.count() < 2) return; + if (pos1 < 0) return; + if (pos1 >= loadedFiles.count()) return; + if (pos2 < 0) return; + if (pos2 >= loadedFiles.count()) return; + + loadedFiles.swap(pos1, pos2); +} /* - The way that the DBC file format works is kind of weird... For intel format signals you count up -from the start bit to the end bit which is (startbit + signallength - 1). At each point -bits are numbered in a sawtooth manner. What that means is that the very first bit is 0 and you count up -from there all of the way to 63 with each byte being 8 bits so bit 0 is the lowest bit in the first byte -and 8 is the lowest bit in the next byte up. The whole thing looks like this: - Bits - 7 6 5 4 3 2 1 0 - - 0 7 6 5 4 3 2 1 0 -b 1 15 14 13 12 11 10 9 8 -y 2 23 22 21 20 19 18 17 16 -t 3 31 30 29 28 27 26 25 24 -e 4 39 38 37 36 35 34 33 32 -s 5 47 46 45 44 43 42 41 40 - 6 55 54 53 52 51 50 49 48 - 7 63 62 61 60 59 58 57 56 - - For intel format you start at the start bit and keep counting up. If you have a signal size of 8 - and start at bit 12 then the bits are 12, 13, 14, 15, 16, 17, 18, 19 which spans across two bytes. - In this format each bit is worth twice as much as the last and you just keep counting up. - Bit 12 is worth 1, 13 is worth 2, 14 is worth 4, etc all of the way to bit 19 is worth 128. - - Motorola format turns most everything on its head. You count backward from the start bit but - only within the current byte. If you are about to exit the current byte you go one higher and then keep - going backward as before. Using the same example as for intel, start bit of 12 and a signal length of 8. - So, the bits are 12, 11, 10, 9, 8, 23, 22, 21. Yes, that's confusing. They now go in reverse value order too. - Bit 12 is worth 128, 11 is worth 64, etc until bit 21 is worth 1. + * Convenience function that encapsulates a whole lot of the details. + * You give it a canbus frame and it'll tell you whether there is a loaded DBC file that can + * interpret that frame for you. + * Returns NULL if there is no message definition that matches. */ - -QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig) +DBC_MESSAGE* DBCHandler::findMessage(const CANFrame &frame) { - - int64_t result = 0; - int bit; - int sigSize; - if (sig.valType == STRING) + for(int i = 0; i < loadedFiles.count(); i++) { - QString buildString; - int startByte = sig.signalSize / 8; - int bytes = sig.signalSize / 8; - for (int x = 0; x < bytes; x++) buildString.append(frame.data[startByte + x]); - return buildString; - } - - sigSize = sig.signalSize; - - if (sig.intelByteOrder) - { - bit = sig.startBit; - for (int bitpos = 0; bitpos < sigSize; bitpos++) + if (loadedFiles[i].getAssocBus() == -1 || frame.bus == loadedFiles[i].getAssocBus()) { - if (frame.data[bit / 8] & (1 << (bit % 8))) - result += (1ULL << bitpos); - - bit++; + DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(frame.ID); + if (msg != NULL) return msg; } } - else //motorola / big endian mode - { - bit = sig.startBit; - for (int bitpos = 0; bitpos < sigSize; bitpos++) - { - if (frame.data[bit / 8] & (1 << (bit % 8))) - result += (1ULL << (sigSize - bitpos - 1)); - - if ((bit % 8) == 0) - bit += 15; - else bit--; - - } - } - - if (sig.valType == SIGNED_INT) - { - int mask = (1 << (sig.signalSize - 1)); - if ((result & mask) == mask) //is the highest bit possible for this signal size set? - { - /* - * if so we need to also set every bit higher in the result int too. - * This leads to the below two lines that are nasty. Here's the theory behind that... - * If the value is signed and the highest bit is set then it is negative. To create - * a negative value out of this even though the variable result is 64 bit we have to - * run 1's all of the way up to bit 63 in result. -1 is all ones for whatever size integer - * you have. So, it's 64 1's in this case. - * signedMask is done this way: - * first you take the signal size and shift 1 up that far. Then subtract one. Lets - * see that for a 16 bit signal: - * (1 << 16) - 1 = the first 16 bits set as 1's. So far so good. We then negate the whole - * thing which flips all bits. Thus signedMask ends up with 1's everwhere that the signal - * doesn't take up in the 64 bit signed integer result. Then, result has an OR operation on - * it with the old value and -1 masked so that the the 1 bits from -1 don't overwrite bits from the - * actual signal. This extends the sign bits out so that the integer result reads as the proper negative - * value. We dont need to do any of this if the sign bit wasn't set. - */ - int signedMask = ~((1 << sig.signalSize) - 1); - result = (-1 & signedMask) | result; - } - } - - double endResult = ((double)result * sig.factor) + sig.bias; - result = (int) endResult; - - QString outputString; - - outputString = sig.name + ": "; - - if (sig.valList.count() > 0) //if this is a value list type then look it up and display the proper string - { - for (int x = 0; x < sig.valList.count(); x++) - { - if (sig.valList.at(x).value == result) outputString += sig.valList.at(x).descript; - } - } - else //otherwise display the actual number and unit (if it exists) - { - outputString += QString::number(endResult) + sig.unitName; - } - - return outputString; + return NULL; } -//given a byte it will reverse the bit order in that byte -unsigned char DBCHandler::reverseBits(unsigned char b) { - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - return b; -} - -unsigned char DBCHandler::processByte(unsigned char input, int start, int end) +int DBCHandler::getFileCount() { - unsigned char output = 0, size = end - start + 1; - //first knock it down so that bottom is is start - output = input >> start; - //then mask off all bits above the proper ending - output &= ((1 << size) - 1); - return output; + return loadedFiles.count(); +} + +DBCFile* DBCHandler::getFileByIdx(int idx) +{ + if (loadedFiles.count() == 0) return NULL; + if (idx < 0) return NULL; + if (idx >= loadedFiles.count()) return NULL; + return &loadedFiles[idx]; +} + +DBCFile* DBCHandler::getFileByName(QString name) +{ + if (loadedFiles.count() == 0) return NULL; + for (int i = 0; i < loadedFiles.count(); i++) + { + if (loadedFiles[i].getFilename().compare(name, Qt::CaseInsensitive) == 0) + { + return &loadedFiles[i]; + } + } + return NULL; } diff --git a/dbchandler.h b/dbchandler.h index b49645c..80376da 100644 --- a/dbchandler.h +++ b/dbchandler.h @@ -6,40 +6,90 @@ #include "can_structs.h" /* - * For ease of quick testing and development this is all run together. - * It should be decoupled once the functionality is confirmed. + * TODO: + * Finish coding up the decoupled design + * */ -class DBCHandler : public QObject + +class DBCSignalHandler: public QObject { Q_OBJECT public: - explicit DBCHandler(QObject *parent = 0); - void loadDBCFile(QString); - void saveDBCFile(QString); - void listDebugging(); - QString processSignal(const CANFrame &frame, const DBC_SIGNAL &sig); - DBC_NODE *findNodeByName(QString name); - DBC_NODE *findNodeByIdx(int idx); + DBC_SIGNAL *findSignalByName(QString name); + DBC_SIGNAL *findSignalByIdx(int idx); + bool addSignal(DBC_SIGNAL &sig); + bool removeSignal(DBC_SIGNAL *sig); + bool removeSignal(int idx); + bool removeSignal(QString name); + void removeAllSignals(); + int getCount(); +private: + QList sigs; //signals is a reserved word or I'd have used that +}; +class DBCMessageHandler: public QObject +{ + Q_OBJECT +public: DBC_MESSAGE *findMsgByID(int id); DBC_MESSAGE *findMsgByIdx(int idx); DBC_MESSAGE *findMsgByName(QString name); + bool addMessage(DBC_MESSAGE &msg); + bool removeMessage(DBC_MESSAGE *msg); + bool removeMessageByIndex(int idx); + bool removeMessage(int ID); + bool removeMessage(QString name); + void removeAllMessages(); + int getCount(); +private: + QList messages; +}; - DBC_SIGNAL *findSignalByName(DBC_MESSAGE *msg, QString name); - DBC_SIGNAL *findSignalByIdx(DBC_MESSAGE *msg, int idx); +//technically there should be a node handler too but I'm sort of treating nodes as second class +//citizens since they aren't really all that important (to me anyway) +class DBCFile: public QObject +{ + Q_OBJECT +public: + DBCFile(); + DBCFile(const DBCFile& cpy); + DBCFile& operator=(const DBCFile& cpy); + DBC_NODE *findNodeByName(QString name); + DBC_NODE *findNodeByIdx(int idx); + void saveFile(QString); + void loadFile(QString); + QString getFullFilename(); + QString getFilename(); + QString getPath(); + int getAssocBus(); + void setAssocBus(int bus); + DBCMessageHandler *messageHandler; QList dbc_nodes; - QList dbc_messages; +private: + QString fileName; + QString filePath; + int assocBuses; //-1 = all buses, 0 = first bus, 1 = second bus +}; -signals: - -public slots: +class DBCHandler: public QObject +{ + Q_OBJECT +public: + DBCFile* loadDBCFile(int); + void saveDBCFile(int); + void removeDBCFile(int); + void removeAllFiles(); + void swapFiles(int pos1, int pos2); + DBC_MESSAGE* findMessage(const CANFrame &frame); + int getFileCount(); + DBCFile* getFileByIdx(int idx); + DBCFile* getFileByName(QString name); + int createBlankFile(); private: - - unsigned char reverseBits(unsigned char); - unsigned char processByte(unsigned char, int, int); + QList loadedFiles; }; #endif // DBCHANDLER_H diff --git a/dbcloadsavewindow.cpp b/dbcloadsavewindow.cpp new file mode 100644 index 0000000..e66a9b5 --- /dev/null +++ b/dbcloadsavewindow.cpp @@ -0,0 +1,151 @@ +#include "dbcloadsavewindow.h" +#include "ui_dbcloadsavewindow.h" + +DBCLoadSaveWindow::DBCLoadSaveWindow(DBCHandler *handler, const QVector *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::DBCLoadSaveWindow) +{ + + dbcHandler = handler; + referenceFrames = frames; + + ui->setupUi(this); + + QStringList header; + header << "Filename" << "Associated Bus"; + ui->tableFiles->setColumnCount(2); + ui->tableFiles->setHorizontalHeaderLabels(header); + ui->tableFiles->setColumnWidth(0, 355); + ui->tableFiles->setColumnWidth(1, 125); + + connect(ui->btnEdit, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::editFile); + connect(ui->btnLoad, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::loadFile); + connect(ui->btnMoveDown, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::moveDown); + connect(ui->btnMoveUp, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::moveUp); + connect(ui->btnRemove, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::removeFile); + connect(ui->btnSave, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::saveFile); + connect(ui->btnNewDBC, &QAbstractButton::clicked, this, &DBCLoadSaveWindow::newFile); + connect(ui->tableFiles, &QTableWidget::cellChanged, this, &DBCLoadSaveWindow::cellChanged); + connect(ui->tableFiles, &QTableWidget::cellDoubleClicked, this, &DBCLoadSaveWindow::cellDoubleClicked); + + editorWindow = new DBCMainEditor(handler, frames); +} + +DBCLoadSaveWindow::~DBCLoadSaveWindow() +{ + delete ui; +} + +void DBCLoadSaveWindow::newFile() +{ + int idx = dbcHandler->createBlankFile(); + idx = ui->tableFiles->rowCount(); + ui->tableFiles->insertRow(ui->tableFiles->rowCount()); + ui->tableFiles->setItem(idx, 0, new QTableWidgetItem("UNNAMEDFILE")); + ui->tableFiles->setItem(idx, 1, new QTableWidgetItem("-1")); +} + +void DBCLoadSaveWindow::loadFile() +{ + DBCFile *file = dbcHandler->loadDBCFile(-1); + int idx = ui->tableFiles->rowCount(); + ui->tableFiles->insertRow(ui->tableFiles->rowCount()); + ui->tableFiles->setItem(idx, 0, new QTableWidgetItem(file->getFullFilename())); + ui->tableFiles->setItem(idx, 1, new QTableWidgetItem("-1")); +} + +void DBCLoadSaveWindow::saveFile() +{ + int idx = ui->tableFiles->currentRow(); + if (idx < 0) return; + dbcHandler->saveDBCFile(idx); + //then update the list to show the new file name (if it changed) + ui->tableFiles->setItem(idx, 0, new QTableWidgetItem(dbcHandler->getFileByIdx(idx)->getFullFilename())); +} + +void DBCLoadSaveWindow::removeFile() +{ + int idx = ui->tableFiles->currentRow(); + if (idx < 0) return; + dbcHandler->removeDBCFile(idx); + ui->tableFiles->removeRow(idx); +} + +void DBCLoadSaveWindow::moveUp() +{ + int idx = ui->tableFiles->currentRow(); + if (idx < 1) return; + dbcHandler->swapFiles(idx - 1, idx); + swapTableRows(true); +} + +void DBCLoadSaveWindow::moveDown() +{ + int idx = ui->tableFiles->currentRow(); + if (idx < 0) return; + if (idx > (dbcHandler->getFileCount() - 2)) return; + dbcHandler->swapFiles(idx, idx + 1); + swapTableRows(false); +} + +void DBCLoadSaveWindow::editFile() +{ + int idx = ui->tableFiles->currentRow(); + if (idx < 0) return; + + editorWindow->setFileIdx(idx); + editorWindow->show(); +} + +void DBCLoadSaveWindow::cellChanged(int row, int col) +{ + if (col == 1) //the bus column + { + DBCFile *file = dbcHandler->getFileByIdx(row); + int bus = ui->tableFiles->item(row, col)->text().toInt(); + if (bus > -2 && bus < 2) + { + file->setAssocBus(bus); + } + } +} + +void DBCLoadSaveWindow::cellDoubleClicked(int row, int col) +{ + editorWindow->setFileIdx(row); + editorWindow->show(); +} + +void DBCLoadSaveWindow::swapTableRows(bool up) +{ + int idx = ui->tableFiles->currentRow(); + const int destIdx = (up ? idx-1 : idx+1); + Q_ASSERT(destIdx >= 0 && destIdx < ui->tableFiles->rowCount()); + + // take whole rows + QList sourceItems = takeRow(idx); + QList destItems = takeRow(destIdx); + + // set back in reverse order + setRow(idx, destItems); + setRow(destIdx, sourceItems); +} + +QList DBCLoadSaveWindow::takeRow(int row) +{ + QList rowItems; + for (int col = 0; col < ui->tableFiles->columnCount(); ++col) + { + rowItems << ui->tableFiles->takeItem(row, col); + } + return rowItems; +} + +void DBCLoadSaveWindow::setRow(int row, const QList& rowItems) +{ + for (int col = 0; col < ui->tableFiles->columnCount(); ++col) + { + ui->tableFiles->setItem(row, col, rowItems.at(col)); + } +} + diff --git a/dbcloadsavewindow.h b/dbcloadsavewindow.h new file mode 100644 index 0000000..41e33ac --- /dev/null +++ b/dbcloadsavewindow.h @@ -0,0 +1,44 @@ +#ifndef DBCLOADSAVEWINDOW_H +#define DBCLOADSAVEWINDOW_H + +#include +#include +#include "dbchandler.h" +#include "dbcmaineditor.h" + +namespace Ui { +class DBCLoadSaveWindow; +} + +class DBCLoadSaveWindow : public QDialog +{ + Q_OBJECT + +public: + explicit DBCLoadSaveWindow(DBCHandler *handler, const QVector *frames, QWidget *parent = 0); + ~DBCLoadSaveWindow(); + +private slots: + void loadFile(); + void saveFile(); + void removeFile(); + void moveUp(); + void moveDown(); + void editFile(); + void cellChanged(int row, int col); + void cellDoubleClicked(int row, int col); + void newFile(); + +private: + Ui::DBCLoadSaveWindow *ui; + DBCHandler *dbcHandler; + const QVector *referenceFrames; + DBCMainEditor *editorWindow; + + void swapTableRows(bool up); + QList takeRow(int row); + void setRow(int row, const QList& rowItems); +}; + +#endif // DBCLOADSAVEWINDOW_H + diff --git a/dbcloadsavewindow.ui b/dbcloadsavewindow.ui new file mode 100644 index 0000000..d32abf1 --- /dev/null +++ b/dbcloadsavewindow.ui @@ -0,0 +1,81 @@ + + + DBCLoadSaveWindow + + + + 0 + 0 + 506 + 665 + + + + DBC File Manager + + + + + + + + + Create new DBC + + + + + + + + + Load + + + + + + + Remove + + + + + + + Move Up + + + + + + + + + + + Save + + + + + + + Edit + + + + + + + Move Down + + + + + + + + + + diff --git a/dbcmaineditor.cpp b/dbcmaineditor.cpp index 44c1e79..7a82e9b 100644 --- a/dbcmaineditor.cpp +++ b/dbcmaineditor.cpp @@ -54,8 +54,8 @@ void DBCMainEditor::showEvent(QShowEvent* event) inhibitCellChanged = true; refreshNodesTable(); - if (dbcHandler->dbc_nodes.count() > 0) - refreshMessagesTable(&dbcHandler->dbc_nodes.at(0)); + if (dbcFile->dbc_nodes.count() > 0) + refreshMessagesTable(&dbcFile->dbc_nodes.at(0)); currRow = 0; inhibitCellChanged = false; @@ -67,6 +67,13 @@ DBCMainEditor::~DBCMainEditor() delete sigEditor; } +void DBCMainEditor::setFileIdx(int idx) +{ + if (idx < 0 || idx > dbcHandler->getFileCount() - 1) return; + dbcFile = dbcHandler->getFileByIdx(idx); + fileIdx = idx; +} + void DBCMainEditor::closeEvent(QCloseEvent *event) { Q_UNUSED(event); @@ -123,9 +130,9 @@ void DBCMainEditor::deleteCurrentNode() if (nodeName.length() > 0 && nodeName.compare("Vector__XXX", Qt::CaseInsensitive) != 0) { ui->NodesTable->removeRow(thisRow); - dbcHandler->dbc_nodes.removeAt(thisRow); + dbcFile->dbc_nodes.removeAt(thisRow); inhibitCellChanged = true; - refreshMessagesTable(&dbcHandler->dbc_nodes[0]); + refreshMessagesTable(&dbcFile->dbc_nodes[0]); ui->NodesTable->selectRow(0); inhibitCellChanged = false; } @@ -137,7 +144,7 @@ void DBCMainEditor::deleteCurrentMessage() if (ui->MessagesTable->item(thisRow, 0)->text().length() > 0) { ui->MessagesTable->removeRow(thisRow); - dbcHandler->dbc_messages.removeAt(thisRow); + dbcFile->messageHandler->removeMessageByIndex(thisRow); } } @@ -156,7 +163,7 @@ void DBCMainEditor::onCellChangedNode(int row,int col) QString newName = ui->NodesTable->item(row, col)->text().simplified().replace(' ', '_'); qDebug() << "new name: " << newName; if (newName.length() == 0) return; - if (dbcHandler->findNodeByName(newName) != NULL) //duplicates an existing node! + if (dbcFile->findNodeByName(newName) != NULL) //duplicates an existing node! { QMessageBox msg; msg.setParent(0); @@ -165,8 +172,8 @@ void DBCMainEditor::onCellChangedNode(int row,int col) return; } newNode.name = newName; - dbcHandler->dbc_nodes.append(newNode); - qDebug() << "# of nodes now " << dbcHandler->dbc_nodes.count(); + dbcFile->dbc_nodes.append(newNode); + qDebug() << "# of nodes now " << dbcFile->dbc_nodes.count(); QTableWidgetItem *widgetName = new QTableWidgetItem(newName); inhibitCellChanged = true; ui->NodesTable->setItem(row, col, widgetName); @@ -178,7 +185,7 @@ void DBCMainEditor::onCellChangedNode(int row,int col) { if (col == 0) { - DBC_NODE *oldNode = dbcHandler->findNodeByIdx(row); + DBC_NODE *oldNode = dbcFile->findNodeByIdx(row); QString nodeName = ui->NodesTable->item(row, col)->text().simplified().replace(' ', '_'); if (oldNode == NULL) return; if (row != 0) oldNode->name = nodeName; @@ -192,7 +199,7 @@ void DBCMainEditor::onCellChangedNode(int row,int col) { QString nodeName = ui->NodesTable->item(row, 0)->text().simplified().replace(' ', '_'); qDebug() << "searching for node " << nodeName; - DBC_NODE *thisNode = dbcHandler->findNodeByName(nodeName); + DBC_NODE *thisNode = dbcFile->findNodeByName(nodeName); if (thisNode == NULL) return; thisNode->comment = ui->NodesTable->item(row, col)->text().simplified(); qDebug() << "New comment: " << thisNode->comment; @@ -213,7 +220,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col) int msgLen; QString msgComment; DBC_MESSAGE newMsg; - DBC_NODE *node = dbcHandler->findNodeByIdx(ui->NodesTable->currentRow()); + DBC_NODE *node = dbcFile->findNodeByIdx(ui->NodesTable->currentRow()); DBC_MESSAGE *msg; if (node == NULL) { @@ -224,14 +231,14 @@ void DBCMainEditor::onCellChangedMessage(int row,int col) msgID = Utility::ParseStringToNum(ui->MessagesTable->item(row, 0)->text()); qDebug() << "Msg ID of edited: " << msgID; - msg = dbcHandler->findMsgByID(msgID); + msg = dbcFile->messageHandler->findMsgByID(msgID); switch(col) { case 0: //msg id if (row == ui->MessagesTable->rowCount() - 1) //new record { - if (dbcHandler->findMsgByID(msgID) != NULL) + if (dbcFile->messageHandler->findMsgByID(msgID) != NULL) { QMessageBox msg; msg.setParent(0); @@ -251,7 +258,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col) break; } } - dbcHandler->dbc_messages.append(newMsg); + dbcFile->messageHandler->addMessage(newMsg); } else //editing an existing record { @@ -270,7 +277,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col) if (msgName.length() == 0) return; if (row == ui->MessagesTable->rowCount() - 1) //new record { - if (dbcHandler->findMsgByName(msgName) != NULL) + if (dbcFile->messageHandler->findMsgByName(msgName) != NULL) { QMessageBox msg; msg.setParent(0); @@ -281,7 +288,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col) newMsg.ID = -1; newMsg.name = msgName; newMsg.sender = node; - dbcHandler->dbc_messages.append(newMsg); + dbcFile->messageHandler->addMessage(newMsg); } else { @@ -304,7 +311,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col) newMsg.name = ""; newMsg.len = msgLen; newMsg.sender = node; - dbcHandler->dbc_messages.append(newMsg); + dbcFile->messageHandler->addMessage(newMsg); } else //editing an existing record { @@ -328,7 +335,7 @@ void DBCMainEditor::onCellChangedMessage(int row,int col) newMsg.len = 0; newMsg.comment = msgComment; newMsg.sender = node; - dbcHandler->dbc_messages.append(newMsg); + dbcFile->messageHandler->addMessage(newMsg); } else //editing an existing record { @@ -363,7 +370,7 @@ void DBCMainEditor::onCellClickedNode(int row, int col) nodeName = item->text(); qDebug() << "Trying to find node with name " << nodeName; - DBC_NODE *node = dbcHandler->findNodeByName(nodeName); + DBC_NODE *node = dbcFile->findNodeByName(nodeName); //qDebug() << "Address of node: " << (int)node; inhibitCellChanged = true; refreshMessagesTable(node); @@ -376,12 +383,13 @@ void DBCMainEditor::onCellClickedMessage(int row, int col) if (col == 3) //3 is the signals field. If clicked we go to the signals dialog { QString idString = ui->MessagesTable->item(row, 0)->text(); - DBC_MESSAGE *message = dbcHandler->findMsgByID(Utility::ParseStringToNum(idString)); + DBC_MESSAGE *message = dbcFile->messageHandler->findMsgByID(Utility::ParseStringToNum(idString)); sigEditor->setMessageRef(message); + sigEditor->setFileIdx(fileIdx); sigEditor->exec(); //blocks this window from being active until we're done //now update the displayed # of signals - inhibitCellChanged = true; - QTableWidgetItem *replacement = new QTableWidgetItem(QString::number(message->msgSignals.count())); + inhibitCellChanged = true; + QTableWidgetItem *replacement = new QTableWidgetItem(QString::number(message->sigHandler->getCount())); ui->MessagesTable->setItem(row, col, replacement); inhibitCellChanged = false; } @@ -394,17 +402,17 @@ void DBCMainEditor::refreshNodesTable() int rowIdx; - if (dbcHandler->findNodeByName("Vector__XXX") == NULL) + if (dbcFile->findNodeByName("Vector__XXX") == NULL) { DBC_NODE newNode; newNode.name = "Vector__XXX"; newNode.comment = "Default node if no other node is specified"; - dbcHandler->dbc_nodes.append(newNode); + dbcFile->dbc_nodes.append(newNode); } - for (int x = 0; x < dbcHandler->dbc_nodes.count(); x++) + for (int x = 0; x < dbcFile->dbc_nodes.count(); x++) { - DBC_NODE node = dbcHandler->dbc_nodes.at(x); + DBC_NODE node = dbcFile->dbc_nodes.at(x); QTableWidgetItem *nodeName = new QTableWidgetItem(node.name); QTableWidgetItem *nodeComment = new QTableWidgetItem(node.comment); rowIdx = ui->NodesTable->rowCount(); @@ -428,17 +436,17 @@ void DBCMainEditor::refreshMessagesTable(const DBC_NODE *node) if (node != NULL) { - for (int x = 0; x < dbcHandler->dbc_messages.count(); x++) + for (int x = 0; x < dbcFile->messageHandler->getCount(); x++) { - DBC_MESSAGE msg = dbcHandler->dbc_messages.at(x); - if (msg.sender == node) + DBC_MESSAGE *msg = dbcFile->messageHandler->findMsgByIdx(x); + if (msg->sender == node) { //many of these are simplistic first versions just to test functionality. - QTableWidgetItem *msgID = new QTableWidgetItem(Utility::formatNumber(msg.ID)); - QTableWidgetItem *msgName = new QTableWidgetItem(msg.name); - QTableWidgetItem *msgLen = new QTableWidgetItem(QString::number(msg.len)); - QTableWidgetItem *msgSignals = new QTableWidgetItem(QString::number(msg.msgSignals.count())); - QTableWidgetItem *msgComment = new QTableWidgetItem(msg.comment); + QTableWidgetItem *msgID = new QTableWidgetItem(Utility::formatNumber(msg->ID)); + QTableWidgetItem *msgName = new QTableWidgetItem(msg->name); + QTableWidgetItem *msgLen = new QTableWidgetItem(QString::number(msg->len)); + QTableWidgetItem *msgSignals = new QTableWidgetItem(QString::number(msg->sigHandler->getCount())); + QTableWidgetItem *msgComment = new QTableWidgetItem(msg->comment); rowIdx = ui->MessagesTable->rowCount(); ui->MessagesTable->insertRow(rowIdx); diff --git a/dbcmaineditor.h b/dbcmaineditor.h index 0225cc8..07e8e11 100644 --- a/dbcmaineditor.h +++ b/dbcmaineditor.h @@ -18,6 +18,7 @@ class DBCMainEditor : public QDialog public: explicit DBCMainEditor(DBCHandler *handler, const QVector *frames, QWidget *parent = 0); ~DBCMainEditor(); + void setFileIdx(int idx); private slots: void onCellChangedNode(int,int); @@ -35,6 +36,8 @@ private: const QVector *referenceFrames; DBCSignalEditor *sigEditor; int currRow; + DBCFile *dbcFile; + int fileIdx; bool inhibitCellChanged; void refreshNodesTable(); diff --git a/dbcsignaleditor.cpp b/dbcsignaleditor.cpp index 6f439f7..c7f73e0 100644 --- a/dbcsignaleditor.cpp +++ b/dbcsignaleditor.cpp @@ -33,11 +33,6 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : ui->comboType->addItem("DOUBLE PRECISION"); ui->comboType->addItem("STRING"); - for (int x = 0; x < dbcHandler->dbc_nodes.count(); x++) - { - ui->comboReceiver->addItem(dbcHandler->dbc_nodes[x].name); - } - connect(ui->signalsList, SIGNAL(currentRowChanged(int)), this, SLOT(clickSignalList(int))); connect(ui->bitfield, SIGNAL(gridClicked(int,int)), this, SLOT(bitfieldClicked(int,int))); connect(ui->signalsList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuSignals(QPoint))); @@ -52,6 +47,8 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : { if (currentSignal == NULL) return; currentSignal->intelByteOrder = ui->cbIntelFormat->isChecked(); + if (currentSignal->valType == SP_FLOAT || currentSignal->valType == DP_FLOAT) + currentSignal->intelByteOrder = false; fillSignalForm(currentSignal); }); @@ -59,7 +56,7 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : [=]() { if (currentSignal == NULL) return; - currentSignal->receiver = dbcHandler->findNodeByName(ui->comboReceiver->currentText()); + currentSignal->receiver = dbcFile->findNodeByName(ui->comboReceiver->currentText()); }); connect(ui->comboType, &QComboBox::currentTextChanged, [=]() @@ -75,14 +72,21 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : break; case 2: currentSignal->valType = SP_FLOAT; + currentSignal->intelByteOrder = false; + if (currentSignal->startBit > 39) currentSignal->startBit = 39; + currentSignal->signalSize = 32; break; case 3: currentSignal->valType = DP_FLOAT; + currentSignal->intelByteOrder = false; + currentSignal->startBit = 7; //has to be! + currentSignal->signalSize = 64; break; case 4: currentSignal->valType = STRING; - break; + break; } + fillSignalForm(currentSignal); }); connect(ui->txtBias, &QLineEdit::editingFinished, [=]() @@ -143,7 +147,8 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : temp = Utility::ParseStringToNum(ui->txtBitLength->text()); if (temp < 0) return; if (temp > 63) return; - currentSignal->signalSize = temp; + if (currentSignal->valType != SP_FLOAT && currentSignal->valType != DP_FLOAT) + currentSignal->signalSize = temp; fillSignalForm(currentSignal); }); connect(ui->txtName, &QLineEdit::editingFinished, @@ -154,6 +159,52 @@ DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) : //need to update the list too. ui->signalsList->currentItem()->setText(currentSignal->name); }); + + connect(ui->txtMultiplexValue, &QLineEdit::editingFinished, + [=]() + { + if (currentSignal == NULL) return; + int temp; + temp = Utility::ParseStringToNum(ui->txtMultiplexValue->text()); + //TODO: could look up the multiplexor and ensure that the value is within a range that the multiplexor could return + currentSignal->multiplexValue = temp; + }); + connect(ui->rbMultiplexed, &QRadioButton::toggled, + [=](bool state) + { + if (state) //signal is now set as a multiplexed signal + { + currentSignal->isMultiplexed = true; + currentSignal->isMultiplexor = false; + //if the set multiplexor for the message was this signal then clear it + if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = NULL; + } + }); + + connect(ui->rbMultiplexor, &QRadioButton::toggled, + [=](bool state) + { + if (state) //signal is now set as a multiplexed signal + { + //don't allow this signal to be a multiplexor if there is already one for this message. + //if (dbcMessage->multiplexorSignal != currentSignal && dbcMessage->multiplexorSignal != NULL) return; //I spoke too soon above... + currentSignal->isMultiplexed = false; + currentSignal->isMultiplexor = true; + //we just set that this is the multiplexor so update the message to show that as well. + dbcMessage->multiplexorSignal = currentSignal; + } + }); + + connect(ui->rbNotMulti, &QRadioButton::toggled, + [=](bool state) + { + if (state) //signal is now set as a multiplexed signal + { + currentSignal->isMultiplexed = false; + currentSignal->isMultiplexor = false; + if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = NULL; + } + }); } DBCSignalEditor::~DBCSignalEditor() @@ -167,6 +218,17 @@ void DBCSignalEditor::closeEvent(QCloseEvent *event) writeSettings(); } +void DBCSignalEditor::setFileIdx(int idx) +{ + if (idx < 0 || idx > dbcHandler->getFileCount() - 1) return; + dbcFile = dbcHandler->getFileByIdx(idx); + + for (int x = 0; x < dbcFile->dbc_nodes.count(); x++) + { + ui->comboReceiver->addItem(dbcFile->dbc_nodes[x].name); + } +} + void DBCSignalEditor::readSettings() { QSettings settings; @@ -200,9 +262,9 @@ void DBCSignalEditor::showEvent(QShowEvent* event) refreshSignalsList(); currentSignal = NULL; - if (dbcMessage->msgSignals.count() > 0) + if (dbcMessage->sigHandler->getCount() > 0) { - currentSignal = &dbcMessage->msgSignals[0]; + currentSignal = dbcMessage->sigHandler->findSignalByIdx(0); fillSignalForm(currentSignal); fillValueTable(currentSignal); } @@ -270,12 +332,17 @@ void DBCSignalEditor::addNewSignal() newSig.intelByteOrder = true; newSig.max = 0.0; newSig.min = 0.0; - newSig.receiver = &dbcHandler->dbc_nodes[0]; + newSig.receiver = dbcFile->findNodeByIdx(0); newSig.signalSize = 1; newSig.startBit = 0; newSig.valType = UNSIGNED_INT; + newSig.isMultiplexed = false; + newSig.isMultiplexor = false; + newSig.multiplexValue = 0; + newSig.parentMessage = dbcMessage; ui->signalsList->addItem(newName); - dbcMessage->msgSignals.append(newSig); + dbcMessage->sigHandler->addSignal(newSig); + if (dbcMessage->sigHandler->getCount() == 1) clickSignalList(0); } void DBCSignalEditor::deleteCurrentSignal() @@ -284,10 +351,10 @@ void DBCSignalEditor::deleteCurrentSignal() if (currIdx > -1) { delete(ui->signalsList->item(currIdx)); - dbcMessage->msgSignals.removeAt(currIdx); + dbcMessage->sigHandler->removeSignal(currIdx); currentSignal = NULL; currIdx = ui->signalsList->currentRow(); - if (currIdx > -1) currentSignal = &dbcMessage->msgSignals[currIdx]; + if (currIdx > -1) currentSignal = dbcMessage->sigHandler->findSignalByIdx(currIdx); fillSignalForm(currentSignal); fillValueTable(currentSignal); } @@ -306,10 +373,10 @@ void DBCSignalEditor::deleteCurrentValue() void DBCSignalEditor::refreshSignalsList() { ui->signalsList->clear(); - for (int x = 0; x < dbcMessage->msgSignals.count(); x++) + for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++) { - DBC_SIGNAL sig = dbcMessage->msgSignals.at(x); - ui->signalsList->addItem(sig.name); + DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x); + ui->signalsList->addItem(sig->name); } } @@ -317,6 +384,8 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) { unsigned char bitpattern[8]; + generateUsedBits(); + if (sig == NULL) { ui->txtName->setText(""); @@ -327,6 +396,10 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) ui->txtMinVal->setText(""); ui->txtScale->setText(""); ui->txtUnitName->setText(""); + ui->txtMultiplexValue->setText(""); + ui->rbMultiplexed->setChecked(false); + ui->rbMultiplexor->setChecked(false); + ui->rbNotMulti->setChecked(true); memset(bitpattern, 0, 8); //clear it out ui->bitfield->setReference(bitpattern, false); ui->bitfield->updateData(bitpattern, true); @@ -338,11 +411,16 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) ui->txtName->setText(sig->name); ui->txtBias->setText(QString::number(sig->bias)); ui->txtBitLength->setText(QString::number(sig->signalSize)); + ui->txtMultiplexValue->setText(QString::number(sig->multiplexValue)); ui->txtComment->setText(sig->comment); ui->txtMaxVal->setText(QString::number(sig->max)); ui->txtMinVal->setText(QString::number(sig->min)); ui->txtScale->setText(QString::number(sig->factor)); ui->txtUnitName->setText(sig->unitName); + ui->rbMultiplexed->setChecked(sig->isMultiplexed); + ui->rbMultiplexor->setChecked(sig->isMultiplexor); + ui->rbNotMulti->setChecked( !(sig->isMultiplexor | sig->isMultiplexed) ); + qDebug() << sig->isMultiplexor << "*" << sig->isMultiplexed; memset(bitpattern, 0, 8); //clear it out first. @@ -379,7 +457,6 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) } } - ui->bitfield->updateData(bitpattern, true); ui->cbIntelFormat->setChecked(sig->intelByteOrder); @@ -446,12 +523,12 @@ void DBCSignalEditor::clickSignalList(int row) { if (row < 0) return; //qDebug() << ui->signalsList->item(row)->text(); - DBC_SIGNAL *thisSig = dbcHandler->findSignalByName(dbcMessage, ui->signalsList->item(row)->text()); + + DBC_SIGNAL *thisSig = dbcMessage->sigHandler->findSignalByName(ui->signalsList->item(row)->text()); if (thisSig == NULL) return; currentSignal = thisSig; fillSignalForm(thisSig); fillValueTable(thisSig); - } void DBCSignalEditor::bitfieldClicked(int x, int y) @@ -459,5 +536,52 @@ void DBCSignalEditor::bitfieldClicked(int x, int y) int bit = (7 - x) + (y * 8); if (currentSignal == NULL) return; currentSignal->startBit = bit; + if (currentSignal->valType == SP_FLOAT) + { + if (currentSignal->startBit > 31) currentSignal->startBit = 39; + } + + if (currentSignal->valType == DP_FLOAT) + currentSignal->startBit = 7; fillSignalForm(currentSignal); } + +void DBCSignalEditor::generateUsedBits() +{ + uint8_t usedBits[8] = {0,0,0,0,0,0,0,0}; + int startBit, endBit; + + for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++) + { + DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x); + + startBit = sig->startBit; + + if (sig->intelByteOrder) + { + endBit = startBit + sig->signalSize - 1; + if (startBit < 0) startBit = 0; + if (endBit > 63) endBit = 63; + for (int y = startBit; y <= endBit; y++) + { + int byt = y / 8; + usedBits[byt] |= 1 << (y % 8); + } + } + else //big endian / motorola format + { + //much more irritating than the intel version... + int size = sig->signalSize; + while (size > 0) + { + int byt = startBit / 8; + usedBits[byt] |= 1 << (startBit % 8); + size--; + if ((startBit % 8) == 0) startBit += 15; + else startBit--; + if (startBit > 63) startBit = 63; + } + } + } + ui->bitfield->setUsed(usedBits, false); +} diff --git a/dbcsignaleditor.h b/dbcsignaleditor.h index 139b0d6..5d89e14 100644 --- a/dbcsignaleditor.h +++ b/dbcsignaleditor.h @@ -17,6 +17,7 @@ public: explicit DBCSignalEditor(DBCHandler *handler, QWidget *parent = 0); void setMessageRef(DBC_MESSAGE *msg); void showEvent(QShowEvent*); + void setFileIdx(int idx); ~DBCSignalEditor(); private slots: @@ -34,11 +35,13 @@ private: DBCHandler *dbcHandler; DBC_MESSAGE *dbcMessage; DBC_SIGNAL *currentSignal; + DBCFile *dbcFile; bool inhibitCellChanged; void refreshSignalsList(); void fillSignalForm(DBC_SIGNAL *sig); void fillValueTable(DBC_SIGNAL *sig); + void generateUsedBits(); void closeEvent(QCloseEvent *event); void readSettings(); diff --git a/dbcsignaleditor.ui b/dbcsignaleditor.ui index 8f46097..9348c7b 100644 --- a/dbcsignaleditor.ui +++ b/dbcsignaleditor.ui @@ -7,7 +7,7 @@ 0 0 884 - 791 + 915 @@ -118,7 +118,7 @@ - + Comment: @@ -153,9 +153,51 @@ - + + + + + Multiplexing + + + + + + + + + Not multiplexed + + + + + + + Multiplexed + + + + + + + Multiplexor + + + + + + + + + Multiplex Value + + + + + + diff --git a/discretestatewindow.cpp b/discretestatewindow.cpp index d198ae3..d5b5f80 100644 --- a/discretestatewindow.cpp +++ b/discretestatewindow.cpp @@ -44,6 +44,7 @@ void DiscreteStateWindow::updatedFrames(int numFrames) } else //just got some new frames. See if they are relevant. { + if (numFrames > modelFrames->count()) return; for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) { thisFrame = modelFrames->at(i); diff --git a/filecomparatorwindow.cpp b/filecomparatorwindow.cpp index c7dde9c..83f120e 100644 --- a/filecomparatorwindow.cpp +++ b/filecomparatorwindow.cpp @@ -58,11 +58,11 @@ void FileComparatorWindow::writeSettings() void FileComparatorWindow::loadInterestedFile() { interestedFrames.clear(); - QString result = FrameFileIO::loadFrameFile(&interestedFrames); - if (result.length() > 0) + QString resultingFileName; + if (FrameFileIO::loadFrameFile(resultingFileName, &interestedFrames)) { - ui->lblFirstFile->setText(result); - interestedFilename = result; + ui->lblFirstFile->setText(resultingFileName); + interestedFilename = resultingFileName; if (interestedFrames.count() > 0 && referenceFrames.count() > 0) calculateDetails(); } } @@ -70,8 +70,8 @@ void FileComparatorWindow::loadInterestedFile() void FileComparatorWindow::loadReferenceFile() { //secondFileFrames.clear(); - QString result = FrameFileIO::loadFrameFile(&referenceFrames); - if (result.length() > 0) + QString resultingFileName; + if (FrameFileIO::loadFrameFile(resultingFileName, &referenceFrames)) { ui->lblRefFrames->setText(QString::number(referenceFrames.length())); if (interestedFrames.count() > 0 && referenceFrames.count() > 0) calculateDetails(); diff --git a/flowviewwindow.cpp b/flowviewwindow.cpp index b52766e..d4fe69d 100644 --- a/flowviewwindow.cpp +++ b/flowviewwindow.cpp @@ -370,6 +370,7 @@ void FlowViewWindow::updatedFrames(int numFrames) } else //just got some new frames. See if they are relevant. { + if (numFrames > modelFrames->count()) return; int refID = frameCache[0].ID; bool needRefresh = false; for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) diff --git a/flowviewwindow.ui b/flowviewwindow.ui index a3b3ddf..81ddf9f 100644 --- a/flowviewwindow.ui +++ b/flowviewwindow.ui @@ -799,7 +799,7 @@ - Trigger Values + Data Seek Values diff --git a/framefileio.cpp b/framefileio.cpp index c2fad71..211b23e 100644 --- a/framefileio.cpp +++ b/framefileio.cpp @@ -7,19 +7,121 @@ FrameFileIO::FrameFileIO() } -QString FrameFileIO::loadFrameFile(QVector* frameCache) +bool FrameFileIO::saveFrameFile(QString &fileName, const QVector* frameCache) +{ + QString filename; + QFileDialog dialog(qApp->activeWindow()); + bool result = false; + + QStringList filters; + filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); + filters.append(QString(tr("CRTD Logs (*.txt *.TXT)"))); + filters.append(QString(tr("Generic ID/Data CSV (*.csv *.CSV)"))); + filters.append(QString(tr("BusMaster Log (*.log *.LOG)"))); + filters.append(QString(tr("Microchip Log (*.can *.CAN)"))); + filters.append(QString(tr("Vector Trace Files (*.trace *.TRACE)"))); + filters.append(QString(tr("IXXAT MiniLog (*.csv *.CSV)"))); + filters.append(QString(tr("CAN-DO Log (*.can *.avc *.evc *.qcc *.CAN *.AVC *.EVC *.QCC)"))); + filters.append(QString(tr("Vehicle Spy (*.csv *.CSV)"))); + + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setNameFilters(filters); + dialog.setViewMode(QFileDialog::Detail); + dialog.setAcceptMode(QFileDialog::AcceptSave); + + if (dialog.exec() == QDialog::Accepted) + { + filename = dialog.selectedFiles()[0]; + + QProgressDialog progress(qApp->activeWindow()); + progress.setWindowModality(Qt::WindowModal); + progress.setLabelText("Saving file..."); + progress.setCancelButton(0); + progress.setRange(0,0); + progress.setMinimumDuration(0); + progress.show(); + + qApp->processEvents(); + + if (dialog.selectedNameFilter() == filters[0]) + { + if (!filename.contains('.')) filename += ".csv"; + result = saveNativeCSVFile(filename, frameCache); + } + if (dialog.selectedNameFilter() == filters[1]) + { + if (!filename.contains('.')) filename += ".txt"; + result = saveCRTDFile(filename, frameCache); + } + if (dialog.selectedNameFilter() == filters[2]) + { + if (!filename.contains('.')) filename += ".csv"; + result = saveGenericCSVFile(filename, frameCache); + } + if (dialog.selectedNameFilter() == filters[3]) + { + if (!filename.contains('.')) filename += ".log"; + result = saveLogFile(filename, frameCache); + } + if (dialog.selectedNameFilter() == filters[4]) + { + if (!filename.contains('.')) filename += ".log"; + result = saveMicrochipFile(filename, frameCache); + } + + if (dialog.selectedNameFilter() == filters[5]) + { + if (!filename.contains('.')) filename += ".trace"; + result = saveTraceFile(filename, frameCache); + } + + if (dialog.selectedNameFilter() == filters[6]) + { + if (!filename.contains('.')) filename += ".csv"; + result = saveIXXATFile(filename, frameCache); + } + + if (dialog.selectedNameFilter() == filters[7]) + { + if (!filename.contains('.')) filename += ".can"; + result = saveCANDOFile(filename, frameCache); + } + + if (dialog.selectedNameFilter() == filters[8]) + { + if (!filename.contains('.')) filename += ".csv"; + result = saveVehicleSpyFile(filename, frameCache); + } + + progress.cancel(); + + if (result) + { + QStringList fileList = filename.split('/'); + fileName = fileList[fileList.length() - 1]; + return true; + } + return false; + } + return false; +} + +bool FrameFileIO::loadFrameFile(QString &fileName, QVector* frameCache) { QString filename; QFileDialog dialog; bool result = false; QStringList filters; - filters.append(QString(tr("CRTD Logs (*.txt)"))); - filters.append(QString(tr("GVRET Logs (*.csv)"))); - filters.append(QString(tr("Generic ID/Data CSV (*.csv)"))); - filters.append(QString(tr("BusMaster Log (*.log)"))); - filters.append(QString(tr("Microchip Log (*.can)"))); - filters.append(QString(tr("Vector trace files (*.trace)"))); + filters.append(QString(tr("GVRET Logs (*.csv *.CSV)"))); + filters.append(QString(tr("CRTD Logs (*.txt *.TXT)"))); + filters.append(QString(tr("Generic ID/Data CSV (*.csv *.CSV)"))); + filters.append(QString(tr("BusMaster Log (*.log *.LOG)"))); + filters.append(QString(tr("Microchip Log (*.can *.CAN)"))); + filters.append(QString(tr("Vector trace files (*.trace *.TRACE)"))); + filters.append(QString(tr("IXXAT MiniLog (*.csv *.CSV)"))); + filters.append(QString(tr("CAN-DO Log (*.avc *.can *.evc *.qcc *.AVC *.CAN *.EVC *.QCC)"))); + filters.append(QString(tr("Vehicle Spy (*.csv *.CSV)"))); dialog.setFileMode(QFileDialog::ExistingFile); dialog.setNameFilters(filters); @@ -39,24 +141,93 @@ QString FrameFileIO::loadFrameFile(QVector* frameCache) qApp->processEvents(); - if (dialog.selectedNameFilter() == filters[0]) result = loadCRTDFile(filename, frameCache); - if (dialog.selectedNameFilter() == filters[1]) result = loadNativeCSVFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[0]) result = loadNativeCSVFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[1]) result = loadCRTDFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[2]) result = loadGenericCSVFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[3]) result = loadLogFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[4]) result = loadMicrochipFile(filename, frameCache); if (dialog.selectedNameFilter() == filters[5]) result = loadTraceFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[6]) result = loadIXXATFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[7]) result = loadCANDOFile(filename, frameCache); + if (dialog.selectedNameFilter() == filters[8]) result = loadVehicleSpyFile(filename, frameCache); progress.cancel(); if (result) { QStringList fileList = filename.split('/'); - filename = fileList[fileList.length() - 1]; - return filename; + fileName = fileList[fileList.length() - 1]; + return true; } - else return QString(""); + else return false; } - return QString(""); + return false; +} + + +//2,2550.368293675,0.003818174999651092,67371008,F,F,HS CAN $119,HS CAN,,119,F,F,00,00,00,00,00,00,0D,8B,,, +//Line,Abs Time(Sec),Rel Time (Sec),Status,Er,Tx,Description,Network,Node,Arb ID,Remote,Xtd,B1,B2,B3,B4,B5,B6,B7,B8,Value,Trigger,Signals +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 +bool FrameFileIO::loadVehicleSpyFile(QString filename, QVector *frames) +{ + QFile *inFile = new QFile(filename); + CANFrame thisFrame; + QByteArray line; + int lineCounter = 0; + bool pastHeader = false; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + while (!inFile->atEnd() && !pastHeader) + { + line = inFile->readLine().simplified().toUpper(); + if (line.startsWith("LINE")) lineCounter++; + if (lineCounter == 2) pastHeader = true; + } + + while (!inFile->atEnd()) { + lineCounter++; + if (lineCounter > 100) + { + qApp->processEvents(); + lineCounter = 0; + } + line = inFile->readLine().simplified().toUpper(); + + QList tokens = line.split(','); + thisFrame.bus = 0; + thisFrame.timestamp = tokens[1].toDouble() * 1000000.0; + if (tokens[5].startsWith("T")) thisFrame.isReceived = false; + else thisFrame.isReceived = true; + thisFrame.ID = tokens[9].toInt(NULL, 16); + if (tokens[11].startsWith("T")) thisFrame.extended = true; + else thisFrame.extended = false; + + thisFrame.len = 0; + for (int i = 0; i < 8; i++) + { + if (tokens[12 + i].length() > 0) + { + thisFrame.data[i] = tokens[12 + i].toInt(NULL, 16); + thisFrame.len++; + } + else break; + } + + frames->append(thisFrame); + } + inFile->close(); + delete inFile; + return true; +} + +bool FrameFileIO::saveVehicleSpyFile(QString filename, const QVector *frames) +{ + return true; } //CRTD format from Mark Webb-Johnson / OVMS project @@ -94,7 +265,7 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector* frames) return false; } - line = inFile->readLine(); //read out the header first and discard it. + line = inFile->readLine().toUpper(); //read out the header first and discard it. while (!inFile->atEnd()) { lineCounter++; @@ -121,11 +292,14 @@ bool FrameFileIO::loadCRTDFile(QString filename, QVector* frames) } //qDebug() << "decimal places " << decimalPlaces; thisFrame.timestamp = (int64_t)(tokens[0].toDouble() * multiplier); - if (tokens[1] == "R11" || tokens[1] == "R29") + char firstChar = tokens[1].left(1)[0]; + if (firstChar == 'R' || firstChar == 'T') { thisFrame.ID = tokens[2].toInt(NULL, 16); - if (tokens[1] == "R29") thisFrame.extended = true; + if (tokens[1] == "R29" || tokens[1] == "T29") thisFrame.extended = true; else thisFrame.extended = false; + if (firstChar == 'T') thisFrame.isReceived = false; + else thisFrame.isReceived = true; thisFrame.bus = 0; thisFrame.len = tokens.length() - 3; for (int d = 0; d < thisFrame.len; d++) @@ -171,11 +345,15 @@ bool FrameFileIO::saveCRTDFile(QString filename, const QVector* frames outFile->write(QString::number(frames->at(c).timestamp / 1000000.0, 'f', 6).toUtf8()); outFile->putChar(' '); + + if (frames->at(c).isReceived) outFile->putChar('R'); + else outFile->putChar('T'); + if (frames->at(c).extended) { - outFile->write("R29 "); + outFile->write("29 "); } - else outFile->write("R11 "); + else outFile->write("11 "); outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8()); outFile->putChar(' '); @@ -199,6 +377,7 @@ bool FrameFileIO::loadNativeCSVFile(QString filename, QVector* frames) QFile *inFile = new QFile(filename); CANFrame thisFrame; QByteArray line; + int fileVersion = 1; long long timeStamp = Utility::GetTimeMS(); int lineCounter = 0; @@ -208,7 +387,8 @@ bool FrameFileIO::loadNativeCSVFile(QString filename, QVector* frames) return false; } - line = inFile->readLine(); //read out the header first and discard it. + line = inFile->readLine().toUpper(); //read out the header first and discard it. + if (line.at(23) == 'D') fileVersion = 2; //Dir is found starting at position 23 if this is a V2 file while (!inFile->atEnd()) { lineCounter++; @@ -236,11 +416,27 @@ bool FrameFileIO::loadNativeCSVFile(QString filename, QVector* frames) thisFrame.ID = tokens[1].toInt(NULL, 16); if (tokens[2].toUpper().contains("TRUE")) thisFrame.extended = 1; else thisFrame.extended = 0; - thisFrame.bus = tokens[3].toInt(); - thisFrame.len = tokens[4].toInt(); - for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; - for (int d = 0; d < thisFrame.len; d++) - thisFrame.data[d] = tokens[5 + d].toInt(NULL, 16); + + if (fileVersion == 1) + { + thisFrame.isReceived = true; + thisFrame.bus = tokens[3].toInt(); + thisFrame.len = tokens[4].toInt(); + for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; + for (int d = 0; d < thisFrame.len; d++) + thisFrame.data[d] = tokens[5 + d].toInt(NULL, 16); + } + else if (fileVersion == 2) + { + if (tokens[3].at(0) == 'R') thisFrame.isReceived = true; + else thisFrame.isReceived = false; + thisFrame.bus = tokens[4].toInt(); + thisFrame.len = tokens[5].toInt(); + for (int c = 0; c < 8; c++) thisFrame.data[c] = 0; + for (int d = 0; d < thisFrame.len; d++) + thisFrame.data[d] = tokens[6 + d].toInt(NULL, 16); + } + frames->append(thisFrame); } } @@ -260,7 +456,7 @@ bool FrameFileIO::saveNativeCSVFile(QString filename, const QVector* f return false; } - outFile->write("Time Stamp,ID,Extended,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8"); + outFile->write("Time Stamp,ID,Extended,Dir,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8"); outFile->write("\n"); for (int c = 0; c < frames->count(); c++) @@ -281,6 +477,9 @@ bool FrameFileIO::saveNativeCSVFile(QString filename, const QVector* f if (frames->at(c).extended) outFile->write("true,"); else outFile->write("false,"); + if (frames->at(c).isReceived) outFile->write("Rx,"); + else outFile->write("Tx,"); + outFile->write(QString::number(frames->at(c).bus).toUtf8()); outFile->putChar(44); @@ -449,7 +648,7 @@ bool FrameFileIO::loadLogFile(QString filename, QVector* frames) lineCounter = 0; } - line = inFile->readLine(); + line = inFile->readLine().toUpper(); if (line.startsWith("***")) continue; if (line.length() > 1) { @@ -458,6 +657,8 @@ bool FrameFileIO::loadLogFile(QString filename, QVector* frames) timeStamp = (timeToks[0].toInt() * (1000ul * 1000ul * 60ul * 60ul)) + (timeToks[1].toInt() * (1000ul * 1000ul * 60ul)) + (timeToks[2].toInt() * (1000ul * 1000ul)) + (timeToks[3].toInt() * 100ul); thisFrame.timestamp = timeStamp; + if (tokens[1].at(0) == 'R') thisFrame.isReceived = true; + else thisFrame.isReceived = false; thisFrame.ID = tokens[3].right(tokens[3].length() - 2).toInt(NULL, 16); if (tokens[4] == "s") thisFrame.extended = false; else thisFrame.extended = true; @@ -513,7 +714,8 @@ bool FrameFileIO::saveLogFile(QString filename, const QVector* frames) tempStamp = timestamp.addMSecs(frames->at(c).timestamp / 1000); outFile->write(tempStamp.toString("h:m:s:z").toUtf8()); - outFile->write(" Rx "); + if (frames->at(c).isReceived) outFile->write(" Rx "); + else outFile->write(" Tx "); outFile->write(QString::number(frames->at(c).bus).toUtf8() + " "); outFile->write(QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8()); if (frames->at(c).extended) outFile->write(" x "); @@ -534,6 +736,226 @@ bool FrameFileIO::saveLogFile(QString filename, const QVector* frames) return true; } +//"00:01:03.03","223","Std","","00 00 00 00 49 00 00 01 " +bool FrameFileIO::loadIXXATFile(QString filename, QVector* frames) +{ + QFile *inFile = new QFile(filename); + CANFrame thisFrame; + QByteArray line; + uint64_t timeStamp = Utility::GetTimeMS(); + int lineCounter = 0; + + if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) + { + delete inFile; + return false; + } + + for (int i = 0; i < 7; i++) line = inFile->readLine(); //read out the header first and discard it. + + while (!inFile->atEnd()) { + lineCounter++; + if (lineCounter > 100) + { + qApp->processEvents(); + lineCounter = 0; + } + + line = inFile->readLine().toUpper(); + if (line.length() > 1) + { + QList tokens = line.split(','); + QString timePortion = unQuote(tokens[0]); + QStringList timeToks = timePortion.split(':'); + timeStamp = (timeToks[0].toInt() * (1000ul * 1000ul * 60ul * 60ul)) + (timeToks[1].toInt() * (1000ul * 1000ul * 60ul)) + + (timeToks[2].toDouble() * (1000.0 * 1000.0)); + thisFrame.timestamp = timeStamp; + thisFrame.ID = unQuote(tokens[1]).toInt(NULL, 16); + if (unQuote(tokens[2]).toUpper().at(0) == 'S') thisFrame.extended = false; + else thisFrame.extended = true; + + thisFrame.isReceived = true; + thisFrame.bus = 0; + + QStringList dataToks = unQuote(tokens[4]).simplified().split(' '); + thisFrame.len = dataToks.length(); + for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = dataToks[d].toInt(NULL, 16); + } + frames->append(thisFrame); + } + inFile->close(); + delete inFile; + return true; +} + +bool FrameFileIO::saveIXXATFile(QString filename, const QVector* frames) +{ + QFile *outFile = new QFile(filename); + QDateTime timestamp, tempStamp; + int lineCounter = 0; + + timestamp = QDateTime::currentDateTime(); + + if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + { + delete outFile; + return false; + } + + outFile->write("ASCII Trace IXXAT SavvyCAN V" + QString::number(VERSION).toUtf8() + "\n"); + outFile->write("Date: " + timestamp.toString("d:M:yyyy").toUtf8() + "\n"); + outFile->write("Start time: " + timestamp.toString("h:m:s").toUtf8() + "\n"); + timestamp.addMSecs((frames->last().timestamp - frames->first().timestamp) / 1000); + outFile->write("Stop time: " + timestamp.toString("h:m:s").toUtf8() + "\n"); + outFile->write("Overruns: 0\n"); + outFile->write("Baudrate: 500 kbit/s\n"); //could be a lie... this code has no way to know the baud rate (at the moment) + outFile->write("\"Time\",\"Identifier (hex)\",\"Format\",\"Flags\",\"Data (hex)\"\n"); + + for (int c = 0; c < frames->count(); c++) + { + lineCounter++; + if (lineCounter > 100) + { + qApp->processEvents(); + lineCounter = 0; + } + + timestamp.setTime(QTime()); + + tempStamp = timestamp.addMSecs(frames->at(c).timestamp / 1000); + outFile->write("\"" + tempStamp.toString("h:m:s.").toUtf8() + tempStamp.toString("z").rightJustified(3, '0').toUtf8() + "\""); + + outFile->write(",\"" + QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8() + "\""); + if (frames->at(c).extended) outFile->write(",\"Ext\""); + else outFile->write(",\"Std\""); + outFile->write(",\"\",\""); + + for (int temp = 0; temp < frames->at(c).len; temp++) + { + outFile->write(QString::number(frames->at(c).data[temp], 16).toUpper().rightJustified(2, '0').toUtf8()); + outFile->putChar(' '); + } + + outFile->write("\"\n"); + + } + outFile->close(); + delete outFile; + return true; +} + +bool FrameFileIO::loadCANDOFile(QString filename, QVector* frames) +{ + QFile *inFile = new QFile(filename); + CANFrame thisFrame; + int lineCounter = 0; + QByteArray data; + int timeOffset = 0; + uint64_t lastTimeStamp = 0; + + if (!inFile->open(QIODevice::ReadOnly)) + { + delete inFile; + return false; + } + + //this file format is in static 12 byte blocks. + //Bytes 0 - 1 are a time stamp + //Bytes 2 - 3 are the data length (top 4 bits) then ID (bottom 11 bits) + //Bytes 4 - 11 are the data bytes (padded with FF for bytes not used) + + while (!inFile->atEnd()) + { + lineCounter++; + if (lineCounter > 100) + { + qApp->processEvents(); + lineCounter = 0; + } + + data = inFile->read(12); + + thisFrame.bus = 0; + thisFrame.isReceived = true; + thisFrame.extended = false; //format is incapable of extended frames + + thisFrame.timestamp = 1000000ul * ((unsigned char)data[0] >> 2); + thisFrame.timestamp += (((data[0] & 3) << 8) + (unsigned char)data[1]) * 1000; + thisFrame.timestamp += timeOffset; + if (thisFrame.timestamp < lastTimeStamp) + { + timeOffset += 60000000ul; + } + lastTimeStamp = thisFrame.timestamp; + thisFrame.ID = ((unsigned char)data[3] * 256 + (unsigned char)data[2]) & 0x7FF; + thisFrame.len = (unsigned char)data[3] >> 4; + + if (thisFrame.len <= 8 && thisFrame.ID <= 0x7FF) + { + for (int d = 0; d < thisFrame.len; d++) thisFrame.data[d] = (unsigned char)data[4 + d]; + frames->append(thisFrame); + } + } + + inFile->close(); + delete inFile; + return true; +} + +bool FrameFileIO::saveCANDOFile(QString filename, const QVector* frames) +{ + QFile *outFile = new QFile(filename); + int lineCounter = 0; + QByteArray data; + CANFrame thisFrame; + int ms, id; + + if (!outFile->open(QIODevice::WriteOnly)) + { + delete outFile; + return false; + } + + data.reserve(13); + + //The initial frame in official files sets the global time but I don't care so it is set all zeros here. + thisFrame = frames->at(0); + ms = (thisFrame.timestamp / 1000); + data[0] = (((ms / 1000) % 60) << 2) + ((ms % 1000) >> 8); + data[1] = (char)(ms & 0xFF); + data[2] = 0xFF; + data[3] = 0xFF; + for (int l = 0; l < 8; l++) data[4 + l] = 0; + outFile->write(data); + + for (int c = 0; c < frames->count(); c++) + { + lineCounter++; + if (lineCounter > 100) + { + qApp->processEvents(); + lineCounter = 0; + } + for (int j = 0; j < 8; j++) data[4 + j] = 0xFF; + + thisFrame = frames->at(c); + if (!thisFrame.extended) + { + ms = (thisFrame.timestamp / 1000); + id = thisFrame.ID & 0x7FF; + data[0] = (((ms / 1000) % 60) << 2) + ((ms % 1000) >> 8); + data[1] = (char)(ms & 0xFF); + data[2] = (char)(id & 0xFF); + data[3] = (char)((id >> 8) + (thisFrame.len << 4)); + for (int d = 0; d < thisFrame.len; d++) data[4 + d] = (char)thisFrame.data[d]; + outFile->write(data); + } + } + outFile->close(); + delete outFile; + return true; +} + //log file from microchip tool /* tokens: @@ -549,7 +971,7 @@ bool FrameFileIO::loadMicrochipFile(QString filename, QVector* frames) CANFrame thisFrame; QByteArray line; bool inComment = false; - long long timeStamp = Utility::GetTimeMS(); + long long timeStamp; int lineCounter = 0; if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) @@ -580,9 +1002,10 @@ bool FrameFileIO::loadMicrochipFile(QString filename, QVector* frames) if (!inComment) { QList tokens = line.split(';'); - timeStamp += 5; + timeStamp = tokens[0].toInt() * 1000; thisFrame.timestamp = timeStamp; - + if (tokens[1].at(0) == 'R') thisFrame.isReceived = true; + else thisFrame.isReceived = false; thisFrame.ID = Utility::ParseStringToNum(tokens[2]); if (thisFrame.ID <= 0x7FF) thisFrame.extended = false; else thisFrame.extended = true; @@ -643,7 +1066,8 @@ bool FrameFileIO::saveMicrochipFile(QString filename, const QVector* f } outFile->write(QString::number((int)(frames->at(c).timestamp / 1000)).toUtf8()); - outFile->write(";RX;"); + if (frames->at(c).isReceived) outFile->write(";RX;"); + else outFile->write(";TX;"); outFile->write("0x" + QString::number(frames->at(c).ID, 16).toUpper().rightJustified(8, '0').toUtf8() + ";"); outFile->write(QString::number(frames->at(c).len).toUtf8() + ";"); @@ -839,3 +1263,8 @@ bool FrameFileIO::saveTraceFile(QString filename, const QVector * fram delete outFile; return true; } + +QString FrameFileIO::unQuote(QString inStr) +{ + return inStr.split('\"')[1]; +} diff --git a/framefileio.h b/framefileio.h index 095dd29..d1a7169 100644 --- a/framefileio.h +++ b/framefileio.h @@ -28,13 +28,23 @@ public: static bool loadLogFile(QString, QVector*); static bool loadMicrochipFile(QString, QVector*); static bool loadTraceFile(QString, QVector*); - static bool saveCRTDFile(QString, const QVector*); + static bool loadIXXATFile(QString, QVector*); + static bool loadCANDOFile(QString, QVector*); + static bool loadVehicleSpyFile(QString, QVector*); + static bool saveCRTDFile(QString, const QVector*); static bool saveNativeCSVFile(QString, const QVector*); static bool saveGenericCSVFile(QString, const QVector*); static bool saveLogFile(QString, const QVector*); static bool saveMicrochipFile(QString, const QVector*); static bool saveTraceFile(QString, const QVector*); - static QString loadFrameFile(QVector*); + static bool saveIXXATFile(QString, const QVector*); + static bool saveCANDOFile(QString, const QVector*); + static bool saveVehicleSpyFile(QString, const QVector*); + static bool loadFrameFile(QString &, QVector*); + static bool saveFrameFile(QString &, const QVector*); + +private: + static QString unQuote(QString); }; #endif // FRAMEFILEIO_H diff --git a/frameinfowindow.cpp b/frameinfowindow.cpp index a982b8d..72d20a0 100644 --- a/frameinfowindow.cpp +++ b/frameinfowindow.cpp @@ -13,8 +13,10 @@ FrameInfoWindow::FrameInfoWindow(const QVector *frames, QWidget *paren modelFrames = frames; - connect(ui->listFrameID, SIGNAL(currentTextChanged(QString)), this, SLOT(updateDetailsWindow(QString))); - connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int))); + connect(ui->listFrameID, &QListWidget::currentTextChanged, this, &FrameInfoWindow::updateDetailsWindow); + connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &FrameInfoWindow::updatedFrames); + connect(ui->btnSave, &QAbstractButton::clicked, this, &FrameInfoWindow::saveDetails); + } void FrameInfoWindow::showEvent(QShowEvent* event) @@ -81,6 +83,7 @@ void FrameInfoWindow::updatedFrames(int numFrames) } else //just got some new frames. See if they are relevant. { + if (numFrames > modelFrames->count()) return; int currID = ui->listFrameID->currentItem()->text().toInt(NULL, 16); bool foundID = false; for (int x = modelFrames->count() - numFrames; x < modelFrames->count(); x++) @@ -113,6 +116,8 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) int maxData[8]; int dataHistogram[256][8]; int bitfieldHistogram[64]; + uint8_t changedBits[8]; + uint8_t referenceBits[8]; QTreeWidgetItem *baseNode, *dataBase, *histBase, *tempItem; targettedID = Utility::ParseStringToNum(newID); @@ -196,6 +201,13 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) } for (int j = 0; j < 64; j++) bitfieldHistogram[j] = 0; + for (int c = 0; c < 8; c++) + { + changedBits[c] = 0; + referenceBits[c] = frameCache.at(0).data[c]; + qDebug() << referenceBits[c]; + } + //then find all data points for (int j = 0; j < frameCache.count(); j++) { @@ -217,10 +229,13 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) bitfieldHistogram[c * 8 + l]++; } } + changedBits[c] |= referenceBits[c] ^ dat; } } - avgInterval = avgInterval / (frameCache.count() - 1); + if (frameCache.count() > 1) + avgInterval = avgInterval / (frameCache.count() - 1); + else avgInterval = 0; tempItem = new QTreeWidgetItem(); @@ -242,6 +257,13 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) dataBase->setText(0, tr("Data Byte ") + QString::number(c)); baseNode->addChild(dataBase); + + tempItem = new QTreeWidgetItem(); + QString builder; + builder = tr("Changed bits: 0x") + QString::number(changedBits[c], 16) + " (" + Utility::formatByteAsBinary(changedBits[c]) + ")"; + tempItem->setText(0, builder); + dataBase->addChild(tempItem); + tempItem = new QTreeWidgetItem(); tempItem->setText(0, tr("Range: ") + Utility::formatNumber(minData[c]) + tr(" to ") + Utility::formatNumber(maxData[c])); dataBase->addChild(tempItem); @@ -253,7 +275,7 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) if (dataHistogram[d][c] > 0) { tempItem = new QTreeWidgetItem(); - tempItem->setText(0, QString::number(d) + "/0x" + QString::number(d, 16) +": " + QString::number(dataHistogram[d][c])); + tempItem->setText(0, QString::number(d) + "/0x" + QString::number(d, 16) +" (" + Utility::formatByteAsBinary(d) +") -> " + QString::number(dataHistogram[d][c])); histBase->addChild(tempItem); } } @@ -300,3 +322,55 @@ void FrameInfoWindow::refreshIDList() ui->listFrameID->sortItems(); ui->lblFrameID->setText(tr("Frame IDs: (") + QString::number(ui->listFrameID->count()) + tr(" unique ids)")); } + +void FrameInfoWindow::saveDetails() +{ + 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; + } + + //go through all IDs, recalculate the data, and then save it to file + for (int i = 0; i < ui->listFrameID->count(); i++) + { + updateDetailsWindow(ui->listFrameID->item(i)->text()); + dumpNode(ui->treeDetails->invisibleRootItem(), outFile, 0); + outFile->write("\n\n"); + } + + outFile->close(); + delete outFile; + } + } +} + +void FrameInfoWindow::dumpNode(QTreeWidgetItem* item, QFile *file, int indent) +{ + for (int i = 0; i < indent; 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 ); +} + + diff --git a/frameinfowindow.h b/frameinfowindow.h index c22a7db..dc3ac57 100644 --- a/frameinfowindow.h +++ b/frameinfowindow.h @@ -2,7 +2,9 @@ #define FRAMEINFOWINDOW_H #include +#include #include +#include #include "can_structs.h" namespace Ui { @@ -21,6 +23,7 @@ public: private slots: void updateDetailsWindow(QString); void updatedFrames(int); + void saveDetails(); private: Ui::FrameInfoWindow *ui; @@ -33,6 +36,7 @@ private: void closeEvent(QCloseEvent *event); void readSettings(); void writeSettings(); + void dumpNode(QTreeWidgetItem* item, QFile *file, int indent); }; #endif // FRAMEINFOWINDOW_H diff --git a/frameinfowindow.ui b/frameinfowindow.ui index e8c5e6c..2b82ef0 100644 --- a/frameinfowindow.ui +++ b/frameinfowindow.ui @@ -13,53 +13,64 @@ Detailed Frame Information - + - + - - - Frame IDs: - - + + + + + Frame IDs: + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + - - - - 0 - 0 - - - - - 120 - 0 - - - + + + + + Details: + + + + + + + + 1 + + + + + - - - - - Details: - - - - - - - - 1 - - - - - + + + Save details to file + + diff --git a/frameplaybackwindow.cpp b/frameplaybackwindow.cpp index 64b66fc..765f482 100644 --- a/frameplaybackwindow.cpp +++ b/frameplaybackwindow.cpp @@ -342,56 +342,33 @@ void FramePlaybackWindow::btnDeleteCurrSeq() void FramePlaybackWindow::btnLoadFile() { QString filename; - QFileDialog dialog(this); - bool result = false; SequenceItem item; - QStringList filters; - filters.append(QString(tr("CRTD Logs (*.txt)"))); - filters.append(QString(tr("GVRET Logs (*.csv)"))); - filters.append(QString(tr("Generic ID/Data CSV (*.csv)"))); - filters.append(QString(tr("BusMaster Log (*.log)"))); - filters.append(QString(tr("Microchip Log (*.log)"))); - - dialog.setFileMode(QFileDialog::ExistingFile); - dialog.setNameFilters(filters); - dialog.setViewMode(QFileDialog::Detail); - - if (dialog.exec() == QDialog::Accepted) + if (FrameFileIO::loadFrameFile(filename, &item.data)) { - filename = dialog.selectedFiles()[0]; - - if (dialog.selectedNameFilter() == filters[0]) result = FrameFileIO::loadCRTDFile(filename, &item.data); - if (dialog.selectedNameFilter() == filters[1]) result = FrameFileIO::loadNativeCSVFile(filename, &item.data); - if (dialog.selectedNameFilter() == filters[2]) result = FrameFileIO::loadGenericCSVFile(filename, &item.data); - if (dialog.selectedNameFilter() == filters[3]) result = FrameFileIO::loadLogFile(filename, &item.data); - if (dialog.selectedNameFilter() == filters[4]) result = FrameFileIO::loadMicrochipFile(filename, &item.data); - if (result) + QStringList fileList = filename.split('/'); + item.filename = fileList[fileList.length() - 1]; + item.currentLoopCount = 0; + item.maxLoops = 1; + fillIDHash(item); + if (ui->tblSequence->currentRow() == -1) { - QStringList fileList = filename.split('/'); - item.filename = fileList[fileList.length() - 1]; - item.currentLoopCount = 0; - item.maxLoops = 1; - fillIDHash(item); - if (ui->tblSequence->currentRow() == -1) - { - ui->tblSequence->setCurrentCell(0,0); - } - seqItems.append(item); - int row = ui->tblSequence->rowCount(); - ui->tblSequence->insertRow(row); - ui->tblSequence->setItem(row, 0, new QTableWidgetItem(item.filename)); - ui->tblSequence->setItem(row, 1, new QTableWidgetItem(QString::number(item.maxLoops))); - qDebug() << currentSeqNum; - if (currentSeqNum == -1) - { - currentSeqNum = 0; - currentSeqItem = &seqItems[0]; - } - updateFrameLabel(); + ui->tblSequence->setCurrentCell(0,0); } + seqItems.append(item); + int row = ui->tblSequence->rowCount(); + ui->tblSequence->insertRow(row); + ui->tblSequence->setItem(row, 0, new QTableWidgetItem(item.filename)); + ui->tblSequence->setItem(row, 1, new QTableWidgetItem(QString::number(item.maxLoops))); + qDebug() << currentSeqNum; + if (currentSeqNum == -1) + { + currentSeqNum = 0; + currentSeqItem = &seqItems[0]; + } + refreshIDList(); + updateFrameLabel(); } - } void FramePlaybackWindow::btnLoadLive() @@ -416,6 +393,7 @@ void FramePlaybackWindow::btnLoadLive() currentSeqNum = 0; currentSeqItem = &seqItems[0]; } + refreshIDList(); updateFrameLabel(); btnStopClick(); } diff --git a/framesenderwindow.cpp b/framesenderwindow.cpp index dce5d09..61cc598 100644 --- a/framesenderwindow.cpp +++ b/framesenderwindow.cpp @@ -109,7 +109,8 @@ void FrameSenderWindow::updatedFrames(int numFrames) buildFrameCache(); } else //just got some new frames. See if they are relevant. - { + { + if (numFrames > modelFrames->count()) return; qDebug() << "New frames in sender window"; //run through the supposedly new frames in order for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) diff --git a/fuzzingwindow.cpp b/fuzzingwindow.cpp new file mode 100644 index 0000000..45e564c --- /dev/null +++ b/fuzzingwindow.cpp @@ -0,0 +1,440 @@ +#include "fuzzingwindow.h" +#include "ui_fuzzingwindow.h" +#include "utility.h" +#include +#include "mainwindow.h" + +FuzzingWindow::FuzzingWindow(const QVector *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::FuzzingWindow) +{ + ui->setupUi(this); + + modelFrames = frames; + + fuzzTimer = new QTimer(); + + connect(ui->btnStartStop, &QPushButton::clicked, this, &FuzzingWindow::toggleFuzzing); + connect(ui->btnAllFilters, &QPushButton::clicked, this, &FuzzingWindow::setAllFilters); + connect(ui->btnNoFilters, &QPushButton::clicked, this, &FuzzingWindow::clearAllFilters); + connect(fuzzTimer, &QTimer::timeout, this, &FuzzingWindow::timerTriggered); + connect(ui->spinTiming, SIGNAL(valueChanged(int)), this, SLOT(changePlaybackSpeed(int))); + connect(ui->listID, &QListWidget::itemChanged, this, &FuzzingWindow::idListChanged); + connect(ui->spinBytes, SIGNAL(valueChanged(int)), this, SLOT(changedNumDataBytes(int))); + connect(ui->bitfield, SIGNAL(gridClicked(int,int)), this, SLOT(bitfieldClicked(int,int))); + + connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int))); + + refreshIDList(); + + currentlyFuzzing = false; + + for (int j = 0; j < 64; j++) bitGrid[j] = 1; + numBits = 64; + bitAccum = 0; + redrawGrid(); + + fuzzTimer->setInterval(ui->spinTiming->value()); + + ui->cbBuses->addItem(tr("0")); + ui->cbBuses->addItem(tr("1")); + ui->cbBuses->addItem(tr("Both")); +} + +FuzzingWindow::~FuzzingWindow() +{ + delete ui; +} + +void FuzzingWindow::updatedFrames(int numFrames) +{ + CANFrame thisFrame; + int id; + if (numFrames == -1) //all frames deleted. Kill the display + { + ui->listID->clear(); + foundIDs.clear(); + refreshIDList(); + } + else if (numFrames == -2) //all new set of frames. Reset + { + ui->listID->clear(); + foundIDs.clear(); + refreshIDList(); + } + else //just got some new frames. See if they are relevant. + { + if (numFrames > modelFrames->count()) return; + for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + { + id = modelFrames->at(i).ID; + if (!foundIDs.contains(id)) + { + foundIDs.append(id); + selectedIDs.append(id); + QListWidgetItem *thisItem = new QListWidgetItem(); + thisItem->setText(Utility::formatNumber(id)); + thisItem->setFlags(thisItem->flags() | Qt::ItemIsUserCheckable); + thisItem->setCheckState(Qt::Checked); + ui->listID->addItem(thisItem); + } + } + } +} + +void FuzzingWindow::changePlaybackSpeed(int newSpeed) +{ + fuzzTimer->setInterval(newSpeed); +} + +void FuzzingWindow::changedNumDataBytes(int newVal) +{ + qDebug() << "new num bytes: " << newVal; + int byt; + for (int i = 0; i < 64; i++) + { + byt = i / 8; + if (byt >= newVal) + { + bitGrid[i] = 3; + } + else + { + if (bitGrid[i] == 3) bitGrid[i] = 1; + } + } + + redrawGrid(); +} + +void FuzzingWindow::timerTriggered() +{ + CANFrame thisFrame; + sendingBuffer.clear(); + int buses = ui->cbBuses->currentIndex() + 1; + if (buses == 0) buses = 1; + for (int count = 0; count < ui->spinBurst->value(); count++) + { + thisFrame.ID = currentID; + for (int i = 0; i < 8; i++) thisFrame.data[i] = currentBytes[i]; + if (currentID > 0x7FF) thisFrame.extended = true; + else thisFrame.extended = false; + thisFrame.bus = 0; //hard coded for now. TODO: do not hard code + thisFrame.len = ui->spinBytes->value(); + if (buses & 1) + { + thisFrame.bus = 0; + sendingBuffer.append(thisFrame); + } + if (buses & 2) + { + thisFrame.bus = 1; + sendingBuffer.append(thisFrame); + } + + calcNextID(); + calcNextBitPattern(); + numSentFrames++; + } + emit sendFrameBatch(&sendingBuffer); + ui->lblNumFrames->setText("# of sent frames: " + QString::number(numSentFrames)); +} + +void FuzzingWindow::clearAllFilters() +{ + for (int i = 0; i < ui->listID->count(); i++) + { + ui->listID->item(i)->setCheckState(Qt::Unchecked); + } +} + +void FuzzingWindow::setAllFilters() +{ + for (int i = 0; i < ui->listID->count(); i++) + { + ui->listID->item(i)->setCheckState(Qt::Checked); + } +} + +void FuzzingWindow::calcNextID() +{ + if (seqIDScan) + { + if (rangeIDSelect) + { + currentID++; + if (currentID > endID) currentID = startID; + } + else //IDs by filter. So, select the first filter + { + currentIdx++; + if (currentIdx >= selectedIDs.length()) currentIdx = 0; + currentID = selectedIDs[currentIdx]; + qDebug() << "idx id: " << currentID; + } + } + else //random IDs + { + if (rangeIDSelect) + { + int range = endID - startID; + if (range != 0) currentID = startID + qrand() % range; + else currentID = startID; + } + else //IDs by filter so pick a random selected ID from the filter list + { + currentIdx = qrand() % selectedIDs.length(); + currentID = selectedIDs[currentIdx]; + } + } +} + +void FuzzingWindow::calcNextBitPattern() +{ + uint64_t accum; + + switch (bitSequenceType) + { + case BitSequenceType::Random: + int thisBit; + for (int byt = 0; byt < ui->spinBytes->value(); byt++) + { + currentBytes[byt] = 0; + for (int bit = 0; bit < 8; bit++) + { + thisBit = bitGrid[byt * 8 + bit]; + if (thisBit == 1) + { + if ((qrand() % 2) == 1) currentBytes[byt] |= (1 << bit); + } + if (thisBit == 2) currentBytes[byt] |= (1 << bit); + } + } + break; + case BitSequenceType::Sequential: + bitAccum++; + bitAccum &= ((1 << numBits) - 1); + accum = bitAccum; + for (int byt = 0; byt < ui->spinBytes->value(); byt++) + { + currentBytes[byt] = 0; + for (int bit = 0; bit < 8; bit++) + { + thisBit = bitGrid[byt * 8 + bit]; + if (thisBit == 1) + { + if (accum & 1) currentBytes[byt] |= (1 << bit); + accum >>= 1; + } + if (thisBit == 2) currentBytes[byt] |= (1 << bit); + } + } + break; + case BitSequenceType::Sweeping: + qDebug() << "Start " << bitAccum; + accum = bitAccum; + + int offset; + for (int i = 1; i < 64; i++) + { + offset = (i + bitAccum) % 64; + if (bitGrid[offset] == 1) + { + bitAccum = offset; + qDebug() << "End " << bitAccum; + break; + } + } + + for (int byt = 0; byt < ui->spinBytes->value(); byt++) + { + currentBytes[byt] = 0; + for (int bit = 0; bit < 8; bit++) + { + thisBit = bitGrid[byt * 8 + bit]; + if (thisBit == 1 && (byt * 8 + bit) == bitAccum) + { + currentBytes[byt] |= (1 << bit); + } + if (thisBit == 2) currentBytes[byt] |= (1 << bit); + } + } + break; + } +} + +void FuzzingWindow::toggleFuzzing() +{ + if (currentlyFuzzing) //stop it then + { + ui->btnStartStop->setText("Start Fuzzing"); + currentlyFuzzing = false; + fuzzTimer->stop(); + } + else //start it then + { + ui->btnStartStop->setText("Stop Fuzzing"); + currentlyFuzzing = true; + + startID = Utility::ParseStringToNum(ui->txtStartID->text()); + endID = Utility::ParseStringToNum(ui->txtEndID->text()); + + seqIDScan = ui->rbSequentialID->isChecked(); + rangeIDSelect = ui->rbRangeIDSel->isChecked(); + if (ui->rbSequentialBits->isChecked()) bitSequenceType = BitSequenceType::Sequential; + if (ui->rbRandomBits->isChecked()) + { + bitSequenceType = BitSequenceType::Random; + bitAccum = 0; + } + if (ui->rbSweep->isChecked()) + { + bitSequenceType = BitSequenceType::Sweeping; + for (int i = 0; i < 64; i++) + { + if (bitGrid[i] == 1) + { + bitAccum = i; + break; + } + } + } + + numSentFrames = 0; + + if (seqIDScan) + { + if (rangeIDSelect) + { + currentID = startID; + } + else //IDs by filter. So, select the first filter + { + currentIdx = 0; + currentID = selectedIDs[currentIdx]; + } + } + else //random IDs + { + if (rangeIDSelect) + { + int range = endID - startID; + if (range != 0) currentID = startID + qrand() % range; + else currentID = startID; + } + else //IDs by filter so pick a random selected ID from the filter list + { + currentIdx = qrand() % selectedIDs.length(); + currentID = selectedIDs[currentIdx]; + } + } + + calcNextBitPattern(); + + fuzzTimer->start(); + } +} + +void FuzzingWindow::refreshIDList() +{ + ui->listID->clear(); + foundIDs.clear(); + + int id; + for (int i = 0; i < modelFrames->count(); i++) + { + id = modelFrames->at(i).ID; + if (!foundIDs.contains(id)) + { + foundIDs.append(id); + selectedIDs.append(id); + QListWidgetItem *thisItem = new QListWidgetItem(); + thisItem->setText(Utility::formatNumber(id)); + thisItem->setFlags(thisItem->flags() | Qt::ItemIsUserCheckable); + thisItem->setCheckState(Qt::Checked); + ui->listID->addItem(thisItem); + } + } + //default is to sort in ascending order + ui->listID->sortItems(); +} + +void FuzzingWindow::idListChanged(QListWidgetItem *item) +{ + int id = Utility::ParseStringToNum(item->text()); + if (item->checkState() == Qt::Checked) + { + if (!selectedIDs.contains(id)) + { + qDebug() << "adding " << id << " to list of selected IDs"; + selectedIDs.append(id); + } + } + else + { + qDebug() << "removing " << id << " from the list of selected ids"; + selectedIDs.removeOne(id); + } +} + +/* +bitGrid stores the state of all 64 bits. +The grid is capable of showing the following colors: +White = not used (left as 0) +Gray = past the end of the valid bits (because of # of data bytes requested) +Green = fuzz it +black = always keep it set to 1 +*/ +void FuzzingWindow::bitfieldClicked(int x, int y) +{ + qDebug() << "X: " << x << " Y: " << y; + int bit = (7 - x) + (y * 8); + if (bitGrid[bit] == 3) return; //naughty! + bitGrid[bit]++; + if (bitGrid[bit] > 2) bitGrid[bit] = 0; + + redrawGrid(); +} + +void FuzzingWindow::redrawGrid() +{ + //now update the bits in the bitfield control + uint8_t refBytes[8]; + uint8_t dataBytes[8]; + uint8_t usedBytes[8]; + + for (int j = 0; j < 8; j++) + { + refBytes[j] = 0; + dataBytes[j] = 0; + usedBytes[j] = 0; + } + + numBits = 0; + + for (int i = 0; i < 64; i++) + { + int byt = i / 8; + int bit = i % 8; + switch (bitGrid[i]) + { + case 0: //white, keep this bit off always + break; + case 1: //Green, fuzz this bit + dataBytes[byt] |= (1 << bit); + numBits++; + break; + case 2: //black, bit always set + dataBytes[byt] |= (1 << bit); + refBytes[byt] |= (1 << bit); + break; + case 3: //gray, this bit doesn't exist + usedBytes[byt] |= (1 << bit); + break; + } + } + + ui->bitfield->setUsed(usedBytes, false); + ui->bitfield->setReference(refBytes, false); + ui->bitfield->updateData(dataBytes, true); +} diff --git a/fuzzingwindow.h b/fuzzingwindow.h new file mode 100644 index 0000000..4521602 --- /dev/null +++ b/fuzzingwindow.h @@ -0,0 +1,69 @@ +#ifndef FUZZINGWINDOW_H +#define FUZZINGWINDOW_H + +#include +#include +#include +#include "can_structs.h" + +namespace Ui { +class FuzzingWindow; +} + +namespace BitSequenceType +{ + enum + { + Sequential, + Sweeping, + Random + }; +} + +class FuzzingWindow : public QDialog +{ + Q_OBJECT + +public: + explicit FuzzingWindow(const QVector *frames, QWidget *parent = 0); + ~FuzzingWindow(); + +signals: + void sendCANFrame(const CANFrame *, int); + void sendFrameBatch(const QList *); + +private slots: + void changePlaybackSpeed(int newSpeed); + void timerTriggered(); + void clearAllFilters(); + void setAllFilters(); + void toggleFuzzing(); + void idListChanged(QListWidgetItem *item); + void bitfieldClicked(int, int); + void changedNumDataBytes(int newVal); + void updatedFrames(int numFrames); + +private: + Ui::FuzzingWindow *ui; + const QVector *modelFrames; + QTimer *fuzzTimer; + QList foundIDs; + QList selectedIDs; + QList sendingBuffer; + int startID, endID, currentID, currentIdx; + bool seqIDScan, rangeIDSelect; + int bitSequenceType; + bool currentlyFuzzing; + uint8_t currentBytes[8]; + uint8_t bitGrid[64]; + uint8_t numBits; + uint64_t bitAccum; + int numSentFrames; + + void refreshIDList(); + void calcNextID(); + void calcNextBitPattern(); + void redrawGrid(); +}; + +#endif // FUZZINGWINDOW_H diff --git a/fuzzingwindow.ui b/fuzzingwindow.ui new file mode 100644 index 0000000..123b5ae --- /dev/null +++ b/fuzzingwindow.ui @@ -0,0 +1,306 @@ + + + FuzzingWindow + + + + 0 + 0 + 760 + 671 + + + + Fuzzing Window + + + + + + + + + + Interframe Timing (ms) + + + + + + + 10000 + + + 10 + + + + + + + Number of data bytes + + + + + + + 8 + + + 8 + + + + + + + + + + + Burst Rate + + + + + + + 1 + + + 10 + + + 1 + + + + + + + Bus or Buses to Send On + + + + + + + + + + + + + + + + ID Scanning + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + Seqential + + + + + + + Random + + + true + + + + + + + + + + ID Selection + + + + + + Range of IDs + + + true + + + + + + + Filter List + + + + + + + + + + Bit Scanning + + + + + + Sequential + + + + + + + Sweep + + + + + + + Random + + + true + + + + + + + + + + + + + + IDs + + + + + + + + Start ID: + + + + + + + + + + + + + + End ID: + + + + + + + + + + + + + + + + + + + + + + All + + + + + + + None + + + + + + + + + + + + + + 0 + 200 + + + + + + + + + 11 + + + + White = Bit always 0, Green = Fuzz Bit, Black = Bit always set, Gray = Past end of data bytes + + + + + + + Start Fuzzing + + + + + + + + 11 + 75 + true + + + + # of sent frames: + + + + + + + + CANDataGrid + QWidget +
candatagrid.h
+ 1 +
+
+ + +
diff --git a/graphingwindow.cpp b/graphingwindow.cpp index cc33d57..afe9c4d 100644 --- a/graphingwindow.cpp +++ b/graphingwindow.cpp @@ -142,6 +142,7 @@ void GraphingWindow::updatedFrames(int numFrames) } else //just got some new frames. See if they are relevant. { + if (numFrames > modelFrames->count()) return; for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) { thisFrame = modelFrames->at(i); @@ -686,21 +687,31 @@ void GraphingWindow::saveDefinitions() { outFile->write(QString::number(iter->ID, 16).toUtf8()); outFile->putChar(','); - outFile->write(QString::number(iter->mask, 16).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->startByte).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->endByte).toUtf8()); - outFile->putChar(','); - if (iter->isSigned) outFile->putChar('Y'); - else outFile->putChar('N'); - outFile->putChar(','); - outFile->write(QString::number(iter->bias).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->scale).toUtf8()); - outFile->putChar(','); - outFile->write(QString::number(iter->stride).toUtf8()); - outFile->putChar(','); + if (iter->isDBCSignal) + { + outFile->putChar('S'); + outFile->putChar(','); + outFile->write(iter->signal.toUtf8()); + outFile->putChar(','); + } + else + { + outFile->write(QString::number(iter->mask, 16).toUtf8()); + outFile->putChar(','); + outFile->write(QString::number(iter->startByte).toUtf8()); + outFile->putChar(','); + outFile->write(QString::number(iter->endByte).toUtf8()); + outFile->putChar(','); + if (iter->isSigned) outFile->putChar('Y'); + else outFile->putChar('N'); + outFile->putChar(','); + outFile->write(QString::number(iter->bias).toUtf8()); + outFile->putChar(','); + outFile->write(QString::number(iter->scale).toUtf8()); + outFile->putChar(','); + outFile->write(QString::number(iter->stride).toUtf8()); + outFile->putChar(','); + } outFile->write(QString::number(iter->color.red()).toUtf8()); outFile->putChar(','); outFile->write(QString::number(iter->color.green()).toUtf8()); @@ -718,10 +729,14 @@ void GraphingWindow::loadDefinitions() { QString filename; QFileDialog dialog; + bool dbcMissing = false; QStringList filters; filters.append(QString(tr("Graph definition (*.gdf)"))); + if (dbcHandler == NULL) return; + if (dbcHandler->getFileCount() == 0) dbcHandler->createBlankFile(); + dialog.setFileMode(QFileDialog::ExistingFile); dialog.setNameFilters(filters); dialog.setViewMode(QFileDialog::Detail); @@ -741,28 +756,65 @@ void GraphingWindow::loadDefinitions() { GraphParams gp; QList tokens = line.split(','); + gp.isDBCSignal = false; gp.ID = tokens[0].toInt(NULL, 16); - gp.mask = tokens[1].toULongLong(NULL, 16); - qDebug() << gp.mask; - gp.startByte = tokens[2].toInt(); - gp.endByte = tokens[3].toInt(); - if (tokens[4] == "Y") gp.isSigned = true; - else gp.isSigned = false; - gp.bias = tokens[5].toFloat(); - gp.scale = tokens[6].toFloat(); - gp.stride = tokens[7].toInt(); - gp.color.setRed(tokens[8].toInt()); - gp.color.setGreen(tokens[9].toInt()); - gp.color.setBlue(tokens[10].toInt()); - if (tokens.length() > 11) - gp.graphName = tokens[11]; + if (tokens[1] == "S") + { + gp.isDBCSignal = true; + //tokens[2] is the signal name. Need to use the message ID and this name to look it up + DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(gp.ID); + if (msg != NULL) + { + DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(tokens[2]); + if (sig == NULL) dbcMissing = true; + gp.signal = tokens[2]; + } + else + { + gp.signal = ""; + dbcMissing = true; + } + } else - gp.graphName = QString(); + { + gp.mask = tokens[1].toULongLong(NULL, 16); + qDebug() << gp.mask; + gp.startByte = tokens[2].toInt(); + gp.endByte = tokens[3].toInt(); + if (tokens[4] == "Y") gp.isSigned = true; + else gp.isSigned = false; + gp.bias = tokens[5].toFloat(); + gp.scale = tokens[6].toFloat(); + gp.stride = tokens[7].toInt(); + } + if (!gp.isDBCSignal) + { + gp.color.setRed(tokens[8].toInt()); + gp.color.setGreen(tokens[9].toInt()); + gp.color.setBlue(tokens[10].toInt()); + if (tokens.length() > 11) + gp.graphName = tokens[11]; + else + gp.graphName = QString(); + } + else + { + gp.color.setRed(tokens[3].toInt()); + gp.color.setGreen(tokens[4].toInt()); + gp.color.setBlue(tokens[5].toInt()); + gp.graphName = tokens[6]; + } createGraph(gp, true); } } inFile->close(); + if (dbcMissing) + { + QMessageBox msg; + msg.setText("One or more graphs could not be loaded\r\nbecause the signal could not be found.\r\nPerhaps you forgot to load\r\nthe DBC file?"); + msg.exec(); + } } } @@ -799,178 +851,61 @@ void GraphingWindow::addNewGraph() void GraphingWindow::appendToGraph(GraphParams ¶ms, CANFrame &frame) { int64_t tempVal; //64 bit temp value. - if (params.endByte == -1 || params.startByte == params.endByte) + if (params.isDBCSignal) { - tempVal = (frame.data[params.startByte] & params.mask); - if (params.isSigned && tempVal > 127) + double tempValue; + DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(params.ID); + DBC_SIGNAL *sig = NULL; + if (msg) sig = msg->sigHandler->findSignalByName(params.signal); + if (sig == NULL) return; + //if the given signal was found and successfully processed in this frame then add it to the graph + if (sig->processAsDouble(frame, tempValue)) { - tempVal = tempVal - 256; - } - if (secondsMode) - { - params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); - } - else - { - params.x.append(frame.timestamp - params.xbias); - } - params.y.append((tempVal * params.scale) + params.bias); + //qDebug() << "tempValue: " << tempValue; + if (secondsMode) + { + params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); + } + else + { + params.x.append(frame.timestamp - params.xbias); + } + params.y.append(tempValue); + } } - else if (params.endByte > params.startByte) //big endian + else { - float tempValue; - int64_t tempValInt; - int numBytes = (params.endByte - params.startByte) + 1; - int64_t shiftRef = 1 << (numBytes * 8); - uint64_t maskShifter; - uint8_t tempByte; - tempValInt = 0; - int64_t expon = 1; - maskShifter = params.mask; - for (int c = 0; c < numBytes; c++) + if (params.endByte == -1 || params.startByte == params.endByte) { - tempByte = frame.data[params.endByte - c]; - tempByte &= maskShifter; - tempValInt += (tempByte * expon); - expon *= 256; - maskShifter = maskShifter >> 8; - } - - tempValInt &= params.mask; - - int64_t twocompPoint = params.mask; - if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef; - //qDebug() << "two comp point: " << twocompPoint; - if (params.isSigned && tempValInt > ((twocompPoint / 2))) - { - tempValInt = tempValInt - twocompPoint; - } - - tempValue = (float)tempValInt; - - if (secondsMode) - { - params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); - } - else - { - params.x.append(frame.timestamp - params.xbias); - } - - params.y.append((tempValue * params.scale) + params.bias); - } - else //little endian - { - float tempValue; - int64_t tempValInt; - int numBytes = (params.startByte - params.endByte) + 1; - int64_t shiftRef = 1 << (numBytes * 8); - uint64_t maskShifter; - uint8_t tempByte; - tempValInt = 0; - int64_t expon = 1; - maskShifter = params.mask; - for (int c = 0; c < numBytes; c++) - { - tempByte = frame.data[params.endByte + c]; - tempByte &= maskShifter; - tempValInt += tempByte * expon; - expon *= 256; - maskShifter = maskShifter >> 8; - } - tempValInt &= params.mask; - - int64_t twocompPoint = params.mask; - if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef; - //qDebug() << "two comp point: " << twocompPoint; - if (params.isSigned && tempValInt > ((twocompPoint / 2))) - { - tempValInt = tempValInt - twocompPoint; - } - - tempValue = (float)tempValInt; - - if (secondsMode) - { - params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); - } - else - { - params.x.append(frame.timestamp - params.xbias); - } - - params.y.append((tempValue * params.scale) + params.bias); - } - - params.ref->setData(params.x,params.y); -} - -void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) -{ - int64_t tempVal; //64 bit temp value. - float yminval=10000000.0, ymaxval = -1000000.0; - float xminval=10000000000.0, xmaxval = -10000000000.0; - GraphParams *refParam = ¶ms; - - qDebug() << "New Graph ID: " << params.ID; - qDebug() << "Start byte: " << params.startByte; - qDebug() << "End Byte: " << params.endByte; - qDebug() << "Mask: " << params.mask; - - frameCache.clear(); - for (int i = 0; i < modelFrames->count(); i++) - { - CANFrame thisFrame = modelFrames->at(i); - if (thisFrame.ID == params.ID) frameCache.append(thisFrame); - } - - int numEntries = frameCache.count() / params.stride; - - params.x.reserve(numEntries); - params.y.reserve(numEntries); - params.x.fill(0, numEntries); - params.y.fill(0, numEntries); - - if (params.endByte == -1 || params.startByte == params.endByte) - { - for (int j = 0; j < numEntries; j++) - { - tempVal = (frameCache[j * params.stride].data[params.startByte] & params.mask); + tempVal = (frame.data[params.startByte] & params.mask); if (params.isSigned && tempVal > 127) { tempVal = tempVal - 256; } if (secondsMode) { - params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; + params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); } else { - params.x[j] = frameCache[j].timestamp; + params.x.append(frame.timestamp - params.xbias); } - params.y[j] = (tempVal * params.scale) + params.bias; - if (params.y[j] < yminval) yminval = params.y[j]; - if (params.y[j] > ymaxval) ymaxval = params.y[j]; - if (params.x[j] < xminval) xminval = params.x[j]; - if (params.x[j] > xmaxval) xmaxval = params.x[j]; + params.y.append((tempVal * params.scale) + params.bias); } - } - else if (params.endByte > params.startByte) //big endian - { - float tempValue; - int64_t tempValInt; - int numBytes = (params.endByte - params.startByte) + 1; - int64_t shiftRef = (uint64_t)1 << (numBytes * 8); - uint64_t maskShifter; - uint8_t tempByte; - for (int j = 0; j < numEntries; j++) + else if (params.endByte > params.startByte) //big endian { + float tempValue; + int64_t tempValInt; + int numBytes = (params.endByte - params.startByte) + 1; + int64_t shiftRef = 1 << (numBytes * 8); + uint64_t maskShifter; + uint8_t tempByte; tempValInt = 0; int64_t expon = 1; maskShifter = params.mask; for (int c = 0; c < numBytes; c++) { - tempByte = frameCache[j * params.stride].data[params.endByte - c]; + tempByte = frame.data[params.endByte - c]; tempByte &= maskShifter; tempValInt += (tempByte * expon); expon *= 256; @@ -991,36 +926,29 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) if (secondsMode) { - params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; + params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); } else { - params.x[j] = frameCache[j].timestamp; + params.x.append(frame.timestamp - params.xbias); } - params.y[j] = (tempValue * params.scale) + params.bias; - if (params.y[j] < yminval) yminval = params.y[j]; - if (params.y[j] > ymaxval) ymaxval = params.y[j]; - if (params.x[j] < xminval) xminval = params.x[j]; - if (params.x[j] > xmaxval) xmaxval = params.x[j]; + params.y.append((tempValue * params.scale) + params.bias); } - } - else //little endian - { - float tempValue; - int64_t tempValInt; - int numBytes = (params.startByte - params.endByte) + 1; - int64_t shiftRef = (uint64_t)1 << (numBytes * 8); - uint64_t maskShifter; - uint8_t tempByte; - for (int j = 0; j < numEntries; j++) + else //little endian { + float tempValue; + int64_t tempValInt; + int numBytes = (params.startByte - params.endByte) + 1; + int64_t shiftRef = 1 << (numBytes * 8); + uint64_t maskShifter; + uint8_t tempByte; tempValInt = 0; int64_t expon = 1; maskShifter = params.mask; for (int c = 0; c < numBytes; c++) { - tempByte = frameCache[j * params.stride].data[params.endByte + c]; + tempByte = frame.data[params.endByte + c]; tempByte &= maskShifter; tempValInt += tempByte * expon; expon *= 256; @@ -1040,33 +968,219 @@ void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) if (secondsMode) { - params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; + params.x.append((double)(frame.timestamp) / 1000000.0 - params.xbias); } else { - params.x[j] = frameCache[j].timestamp; + params.x.append(frame.timestamp - params.xbias); } - params.y[j] = (tempValue * params.scale) + params.bias; - if (params.y[j] < yminval) yminval = params.y[j]; - if (params.y[j] > ymaxval) ymaxval = params.y[j]; - if (params.x[j] < xminval) xminval = params.x[j]; - if (params.x[j] > xmaxval) xmaxval = params.x[j]; + params.y.append((tempValue * params.scale) + params.bias); + } + } + + params.ref->setData(params.x,params.y); +} + +void GraphingWindow::createGraph(GraphParams ¶ms, bool createGraphParam) +{ + int64_t tempVal; //64 bit temp value. + float yminval=10000000.0, ymaxval = -1000000.0; + float xminval=10000000000.0, xmaxval = -10000000000.0; + GraphParams *refParam = ¶ms; + DBC_MESSAGE *msg = NULL; + DBC_SIGNAL *sig = NULL; + + if (params.isDBCSignal) + { + msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByID(params.ID); + if (msg) sig = msg->sigHandler->findSignalByName(params.signal); + if (sig == NULL) return; + qDebug() << "New signal graph: " << params.signal <<" in ID:" << params.ID; + } + else + { + qDebug() << "New Graph ID: " << params.ID; + qDebug() << "Start byte: " << params.startByte; + qDebug() << "End Byte: " << params.endByte; + qDebug() << "Mask: " << params.mask; + } + + frameCache.clear(); + for (int i = 0; i < modelFrames->count(); i++) + { + CANFrame thisFrame = modelFrames->at(i); + if (thisFrame.ID == params.ID) frameCache.append(thisFrame); + } + + int numEntries = frameCache.count() / params.stride; + + params.x.reserve(numEntries); + params.y.reserve(numEntries); + params.x.fill(0, numEntries); + params.y.fill(0, numEntries); + + if (params.isDBCSignal) + { + double tempValue; + int l = 0; + for (int j = 0; j < numEntries; j++) + { + //if the given signal was found and successfully processed in this frame then add it to the graph + if (sig->processAsDouble(frameCache[j], tempValue)) + { + //qDebug() << "tempValue: " << tempValue; + if (secondsMode) + { + params.x[l] = (double)(frameCache[j].timestamp) / 1000000.0; + } + else + { + params.x[l] = frameCache[j].timestamp; + } + params.y[l] = tempValue; + if (params.y[l] < yminval) yminval = params.y[l]; + if (params.y[l] > ymaxval) ymaxval = params.y[l]; + if (params.x[l] < xminval) xminval = params.x[l]; + if (params.x[l] > xmaxval) xmaxval = params.x[l]; + l++; + } + } + params.x.resize(l); + params.y.resize(l); + params.x.squeeze(); + params.y.squeeze(); + } + else + { + + if (params.endByte == -1 || params.startByte == params.endByte) + { + for (int j = 0; j < numEntries; j++) + { + tempVal = (frameCache[j * params.stride].data[params.startByte] & params.mask); + if (params.isSigned && tempVal > 127) + { + tempVal = tempVal - 256; + } + if (secondsMode) + { + params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; + } + else + { + params.x[j] = frameCache[j].timestamp; + } + params.y[j] = (tempVal * params.scale) + params.bias; + if (params.y[j] < yminval) yminval = params.y[j]; + if (params.y[j] > ymaxval) ymaxval = params.y[j]; + if (params.x[j] < xminval) xminval = params.x[j]; + if (params.x[j] > xmaxval) xmaxval = params.x[j]; + } + } + else if (params.endByte > params.startByte) //big endian + { + float tempValue; + int64_t tempValInt; + int numBytes = (params.endByte - params.startByte) + 1; + int64_t shiftRef = (uint64_t)1 << (numBytes * 8); + uint64_t maskShifter; + uint8_t tempByte; + for (int j = 0; j < numEntries; j++) + { + tempValInt = 0; + int64_t expon = 1; + maskShifter = params.mask; + for (int c = 0; c < numBytes; c++) + { + tempByte = frameCache[j * params.stride].data[params.endByte - c]; + tempByte &= maskShifter; + tempValInt += (tempByte * expon); + expon *= 256; + maskShifter = maskShifter >> 8; + } + + tempValInt &= params.mask; + + int64_t twocompPoint = params.mask; + if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef; + //qDebug() << "two comp point: " << twocompPoint; + if (params.isSigned && tempValInt > ((twocompPoint / 2))) + { + tempValInt = tempValInt - twocompPoint; + } + + tempValue = (float)tempValInt; + + if (secondsMode) + { + params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; + } + else + { + params.x[j] = frameCache[j].timestamp; + } + + params.y[j] = (tempValue * params.scale) + params.bias; + if (params.y[j] < yminval) yminval = params.y[j]; + if (params.y[j] > ymaxval) ymaxval = params.y[j]; + if (params.x[j] < xminval) xminval = params.x[j]; + if (params.x[j] > xmaxval) xmaxval = params.x[j]; + } + } + else //little endian + { + float tempValue; + int64_t tempValInt; + int numBytes = (params.startByte - params.endByte) + 1; + int64_t shiftRef = (uint64_t)1 << (numBytes * 8); + uint64_t maskShifter; + uint8_t tempByte; + for (int j = 0; j < numEntries; j++) + { + tempValInt = 0; + int64_t expon = 1; + maskShifter = params.mask; + for (int c = 0; c < numBytes; c++) + { + tempByte = frameCache[j * params.stride].data[params.endByte + c]; + tempByte &= maskShifter; + tempValInt += tempByte * expon; + expon *= 256; + maskShifter = maskShifter >> 8; + } + tempValInt &= params.mask; + + int64_t twocompPoint = params.mask; + if (shiftRef < twocompPoint || twocompPoint == -1) twocompPoint = shiftRef; + //qDebug() << "two comp point: " << twocompPoint; + if (params.isSigned && tempValInt > ((twocompPoint / 2))) + { + tempValInt = tempValInt - twocompPoint; + } + + tempValue = (float)tempValInt; + + if (secondsMode) + { + params.x[j] = (double)(frameCache[j].timestamp) / 1000000.0; + } + else + { + params.x[j] = frameCache[j].timestamp; + } + + params.y[j] = (tempValue * params.scale) + params.bias; + if (params.y[j] < yminval) yminval = params.y[j]; + if (params.y[j] > ymaxval) ymaxval = params.y[j]; + if (params.x[j] < xminval) xminval = params.x[j]; + if (params.x[j] > xmaxval) xmaxval = params.x[j]; + } } } params.xbias = 0; - /* - for (int ct = 0; ct < params.x.count(); ct++) - { - params.x[ct] -= xminval; - } - params.xbias = xminval; - xmaxval -= xminval; - xminval = 0; - */ - ui->graphingView->addGraph(); params.ref = ui->graphingView->graph(); if (createGraphParam) diff --git a/graphingwindow.h b/graphingwindow.h index 7c29e70..80c8d4f 100644 --- a/graphingwindow.h +++ b/graphingwindow.h @@ -24,7 +24,9 @@ public: QColor color; QCPGraph *ref; QString graphName; - //the below stuff is used for internal purposes only + bool isDBCSignal; //is this graph actually a DBC signal? If so the below pointer should be filled out + QString signal; + //the below stuff is used for internal purposes only - code should be refactored so these can be private QVector x, y; double xbias; }; diff --git a/mainsettingsdialog.cpp b/mainsettingsdialog.cpp index 3ab2117..a4671cc 100644 --- a/mainsettingsdialog.cpp +++ b/mainsettingsdialog.cpp @@ -29,7 +29,6 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) : ui->cbTimeSeconds->setChecked(settings->value("Main/TimeSeconds", false).toBool()); ui->comboSendingBus->setCurrentIndex(settings->value("Playback/SendingBus", 4).toInt()); ui->cbUseFiltered->setChecked(settings->value("Main/UseFiltered", false).toBool()); - ui->cbSingleWire->setChecked(settings->value("Main/SingleWireMode", false).toBool()); //just for simplicity they all call the same function and that function updates all settings at once connect(ui->cbDisplayHex, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); @@ -44,7 +43,6 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) : connect(ui->cbTimeSeconds, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); connect(ui->comboSendingBus, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSettings())); connect(ui->cbUseFiltered, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); - connect(ui->cbSingleWire, SIGNAL(toggled(bool)), this, SLOT(updateSettings())); } MainSettingsDialog::~MainSettingsDialog() @@ -73,7 +71,6 @@ void MainSettingsDialog::updateSettings() settings->setValue("Main/TimeSeconds", ui->cbTimeSeconds->isChecked()); settings->setValue("Playback/SendingBus", ui->comboSendingBus->currentIndex()); settings->setValue("Main/UseFiltered", ui->cbUseFiltered->isChecked()); - settings->setValue("Main/SingleWireMode", ui->cbSingleWire->isChecked()); settings->sync(); } diff --git a/mainsettingsdialog.ui b/mainsettingsdialog.ui index daa7192..b7f15a3 100644 --- a/mainsettingsdialog.ui +++ b/mainsettingsdialog.ui @@ -55,13 +55,6 @@
- - - - Put second canbus in single wire mode - - -
diff --git a/mainwindow.cpp b/mainwindow.cpp index bd53ef5..71bba18 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -9,14 +9,17 @@ #include "serialworker.h" /* -Things that were planned for the GVRET-PC project but never completed. +Compile for all platforms and create release (remember to include QScintilla libs) and make Win32 binary. + +Some notes on things I'd like to put into the program but haven't put on github (yet) Single / Multi state - The goal is to find bits that change based on toggles or discrete state items (shifters, etc) -Range state - Find things that range like accelerator pedal inputs, road speed, tach, etc - fuzzy scope - Try to find potential places where a given value might be stored - offer guesses and the program tries to find candidates for you or, try to find things that appear to be multi-byte integers + +Allow scripts to read/write signals from DBC files +allow scripts to load DBC files in support of the script - maybe the graphing system too. */ QString MainWindow::loadedFileName = ""; @@ -49,43 +52,22 @@ MainWindow::MainWindow(QWidget *parent) : readSettings(); - QStringList headers; - headers << "Timestamp" << "ID" << "Ext" << "Bus" << "Len" << "Data"; - //model->setHorizontalHeaderLabels(headers); ui->canFramesView->setColumnWidth(0, 110); ui->canFramesView->setColumnWidth(1, 70); ui->canFramesView->setColumnWidth(2, 40); ui->canFramesView->setColumnWidth(3, 40); ui->canFramesView->setColumnWidth(4, 40); - ui->canFramesView->setColumnWidth(5, 275); + ui->canFramesView->setColumnWidth(5, 40); + ui->canFramesView->setColumnWidth(6, 275); QHeaderView *HorzHdr = ui->canFramesView->horizontalHeader(); HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview //enabling the below line kills performance in every way imaginable. Left here as a warning. Do not do this. //ui->canFramesView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - ports = QSerialPortInfo::availablePorts(); - - for (int i = 0; i < ports.count(); i++) - { - ui->cbSerialPorts->addItem(ports[i].portName()); - } - - ui->cbSpeed1->addItem(tr("Disabled")); - ui->cbSpeed1->addItem(tr("125000")); - ui->cbSpeed1->addItem(tr("250000")); - ui->cbSpeed1->addItem(tr("500000")); - ui->cbSpeed1->addItem(tr("1000000")); - ui->cbSpeed1->addItem(tr("33333")); - ui->cbSpeed2->addItem(tr("Disabled")); - ui->cbSpeed2->addItem(tr("125000")); - ui->cbSpeed2->addItem(tr("250000")); - ui->cbSpeed2->addItem(tr("500000")); - ui->cbSpeed2->addItem(tr("1000000")); - ui->cbSpeed2->addItem(tr("33333")); - worker = new SerialWorker(model); worker->moveToThread(&serialWorkerThread); connect(&serialWorkerThread, &QThread::finished, worker, &QObject::deleteLater); + connect(&serialWorkerThread, &QThread::started, worker, &SerialWorker::run); //setup timers within the proper thread connect(this, &MainWindow::sendSerialPort, worker, &SerialWorker::setSerialPort, Qt::QueuedConnection); connect(worker, &SerialWorker::frameUpdateTick, this, &MainWindow::gotFrames, Qt::QueuedConnection); connect(this, &MainWindow::updateBaudRates, worker, &SerialWorker::updateBaudRates, Qt::QueuedConnection); @@ -98,7 +80,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(this, &MainWindow::stopFrameCapturing, worker, &SerialWorker::stopFrameCapture); connect(this, &MainWindow::settingsUpdated, worker, &SerialWorker::readSettings); serialWorkerThread.start(); - serialWorkerThread.setPriority(QThread::HighPriority); + serialWorkerThread.setPriority(QThread::HighPriority); graphingWindow = NULL; frameInfoWindow = NULL; @@ -110,46 +92,53 @@ MainWindow::MainWindow(QWidget *parent) : settingsDialog = NULL; firmwareUploaderWindow = NULL; discreteStateWindow = NULL; + connectionWindow = NULL; + scriptingWindow = NULL; + rangeWindow = NULL; + dbcFileWindow = NULL; + fuzzingWindow = NULL; + udsScanWindow = NULL; dbcHandler = new DBCHandler; bDirty = false; inhibitFilterUpdate = false; model->setDBCHandler(dbcHandler); - connect(ui->btnConnect, SIGNAL(clicked(bool)), this, SLOT(connButtonPress())); - connect(ui->actionOpen_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleLoadFile())); - connect(ui->actionGraph_Dta, SIGNAL(triggered(bool)), this, SLOT(showGraphingWindow())); - connect(ui->actionFrame_Data_Analysis, SIGNAL(triggered(bool)), this, SLOT(showFrameDataAnalysis())); - connect(ui->btnClearFrames, SIGNAL(clicked(bool)), this, SLOT(clearFrames())); - connect(ui->actionSave_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveFile())); - connect(ui->actionSave_Filtered_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveFilteredFile())); - connect(ui->actionLoad_Filter_Definition, SIGNAL(triggered(bool)), this, SLOT(handleLoadFilters())); - connect(ui->actionSave_Filter_Definition, SIGNAL(triggered(bool)), this, SLOT(handleSaveFilters())); - connect(ui->action_Playback, SIGNAL(triggered(bool)), this, SLOT(showPlaybackWindow())); - connect(ui->btnBaudSet, SIGNAL(clicked(bool)), this, SLOT(changeBaudRates())); - connect(ui->actionFlow_View, SIGNAL(triggered(bool)), this, SLOT(showFlowViewWindow())); - connect(ui->action_Custom, SIGNAL(triggered(bool)), this, SLOT(showFrameSenderWindow())); - connect(ui->actionLoad_DBC_File, SIGNAL(triggered(bool)), this, SLOT(handleLoadDBC())); - connect(ui->actionSave_DBC_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveDBC())); - connect(ui->canFramesView, SIGNAL(clicked(QModelIndex)), this, SLOT(gridClicked(QModelIndex))); - connect(ui->canFramesView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(gridDoubleClicked(QModelIndex))); - connect(ui->cbInterpret, SIGNAL(toggled(bool)), this, SLOT(interpretToggled(bool))); - connect(ui->cbOverwrite, SIGNAL(toggled(bool)), this, SLOT(overwriteToggled(bool))); - connect(ui->actionEdit_Messages_Signals, SIGNAL(triggered(bool)), this, SLOT(showDBCEditor())); - connect(ui->btnCaptureToggle, SIGNAL(clicked(bool)), this, SLOT(toggleCapture())); - connect(ui->actionExit_Application, SIGNAL(triggered(bool)), this, SLOT(exitApp())); - connect(ui->actionFuzzy_Scope, SIGNAL(triggered(bool)), this, SLOT(showFuzzyScopeWindow())); - connect(ui->actionRange_State, SIGNAL(triggered(bool)), this, SLOT(showRangeWindow())); - connect(ui->actionSave_Decoded_Frames, SIGNAL(triggered(bool)), this, SLOT(handleSaveDecoded())); - connect(ui->actionSingle_Multi_State, SIGNAL(triggered(bool)), this, SLOT(showSingleMultiWindow())); - connect(ui->actionFile_Comparison, SIGNAL(triggered(bool)), this, SLOT(showComparisonWindow())); - connect(ui->btnNormalize, SIGNAL(clicked(bool)), this, SLOT(normalizeTiming())); - connect(ui->actionPreferences, SIGNAL(triggered(bool)), this, SLOT(showSettingsDialog())); - connect(model, SIGNAL(updatedFiltersList()), this, SLOT(updateFilterList())); - connect(ui->listFilters, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(filterListItemChanged(QListWidgetItem*))); - connect(ui->btnFilterAll, SIGNAL(clicked(bool)), this, SLOT(filterSetAll())); - connect(ui->btnFilterNone, SIGNAL(clicked(bool)), this, SLOT(filterClearAll())); - connect(ui->actionFirmware_Uploader, SIGNAL(triggered(bool)), this, SLOT(showFirmwareUploaderWindow())); + connect(ui->actionSetup, SIGNAL(triggered(bool)), SLOT(showConnectionSettingsWindow())); + connect(ui->actionConnect, SIGNAL(triggered(bool)), this, SLOT(connButtonPress())); + connect(ui->actionOpen_Log_File, &QAction::triggered, this, &MainWindow::handleLoadFile); + connect(ui->actionGraph_Dta, &QAction::triggered, this, &MainWindow::showGraphingWindow); + connect(ui->actionFrame_Data_Analysis, &QAction::triggered, this, &MainWindow::showFrameDataAnalysis); + connect(ui->btnClearFrames, &QAbstractButton::clicked, this, &MainWindow::clearFrames); + connect(ui->actionSave_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFile); + connect(ui->actionSave_Filtered_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFilteredFile); + connect(ui->actionLoad_Filter_Definition, &QAction::triggered, this, &MainWindow::handleLoadFilters); + connect(ui->actionSave_Filter_Definition, &QAction::triggered, this, &MainWindow::handleSaveFilters); + connect(ui->action_Playback, &QAction::triggered, this, &MainWindow::showPlaybackWindow); + connect(ui->actionFlow_View, &QAction::triggered, this, &MainWindow::showFlowViewWindow); + connect(ui->action_Custom, &QAction::triggered, this, &MainWindow::showFrameSenderWindow); + connect(ui->canFramesView, &QAbstractItemView::clicked, this, &MainWindow::gridClicked); + connect(ui->canFramesView, &QAbstractItemView::doubleClicked, this, &MainWindow::gridDoubleClicked); + connect(ui->cbInterpret, &QAbstractButton::toggled, this, &MainWindow::interpretToggled); + connect(ui->cbOverwrite, &QAbstractButton::toggled, this, &MainWindow::overwriteToggled); + connect(ui->btnCaptureToggle, &QAbstractButton::clicked, this, &MainWindow::toggleCapture); + connect(ui->actionExit_Application, &QAction::triggered, this, &MainWindow::exitApp); + connect(ui->actionFuzzy_Scope, &QAction::triggered, this, &MainWindow::showFuzzyScopeWindow); + connect(ui->actionRange_State_2, &QAction::triggered, this, &MainWindow::showRangeWindow); + connect(ui->actionSave_Decoded_Frames, &QAction::triggered, this, &MainWindow::handleSaveDecoded); + connect(ui->actionSingle_Multi_State_2, &QAction::triggered, this, &MainWindow::showSingleMultiWindow); + connect(ui->actionFile_Comparison, &QAction::triggered, this, &MainWindow::showComparisonWindow); + connect(ui->actionScripting_INterface, &QAction::triggered, this, &MainWindow::showScriptingWindow); + connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming); + connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::showSettingsDialog); + connect(model, &CANFrameModel::updatedFiltersList, this, &MainWindow::updateFilterList); + connect(ui->listFilters, &QListWidget::itemChanged, this, &MainWindow::filterListItemChanged); + connect(ui->btnFilterAll, &QAbstractButton::clicked, this, &MainWindow::filterSetAll); + connect(ui->btnFilterNone, &QAbstractButton::clicked, this, &MainWindow::filterClearAll); + connect(ui->actionFirmware_Uploader, &QAction::triggered, this, &MainWindow::showFirmwareUploaderWindow); + connect(ui->actionDBC_File_Manager, &QAction::triggered, this, &MainWindow::showDBCFileWindow); + connect(ui->actionFuzzing, &QAction::triggered, this, &MainWindow::showFuzzingWindow); + connect(ui->actionUDS_Scanner, &QAction::triggered, this, &MainWindow::showUDSScanWindow); lbStatusConnected.setText(tr("Not connected")); updateFileStatus(); @@ -175,6 +164,10 @@ MainWindow::MainWindow(QWidget *parent) : normalRowHeight = ui->canFramesView->rowHeight(0); qDebug() << "normal row height = " << normalRowHeight; model->clearFrames(); + + //Automatically create the connection window so it can be updated even if we never opened it. + connectionWindow = new ConnectionWindow(); + connect(connectionWindow, SIGNAL(updateConnectionSettings(QString,QString,int,int)), this, SLOT(updateConnectionSettings(QString,QString,int,int))); } MainWindow::~MainWindow() @@ -237,12 +230,68 @@ MainWindow::~MainWindow() delete discreteStateWindow; } + if (connectionWindow) + { + connectionWindow->close(); + delete connectionWindow; + } + + if (scriptingWindow) + { + scriptingWindow->close(); + delete scriptingWindow; + } + + if (rangeWindow) + { + rangeWindow->close(); + delete rangeWindow; + } + + if (dbcFileWindow) + { + dbcFileWindow->close(); + delete dbcFileWindow; + } + + if (fuzzingWindow) + { + fuzzingWindow->close(); + delete fuzzingWindow; + } + + if (udsScanWindow) + { + udsScanWindow->close(); + delete udsScanWindow; + } + delete ui; delete dbcHandler; model->clearFrames(); delete model; } +void MainWindow::exitApp() +{ + if (graphingWindow) graphingWindow->close(); + if (frameInfoWindow) frameInfoWindow->close(); + if (playbackWindow) playbackWindow->close(); + if (flowViewWindow) flowViewWindow->close(); + if (frameSenderWindow) frameSenderWindow->close(); + if (dbcMainEditor) dbcMainEditor->close(); + if (comparatorWindow) comparatorWindow->close(); + if (connectionWindow) connectionWindow->close(); + if (settingsDialog) settingsDialog->close(); + if (discreteStateWindow) discreteStateWindow->close(); + if (scriptingWindow) scriptingWindow->close(); + if (rangeWindow) rangeWindow->close(); + if (dbcFileWindow) dbcFileWindow->close(); + if (fuzzingWindow) fuzzingWindow->close(); + if (udsScanWindow) udsScanWindow->close(); + this->close(); +} + void MainWindow::closeEvent(QCloseEvent *event) { Q_UNUSED(event); @@ -268,6 +317,20 @@ void MainWindow::readSettings() { ui->cbAutoScroll->setChecked(true); } + + int cType = settings.value("Main/DefaultConnectionType", 0).toInt(); + if (cType == 0) connType = "GVRET"; + if (cType == 1) connType = "KVASER"; + if (cType == 2) connType = "SOCKETCAN"; + portName = settings.value("Main/DefaultConnectionPort", "").toString(); + canSpeed0 = -1; + canSpeed1 = -1; + + qDebug() << connType; + qDebug() << portName; + + //"Main/SingleWireMode" + readUpdateableSettings(); } @@ -293,6 +356,57 @@ void MainWindow::writeSettings() } } +void MainWindow::updateConnectionSettings(QString connectionType, QString port, int speed0, int speed1) +{ + connType = connectionType; + portName = port; + + canSpeed0 = speed0; + canSpeed1 = speed1; + if (isConnected) + { + emit updateBaudRates(speed0, speed1); + } +} + +void MainWindow::connButtonPress() +{ + if (!isConnected) + { + if (connType == "GVRET") + { + QList ports; + ports = QSerialPortInfo::availablePorts(); + + for (int i = 0; i < ports.count(); i++) + { + if (ports[i].portName() == portName) + { + portInfo = ports[i]; + //need to create some way to send single wire mode to serial port code + emit sendSerialPort(&portInfo); + lbStatusConnected.setText(tr("Attempting to connect to port ") + portName); + } + } + } + else if (connType == "KVASER") + { + + } + else if (connType == "SOCKETCAN") + { + + } + } + else + { + emit closeSerialPort(); + isConnected = false; + lbStatusConnected.setText(tr("Not Connected")); + ui->actionConnect->setText(tr("Connect")); + } +} + void MainWindow::gridClicked(QModelIndex idx) { if (ui->canFramesView->rowHeight(idx.row()) > normalRowHeight) @@ -437,8 +551,8 @@ void MainWindow::changeBaudRates() { int Speed1 = 0, Speed2 = 0; - Speed1 = ui->cbSpeed1->currentText().toInt(); - Speed2 = ui->cbSpeed2->currentText().toInt(); + //Speed1 = ui->cbSpeed1->currentText().toInt(); + //Speed2 = ui->cbSpeed2->currentText().toInt(); emit updateBaudRates(Speed1, Speed2); } @@ -446,216 +560,42 @@ void MainWindow::changeBaudRates() void MainWindow::handleLoadFile() { QString filename; - QFileDialog dialog(this); - bool result = false; + QVector tempFrames; - QStringList filters; - filters.append(QString(tr("CRTD Logs (*.txt)"))); - filters.append(QString(tr("GVRET Logs (*.csv)"))); - filters.append(QString(tr("Generic ID/Data CSV (*.csv)"))); - filters.append(QString(tr("BusMaster Log (*.log)"))); - filters.append(QString(tr("Microchip Log (*.can)"))); - filters.append(QString(tr("Vector Trace Files (*.trace)"))); - - dialog.setFileMode(QFileDialog::ExistingFile); - dialog.setNameFilters(filters); - dialog.setViewMode(QFileDialog::Detail); - - if (dialog.exec() == QDialog::Accepted) + if (FrameFileIO::loadFrameFile(filename, &tempFrames)) { - filename = dialog.selectedFiles()[0]; ui->canFramesView->scrollToTop(); model->clearFrames(); + model->insertFrames(tempFrames); + loadedFileName = filename; + model->recalcOverwrite(); + ui->lbNumFrames->setText(QString::number(model->rowCount())); + if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); - QVector tempFrames; - - QProgressDialog progress(this); - progress.setWindowModality(Qt::WindowModal); - progress.setLabelText("Loading file..."); - progress.setCancelButton(0); - progress.setRange(0,0); - progress.setMinimumDuration(0); - progress.show(); - - qApp->processEvents(); - - if (dialog.selectedNameFilter() == filters[0]) result = FrameFileIO::loadCRTDFile(filename, &tempFrames); - if (dialog.selectedNameFilter() == filters[1]) result = FrameFileIO::loadNativeCSVFile(filename, &tempFrames); - if (dialog.selectedNameFilter() == filters[2]) result = FrameFileIO::loadGenericCSVFile(filename, &tempFrames); - if (dialog.selectedNameFilter() == filters[3]) result = FrameFileIO::loadLogFile(filename, &tempFrames); - if (dialog.selectedNameFilter() == filters[4]) result = FrameFileIO::loadMicrochipFile(filename, &tempFrames); - if (dialog.selectedNameFilter() == filters[5]) result = FrameFileIO::loadTraceFile(filename, &tempFrames); - - progress.cancel(); - - if (result) - { - model->insertFrames(tempFrames); - - QStringList fileList = filename.split('/'); - loadedFileName = fileList[fileList.length() - 1]; - - model->recalcOverwrite(); - ui->lbNumFrames->setText(QString::number(model->rowCount())); - if (ui->cbAutoScroll->isChecked()) ui->canFramesView->scrollToBottom(); - - updateFileStatus(); - emit framesUpdated(-2); - } + updateFileStatus(); + emit framesUpdated(-2); } } void MainWindow::handleSaveFile() { QString filename; - QFileDialog dialog(this); - bool result = false; - QStringList filters; - filters.append(QString(tr("CRTD Logs (*.txt)"))); - filters.append(QString(tr("GVRET Logs (*.csv)"))); - filters.append(QString(tr("Generic ID/Data CSV (*.csv)"))); - filters.append(QString(tr("BusMaster Log (*.log)"))); - filters.append(QString(tr("Microchip Log (*.can)"))); - filters.append(QString(tr("Vector Trace Files (*.trace)"))); - - dialog.setFileMode(QFileDialog::AnyFile); - dialog.setNameFilters(filters); - dialog.setViewMode(QFileDialog::Detail); - dialog.setAcceptMode(QFileDialog::AcceptSave); - - if (dialog.exec() == QDialog::Accepted) + if (FrameFileIO::saveFrameFile(filename, model->getListReference())) { - const QVector *frames = model->getListReference(); - filename = dialog.selectedFiles()[0]; - - QProgressDialog progress(this); - progress.setWindowModality(Qt::WindowModal); - progress.setLabelText("Saving file..."); - progress.setCancelButton(0); - progress.setRange(0,0); - progress.setMinimumDuration(0); - progress.show(); - - qApp->processEvents(); - - if (dialog.selectedNameFilter() == filters[0]) - { - if (!filename.contains('.')) filename += ".txt"; - result = FrameFileIO::saveCRTDFile(filename, frames); - } - if (dialog.selectedNameFilter() == filters[1]) - { - if (!filename.contains('.')) filename += ".csv"; - result = FrameFileIO::saveNativeCSVFile(filename, frames); - } - if (dialog.selectedNameFilter() == filters[2]) - { - if (!filename.contains('.')) filename += ".csv"; - result = FrameFileIO::saveGenericCSVFile(filename, frames); - } - if (dialog.selectedNameFilter() == filters[3]) - { - if (!filename.contains('.')) filename += ".log"; - result = FrameFileIO::saveLogFile(filename, frames); - } - if (dialog.selectedNameFilter() == filters[4]) - { - if (!filename.contains('.')) filename += ".log"; - result = FrameFileIO::saveMicrochipFile(filename, frames); - } - - if (dialog.selectedNameFilter() == filters[5]) - { - if (!filename.contains('.')) filename += ".trace"; - result = FrameFileIO::saveTraceFile(filename, frames); - } - - progress.cancel(); - - if (result) - { - QStringList fileList = filename.split('/'); - loadedFileName = fileList[fileList.length() - 1]; - updateFileStatus(); - } + loadedFileName = filename; + updateFileStatus(); } } void MainWindow::handleSaveFilteredFile() { QString filename; - QFileDialog dialog(this); - bool result = false; - QStringList filters; - filters.append(QString(tr("CRTD Logs (*.txt)"))); - filters.append(QString(tr("GVRET Logs (*.csv)"))); - filters.append(QString(tr("Generic ID/Data CSV (*.csv)"))); - filters.append(QString(tr("BusMaster Log (*.log)"))); - filters.append(QString(tr("Microchip Log (*.log)"))); - filters.append(QString(tr("Vector Trace Files (*.trace)"))); - - dialog.setFileMode(QFileDialog::AnyFile); - dialog.setNameFilters(filters); - dialog.setViewMode(QFileDialog::Detail); - dialog.setAcceptMode(QFileDialog::AcceptSave); - - if (dialog.exec() == QDialog::Accepted) + if (FrameFileIO::saveFrameFile(filename, model->getFilteredListReference())) { - const QVector *frames = model->getFilteredListReference(); - filename = dialog.selectedFiles()[0]; - - QProgressDialog progress(this); - progress.setWindowModality(Qt::WindowModal); - progress.setLabelText("Saving filtered file..."); - progress.setCancelButton(0); - progress.setRange(0,0); - progress.setMinimumDuration(0); - progress.show(); - - qApp->processEvents(); - - if (dialog.selectedNameFilter() == filters[0]) - { - if (!filename.contains('.')) filename += ".txt"; - result = FrameFileIO::saveCRTDFile(filename, frames); - } - if (dialog.selectedNameFilter() == filters[1]) - { - if (!filename.contains('.')) filename += ".csv"; - result = FrameFileIO::saveNativeCSVFile(filename, frames); - } - if (dialog.selectedNameFilter() == filters[2]) - { - if (!filename.contains('.')) filename += ".csv"; - result = FrameFileIO::saveGenericCSVFile(filename, frames); - } - if (dialog.selectedNameFilter() == filters[3]) - { - if (!filename.contains('.')) filename += ".log"; - result = FrameFileIO::saveLogFile(filename, frames); - } - if (dialog.selectedNameFilter() == filters[4]) - { - if (!filename.contains('.')) filename += ".log"; - result = FrameFileIO::saveMicrochipFile(filename, frames); - } - - if (dialog.selectedNameFilter() == filters[5]) - { - if (!filename.contains('.')) filename += ".trace"; - result = FrameFileIO::saveTraceFile(filename, frames); - } - - progress.cancel(); - - if (result) - { - QStringList fileList = filename.split('/'); - loadedFileName = fileList[fileList.length() - 1]; - updateFileStatus(); - } + loadedFileName = filename; + updateFileStatus(); } } @@ -700,52 +640,7 @@ void MainWindow::handleLoadFilters() } } -void MainWindow::handleLoadDBC() -{ - QString filename; - QFileDialog dialog(this); - - QStringList filters; - filters.append(QString(tr("DBC File (*.dbc)"))); - - dialog.setFileMode(QFileDialog::ExistingFile); - dialog.setNameFilters(filters); - dialog.setViewMode(QFileDialog::Detail); - - if (dialog.exec() == QDialog::Accepted) - { - filename = dialog.selectedFiles()[0]; - //right now there is only one file type that can be loaded here so just do it. - dbcHandler->loadDBCFile(filename); - //dbcHandler->listDebugging(); - QStringList fileList = filename.split('/'); - lbStatusDatabase.setText(fileList[fileList.length() - 1] + tr(" loaded.")); - } -} - -void MainWindow::handleSaveDBC() -{ - QString filename; - QFileDialog dialog(this); - - QStringList filters; - filters.append(QString(tr("DBC File (*.dbc)"))); - - 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 += ".dbc"; - dbcHandler->saveDBCFile(filename); - QStringList fileList = filename.split('/'); - lbStatusDatabase.setText(fileList[fileList.length() - 1] + tr(" loaded.")); - } - -} +//lbStatusDatabase.setText(fileList[fileList.length() - 1] + tr(" loaded.")); void MainWindow::handleSaveDecoded() { @@ -771,7 +666,7 @@ void MainWindow::handleSaveDecoded() void MainWindow::saveDecodedTextFile(QString filename) { QFile *outFile = new QFile(filename); - const QVector *frames = model->getListReference(); + const QVector *frames = model->getFilteredListReference(); if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) return; @@ -803,14 +698,18 @@ Data Bytes: 88 10 00 13 BB 00 06 00 builderString = ""; if (dbcHandler != NULL) { - DBC_MESSAGE *msg = dbcHandler->findMsgByID(thisFrame.ID); + DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame); if (msg != NULL) { - for (int j = 0; j < msg->msgSignals.length(); j++) + for (int j = 0; j < msg->sigHandler->getCount(); j++) { - builderString.append("\t" + dbcHandler->processSignal(thisFrame, msg->msgSignals.at(j))); - builderString.append("\n"); + QString temp; + if (msg->sigHandler->findSignalByIdx(j)->processAsText(thisFrame, temp)) + { + builderString.append("\t" + temp); + builderString.append("\n"); + } } } builderString.append("\n"); @@ -820,29 +719,6 @@ Data Bytes: 88 10 00 13 BB 00 06 00 outFile->close(); } -void MainWindow::connButtonPress() -{ - if (!isConnected) - { - for (int x = 0; x < ports.count(); x++) - { - if (ports.at(x).portName() == ui->cbSerialPorts->currentText()) - { - emit sendSerialPort(&ports[x]); - lbStatusConnected.setText(tr("Attempting to connect to port ") + ports[x].portName()); - return; - } - } - } - else - { - emit closeSerialPort(); - isConnected = false; - lbStatusConnected.setText(tr("Not Connected")); - ui->btnConnect->setText(tr("Connect to GVRET")); - } -} - void MainWindow::toggleCapture() { allowCapture = !allowCapture; @@ -863,7 +739,7 @@ void MainWindow::connectionSucceeded(int baud0, int baud1) lbStatusConnected.setText(tr("Connected to GVRET")); //finally, find the baud rate in the list of rates or //add it to the bottom if needed (that'll be weird though... - +/* int idx = ui->cbSpeed1->findText(QString::number(baud0)); if (idx > -1) ui->cbSpeed1->setCurrentIndex(idx); else @@ -879,8 +755,10 @@ void MainWindow::connectionSucceeded(int baud0, int baud1) ui->cbSpeed2->addItem(QString::number(baud1)); ui->cbSpeed2->setCurrentIndex(ui->cbSpeed2->count() - 1); } + */ + if (connectionWindow) connectionWindow->setSpeeds(baud0, baud1); //ui->btnConnect->setEnabled(false); - ui->btnConnect->setText(tr("Disconnect from GVRET")); + ui->actionConnect->setText(tr("Disconnect")); isConnected = true; } @@ -933,6 +811,12 @@ void MainWindow::gotDeviceInfo(int build, int swCAN) + "\n\nPlease upgrade your firmware version.\nOtherwise, you may experience difficulties."); msgBox.exec(); } + + if (connectionWindow) + { + if (swCAN == 1) connectionWindow->setCAN1SWMode(true); + else connectionWindow->setCAN1SWMode(false); + } } void MainWindow::setTargettedID(int id) @@ -989,7 +873,7 @@ void MainWindow::showFrameSenderWindow() else frameSenderWindow = new FrameSenderWindow(model->getFilteredListReference()); - connect(frameSenderWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int))); + connect(frameSenderWindow, &FrameSenderWindow::sendCANFrame, worker, &SerialWorker::sendFrame); } frameSenderWindow->show(); } @@ -1035,9 +919,44 @@ void MainWindow::showSingleMultiWindow() discreteStateWindow->show(); } +void MainWindow::showFuzzingWindow() +{ + if (!fuzzingWindow) + { + fuzzingWindow = new FuzzingWindow(model->getListReference()); + connect(fuzzingWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int))); + connect(fuzzingWindow, SIGNAL(sendFrameBatch(const QList*)), worker, SLOT(sendFrameBatch(const QList*))); + } + fuzzingWindow->show(); +} + +void MainWindow::showUDSScanWindow() +{ + if (!udsScanWindow) + { + udsScanWindow = new UDSScanWindow(model->getListReference()); + connect(udsScanWindow, SIGNAL(sendCANFrame(const CANFrame*,int)), worker, SLOT(sendFrame(const CANFrame*,int))); + } + udsScanWindow->show(); +} + +void MainWindow::showScriptingWindow() +{ + if (!scriptingWindow) + { + scriptingWindow = new ScriptingWindow(model->getListReference()); + connect(scriptingWindow, &ScriptingWindow::sendCANFrame, worker, &SerialWorker::sendFrame); + } + scriptingWindow->show(); +} + void MainWindow::showRangeWindow() { - //not done yet + if (!rangeWindow) + { + rangeWindow = new RangeStateWindow(model->getListReference()); + } + rangeWindow->show(); } void MainWindow::showFuzzyScopeWindow() @@ -1045,18 +964,6 @@ void MainWindow::showFuzzyScopeWindow() //not done yet } -void MainWindow::exitApp() -{ - if (graphingWindow) graphingWindow->close(); - if (frameInfoWindow) frameInfoWindow->close(); - if (playbackWindow) playbackWindow->close(); - if (flowViewWindow) flowViewWindow->close(); - if (frameSenderWindow) frameSenderWindow->close(); - if (dbcMainEditor) dbcMainEditor->close(); - if (comparatorWindow) comparatorWindow->close(); - this->close(); -} - void MainWindow::showFlowViewWindow() { if (!flowViewWindow) @@ -1078,12 +985,21 @@ void MainWindow::showFlowViewWindow() flowViewWindow->show(); } -//this one always gets the unfiltered list intentionally. -void MainWindow::showDBCEditor() +void MainWindow::showDBCFileWindow() { - if (!dbcMainEditor) + if (!dbcFileWindow) { - dbcMainEditor = new DBCMainEditor(dbcHandler, model->getListReference()); + dbcFileWindow = new DBCLoadSaveWindow(dbcHandler, model->getListReference()); } - dbcMainEditor->show(); + dbcFileWindow->show(); +} + +void MainWindow::showConnectionSettingsWindow() +{ + if (!connectionWindow) + { + connectionWindow = new ConnectionWindow(); + connect(connectionWindow, SIGNAL(updateConnectionSettings(QString,QString,int,int)), this, SLOT(updateConnectionSettings(QString,QString,int,int))); + } + connectionWindow->show(); } diff --git a/mainwindow.h b/mainwindow.h index 6149657..cf99eba 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -19,6 +19,12 @@ #include "mainsettingsdialog.h" #include "firmwareuploaderwindow.h" #include "discretestatewindow.h" +#include "connectionwindow.h" +#include "scriptingwindow.h" +#include "rangestatewindow.h" +#include "dbcloadsavewindow.h" +#include "fuzzingwindow.h" +#include "udsscanwindow.h" namespace Ui { class MainWindow; @@ -39,8 +45,6 @@ private slots: void handleLoadFile(); void handleSaveFile(); void handleSaveFilteredFile(); - void handleLoadDBC(); - void handleSaveDBC(); void handleSaveFilters(); void handleLoadFilters(); void connButtonPress(); @@ -56,6 +60,11 @@ private slots: void showComparisonWindow(); void showSettingsDialog(); void showFirmwareUploaderWindow(); + void showConnectionSettingsWindow(); + void showScriptingWindow(); + void showDBCFileWindow(); + void showFuzzingWindow(); + void showUDSScanWindow(); void exitApp(); void handleSaveDecoded(); void changeBaudRates(); @@ -66,7 +75,6 @@ private slots: void gridDoubleClicked(QModelIndex); void interpretToggled(bool); void overwriteToggled(bool); - void showDBCEditor(); void toggleCapture(); void normalizeTiming(); void updateFilterList(); @@ -78,6 +86,7 @@ public slots: void gotFrames(int, int); void updateSettings(); void gotCenterTimeID(int32_t ID, double timestamp); + void updateConnectionSettings(QString connectionType, QString port, int speed0, int speed1); signals: void sendSerialPort(QSerialPortInfo *port); @@ -98,8 +107,7 @@ private: //canbus related data CANFrameModel *model; - DBCHandler *dbcHandler; - QList ports; + DBCHandler *dbcHandler; QThread serialWorkerThread; SerialWorker *worker; QByteArray inputBuffer; @@ -121,6 +129,12 @@ private: MainSettingsDialog *settingsDialog; DiscreteStateWindow *discreteStateWindow; FirmwareUploaderWindow *firmwareUploaderWindow; + ConnectionWindow *connectionWindow; + ScriptingWindow *scriptingWindow; + RangeStateWindow *rangeWindow; + DBCLoadSaveWindow *dbcFileWindow; + FuzzingWindow *fuzzingWindow; + UDSScanWindow *udsScanWindow; //various private storage QLabel lbStatusConnected; @@ -128,6 +142,9 @@ private: QLabel lbStatusDatabase; int normalRowHeight; bool isConnected; + QSerialPortInfo portInfo; + QString connType, portName; + int canSpeed0, canSpeed1; //private methods void saveDecodedTextFile(QString); diff --git a/mainwindow.ui b/mainwindow.ui index 7ce39f6..4e7a167 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -46,102 +46,6 @@ - - - - - 0 - 0 - - - - Serial Port - - - - - - - - - - Connect To GVRET - - - - - - - - 0 - 0 - - - - CANBus Speeds: - - - - - - - QLayout::SetDefaultConstraint - - - - - - - - 0 - 0 - - - - First Bus: - - - - - - - - - - - - 7 - - - QLayout::SetDefaultConstraint - - - - - - 0 - 0 - - - - Second Bus: - - - - - - - - - - - - - - Set CANBUS Speeds - - - @@ -293,7 +197,7 @@ 0 0 800 - 21 + 27 @@ -304,6 +208,8 @@ + + @@ -311,6 +217,9 @@ + + + @@ -323,18 +232,24 @@ - - - + + + + Connection + + + + + @@ -347,16 +262,6 @@ Custom - - - Single / Multi State - - - - - Range State - - Flow View @@ -442,6 +347,46 @@ Firmware Uploader + + + Set Port + + + + + Connect + + + + + Scripting Interface + + + + + Range State + + + + + Single/Multi State + + + + + DBC File Manager + + + + + Fuzzing + + + + + UDS Scanner + + diff --git a/newgraphdialog.cpp b/newgraphdialog.cpp index 41aa28f..409e2f9 100644 --- a/newgraphdialog.cpp +++ b/newgraphdialog.cpp @@ -25,6 +25,8 @@ NewGraphDialog::NewGraphDialog(DBCHandler *handler, QWidget *parent) : connect(ui->cbMessages, SIGNAL(currentIndexChanged(int)), this, SLOT(loadSignals(int))); connect(ui->cbSignals, SIGNAL(currentIndexChanged(int)), this, SLOT(fillFormFromSignal(int))); + connect(ui->rbSignalGraph, SIGNAL(toggled(bool)), this, SLOT(setSignalActive(bool))); + connect(ui->rbStandardGraph, SIGNAL(toggled(bool)), this, SLOT(setStandardActive(bool))); loadMessages(); } @@ -58,28 +60,41 @@ void NewGraphDialog::clearParams() ui->txtStride->clear(); ui->txtName->clear(); ui->txtData->clear(); + ui->rbStandardGraph->setChecked(true); + setStandardActive(true); } void NewGraphDialog::setParams(GraphParams ¶ms) { - ui->txtID->setText(Utility::formatNumber(params.ID)); - ui->txtBias->setText(QString::number(params.bias)); - ui->txtMask->setText(Utility::formatNumber(params.mask)); - ui->txtScale->setText(QString::number(params.scale)); - ui->txtStride->setText(QString::number(params.stride)); - ui->cbSigned->setChecked(params.isSigned); - ui->txtName->setText(params.graphName); - - if (params.endByte > -1) + if (params.isDBCSignal) { - ui->txtData->setText(QString::number(params.startByte) + "-" + QString::number(params.endByte)); + clearParams(); + setSignalActive(true); + //loadMessages(); + } else { - ui->txtData->setText(QString::number(params.startByte)); + setStandardActive(true); + ui->txtBias->setText(QString::number(params.bias)); + ui->txtMask->setText(Utility::formatNumber(params.mask)); + ui->txtScale->setText(QString::number(params.scale)); + ui->txtStride->setText(QString::number(params.stride)); + ui->cbSigned->setChecked(params.isSigned); + + if (params.endByte > -1) + { + ui->txtData->setText(QString::number(params.startByte) + "-" + QString::number(params.endByte)); + } + else + { + ui->txtData->setText(QString::number(params.startByte)); + } } + ui->txtID->setText(Utility::formatNumber(params.ID)); + ui->txtName->setText(params.graphName); QPalette p = ui->colorSwatch->palette(); p.setColor(QPalette::Button, params.color); ui->colorSwatch->setPalette(p); @@ -87,40 +102,58 @@ void NewGraphDialog::setParams(GraphParams ¶ms) void NewGraphDialog::getParams(GraphParams ¶ms) { - params.ID = Utility::ParseStringToNum(ui->txtID->text()); - params.bias = ui->txtBias->text().toFloat(); + params.isDBCSignal = ui->rbSignalGraph->isChecked(); params.color = ui->colorSwatch->palette().button().color(); - params.isSigned = ui->cbSigned->isChecked(); - params.mask = Utility::ParseStringToNum(ui->txtMask->text()); - params.scale = ui->txtScale->text().toFloat(); - params.stride = Utility::ParseStringToNum(ui->txtStride->text()); - - QStringList values = ui->txtData->text().split('-'); - params.startByte = -1; - params.endByte = -1; - if (values.count() > 0) - { - params.startByte = values[0].toInt(); - if (values.count() > 1) - { - params.endByte = values[1].toInt(); - } - } - - //now catch stupidity and bring it to defaults - if (params.mask == 0) params.mask = 0xFFFFFFFF; - if (fabs(params.scale) < 0.00000001) params.scale = 1.0f; - if (params.stride < 1) params.stride = 1; params.graphName = ui->txtName->text(); + + if (params.isDBCSignal) + { + params.signal = ui->cbSignals->currentText(); + params.ID = Utility::ParseStringToNum(ui->txtID->text()); + params.bias = 0; + params.isSigned = false; + params.mask = 0; + params.scale = 1; + params.bias = 0; + params.stride = 1; + + } + else { + params.ID = Utility::ParseStringToNum(ui->txtID->text()); + params.bias = ui->txtBias->text().toFloat(); + params.isSigned = ui->cbSigned->isChecked(); + params.mask = Utility::ParseStringToNum(ui->txtMask->text()); + params.scale = ui->txtScale->text().toFloat(); + params.stride = Utility::ParseStringToNum(ui->txtStride->text()); + params.signal = ""; + + QStringList values = ui->txtData->text().split('-'); + params.startByte = -1; + params.endByte = -1; + if (values.count() > 0) + { + params.startByte = values[0].toInt(); + if (values.count() > 1) + { + params.endByte = values[1].toInt(); + } + } + + //now catch stupidity and bring it to defaults + if (params.mask == 0) params.mask = 0xFFFFFFFF; + if (fabs(params.scale) < 0.00000001) params.scale = 1.0f; + if (params.stride < 1) params.stride = 1; + } } void NewGraphDialog::loadMessages() { ui->cbMessages->clear(); if (dbcHandler == NULL) return; - for (int x = 0; x < dbcHandler->dbc_messages.count(); x++) + if (dbcHandler->getFileCount() == 0) dbcHandler->createBlankFile(); + for (int x = 0; x < dbcHandler->getFileByIdx(0)->messageHandler->getCount(); x++) { - ui->cbMessages->addItem(dbcHandler->dbc_messages[x].name); + ui->cbMessages->addItem(dbcHandler->getFileByIdx(0)->messageHandler->findMsgByIdx(x)->name); } } @@ -131,13 +164,13 @@ void NewGraphDialog::loadSignals(int idx) //in the data structure so it should have been possible to just //look it up based on index but by name is probably safer and this operation //is not time critical at all. - DBC_MESSAGE *msg = dbcHandler->findMsgByName(ui->cbMessages->currentText()); + DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByName(ui->cbMessages->currentText()); if (msg == NULL) return; ui->cbSignals->clear(); - for (int x = 0; x < msg->msgSignals.count(); x++) + for (int x = 0; x < msg->sigHandler->getCount(); x++) { - ui->cbSignals->addItem(msg->msgSignals[x].name); + ui->cbSignals->addItem(msg->sigHandler->findSignalByIdx(x)->name); } } @@ -145,23 +178,24 @@ void NewGraphDialog::fillFormFromSignal(int idx) { Q_UNUSED(idx); GraphParams params; - DBC_MESSAGE *msg = dbcHandler->findMsgByName(ui->cbMessages->currentText()); + DBC_MESSAGE *msg = dbcHandler->getFileByIdx(0)->messageHandler->findMsgByName(ui->cbMessages->currentText()); if (msg == NULL) return; - DBC_SIGNAL *sig = dbcHandler->findSignalByName(msg, ui->cbSignals->currentText()); + DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(ui->cbSignals->currentText()); if (sig == NULL) return; params.graphName = sig->name; params.ID = msg->ID; - params.bias = sig->bias; - params.scale = sig->factor; - params.stride = 1; - params.mask = (1 << (sig->signalSize)) - 1; - if (sig->valType == SIGNED_INT) params.isSigned = true; - else params.isSigned = false; + //params.bias = sig->bias; + //params.scale = sig->factor; + //params.stride = 1; + //params.mask = (1 << (sig->signalSize)) - 1; + //if (sig->valType == SIGNED_INT) params.isSigned = true; + //else params.isSigned = false; params.color = ui->colorSwatch->palette().color(QPalette::Button); + /* if (sig->intelByteOrder) { //for this ordering the byte order is reserved and starting byte @@ -175,6 +209,38 @@ void NewGraphDialog::fillFormFromSignal(int idx) params.startByte = sig->startBit / 8; params.endByte = (sig->startBit + sig->signalSize - 1) / 8; } - + */ setParams(params); } + +void NewGraphDialog::setSignalActive(bool state) +{ + if (!state) return; + ui->rbSignalGraph->setChecked(true); + ui->rbStandardGraph->setChecked(false); + ui->cbMessages->setEnabled(true); + ui->cbSignals->setEnabled(true); + ui->cbSigned->setEnabled(false); + ui->txtBias->setEnabled(false); + ui->txtData->setEnabled(false); + ui->txtID->setEnabled(false); + ui->txtMask->setEnabled(false); + ui->txtScale->setEnabled(false); + ui->txtStride->setEnabled(false); +} + +void NewGraphDialog::setStandardActive(bool state) +{ + if (!state) return; + ui->rbStandardGraph->setChecked(true); + ui->rbSignalGraph->setChecked(false); + ui->cbMessages->setEnabled(false); + ui->cbSignals->setEnabled(false); + ui->cbSigned->setEnabled(true); + ui->txtBias->setEnabled(true); + ui->txtData->setEnabled(true); + ui->txtID->setEnabled(true); + ui->txtMask->setEnabled(true); + ui->txtScale->setEnabled(true); + ui->txtStride->setEnabled(true); +} diff --git a/newgraphdialog.h b/newgraphdialog.h index 9c9686f..67fe5f0 100644 --- a/newgraphdialog.h +++ b/newgraphdialog.h @@ -26,6 +26,8 @@ private slots: void loadMessages(); void loadSignals(int idx); void fillFormFromSignal(int idx); + void setSignalActive(bool); + void setStandardActive(bool); private: Ui::NewGraphDialog *ui; diff --git a/newgraphdialog.ui b/newgraphdialog.ui index 48b32b0..dcb7f12 100644 --- a/newgraphdialog.ui +++ b/newgraphdialog.ui @@ -7,13 +7,37 @@ 0 0 492 - 309 + 340 Graph Settings + + + + + + Standard Graph + + + true + + + + + + + DBC Signal Graph + + + false + + + + + diff --git a/rangestatewindow.cpp b/rangestatewindow.cpp new file mode 100644 index 0000000..4171fc1 --- /dev/null +++ b/rangestatewindow.cpp @@ -0,0 +1,430 @@ +#include "rangestatewindow.h" +#include "ui_rangestatewindow.h" +#include "mainwindow.h" +#include "utility.h" + +RangeStateWindow::RangeStateWindow(const QVector *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::RangeStateWindow) +{ + ui->setupUi(this); + + modelFrames = frames; + + ui->graphSignal->xAxis->setRange(0, 8); + ui->graphSignal->yAxis->setRange(-10, 265); //run range a bit outside possible number so they aren't plotted in a hard to see place + ui->graphSignal->xAxis->setVisible(false); + ui->graphSignal->yAxis->setVisible(false); + + //ui->graphSignal->axisRect()->setupFullAxesBox(); + + ui->cbSignalMode->addItem(tr("Big Endian")); + ui->cbSignalMode->addItem(tr("Little Endian")); + ui->cbSignalMode->addItem(tr("Try Both")); + + ui->cbSignedMode->addItem(tr("Signed Value")); + ui->cbSignedMode->addItem(tr("Unsigned Value")); + ui->cbSignedMode->addItem(tr("Try Both")); + +//lambda expressions used here because these are tiny functions that don't have any reason to be full named functions +//unfortunately the fact that valueChanged is overloaded makes the syntax here HORRIBLE. That sucks. + connect(ui->spinMaxSigSize, static_cast(&QSpinBox::valueChanged), + [=](int newVal) + { + if (newVal < ui->spinMinSigSize->value()) ui->spinMinSigSize->setValue(newVal); + }); + + connect(ui->spinMinSigSize, static_cast(&QSpinBox::valueChanged), + [=](int newVal) + { + if (newVal > ui->spinMaxSigSize->value()) ui->spinMaxSigSize->setValue(newVal); + }); + + connect(ui->btnAllFilter, &QAbstractButton::clicked, + [=]() + { + for (int i = 0; i < ui->listFilter->count(); i++) + { + QListWidgetItem *item = ui->listFilter->item(i); + item->setCheckState(Qt::Checked); + idFilters[Utility::ParseStringToNum(item->text())] = true; + } + }); + + connect(ui->btnNoneFilter, &QAbstractButton::clicked, + [=]() + { + for (int i = 0; i < ui->listFilter->count(); i++) + { + QListWidgetItem *item = ui->listFilter->item(i); + item->setCheckState(Qt::Unchecked); + idFilters[Utility::ParseStringToNum(item->text())] = false; + } + }); + + connect(ui->listFilter, &QListWidget::itemChanged, + [=](QListWidgetItem *item) + { + bool isChecked = false; + int id = Utility::ParseStringToNum(item->text()); + if (item->checkState() == Qt::Checked) isChecked = true; + idFilters[id] = isChecked; + }); + + connect(ui->btnRecalc, &QAbstractButton::clicked, this, &RangeStateWindow::recalcButton); + connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int))); + connect(ui->listCandidates, &QListWidget::currentRowChanged, this, &RangeStateWindow::clickedSignalList); +} + +RangeStateWindow::~RangeStateWindow() +{ + delete ui; +} + +void RangeStateWindow::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + + readSettings(); + + refreshFilterList(); +} + +void RangeStateWindow::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event); + writeSettings(); +} + +void RangeStateWindow::readSettings() +{ + QSettings settings; + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + resize(settings.value("RangeStateView/WindowSize", QSize(765, 615)).toSize()); + move(settings.value("RangeStateView/WindowPos", QPoint(50, 50)).toPoint()); + } +} + +void RangeStateWindow::writeSettings() +{ + QSettings settings; + + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + settings.setValue("RangeStateView/WindowSize", size()); + settings.setValue("RangeStateView/WindowPos", pos()); + } +} + +void RangeStateWindow::updatedFrames(int numFrames) +{ + CANFrame thisFrame; + if (numFrames == -1) //all frames deleted. We don't need to do a thing on this window but erase everything in the filters section + { + ui->listFilter->clear(); + idFilters.clear(); + } + else if (numFrames == -2) //all new set of frames. Reset + { + refreshFilterList(); + } + else //just got some new frames. See if we need to update the filters list. Otherwise nothing to do - no recalc happens until the button is pressed + { + if (numFrames > modelFrames->count()) return; + for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + { + thisFrame = modelFrames->at(i); + if (!idFilters.contains(thisFrame.ID)) + { + idFilters.insert(thisFrame.ID, true); + QListWidgetItem* listItem = new QListWidgetItem(Utility::formatNumber(thisFrame.ID), ui->listFilter); + listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable); // set checkable flag + listItem->setCheckState(Qt::Checked); //default all filters to be set active + } + } + } +} + +void RangeStateWindow::refreshFilterList() +{ + int id; + + idFilters.clear(); + ui->listFilter->clear(); + + for (int i = 0; i < modelFrames->length(); i++) + { + id = modelFrames->at(i).ID; + if (!idFilters.contains(id)) + { + idFilters.insert(id, true); + QListWidgetItem* listItem = new QListWidgetItem(Utility::formatNumber(id), ui->listFilter); + listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable); // set checkable flag + listItem->setCheckState(Qt::Checked); //default all filters to be set active + } + } + + ui->listFilter->sortItems(); +} + +void RangeStateWindow::recalcButton() +{ + QHash::iterator iter; + int id; + + ui->listCandidates->clear(); + foundSignals.clear(); + + for (iter = idFilters.begin(); iter != idFilters.end(); ++iter) + { + if (iter.value() == true) + { + qDebug() << "Processing for ID: " << iter.key(); + //so, we're supposed to process this frame ID. We'll need to create a frame cache for it + frameCache.clear(); + frameCache.reserve(modelFrames->count()); //block allocate more than enough space + id = iter.key(); + for (int j = 0; j < modelFrames->count(); j++) + { + if (modelFrames->at(j).ID == id) frameCache.append(modelFrames->at(j)); + } + //now we've got a list with all the same ID. Time to send it off for processing + signalsFactory(); + } + } + + qDebug() << "Found " << foundSignals.count() << " signals total."; +} + +/* + * Uses the settings exposed to the user to generate a set of candidate signals that should be checked. + * The user could specify signal sizes, granularity, endian type and we generate all the permutations from there + * Should process from max to min and stop when a valid signal is found (at least as an option) to declutter a bit. + * Mostly what we're interested in is the largest signal that matches +*/ +void RangeStateWindow::signalsFactory() +{ + int minSig = ui->spinMinSigSize->value(); + int maxSig = ui->spinMaxSigSize->value(); + int granularity = ui->spinGranularity->value(); + int sigType = ui->cbSignalMode->currentIndex() + 1; + int signedType = ui->cbSignedMode->currentIndex() + 1; + int maxBits = frameCache.at(0).len * 8; + int sens = ui->slideSensitivity->value(); + + for (int sigSize = maxSig; sigSize >= minSig; sigSize--) + { + for (int startBit = 0; startBit < maxBits; startBit += granularity) + { + if (sigType & 1) + { + if (signedType & 1) processSignal(startBit, sigSize, sens, true, true); + if (signedType & 2) processSignal(startBit, sigSize, sens, true, false); + } + if (sigType & 2) + { + if (signedType & 1) processSignal(startBit, sigSize, sens, false, true); + if (signedType & 2) processSignal(startBit, sigSize, sens, false, false); + } + //have to try both types even with 8 bit and smaller signals + //because they could cross byte boundaries. Could check whether they + //do and not try both types if it is impossible. + } + } +} + +/* + * Given the signal we generate the relevant data and figure out whether this signal seems to be a smooth range signal +*/ +bool RangeStateWindow::processSignal(int startBit, int bitLength, int sensitivity, bool bigEndian, bool isSigned) +{ + qDebug() << ""; + qDebug() << "S:" << startBit << " B:" << bitLength << " Sens:" << sensitivity << " Big E:" << bigEndian << " Signed: " << isSigned; + + QVector scaledVals; + QVector diff1; + QVector diff2; + int64_t valu; + int64_t highestValue = -1000000000000LL; + int64_t lowestValue = 1000000000000LL; + double multiplier; + int numFrames = frameCache.count(); + + scaledVals.reserve(frameCache.count()); + diff1.reserve(frameCache.count() - 1); + diff2.reserve(frameCache.count() - 2); + + int i; + + for (i = 0; i < numFrames; i++) + { + valu = Utility::processIntegerSignal(frameCache.at(i).data, startBit, bitLength, !bigEndian, isSigned); + if (valu < lowestValue) lowestValue = valu; + if (valu > highestValue) highestValue = valu; + } + + qDebug() << "Min: " << lowestValue << " Max: " << highestValue; + + if (lowestValue == highestValue) return false; //a signal that never changes is worthless and not a range signal + + //multiplier is calculated such that the range of the signal is scaled down to the value of sensitivity. + //so, if we have a sensitivity of 50 then the range is scaled so that it is 50 + //this has the effect of smoothing the data a bit by completely removing the fine detail. It's irrelevant to + //an algorithm like this anyway and just gets in the way. + //Also, we're going to offset by lowest value so that everything is based at a value of 0 as the low end. + //All of this drastically modifies the actual signal but that's OK, we're looking for patterns here not + //being faithful to the signal (what a dirty cheater) + int range = highestValue - lowestValue; + multiplier = (double)sensitivity / (double)range; + + for (i = 0; i < numFrames; i++) scaledVals.append((int)((Utility::processIntegerSignal(frameCache.at(i).data, startBit, bitLength, !bigEndian, isSigned) - lowestValue) * multiplier)); + + for (i = 1; i < numFrames; i++) + { + valu = scaledVals[i-1] - scaledVals[i]; + diff1.append(valu); + } + + for (i = 1; i < diff1.count(); i++) + { + valu = diff1[i-1] - diff1[i]; + diff2.append(valu); + } + + //now the differences are all stored so let's go through and see if they seem to suggest a ramping sort of signal or not. + //for a first test lets let through any signal where the acceleration values are all much smaller than the signal range + bool isGood = true; + int comparisonValue = Utility::Lerp(sensitivity / 5, sensitivity / 40, (sensitivity - 10) / 240.0); + qDebug() << "range: " << sensitivity << " comparisonvalue: " << comparisonValue; + int overValues = 0; + for (i = 0; i < diff2.count(); i++) + { + if (abs(diff1[i]) > comparisonValue) overValues++; + } + int maxOvers = Utility::Lerp(4, numFrames / 50.0, 1.0 - ((sensitivity -10) / 240.0)); + qDebug() << "Max Overs: " << maxOvers; + if (overValues > maxOvers) isGood = false; + qDebug() << "Is this signal good: " << isGood << " Num overs: " << overValues; + if (isGood) + { + //createGraph(scaledVals); + QString temp; + temp = "ID: " + QString::number(frameCache.at(0).ID, 16) + " startBit: " + QString::number(startBit) + " len: " + QString::number(bitLength); + int64_t foundSig; + foundSig = frameCache.at(0).ID; + foundSig += (int64_t)startBit << 32; + foundSig += (int64_t)bitLength << 40; + + if (isSigned) + { + temp += " Signed"; + foundSig += (int64_t)1 << 48; + } + else + { + temp += " Unsigned"; + } + + if (bigEndian) + { + temp += " BigEndian"; + foundSig += (int64_t)1 << 49; + } + else + { + temp += " LittleEndian"; + } + + ui->listCandidates->addItem(temp); + foundSignals.append(foundSig); + } + return isGood; +} + +//graphs the vector such that the X axis is just the index into the vector and Y is perfectly graphed within the window +void RangeStateWindow::createGraph(QVector values) +{ + + int tempVal; + float minval=1000000, maxval = -100000; + + int numEntries = values.count(); + + ui->graphSignal->clearGraphs(); + + QVector x(numEntries), y(numEntries); + + for (int j = 0; j < numEntries; j++) + { + tempVal = values[j]; + + x[j] = j; + + y[j] = tempVal; + if (y[j] < minval) minval = y[j]; + if (y[j] > maxval) maxval = y[j]; + } + + qDebug() << "YMin: " << minval << " YMax: " << maxval; + + double ymin, ymax; + + ymin = minval; + + if (ymin < 0) ymin *= 1.2; + else ymin *= 0.8; + + ymax = maxval; + if (ymax < 0) ymax * 0.8; + else ymax *= 1.2; + + if (fabs(ymin) < 0.01) ymin -= (ymax / 60.0); + if (fabs(ymax) < 0.01) ymax -= (ymin / 60.0); + + qDebug() << "YFm: " << ymin << " YFM: " << ymax; + + ui->graphSignal->addGraph(); + ui->graphSignal->graph()->setName("Graph"); + ui->graphSignal->graph()->setData(x,y); + ui->graphSignal->graph()->setLineStyle(QCPGraph::lsLine); //connect points with lines + QPen graphPen; + graphPen.setColor(Qt::black); + graphPen.setWidth(1); + ui->graphSignal->graph()->setPen(graphPen); + ui->graphSignal->xAxis->setRange(0, numEntries); + ui->graphSignal->yAxis->setRange(ymin, ymax); + ui->graphSignal->replot(); +} + +void RangeStateWindow::clickedSignalList(int idx) +{ + if (idx == -1) return; //just in case... + + int id, startBit, bitLength; + bool isSigned = false, isBigEndian = false; + int64_t valu; + + valu = foundSignals.at(idx); + id = valu & 0xFFFFFFFFULL; + startBit = (valu >> 32) & 0xFF; + bitLength = (valu >> 40) & 0xFF; + if (valu & (1LL << 48)) isSigned = true; + if (valu & (1LL << 49)) isBigEndian = true; + + qDebug() << "I:" << id << " sb:" << startBit << " len:" << bitLength << " signed:" << isSigned << " big:" << isBigEndian; + + frameCache.clear(); + frameCache.reserve(modelFrames->count()); //block allocate more than enough space + + for (int j = 0; j < modelFrames->count(); j++) + { + if (modelFrames->at(j).ID == id) frameCache.append(modelFrames->at(j)); + } + + int numFrames = frameCache.count(); + QVector values; + values.reserve(numFrames); + for (int i = 0; i < numFrames; i++) values.append((int)((Utility::processIntegerSignal(frameCache.at(i).data, startBit, bitLength, !isBigEndian, isSigned)))); + createGraph(values); +} diff --git a/rangestatewindow.h b/rangestatewindow.h new file mode 100644 index 0000000..4f59457 --- /dev/null +++ b/rangestatewindow.h @@ -0,0 +1,41 @@ +#ifndef RANGESTATEWINDOW_H +#define RANGESTATEWINDOW_H + +#include +#include "can_structs.h" + +namespace Ui { +class RangeStateWindow; +} + +class RangeStateWindow : public QDialog +{ + Q_OBJECT + +public: + explicit RangeStateWindow(const QVector *frames, QWidget *parent = 0); + ~RangeStateWindow(); + void showEvent(QShowEvent*); + +private slots: + void updatedFrames(int); + void recalcButton(); + void clickedSignalList(int idx); + +private: + Ui::RangeStateWindow *ui; + const QVector *modelFrames; + QVector frameCache; + QList foundSignals; + QHash idFilters; + + void refreshFilterList(); + void closeEvent(QCloseEvent *event); + void readSettings(); + void writeSettings(); + void signalsFactory(); + bool processSignal(int startBit, int bitLength, int sensitivity, bool bigEndian, bool isSigned); + void createGraph(QVector values); +}; + +#endif // RANGESTATEWINDOW_H diff --git a/rangestatewindow.ui b/rangestatewindow.ui new file mode 100644 index 0000000..f9a7507 --- /dev/null +++ b/rangestatewindow.ui @@ -0,0 +1,207 @@ + + + RangeStateWindow + + + + 0 + 0 + 763 + 938 + + + + Range State Window + + + + + + + + Candidate Signals: + + + + + + + + + + Graphical Representation of Selected Signal: + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Sensitivity + + + + + + + 10 + + + 250 + + + Qt::Horizontal + + + + + + + Min Signal Size: + + + + + + + 1 + + + 32 + + + + + + + Max Signal Size: + + + + + + + 1 + + + 32 + + + + + + + Granularity: + + + + + + + 1 + + + 32 + + + + + + + Signal Mode: + + + + + + + + + + Signed Mode: + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + ID Filter: + + + + + + + + + + All + + + + + + + None + + + + + + + + + + + Recalculate Candidate Signals + + + + + + + + QCustomPlot + QWidget +
qcustomplot.h
+ 1 +
+
+ + +
diff --git a/scriptcontainer.cpp b/scriptcontainer.cpp new file mode 100644 index 0000000..236b979 --- /dev/null +++ b/scriptcontainer.cpp @@ -0,0 +1,148 @@ +#include "scriptcontainer.h" + +#include +#include + +ScriptContainer::ScriptContainer() +{ + fileName = QString(); + filePath = QString(); + scriptText = QString(); + + connect(&timer, SIGNAL(timeout()), this, SLOT(tick())); +} + +void ScriptContainer::compileScript() +{ + QJSValue result = scriptEngine.evaluate(scriptText, fileName); + + errorWidget->clear(); + + if (result.isError() && errorWidget) + { + errorWidget->addItem("SCRIPT EXCEPTION!"); + errorWidget->addItem("Line: " + result.property("lineNumber").toString()); + errorWidget->addItem(result.property("message").toString()); + errorWidget->addItem("Stack:"); + errorWidget->addItem(result.property("stack").toString()); + } + else + { + compiledScript = result; + + QJSValue hostObj = scriptEngine.newQObject(this); + scriptEngine.globalObject().setProperty("host", hostObj); + + setupFunction = scriptEngine.globalObject().property("setup"); + gotFrameFunction = scriptEngine.globalObject().property("gotFrame"); + tickFunction = scriptEngine.globalObject().property("tick"); + + if (setupFunction.isCallable()) + { + qDebug() << "setup exists"; + QJSValue res = setupFunction.call(); + if (res.isError()) + { + errorWidget->addItem("Error in setup function on line " + res.property("lineNumber").toString()); + errorWidget->addItem(res.property("message").toString()); + } + } + + if (gotFrameFunction.isCallable()) qDebug() << "gotFrame exists"; + if (tickFunction.isCallable()) qDebug() << "tick exists"; + } +} + +void ScriptContainer::setErrorWidget(QListWidget *list) +{ + errorWidget = list; +} + +void ScriptContainer::setFilter(QJSValue id, QJSValue mask, QJSValue bus) +{ + uint32_t idVal = id.toUInt(); + uint32_t maskVal = mask.toUInt(); + int busVal = bus.toInt(); + qDebug() << "Called set filter"; + qDebug() << idVal << "*" << maskVal << "*" << busVal; + CANFilter filter; + filter.setFilter(idVal, maskVal, busVal); + filters.append(filter); +} + +void ScriptContainer::setTickInterval(QJSValue interval) +{ + int intervalValue = interval.toInt(); + qDebug() << "called set tick interval with value " << intervalValue; + if (intervalValue > 0) + { + timer.setInterval(intervalValue); + timer.start(); + } + else timer.stop(); +} + +void ScriptContainer::clearFilters() +{ + qDebug() << "Called clear filters"; + filters.clear(); +} + +void ScriptContainer::sendFrame(QJSValue bus, QJSValue id, QJSValue length, QJSValue data) +{ + CANFrame frame; + frame.extended = false; + frame.ID = id.toInt(); + frame.len = length.toInt(); + if (frame.len < 0) frame.len = 0; + if (frame.len > 8) frame.len = 8; + + if (!data.isArray()) qDebug() << "data isn't an array"; + + for (int i = 0; i < frame.len; i++) + { + frame.data[i] = (uint8_t)data.property(i).toInt(); + } + + frame.bus = bus.toInt(); + if (frame.bus < 0) frame.bus = 0; + if (frame.bus > 1) frame.bus = 1; + + if (frame.ID > 0x7FF) frame.extended = true; + + //qDebug() << "Sending frame from script"; + emit sendCANFrame(&frame, frame.bus); +} + +void ScriptContainer::gotFrame(const CANFrame &frame) +{ + if (!gotFrameFunction.isCallable()) return; //nothing to do if we can't even call the function + //qDebug() << "Got frame in script interface"; + for (int i = 0; i < filters.length(); i++) + { + if (filters[i].checkFilter(frame.ID, frame.bus)) + { + QJSValueList args; + args << frame.bus << frame.ID << frame.len; + QJSValue dataBytes = scriptEngine.newArray(frame.len); + for (int j = 0; j < frame.len; j++) dataBytes.setProperty(j, QJSValue(frame.data[j])); + args.append(dataBytes); + gotFrameFunction.call(args); + return; //as soon as one filter matches we jump out + } + } +} + +void ScriptContainer::tick() +{ + if (tickFunction.isCallable()) + { + //qDebug() << "Calling tick function"; + QJSValue res = tickFunction.call(); + if (res.isError()) + { + errorWidget->addItem("Error in tick function on line " + res.property("lineNumber").toString()); + errorWidget->addItem(res.property("message").toString()); + } + } +} diff --git a/scriptcontainer.h b/scriptcontainer.h new file mode 100644 index 0000000..b570f12 --- /dev/null +++ b/scriptcontainer.h @@ -0,0 +1,48 @@ +#ifndef SCRIPTCONTAINER_H +#define SCRIPTCONTAINER_H + +#include "can_structs.h" +#include "canfilter.h" + +#include +#include +#include + +class ScriptContainer : public QObject +{ + Q_OBJECT + +public: + ScriptContainer(); + void gotFrame(const CANFrame &frame); + + QString fileName; + QString filePath; + QString scriptText; + +public slots: + void compileScript(); + void setErrorWidget(QListWidget *list); + void setFilter(QJSValue id, QJSValue mask, QJSValue bus); + void setTickInterval(QJSValue interval); + void clearFilters(); + void sendFrame(QJSValue bus, QJSValue id, QJSValue length, QJSValue data); + +private slots: + void tick(); + +signals: + void sendCANFrame(const CANFrame *, int); + +private: + QJSEngine scriptEngine; + QJSValue compiledScript; + QJSValue setupFunction; + QJSValue gotFrameFunction; + QJSValue tickFunction; + QTimer timer; + QListWidget *errorWidget; + QList filters; +}; + +#endif // SCRIPTCONTAINER_H diff --git a/scriptingwindow.cpp b/scriptingwindow.cpp new file mode 100644 index 0000000..19ab11d --- /dev/null +++ b/scriptingwindow.cpp @@ -0,0 +1,204 @@ +#include "scriptingwindow.h" +#include "ui_scriptingwindow.h" + +#include +#include +#include + +#include "mainwindow.h" + +ScriptingWindow::ScriptingWindow(const QVector *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::ScriptingWindow) +{ + ui->setupUi(this); + + readSettings(); + + modelFrames = frames; + + connect(ui->btnLoadScript, &QAbstractButton::pressed, this, &ScriptingWindow::loadNewScript); + connect(ui->btnNewScript, &QAbstractButton::pressed, this, &ScriptingWindow::createNewScript); + connect(ui->btnRecompile, &QAbstractButton::pressed, this, &ScriptingWindow::recompileScript); + connect(ui->btnRemoveScript, &QAbstractButton::pressed, this, &ScriptingWindow::deleteCurrentScript); + connect(ui->btnRevertScript, &QAbstractButton::pressed, this, &ScriptingWindow::revertScript); + connect(ui->btnSaveScript, &QAbstractButton::pressed, this, &ScriptingWindow::saveScript); + connect(MainWindow::getReference(), &MainWindow::framesUpdated, this, &ScriptingWindow::updatedFrames); + + ui->txtScriptSource->setLexer(new QsciLexerJavaScript(ui->txtScriptSource)); +} + +ScriptingWindow::~ScriptingWindow() +{ + delete ui; +} + +void ScriptingWindow::updatedFrames(int numFrames) +{ + CANFrame thisFrame; + //-1 means all frames deleted and -2 means a full refresh, neither of which we care about here. + if (numFrames > 0) + { + if (numFrames > modelFrames->count()) return; + qDebug() << "Got frames into script window: " << numFrames; + //for every new frame pass it on to each script container. The container will determine if it needs to actually + //notify the script and do that if applicable. + for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + { + thisFrame = modelFrames->at(i); + for (int j = 0; j < scripts.length(); j++) + { + scripts[j]->gotFrame(thisFrame); + } + } + } +} + +void ScriptingWindow::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event); + writeSettings(); +} + +void ScriptingWindow::readSettings() +{ + QSettings settings; + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + resize(settings.value("ScriptingWindow/WindowSize", QSize(860, 650)).toSize()); + move(settings.value("ScriptingWindow/WindowPos", QPoint(100, 100)).toPoint()); + } +} + +void ScriptingWindow::writeSettings() +{ + QSettings settings; + + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + settings.setValue("ScriptingWindow/WindowSize", size()); + settings.setValue("ScriptingWindow/WindowPos", pos()); + } +} + +void ScriptingWindow::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); +} + +void ScriptingWindow::loadNewScript() +{ + QString filename; + QFileDialog dialog; + bool result = false; + ScriptContainer *container; + + QStringList filters; + filters.append(QString(tr("Javascript File (*.js)"))); + + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setNameFilters(filters); + dialog.setViewMode(QFileDialog::Detail); + + if (dialog.exec() == QDialog::Accepted) + { + filename = dialog.selectedFiles()[0]; + + if (dialog.selectedNameFilter() == filters[0]) + { + QFile scriptFile(filename); + + if (scriptFile.open(QIODevice::ReadOnly)) + { + QString contents = scriptFile.readAll(); + scriptFile.close(); + QStringList fileList = filename.split('/'); + QString justFileName = fileList[fileList.length() - 1]; + ui->listLoadedScripts->addItem(justFileName); + container = new ScriptContainer(); + container->fileName = justFileName; + container->filePath = filename; + container->scriptText = contents; + container->setErrorWidget(ui->listErrors); + container->compileScript(); + connect(container, &ScriptContainer::sendCANFrame, this, &ScriptingWindow::sendCANFrame); + scripts.append(container); + currentScript = container; + ui->txtScriptSource->setText(container->scriptText); + ui->txtScriptSource->setEnabled(true); + } + } + } +} + +void ScriptingWindow::createNewScript() +{ + ScriptContainer *container; + + container = new ScriptContainer(); + + container->fileName = "UNNAMED_" + QString::number((qrand() % 10000)) + ".js"; + container->filePath = QString(); + container->scriptText = QString(); + container->setErrorWidget(ui->listErrors); + connect(container, &ScriptContainer::sendCANFrame, this, &ScriptingWindow::sendCANFrame); + scripts.append(container); + ui->listLoadedScripts->addItem(container->fileName); + currentScript = container; + ui->txtScriptSource->setText(container->scriptText); + ui->txtScriptSource->setEnabled(true); +} + +void ScriptingWindow::deleteCurrentScript() +{ + +} + +void ScriptingWindow::refreshSourceWindow() +{ + ui->txtScriptSource->setText(currentScript->scriptText); +} + +void ScriptingWindow::saveScript() +{ + QString filename; + QFileDialog dialog(this); + + QStringList filters; + filters.append(QString(tr("Javascript File (*.js)"))); + + 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 += ".js"; + if (dialog.selectedNameFilter() == filters[0]) + { + QFile *outFile = new QFile(filename); + + if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) + { + delete outFile; + return; + } + outFile->write(ui->txtScriptSource->text().toUtf8()); + outFile->close(); + delete outFile; + } + } +} + +void ScriptingWindow::revertScript() +{ + +} + +void ScriptingWindow::recompileScript() +{ + currentScript->scriptText = ui->txtScriptSource->text(); + currentScript->compileScript(); +} diff --git a/scriptingwindow.h b/scriptingwindow.h new file mode 100644 index 0000000..d27620b --- /dev/null +++ b/scriptingwindow.h @@ -0,0 +1,51 @@ +#ifndef SCRIPTINGWINDOW_H +#define SCRIPTINGWINDOW_H + +#include "can_structs.h" +#include "scriptcontainer.h" + +#include +#include + +namespace Ui { +class ScriptingWindow; +} + +class ScriptingWindow : public QDialog +{ + Q_OBJECT + +public: + explicit ScriptingWindow(const QVector *frames, QWidget *parent = 0); + void showEvent(QShowEvent*); + ~ScriptingWindow(); + +private slots: + void loadNewScript(); + void createNewScript(); + void deleteCurrentScript(); + void refreshSourceWindow(); + void saveScript(); + void revertScript(); + void recompileScript(); + void updatedFrames(int); + +public slots: + +signals: + void sendCANFrame(const CANFrame *, int); + +private: + void closeEvent(QCloseEvent *event); + void readSettings(); + void writeSettings(); + + Ui::ScriptingWindow *ui; + QList scripts; + ScriptContainer *currentScript; + const QVector *modelFrames; + + +}; + +#endif // SCRIPTINGWINDOW_H diff --git a/scriptingwindow.ui b/scriptingwindow.ui new file mode 100644 index 0000000..5137ac6 --- /dev/null +++ b/scriptingwindow.ui @@ -0,0 +1,128 @@ + + + ScriptingWindow + + + + 0 + 0 + 862 + 651 + + + + Scripting Interface + + + + + + + + Loaded Scripts + + + Qt::AlignCenter + + + + + + + + + + Load New Script + + + + + + + Create New Script + + + + + + + Remove Current Script + + + + + + + + + + + Script Editing Window + + + Qt::AlignCenter + + + + + + + + + + + + + + + + + + + Save + + + + + + + Revert + + + + + + + Recompile + + + + + + + + + Current Scripting Errors + + + Qt::AlignCenter + + + + + + + + + + + + + QsciScintilla + QFrame +
Qsci/qsciscintilla.h
+
+
+ + +
diff --git a/serialworker.cpp b/serialworker.cpp index 096675f..d70fc6c 100644 --- a/serialworker.cpp +++ b/serialworker.cpp @@ -40,6 +40,19 @@ SerialWorker::~SerialWorker() if (ticker != NULL) ticker->stop(); } +void SerialWorker::run() +{ + ticker = new QTimer; + connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick())); + + elapsedTime = new QTime; + elapsedTime->start(); + + ticker->setInterval(250); //tick four times per second + ticker->setSingleShot(false); //keep ticking + ticker->start(); +} + void SerialWorker::readSettings() { QSettings settings; @@ -69,7 +82,7 @@ void SerialWorker::setSerialPort(QSerialPortInfo *port) delete serial; } - serial = new QSerialPort(*port); + serial = new QSerialPort(*port); qDebug() << "Serial port name is " << port->portName(); //serial->setBaudRate(10000000); //more speed! probably does nothing for USB serial @@ -110,19 +123,6 @@ void SerialWorker::setSerialPort(QSerialPortInfo *port) else connected = true; connect(serial, SIGNAL(readyRead()), this, SLOT(readSerialData())); if (doValidation) QTimer::singleShot(1000, this, SLOT(connectionTimeout())); - if (ticker == NULL) - { - ticker = new QTimer; - connect(ticker, SIGNAL(timeout()), this, SLOT(handleTick())); - } - if (elapsedTime == NULL) - { - elapsedTime = new QTime; - elapsedTime->start(); - } - ticker->setInterval(250); //tick four times per second - ticker->setSingleShot(false); //keep ticking - ticker->start(); } void SerialWorker::connectionTimeout() @@ -132,7 +132,7 @@ void SerialWorker::connectionTimeout() { //then emit the the failure signal and see if anyone cares qDebug() << "Failed to connect to GVRET at that com port"; - ticker->stop(); + //ticker->stop(); closeSerialPort(); //make sure it's properly closed anyway emit connectionFailure(); } @@ -155,9 +155,17 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0) QByteArray buffer; int c; int ID; + CANFrame tempFrame = *frame; + tempFrame.isReceived = false; //qDebug() << "Sending out frame with id " << frame->ID; + //show our sent frames in the list too. This happens even if we're not connected. + canModel->addFrame(tempFrame, false); + gotFrames++; + + if (serial == NULL) return; + if (!serial->isOpen()) return; if (!connected) return; ID = frame->ID; @@ -177,8 +185,6 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0) } buffer[8 + frame->len] = 0; - if (serial == NULL) return; - if (!serial->isOpen()) return; //qDebug() << "writing " << buffer.length() << " bytes to serial port"; serial->write(buffer); } @@ -188,7 +194,9 @@ void SerialWorker::sendFrame(const CANFrame *frame, int bus = 0) //buffers and besides, the other end will get buried in traffic. void SerialWorker::sendFrameBatch(const QList *frames) { + sendBulkMutex.lock(); for (int i = 0; i < frames->length(); i++) sendFrame(&frames->at(i), frames->at(i).bus); + sendBulkMutex.unlock(); } void SerialWorker::updateBaudRates(int Speed1, int Speed2) @@ -306,6 +314,7 @@ void SerialWorker::procRXChar(unsigned char c) //qDebug() << "emit from serial handler to main form id: " << buildFrame->ID; if (capturing) { + buildFrame->isReceived = true; canModel->addFrame(*buildFrame, false); gotFrames++; if (buildFrame->ID == targetID) emit gotTargettedFrame(canModel->rowCount() - 1); @@ -422,14 +431,19 @@ void SerialWorker::handleTick() { //qDebug() << "Tick!"; - if (!gotValidated) + if (connected) { - if (serial->isOpen()) //if it's still false we have a problem... + if (!gotValidated && doValidation) { - qDebug() << "Comm validation failed. "; - closeSerialPort(); //start by stopping everything. - QTimer::singleShot(500, this, SLOT(handleReconnect())); - return; + if (serial == NULL) return; + if (serial->isOpen()) //if it's still false we have a problem... + { + qDebug() << "Comm validation failed. "; + closeSerialPort(); //start by stopping everything. + //Then wait 500ms and restart the connection automatically + QTimer::singleShot(500, this, SLOT(handleReconnect())); + return; + } } } @@ -438,7 +452,7 @@ void SerialWorker::handleTick() emit frameUpdateTick(framesPerSec / 4, gotFrames); //sends stats to interested parties canModel->sendBulkRefresh(gotFrames); gotFrames = 0; - if (doValidation) sendCommValidation(); + if (doValidation && serial && serial->isOpen()) sendCommValidation(); } void SerialWorker::handleReconnect() @@ -454,6 +468,10 @@ void SerialWorker::sendCommValidation() gotValidated = false; output.append((char)0xF1); //another command to the GVRET output.append((char)0x09); //request a reply to get validation + //send it twice for good measure. + output.append((char)0xF1); //another command to the GVRET + output.append((char)0x09); //request a reply to get validation + serial->write(output); } @@ -467,7 +485,8 @@ void SerialWorker::closeSerialPort() serial->close(); } serial->disconnect(); - ticker->stop(); + //do not stop the ticker here. It always stays running now. + //ticker->stop(); delete serial; serial = NULL; } diff --git a/serialworker.h b/serialworker.h index e945e0b..2b71266 100644 --- a/serialworker.h +++ b/serialworker.h @@ -54,6 +54,7 @@ private slots: //we receive things in slots void handleReconnect(); public slots: + void run(); void setSerialPort(QSerialPortInfo*); void closeSerialPort(); void sendFrame(const CANFrame *, int); @@ -75,6 +76,7 @@ private: CANFrameModel *canModel; QTimer *ticker; QTime *elapsedTime; + QMutex sendBulkMutex; int framesPerSec; int gotFrames; int targetID; diff --git a/udsscanwindow.cpp b/udsscanwindow.cpp new file mode 100644 index 0000000..37c9d07 --- /dev/null +++ b/udsscanwindow.cpp @@ -0,0 +1,130 @@ +#include "udsscanwindow.h" +#include "ui_udsscanwindow.h" +#include "mainwindow.h" + +UDSScanWindow::UDSScanWindow(const QVector *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::UDSScanWindow) +{ + ui->setupUi(this); + + modelFrames = frames; + + waitTimer = new QTimer; + waitTimer->setInterval(500); + + connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int))); + connect(ui->btnScan, &QPushButton::clicked, this, &UDSScanWindow::scanUDS); + connect(waitTimer, &QTimer::timeout, this, &UDSScanWindow::timeOut); + + ui->cbBuses->addItem("0"); + ui->cbBuses->addItem("1"); + ui->cbBuses->addItem("Both"); + +} + +UDSScanWindow::~UDSScanWindow() +{ + delete ui; + waitTimer->stop(); + delete waitTimer; +} + +void UDSScanWindow::scanUDS() +{ + ui->listResults->clear(); + sendingFrames.clear(); + + CANFrame frame; + int startID, endID; + startID = Utility::ParseStringToNum(ui->txtStartID->text()); + endID = Utility::ParseStringToNum(ui->txtEndID->text()); + + int buses = ui->cbBuses->currentIndex(); + buses++; + if (buses < 1) buses = 1; + for (int typ = 1; typ < 5; typ++) + { + for (int id = startID; id < endID; id++) + { + 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; + + if (buses & 1) + { + frame.bus = 0; + sendingFrames.append(frame); + } + if (buses & 2) + { + frame.bus = 1; + sendingFrames.append(frame); + } + } + } + + waitTimer->start(); + currIdx = -1; + sendNextMsg(); +} + +void UDSScanWindow::updatedFrames(int numFrames) +{ + CANFrame thisFrame; + QString result; + int id; + if (numFrames == -1) //all frames deleted. We don't care + { + } + else if (numFrames == -2) //all new set of frames. Don't care. + { + } + else //just got some new frames. See if they are relevant. + { + if (numFrames > modelFrames->count()) return; + for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + { + thisFrame = modelFrames->at(i); + id = thisFrame.ID; + if (id >= 0x7E8 && id <= 0x7EF) + { + id -= 8; //back to original ECU id + result = "ECU at bus " + QString::number(thisFrame.bus) + " ID: " + QString::number(id, 16) + " responds to mode " + + QString::number(sendingFrames[currIdx].data[2]) + " with: " + QString::number(thisFrame.data[0], 16) + " " + + QString::number(thisFrame.data[1], 16) + " " + QString::number(thisFrame.data[2], 16) + + " " + QString::number(thisFrame.data[3], 16); + ui->listResults->addItem(result); + sendNextMsg(); + } + } + } +} + +void UDSScanWindow::timeOut() +{ + QString result; + result = "ECU at bus " + QString::number(sendingFrames[currIdx].bus) + " ID: " + QString::number(sendingFrames[currIdx].ID, 16) + " did not respond to mode " + + QString::number(sendingFrames[currIdx].data[2]); + ui->listResults->addItem(result); + + sendNextMsg(); +} + +void UDSScanWindow::sendNextMsg() +{ + currIdx++; + if (currIdx < sendingFrames.count()) + { + emit sendCANFrame(&sendingFrames[currIdx], sendingFrames[currIdx].bus); + } + else + { + waitTimer->stop(); + } +} diff --git a/udsscanwindow.h b/udsscanwindow.h new file mode 100644 index 0000000..39b06d5 --- /dev/null +++ b/udsscanwindow.h @@ -0,0 +1,38 @@ +#ifndef UDSSCANWINDOW_H +#define UDSSCANWINDOW_H + +#include "can_structs.h" + +#include + +namespace Ui { +class UDSScanWindow; +} + +class UDSScanWindow : public QDialog +{ + Q_OBJECT + +public: + explicit UDSScanWindow(const QVector *frames, QWidget *parent = 0); + ~UDSScanWindow(); + +signals: + void sendCANFrame(const CANFrame *, int); + +private slots: + void updatedFrames(int numFrames); + void scanUDS(); + void timeOut(); + +private: + Ui::UDSScanWindow *ui; + const QVector *modelFrames; + QTimer *waitTimer; + QList sendingFrames; + int currIdx = 0; + + void sendNextMsg(); +}; + +#endif // UDSSCANWINDOW_H diff --git a/udsscanwindow.ui b/udsscanwindow.ui new file mode 100644 index 0000000..14aff43 --- /dev/null +++ b/udsscanwindow.ui @@ -0,0 +1,88 @@ + + + UDSScanWindow + + + + 0 + 0 + 742 + 527 + + + + Dialog + + + + + + Results + + + + + + + + + + + + + + Starting ID + + + + + + + 0x7E0 + + + + + + + + + + + Ending ID + + + + + + + 0x7E7 + + + + + + + + + + + Buses to scan + + + + + + + + + + Start Scan + + + + + + + + diff --git a/utility.h b/utility.h index 9e86fa7..0479172 100644 --- a/utility.h +++ b/utility.h @@ -73,6 +73,17 @@ public: else return formatHexNum(value); } + static QString formatByteAsBinary(uint8_t value) + { + QString output; + for (int b = 7; b >= 0; b--) + { + if (value & (1 << b)) output += "1"; + else output += "0"; + } + return output; + } + //parses the input string to grab as much of it as possible while staying alpha numeric static QString grabAlphaNumeric(QString &input) { @@ -113,6 +124,67 @@ public: { return (value1 * (1.0 - samplePoint)) + (value2 * samplePoint); } + + static int64_t processIntegerSignal(const uint8_t *data, int startBit, int sigSize, bool littleEndian, bool isSigned) + { + + int64_t result = 0; + int bit; + + if (littleEndian) + { + bit = startBit; + for (int bitpos = 0; bitpos < sigSize; bitpos++) + { + if (data[bit / 8] & (1 << (bit % 8))) + result += (1ULL << bitpos); + bit++; + } + } + else //motorola / big endian mode + { + bit = startBit; + for (int bitpos = 0; bitpos < sigSize; bitpos++) + { + if (data[bit / 8] & (1 << (bit % 8))) + result += (1ULL << (sigSize - bitpos - 1)); + + if ((bit % 8) == 0) + bit += 15; + else bit--; + + } + } + + if (isSigned) + { + int64_t mask = (1ULL << (sigSize - 1)); + if ((result & mask) == mask) //is the highest bit possible for this signal size set? + { + /* + * if so we need to also set every bit higher in the result int too. + * This leads to the below two lines that are nasty. Here's the theory behind that... + * If the value is signed and the highest bit is set then it is negative. To create + * a negative value out of this even though the variable result is 64 bit we have to + * run 1's all of the way up to bit 63 in result. -1 is all ones for whatever size integer + * you have. So, it's 64 1's in this case. + * signedMask is done this way: + * first you take the signal size and shift 1 up that far. Then subtract one. Lets + * see that for a 16 bit signal: + * (1 << 16) - 1 = the first 16 bits set as 1's. So far so good. We then negate the whole + * thing which flips all bits. Thus signedMask ends up with 1's everwhere that the signal + * doesn't take up in the 64 bit signed integer result. Then, result has an OR operation on + * it with the old value and -1 masked so that the the 1 bits from -1 don't overwrite bits from the + * actual signal. This extends the sign bits out so that the integer result reads as the proper negative + * value. We dont need to do any of this if the sign bit wasn't set. + */ + int64_t signedMask = ~((1ULL << sigSize) - 1); + result = (-1LL & signedMask) | result; + } + } + + return result; + } }; #endif // UTILITY_H