2511 lines
85 KiB
C++
2511 lines
85 KiB
C++
#include "dbchandler.h"
|
|
|
|
#include <QFile>
|
|
#include <QRegularExpression>
|
|
#include <QDebug>
|
|
#include <QMessageBox>
|
|
#include <QFileDialog>
|
|
#include <QApplication>
|
|
#include <QPalette>
|
|
#include <QSettings>
|
|
#include <QJsonDocument>
|
|
#include <QJsonArray>
|
|
#include <QJsonObject>
|
|
#include "utility.h"
|
|
#include "connections/canconmanager.h"
|
|
#include <algorithm>
|
|
|
|
DBCHandler* DBCHandler::instance = nullptr;
|
|
|
|
static bool parseValueDescriptions(QString tokenString, QList<DBC_VAL_ENUM_ENTRY> &values)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
DBC_VAL_ENUM_ENTRY val;
|
|
|
|
values.clear();
|
|
tokenString = tokenString.trimmed();
|
|
while (tokenString.length() > 2)
|
|
{
|
|
regex.setPattern("^([+-]?\\d+) \\\"(.*?)\\\"(.*)");
|
|
match = regex.match(tokenString);
|
|
if (!match.hasMatch()) return false;
|
|
|
|
val.value = match.captured(1).toInt();
|
|
val.descript = match.captured(2);
|
|
values.append(val);
|
|
tokenString = match.captured(3).trimmed();
|
|
}
|
|
return values.count() > 0;
|
|
}
|
|
|
|
DBC_SIGNAL* DBCSignalHandler::findSignalByIdx(int idx)
|
|
{
|
|
if (sigs.count() == 0) return nullptr;
|
|
if (idx < 0) return nullptr;
|
|
if (idx >= sigs.count()) return nullptr;
|
|
|
|
return &sigs[idx];
|
|
}
|
|
|
|
DBC_SIGNAL* DBCSignalHandler::findSignalByName(QString name)
|
|
{
|
|
if (sigs.count() == 0) return nullptr;
|
|
for (int i = 0; i < sigs.count(); i++)
|
|
{
|
|
if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0)
|
|
{
|
|
return &sigs[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool DBCSignalHandler::addSignal(DBC_SIGNAL &sig)
|
|
{
|
|
sigs.append(sig);
|
|
return true;
|
|
}
|
|
|
|
bool DBCSignalHandler::removeSignal(DBC_SIGNAL *sig)
|
|
{
|
|
qDebug() << "Total # of signals: " << getCount();
|
|
for (int i = 0; i < getCount(); i++)
|
|
{
|
|
if (sigs[i].name == sig->name)
|
|
{
|
|
sigs.removeAt(i);
|
|
qDebug() << "Removed signal at idx " << i;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
// sort by multiplex value ascending, -1 if non multiplexed/multiplexor
|
|
static int signalMuxSortValue(DBC_SIGNAL &sig)
|
|
{
|
|
if (sig.isMultiplexed)
|
|
return sig.getSimpleMultiplexValue();
|
|
return -1;
|
|
}
|
|
|
|
bool signal_cmp(DBC_SIGNAL a, DBC_SIGNAL b)
|
|
{
|
|
int muxA = signalMuxSortValue(a);
|
|
int muxB = signalMuxSortValue(b);
|
|
if (muxA != muxB)
|
|
return muxA < muxB;
|
|
|
|
if (a.startBit == b.startBit) {
|
|
return QString::compare(a.name, b.name, Qt::CaseInsensitive) < 0;
|
|
} else {
|
|
return a.startBit < b.startBit;
|
|
}
|
|
}
|
|
|
|
void DBCSignalHandler::sort()
|
|
{
|
|
std::sort(sigs.begin(), sigs.end(), signal_cmp);
|
|
}
|
|
|
|
DBC_MESSAGE* DBCMessageHandler::findMsgByID(uint32_t id)
|
|
{
|
|
if (messages.count() == 0) return nullptr;
|
|
DBC_MESSAGE *bestMatch = nullptr;
|
|
|
|
for (int i = 0; i < messages.count(); i++)
|
|
{
|
|
if ( messages[i].ID == id )
|
|
{
|
|
return &messages[i];
|
|
}
|
|
|
|
if (matchingCriteria == J1939)
|
|
{
|
|
// include data page and extended data page in the pgn
|
|
uint32_t pgn = (id & 0x3FFFF00) >> 8;
|
|
if ( (pgn & 0xFF00) <= 0xEF00 )
|
|
{
|
|
// PDU1 format
|
|
pgn &= 0x3FF00;
|
|
if ((messages[i].ID & 0x3FF0000) == (pgn << 8))
|
|
{
|
|
bestMatch = &messages[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// PDU2 format
|
|
if ((messages[i].ID & 0x3FFFF00) == (pgn << 8))
|
|
{
|
|
bestMatch = &messages[i];
|
|
}
|
|
}
|
|
}
|
|
else if (matchingCriteria == GMLAN)
|
|
{
|
|
// Match the bits 14-26 (Arbitration Id) of GMLAN 29bit header
|
|
uint32_t arbId = id &0x3FFE000;
|
|
if ( (arbId != 0) && (messages[i].ID & 0x3FFE000) == arbId )
|
|
bestMatch = &messages[i];
|
|
}
|
|
}
|
|
return bestMatch;
|
|
}
|
|
|
|
DBC_MESSAGE* DBCMessageHandler::findMsgByIdx(int idx)
|
|
{
|
|
if (messages.count() == 0) return nullptr;
|
|
if (idx < 0) return nullptr;
|
|
if (idx >= messages.count()) return nullptr;
|
|
return &messages[idx];
|
|
}
|
|
|
|
DBC_MESSAGE* DBCMessageHandler::findMsgByName(QString name)
|
|
{
|
|
if (messages.count() == 0) return nullptr;
|
|
for (int i = 0; i < messages.count(); i++)
|
|
{
|
|
if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0)
|
|
{
|
|
return &messages[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//allow for finding a message just by part of the name
|
|
DBC_MESSAGE* DBCMessageHandler::findMsgByPartialName(QString name)
|
|
{
|
|
if (messages.count() == 0) return nullptr;
|
|
for (int i = 0; i < messages.count(); i++)
|
|
{
|
|
if (messages[i].name.contains(name, Qt::CaseInsensitive))
|
|
{
|
|
return &messages[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
QList<DBC_MESSAGE*> DBCMessageHandler::findMsgsByNode(DBC_NODE* node)
|
|
{
|
|
QList<DBC_MESSAGE*> messagesForNode;
|
|
|
|
if (messages.count() == 0)
|
|
return messagesForNode;
|
|
|
|
for (int i = 0; i < messages.count(); i++)
|
|
{
|
|
if (messages[i].sender == node)
|
|
{
|
|
messagesForNode.append(&messages[i]);
|
|
}
|
|
}
|
|
|
|
return messagesForNode;
|
|
}
|
|
|
|
bool DBCMessageHandler::addMessage(DBC_MESSAGE &msg)
|
|
{
|
|
messages.append(msg);
|
|
return true;
|
|
}
|
|
|
|
bool DBCMessageHandler::removeMessage(DBC_MESSAGE *msg)
|
|
{
|
|
qDebug() << "Total # of messages: " << getCount();
|
|
for (int i = 0; i < getCount(); i++)
|
|
{
|
|
if (messages[i].name == msg->name)
|
|
{
|
|
messages.removeAt(i);
|
|
qDebug() << "Removed message at idx " << i;
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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(uint32_t 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();
|
|
}
|
|
|
|
bool msg_cmp(DBC_MESSAGE a, DBC_MESSAGE b)
|
|
{
|
|
return a.ID < b.ID;
|
|
}
|
|
|
|
void DBCMessageHandler::sort()
|
|
{
|
|
std::sort(messages.begin(), messages.end(), msg_cmp);
|
|
for (int i = 0; i < messages.count(); i++)
|
|
{
|
|
messages[i].sigHandler->sort();
|
|
}
|
|
}
|
|
|
|
bool DBCMessageHandler::filterLabeling()
|
|
{
|
|
return filterLabelingEnabled;
|
|
}
|
|
|
|
void DBCMessageHandler::setFilterLabeling(bool filterLabeling)
|
|
{
|
|
filterLabelingEnabled = filterLabeling;
|
|
}
|
|
|
|
MatchingCriteria_t DBCMessageHandler::getMatchingCriteria()
|
|
{
|
|
return matchingCriteria;
|
|
}
|
|
|
|
void DBCMessageHandler::setMatchingCriteria(MatchingCriteria_t _matchingCriteria)
|
|
{
|
|
matchingCriteria = _matchingCriteria;
|
|
}
|
|
|
|
DBCFile::DBCFile()
|
|
{
|
|
messageHandler = new DBCMessageHandler;
|
|
messageHandler->setMatchingCriteria(EXACT);
|
|
messageHandler->setFilterLabeling(false);
|
|
isDirty = false;
|
|
fileName = "<Unsaved File>";
|
|
}
|
|
|
|
DBCFile::DBCFile(const DBCFile& cpy) : QObject()
|
|
{
|
|
messageHandler = new DBCMessageHandler;
|
|
for (int i = 0 ; i < cpy.messageHandler->getCount() ; i++)
|
|
messageHandler->addMessage(*cpy.messageHandler->findMsgByIdx(i));
|
|
|
|
messageHandler->setMatchingCriteria(cpy.messageHandler->getMatchingCriteria());
|
|
messageHandler->setFilterLabeling(cpy.messageHandler->filterLabeling());
|
|
fileName = cpy.fileName;
|
|
filePath = cpy.filePath;
|
|
assocBuses = cpy.assocBuses;
|
|
dbc_nodes.clear();
|
|
dbc_nodes.append(cpy.dbc_nodes);
|
|
dbc_value_tables.clear();
|
|
dbc_value_tables.append(cpy.dbc_value_tables);
|
|
dbc_attributes.clear();
|
|
dbc_attributes.append(cpy.dbc_attributes);
|
|
isDirty = cpy.isDirty;
|
|
}
|
|
|
|
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);
|
|
dbc_value_tables.clear();
|
|
dbc_value_tables.append(cpy.dbc_value_tables);
|
|
dbc_attributes.clear();
|
|
dbc_attributes.append(cpy.dbc_attributes);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void DBCFile::sort()
|
|
{
|
|
std::sort(dbc_nodes.begin(), dbc_nodes.end()); //sort node names
|
|
messageHandler->sort(); //sort messages, each of which sorts its signals too
|
|
}
|
|
|
|
DBC_NODE* DBCFile::findNodeByIdx(int idx)
|
|
{
|
|
if (idx < 0) return nullptr;
|
|
if (idx >= dbc_nodes.count()) return nullptr;
|
|
return &dbc_nodes[idx];
|
|
}
|
|
|
|
DBC_NODE* DBCFile::findNodeByName(QString name)
|
|
{
|
|
if (dbc_nodes.length() == 0) return nullptr;
|
|
for (int i = 0; i < dbc_nodes.length(); i++)
|
|
{
|
|
if (name.compare(dbc_nodes[i].name, Qt::CaseInsensitive) == 0)
|
|
{
|
|
return &dbc_nodes[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
DBC_NODE* DBCFile::findNodeByNameAndComment(QString fullname)
|
|
{
|
|
QString nameAndComment;
|
|
if (dbc_nodes.length() == 0) return nullptr;
|
|
for (int i = 0; i < dbc_nodes.length(); i++)
|
|
{
|
|
if(dbc_nodes[i].comment.isEmpty())
|
|
nameAndComment = dbc_nodes[i].name;
|
|
else
|
|
nameAndComment = dbc_nodes[i].name + " - " + dbc_nodes[i].comment;
|
|
|
|
if (fullname.compare(nameAndComment, Qt::CaseInsensitive) == 0)
|
|
{
|
|
return &dbc_nodes[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
DBC_VAL_TABLE* DBCFile::findValueTableByName(QString name)
|
|
{
|
|
if (dbc_value_tables.length() == 0) return nullptr;
|
|
for (int i = 0; i < dbc_value_tables.length(); i++)
|
|
{
|
|
if (name.compare(dbc_value_tables[i].name, Qt::CaseInsensitive) == 0)
|
|
{
|
|
return &dbc_value_tables[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
QString DBCFile::getFullFilename()
|
|
{
|
|
return filePath + fileName;
|
|
}
|
|
|
|
QString DBCFile::getFilename()
|
|
{
|
|
return fileName;
|
|
}
|
|
|
|
QString DBCFile::getFilenameNoExt()
|
|
{
|
|
return fileName.split(".dbc")[0];
|
|
}
|
|
|
|
QString DBCFile::getPath()
|
|
{
|
|
return filePath;
|
|
}
|
|
|
|
int DBCFile::getAssocBus()
|
|
{
|
|
return assocBuses;
|
|
}
|
|
|
|
void DBCFile::setAssocBus(int bus)
|
|
{
|
|
if (bus < -1) return;
|
|
// To allow setting bus numbers even before connection is configured, do not enforce "valid" bus numbers
|
|
//int numBuses = CANConManager::getInstance()->getNumBuses();
|
|
//if (bus >= numBuses) return;
|
|
assocBuses = bus;
|
|
}
|
|
|
|
DBC_ATTRIBUTE *DBCFile::findAttributeByName(QString name, DBC_ATTRIBUTE_TYPE type)
|
|
{
|
|
if (dbc_attributes.length() == 0) return nullptr;
|
|
for (int i = 0; i < dbc_attributes.length(); i++)
|
|
{
|
|
if (dbc_attributes[i].name.compare(name, Qt::CaseInsensitive) == 0 && ((type == dbc_attributes[i].attrType) || (type == ATTR_TYPE_ANY) ) )
|
|
{
|
|
return &dbc_attributes[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
DBC_ATTRIBUTE *DBCFile::findAttributeByIdx(int idx)
|
|
{
|
|
if (idx < 0) return nullptr;
|
|
if (idx >= dbc_attributes.count()) return nullptr;
|
|
return &dbc_attributes[idx];
|
|
}
|
|
|
|
void DBCFile::findAttributesByType(DBC_ATTRIBUTE_TYPE typ, QList<DBC_ATTRIBUTE> *list)
|
|
{
|
|
if (!list) return;
|
|
list->clear();
|
|
foreach (DBC_ATTRIBUTE attr, dbc_attributes)
|
|
{
|
|
if (attr.attrType == typ) list->append(attr);
|
|
}
|
|
}
|
|
|
|
void DBCFile::setDirtyFlag()
|
|
{
|
|
isDirty = true;
|
|
}
|
|
|
|
//BE CAREFUL HERE. Do not clear the dirty flag unless you're absolutely sure nothing has changed.
|
|
//Currently the signal editor clears this flag if the entire undo buffer is emptied but still
|
|
//it's possible that signals or messages were deleted or added so this is potentially not that safe
|
|
//It would be better if every node, message, and signal had a dirty flag. Then the DBCFile getDirtyFlag
|
|
//function could traverse the tree and see if anything is dirty.
|
|
void DBCFile::clearDirtyFlag()
|
|
{
|
|
isDirty = false;
|
|
}
|
|
|
|
bool DBCFile::getDirtyFlag()
|
|
{
|
|
return isDirty;
|
|
}
|
|
|
|
DBC_MESSAGE* DBCFile::parseMessageLine(QString line)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
DBC_MESSAGE *msgPtr;
|
|
|
|
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;
|
|
uint32_t ID = match.captured(1).toULong(); //the ID is always stored in decimal format
|
|
msg.ID = ID & 0x1FFFFFFFul;
|
|
msg.extendedID = (ID & 0x80000000ul) ? true : false;
|
|
msg.name = match.captured(2);
|
|
msg.len = match.captured(3).toUInt();
|
|
msg.sender = findNodeByName(match.captured(4));
|
|
if (!msg.sender) msg.sender = findNodeByIdx(0);
|
|
messageHandler->addMessage(msg);
|
|
msgPtr = messageHandler->findMsgByID(msg.ID);
|
|
}
|
|
else msgPtr = nullptr;
|
|
return msgPtr;
|
|
}
|
|
|
|
DBC_SIGNAL* DBCFile::parseSignalLine(QString line, DBC_MESSAGE *msg)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
int offset = 0;
|
|
bool isMessageMultiplexor = false;
|
|
//bool isMultiplexed = false;
|
|
DBC_SIGNAL sig;
|
|
|
|
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";
|
|
isMessageMultiplexor = 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";
|
|
sig.addMultiplexRange(match.captured(2).toInt(), match.captured(2).toInt());
|
|
offset = 1;
|
|
}
|
|
else
|
|
{
|
|
regex.setPattern("^SG\\_ *([-\\w]+) +m(\\d+)M *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
|
|
match = regex.match(line);
|
|
if (match.hasMatch())
|
|
{
|
|
qDebug() << "Extended Multiplexor Signal";
|
|
sig.isMultiplexor = true; //we don't set the local isMessageMultiplexor variable because this isn't the top level multiplexor
|
|
sig.addMultiplexRange(match.captured(2).toInt(), match.captured(2).toInt());
|
|
offset = 1;
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "standard signal";
|
|
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;
|
|
case 5: //single point float in little endian
|
|
sig.valType = SP_FLOAT;
|
|
sig.intelByteOrder = true;
|
|
break;
|
|
case 6: //double point float in little endian
|
|
sig.valType = DP_FLOAT;
|
|
sig.intelByteOrder = true;
|
|
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 + offset).split(',')[0];
|
|
sig.receiver = findNodeByName(tmp);
|
|
}
|
|
else sig.receiver = findNodeByName(match.captured(11 + offset));
|
|
|
|
if (!sig.receiver) sig.receiver = findNodeByIdx(0); //apply default if there was no match
|
|
|
|
sig.parentMessage = msg;
|
|
if (msg)
|
|
{
|
|
msg->sigHandler->addSignal(sig);
|
|
if (isMessageMultiplexor) msg->multiplexorSignal = msg->sigHandler->findSignalByName(sig.name);
|
|
return msg->sigHandler->findSignalByName(sig.name);
|
|
}
|
|
else return nullptr;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//SG_MUL_VAL_ 2024 S1_PID_0D_VehicleSpeed S1 13-13;
|
|
bool DBCFile::parseSignalMultiplexValueLine(QString line)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
qDebug() << "Found a multiplex definition line";
|
|
regex.setPattern("^SG\\_MUL\\_VAL\\_ (\\d+) ([-\\w]+) ([-\\w]+) ((?:\\d+\\-\\d+[\\s]*[\\,]?[\\s]*)*)");
|
|
match = regex.match(line);
|
|
//captured 1 is message ID
|
|
//Captured 2 is signal name
|
|
//Captured 3 is parent multiplexor
|
|
//captured 4 is the lower bound
|
|
//captured 5 is the upper bound
|
|
if (match.hasMatch())
|
|
{
|
|
DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toULong() & 0x1FFFFFFFUL);
|
|
if (msg != nullptr)
|
|
{
|
|
DBC_SIGNAL *thisSignal = msg->sigHandler->findSignalByName(match.captured(2));
|
|
if (thisSignal != nullptr)
|
|
{
|
|
DBC_SIGNAL *parentSignal = msg->sigHandler->findSignalByName(match.captured(3));
|
|
if (parentSignal != nullptr)
|
|
{
|
|
const QStringList ranges = match.captured(4).split(QChar(','), Qt::SkipEmptyParts);
|
|
for (const QString &range : ranges) {
|
|
//now need to add "thisSignal" to the children multiplexed signals of "parentSignal"
|
|
const QStringList rangeSides = range.trimmed().split(QChar('-'));
|
|
if (rangeSides.count() != 2) {
|
|
qDebug() << QString("Malformed range definition: '%2' found in the multiplexed signal: %1")
|
|
.arg(match.captured(1).arg(range.trimmed()));
|
|
return false;
|
|
}
|
|
int rangeMin = rangeSides.at(0).toInt();
|
|
int rangeMax = rangeSides.at(1).toInt();
|
|
thisSignal->multiplexParent = parentSignal;
|
|
thisSignal->addMultiplexRange(rangeMin, rangeMax);
|
|
}
|
|
parentSignal->multiplexedChildren.append(thisSignal);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DBCFile::parseSignalValueTypeLine(QString line)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
qDebug() << "Found a signal valtype line";
|
|
regex.setPattern("^SIG\\_VALTYPE\\_ *(\\d+) *([-\\w]+) *: *(\\d+);");
|
|
match = regex.match(line);
|
|
|
|
// captured 1 is the message id
|
|
// captured 2 is the signal name
|
|
// captured 3 is the valtype
|
|
if (!match.hasMatch()) { return false; }
|
|
uint32_t id = match.captured(1).toULong() & 0x1FFFFFFFUL;
|
|
|
|
DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toULong() & 0x1FFFFFFFUL);
|
|
if (msg == nullptr) { return false; }
|
|
|
|
DBC_SIGNAL *thisSignal = msg->sigHandler->findSignalByName(match.captured(2));
|
|
if (thisSignal == nullptr) { return false; }
|
|
int valType = match.captured(3).toInt();
|
|
|
|
switch (valType) {
|
|
case 1: {
|
|
thisSignal->valType = SP_FLOAT;
|
|
break;
|
|
}
|
|
case 2: {
|
|
thisSignal->valType = DP_FLOAT;
|
|
break;
|
|
}
|
|
default: {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool DBCFile::parseValueTableLine(QString line)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
regex.setPattern("^VAL\\_TABLE\\_ ([-\\w]+) (.*);");
|
|
match = regex.match(line);
|
|
//captured 1 is the table name
|
|
//captured 2 is a series of values in the form (number "text")
|
|
if (match.hasMatch())
|
|
{
|
|
DBC_VAL_TABLE table;
|
|
table.name = match.captured(1);
|
|
if (!parseValueDescriptions(match.captured(2), table.valList)) return false;
|
|
|
|
DBC_VAL_TABLE *existingTable = findValueTableByName(table.name);
|
|
if (existingTable) existingTable->valList = table.valList;
|
|
else dbc_value_tables.append(table);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DBCFile::parseValueLine(QString line)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
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).toULong() & 0x1FFFFFFFul);
|
|
if (msg != nullptr)
|
|
{
|
|
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2));
|
|
if (sig != nullptr)
|
|
{
|
|
QString tokenString = match.captured(3);
|
|
QList<DBC_VAL_ENUM_ENTRY> values;
|
|
DBC_VAL_TABLE *valueTable = findValueTableByName(tokenString.trimmed());
|
|
if (valueTable)
|
|
{
|
|
sig->valList = valueTable->valList;
|
|
return true;
|
|
}
|
|
if (parseValueDescriptions(tokenString, values))
|
|
{
|
|
sig->valList.append(values);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DBCFile::parseAttributeLine(QString line)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
regex.setPattern("^BA\\_ \\\"*([-\\w]+)\\\"* BO\\_ (\\d+) \\\"*([#\\w]+)\\\"*");
|
|
match = regex.match(line);
|
|
//captured 1 is the attribute name
|
|
//captured 2 is the message ID number (frame ID)
|
|
//captured 3 is the attribute value
|
|
if (match.hasMatch())
|
|
{
|
|
qDebug() << "Found an attribute setting line for a message";
|
|
DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1));
|
|
if (foundAttr)
|
|
{
|
|
qDebug() << "That message attribute does exist";
|
|
DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toULong() & 0x1FFFFFFFul);
|
|
if (foundMsg)
|
|
{
|
|
qDebug() << "It references a valid, registered message";
|
|
DBC_ATTRIBUTE_VALUE *foundAttrVal = foundMsg->findAttrValByName(match.captured(1));
|
|
if (foundAttrVal) {
|
|
foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType);
|
|
}
|
|
else
|
|
{
|
|
DBC_ATTRIBUTE_VALUE val;
|
|
val.attrName = match.captured(1);
|
|
val.value = processAttributeVal(match.captured(3), foundAttr->valType);
|
|
foundMsg->attributes.append(val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
regex.setPattern("^BA\\_ \\\"*([-\\w]+)\\\"* SG\\_ (\\d+) \\\"*([-\\w]+)\\\"* \\\"*([#\\w]+)\\\"*");
|
|
match = regex.match(line);
|
|
//captured 1 is the attribute name
|
|
//captured 2 is the message ID number (frame ID)
|
|
//captured 3 is the signal name to bind to
|
|
//captured 4 is the attribute value
|
|
if (match.hasMatch())
|
|
{
|
|
qDebug() << "Found an attribute setting line for a signal";
|
|
DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1));
|
|
if (foundAttr)
|
|
{
|
|
qDebug() << "That signal attribute does exist";
|
|
DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toULong() & 0x1FFFFFFFUL);
|
|
if (foundMsg)
|
|
{
|
|
qDebug() << "It references a valid, registered message";
|
|
DBC_SIGNAL *foundSig = foundMsg->sigHandler->findSignalByName(match.captured(3));
|
|
if (foundSig)
|
|
{
|
|
DBC_ATTRIBUTE_VALUE *foundAttrVal = foundSig->findAttrValByName(match.captured(1));
|
|
if (foundAttrVal) foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType);
|
|
else
|
|
{
|
|
DBC_ATTRIBUTE_VALUE val;
|
|
val.attrName = match.captured(1);
|
|
val.value = processAttributeVal(match.captured(3), foundAttr->valType);
|
|
foundSig->attributes.append(val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
regex.setPattern("^BA\\_ \\\"*([-\\w]+)\\\"* BU\\_ \\\"*([-\\w]+)\\\"* \\\"*([#\\w]+)\\\"*");
|
|
match = regex.match(line);
|
|
//captured 1 is the attribute name
|
|
//captured 2 is the name of the node
|
|
//captured 3 is the attribute value
|
|
if (match.hasMatch())
|
|
{
|
|
qDebug() << "Found an attribute setting line for a node";
|
|
DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1));
|
|
if (foundAttr)
|
|
{
|
|
qDebug() << "That node attribute does exist";
|
|
DBC_NODE *foundNode = findNodeByName(match.captured(2));
|
|
if (foundNode)
|
|
{
|
|
qDebug() << "References a valid node name";
|
|
DBC_ATTRIBUTE_VALUE *foundAttrVal = foundNode->findAttrValByName(match.captured(1));
|
|
if (foundAttrVal) foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType);
|
|
else
|
|
{
|
|
DBC_ATTRIBUTE_VALUE val;
|
|
val.attrName = match.captured(1);
|
|
val.value = processAttributeVal(match.captured(3), foundAttr->valType);
|
|
foundNode->attributes.append(val);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DBCFile::parseDefaultAttrLine(QString line)
|
|
{
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
regex.setPattern("^BA\\_DEF\\_DEF\\_ \\\"*([-\\w]+)\\\"* \\\"*([#\\w]*)\\\"*");
|
|
match = regex.match(line);
|
|
//captured 1 is the name of the attribute
|
|
//captured 2 is the default value for that attribute
|
|
if (match.hasMatch())
|
|
{
|
|
qDebug() << "Found an attribute default value line, searching for an attribute named " << match.captured(1) << "with data " << match.captured(2);
|
|
DBC_ATTRIBUTE *found = findAttributeByName(match.captured(1));
|
|
if (found)
|
|
{
|
|
switch (found->valType)
|
|
{
|
|
case ATTR_STRING:
|
|
found->defaultValue = match.captured(2);
|
|
break;
|
|
case ATTR_FLOAT:
|
|
found->defaultValue = match.captured(2).toFloat();
|
|
break;
|
|
case ATTR_INT:
|
|
found->defaultValue = match.captured(2).toInt();
|
|
break;
|
|
case ATTR_ENUM:
|
|
QString temp = match.captured(2);
|
|
found->defaultValue = 0;
|
|
for (int x = 0; x < found->enumVals.count(); x++)
|
|
{
|
|
if (!found->enumVals[x].compare(temp, Qt::CaseInsensitive))
|
|
{
|
|
found->defaultValue = x;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
qDebug() << "Matched an attribute. Setting default value to " << found->defaultValue;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DBCFile::loadFile(QString fileName)
|
|
{
|
|
QFile *inFile = new QFile(fileName);
|
|
QString line, rawLine;
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
DBC_MESSAGE *currentMessage = nullptr;
|
|
DBC_ATTRIBUTE attr;
|
|
int numSigFaults = 0, numMsgFaults = 0;
|
|
int linesSinceYield = 0;
|
|
QString fileBaseName = QFileInfo(fileName).baseName();
|
|
|
|
bool inMultilineBU = false;
|
|
bool inMultilineValTable = false;
|
|
QString multilineValTable;
|
|
|
|
qDebug() << "DBC File: " << fileName;
|
|
|
|
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
|
|
{
|
|
delete inFile;
|
|
qDebug() << "Could not load the file!";
|
|
return false;
|
|
}
|
|
|
|
qDebug() << "Starting DBC load";
|
|
dbc_nodes.clear();
|
|
dbc_value_tables.clear();
|
|
messageHandler->removeAllMessages();
|
|
messageHandler->setMatchingCriteria(EXACT);
|
|
messageHandler->setFilterLabeling(false);
|
|
|
|
DBC_NODE falseNode;
|
|
falseNode.name = "Vector__XXX";
|
|
falseNode.comment = "Default node if none specified";
|
|
dbc_nodes.append(falseNode);
|
|
|
|
while (!inFile->atEnd()) {
|
|
rawLine = QString(inFile->readLine());
|
|
line = rawLine.simplified();
|
|
|
|
linesSinceYield++;
|
|
if (linesSinceYield > 100)
|
|
{
|
|
qApp->processEvents();
|
|
}
|
|
|
|
if (inMultilineBU)
|
|
{
|
|
if (rawLine.startsWith("\t") || rawLine.startsWith(" "))
|
|
{
|
|
DBC_NODE node;
|
|
node.sourceFileName = fileBaseName;
|
|
node.name = line;
|
|
dbc_nodes.append(node);
|
|
}
|
|
else inMultilineBU = false;
|
|
}
|
|
|
|
if (inMultilineValTable)
|
|
{
|
|
multilineValTable.append(" " + line);
|
|
if (line.endsWith(";"))
|
|
{
|
|
parseValueTableLine(multilineValTable);
|
|
multilineValTable.clear();
|
|
inMultilineValTable = false;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!inMultilineBU)
|
|
{
|
|
if (line.startsWith("BO_ ")) //defines a message
|
|
{
|
|
currentMessage = parseMessageLine(line);
|
|
if (currentMessage == nullptr) numMsgFaults++;
|
|
}
|
|
if (line.startsWith("SG_ ")) //defines a signal
|
|
{
|
|
if (!parseSignalLine(line, currentMessage)) numSigFaults++;
|
|
}
|
|
|
|
if (line.startsWith("SG_MUL_VAL_ ")) //defines a signal multiplexing value definition
|
|
{
|
|
if (!parseSignalMultiplexValueLine(line)) numSigFaults++;
|
|
}
|
|
|
|
if (line.startsWith("SIG_VALTYPE_ ")) //defines a signal value type
|
|
{
|
|
if (!parseSignalValueTypeLine(line)) 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.sourceFileName = fileBaseName;
|
|
node.name = nodeStrings[i];
|
|
dbc_nodes.append(node);
|
|
}
|
|
}
|
|
inMultilineBU = true; //we might be... Need to check next line.
|
|
}
|
|
}
|
|
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).toULong() & 0x1FFFFFFFul);
|
|
if (msg != nullptr)
|
|
{
|
|
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2));
|
|
if (sig != nullptr)
|
|
{
|
|
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).toULong() & 0x1FFFFFFFul);
|
|
if (msg != nullptr)
|
|
{
|
|
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 != nullptr)
|
|
{
|
|
node->comment = match.captured(2);
|
|
}
|
|
}
|
|
}
|
|
if (line.startsWith("VAL_TABLE_ "))
|
|
{
|
|
if (line.endsWith(";"))
|
|
{
|
|
parseValueTableLine(line);
|
|
}
|
|
else
|
|
{
|
|
multilineValTable = line;
|
|
inMultilineValTable = true;
|
|
}
|
|
}
|
|
|
|
//VAL_ (1090) (VCUPresentParkLightOC) (1 "Error present" 0 "Error not present") ;
|
|
if (line.startsWith("VAL_ "))
|
|
{
|
|
parseValueLine(line);
|
|
}
|
|
|
|
if (line.startsWith("BA_DEF_ SG_ "))
|
|
{
|
|
qDebug() << "Found a SG attribute line";
|
|
|
|
if (parseAttribute(line.right(line.length() - 12), attr))
|
|
{
|
|
//qDebug() << "Success";
|
|
attr.attrType = ATTR_TYPE_SIG;
|
|
dbc_attributes.append(attr);
|
|
}
|
|
}
|
|
else if (line.startsWith("BA_DEF_ BO_ ")) //definition of a message attribute
|
|
{
|
|
qDebug() << "Found a BO attribute line";
|
|
|
|
if (parseAttribute(line.right(line.length() - 12), attr))
|
|
{
|
|
qDebug() << "Success";
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
dbc_attributes.append(attr);
|
|
}
|
|
}
|
|
else if (line.startsWith("BA_DEF_ BU_ ")) //definition of a node attribute
|
|
{
|
|
qDebug() << "Found a BU attribute line";
|
|
|
|
if (parseAttribute(line.right(line.length() - 12), attr))
|
|
{
|
|
//qDebug() << "Success";
|
|
attr.attrType = ATTR_TYPE_NODE;
|
|
dbc_attributes.append(attr);
|
|
}
|
|
}
|
|
|
|
else if (line.startsWith("BA_DEF_ ")) //definition of a root attribute
|
|
{
|
|
qDebug() << "Found a BU attribute line";
|
|
|
|
if (parseAttribute(line.right(line.length() - 9), attr))
|
|
{
|
|
//qDebug() << "Success";
|
|
attr.attrType = ATTR_TYPE_GENERAL;
|
|
dbc_attributes.append(attr);
|
|
}
|
|
}
|
|
|
|
|
|
if (line.startsWith("BA_DEF_DEF_ ")) //definition of default value for an attribute
|
|
{
|
|
parseDefaultAttrLine(line);
|
|
}
|
|
|
|
//BA_ "GenMsgCycleTime" BO_ 101 100;
|
|
if (line.startsWith("BA_ ")) //set value of attribute
|
|
{
|
|
parseAttributeLine(line);
|
|
}
|
|
}
|
|
}
|
|
|
|
//upon loading the file add our custom foreground and background color attributes if they don't exist already
|
|
if (!findAttributeByName("GenMsgBackgroundColor"))
|
|
{
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
attr.defaultValue = QApplication::palette().color(QPalette::Base).name();
|
|
attr.enumVals.clear();
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.name = "GenMsgBackgroundColor";
|
|
attr.valType = ATTR_STRING;
|
|
dbc_attributes.append(attr);
|
|
}
|
|
|
|
if (!findAttributeByName("GenMsgForegroundColor"))
|
|
{
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
attr.defaultValue = QApplication::palette().color(QPalette::WindowText).name();
|
|
attr.enumVals.clear();
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.name = "GenMsgForegroundColor";
|
|
attr.valType = ATTR_STRING;
|
|
dbc_attributes.append(attr);
|
|
}
|
|
|
|
DBC_ATTRIBUTE *mc_attr = findAttributeByName("matchingcriteria");
|
|
if (mc_attr)
|
|
{
|
|
messageHandler->setMatchingCriteria((MatchingCriteria_t)mc_attr->defaultValue.toInt());
|
|
}
|
|
else
|
|
{
|
|
messageHandler->setMatchingCriteria(EXACT);
|
|
}
|
|
|
|
DBC_ATTRIBUTE *fl_attr = findAttributeByName("filterlabeling");
|
|
if (fl_attr)
|
|
{
|
|
messageHandler->setFilterLabeling(fl_attr->defaultValue.toInt());
|
|
}
|
|
else
|
|
{
|
|
messageHandler->setFilterLabeling(false);
|
|
}
|
|
|
|
QColor DefaultBG = QColor(findAttributeByName("GenMsgBackgroundColor")->defaultValue.toString());
|
|
QColor DefaultFG = QColor(findAttributeByName("GenMsgForegroundColor")->defaultValue.toString());
|
|
|
|
for (int x = 0; x < messageHandler->getCount(); x++)
|
|
{
|
|
DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x);
|
|
msg->bgColor = DefaultBG;
|
|
msg->fgColor = DefaultFG;
|
|
DBC_ATTRIBUTE_VALUE *thisBG = msg->findAttrValByName("GenMsgBackgroundColor");
|
|
DBC_ATTRIBUTE_VALUE *thisFG = msg->findAttrValByName("GenMsgForegroundColor");
|
|
if (thisBG) msg->bgColor = QColor(thisBG->value.toString());
|
|
if (thisFG) msg->fgColor = QColor(thisFG->value.toString());
|
|
for (int y = 0; y < msg->sigHandler->getCount(); y++)
|
|
{
|
|
DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(y);
|
|
//if this doesn't have a multiplex parent set but is multiplexed then it must have used
|
|
//simple multiplexing instead of any extended specification. So, fill in the multiplexor signal here
|
|
//and also write the extended entry for it too.
|
|
if (sig->isMultiplexed && (sig->multiplexParent == nullptr) && (msg->multiplexorSignal) )
|
|
{
|
|
sig->multiplexParent = msg->multiplexorSignal;
|
|
msg->multiplexorSignal->multiplexedChildren.append(sig);
|
|
}
|
|
if ( sig->isMultiplexed && (!msg->multiplexorSignal) ) //marked multiplexed but there is no multiplexor.
|
|
{
|
|
sig->isMultiplexed = false; //can't multiplex if there is no multiplexor!
|
|
}
|
|
}
|
|
}
|
|
|
|
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.\n\n";
|
|
msg += "All other entries are, however, 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;
|
|
isDirty = false;
|
|
return true;
|
|
}
|
|
|
|
QVariant DBCFile::processAttributeVal(QString input, DBC_ATTRIBUTE_VAL_TYPE typ)
|
|
{
|
|
QVariant out;
|
|
switch (typ)
|
|
{
|
|
case ATTR_STRING:
|
|
out = input;
|
|
break;
|
|
case ATTR_FLOAT:
|
|
out = input.toFloat();
|
|
break;
|
|
case ATTR_INT:
|
|
case ATTR_ENUM:
|
|
out = input.toInt();
|
|
break;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
bool DBCFile::parseAttribute(QString inpString, DBC_ATTRIBUTE &attr)
|
|
{
|
|
bool goodAttr = false;
|
|
QRegularExpression regex;
|
|
QRegularExpressionMatch match;
|
|
|
|
regex.setPattern("\\\"*(\\w+)\\\"* \\\"*(\\w+)\\\"* (\\d+) (\\d+)");
|
|
match = regex.match(inpString);
|
|
//captured 1 is the name of the attribute to set up
|
|
//captured 2 is the type of signal attribute to create.
|
|
//captured 3 is the lower bound value for this attribute
|
|
//captured 4 is the upper bound value for this attribute
|
|
if (match.hasMatch())
|
|
{
|
|
qDebug() << "Parsing an attribute with low/high values";
|
|
attr.name = match.captured(1);
|
|
QString typ = match.captured(2);
|
|
attr.attrType = ATTR_TYPE_SIG;
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.valType = ATTR_STRING;
|
|
if (!typ.compare("INT", Qt::CaseInsensitive))
|
|
{
|
|
qDebug() << "INT attribute named " << attr.name;
|
|
attr.valType = ATTR_INT;
|
|
attr.lower = match.captured(3).toInt();
|
|
attr.upper = match.captured(4).toInt();
|
|
goodAttr = true;
|
|
}
|
|
if (!typ.compare("FLOAT", Qt::CaseInsensitive))
|
|
{
|
|
qDebug() << "FLOAT attribute named " << attr.name;
|
|
attr.valType = ATTR_FLOAT;
|
|
attr.lower = match.captured(3).toDouble();
|
|
attr.upper = match.captured(4).toDouble();
|
|
goodAttr = true;
|
|
}
|
|
if (!typ.compare("STRING", Qt::CaseInsensitive))
|
|
{
|
|
qDebug() << "STRING attribute named " << attr.name;
|
|
attr.valType = ATTR_STRING;
|
|
goodAttr = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
regex.setPattern("\\\"*(\\w+)\\\"* \\\"*(\\w+)\\\"* (.*)");
|
|
match = regex.match(inpString);
|
|
//Same as above but no upper/lower bound values.
|
|
if (match.hasMatch())
|
|
{
|
|
qDebug() << "Parsing an attribute without boundaries";
|
|
attr.name = match.captured(1);
|
|
QString typ = match.captured(2);
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.attrType = ATTR_TYPE_SIG;
|
|
|
|
if (!typ.compare("INT", Qt::CaseInsensitive))
|
|
{
|
|
qDebug() << "INT attribute named " << attr.name;
|
|
attr.valType = ATTR_INT;
|
|
goodAttr = true;
|
|
}
|
|
|
|
if (!typ.compare("FLOAT", Qt::CaseInsensitive))
|
|
{
|
|
qDebug() << "FLOAT attribute named " << attr.name;
|
|
attr.valType = ATTR_FLOAT;
|
|
goodAttr = true;
|
|
}
|
|
|
|
if (!typ.compare("STRING", Qt::CaseInsensitive))
|
|
{
|
|
qDebug() << "STRING attribute named " << attr.name;
|
|
attr.valType = ATTR_STRING;
|
|
goodAttr = true;
|
|
}
|
|
|
|
if (!typ.compare("ENUM", Qt::CaseInsensitive))
|
|
{
|
|
qDebug() << "ENUM attribute named " << attr.name;
|
|
QStringList enumLst = match.captured(3).split(',');
|
|
foreach (QString enumStr, enumLst)
|
|
{
|
|
attr.enumVals.append(Utility::unQuote(enumStr));
|
|
qDebug() << "Enum value: " << enumStr;
|
|
}
|
|
attr.valType = ATTR_ENUM;
|
|
goodAttr = true;
|
|
}
|
|
}
|
|
}
|
|
return goodAttr;
|
|
}
|
|
|
|
bool DBCFile::saveFile(QString fileName)
|
|
{
|
|
// sort();
|
|
messageHandler->sort(); // sort messages, each of which sorts its signals too
|
|
int nodeNumber = 1;
|
|
int msgNumber = 1;
|
|
int sigNumber = 1;
|
|
QFile *outFile = new QFile(fileName);
|
|
QString nodesOutput, msgOutput, commentsOutput, valuesOutput, valueTableOutput, extMultiplexOutput;
|
|
QString defaultsOutput, attrValOutput, sigValTypeOutput;
|
|
bool hasExtendedMultiplexing = false;
|
|
|
|
if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
|
|
{
|
|
delete outFile;
|
|
return false;
|
|
}
|
|
|
|
//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("\tNS_DESC_\n");
|
|
outFile->write("\tCM_\n");
|
|
outFile->write("\tBA_DEF_\n");
|
|
outFile->write("\tBA_\n");
|
|
outFile->write("\tVAL_\n");
|
|
outFile->write("\tCAT_DEF_\n");
|
|
outFile->write("\tCAT_\n");
|
|
outFile->write("\tFILTER\n");
|
|
outFile->write("\tBA_DEF_DEF_\n");
|
|
outFile->write("\tEV_DATA_\n");
|
|
outFile->write("\tENVVAR_DATA_\n");
|
|
outFile->write("\tSGTYPE_\n");
|
|
outFile->write("\tSGTYPE_VAL_\n");
|
|
outFile->write("\tBA_DEF_SGTYPE_\n");
|
|
outFile->write("\tBA_SGTYPE_\n");
|
|
outFile->write("\tSIG_TYPE_REF_\n");
|
|
outFile->write("\tVAL_TABLE_\n");
|
|
outFile->write("\tSIG_GROUP_\n");
|
|
outFile->write("\tSIG_VALTYPE_\n");
|
|
outFile->write("\tSIGTYPE_VALTYPE_\n");
|
|
outFile->write("\tBO_TX_BU_\n");
|
|
outFile->write("\tBA_DEF_REL_\n");
|
|
outFile->write("\tBA_REL_\n");
|
|
outFile->write("\tBA_DEF_DEF_REL_\n");
|
|
outFile->write("\tBU_SG_REL_\n");
|
|
outFile->write("\tBU_EV_REL_\n");
|
|
outFile->write("\tBU_BO_REL_\n");
|
|
outFile->write("\tSG_MUL_VAL_\n");
|
|
outFile->write("\n");
|
|
outFile->write("BS_: \n");
|
|
|
|
//Build list of nodes line
|
|
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)
|
|
{
|
|
if (node.name.length() < 1) //detect an empty string and fill it out with something
|
|
{
|
|
node.name = "NODE" + QString::number(nodeNumber);
|
|
nodeNumber++;
|
|
}
|
|
nodesOutput.append(node.name + " ");
|
|
if (node.comment.length() > 0)
|
|
{
|
|
commentsOutput.append("CM_ BU_ " + node.name + " \"" + node.comment + "\";\n");
|
|
}
|
|
if (node.attributes.count() > 0)
|
|
{
|
|
foreach (DBC_ATTRIBUTE_VALUE val, node.attributes) {
|
|
attrValOutput.append("BA_ \"" + val.attrName + "\" BU_ ");
|
|
switch (val.value.type())
|
|
{
|
|
case QVariant::Type::String:
|
|
attrValOutput.append("\"" + val.value.toString() + "\";\n");
|
|
break;
|
|
case QVariant::Type::Bool:
|
|
attrValOutput.append(QString::number(val.value.toBool() ? 1 : 0) + ";\n");
|
|
break;
|
|
default:
|
|
attrValOutput.append(val.value.toString() + ";\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nodesOutput.append("\n");
|
|
outFile->write(nodesOutput.toUtf8());
|
|
|
|
for (int x = 0; x < dbc_value_tables.count(); x++)
|
|
{
|
|
DBC_VAL_TABLE table = dbc_value_tables[x];
|
|
valueTableOutput.append("VAL_TABLE_ " + table.name);
|
|
for (int v = 0; v < table.valList.count(); v++)
|
|
{
|
|
DBC_VAL_ENUM_ENTRY val = table.valList[v];
|
|
valueTableOutput.append(" " + QString::number(val.value) + " \"" + val.descript + "\"");
|
|
}
|
|
valueTableOutput.append(";\n");
|
|
}
|
|
if (!valueTableOutput.isEmpty())
|
|
{
|
|
outFile->write(valueTableOutput.toUtf8());
|
|
valueTableOutput.clear();
|
|
}
|
|
|
|
//Go through all messages one at at time issuing the message line then all signals in there too.
|
|
for (int x = 0; x < messageHandler->getCount(); x++)
|
|
{
|
|
DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x);
|
|
|
|
if (msg->name.length() < 1) //detect an empty string and fill it out with something
|
|
{
|
|
msg->name = "MSG" + QString::number(msgNumber);
|
|
msgNumber++;
|
|
}
|
|
|
|
uint32_t ID = msg->ID;
|
|
if (msg->ID > 0x7FF || msg->extendedID)
|
|
{
|
|
ID += 0x80000000ul; //set bit 31 if this ID is extended.
|
|
}
|
|
|
|
msgOutput.append("BO_ " + QString::number(ID) + " " + msg->name + ": " + QString::number(msg->len) +
|
|
" " + msg->sender->name + "\n");
|
|
if (msg->comment.length() > 0)
|
|
{
|
|
commentsOutput.append("CM_ BO_ " + QString::number(ID) + " \"" + msg->comment + "\";\n");
|
|
}
|
|
|
|
//If this message has attributes then compile them into attributes list to output later on.
|
|
if (msg->attributes.count() > 0)
|
|
{
|
|
foreach (DBC_ATTRIBUTE_VALUE val, msg->attributes) {
|
|
attrValOutput.append("BA_ \"" + val.attrName + "\" BO_ " + QString::number(ID) + " ");
|
|
switch (val.value.type())
|
|
{
|
|
case QVariant::Type::String:
|
|
attrValOutput.append("\"" + val.value.toString() + "\";\n");
|
|
break;
|
|
case QVariant::Type::Bool:
|
|
attrValOutput.append(QString::number(val.value.toBool() ? 1 : 0) + ";\n");
|
|
break;
|
|
default:
|
|
attrValOutput.append(val.value.toString() + ";\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int s = 0; s < msg->sigHandler->getCount(); s++)
|
|
{
|
|
DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s);
|
|
|
|
if (sig->name.length() < 1) //detect an empty string and fill it out with something
|
|
{
|
|
sig->name = "SIG" + QString::number(sigNumber);
|
|
sigNumber++;
|
|
}
|
|
|
|
msgOutput.append("\tSG_ " + sig->name);
|
|
|
|
if (sig->isMultiplexed)
|
|
{
|
|
msgOutput.append(" m" + QString::number(sig->getSimpleMultiplexValue()));
|
|
|
|
}
|
|
if (sig->isMultiplexor)
|
|
{
|
|
if (!sig->isMultiplexed) msgOutput.append(" ");
|
|
msgOutput.append("M");
|
|
}
|
|
//check for the two telltale signs that we've got extended multiplexing going on.
|
|
if (sig->isMultiplexed && sig->isMultiplexor) hasExtendedMultiplexing = true;
|
|
if (sig->hasExtendedMultiplexing) hasExtendedMultiplexing = true;
|
|
|
|
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:
|
|
case SP_FLOAT:
|
|
case DP_FLOAT:
|
|
if (sig->intelByteOrder) msgOutput.append("1-");
|
|
else msgOutput.append("0-");
|
|
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->valType == SP_FLOAT || sig->valType == DP_FLOAT)
|
|
sigValTypeOutput.append("SIG_VALTYPE_ " + QString::number(ID) + " " + sig->name + " : " + QString::number(sig->valType == SP_FLOAT ? 1 : 2) + ";\n");
|
|
if (sig->comment.length() > 0)
|
|
{
|
|
commentsOutput.append("CM_ SG_ " + QString::number(ID) + " " + sig->name + " \"" + sig->comment + "\";\n");
|
|
}
|
|
|
|
//if this signal has attributes then compile them in a special list of attributes
|
|
if (sig->attributes.count() > 0)
|
|
{
|
|
foreach (DBC_ATTRIBUTE_VALUE val, sig->attributes) {
|
|
attrValOutput.append("BA_ \"" + val.attrName + "\" SG_ " + QString::number(ID) + " " + sig->name + " ");
|
|
switch (val.value.type())
|
|
{
|
|
case QVariant::Type::String:
|
|
attrValOutput.append("\"" + val.value.toString() + "\";\n");
|
|
break;
|
|
case QVariant::Type::Bool:
|
|
attrValOutput.append(QString::number(val.value.toBool() ? 1 : 0) + ";\n");
|
|
break;
|
|
default:
|
|
attrValOutput.append(val.value.toString() + ";\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sig->valList.count() > 0)
|
|
{
|
|
valuesOutput.append("VAL_ " + QString::number(ID) + " " + sig->name);
|
|
for (int v = 0; v < sig->valList.count(); v++)
|
|
{
|
|
DBC_VAL_ENUM_ENTRY 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
|
|
}
|
|
|
|
outFile->write(commentsOutput.toUtf8());
|
|
commentsOutput.clear();
|
|
|
|
//Now dump out all of the stored attributes
|
|
for (int x = 0; x < dbc_attributes.count(); x++)
|
|
{
|
|
msgOutput.append("BA_DEF_ ");
|
|
switch (dbc_attributes[x].attrType)
|
|
{
|
|
case ATTR_TYPE_GENERAL:
|
|
break;
|
|
case ATTR_TYPE_NODE:
|
|
msgOutput.append("BU_ ");
|
|
break;
|
|
case ATTR_TYPE_MESSAGE:
|
|
msgOutput.append("BO_ ");
|
|
break;
|
|
case ATTR_TYPE_SIG:
|
|
msgOutput.append("SG_ ");
|
|
break;
|
|
case ATTR_TYPE_ANY:
|
|
break;
|
|
}
|
|
|
|
msgOutput.append("\"" + dbc_attributes[x].name + "\" ");
|
|
|
|
switch (dbc_attributes[x].valType)
|
|
{
|
|
case ATTR_INT:
|
|
msgOutput.append("INT " + QString::number(dbc_attributes[x].lower) + " " + QString::number(dbc_attributes[x].upper));
|
|
break;
|
|
case ATTR_FLOAT:
|
|
msgOutput.append("FLOAT " + QString::number(dbc_attributes[x].lower) + " " + QString::number(dbc_attributes[x].upper));
|
|
break;
|
|
case ATTR_STRING:
|
|
msgOutput.append("STRING ");
|
|
break;
|
|
case ATTR_ENUM:
|
|
msgOutput.append("ENUM ");
|
|
foreach (QString str, dbc_attributes[x].enumVals)
|
|
{
|
|
msgOutput.append("\"" + str + "\",");
|
|
}
|
|
msgOutput.truncate(msgOutput.length() - 1); //remove trailing ,
|
|
break;
|
|
}
|
|
|
|
msgOutput.append(";\n");
|
|
|
|
outFile->write(msgOutput.toUtf8());
|
|
msgOutput.clear(); //got to reset it after writing
|
|
|
|
if (dbc_attributes[x].defaultValue.isValid())
|
|
{
|
|
defaultsOutput.append("BA_DEF_DEF_ \"" + dbc_attributes[x].name + "\" ");
|
|
switch (dbc_attributes[x].valType)
|
|
{
|
|
case ATTR_STRING:
|
|
defaultsOutput.append("\"" + dbc_attributes[x].defaultValue.toString() + "\";\n");
|
|
break;
|
|
case ATTR_ENUM:
|
|
defaultsOutput.append("\"" + dbc_attributes[x].enumVals[dbc_attributes[x].defaultValue.toInt()] + "\";\n");
|
|
break;
|
|
case ATTR_INT:
|
|
defaultsOutput.append(QString::number(dbc_attributes[x].defaultValue.toLongLong()) + ";\n");
|
|
break;
|
|
case ATTR_FLOAT:
|
|
defaultsOutput.append(dbc_attributes[x].defaultValue.toString() + ";\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//extended multiplexing uses SG_MUL_VAL_ to specify the relationships. If a signal is marked
|
|
//as multiplexed then output a record for it. That's the most complete option. We've already
|
|
//given the single value multiplex above for backward compatibility with things that don't support extended mode
|
|
if (hasExtendedMultiplexing)
|
|
{
|
|
for (int x = 0; x < messageHandler->getCount(); x++)
|
|
{
|
|
DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x);
|
|
|
|
uint32_t ID = msg->ID;
|
|
if (msg->ID > 0x7FF || msg->extendedID)
|
|
{
|
|
ID += 0x80000000ul; //set bit 31 if this ID is extended.
|
|
}
|
|
|
|
for (int s = 0; s < msg->sigHandler->getCount(); s++)
|
|
{
|
|
DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s);
|
|
|
|
if (sig->isMultiplexed)
|
|
{
|
|
msgOutput.append("SG_MUL_VAL_ " + QString::number(ID) + " ");
|
|
msgOutput.append(sig->name + " " + sig->multiplexParent->name + " ");
|
|
msgOutput.append(sig->multiplexDbcString() + ";");
|
|
msgOutput.append("\n");
|
|
extMultiplexOutput.append(msgOutput);
|
|
msgOutput.clear(); //got to reset it after writing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//now write out all of the accumulated comments and value tables from above
|
|
outFile->write(defaultsOutput.toUtf8());
|
|
outFile->write(attrValOutput.toUtf8());
|
|
outFile->write(valuesOutput.toUtf8());
|
|
outFile->write(extMultiplexOutput.toUtf8());
|
|
if (!sigValTypeOutput.isEmpty())
|
|
outFile->write(sigValTypeOutput.toUtf8());
|
|
|
|
attrValOutput.clear();
|
|
defaultsOutput.clear();
|
|
valuesOutput.clear();
|
|
extMultiplexOutput.clear();
|
|
sigValTypeOutput.clear();
|
|
|
|
outFile->close();
|
|
delete outFile;
|
|
|
|
isDirty = false;
|
|
|
|
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());
|
|
return true;
|
|
}
|
|
|
|
void DBCHandler::saveDBCFile(int idx)
|
|
{
|
|
QSettings settings;
|
|
|
|
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.setDirectory(settings.value("DBC/LoadSaveDirectory", dialog.directory().path()).toString());
|
|
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);
|
|
settings.setValue("DBC/LoadSaveDirectory", dialog.directory().path());
|
|
}
|
|
}
|
|
|
|
int DBCHandler::createBlankFile()
|
|
{
|
|
DBCFile newFile;
|
|
DBC_ATTRIBUTE attr;
|
|
|
|
//add our custom attributes to the new file so that we know they're already there.
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
attr.defaultValue = QApplication::palette().color(QPalette::Base).name();
|
|
qDebug() << attr.defaultValue;
|
|
attr.enumVals.clear();
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.name = "GenMsgBackgroundColor";
|
|
attr.valType = ATTR_STRING;
|
|
newFile.dbc_attributes.append(attr);
|
|
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
attr.defaultValue = QApplication::palette().color(QPalette::WindowText).name();
|
|
qDebug() << attr.defaultValue;
|
|
attr.enumVals.clear();
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.name = "GenMsgForegroundColor";
|
|
attr.valType = ATTR_STRING;
|
|
newFile.dbc_attributes.append(attr);
|
|
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
attr.defaultValue = 0;
|
|
attr.enumVals.clear();
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.name = "matchingcriteria";
|
|
attr.valType = ATTR_INT;
|
|
newFile.dbc_attributes.append(attr);
|
|
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
attr.defaultValue = 0;
|
|
attr.enumVals.clear();
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.name = "filterlabeling";
|
|
attr.valType = ATTR_INT;
|
|
newFile.dbc_attributes.append(attr);
|
|
|
|
DBC_NODE falseNode;
|
|
falseNode.name = "Vector__XXX";
|
|
falseNode.comment = "Default node if none specified";
|
|
newFile.dbc_nodes.append(falseNode);
|
|
newFile.setAssocBus(-1);
|
|
|
|
loadedFiles.append(newFile);
|
|
return loadedFiles.count();
|
|
}
|
|
|
|
DBCFile* DBCHandler::loadDBCFile(QString filename)
|
|
{
|
|
DBCFile newFile;
|
|
if (newFile.loadFile(filename))
|
|
{
|
|
loadedFiles.append(newFile);
|
|
}
|
|
else
|
|
{
|
|
//createBlankFile();
|
|
}
|
|
if (loadedFiles.count()> 0) return &loadedFiles.last();
|
|
else return nullptr;
|
|
}
|
|
|
|
//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;
|
|
QSettings settings;
|
|
|
|
QStringList filters;
|
|
filters.append(QString(tr("DBC File (*.dbc)")));
|
|
|
|
dialog.setDirectory(settings.value("DBC/LoadSaveDirectory", dialog.directory().path()).toString());
|
|
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.
|
|
settings.setValue("DBC/LoadSaveDirectory", dialog.directory().path());
|
|
return loadDBCFile(filename);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
DBCFile* DBCHandler::loadSecretCSVFile(QString filename)
|
|
{
|
|
DBCFile *thisFile;
|
|
DBC_MESSAGE *pMsg;
|
|
DBC_SIGNAL *pSig;
|
|
QByteArray line;
|
|
int lineCounter = 0;
|
|
createBlankFile();
|
|
thisFile = &loadedFiles.last();
|
|
|
|
QFile *inFile = new QFile(filename);
|
|
|
|
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
|
|
{
|
|
delete inFile;
|
|
return nullptr;
|
|
}
|
|
|
|
//burn first two lines. contains header
|
|
line = inFile->readLine().simplified().toUpper();
|
|
line = inFile->readLine().simplified().toUpper();
|
|
|
|
while (!inFile->atEnd())
|
|
{
|
|
lineCounter++;
|
|
if (lineCounter > 100)
|
|
{
|
|
qApp->processEvents();
|
|
lineCounter = 0;
|
|
}
|
|
line = inFile->readLine().simplified().toUpper();
|
|
qDebug() << line;
|
|
|
|
QList<QByteArray> tokens = line.split(',');
|
|
if (tokens.length() == 10)
|
|
{
|
|
//Message,CAN ID,Signal,Short Name,Start Byte,Start Bit,Len,Data,Range,Conversion
|
|
//Whl_Rotational_Stat_CheckVal_CE,$0C0,Wheel Rotational Status Check Data,WhlRotatStatChkData, 0, 7, 40,PKT,N/A,N/A
|
|
// 0 1 2 3 4 5 6 7 8 9
|
|
if (tokens[0].length() > 2) //start of a signal def that starts a new message
|
|
{
|
|
DBC_MESSAGE msg;
|
|
msg.name = tokens[0];
|
|
msg.ID = tokens[1].mid(1).toLong(nullptr, 16);
|
|
msg.comment = "";
|
|
msg.len = 8;
|
|
msg.sender = thisFile->findNodeByIdx(0);
|
|
msg.bgColor = QColor(thisFile->findAttributeByName("GenMsgBackgroundColor")->defaultValue.toString());
|
|
msg.fgColor = QColor(thisFile->findAttributeByName("GenMsgForegroundColor")->defaultValue.toString());
|
|
thisFile->messageHandler->addMessage(msg);
|
|
pMsg = thisFile->messageHandler->findMsgByID(msg.ID);
|
|
|
|
DBC_SIGNAL sig;
|
|
sig.parentMessage = pMsg;
|
|
sig.name = tokens[3];
|
|
sig.comment = tokens[2];
|
|
int startBit = (tokens[4].toInt() * 8) + tokens[5].toInt();
|
|
sig.startBit = startBit;
|
|
sig.signalSize = tokens[6].toInt();
|
|
sig.intelByteOrder = false; //always for global-a?
|
|
sig.receiver = thisFile->findNodeByIdx(0);
|
|
QList<QByteArray> rangeToks = tokens[8].split('-');
|
|
if (rangeToks.length() == 2)
|
|
{
|
|
sig.min = rangeToks[0].simplified().toDouble();
|
|
sig.max = rangeToks[1].simplified().toDouble();
|
|
}
|
|
if (tokens[9].startsWith("E = N")) //not a value table, instead do scaling and bias
|
|
{
|
|
QList<QByteArray> scalingToks = tokens[9].simplified().split(' ');
|
|
sig.factor = scalingToks[4].toDouble();
|
|
if (scalingToks.count() > 5)
|
|
{
|
|
if (scalingToks[5] == "+")
|
|
{
|
|
sig.bias = scalingToks[6].toDouble();
|
|
}
|
|
if (scalingToks[5] == "-")
|
|
{
|
|
sig.bias = scalingToks[6].toDouble() * 1.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sig.factor = 1;
|
|
sig.bias = 0;
|
|
}
|
|
}
|
|
else if (tokens[9].startsWith("$")) //one or more values table entries
|
|
{
|
|
//$0=Inactive
|
|
QList<QByteArray> valToks = tokens[9].simplified().mid(1).split('=');
|
|
DBC_VAL_ENUM_ENTRY entry;
|
|
entry.value = valToks[0].toInt();
|
|
entry.descript = valToks[1];
|
|
sig.valList.append(entry);
|
|
}
|
|
pMsg->sigHandler->addSignal(sig);
|
|
pSig = pMsg->sigHandler->findSignalByIdx(pMsg->sigHandler->getCount()-1);
|
|
}
|
|
// 0 1 2 3 4 5 6 7 8 9
|
|
//Message,CAN ID,Signal,Short Name,Start Byte,Start Bit,Len,Data,Range,Conversion
|
|
//,,Wheel Rotational Status Check Data : Left Driven Sequence Number,WRSCD_LftDrvnSqNm, 0, 7, 2,UNM,0 - 3 ,E = N * 1
|
|
// 2 3 4 5 6 7 8 9
|
|
else if (tokens[2].length() > 2) //signal definition continuation of previous message
|
|
{
|
|
DBC_SIGNAL sig;
|
|
sig.parentMessage = pMsg;
|
|
sig.name = tokens[3];
|
|
sig.comment = tokens[2];
|
|
int startBit = (tokens[4].toInt() * 8) + tokens[5].toInt();
|
|
sig.startBit = startBit;
|
|
sig.signalSize = tokens[6].toInt();
|
|
sig.intelByteOrder = false; //always for global-a?
|
|
sig.receiver = thisFile->findNodeByIdx(0);
|
|
QList<QByteArray> rangeToks = tokens[8].split('-');
|
|
if (rangeToks.length() == 2)
|
|
{
|
|
sig.min = rangeToks[0].simplified().toDouble();
|
|
sig.max = rangeToks[1].simplified().toDouble();
|
|
}
|
|
if (tokens[9].startsWith("E = N")) //not a value table, instead do scaling and bias
|
|
{
|
|
QList<QByteArray> scalingToks = tokens[9].simplified().split(' ');
|
|
sig.factor = scalingToks[4].toDouble();
|
|
if (scalingToks.count() > 5)
|
|
{
|
|
if (scalingToks[5] == "+")
|
|
{
|
|
sig.bias = scalingToks[6].toDouble();
|
|
}
|
|
if (scalingToks[5] == "-")
|
|
{
|
|
sig.bias = scalingToks[6].toDouble() * 1.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sig.bias = 0;
|
|
sig.factor = 1;
|
|
}
|
|
}
|
|
else if (tokens[9].startsWith("$")) //one or more values table entries
|
|
{
|
|
//$0=Inactive
|
|
QList<QByteArray> valToks = tokens[9].simplified().mid(1).split('=');
|
|
DBC_VAL_ENUM_ENTRY entry;
|
|
entry.value = valToks[0].toInt();
|
|
entry.descript = valToks[1];
|
|
sig.valList.append(entry);
|
|
}
|
|
pMsg->sigHandler->addSignal(sig);
|
|
pSig = pMsg->sigHandler->findSignalByIdx(pMsg->sigHandler->getCount()-1);
|
|
}
|
|
else if (tokens[9].length() > 2) //additional values
|
|
{
|
|
//$0=Inactive
|
|
QList<QByteArray> valToks = tokens[9].simplified().mid(1).split('=');
|
|
DBC_VAL_ENUM_ENTRY entry;
|
|
entry.value = valToks[0].toInt();
|
|
entry.descript = valToks[1];
|
|
pSig->valList.append(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
thisFile->setDirtyFlag();
|
|
return thisFile;
|
|
}
|
|
|
|
DBCFile* DBCHandler::loadJSONFile(QString filename)
|
|
{
|
|
QSettings settings;
|
|
DBCFile *thisFile;
|
|
DBC_MESSAGE *pMsg;
|
|
|
|
createBlankFile();
|
|
thisFile = &loadedFiles.last();
|
|
|
|
QFile *inFile = new QFile(filename);
|
|
if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
|
|
{
|
|
qDebug() << "Could not open JSON file for reading.";
|
|
delete inFile;
|
|
return nullptr;
|
|
}
|
|
QByteArray wholeFileData = inFile->readAll();
|
|
inFile->close();
|
|
delete inFile;
|
|
//qDebug() << "Data Length: " << wholeFileData.length();
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(wholeFileData);
|
|
if (jsonDoc.isNull())
|
|
{
|
|
qDebug() << "Couldn't load and parse the JSON file for some reason.";
|
|
return nullptr;
|
|
}
|
|
qDebug() << "Loaded JSON";
|
|
|
|
QJsonObject jsonObj = jsonDoc.object();
|
|
QJsonObject jsonMessages = jsonObj["messages"].toObject();
|
|
|
|
QJsonObject::iterator iter;
|
|
for (iter = jsonMessages.begin(); iter != jsonMessages.end(); iter++)
|
|
{
|
|
qDebug() << iter.key();
|
|
DBC_MESSAGE msg;
|
|
msg.ID = static_cast<uint32_t>(iter->toObject().find("message_id").value().toInt());
|
|
msg.name = QString(iter.key().toUtf8());
|
|
msg.len = static_cast<unsigned int>(iter->toObject().find("length_bytes").value().toInt());
|
|
msg.sender = thisFile->findNodeByIdx(0);
|
|
QString nodeName = iter->toObject().find("originNode").value().toString();
|
|
msg.sender = thisFile->findNodeByName(nodeName);
|
|
msg.bgColor = QColor(thisFile->findAttributeByName("GenMsgBackgroundColor")->defaultValue.toString());
|
|
msg.fgColor = QColor(thisFile->findAttributeByName("GenMsgForegroundColor")->defaultValue.toString());
|
|
if (!msg.sender && nodeName.length() > 1)
|
|
{
|
|
DBC_NODE node;
|
|
node.name = nodeName;
|
|
thisFile->dbc_nodes.append(node);
|
|
msg.sender = thisFile->findNodeByName(nodeName);
|
|
}
|
|
if (nodeName.length() < 2) msg.sender = thisFile->findNodeByIdx(0);
|
|
thisFile->messageHandler->addMessage(msg);
|
|
pMsg = thisFile->messageHandler->findMsgByID(msg.ID);
|
|
if (!pMsg)
|
|
{
|
|
qDebug() << "pMsg was null... I have no idea how that is possible. DEBUG ME";
|
|
return nullptr;
|
|
}
|
|
|
|
QJsonObject jsonSigs = iter->toObject().find("signals").value().toObject();
|
|
QJsonObject::iterator sigIter;
|
|
for (sigIter = jsonSigs.begin(); sigIter != jsonSigs.end(); sigIter++)
|
|
{
|
|
qDebug() << sigIter.key();
|
|
DBC_SIGNAL sig;
|
|
QJsonObject sigObj = sigIter->toObject();
|
|
if (sigObj.isEmpty())
|
|
{
|
|
qDebug() << "EMPTY!?";
|
|
}
|
|
sig.name = QString(sigIter.key().toUtf8());
|
|
sig.factor = sigObj.find("scale").value().toDouble();
|
|
sig.bias = sigObj.find("offset").value().toDouble();
|
|
sig.max = sigObj.find("max").value().toDouble();
|
|
sig.min = sigObj.find("min").value().toDouble();
|
|
sig.startBit = sigObj.find("start_position").value().toInt();
|
|
sig.unitName = sigObj.find("units").value().toString();
|
|
sig.signalSize = sigObj.find("width").value().toInt();
|
|
sig.isMultiplexed = false;
|
|
sig.isMultiplexor = false;
|
|
sig.parentMessage = pMsg;
|
|
if (!sigObj.find("mux_id")->isUndefined())
|
|
{
|
|
QJsonValue muxVal = sigObj.find("mux_id").value();
|
|
sig.addMultiplexRange(muxVal.toInt(), muxVal.toInt());
|
|
}
|
|
QJsonValue muxerVal = sigObj.find("is_muxer").value();
|
|
if (!muxerVal.isNull())
|
|
{
|
|
if (muxerVal.toBool())
|
|
{
|
|
sig.isMultiplexor = true;
|
|
}
|
|
}
|
|
|
|
QJsonObject valuesObj = sigObj.find("value_description")->toObject();
|
|
if (!valuesObj.isEmpty())
|
|
{
|
|
QJsonObject::iterator valIter;
|
|
for (valIter = valuesObj.begin(); valIter != valuesObj.end(); valIter++)
|
|
{
|
|
DBC_VAL_ENUM_ENTRY valEnum;
|
|
valEnum.value = valIter.value().toInt();
|
|
valEnum.descript = valIter.key().toUtf8();
|
|
sig.valList.append(valEnum);
|
|
}
|
|
}
|
|
|
|
QJsonArray rxArray = sigObj.find("receivers")->toArray();
|
|
if (rxArray.size() < 1) sig.receiver = thisFile->findNodeByIdx(0);
|
|
else
|
|
{
|
|
qDebug() << rxArray[0].toString();
|
|
sig.receiver = thisFile->findNodeByName(rxArray[0].toString());
|
|
if (!sig.receiver && rxArray[0].toString().length() > 1)
|
|
{
|
|
DBC_NODE node;
|
|
node.name = rxArray[0].toString();
|
|
thisFile->dbc_nodes.append(node);
|
|
sig.receiver = thisFile->findNodeByName(rxArray[0].toString());
|
|
}
|
|
if (!sig.receiver) sig.receiver = thisFile->findNodeByIdx(0);
|
|
}
|
|
if (!sigObj.find("endianness").value().toString().compare("BIG"))
|
|
{
|
|
sig.intelByteOrder = false;
|
|
}
|
|
else sig.intelByteOrder = true;
|
|
|
|
if (!sigObj.find("signedness").value().toString().compare("UNSIGNED"))
|
|
{
|
|
sig.valType = DBC_SIG_VAL_TYPE::UNSIGNED_INT;
|
|
}
|
|
else sig.valType = DBC_SIG_VAL_TYPE::SIGNED_INT;
|
|
|
|
pMsg->sigHandler->addSignal(sig);
|
|
if (sig.isMultiplexor) //if this signal was the multiplexor then store that info
|
|
{
|
|
DBC_SIGNAL *pSig = pMsg->sigHandler->findSignalByName(sig.name);
|
|
if (pSig) pMsg->multiplexorSignal = pSig;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int x = 0; x < thisFile->messageHandler->getCount(); x++)
|
|
{
|
|
DBC_MESSAGE *msg = thisFile->messageHandler->findMsgByIdx(x);
|
|
for (int y = 0; y < msg->sigHandler->getCount(); y++)
|
|
{
|
|
DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(y);
|
|
//if this doesn't have a multiplex parent set but is multiplexed then it must have used
|
|
//simple multiplexing instead of any extended specification. So, fill in the multiplexor signal here
|
|
//and also write the extended entry for it too.
|
|
if (sig->isMultiplexed && (sig->multiplexParent == nullptr) )
|
|
{
|
|
sig->multiplexParent = msg->multiplexorSignal;
|
|
msg->multiplexorSignal->multiplexedChildren.append(sig);
|
|
}
|
|
}
|
|
}
|
|
|
|
thisFile->setDirtyFlag();
|
|
return thisFile;
|
|
}
|
|
|
|
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.swapItemsAt(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 nullptr 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.frameId());
|
|
if (msg != nullptr) return msg;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
DBC_MESSAGE* DBCHandler::findMessage(uint32_t id)
|
|
{
|
|
for(int i = 0; i < loadedFiles.count(); i++)
|
|
{
|
|
DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(id);
|
|
if (msg != nullptr)
|
|
{
|
|
return msg;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// This function won't care which bus the DBC file is associated, but will return any message as long as ID matches and the file
|
|
// has filter labeling enabled.
|
|
// Returns the found message as well as the matching criteria (exact/J1939/GMLAN)
|
|
// Used for quickly populating the Frame Filtering section with interpreted values
|
|
DBC_MESSAGE* DBCHandler::findMessageForFilter(uint32_t id, MatchingCriteria_t * matchingCriteria)
|
|
{
|
|
for(int i = 0; i < loadedFiles.count(); i++)
|
|
{
|
|
if (loadedFiles[i].messageHandler->filterLabeling())
|
|
{
|
|
DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(id);
|
|
if (msg != nullptr)
|
|
{
|
|
if (matchingCriteria) *matchingCriteria = loadedFiles[i].messageHandler->getMatchingCriteria();
|
|
return msg;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
/*
|
|
* As above, a real shortcut function that searches all files in order to try to find a message with the given name
|
|
* This has pitfalls because the same message name can easily exist in multiple dbc files AND nodes in a single file
|
|
* By DBC standards only the MSG ID is required to be unique
|
|
*/
|
|
DBC_MESSAGE* DBCHandler::findMessage(const QString msgName)
|
|
{
|
|
DBC_MESSAGE *msg = nullptr;
|
|
for(int i = 0; i < loadedFiles.count(); i++)
|
|
{
|
|
DBCFile * file = getFileByIdx(i);
|
|
msg = file->messageHandler->findMsgByName(msgName);
|
|
if (msg) return msg; //if it's not null then we have a match so return it
|
|
}
|
|
return nullptr; //no match, tough luck, return null
|
|
}
|
|
|
|
DBC_MESSAGE* DBCHandler::findMessage(const QString msgName, const QString nodeName, const QString fileNameNoExt)
|
|
{
|
|
DBC_MESSAGE *msg = nullptr;
|
|
for(int i = 0; i < loadedFiles.count(); i++)
|
|
{
|
|
DBCFile * file = getFileByIdx(i);
|
|
if(file->getFilenameNoExt() == fileNameNoExt)
|
|
{
|
|
int msgCount = file->messageHandler->getCount();
|
|
for(int f = 0; f < msgCount; f++)
|
|
{
|
|
msg = file->messageHandler->findMsgByIdx(f);
|
|
if (msg && msg->name == msgName && msg->sender->name == nodeName)
|
|
return msg; //if it's not null and the node name matches return it
|
|
}
|
|
}
|
|
}
|
|
return nullptr; //no match, tough luck, return null
|
|
}
|
|
|
|
DBC_MESSAGE* DBCHandler::findMessage(const QString msgName, const QString fullyQualifiedNodeName)
|
|
{
|
|
QStringList nodeNameParts = fullyQualifiedNodeName.split(Utility::fullyQualifiedNameSeperator);
|
|
if(nodeNameParts.count() != 2)
|
|
{
|
|
qDebug() << "Error parsing fully qualified node name for message search";
|
|
return nullptr;
|
|
}
|
|
|
|
QString fileNameNoExt = nodeNameParts[0];
|
|
QString nodeName = nodeNameParts[1];
|
|
|
|
DBC_MESSAGE *msg = nullptr;
|
|
msg = findMessage(msgName, nodeName, fileNameNoExt);
|
|
return msg;
|
|
}
|
|
|
|
int DBCHandler::getFileCount()
|
|
{
|
|
return loadedFiles.count();
|
|
}
|
|
|
|
DBCFile* DBCHandler::getFileByIdx(int idx)
|
|
{
|
|
if (loadedFiles.count() == 0) return nullptr;
|
|
if (idx < 0) return nullptr;
|
|
if (idx >= loadedFiles.count()) return nullptr;
|
|
return &loadedFiles[idx];
|
|
}
|
|
|
|
DBCFile* DBCHandler::getFileByName(QString name)
|
|
{
|
|
if (loadedFiles.count() == 0) return nullptr;
|
|
for (int i = 0; i < loadedFiles.count(); i++)
|
|
{
|
|
if (loadedFiles[i].getFilename().compare(name, Qt::CaseInsensitive) == 0)
|
|
{
|
|
return &loadedFiles[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
DBCHandler::DBCHandler()
|
|
{
|
|
// Load previously saved DBC file settings
|
|
QSettings settings;
|
|
qDebug() <<"Settings file: " << settings.fileName();
|
|
int filecount = settings.value("DBC/FileCount", 0).toInt();
|
|
qDebug() << "Previously loaded DBC file count: " << filecount;
|
|
for (int i=0; i<filecount; i++)
|
|
{
|
|
QString filename = settings.value("DBC/Filename_" + QString::number(i),"").toString();
|
|
DBCFile * file = loadDBCFile(filename);
|
|
if (file)
|
|
{
|
|
int bus = settings.value("DBC/AssocBus_" + QString::number(i),0).toInt();
|
|
file->setAssocBus(bus);
|
|
|
|
MatchingCriteria_t matchingCriteria = (MatchingCriteria_t)settings.value("DBC/MatchingCriteria_" + QString::number(i),0).toInt();
|
|
|
|
DBC_ATTRIBUTE attr;
|
|
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
attr.defaultValue = matchingCriteria;
|
|
attr.enumVals.clear();
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.name = "matchingcriteria";
|
|
attr.valType = ATTR_INT;
|
|
// file->dbc_attributes.append(attr);
|
|
// file->messageHandler->setMatchingCriteria(matchingCriteria);
|
|
|
|
int filterLabeling = settings.value("DBC/FilterLabeling_" + QString::number(i),0).toInt();
|
|
attr.attrType = ATTR_TYPE_MESSAGE;
|
|
attr.defaultValue = filterLabeling;
|
|
attr.enumVals.clear();
|
|
attr.lower = 0;
|
|
attr.upper = 0;
|
|
attr.name = "filterlabeling";
|
|
attr.valType = ATTR_INT;
|
|
// file->dbc_attributes.append(attr);
|
|
// file->messageHandler->setFilterLabeling(filterLabeling);
|
|
|
|
qInfo() << "Loaded DBC file" << filename << " (bus:" << bus
|
|
<< ", Matching Criteria:" << (int)matchingCriteria << "Filter labeling: " << (filterLabeling?"enabled":"disabled") << ")";
|
|
}
|
|
}
|
|
}
|
|
|
|
DBCHandler* DBCHandler::getReference()
|
|
{
|
|
if (!instance) instance = new DBCHandler();
|
|
return instance;
|
|
}
|