Reorganized source code

This commit is contained in:
Collin Kidder
2016-12-11 21:06:41 -05:00
parent d8ffbcaf5f
commit 0b46f564ab
56 changed files with 67 additions and 69 deletions
+204
View File
@@ -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;
}
+106
View File
@@ -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
+841
View File
@@ -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;
}
+95
View File
@@ -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
+153
View File
@@ -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));
}
}
+44
View File
@@ -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
+462
View File
@@ -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);
}
}
+53
View File
@@ -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
+602
View File
@@ -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);
}
+51
View File
@@ -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