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/config.h b/config.h index 4c832a0..c1d2878 100644 --- a/config.h +++ b/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG #define CONFIG -#define VERSION 145 +#define VERSION 146 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision diff --git a/dbc_classes.h b/dbc_classes.h index 7cbde33..0a43bbf 100644 --- a/dbc_classes.h +++ b/dbc_classes.h @@ -61,6 +61,9 @@ public: 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; @@ -83,6 +86,7 @@ public: DBC_NODE *sender; QList attributes; QList msgSignals; + DBC_SIGNAL* multiplexorSignal; }; diff --git a/dbchandler.cpp b/dbchandler.cpp index fea369f..bf3f2d3 100644 --- a/dbchandler.cpp +++ b/dbchandler.cpp @@ -62,10 +62,42 @@ void DBCHandler::loadDBCFile(QString filename) } if (line.startsWith("SG_ ")) //defines a signal { + int offset = 0; + bool isMultiplexor = false; + bool isMultiplexed = false; + DBC_SIGNAL sig; + qDebug() << "Found a SG line"; - regex.setPattern("^SG\\_ (\\w+) ?: ?(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)"); + 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); + } + } + //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 @@ -78,15 +110,14 @@ void DBCHandler::loadDBCFile(QString filename) //captured 11 is the receiving node 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) @@ -107,18 +138,19 @@ 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)); + else sig.receiver = findNodeByName(match.captured(11 + offset)); currentMessage->msgSignals.append(sig); + if (isMultiplexor) currentMessage->multiplexorSignal = ¤tMessage->msgSignals.last(); } else numSigFaults++; } @@ -357,7 +389,16 @@ void DBCHandler::saveDBCFile(QString filename) for (int s = 0; s < msg.msgSignals.count(); s++) { DBC_SIGNAL sig = msg.msgSignals[s]; - msgOutput.append(" SG_ " + sig.name + " : " + QString::number(sig.startBit) + "|" + QString::number(sig.signalSize) + "@"); + 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: @@ -554,6 +595,8 @@ QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig) int64_t result = 0; bool isSigned = false; + double endResult; + if (sig.valType == STRING) { QString buildString; @@ -564,10 +607,29 @@ QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig) } if (sig.valType == SIGNED_INT) isSigned = true; - result = Utility::processIntegerSignal(frame.data, sig.startBit, sig.signalSize, sig.intelByteOrder, isSigned); - - double endResult = ((double)result * sig.factor) + sig.bias; - result = (int) endResult; + if (sig.valType == SIGNED_INT || sig.valType == UNSIGNED_INT) + { + result = Utility::processIntegerSignal(frame.data, sig.startBit, sig.signalSize, sig.intelByteOrder, isSigned); + endResult = ((double)result * sig.factor) + sig.bias; + result = (int64_t)endResult; + } + else if (sig.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, sig.startBit, 32, false, false); + endResult = (*((float *)(&result)) * sig.factor) + sig.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)) * sig.factor) + sig.bias; + } QString outputString; @@ -581,13 +643,35 @@ QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig) } } else //otherwise display the actual number and unit (if it exists) - { + { outputString += QString::number(endResult) + sig.unitName; } return outputString; } +//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. If it isn't you get -1 back. +int32_t processSignalInt(const CANFrame &frame, const DBC_SIGNAL &sig) +{ + int32_t result = 0; + bool isSigned = false; + if (sig.valType == STRING || sig.valType == SP_FLOAT || sig.valType == DP_FLOAT) + { + return -1; //I warned you! + } + + if (sig.valType == SIGNED_INT) isSigned = true; + result = Utility::processIntegerSignal(frame.data, sig.startBit, sig.signalSize, sig.intelByteOrder, isSigned); + + double endResult = ((double)result * sig.factor) + sig.bias; + result = (int32_t)endResult; + + return result; +} + //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; diff --git a/dbchandler.h b/dbchandler.h index b49645c..8bb6f07 100644 --- a/dbchandler.h +++ b/dbchandler.h @@ -19,6 +19,7 @@ public: void saveDBCFile(QString); void listDebugging(); QString processSignal(const CANFrame &frame, const DBC_SIGNAL &sig); + int32_t processSignalInt(const CANFrame &frame, const DBC_SIGNAL &sig); DBC_NODE *findNodeByName(QString name); DBC_NODE *findNodeByIdx(int idx); diff --git a/dbcsignaleditor.cpp b/dbcsignaleditor.cpp index 6f439f7..1c0b098 100644 --- a/dbcsignaleditor.cpp +++ b/dbcsignaleditor.cpp @@ -52,6 +52,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); }); @@ -75,14 +77,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 +152,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 +164,45 @@ 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; + } + }); + + connect(ui->rbMultiplexor, &QRadioButton::toggled, + [=](bool state) + { + if (state) //signal is now set as a multiplexed signal + { + currentSignal->isMultiplexed = false; + currentSignal->isMultiplexor = true; + } + }); + + connect(ui->rbNotMulti, &QRadioButton::toggled, + [=](bool state) + { + if (state) //signal is now set as a multiplexed signal + { + currentSignal->isMultiplexed = false; + currentSignal->isMultiplexor = false; + } + }); } DBCSignalEditor::~DBCSignalEditor() @@ -274,8 +323,12 @@ void DBCSignalEditor::addNewSignal() newSig.signalSize = 1; newSig.startBit = 0; newSig.valType = UNSIGNED_INT; + newSig.isMultiplexed = false; + newSig.isMultiplexor = false; + newSig.multiplexValue = 0; ui->signalsList->addItem(newName); dbcMessage->msgSignals.append(newSig); + if (dbcMessage->msgSignals.count() == 1) clickSignalList(0); } void DBCSignalEditor::deleteCurrentSignal() @@ -317,6 +370,8 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) { unsigned char bitpattern[8]; + generateUsedBits(); + if (sig == NULL) { ui->txtName->setText(""); @@ -327,6 +382,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 +397,15 @@ 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) ); memset(bitpattern, 0, 8); //clear it out first. @@ -379,7 +442,6 @@ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) } } - ui->bitfield->updateData(bitpattern, true); ui->cbIntelFormat->setChecked(sig->intelByteOrder); @@ -446,6 +508,7 @@ 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()); if (thisSig == NULL) return; currentSignal = thisSig; @@ -459,5 +522,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->msgSignals.count(); x++) + { + DBC_SIGNAL sig = dbcMessage->msgSignals.at(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..beef97b 100644 --- a/dbcsignaleditor.h +++ b/dbcsignaleditor.h @@ -39,6 +39,7 @@ private: 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/mainwindow.cpp b/mainwindow.cpp index ff4d6e5..3596f8e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -15,6 +15,8 @@ Single / Multi state - The goal is to find bits that change based on toggles or 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 + +Change dbc editor to be able to do multiplexed signals */ QString MainWindow::loadedFileName = ""; @@ -820,7 +822,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;