Reorganized source code
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
#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;
|
||||
}
|
||||
/*TODO: It should be noted that the below floating point has not even been tested. For shame! Test it!*/
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#ifndef DBC_CLASSES_H
|
||||
#define DBC_CLASSES_H
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#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.
|
||||
|
||||
These things sort of form a hierarchy. Nodes send and receive messages.
|
||||
Messages are comprised of signals. Signals and messages have attributes.
|
||||
All of them can have comments.
|
||||
*/
|
||||
|
||||
enum DBC_SIG_VAL_TYPE
|
||||
{
|
||||
UNSIGNED_INT,
|
||||
SIGNED_INT,
|
||||
SP_FLOAT,
|
||||
DP_FLOAT,
|
||||
STRING
|
||||
};
|
||||
|
||||
enum DBC_ATTRIBUTE_VAL_TYPE
|
||||
{
|
||||
HEX,
|
||||
QFLOAT,
|
||||
QSTRING,
|
||||
ENUM
|
||||
};
|
||||
|
||||
class DBC_ATTRIBUTE
|
||||
{
|
||||
public:
|
||||
QString name;
|
||||
DBC_ATTRIBUTE_VAL_TYPE type;
|
||||
double startVal;
|
||||
double endVal;
|
||||
QStringList enumVals; //also used for STRING type but then you're assured to have only one
|
||||
};
|
||||
|
||||
class DBC_VAL
|
||||
{
|
||||
public:
|
||||
int value;
|
||||
QString descript;
|
||||
};
|
||||
|
||||
class DBC_NODE
|
||||
{
|
||||
public:
|
||||
QString name;
|
||||
QString comment;
|
||||
QList<DBC_ATTRIBUTE> 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: //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; //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<DBC_ATTRIBUTE> attributes;
|
||||
QList<DBC_VAL> 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();
|
||||
|
||||
uint32_t ID;
|
||||
QString name;
|
||||
QString comment;
|
||||
unsigned int len;
|
||||
DBC_NODE *sender;
|
||||
QList<DBC_ATTRIBUTE> attributes;
|
||||
DBCSignalHandler *sigHandler;
|
||||
DBC_SIGNAL* multiplexorSignal;
|
||||
};
|
||||
|
||||
|
||||
#endif // DBC_CLASSES_H
|
||||
|
||||
@@ -0,0 +1,841 @@
|
||||
#include "dbchandler.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include "utility.h"
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
DBC_SIGNAL* DBCSignalHandler::findSignalByName(QString name)
|
||||
{
|
||||
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;
|
||||
|
||||
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
delete inFile;
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Starting DBC load";
|
||||
dbc_nodes.clear();
|
||||
messageHandler->removeAllMessages();
|
||||
|
||||
DBC_NODE falseNode;
|
||||
falseNode.name = "Vector__XXX";
|
||||
falseNode.comment = "Default node if none specified";
|
||||
dbc_nodes.append(falseNode);
|
||||
|
||||
while (!inFile->atEnd()) {
|
||||
line = QString(inFile->readLine().simplified());
|
||||
if (line.startsWith("BO_ ")) //defines a message
|
||||
{
|
||||
qDebug() << "Found a BO line";
|
||||
regex.setPattern("^BO\\_ (\\w+) (\\w+) *: (\\w+) (\\w+)");
|
||||
match = regex.match(line);
|
||||
//captured 1 = the ID in decimal
|
||||
//captured 2 = The message name
|
||||
//captured 3 = the message length
|
||||
//captured 4 = the NODE responsible for this message
|
||||
if (match.hasMatch())
|
||||
{
|
||||
DBC_MESSAGE msg;
|
||||
msg.ID = match.captured(1).toULong() & 0x7FFFFFFFul; //the ID is always stored in decimal format
|
||||
msg.name = match.captured(2);
|
||||
msg.len = match.captured(3).toInt();
|
||||
msg.sender = findNodeByName(match.captured(4));
|
||||
messageHandler->addMessage(msg);
|
||||
currentMessage = messageHandler->findMsgByID(msg.ID);
|
||||
}
|
||||
else numMsgFaults++;
|
||||
}
|
||||
if (line.startsWith("SG_ ")) //defines a signal
|
||||
{
|
||||
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
|
||||
//captured 5 specifies signed/unsigned for ints
|
||||
//captured 6 is the scaling factor
|
||||
//captured 7 is the offset
|
||||
//captured 8 is the minimum value
|
||||
//captured 9 is the maximum value
|
||||
//captured 10 is the unit
|
||||
//captured 11 is the receiving node
|
||||
|
||||
if (match.hasMatch())
|
||||
{
|
||||
sig.name = match.captured(1);
|
||||
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 + offset) == "+") sig.valType = UNSIGNED_INT;
|
||||
else sig.valType = SIGNED_INT;
|
||||
}
|
||||
switch (val)
|
||||
{
|
||||
case 0: //big endian mode
|
||||
sig.intelByteOrder = false;
|
||||
break;
|
||||
case 1: //little endian mode
|
||||
sig.intelByteOrder = true;
|
||||
break;
|
||||
case 2:
|
||||
sig.valType = SP_FLOAT;
|
||||
break;
|
||||
case 3:
|
||||
sig.valType = DP_FLOAT;
|
||||
break;
|
||||
case 4:
|
||||
sig.valType = STRING;
|
||||
break;
|
||||
}
|
||||
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 + offset));
|
||||
sig.parentMessage = currentMessage;
|
||||
currentMessage->sigHandler->addSignal(sig);
|
||||
if (isMultiplexor) currentMessage->multiplexorSignal = currentMessage->sigHandler->findSignalByName(sig.name);
|
||||
}
|
||||
else numSigFaults++;
|
||||
}
|
||||
if (line.startsWith("BU_:")) //line specifies the nodes on this canbus
|
||||
{
|
||||
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
|
||||
if (match.hasMatch())
|
||||
{
|
||||
QStringList nodeStrings = match.captured(1).split(' ');
|
||||
qDebug() << "Found " << nodeStrings.count() << " node names";
|
||||
for (int i = 0; i < nodeStrings.count(); i++)
|
||||
{
|
||||
//qDebug() << nodeStrings[i];
|
||||
if (nodeStrings[i].length() > 1)
|
||||
{
|
||||
DBC_NODE node;
|
||||
node.name = nodeStrings[i];
|
||||
dbc_nodes.append(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (line.startsWith("CM_ SG_ "))
|
||||
{
|
||||
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
|
||||
//captured 2 is the signal name from that message
|
||||
//captured 3 is the comment itself
|
||||
if (match.hasMatch())
|
||||
{
|
||||
//qDebug() << "Comment was: " << match.captured(3);
|
||||
DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt());
|
||||
if (msg != NULL)
|
||||
{
|
||||
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2));
|
||||
if (sig != NULL)
|
||||
{
|
||||
sig->comment = match.captured(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (line.startsWith("CM_ BO_ "))
|
||||
{
|
||||
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
|
||||
//captured 2 is the comment itself
|
||||
if (match.hasMatch())
|
||||
{
|
||||
//qDebug() << "Comment was: " << match.captured(2);
|
||||
DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt());
|
||||
if (msg != NULL)
|
||||
{
|
||||
msg->comment = match.captured(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (line.startsWith("CM_ BU_ "))
|
||||
{
|
||||
qDebug() << "Found a BU comment line";
|
||||
regex.setPattern("^CM\\_ BU\\_ *(\\w+) *\\\"(.*)\\\";");
|
||||
match = regex.match(line);
|
||||
//captured 1 is the Node name
|
||||
//captured 2 is the comment itself
|
||||
if (match.hasMatch())
|
||||
{
|
||||
//qDebug() << "Comment was: " << match.captured(2);
|
||||
DBC_NODE *node = findNodeByName(match.captured(1));
|
||||
if (node != NULL)
|
||||
{
|
||||
node->comment = match.captured(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
//VAL_ (1090) (VCUPresentParkLightOC) (1 "Error present" 0 "Error not present") ;
|
||||
if (line.startsWith("VAL_ "))
|
||||
{
|
||||
qDebug() << "Found a value definition line";
|
||||
regex.setPattern("^VAL\\_ (\\w+) (\\w+) (.*);");
|
||||
match = regex.match(line);
|
||||
//captured 1 is the ID to match against
|
||||
//captured 2 is the signal name to match against
|
||||
//captured 3 is a series of values in the form (number "text") that is, all sep'd by spaces
|
||||
if (match.hasMatch())
|
||||
{
|
||||
//qDebug() << "Data was: " << match.captured(3);
|
||||
DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt());
|
||||
if (msg != NULL)
|
||||
{
|
||||
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2));
|
||||
if (sig != NULL)
|
||||
{
|
||||
QString tokenString = match.captured(3);
|
||||
DBC_VAL val;
|
||||
while (tokenString.length() > 2)
|
||||
{
|
||||
regex.setPattern("(\\d+) \\\"(.*?)\\\"(.*)");
|
||||
match = regex.match(tokenString);
|
||||
if (match.hasMatch())
|
||||
{
|
||||
val.value = match.captured(1).toInt();
|
||||
val.descript = match.captured(2);
|
||||
//qDebug() << "sig val " << val.value << " desc " <<val.descript;
|
||||
sig->valList.append(val);
|
||||
int rightSize = tokenString.length() - match.captured(1).length() - match.captured(2).length() - 4;
|
||||
if (rightSize > 0) tokenString = tokenString.right(rightSize);
|
||||
else tokenString = "";
|
||||
//qDebug() << "New token string: " << tokenString;
|
||||
}
|
||||
else tokenString = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (line.startsWith("BA_DEF_ SG_ "))
|
||||
{
|
||||
qDebug() << "Found a SG attribute line";
|
||||
regex.setPattern("^BA\\_DEF\\_ SG\\_ +\\\"([A-Za-z0-9\-_]+)\\\" +(.+);");
|
||||
match = regex.match(line);
|
||||
//captured 1 is the Node name
|
||||
//captured 2 is the comment itself
|
||||
if (match.hasMatch())
|
||||
{
|
||||
qDebug() << "Comment was: " << match.captured(2);
|
||||
}
|
||||
}
|
||||
if (line.startsWith("BA_DEF_ BO_ "))
|
||||
{
|
||||
|
||||
}
|
||||
if (line.startsWith("BA_DEF_ BU_ "))
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
void DBCFile::saveFile(QString fileName)
|
||||
{
|
||||
QFile *outFile = new QFile(fileName);
|
||||
QString nodesOutput, msgOutput, commentsOutput, valuesOutput;
|
||||
|
||||
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
{
|
||||
delete outFile;
|
||||
return;
|
||||
}
|
||||
|
||||
//right now it outputs a standard hard coded boilerplate
|
||||
outFile->write("VERSION \"\"\n");
|
||||
outFile->write("\n");
|
||||
outFile->write("\n");
|
||||
outFile->write("NS_ :\n");
|
||||
outFile->write(" NS_DESC_\n");
|
||||
outFile->write(" CM_\n");
|
||||
outFile->write(" BA_DEF_\n");
|
||||
outFile->write(" BA_\n");
|
||||
outFile->write(" VAL_\n");
|
||||
outFile->write(" CAT_DEF_\n");
|
||||
outFile->write(" CAT_\n");
|
||||
outFile->write(" FILTER\n");
|
||||
outFile->write(" BA_DEF_DEF_\n");
|
||||
outFile->write(" EV_DATA_\n");
|
||||
outFile->write(" ENVVAR_DATA_\n");
|
||||
outFile->write(" SGTYPE_\n");
|
||||
outFile->write(" SGTYPE_VAL_\n");
|
||||
outFile->write(" BA_DEF_SGTYPE_\n");
|
||||
outFile->write(" BA_SGTYPE_\n");
|
||||
outFile->write(" SIG_TYPE_REF_\n");
|
||||
outFile->write(" VAL_TABLE_\n");
|
||||
outFile->write(" SIG_GROUP_\n");
|
||||
outFile->write(" SIG_VALTYPE_\n");
|
||||
outFile->write(" SIGTYPE_VALTYPE_\n");
|
||||
outFile->write(" BO_TX_BU_\n");
|
||||
outFile->write(" BA_DEF_REL_\n");
|
||||
outFile->write(" BA_REL_\n");
|
||||
outFile->write(" BA_DEF_DEF_REL_\n");
|
||||
outFile->write(" BU_SG_REL_\n");
|
||||
outFile->write(" BU_EV_REL_\n");
|
||||
outFile->write(" BU_BO_REL_\n");
|
||||
outFile->write(" SG_MUL_VAL_\n");
|
||||
outFile->write("\n");
|
||||
outFile->write("BS_: \n");
|
||||
|
||||
nodesOutput.append("BU_: ");
|
||||
for (int x = 0; x < dbc_nodes.count(); x++)
|
||||
{
|
||||
DBC_NODE node = dbc_nodes[x];
|
||||
if (node.name.compare("Vector__XXX", Qt::CaseInsensitive) != 0)
|
||||
{
|
||||
nodesOutput.append(node.name + " ");
|
||||
if (node.comment.length() > 0)
|
||||
{
|
||||
commentsOutput.append("CM_ BU_ " + node.name + " \"" + node.comment + "\";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
nodesOutput.append("\n");
|
||||
outFile->write(nodesOutput.toUtf8());
|
||||
|
||||
for (int x = 0; x < messageHandler->getCount(); x++)
|
||||
{
|
||||
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");
|
||||
}
|
||||
for (int s = 0; s < msg->sigHandler->getCount(); s++)
|
||||
{
|
||||
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+");
|
||||
else msgOutput.append("0+");
|
||||
break;
|
||||
case SIGNED_INT:
|
||||
if (sig->intelByteOrder) msgOutput.append("1-");
|
||||
else msgOutput.append("0-");
|
||||
break;
|
||||
case SP_FLOAT:
|
||||
msgOutput.append("2-");
|
||||
break;
|
||||
case DP_FLOAT:
|
||||
msgOutput.append("3-");
|
||||
break;
|
||||
case STRING:
|
||||
msgOutput.append("4-");
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
commentsOutput.append("CM_ SG_ " + QString::number(msg->ID) + " " + sig->name + " \"" + sig->comment + "\";\n");
|
||||
}
|
||||
if (sig->valList.count() > 0)
|
||||
{
|
||||
valuesOutput.append("VAL_ " + QString::number(msg->ID) + " " + sig->name);
|
||||
for (int v = 0; v < sig->valList.count(); v++)
|
||||
{
|
||||
DBC_VAL val = sig->valList[v];
|
||||
valuesOutput.append(" " + QString::number(val.value) + " \"" + val.descript +"\"");
|
||||
}
|
||||
valuesOutput.append(";\n");
|
||||
}
|
||||
}
|
||||
msgOutput.append("\n");
|
||||
//write it out every message so the string doesn't end up too huge
|
||||
outFile->write(msgOutput.toUtf8());
|
||||
msgOutput.clear(); //got to reset it after writing
|
||||
}
|
||||
|
||||
//now write out all of the accumulated comments and value tables from above
|
||||
outFile->write(commentsOutput.toUtf8());
|
||||
outFile->write(valuesOutput.toUtf8());
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
void DBCHandler::saveDBCFile(int idx)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
DBC_MESSAGE* DBCHandler::findMessage(const CANFrame &frame)
|
||||
{
|
||||
for(int i = 0; i < loadedFiles.count(); i++)
|
||||
{
|
||||
if (loadedFiles[i].getAssocBus() == -1 || frame.bus == loadedFiles[i].getAssocBus())
|
||||
{
|
||||
DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(frame.ID);
|
||||
if (msg != NULL) return msg;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int DBCHandler::getFileCount()
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef DBCHANDLER_H
|
||||
#define DBCHANDLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include "dbc_classes.h"
|
||||
#include "can_structs.h"
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Finish coding up the decoupled design
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
class DBCSignalHandler: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
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<DBC_SIGNAL> 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<DBC_MESSAGE> messages;
|
||||
};
|
||||
|
||||
//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_NODE> dbc_nodes;
|
||||
private:
|
||||
QString fileName;
|
||||
QString filePath;
|
||||
int assocBuses; //-1 = all buses, 0 = first bus, 1 = second bus
|
||||
};
|
||||
|
||||
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:
|
||||
QList<DBCFile> loadedFiles;
|
||||
};
|
||||
|
||||
#endif // DBCHANDLER_H
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "dbcloadsavewindow.h"
|
||||
#include "ui_dbcloadsavewindow.h"
|
||||
|
||||
DBCLoadSaveWindow::DBCLoadSaveWindow(DBCHandler *handler, const QVector<CANFrame> *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);
|
||||
if(file) {
|
||||
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<QTableWidgetItem*> sourceItems = takeRow(idx);
|
||||
QList<QTableWidgetItem*> destItems = takeRow(destIdx);
|
||||
|
||||
// set back in reverse order
|
||||
setRow(idx, destItems);
|
||||
setRow(destIdx, sourceItems);
|
||||
}
|
||||
|
||||
QList<QTableWidgetItem*> DBCLoadSaveWindow::takeRow(int row)
|
||||
{
|
||||
QList<QTableWidgetItem*> 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<QTableWidgetItem*>& rowItems)
|
||||
{
|
||||
for (int col = 0; col < ui->tableFiles->columnCount(); ++col)
|
||||
{
|
||||
ui->tableFiles->setItem(row, col, rowItems.at(col));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef DBCLOADSAVEWINDOW_H
|
||||
#define DBCLOADSAVEWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTableWidget>
|
||||
#include "dbchandler.h"
|
||||
#include "dbcmaineditor.h"
|
||||
|
||||
namespace Ui {
|
||||
class DBCLoadSaveWindow;
|
||||
}
|
||||
|
||||
class DBCLoadSaveWindow : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DBCLoadSaveWindow(DBCHandler *handler, const QVector<CANFrame> *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<CANFrame> *referenceFrames;
|
||||
DBCMainEditor *editorWindow;
|
||||
|
||||
void swapTableRows(bool up);
|
||||
QList<QTableWidgetItem*> takeRow(int row);
|
||||
void setRow(int row, const QList<QTableWidgetItem*>& rowItems);
|
||||
};
|
||||
|
||||
#endif // DBCLOADSAVEWINDOW_H
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
#include "dbcmaineditor.h"
|
||||
#include "ui_dbcmaineditor.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
|
||||
#define MT_COLUMN_COUNT 5
|
||||
|
||||
DBCMainEditor::DBCMainEditor(DBCHandler *handler, const QVector<CANFrame> *frames, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::DBCMainEditor)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
readSettings();
|
||||
|
||||
dbcHandler = handler;
|
||||
referenceFrames = frames;
|
||||
|
||||
QStringList headers;
|
||||
headers << "Node Name" << "Comment";
|
||||
ui->NodesTable->setColumnCount(2);
|
||||
ui->NodesTable->setColumnWidth(0, 240);
|
||||
ui->NodesTable->setColumnWidth(1, 700);
|
||||
ui->NodesTable->setHorizontalHeaderLabels(headers);
|
||||
ui->NodesTable->horizontalHeader()->setStretchLastSection(true);
|
||||
|
||||
QStringList headers2;
|
||||
headers2 << "Msg ID" << "Msg Name" << "Data Len" << "Signals" << "Comment";
|
||||
ui->MessagesTable->setColumnCount(MT_COLUMN_COUNT);
|
||||
ui->MessagesTable->setColumnWidth(0, 80);
|
||||
ui->MessagesTable->setColumnWidth(1, 240);
|
||||
ui->MessagesTable->setColumnWidth(2, 80);
|
||||
ui->MessagesTable->setColumnWidth(3, 80);
|
||||
ui->MessagesTable->setColumnWidth(4, 340);
|
||||
ui->MessagesTable->setHorizontalHeaderLabels(headers2);
|
||||
ui->MessagesTable->horizontalHeader()->setStretchLastSection(true);
|
||||
|
||||
connect(ui->NodesTable, SIGNAL(cellChanged(int,int)), this, SLOT(onCellChangedNode(int,int)));
|
||||
connect(ui->NodesTable, SIGNAL(cellClicked(int,int)), this, SLOT(onCellClickedNode(int,int)));
|
||||
connect(ui->NodesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuNode(QPoint)));
|
||||
ui->NodesTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(ui->MessagesTable, SIGNAL(cellChanged(int,int)), this, SLOT(onCellChangedMessage(int,int)));
|
||||
connect(ui->MessagesTable, SIGNAL(cellClicked(int,int)), this, SLOT(onCellClickedMessage(int,int)));
|
||||
connect(ui->MessagesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuMessage(QPoint)));
|
||||
ui->MessagesTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
sigEditor = new DBCSignalEditor(handler);
|
||||
}
|
||||
|
||||
void DBCMainEditor::showEvent(QShowEvent* event)
|
||||
{
|
||||
QDialog::showEvent(event);
|
||||
|
||||
inhibitCellChanged = true;
|
||||
refreshNodesTable();
|
||||
if (dbcFile->dbc_nodes.count() > 0)
|
||||
refreshMessagesTable(&dbcFile->dbc_nodes.at(0));
|
||||
|
||||
currRow = 0;
|
||||
inhibitCellChanged = false;
|
||||
}
|
||||
|
||||
DBCMainEditor::~DBCMainEditor()
|
||||
{
|
||||
delete ui;
|
||||
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);
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
void DBCMainEditor::readSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
||||
{
|
||||
resize(settings.value("DBCMainEditor/WindowSize", QSize(1103, 571)).toSize());
|
||||
move(settings.value("DBCMainEditor/WindowPos", QPoint(50, 50)).toPoint());
|
||||
}
|
||||
}
|
||||
|
||||
void DBCMainEditor::writeSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
||||
{
|
||||
settings.setValue("DBCMainEditor/WindowSize", size());
|
||||
settings.setValue("DBCMainEditor/WindowPos", pos());
|
||||
}
|
||||
}
|
||||
|
||||
void DBCMainEditor::onCustomMenuNode(QPoint point)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
menu->addAction(tr("Delete currently selected node"), this, SLOT(deleteCurrentNode()));
|
||||
|
||||
menu->popup(ui->NodesTable->mapToGlobal(point));
|
||||
|
||||
}
|
||||
|
||||
void DBCMainEditor::onCustomMenuMessage(QPoint point)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
menu->addAction(tr("Delete currently selected message"), this, SLOT(deleteCurrentMessage()));
|
||||
|
||||
menu->popup(ui->MessagesTable->mapToGlobal(point));
|
||||
|
||||
}
|
||||
|
||||
void DBCMainEditor::deleteCurrentNode()
|
||||
{
|
||||
int thisRow = ui->NodesTable->currentRow();
|
||||
QString nodeName = ui->NodesTable->item(thisRow, 0)->text();
|
||||
if (nodeName.length() > 0 && nodeName.compare("Vector__XXX", Qt::CaseInsensitive) != 0)
|
||||
{
|
||||
ui->NodesTable->removeRow(thisRow);
|
||||
dbcFile->dbc_nodes.removeAt(thisRow);
|
||||
inhibitCellChanged = true;
|
||||
refreshMessagesTable(&dbcFile->dbc_nodes[0]);
|
||||
ui->NodesTable->selectRow(0);
|
||||
inhibitCellChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DBCMainEditor::deleteCurrentMessage()
|
||||
{
|
||||
int thisRow = ui->MessagesTable->currentRow();
|
||||
if (ui->MessagesTable->item(thisRow, 0)->text().length() > 0)
|
||||
{
|
||||
ui->MessagesTable->removeRow(thisRow);
|
||||
dbcFile->messageHandler->removeMessageByIndex(thisRow);
|
||||
}
|
||||
}
|
||||
|
||||
void DBCMainEditor::onCellChangedNode(int row,int col)
|
||||
{
|
||||
if (inhibitCellChanged) return;
|
||||
if (row == ui->NodesTable->rowCount() - 1)
|
||||
{
|
||||
//if this was the last row then it's new and we need to both
|
||||
//create another potentially new record underneath and also
|
||||
//add a node entry in the nodes list and store the record
|
||||
//That is, so long as the node name was filled out
|
||||
if (col == 0)
|
||||
{
|
||||
DBC_NODE newNode;
|
||||
QString newName = ui->NodesTable->item(row, col)->text().simplified().replace(' ', '_');
|
||||
qDebug() << "new name: " << newName;
|
||||
if (newName.length() == 0) return;
|
||||
if (dbcFile->findNodeByName(newName) != NULL) //duplicates an existing node!
|
||||
{
|
||||
QMessageBox msg;
|
||||
msg.setParent(0);
|
||||
msg.setText("An existing node with that name already exists! Aborting!");
|
||||
msg.exec();
|
||||
return;
|
||||
}
|
||||
newNode.name = newName;
|
||||
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);
|
||||
ui->NodesTable->insertRow(ui->NodesTable->rowCount());
|
||||
inhibitCellChanged = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (col == 0)
|
||||
{
|
||||
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;
|
||||
else nodeName = oldNode->name;
|
||||
inhibitCellChanged = true;
|
||||
QTableWidgetItem *widgetName = new QTableWidgetItem(nodeName);
|
||||
ui->NodesTable->setItem(row, col, widgetName);
|
||||
inhibitCellChanged = false;
|
||||
}
|
||||
if (col == 1) //must have been col 1 then
|
||||
{
|
||||
QString nodeName = ui->NodesTable->item(row, 0)->text().simplified().replace(' ', '_');
|
||||
qDebug() << "searching for node " << 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;
|
||||
}
|
||||
}
|
||||
ui->NodesTable->setCurrentCell(row, col);
|
||||
}
|
||||
|
||||
void DBCMainEditor::onCellChangedMessage(int row,int col)
|
||||
{
|
||||
QTableWidgetItem* item = NULL;
|
||||
bool ret = false;
|
||||
DBC_MESSAGE *msg = NULL;
|
||||
uint msgID;
|
||||
|
||||
if (inhibitCellChanged) return;
|
||||
|
||||
DBC_NODE *node = dbcFile->findNodeByIdx(ui->NodesTable->currentRow());
|
||||
if (node == NULL)
|
||||
{
|
||||
qDebug() << "No node set?!? This is bad!";
|
||||
return;
|
||||
}
|
||||
|
||||
item = ui->MessagesTable->item(row, 0);
|
||||
if(!item) return;
|
||||
|
||||
msgID = Utility::ParseStringToNum2(item->text(), &ret);
|
||||
msg = dbcFile->messageHandler->findMsgByID(msgID);
|
||||
|
||||
switch(col)
|
||||
{
|
||||
case 0: //msg id
|
||||
{
|
||||
/* sanity checks */
|
||||
if(!ret)
|
||||
{
|
||||
/* bad message id */
|
||||
ui->MessagesTable->item(row, 0)->setText("");
|
||||
return;
|
||||
}
|
||||
if (msg != NULL)
|
||||
{
|
||||
QMessageBox msg;
|
||||
msg.setParent(0);
|
||||
msg.setText("An existing msg with that ID already exists! Aborting!");
|
||||
msg.exec();
|
||||
|
||||
ui->MessagesTable->item(row, 0)->setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
/* insert row */
|
||||
DBC_MESSAGE newMsg;
|
||||
newMsg.ID = msgID;
|
||||
newMsg.name = "";
|
||||
newMsg.sender = node;
|
||||
newMsg.len = 0;
|
||||
for (int i = 0; i < referenceFrames->length(); i++)
|
||||
{
|
||||
if ((uint) referenceFrames->at(i).ID == msgID)
|
||||
{
|
||||
newMsg.len = referenceFrames->at(i).len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dbcFile->messageHandler->addMessage(newMsg);
|
||||
|
||||
/* insert message in table */
|
||||
inhibitCellChanged = true;
|
||||
|
||||
item = ui->MessagesTable->item(row, 0);
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
item->setText(Utility::formatNumber(msgID));
|
||||
|
||||
for(int i=1 ; i<MT_COLUMN_COUNT ; i++)
|
||||
{
|
||||
item = ui->MessagesTable->item(row, i);
|
||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||
}
|
||||
|
||||
/* set length */
|
||||
item = ui->MessagesTable->item(row, 2);
|
||||
item->setText(QString::number(newMsg.len));
|
||||
|
||||
inhibitCellChanged = false;
|
||||
|
||||
/* insert a new row */
|
||||
insertBlankRow();
|
||||
break;
|
||||
}
|
||||
case 1: //msg name
|
||||
{
|
||||
QString msgName = ui->MessagesTable->item(row, 1)->text().simplified().replace(' ', '_');
|
||||
if (msgName.length() == 0) return;
|
||||
if( ret && (msg!=NULL) )
|
||||
msg->name = msgName;
|
||||
break;
|
||||
}
|
||||
case 2: //data length
|
||||
{
|
||||
bool parseOk = false;
|
||||
uint msgLen = ui->MessagesTable->item(row, col)->text().toUInt(&parseOk);
|
||||
|
||||
/* sanity checks */
|
||||
if(!parseOk)
|
||||
{
|
||||
ui->MessagesTable->item(row, col)->setText("");
|
||||
return;
|
||||
}
|
||||
if (msgLen > 8)
|
||||
{
|
||||
msgLen = 8;
|
||||
ui->MessagesTable->item(row, col)->setText(QString::number(msgLen));
|
||||
}
|
||||
|
||||
if( ret && (msg!=NULL) )
|
||||
msg->len = msgLen;
|
||||
break;
|
||||
}
|
||||
case 3: //signals (number) - we don't handle anything here. User cannot directly change this value
|
||||
break;
|
||||
case 4: //comment
|
||||
{
|
||||
QString msgComment = ui->MessagesTable->item(row, col)->text().simplified();
|
||||
if( ret && (msgComment!=NULL) )
|
||||
msg->comment = msgComment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ui->MessagesTable->setCurrentCell(row, col);
|
||||
}
|
||||
|
||||
void DBCMainEditor::onCellClickedNode(int row, int col)
|
||||
{
|
||||
//reload messages list if the currently selected row has changed.
|
||||
qDebug() << "Clicked at row " << row << " col " << col;
|
||||
if (row != currRow)
|
||||
{
|
||||
currRow = row;
|
||||
QTableWidgetItem *item = ui->NodesTable->item(currRow, 0);
|
||||
QString nodeName;
|
||||
if (item == NULL) return;
|
||||
|
||||
nodeName = item->text();
|
||||
qDebug() << "Trying to find node with name " << nodeName;
|
||||
DBC_NODE *node = dbcFile->findNodeByName(nodeName);
|
||||
//qDebug() << "Address of node: " << (int)node;
|
||||
inhibitCellChanged = true;
|
||||
refreshMessagesTable(node);
|
||||
inhibitCellChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DBCMainEditor::onCellClickedMessage(int row, int col)
|
||||
{
|
||||
if (col == 3) //3 is the signals field. If clicked we go to the signals dialog
|
||||
{
|
||||
QTableWidgetItem* msg = ui->MessagesTable->item(row, 0);
|
||||
if(msg) {
|
||||
QString idString = msg->text();
|
||||
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->sigHandler->getCount()));
|
||||
ui->MessagesTable->setItem(row, col, replacement);
|
||||
inhibitCellChanged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBCMainEditor::refreshNodesTable()
|
||||
{
|
||||
ui->NodesTable->clearContents(); //first step, clear out any previous crap
|
||||
ui->NodesTable->setRowCount(0);
|
||||
|
||||
int rowIdx;
|
||||
|
||||
if (dbcFile->findNodeByName("Vector__XXX") == NULL)
|
||||
{
|
||||
DBC_NODE newNode;
|
||||
newNode.name = "Vector__XXX";
|
||||
newNode.comment = "Default node if no other node is specified";
|
||||
dbcFile->dbc_nodes.append(newNode);
|
||||
}
|
||||
|
||||
for (int x = 0; x < dbcFile->dbc_nodes.count(); 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();
|
||||
ui->NodesTable->insertRow(rowIdx);
|
||||
ui->NodesTable->setItem(rowIdx, 0, nodeName);
|
||||
ui->NodesTable->setItem(rowIdx, 1, nodeComment);
|
||||
}
|
||||
|
||||
//insert a fresh entry at the bottom that contains nothing
|
||||
ui->NodesTable->insertRow(ui->NodesTable->rowCount());
|
||||
ui->NodesTable->selectRow(0);
|
||||
}
|
||||
|
||||
void DBCMainEditor::refreshMessagesTable(const DBC_NODE *node)
|
||||
{
|
||||
ui->MessagesTable->clearContents();
|
||||
ui->MessagesTable->setRowCount(0);
|
||||
|
||||
qDebug() << "In refreshMessagesTable";
|
||||
|
||||
int rowIdx;
|
||||
|
||||
if (node != NULL)
|
||||
{
|
||||
for (int x = 0; x < dbcFile->messageHandler->getCount(); x++)
|
||||
{
|
||||
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->sigHandler->getCount()));
|
||||
QTableWidgetItem *msgComment = new QTableWidgetItem(msg->comment);
|
||||
|
||||
rowIdx = ui->MessagesTable->rowCount();
|
||||
ui->MessagesTable->insertRow(rowIdx);
|
||||
ui->MessagesTable->setItem(rowIdx, 0, msgID);
|
||||
ui->MessagesTable->setItem(rowIdx, 1, msgName);
|
||||
ui->MessagesTable->setItem(rowIdx, 2, msgLen);
|
||||
ui->MessagesTable->setItem(rowIdx, 3, msgSignals);
|
||||
ui->MessagesTable->setItem(rowIdx, 4, msgComment);
|
||||
//note that there is a sending node field in the structure but we're
|
||||
//not displaying it. It has to be the node we selected from the node list
|
||||
//so no need to show that here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//insert blank record that can be used to add new messages
|
||||
insertBlankRow();
|
||||
}
|
||||
|
||||
void DBCMainEditor::insertBlankRow()
|
||||
{
|
||||
int rowIdx = ui->MessagesTable->rowCount();
|
||||
ui->MessagesTable->insertRow(rowIdx);
|
||||
for(int i=1 ; i<MT_COLUMN_COUNT ; i++)
|
||||
{
|
||||
QTableWidgetItem *item = new QTableWidgetItem("");
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||||
ui->MessagesTable->setItem(rowIdx, i, item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef DBCMAINEDITOR_H
|
||||
#define DBCMAINEDITOR_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDebug>
|
||||
#include "dbchandler.h"
|
||||
#include "dbcsignaleditor.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace Ui {
|
||||
class DBCMainEditor;
|
||||
}
|
||||
|
||||
class DBCMainEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DBCMainEditor(DBCHandler *handler, const QVector<CANFrame> *frames, QWidget *parent = 0);
|
||||
~DBCMainEditor();
|
||||
void setFileIdx(int idx);
|
||||
|
||||
private slots:
|
||||
void onCellChangedNode(int,int);
|
||||
void onCellClickedNode(int, int);
|
||||
void onCellClickedMessage(int, int);
|
||||
void onCellChangedMessage(int,int);
|
||||
void onCustomMenuNode(QPoint);
|
||||
void onCustomMenuMessage(QPoint);
|
||||
void deleteCurrentNode();
|
||||
void deleteCurrentMessage();
|
||||
|
||||
private:
|
||||
Ui::DBCMainEditor *ui;
|
||||
DBCHandler *dbcHandler;
|
||||
const QVector<CANFrame> *referenceFrames;
|
||||
DBCSignalEditor *sigEditor;
|
||||
int currRow;
|
||||
DBCFile *dbcFile;
|
||||
int fileIdx;
|
||||
bool inhibitCellChanged;
|
||||
|
||||
void refreshNodesTable();
|
||||
void refreshMessagesTable(const DBC_NODE *node);
|
||||
void showEvent(QShowEvent* event);
|
||||
void closeEvent(QCloseEvent *event);
|
||||
void readSettings();
|
||||
void writeSettings();
|
||||
void insertBlankRow();
|
||||
|
||||
};
|
||||
|
||||
#endif // DBCMAINEDITOR_H
|
||||
@@ -0,0 +1,602 @@
|
||||
#include "dbcsignaleditor.h"
|
||||
#include "ui_dbcsignaleditor.h"
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QMenu>
|
||||
#include <QSettings>
|
||||
|
||||
DBCSignalEditor::DBCSignalEditor(DBCHandler *handler, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::DBCSignalEditor)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
readSettings();
|
||||
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch());
|
||||
|
||||
dbcHandler = handler;
|
||||
dbcMessage = NULL;
|
||||
currentSignal = NULL;
|
||||
|
||||
QStringList headers2;
|
||||
headers2 << "Value" << "Text";
|
||||
ui->valuesTable->setColumnCount(2);
|
||||
ui->valuesTable->setColumnWidth(0, 200);
|
||||
ui->valuesTable->setColumnWidth(1, 440);
|
||||
ui->valuesTable->setHorizontalHeaderLabels(headers2);
|
||||
ui->valuesTable->horizontalHeader()->setStretchLastSection(true);
|
||||
|
||||
ui->comboType->addItem("UNSIGNED INTEGER");
|
||||
ui->comboType->addItem("SIGNED INTEGER");
|
||||
ui->comboType->addItem("SINGLE PRECISION");
|
||||
ui->comboType->addItem("DOUBLE PRECISION");
|
||||
ui->comboType->addItem("STRING");
|
||||
|
||||
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)));
|
||||
ui->signalsList->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->valuesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuValues(QPoint)));
|
||||
ui->valuesTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->valuesTable, SIGNAL(cellChanged(int,int)), this, SLOT(onValuesCellChanged(int,int)));
|
||||
|
||||
//now with 100% more lambda expressions just to make it interesting (and shorter, and easier...)
|
||||
connect(ui->cbIntelFormat, &QCheckBox::toggled,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
currentSignal->intelByteOrder = ui->cbIntelFormat->isChecked();
|
||||
if (currentSignal->valType == SP_FLOAT || currentSignal->valType == DP_FLOAT)
|
||||
currentSignal->intelByteOrder = false;
|
||||
fillSignalForm(currentSignal);
|
||||
});
|
||||
|
||||
connect(ui->comboReceiver, &QComboBox::currentTextChanged,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
currentSignal->receiver = dbcFile->findNodeByName(ui->comboReceiver->currentText());
|
||||
});
|
||||
connect(ui->comboType, &QComboBox::currentTextChanged,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
switch (ui->comboType->currentIndex())
|
||||
{
|
||||
case 0:
|
||||
currentSignal->valType = UNSIGNED_INT;
|
||||
break;
|
||||
case 1:
|
||||
currentSignal->valType = SIGNED_INT;
|
||||
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;
|
||||
}
|
||||
fillSignalForm(currentSignal);
|
||||
});
|
||||
connect(ui->txtBias, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
double temp;
|
||||
bool result;
|
||||
temp = ui->txtBias->text().toDouble(&result);
|
||||
if (result) currentSignal->bias = temp;
|
||||
});
|
||||
|
||||
connect(ui->txtMaxVal, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
double temp;
|
||||
bool result;
|
||||
temp = ui->txtMaxVal->text().toDouble(&result);
|
||||
if (result) currentSignal->max = temp;
|
||||
});
|
||||
|
||||
connect(ui->txtMinVal, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
double temp;
|
||||
bool result;
|
||||
temp = ui->txtMinVal->text().toDouble(&result);
|
||||
if (result) currentSignal->min = temp;
|
||||
});
|
||||
connect(ui->txtScale, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
double temp;
|
||||
bool result;
|
||||
temp = ui->txtScale->text().toDouble(&result);
|
||||
if (result) currentSignal->factor = temp;
|
||||
});
|
||||
connect(ui->txtComment, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
currentSignal->comment = ui->txtComment->text();
|
||||
});
|
||||
|
||||
connect(ui->txtUnitName, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
currentSignal->unitName = ui->txtUnitName->text();
|
||||
});
|
||||
connect(ui->txtBitLength, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
int temp;
|
||||
temp = Utility::ParseStringToNum(ui->txtBitLength->text());
|
||||
if (temp < 0) return;
|
||||
if (temp > 63) return;
|
||||
if (currentSignal->valType != SP_FLOAT && currentSignal->valType != DP_FLOAT)
|
||||
currentSignal->signalSize = temp;
|
||||
fillSignalForm(currentSignal);
|
||||
});
|
||||
connect(ui->txtName, &QLineEdit::editingFinished,
|
||||
[=]()
|
||||
{
|
||||
if (currentSignal == NULL) return;
|
||||
currentSignal->name = ui->txtName->text();
|
||||
//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 && currentSignal) //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 && currentSignal) //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 && currentSignal) //signal is now set as a multiplexed signal
|
||||
{
|
||||
currentSignal->isMultiplexed = false;
|
||||
currentSignal->isMultiplexor = false;
|
||||
if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = NULL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DBCSignalEditor::~DBCSignalEditor()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void DBCSignalEditor::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
Q_UNUSED(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;
|
||||
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
||||
{
|
||||
resize(settings.value("DBCSignalEditor/WindowSize", QSize(800, 572)).toSize());
|
||||
move(settings.value("DBCSignalEditor/WindowPos", QPoint(100, 100)).toPoint());
|
||||
}
|
||||
}
|
||||
|
||||
void DBCSignalEditor::writeSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
if (settings.value("Main/SaveRestorePositions", false).toBool())
|
||||
{
|
||||
settings.setValue("DBCSignalEditor/WindowSize", size());
|
||||
settings.setValue("DBCSignalEditor/WindowPos", pos());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DBCSignalEditor::setMessageRef(DBC_MESSAGE *msg)
|
||||
{
|
||||
dbcMessage = msg;
|
||||
}
|
||||
|
||||
void DBCSignalEditor::showEvent(QShowEvent* event)
|
||||
{
|
||||
QDialog::showEvent(event);
|
||||
|
||||
currentSignal = NULL;
|
||||
refreshSignalsList();
|
||||
}
|
||||
|
||||
void DBCSignalEditor::onValuesCellChanged(int row,int col)
|
||||
{
|
||||
if (inhibitCellChanged) return;
|
||||
|
||||
if (row == ui->valuesTable->rowCount() - 1)
|
||||
{
|
||||
DBC_VAL newVal;
|
||||
newVal.value = 0;
|
||||
newVal.descript = "No Description";
|
||||
currentSignal->valList.append(newVal);
|
||||
qDebug() << "Created new entry in value list";
|
||||
|
||||
//QTableWidgetItem *widgetVal = new QTableWidgetItem(QString::number(newVal.value));
|
||||
//ui->valuesTable->setItem(row, 0, widgetVal);
|
||||
//QTableWidgetItem *widgetDesc = new QTableWidgetItem(newVal.descript);
|
||||
//ui->valuesTable->setItem(row, 1, widgetDesc);
|
||||
|
||||
//add the blank at the end again
|
||||
ui->valuesTable->insertRow(ui->valuesTable->rowCount());
|
||||
}
|
||||
|
||||
if (col == 0)
|
||||
{
|
||||
currentSignal->valList[row].value = Utility::ParseStringToNum(ui->valuesTable->item(row, col)->text());
|
||||
}
|
||||
else if (col == 1)
|
||||
{
|
||||
currentSignal->valList[row].descript = ui->valuesTable->item(row, col)->text();
|
||||
}
|
||||
}
|
||||
|
||||
void DBCSignalEditor::onCustomMenuSignals(QPoint point)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
menu->addAction(tr("Add a new signal"), this, SLOT(addNewSignal()));
|
||||
menu->addAction(tr("Delete currently selected signal"), this, SLOT(deleteCurrentSignal()));
|
||||
|
||||
menu->popup(ui->signalsList->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void DBCSignalEditor::onCustomMenuValues(QPoint point)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
menu->addAction(tr("Delete currently selected value"), this, SLOT(deleteCurrentValue()));
|
||||
|
||||
menu->popup(ui->valuesTable->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void DBCSignalEditor::addNewSignal()
|
||||
{
|
||||
int num = qrand() % 70000;
|
||||
QString newName = "SIGNAL" + QString::number(num);
|
||||
DBC_SIGNAL newSig;
|
||||
newSig.name = newName;
|
||||
newSig.bias = 0.0;
|
||||
newSig.factor = 1.0;
|
||||
newSig.intelByteOrder = true;
|
||||
newSig.max = 0.0;
|
||||
newSig.min = 0.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;
|
||||
|
||||
dbcMessage->sigHandler->addSignal(newSig);
|
||||
|
||||
/* add item at the end of the list and select it */
|
||||
/* this will call clickSignalList */
|
||||
ui->signalsList->addItem(newName);
|
||||
ui->signalsList->setCurrentRow(ui->signalsList->count()-1);
|
||||
|
||||
}
|
||||
|
||||
void DBCSignalEditor::deleteCurrentSignal()
|
||||
{
|
||||
int currIdx = ui->signalsList->currentRow();
|
||||
|
||||
if(currIdx==ui->signalsList->count()-1)
|
||||
return;
|
||||
|
||||
if (currIdx > -1)
|
||||
{
|
||||
delete(ui->signalsList->item(currIdx));
|
||||
dbcMessage->sigHandler->removeSignal(currIdx);
|
||||
currentSignal = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void DBCSignalEditor::deleteCurrentValue()
|
||||
{
|
||||
int currIdx = ui->valuesTable->currentRow();
|
||||
if (currIdx > -1)
|
||||
{
|
||||
ui->valuesTable->removeRow(currIdx);
|
||||
currentSignal->valList.removeAt(currIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void DBCSignalEditor::refreshSignalsList()
|
||||
{
|
||||
ui->signalsList->clear();
|
||||
|
||||
|
||||
for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++)
|
||||
{
|
||||
DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x);
|
||||
ui->signalsList->addItem(sig->name);
|
||||
}
|
||||
|
||||
if( ui->signalsList->count()>0 ) {
|
||||
/* click first element */
|
||||
ui->signalsList->setCurrentRow(0);
|
||||
}
|
||||
else
|
||||
clickSignalList(-1);
|
||||
}
|
||||
|
||||
/* fillSignalForm also handles group "enabled" state */
|
||||
/* WARNING: fillSignalForm can be called recursively since it is in the listener of cbIntelFormat */
|
||||
void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig)
|
||||
{
|
||||
unsigned char bitpattern[8];
|
||||
|
||||
if (sig == NULL) {
|
||||
ui->groupBox->setEnabled(false);
|
||||
ui->txtName->setText("");
|
||||
ui->txtBias->setText("");
|
||||
ui->txtBitLength->setText("");
|
||||
ui->txtComment->setText("");
|
||||
ui->txtMaxVal->setText("");
|
||||
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);
|
||||
ui->comboReceiver->setCurrentIndex(0);
|
||||
ui->comboType->setCurrentIndex(0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* we have a signal */
|
||||
ui->groupBox->setEnabled(true);
|
||||
|
||||
generateUsedBits();
|
||||
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.
|
||||
|
||||
int startBit, endBit;
|
||||
|
||||
startBit = sig->startBit;
|
||||
|
||||
bitpattern[startBit / 8] |= 1 << (startBit % 8); //make the start bit a different color to set it apart
|
||||
ui->bitfield->setReference(bitpattern, false);
|
||||
|
||||
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;
|
||||
bitpattern[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;
|
||||
bitpattern[byt] |= 1 << (startBit % 8);
|
||||
size--;
|
||||
if ((startBit % 8) == 0) startBit += 15;
|
||||
else startBit--;
|
||||
if (startBit > 63) startBit = 63;
|
||||
}
|
||||
}
|
||||
|
||||
ui->bitfield->updateData(bitpattern, true);
|
||||
ui->cbIntelFormat->setChecked(sig->intelByteOrder);
|
||||
|
||||
switch (sig->valType)
|
||||
{
|
||||
case UNSIGNED_INT:
|
||||
ui->comboType->setCurrentIndex(0);
|
||||
break;
|
||||
case SIGNED_INT:
|
||||
ui->comboType->setCurrentIndex(1);
|
||||
break;
|
||||
case SP_FLOAT:
|
||||
ui->comboType->setCurrentIndex(2);
|
||||
break;
|
||||
case DP_FLOAT:
|
||||
ui->comboType->setCurrentIndex(3);
|
||||
break;
|
||||
case STRING:
|
||||
ui->comboType->setCurrentIndex(4);
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ui->comboReceiver->count(); i++)
|
||||
{
|
||||
if (ui->comboReceiver->itemText(i) == sig->receiver->name)
|
||||
{
|
||||
ui->comboReceiver->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* fillValueTable also handles "enabled" state */
|
||||
void DBCSignalEditor::fillValueTable(DBC_SIGNAL *sig)
|
||||
{
|
||||
int rowIdx;
|
||||
|
||||
inhibitCellChanged = true;
|
||||
|
||||
ui->valuesTable->clearContents();
|
||||
ui->valuesTable->setRowCount(0);
|
||||
|
||||
if (sig == NULL) {
|
||||
ui->valuesTable->setEnabled(false);
|
||||
inhibitCellChanged = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ui->valuesTable->setEnabled(true);
|
||||
|
||||
for (int i = 0; i < sig->valList.count(); i++)
|
||||
{
|
||||
QTableWidgetItem *val = new QTableWidgetItem(Utility::formatNumber(sig->valList[i].value));
|
||||
QTableWidgetItem *desc = new QTableWidgetItem(sig->valList[i].descript);
|
||||
rowIdx = ui->valuesTable->rowCount();
|
||||
ui->valuesTable->insertRow(rowIdx);
|
||||
ui->valuesTable->setItem(rowIdx, 0, val);
|
||||
ui->valuesTable->setItem(rowIdx, 1, desc);
|
||||
}
|
||||
|
||||
//Add a blank row at the end to allow for adding records easily
|
||||
ui->valuesTable->insertRow(ui->valuesTable->rowCount());
|
||||
|
||||
inhibitCellChanged = false;
|
||||
}
|
||||
|
||||
void DBCSignalEditor::clickSignalList(int row)
|
||||
{
|
||||
//qDebug() << ui->signalsList->item(row)->text();
|
||||
|
||||
DBC_SIGNAL *thisSig = (row<0) ? NULL : dbcMessage->sigHandler->findSignalByName(ui->signalsList->item(row)->text());
|
||||
currentSignal = thisSig;
|
||||
fillSignalForm(thisSig);
|
||||
fillValueTable(thisSig);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef DBCSIGNALEDITOR_H
|
||||
#define DBCSIGNALEDITOR_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "dbchandler.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace Ui {
|
||||
class DBCSignalEditor;
|
||||
}
|
||||
|
||||
class DBCSignalEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DBCSignalEditor(DBCHandler *handler, QWidget *parent = 0);
|
||||
void setMessageRef(DBC_MESSAGE *msg);
|
||||
void showEvent(QShowEvent*);
|
||||
void setFileIdx(int idx);
|
||||
~DBCSignalEditor();
|
||||
|
||||
private slots:
|
||||
void clickSignalList(int);
|
||||
void bitfieldClicked(int x, int y);
|
||||
void onValuesCellChanged(int row,int col);
|
||||
void onCustomMenuSignals(QPoint);
|
||||
void onCustomMenuValues(QPoint);
|
||||
void addNewSignal();
|
||||
void deleteCurrentSignal();
|
||||
void deleteCurrentValue();
|
||||
|
||||
private:
|
||||
Ui::DBCSignalEditor *ui;
|
||||
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();
|
||||
void writeSettings();
|
||||
};
|
||||
|
||||
#endif // DBCSIGNALEDITOR_H
|
||||
Reference in New Issue
Block a user