1 #include "dbchandler.h"
2 
3 #include <QFile>
4 #include <QRegularExpression>
5 #include <QDebug>
6 #include <QMessageBox>
7 #include <QFileDialog>
8 #include <QApplication>
9 #include <QPalette>
10 #include <QSettings>
11 #include <QJsonDocument>
12 #include <QJsonArray>
13 #include <QJsonObject>
14 #include "utility.h"
15 #include "connections/canconmanager.h"
16 
17 DBCHandler* DBCHandler::instance = nullptr;
18 
findSignalByIdx(int idx)19 DBC_SIGNAL* DBCSignalHandler::findSignalByIdx(int idx)
20 {
21     if (sigs.count() == 0) return nullptr;
22     if (idx < 0) return nullptr;
23     if (idx >= sigs.count()) return nullptr;
24 
25     return &sigs[idx];
26 }
27 
findSignalByName(QString name)28 DBC_SIGNAL* DBCSignalHandler::findSignalByName(QString name)
29 {
30     if (sigs.count() == 0) return nullptr;
31     for (int i = 0; i < sigs.count(); i++)
32     {
33         if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0)
34         {
35             return &sigs[i];
36         }
37     }
38     return nullptr;
39 }
40 
addSignal(DBC_SIGNAL & sig)41 bool DBCSignalHandler::addSignal(DBC_SIGNAL &sig)
42 {
43     sigs.append(sig);
44     return true;
45 }
46 
removeSignal(DBC_SIGNAL * sig)47 bool DBCSignalHandler::removeSignal(DBC_SIGNAL *sig)
48 {
49     qDebug() << "Total # of signals: " << getCount();
50     for (int i = 0; i < getCount(); i++)
51     {
52         if (sigs[i].name == sig->name)
53         {
54             sigs.removeAt(i);
55             qDebug() << "Removed signal at idx " << i;
56         }
57     }
58     return true;
59 }
60 
removeSignal(int idx)61 bool DBCSignalHandler::removeSignal(int idx)
62 {
63     if (sigs.count() == 0) return false;
64     if (idx < 0) return false;
65     if (idx >= sigs.count()) return false;
66     sigs.removeAt(idx);
67     return true;
68 }
69 
removeSignal(QString name)70 bool DBCSignalHandler::removeSignal(QString name)
71 {
72     bool foundSome = false;
73     if (sigs.count() == 0) return false;
74     for (int i = sigs.count() - 1; i >= 0; i--)
75     {
76         if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0)
77         {
78             sigs.removeAt(i);
79             foundSome = true;
80         }
81     }
82     return foundSome;
83 }
84 
removeAllSignals()85 void DBCSignalHandler::removeAllSignals()
86 {
87     sigs.clear();
88 }
89 
getCount()90 int DBCSignalHandler::getCount()
91 {
92     return sigs.count();
93 }
94 
sort()95 void DBCSignalHandler::sort()
96 {
97     std::sort(sigs.begin(), sigs.end());
98 }
99 
findMsgByID(uint32_t id)100 DBC_MESSAGE* DBCMessageHandler::findMsgByID(uint32_t id)
101 {
102     if (messages.count() == 0) return nullptr;
103     for (int i = 0; i < messages.count(); i++)
104     {
105         if (matchingCriteria == J1939)
106         {
107             // include data page and extended data page in the pgn
108             uint32_t pgn = (id & 0x3FFFF00) >> 8;
109             if ( (pgn & 0xFF00) <= 0xEF00 )
110             {
111                 // PDU1 format
112                 pgn &= 0x3FF00;
113                 if ((messages[i].ID & 0x3FF0000) == (pgn << 8))
114                 {
115                     return &messages[i];
116                 }
117             }
118             else
119             {
120                 // PDU2 format
121                 if ((messages[i].ID & 0x3FFFF00) == (pgn << 8))
122                 {
123                     return &messages[i];
124                 }
125             }
126         }
127         else if (matchingCriteria == GMLAN)
128         {
129             // Match the bits 14-26 (Arbitration Id) of GMLAN 29bit header
130             uint32_t arbId = id &0x3FFE000;
131             if ( (arbId != 0) && (messages[i].ID & 0x3FFE000) == arbId )
132                 return &messages[i];
133         }
134         else
135         {
136             if ( messages[i].ID == id )
137             {
138                 return &messages[i];
139             }
140         }
141     }
142     return nullptr;
143 }
144 
findMsgByIdx(int idx)145 DBC_MESSAGE* DBCMessageHandler::findMsgByIdx(int idx)
146 {
147     if (messages.count() == 0) return nullptr;
148     if (idx < 0) return nullptr;
149     if (idx >= messages.count()) return nullptr;
150     return &messages[idx];
151 }
152 
findMsgByName(QString name)153 DBC_MESSAGE* DBCMessageHandler::findMsgByName(QString name)
154 {
155     if (messages.count() == 0) return nullptr;
156     for (int i = 0; i < messages.count(); i++)
157     {
158         if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0)
159         {
160             return &messages[i];
161         }
162     }
163     return nullptr;
164 }
165 
166 //allow for finding a message just by part of the name
findMsgByPartialName(QString name)167 DBC_MESSAGE* DBCMessageHandler::findMsgByPartialName(QString name)
168 {
169     if (messages.count() == 0) return nullptr;
170     for (int i = 0; i < messages.count(); i++)
171     {
172         if (messages[i].name.contains(name, Qt::CaseInsensitive))
173         {
174             return &messages[i];
175         }
176     }
177     return nullptr;
178 }
179 
addMessage(DBC_MESSAGE & msg)180 bool DBCMessageHandler::addMessage(DBC_MESSAGE &msg)
181 {
182     messages.append(msg);
183     return true;
184 }
185 
removeMessage(DBC_MESSAGE * msg)186 bool DBCMessageHandler::removeMessage(DBC_MESSAGE *msg)
187 {
188     qDebug() << "Total # of messages: " << getCount();
189     for (int i = 0; i < getCount(); i++)
190     {
191         if (messages[i].name == msg->name)
192         {
193             messages.removeAt(i);
194             qDebug() << "Removed message at idx " << i;
195         }
196     }
197     return true;
198 }
199 
removeMessageByIndex(int idx)200 bool DBCMessageHandler::removeMessageByIndex(int idx)
201 {
202     if (messages.count() == 0) return false;
203     if (idx < 0) return false;
204     if (idx >= messages.count()) return false;
205     messages.removeAt(idx);
206     return true;
207 }
208 
removeMessage(uint32_t ID)209 bool DBCMessageHandler::removeMessage(uint32_t ID)
210 {
211     bool foundSome = false;
212     if (messages.count() == 0) return false;
213     for (int i = messages.count() - 1; i >= 0; i--)
214     {
215         if (messages[i].ID == ID)
216         {
217             messages.removeAt(i);
218             foundSome = true;
219         }
220     }
221     return foundSome;
222 }
223 
removeMessage(QString name)224 bool DBCMessageHandler::removeMessage(QString name)
225 {
226     bool foundSome = false;
227     if (messages.count() == 0) return false;
228     for (int i = messages.count() - 1; i >= 0; i--)
229     {
230         if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0)
231         {
232             messages.removeAt(i);
233             foundSome = true;
234         }
235     }
236     return foundSome;
237 }
238 
removeAllMessages()239 void DBCMessageHandler::removeAllMessages()
240 {
241     messages.clear();
242 }
243 
getCount()244 int DBCMessageHandler::getCount()
245 {
246     return messages.count();
247 }
248 
sort()249 void DBCMessageHandler::sort()
250 {
251     std::sort(messages.begin(), messages.end());
252     for (int i = 0; i < messages.count(); i++)
253     {
254         messages[i].sigHandler->sort();
255     }
256 }
257 
filterLabeling()258 bool DBCMessageHandler::filterLabeling()
259 {
260     return filterLabelingEnabled;
261 }
262 
setFilterLabeling(bool filterLabeling)263 void DBCMessageHandler::setFilterLabeling(bool filterLabeling)
264 {
265     filterLabelingEnabled = filterLabeling;
266 }
267 
getMatchingCriteria()268 MatchingCriteria_t DBCMessageHandler::getMatchingCriteria()
269 {
270     return matchingCriteria;
271 }
272 
setMatchingCriteria(MatchingCriteria_t _matchingCriteria)273 void DBCMessageHandler::setMatchingCriteria(MatchingCriteria_t _matchingCriteria)
274 {
275     matchingCriteria = _matchingCriteria;
276 }
277 
DBCFile()278 DBCFile::DBCFile()
279 {
280     messageHandler = new DBCMessageHandler;
281     messageHandler->setMatchingCriteria(EXACT);
282     messageHandler->setFilterLabeling(false);
283     isDirty = false;
284     fileName = "<Unsaved File>";
285 }
286 
DBCFile(const DBCFile & cpy)287 DBCFile::DBCFile(const DBCFile& cpy) : QObject()
288 {
289     messageHandler = new DBCMessageHandler;
290     for (int i = 0 ; i < cpy.messageHandler->getCount() ; i++)
291         messageHandler->addMessage(*cpy.messageHandler->findMsgByIdx(i));
292 
293     messageHandler->setMatchingCriteria(cpy.messageHandler->getMatchingCriteria());
294     messageHandler->setFilterLabeling(cpy.messageHandler->filterLabeling());
295     fileName = cpy.fileName;
296     filePath = cpy.filePath;
297     assocBuses = cpy.assocBuses;
298     dbc_nodes.clear();
299     dbc_nodes.append(cpy.dbc_nodes);
300     dbc_attributes.clear();
301     dbc_attributes.append(cpy.dbc_attributes);
302     isDirty = cpy.isDirty;
303 }
304 
operator =(const DBCFile & cpy)305 DBCFile& DBCFile::operator=(const DBCFile& cpy)
306 {
307     if (this != &cpy) // protect against invalid self-assignment
308     {
309         messageHandler = cpy.messageHandler;
310         fileName = cpy.fileName;
311         filePath = cpy.filePath;
312         assocBuses = cpy.assocBuses;
313         dbc_nodes.clear();
314         dbc_nodes.append(cpy.dbc_nodes);
315         dbc_attributes.clear();
316         dbc_attributes.append(cpy.dbc_attributes);
317     }
318     return *this;
319 }
320 
sort()321 void DBCFile::sort()
322 {
323     std::sort(dbc_nodes.begin(), dbc_nodes.end()); //sort node names
324     messageHandler->sort(); //sort messages, each of which sorts its signals too
325 }
326 
findNodeByIdx(int idx)327 DBC_NODE* DBCFile::findNodeByIdx(int idx)
328 {
329     if (idx < 0) return nullptr;
330     if (idx >= dbc_nodes.count()) return nullptr;
331     return &dbc_nodes[idx];
332 }
333 
findNodeByName(QString name)334 DBC_NODE* DBCFile::findNodeByName(QString name)
335 {
336     if (dbc_nodes.length() == 0) return nullptr;
337     for (int i = 0; i < dbc_nodes.length(); i++)
338     {
339         if (dbc_nodes[i].name.compare(name, Qt::CaseInsensitive) == 0)
340         {
341             return &dbc_nodes[i];
342         }
343     }
344     return nullptr;
345 }
346 
getFullFilename()347 QString DBCFile::getFullFilename()
348 {
349     return filePath + fileName;
350 }
351 
getFilename()352 QString DBCFile::getFilename()
353 {
354     return fileName;
355 }
356 
getPath()357 QString DBCFile::getPath()
358 {
359     return filePath;
360 }
361 
getAssocBus()362 int DBCFile::getAssocBus()
363 {
364     return assocBuses;
365 }
366 
setAssocBus(int bus)367 void DBCFile::setAssocBus(int bus)
368 {
369     if (bus < -1) return;
370     // To allow setting bus numbers even before connection is configured, do not enforce "valid" bus numbers
371     //int numBuses = CANConManager::getInstance()->getNumBuses();
372     //if (bus >= numBuses) return;
373     assocBuses = bus;
374 }
375 
findAttributeByName(QString name,DBC_ATTRIBUTE_TYPE type)376 DBC_ATTRIBUTE *DBCFile::findAttributeByName(QString name, DBC_ATTRIBUTE_TYPE type)
377 {
378     if (dbc_attributes.length() == 0) return nullptr;
379     for (int i = 0; i < dbc_attributes.length(); i++)
380     {
381         if (dbc_attributes[i].name.compare(name, Qt::CaseInsensitive) == 0 && ((type == dbc_attributes[i].attrType) || (type == ATTR_TYPE_ANY) ) )
382         {
383             return &dbc_attributes[i];
384         }
385     }
386     return nullptr;
387 }
388 
findAttributeByIdx(int idx)389 DBC_ATTRIBUTE *DBCFile::findAttributeByIdx(int idx)
390 {
391     if (idx < 0) return nullptr;
392     if (idx >= dbc_attributes.count()) return nullptr;
393     return &dbc_attributes[idx];
394 }
395 
findAttributesByType(DBC_ATTRIBUTE_TYPE typ,QList<DBC_ATTRIBUTE> * list)396 void DBCFile::findAttributesByType(DBC_ATTRIBUTE_TYPE typ, QList<DBC_ATTRIBUTE> *list)
397 {
398     if (!list) return;
399     list->clear();
400     foreach (DBC_ATTRIBUTE attr, dbc_attributes)
401     {
402         if (attr.attrType == typ) list->append(attr);
403     }
404 }
405 
406 //there's no external way to clear the flag. It is only cleared when the file is saved by this object.
setDirtyFlag()407 void DBCFile::setDirtyFlag()
408 {
409     isDirty = true;
410 }
411 
getDirtyFlag()412 bool DBCFile::getDirtyFlag()
413 {
414     return isDirty;
415 }
416 
parseMessageLine(QString line)417 DBC_MESSAGE* DBCFile::parseMessageLine(QString line)
418 {
419     QRegularExpression regex;
420     QRegularExpressionMatch match;
421 
422     DBC_MESSAGE *msgPtr;
423 
424     qDebug() << "Found a BO line";
425     regex.setPattern("^BO\\_ (\\w+) (\\w+) *: (\\w+) (\\w+)");
426     match = regex.match(line);
427     //captured 1 = the ID in decimal
428     //captured 2 = The message name
429     //captured 3 = the message length
430     //captured 4 = the NODE responsible for this message
431     if (match.hasMatch())
432     {
433         DBC_MESSAGE msg;
434         uint32_t ID = match.captured(1).toULong(); //the ID is always stored in decimal format
435         msg.ID = ID & 0x1FFFFFFFul;
436         msg.extendedID = (ID & 80000000ul) ? true : false;
437         msg.name = match.captured(2);
438         msg.len = match.captured(3).toUInt();
439         msg.sender = findNodeByName(match.captured(4));
440         if (!msg.sender) msg.sender = findNodeByIdx(0);
441         messageHandler->addMessage(msg);
442         msgPtr = messageHandler->findMsgByID(msg.ID);
443     }
444     else msgPtr = nullptr;
445     return msgPtr;
446 }
447 
parseSignalLine(QString line,DBC_MESSAGE * msg)448 DBC_SIGNAL* DBCFile::parseSignalLine(QString line, DBC_MESSAGE *msg)
449 {
450     QRegularExpression regex;
451     QRegularExpressionMatch match;
452 
453     int offset = 0;
454     bool isMessageMultiplexor = false;
455     //bool isMultiplexed = false;
456     DBC_SIGNAL sig;
457 
458     sig.multiplexLowValue = 0;
459     sig.multiplexHighValue = 0;
460     sig.isMultiplexed = false;
461     sig.isMultiplexor = false;
462 
463     qDebug() << "Found a SG line";
464     regex.setPattern("^SG\\_ *(\\w+) +M *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
465 
466     match = regex.match(line);
467     if (match.hasMatch())
468     {
469         qDebug() << "Multiplexor signal";
470         isMessageMultiplexor = true;
471         sig.isMultiplexor = true;
472     }
473     else
474     {
475         regex.setPattern("^SG\\_ *(\\w+) +m(\\d+) *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
476         match = regex.match(line);
477         if (match.hasMatch())
478         {
479             qDebug() << "Multiplexed signal";
480             //isMultiplexed = true;
481             sig.isMultiplexed = true;
482             sig.multiplexLowValue = match.captured(2).toInt();
483             sig.multiplexHighValue = sig.multiplexLowValue;
484             offset = 1;
485         }
486         else
487         {
488             regex.setPattern("^SG\\_ *(\\w+) +m(\\d+)M *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
489             match = regex.match(line);
490             if (match.hasMatch())
491             {
492                 qDebug() << "Extended Multiplexor Signal";
493                 sig.isMultiplexor = true; //we don't set the local isMessageMultiplexor variable because this isn't the top level multiplexor
494                 sig.isMultiplexed = true; //but, it is both a multiplexor and multiplexed
495                 sig.multiplexLowValue = match.captured(2).toInt();
496                 sig.multiplexHighValue = sig.multiplexLowValue;
497                 offset = 1;
498             }
499             else
500             {
501                 qDebug() << "standard signal";
502                 regex.setPattern("^SG\\_ *(\\w+) *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)");
503                 match = regex.match(line);
504                 sig.isMultiplexed = false;
505                 sig.isMultiplexor = false;
506             }
507         }
508     }
509 
510     //captured 1 is the signal name
511     //captured 2 would be multiplex value if this is a multiplex signal. Then offset the rest of these by 1
512     //captured 2 is the starting bit
513     //captured 3 is the length in bits
514     //captured 4 is the byte order / value type
515     //captured 5 specifies signed/unsigned for ints
516     //captured 6 is the scaling factor
517     //captured 7 is the offset
518     //captured 8 is the minimum value
519     //captured 9 is the maximum value
520     //captured 10 is the unit
521     //captured 11 is the receiving node
522 
523     if (match.hasMatch())
524     {
525         sig.name = match.captured(1);
526         sig.startBit = match.captured(2 + offset).toInt();
527         sig.signalSize = match.captured(3 + offset).toInt();
528         int val = match.captured(4 + offset).toInt();
529         if (val < 2)
530         {
531             if (match.captured(5 + offset) == "+") sig.valType = UNSIGNED_INT;
532             else sig.valType = SIGNED_INT;
533         }
534         switch (val)
535         {
536         case 0: //big endian mode
537             sig.intelByteOrder = false;
538             break;
539         case 1: //little endian mode
540             sig.intelByteOrder = true;
541             break;
542         case 2:
543             sig.valType = SP_FLOAT;
544             break;
545         case 3:
546             sig.valType = DP_FLOAT;
547             break;
548         case 4:
549             sig.valType = STRING;
550             break;
551         case 5: //single point float in little endian
552             sig.valType = SP_FLOAT;
553             sig.intelByteOrder = true;
554             break;
555         case 6: //double point float in little endian
556             sig.valType = DP_FLOAT;
557             sig.intelByteOrder = true;
558             break;
559         }
560         sig.factor = match.captured(6 + offset).toDouble();
561         sig.bias = match.captured(7 + offset).toDouble();
562         sig.min = match.captured(8 + offset).toDouble();
563         sig.max = match.captured(9 + offset).toDouble();
564         sig.unitName = match.captured(10 + offset);
565         if (match.captured(11 + offset).contains(','))
566         {
567             QString tmp = match.captured(11 + offset).split(',')[0];
568             sig.receiver = findNodeByName(tmp);
569         }
570         else sig.receiver = findNodeByName(match.captured(11 + offset));
571 
572         if (!sig.receiver) sig.receiver = findNodeByIdx(0); //apply default if there was no match
573 
574         sig.parentMessage = msg;
575         if (msg)
576         {
577             msg->sigHandler->addSignal(sig);
578             if (isMessageMultiplexor) msg->multiplexorSignal = msg->sigHandler->findSignalByName(sig.name);
579             return msg->sigHandler->findSignalByName(sig.name);
580         }
581         else return nullptr;
582     }
583 
584     return nullptr;
585 }
586 
587 //SG_MUL_VAL_ 2024 S1_PID_0D_VehicleSpeed S1 13-13;
parseSignalMultiplexValueLine(QString line)588 bool DBCFile::parseSignalMultiplexValueLine(QString line)
589 {
590     QRegularExpression regex;
591     QRegularExpressionMatch match;
592 
593     qDebug() << "Found a multiplex definition line";
594     regex.setPattern("^SG\\_MUL\\_VAL\\_ (\\d+) (\\w+) (\\w+) (\\d+)\\-(\\d+);");
595     match = regex.match(line);
596     //captured 1 is message ID
597     //Captured 2 is signal name
598     //Captured 3 is parent multiplexor
599     //captured 4 is the lower bound
600     //captured 5 is the upper bound
601     if (match.hasMatch())
602     {
603         DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toULong() & 0x1FFFFFFFUL);
604         if (msg != nullptr)
605         {
606             DBC_SIGNAL *thisSignal = msg->sigHandler->findSignalByName(match.captured(2));
607             if (thisSignal != nullptr)
608             {
609                 DBC_SIGNAL *parentSignal = msg->sigHandler->findSignalByName(match.captured(3));
610                 if (parentSignal != nullptr)
611                 {
612                     //now need to add "thisSignal" to the children multiplexed signals of "parentSignal"
613                     parentSignal->multiplexedChildren.append(thisSignal);
614                     thisSignal->multiplexParent = parentSignal;
615                     return true;
616                 }
617             }
618         }
619     }
620     return false;
621 }
622 
parseValueLine(QString line)623 bool DBCFile::parseValueLine(QString line)
624 {
625     QRegularExpression regex;
626     QRegularExpressionMatch match;
627 
628     qDebug() << "Found a value definition line";
629     regex.setPattern("^VAL\\_ (\\w+) (\\w+) (.*);");
630     match = regex.match(line);
631     //captured 1 is the ID to match against
632     //captured 2 is the signal name to match against
633     //captured 3 is a series of values in the form (number "text") that is, all sep'd by spaces
634     if (match.hasMatch())
635     {
636         //qDebug() << "Data was: " << match.captured(3);
637         DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toULong() & 0x1FFFFFFFul);
638         if (msg != nullptr)
639         {
640             DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2));
641             if (sig != nullptr)
642             {
643                 QString tokenString = match.captured(3);
644                 DBC_VAL_ENUM_ENTRY val;
645                 while (tokenString.length() > 2)
646                 {
647                     regex.setPattern("(\\d+) \\\"(.*?)\\\"(.*)");
648                     match = regex.match(tokenString);
649                     if (match.hasMatch())
650                     {
651                         val.value = match.captured(1).toULong() & 0x1FFFFFFFul;
652                         val.descript = match.captured(2);
653                         //qDebug() << "sig val " << val.value << " desc " <<val.descript;
654                         sig->valList.append(val);
655                         int rightSize = tokenString.length() - match.captured(1).length() - match.captured(2).length() - 4;
656                         if (rightSize > 0) tokenString = tokenString.right(rightSize);
657                         else tokenString = "";
658                         //qDebug() << "New token string: " << tokenString;
659                     }
660                     else tokenString = "";
661                 }
662                 return true;
663             }
664         }
665     }
666     return false;
667 }
668 
parseAttributeLine(QString line)669 bool DBCFile::parseAttributeLine(QString line)
670 {
671     QRegularExpression regex;
672     QRegularExpressionMatch match;
673 
674     regex.setPattern("^BA\\_ \\\"*(\\w+)\\\"* BO\\_ (\\d+) \\\"*([#\\w]+)\\\"*");
675     match = regex.match(line);
676     //captured 1 is the attribute name
677     //captured 2 is the message ID number (frame ID)
678     //captured 3 is the attribute value
679     if (match.hasMatch())
680     {
681         qDebug() << "Found an attribute setting line for a message";
682         DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1));
683         if (foundAttr)
684         {
685             qDebug() << "That attribute does exist";
686             DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toUInt() & 0x1FFFFFFFul);
687             if (foundMsg)
688             {
689                 qDebug() << "It references a valid, registered message";
690                 DBC_ATTRIBUTE_VALUE *foundAttrVal = foundMsg->findAttrValByName(match.captured(1));
691                 if (foundAttrVal) {
692                     foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType);
693                 }
694                 else
695                 {
696                     DBC_ATTRIBUTE_VALUE val;
697                     val.attrName = match.captured(1);
698                     val.value = processAttributeVal(match.captured(3), foundAttr->valType);
699                     foundMsg->attributes.append(val);
700                 }
701             }
702         }
703     }
704 
705     regex.setPattern("^BA\\_ \\\"*(\\w+)\\\"* SG\\_ (\\d+) \\\"*(\\w+)\\\"* \\\"*([#\\w]+)\\\"*");
706     match = regex.match(line);
707     //captured 1 is the attribute name
708     //captured 2 is the message ID number (frame ID)
709     //captured 3 is the signal name to bind to
710     //captured 4 is the attribute value
711     if (match.hasMatch())
712     {
713         qDebug() << "Found an attribute setting line for a signal";
714         DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1));
715         if (foundAttr)
716         {
717             qDebug() << "That attribute does exist";
718             DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toUInt() & 0x1FFFFFFFUL);
719             if (foundMsg)
720             {
721                 qDebug() << "It references a valid, registered message";
722                 DBC_SIGNAL *foundSig = foundMsg->sigHandler->findSignalByName(match.captured(3));
723                 if (foundSig)
724                 {
725                     DBC_ATTRIBUTE_VALUE *foundAttrVal = foundSig->findAttrValByName(match.captured(1));
726                     if (foundAttrVal) foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType);
727                     else
728                     {
729                         DBC_ATTRIBUTE_VALUE val;
730                         val.attrName = match.captured(1);
731                         val.value = processAttributeVal(match.captured(3), foundAttr->valType);
732                         foundSig->attributes.append(val);
733                     }
734                 }
735             }
736         }
737     }
738 
739     regex.setPattern("^BA\\_ \\\"*(\\w+)\\\"* BU\\_ \\\"*(\\w+)\\\"* \\\"*([#\\w]+)\\\"*");
740     match = regex.match(line);
741     //captured 1 is the attribute name
742     //captured 2 is the name of the node
743     //captured 3 is the attribute value
744     if (match.hasMatch())
745     {
746         qDebug() << "Found an attribute setting line for a node";
747         DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1));
748         if (foundAttr)
749         {
750             qDebug() << "That attribute does exist";
751             DBC_NODE *foundNode = findNodeByName(match.captured(2));
752             if (foundNode)
753             {
754                 qDebug() << "References a valid node name";
755                 DBC_ATTRIBUTE_VALUE *foundAttrVal = foundNode->findAttrValByName(match.captured(1));
756                 if (foundAttrVal) foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType);
757                 else
758                 {
759                     DBC_ATTRIBUTE_VALUE val;
760                     val.attrName = match.captured(1);
761                     val.value = processAttributeVal(match.captured(3), foundAttr->valType);
762                     foundNode->attributes.append(val);
763                 }
764             }
765             return true;
766         }
767     }
768 
769     return false;
770 }
771 
parseDefaultAttrLine(QString line)772 bool DBCFile::parseDefaultAttrLine(QString line)
773 {
774     QRegularExpression regex;
775     QRegularExpressionMatch match;
776 
777     regex.setPattern("^BA\\_DEF\\_DEF\\_ \\\"*(\\w+)\\\"* \\\"*([#\\w]*)\\\"*");
778     match = regex.match(line);
779     //captured 1 is the name of the attribute
780     //captured 2 is the default value for that attribute
781     if (match.hasMatch())
782     {
783         qDebug() << "Found an attribute default value line, searching for an attribute named " << match.captured(1) << "with data " << match.captured(2);
784         DBC_ATTRIBUTE *found = findAttributeByName(match.captured(1));
785         if (found)
786         {
787             switch (found->valType)
788             {
789             case ATTR_STRING:
790                 found->defaultValue = match.captured(2);
791                 break;
792             case ATTR_FLOAT:
793                 found->defaultValue = match.captured(2).toFloat();
794                 break;
795             case ATTR_INT:
796                 found->defaultValue = match.captured(2).toInt();
797                 break;
798             case ATTR_ENUM:
799                 QString temp = match.captured(2);
800                 found->defaultValue = 0;
801                 for (int x = 0; x < found->enumVals.count(); x++)
802                 {
803                     if (!found->enumVals[x].compare(temp, Qt::CaseInsensitive))
804                     {
805                         found->defaultValue = x;
806                         break;
807                     }
808                 }
809             }
810             qDebug() << "Matched an attribute. Setting default value to " << found->defaultValue;
811             return true;
812         }
813     }
814     return false;
815 }
816 
loadFile(QString fileName)817 bool DBCFile::loadFile(QString fileName)
818 {
819     QFile *inFile = new QFile(fileName);
820     QString line, rawLine;
821     QRegularExpression regex;
822     QRegularExpressionMatch match;
823     DBC_MESSAGE *currentMessage = nullptr;
824     DBC_ATTRIBUTE attr;
825     int numSigFaults = 0, numMsgFaults = 0;
826     int linesSinceYield = 0;
827 
828     bool inMultilineBU = false;
829 
830     qDebug() << "DBC File: " << fileName;
831 
832     if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
833     {
834         delete inFile;
835         qDebug() << "Could not load the file!";
836         return false;
837     }
838 
839     qDebug() << "Starting DBC load";
840     dbc_nodes.clear();
841     messageHandler->removeAllMessages();
842     messageHandler->setMatchingCriteria(EXACT);
843     messageHandler->setFilterLabeling(false);
844 
845     DBC_NODE falseNode;
846     falseNode.name = "Vector__XXX";
847     falseNode.comment = "Default node if none specified";
848     dbc_nodes.append(falseNode);
849 
850     while (!inFile->atEnd()) {
851         rawLine = QString(inFile->readLine());
852         line = rawLine.simplified();
853 
854         linesSinceYield++;
855         if (linesSinceYield > 100)
856         {
857             qApp->processEvents();
858         }
859 
860         if (inMultilineBU)
861         {
862             if (rawLine.startsWith("\t") || rawLine.startsWith("   "))
863             {
864                 DBC_NODE node;
865                 node.name = line;
866                 dbc_nodes.append(node);
867             }
868             else inMultilineBU = false;
869         }
870 
871         if (!inMultilineBU)
872         {
873             if (line.startsWith("BO_ ")) //defines a message
874             {
875                 currentMessage = parseMessageLine(line);
876                 if (currentMessage == nullptr) numMsgFaults++;
877             }
878             if (line.startsWith("SG_ ")) //defines a signal
879             {
880                 if (!parseSignalLine(line, currentMessage)) numSigFaults++;
881             }
882 
883             if (line.startsWith("SG_MUL_VAL_ ")) //defines a signal multiplexing value definition
884             {
885                 if (!parseSignalMultiplexValueLine(line)) numSigFaults++;
886             }
887 
888             if (line.startsWith("BU_:")) //line specifies the nodes on this canbus
889             {
890                 qDebug() << "Found a BU line";
891                 regex.setPattern("^BU\\_\\:(.*)");
892                 match = regex.match(line);
893                 //captured 1 = a list of node names separated by spaces. No idea how many yet
894                 if (match.hasMatch())
895                 {
896                     QStringList nodeStrings = match.captured(1).split(' ');
897                     qDebug() << "Found " << nodeStrings.count() << " node names";
898                     for (int i = 0; i < nodeStrings.count(); i++)
899                     {
900                         //qDebug() << nodeStrings[i];
901                         if (nodeStrings[i].length() > 1)
902                         {
903                             DBC_NODE node;
904                             node.name = nodeStrings[i];
905                             dbc_nodes.append(node);
906                         }
907                     }
908                     inMultilineBU = true; //we might be... Need to check next line.
909                 }
910             }
911             if (line.startsWith("CM_ SG_ "))
912             {
913                 qDebug() << "Found an SG comment line";
914                 regex.setPattern("^CM\\_ SG\\_ *(\\w+) *(\\w+) *\\\"(.*)\\\";");
915                 match = regex.match(line);
916                 //captured 1 is the ID to match against to get to the message
917                 //captured 2 is the signal name from that message
918                 //captured 3 is the comment itself
919                 if (match.hasMatch())
920                 {
921                     //qDebug() << "Comment was: " << match.captured(3);
922                     DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toUInt());
923                     if (msg != nullptr)
924                     {
925                         DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2));
926                         if (sig != nullptr)
927                         {
928                             sig->comment = match.captured(3);
929                         }
930                     }
931                 }
932             }
933             if (line.startsWith("CM_ BO_ "))
934             {
935                 qDebug() << "Found a BO comment line";
936                 regex.setPattern("^CM\\_ BO\\_ *(\\w+) *\\\"(.*)\\\";");
937                 match = regex.match(line);
938                 //captured 1 is the ID to match against to get to the message
939                 //captured 2 is the comment itself
940                 if (match.hasMatch())
941                 {
942                     //qDebug() << "Comment was: " << match.captured(2);
943                     DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toUInt());
944                     if (msg != nullptr)
945                     {
946                         msg->comment = match.captured(2);
947                     }
948                 }
949             }
950             if (line.startsWith("CM_ BU_ "))
951             {
952                 qDebug() << "Found a BU comment line";
953                 regex.setPattern("^CM\\_ BU\\_ *(\\w+) *\\\"(.*)\\\";");
954                 match = regex.match(line);
955                 //captured 1 is the Node name
956                 //captured 2 is the comment itself
957                 if (match.hasMatch())
958                 {
959                     //qDebug() << "Comment was: " << match.captured(2);
960                     DBC_NODE *node = findNodeByName(match.captured(1));
961                     if (node != nullptr)
962                     {
963                         node->comment = match.captured(2);
964                     }
965                 }
966             }
967             //VAL_ (1090) (VCUPresentParkLightOC) (1 "Error present" 0 "Error not present") ;
968             if (line.startsWith("VAL_ "))
969             {
970                 parseValueLine(line);
971             }
972 
973             if (line.startsWith("BA_DEF_ SG_ "))
974             {
975                 qDebug() << "Found a SG attribute line";
976 
977                 if (parseAttribute(line.right(line.length() - 12), attr))
978                 {
979                     //qDebug() << "Success";
980                     attr.attrType = ATTR_TYPE_SIG;
981                     dbc_attributes.append(attr);
982                 }
983             }
984             else if (line.startsWith("BA_DEF_ BO_ ")) //definition of a message attribute
985             {
986                 qDebug() << "Found a BO attribute line";
987 
988                 if (parseAttribute(line.right(line.length() - 12), attr))
989                 {
990                     qDebug() << "Success";
991                     attr.attrType = ATTR_TYPE_MESSAGE;
992                     dbc_attributes.append(attr);
993                 }
994             }
995             else if (line.startsWith("BA_DEF_ BU_ ")) //definition of a node attribute
996             {
997                 qDebug() << "Found a BU attribute line";
998 
999                 if (parseAttribute(line.right(line.length() - 12), attr))
1000                 {
1001                     //qDebug() << "Success";
1002                     attr.attrType = ATTR_TYPE_NODE;
1003                     dbc_attributes.append(attr);
1004                 }
1005             }
1006 
1007             else if (line.startsWith("BA_DEF_ ")) //definition of a root attribute
1008             {
1009                 qDebug() << "Found a BU attribute line";
1010 
1011                 if (parseAttribute(line.right(line.length() - 9), attr))
1012                 {
1013                     //qDebug() << "Success";
1014                     attr.attrType = ATTR_TYPE_GENERAL;
1015                     dbc_attributes.append(attr);
1016                 }
1017             }
1018 
1019 
1020             if (line.startsWith("BA_DEF_DEF_ ")) //definition of default value for an attribute
1021             {
1022                 parseDefaultAttrLine(line);
1023             }
1024 
1025             //BA_ "GenMsgCycleTime" BO_ 101 100;
1026             if (line.startsWith("BA_ ")) //set value of attribute
1027             {
1028                 parseAttributeLine(line);
1029             }
1030         }
1031     }
1032 
1033     //upon loading the file add our custom foreground and background color attributes if they don't exist already
1034     DBC_ATTRIBUTE *bgAttr = findAttributeByName("GenMsgBackgroundColor");
1035     if (!bgAttr)
1036     {
1037         attr.attrType = ATTR_TYPE_MESSAGE;
1038         attr.defaultValue = QApplication::palette().color(QPalette::Base).name();
1039         attr.enumVals.clear();
1040         attr.lower = 0;
1041         attr.upper = 0;
1042         attr.name = "GenMsgBackgroundColor";
1043         attr.valType = ATTR_STRING;
1044         dbc_attributes.append(attr);
1045         bgAttr = findAttributeByName("GenMsgBackgroundColor");
1046     }
1047 
1048     DBC_ATTRIBUTE *fgAttr = findAttributeByName("GenMsgForegroundColor");
1049     if (!fgAttr)
1050     {
1051         attr.attrType = ATTR_TYPE_MESSAGE;
1052         attr.defaultValue = QApplication::palette().color(QPalette::WindowText).name();
1053         attr.enumVals.clear();
1054         attr.lower = 0;
1055         attr.upper = 0;
1056         attr.name = "GenMsgForegroundColor";
1057         attr.valType = ATTR_STRING;
1058         dbc_attributes.append(attr);
1059         fgAttr = findAttributeByName("GenMsgForegroundColor");
1060     }
1061 
1062     DBC_ATTRIBUTE *mc_attr = findAttributeByName("matchingcriteria");
1063     if (mc_attr)
1064     {
1065         messageHandler->setMatchingCriteria((MatchingCriteria_t)mc_attr->defaultValue.toInt());
1066     }
1067     else
1068     {
1069         messageHandler->setMatchingCriteria(EXACT);
1070     }
1071 
1072     DBC_ATTRIBUTE *fl_attr = findAttributeByName("filterlabeling");
1073     if (fl_attr)
1074     {
1075         messageHandler->setFilterLabeling(fl_attr->defaultValue.toInt());
1076     }
1077     else
1078     {
1079         messageHandler->setFilterLabeling(false);
1080     }
1081 
1082     QColor DefaultBG = QColor(bgAttr->defaultValue.toString());
1083     QColor DefaultFG = QColor(fgAttr->defaultValue.toString());
1084 
1085     DBC_ATTRIBUTE_VALUE *thisBG;
1086     DBC_ATTRIBUTE_VALUE *thisFG;
1087 
1088     for (int x = 0; x < messageHandler->getCount(); x++)
1089     {
1090         DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x);
1091         msg->bgColor = DefaultBG;
1092         msg->fgColor = DefaultFG;
1093         thisBG = msg->findAttrValByName("GenMsgBackgroundColor");
1094         thisFG = msg->findAttrValByName("GenMsgForegroundColor");
1095         if (thisBG) msg->bgColor = QColor(thisBG->value.toString());
1096         if (thisFG) msg->fgColor = QColor(thisFG->value.toString());
1097         for (int y = 0; y < msg->sigHandler->getCount(); y++)
1098         {
1099             DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(y);
1100             //if this doesn't have a multiplex parent set but is multiplexed then it must have used
1101             //simple multiplexing instead of any extended specification. So, fill in the multiplexor signal here
1102             //and also write the extended entry for it too.
1103             if (sig->isMultiplexed && (sig->multiplexParent == nullptr) && (msg->multiplexorSignal) )
1104             {
1105                 sig->multiplexParent = msg->multiplexorSignal;
1106                 msg->multiplexorSignal->multiplexedChildren.append(sig);
1107             }
1108             if ( sig->isMultiplexed && (!msg->multiplexorSignal) ) //marked multiplexed but there is no multiplexor.
1109             {
1110                 sig->isMultiplexed = false; //can't multiplex if there is no multiplexor!
1111             }
1112         }
1113     }
1114 
1115     if (numSigFaults > 0 || numMsgFaults > 0)
1116     {
1117         QMessageBox msgBox;
1118         QString msg = "DBC file loaded with errors!\n";
1119         msg += "Number of faulty message entries: " + QString::number(numMsgFaults) + "\n";
1120         msg += "Number of faulty signal entries: " + QString::number(numSigFaults) + "\n\n";
1121         msg += "Faulty entries have not been loaded.\n\n";
1122         msg += "All other entries are, however, loaded.";
1123         msgBox.setText(msg);
1124         msgBox.exec();
1125     }
1126     inFile->close();
1127     delete inFile;
1128     QStringList fileList = fileName.split('/');
1129     this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function.
1130     filePath = fileName.left(fileName.length() - this->fileName.length());
1131     assocBuses = -1;
1132     isDirty = false;
1133     return true;
1134 }
1135 
processAttributeVal(QString input,DBC_ATTRIBUTE_VAL_TYPE typ)1136 QVariant DBCFile::processAttributeVal(QString input, DBC_ATTRIBUTE_VAL_TYPE typ)
1137 {
1138     QVariant out;
1139     switch (typ)
1140     {
1141     case ATTR_STRING:
1142         out = input;
1143         break;
1144     case ATTR_FLOAT:
1145         out = input.toFloat();
1146         break;
1147     case ATTR_INT:
1148     case ATTR_ENUM:
1149         out = input.toInt();
1150         break;
1151     }
1152     return out;
1153 }
1154 
parseAttribute(QString inpString,DBC_ATTRIBUTE & attr)1155 bool DBCFile::parseAttribute(QString inpString, DBC_ATTRIBUTE &attr)
1156 {
1157     bool goodAttr = false;
1158     QRegularExpression regex;
1159     QRegularExpressionMatch match;
1160 
1161     regex.setPattern("\\\"*(\\w+)\\\"* \\\"*(\\w+)\\\"* (\\d+) (\\d+)");
1162     match = regex.match(inpString);
1163     //captured 1 is the name of the attribute to set up
1164     //captured 2 is the type of signal attribute to create.
1165     //captured 3 is the lower bound value for this attribute
1166     //captured 4 is the upper bound value for this attribute
1167     if (match.hasMatch())
1168     {
1169         qDebug() << "Parsing an attribute with low/high values";
1170         attr.name = match.captured(1);
1171         QString typ = match.captured(2);
1172         attr.attrType = ATTR_TYPE_SIG;
1173         attr.lower = 0;
1174         attr.upper = 0;
1175         attr.valType = ATTR_STRING;
1176         if (!typ.compare("INT", Qt::CaseInsensitive))
1177         {
1178             qDebug() << "INT attribute named " << attr.name;
1179             attr.valType = ATTR_INT;
1180             attr.lower = match.captured(3).toInt();
1181             attr.upper = match.captured(4).toInt();
1182             goodAttr = true;
1183         }
1184         if (!typ.compare("FLOAT", Qt::CaseInsensitive))
1185         {
1186             qDebug() << "FLOAT attribute named " << attr.name;
1187             attr.valType = ATTR_FLOAT;
1188             attr.lower = match.captured(3).toDouble();
1189             attr.upper = match.captured(4).toDouble();
1190             goodAttr = true;
1191         }
1192         if (!typ.compare("STRING", Qt::CaseInsensitive))
1193         {
1194             qDebug() << "STRING attribute named " << attr.name;
1195             attr.valType = ATTR_STRING;
1196             goodAttr = true;
1197         }
1198     }
1199     else
1200     {
1201         regex.setPattern("\\\"*(\\w+)\\\"* \\\"*(\\w+)\\\"* (.*)");
1202         match = regex.match(inpString);
1203         //Same as above but no upper/lower bound values.
1204         if (match.hasMatch())
1205         {
1206             qDebug() << "Parsing an attribute without boundaries";
1207             attr.name = match.captured(1);
1208             QString typ = match.captured(2);
1209             attr.lower = 0;
1210             attr.upper = 0;
1211             attr.attrType = ATTR_TYPE_SIG;
1212 
1213             if (!typ.compare("INT", Qt::CaseInsensitive))
1214             {
1215                 qDebug() << "INT attribute named " << attr.name;
1216                 attr.valType = ATTR_INT;
1217                 goodAttr = true;
1218             }
1219 
1220             if (!typ.compare("FLOAT", Qt::CaseInsensitive))
1221             {
1222                 qDebug() << "FLOAT attribute named " << attr.name;
1223                 attr.valType = ATTR_FLOAT;
1224                 goodAttr = true;
1225             }
1226 
1227             if (!typ.compare("STRING", Qt::CaseInsensitive))
1228             {
1229                 qDebug() << "STRING attribute named " << attr.name;
1230                 attr.valType = ATTR_STRING;
1231                 goodAttr = true;
1232             }
1233 
1234             if (!typ.compare("ENUM", Qt::CaseInsensitive))
1235             {
1236                 qDebug() << "ENUM attribute named " << attr.name;
1237                 QStringList enumLst = match.captured(3).split(',');
1238                 foreach (QString enumStr, enumLst)
1239                 {
1240                     attr.enumVals.append(Utility::unQuote(enumStr));
1241                     qDebug() << "Enum value: " << enumStr;
1242                 }
1243                 attr.valType = ATTR_ENUM;
1244                 goodAttr = true;
1245             }
1246         }
1247     }
1248     return goodAttr;
1249 }
1250 
saveFile(QString fileName)1251 bool DBCFile::saveFile(QString fileName)
1252 {
1253     int nodeNumber = 1;
1254     int msgNumber = 1;
1255     int sigNumber = 1;
1256     QFile *outFile = new QFile(fileName);
1257     QString nodesOutput, msgOutput, commentsOutput, valuesOutput;
1258     QString defaultsOutput, attrValOutput;
1259     bool hasExtendedMultiplexing = false;
1260 
1261     if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
1262     {
1263         delete outFile;
1264         return false;
1265     }
1266 
1267     //right now it outputs a standard hard coded boilerplate
1268     outFile->write("VERSION \"\"\n");
1269     outFile->write("\n");
1270     outFile->write("\n");
1271     outFile->write("NS_ :\n");
1272     outFile->write("    NS_DESC_\n");
1273     outFile->write("    CM_\n");
1274     outFile->write("    BA_DEF_\n");
1275     outFile->write("    BA_\n");
1276     outFile->write("    VAL_\n");
1277     outFile->write("    CAT_DEF_\n");
1278     outFile->write("    CAT_\n");
1279     outFile->write("    FILTER\n");
1280     outFile->write("    BA_DEF_DEF_\n");
1281     outFile->write("    EV_DATA_\n");
1282     outFile->write("    ENVVAR_DATA_\n");
1283     outFile->write("    SGTYPE_\n");
1284     outFile->write("    SGTYPE_VAL_\n");
1285     outFile->write("    BA_DEF_SGTYPE_\n");
1286     outFile->write("    BA_SGTYPE_\n");
1287     outFile->write("    SIG_TYPE_REF_\n");
1288     outFile->write("    VAL_TABLE_\n");
1289     outFile->write("    SIG_GROUP_\n");
1290     outFile->write("    SIG_VALTYPE_\n");
1291     outFile->write("    SIGTYPE_VALTYPE_\n");
1292     outFile->write("    BO_TX_BU_\n");
1293     outFile->write("    BA_DEF_REL_\n");
1294     outFile->write("    BA_REL_\n");
1295     outFile->write("    BA_DEF_DEF_REL_\n");
1296     outFile->write("    BU_SG_REL_\n");
1297     outFile->write("    BU_EV_REL_\n");
1298     outFile->write("    BU_BO_REL_\n");
1299     outFile->write("    SG_MUL_VAL_\n");
1300     outFile->write("\n");
1301     outFile->write("BS_: \n");
1302 
1303     //Build list of nodes line
1304     nodesOutput.append("BU_: ");
1305     for (int x = 0; x < dbc_nodes.count(); x++)
1306     {
1307         DBC_NODE node = dbc_nodes[x];
1308         if (node.name.compare("Vector__XXX", Qt::CaseInsensitive) != 0)
1309         {
1310             if (node.name.length() < 1) //detect an empty string and fill it out with something
1311             {
1312                 node.name = "NODE" + QString::number(nodeNumber);
1313                 nodeNumber++;
1314             }
1315             nodesOutput.append(node.name + " ");
1316             if (node.comment.length() > 0)
1317             {
1318                 commentsOutput.append("CM_ BU_ " + node.name + " \"" + node.comment + "\";\n");
1319             }
1320             if (node.attributes.count() > 0)
1321             {
1322                 foreach (DBC_ATTRIBUTE_VALUE val, node.attributes) {
1323                     attrValOutput.append("BA_ \"" + val.attrName + "\" BU_ ");
1324                     switch (val.value.type())
1325                     {
1326                     case QMetaType::QString:
1327                         attrValOutput.append("\"" + val.value.toString() + "\";\n");
1328                         break;
1329                     default:
1330                         attrValOutput.append(val.value.toString() + ";\n");
1331                         break;
1332                     }
1333                 }
1334             }
1335         }
1336     }
1337     nodesOutput.append("\n");
1338     outFile->write(nodesOutput.toUtf8());
1339 
1340     //Go through all messages one at at time issuing the message line then all signals in there too.
1341     for (int x = 0; x < messageHandler->getCount(); x++)
1342     {
1343         DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x);
1344 
1345         if (msg->name.length() < 1) //detect an empty string and fill it out with something
1346         {
1347             msg->name = "MSG" + QString::number(msgNumber);
1348             msgNumber++;
1349         }
1350 
1351         uint32_t ID = msg->ID;
1352         if (msg->ID > 0x7FF || msg->extendedID) msg->ID += 0x80000000ul; //set bit 31 if this ID is extended.
1353 
1354         msgOutput.append("BO_ " + QString::number(ID) + " " + msg->name + ": " + QString::number(msg->len) +
1355                          " " + msg->sender->name + "\n");
1356         if (msg->comment.length() > 0)
1357         {
1358             commentsOutput.append("CM_ BO_ " + QString::number(ID) + " \"" + msg->comment + "\";\n");
1359         }
1360 
1361         //If this message has attributes then compile them into attributes list to output later on.
1362         if (msg->attributes.count() > 0)
1363         {
1364             foreach (DBC_ATTRIBUTE_VALUE val, msg->attributes) {
1365                 attrValOutput.append("BA_ \"" + val.attrName + "\" BO_ " + QString::number(ID) + " ");
1366                 switch (val.value.type())
1367                 {
1368                 case QMetaType::QString:
1369                     attrValOutput.append("\"" + val.value.toString() + "\";\n");
1370                     break;
1371                 default:
1372                     attrValOutput.append(val.value.toString() + ";\n");
1373                     break;
1374                 }
1375             }
1376         }
1377 
1378         for (int s = 0; s < msg->sigHandler->getCount(); s++)
1379         {
1380             DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s);
1381 
1382             if (sig->name.length() < 1) //detect an empty string and fill it out with something
1383             {
1384                 sig->name = "SIG" + QString::number(sigNumber);
1385                 sigNumber++;
1386             }
1387 
1388             msgOutput.append("   SG_ " + sig->name);
1389 
1390             if (sig->isMultiplexed)
1391             {
1392                 msgOutput.append(" m" + QString::number(sig->multiplexLowValue));
1393             }
1394             if (sig->isMultiplexor)
1395             {
1396                 if (!sig->isMultiplexed) msgOutput.append(" ");
1397                 msgOutput.append("M");
1398             }
1399             //check for the two telltale signs that we've got extended multiplexing going on.
1400             if (sig->isMultiplexed && sig->isMultiplexor) hasExtendedMultiplexing = true;
1401             if (sig->multiplexLowValue != sig->multiplexHighValue) hasExtendedMultiplexing = true;
1402 
1403             msgOutput.append(" : " + QString::number(sig->startBit) + "|" + QString::number(sig->signalSize) + "@");
1404 
1405             switch (sig->valType)
1406             {
1407             case UNSIGNED_INT:
1408                 if (sig->intelByteOrder) msgOutput.append("1+");
1409                 else msgOutput.append("0+");
1410                 break;
1411             case SIGNED_INT:
1412                 if (sig->intelByteOrder) msgOutput.append("1-");
1413                 else msgOutput.append("0-");
1414                 break;
1415             case SP_FLOAT:
1416                 if (sig->intelByteOrder) msgOutput.append("5-");
1417                 else msgOutput.append("2-");
1418                 break;
1419             case DP_FLOAT:
1420                 if (sig->intelByteOrder) msgOutput.append("6-");
1421                 else msgOutput.append("3-");
1422                 break;
1423             case STRING:
1424                 msgOutput.append("4-");
1425                 break;
1426             default:
1427                 msgOutput.append("0-");
1428                 break;
1429             }
1430             msgOutput.append(" (" + QString::number(sig->factor) + "," + QString::number(sig->bias) + ") [" +
1431                              QString::number(sig->min) + "|" + QString::number(sig->max) + "] \"" + sig->unitName
1432                              + "\" " + sig->receiver->name + "\n");
1433             if (sig->comment.length() > 0)
1434             {
1435                 commentsOutput.append("CM_ SG_ " + QString::number(ID) + " " + sig->name + " \"" + sig->comment + "\";\n");
1436             }
1437 
1438             //if this signal has attributes then compile them in a special list of attributes
1439             if (sig->attributes.count() > 0)
1440             {
1441                 foreach (DBC_ATTRIBUTE_VALUE val, sig->attributes) {
1442                     attrValOutput.append("BA_ \"" + val.attrName + "\" SG_ " + QString::number(ID) + " " + sig->name + " ");
1443                     switch (val.value.type())
1444                     {
1445                     case QMetaType::QString:
1446                         attrValOutput.append("\"" + val.value.toString() + "\";\n");
1447                         break;
1448                     default:
1449                         attrValOutput.append(val.value.toString() + ";\n");
1450                         break;
1451                     }
1452                 }
1453             }
1454 
1455             if (sig->valList.count() > 0)
1456             {
1457                 valuesOutput.append("VAL_ " + QString::number(ID) + " " + sig->name);
1458                 for (int v = 0; v < sig->valList.count(); v++)
1459                 {
1460                     DBC_VAL_ENUM_ENTRY val = sig->valList[v];
1461                     valuesOutput.append(" " + QString::number(val.value) + " \"" + val.descript +"\"");
1462                 }
1463                 valuesOutput.append(";\n");
1464             }
1465         }
1466         msgOutput.append("\n");
1467         //write it out every message so the string doesn't end up too huge
1468         outFile->write(msgOutput.toUtf8());
1469         msgOutput.clear(); //got to reset it after writing
1470     }
1471 
1472     //Now dump out all of the stored attributes
1473     for (int x = 0; x < dbc_attributes.count(); x++)
1474     {
1475         msgOutput.append("BA_DEF_ ");
1476         switch (dbc_attributes[x].attrType)
1477         {
1478         case ATTR_TYPE_GENERAL:
1479             break;
1480         case ATTR_TYPE_NODE:
1481             msgOutput.append("BU_ ");
1482             break;
1483         case ATTR_TYPE_MESSAGE:
1484             msgOutput.append("BO_ ");
1485             break;
1486         case ATTR_TYPE_SIG:
1487             msgOutput.append("SG_ ");
1488             break;
1489         }
1490 
1491         msgOutput.append("\"" + dbc_attributes[x].name + "\" ");
1492 
1493         switch (dbc_attributes[x].valType)
1494         {
1495         case ATTR_INT:
1496             msgOutput.append("INT " + QString::number(dbc_attributes[x].lower) + " " + QString::number(dbc_attributes[x].upper));
1497             break;
1498         case ATTR_FLOAT:
1499             msgOutput.append("FLOAT " + QString::number(dbc_attributes[x].lower) + " " + QString::number(dbc_attributes[x].upper));
1500             break;
1501         case ATTR_STRING:
1502             msgOutput.append("STRING ");
1503             break;
1504         case ATTR_ENUM:
1505             msgOutput.append("ENUM ");
1506             foreach (QString str, dbc_attributes[x].enumVals)
1507             {
1508                 msgOutput.append("\"" + str + "\",");
1509             }
1510             msgOutput.truncate(msgOutput.length() - 1); //remove trailing ,
1511             break;
1512         }
1513 
1514         msgOutput.append(";\n");
1515 
1516         outFile->write(msgOutput.toUtf8());
1517         msgOutput.clear(); //got to reset it after writing
1518 
1519         if (dbc_attributes[x].defaultValue.isValid())
1520         {
1521             defaultsOutput.append("BA_DEF_DEF_ \"" + dbc_attributes[x].name + "\" ");
1522             switch (dbc_attributes[x].valType)
1523             {
1524             case ATTR_STRING:
1525                 defaultsOutput.append("\"" + dbc_attributes[x].defaultValue.toString() + "\";\n");
1526                 break;
1527             case ATTR_ENUM:
1528                 defaultsOutput.append("\"" + dbc_attributes[x].enumVals[dbc_attributes[x].defaultValue.toInt()] + "\";\n");
1529                 break;
1530             case ATTR_INT:
1531             case ATTR_FLOAT:
1532                 defaultsOutput.append(dbc_attributes[x].defaultValue.toString() + ";\n");
1533                 break;
1534             }
1535         }
1536     }
1537 
1538     //extended multiplexing uses SG_MUL_VAL_ to specify the relationships. If a signal is marked
1539     //as multiplexed then output a record for it. That's the most complete option. We've already
1540     //given the single value multiplex above for backward compatibility with things that don't support extended mode
1541     if (hasExtendedMultiplexing)
1542     {
1543         for (int x = 0; x < messageHandler->getCount(); x++)
1544         {
1545             DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x);
1546 
1547             uint32_t ID = msg->ID;
1548             if (msg->ID > 0x7FF || msg->extendedID) msg->ID += 0x80000000ul; //set bit 31 if this ID is extended.
1549 
1550             for (int s = 0; s < msg->sigHandler->getCount(); s++)
1551             {
1552                 DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s);
1553 
1554                 if (sig->isMultiplexed)
1555                 {
1556                     msgOutput.append("SG_MUL_VAL_ " + QString::number(ID) + " ");
1557                     msgOutput.append(sig->name + " " + sig->parentMessage->name + " ");
1558                     msgOutput.append(QString::number(sig->multiplexLowValue) + "-" + QString::number(sig->multiplexHighValue) + ";");
1559                     msgOutput.append("\n");
1560                     outFile->write(msgOutput.toUtf8());
1561                     msgOutput.clear(); //got to reset it after writing
1562                 }
1563             }
1564         }
1565     }
1566 
1567     //now write out all of the accumulated comments and value tables from above
1568     outFile->write(attrValOutput.toUtf8());
1569     outFile->write(defaultsOutput.toUtf8());
1570     outFile->write(commentsOutput.toUtf8());
1571     outFile->write(valuesOutput.toUtf8());
1572 
1573     attrValOutput.clear();
1574     defaultsOutput.clear();
1575     commentsOutput.clear();
1576     valuesOutput.clear();
1577 
1578     outFile->close();
1579     delete outFile;
1580 
1581     isDirty = false;
1582 
1583     QStringList fileList = fileName.split('/');
1584     this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function.
1585     filePath = fileName.left(fileName.length() - this->fileName.length());
1586     return true;
1587 }
1588 
saveDBCFile(int idx)1589 void DBCHandler::saveDBCFile(int idx)
1590 {
1591     QSettings settings;
1592 
1593     if (loadedFiles.count() == 0) return;
1594     if (idx < 0) return;
1595     if (idx >= loadedFiles.count()) return;
1596 
1597     QString filename;
1598     QFileDialog dialog;
1599 
1600     QStringList filters;
1601     filters.append(QString(tr("DBC File (*.dbc)")));
1602 
1603     dialog.setDirectory(settings.value("DBC/LoadSaveDirectory", dialog.directory().path()).toString());
1604     dialog.setFileMode(QFileDialog::AnyFile);
1605     dialog.setNameFilters(filters);
1606     dialog.setViewMode(QFileDialog::Detail);
1607     dialog.setAcceptMode(QFileDialog::AcceptSave);
1608     dialog.selectFile(loadedFiles[idx].getFullFilename());
1609 
1610     if (dialog.exec() == QDialog::Accepted)
1611     {
1612         filename = dialog.selectedFiles()[0];
1613         if (!filename.contains('.')) filename += ".dbc";
1614         loadedFiles[idx].saveFile(filename);
1615         settings.setValue("DBC/LoadSaveDirectory", dialog.directory().path());
1616     }
1617 }
1618 
createBlankFile()1619 int DBCHandler::createBlankFile()
1620 {
1621     DBCFile newFile;
1622     DBC_ATTRIBUTE attr;
1623 
1624     //add our custom attributes to the new file so that we know they're already there.
1625     attr.attrType = ATTR_TYPE_MESSAGE;
1626     attr.defaultValue = QApplication::palette().color(QPalette::Base).name();
1627     qDebug() << attr.defaultValue;
1628     attr.enumVals.clear();
1629     attr.lower = 0;
1630     attr.upper = 0;
1631     attr.name = "GenMsgBackgroundColor";
1632     attr.valType = ATTR_STRING;
1633     newFile.dbc_attributes.append(attr);
1634 
1635     attr.attrType = ATTR_TYPE_MESSAGE;
1636     attr.defaultValue = QApplication::palette().color(QPalette::WindowText).name();
1637     qDebug() << attr.defaultValue;
1638     attr.enumVals.clear();
1639     attr.lower = 0;
1640     attr.upper = 0;
1641     attr.name = "GenMsgForegroundColor";
1642     attr.valType = ATTR_STRING;
1643     newFile.dbc_attributes.append(attr);
1644 
1645     attr.attrType = ATTR_TYPE_MESSAGE;
1646     attr.defaultValue = 0;
1647     attr.enumVals.clear();
1648     attr.lower = 0;
1649     attr.upper = 0;
1650     attr.name = "matchingcriteria";
1651     attr.valType = ATTR_INT;
1652     newFile.dbc_attributes.append(attr);
1653 
1654     attr.attrType = ATTR_TYPE_MESSAGE;
1655     attr.defaultValue = 0;
1656     attr.enumVals.clear();
1657     attr.lower = 0;
1658     attr.upper = 0;
1659     attr.name = "filterlabeling";
1660     attr.valType = ATTR_INT;
1661     newFile.dbc_attributes.append(attr);
1662 
1663     DBC_NODE falseNode;
1664     falseNode.name = "Vector__XXX";
1665     falseNode.comment = "Default node if none specified";
1666     newFile.dbc_nodes.append(falseNode);
1667     newFile.setAssocBus(-1);
1668 
1669     loadedFiles.append(newFile);
1670     return loadedFiles.count();
1671 }
1672 
loadDBCFile(QString filename)1673 DBCFile* DBCHandler::loadDBCFile(QString filename)
1674 {
1675     DBCFile newFile;
1676     if (newFile.loadFile(filename))
1677     {
1678         loadedFiles.append(newFile);
1679     }
1680     else
1681     {
1682         //createBlankFile();
1683     }
1684     if (loadedFiles.count()> 0) return &loadedFiles.last();
1685     else return nullptr;
1686 }
1687 
1688 //the only reason to even bother sending the index is to see if
1689 //the user wants to replace an already loaded DBC.
1690 //Otherwise add a new one. Well, always add a new one.
1691 //If a valid index is passed we'll remove that one and then commence
1692 //adding. Otherwise, just go straight to adding.
loadDBCFile(int idx)1693 DBCFile* DBCHandler::loadDBCFile(int idx)
1694 {
1695    if (idx > -1 && idx < loadedFiles.count()) removeDBCFile(idx);
1696 
1697     QString filename;
1698     QFileDialog dialog;
1699     QSettings settings;
1700 
1701     QStringList filters;
1702     filters.append(QString(tr("DBC File (*.dbc)")));
1703 
1704     dialog.setDirectory(settings.value("DBC/LoadSaveDirectory", dialog.directory().path()).toString());
1705     dialog.setFileMode(QFileDialog::ExistingFile);
1706     dialog.setNameFilters(filters);
1707     dialog.setViewMode(QFileDialog::Detail);
1708 
1709     if (dialog.exec() == QDialog::Accepted)
1710     {
1711         filename = dialog.selectedFiles()[0];
1712         //right now there is only one file type that can be loaded here so just do it.
1713         settings.setValue("DBC/LoadSaveDirectory", dialog.directory().path());
1714         return loadDBCFile(filename);
1715     }
1716 
1717     return nullptr;
1718 }
1719 
loadJSONFile(QString filename)1720 DBCFile* DBCHandler::loadJSONFile(QString filename)
1721 {
1722      QSettings settings;
1723      DBCFile *thisFile;
1724      DBC_MESSAGE *pMsg;
1725 
1726          createBlankFile();
1727          thisFile = &loadedFiles.last();
1728 
1729          QFile *inFile = new QFile(filename);
1730          if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
1731          {
1732              qDebug() << "Could not open JSON file for reading.";
1733              delete inFile;
1734              return nullptr;
1735          }
1736          QByteArray wholeFileData = inFile->readAll();
1737          inFile->close();
1738          delete inFile;
1739          //qDebug() << "Data Length: " << wholeFileData.length();
1740          QJsonDocument jsonDoc = QJsonDocument::fromJson(wholeFileData);
1741          if (jsonDoc.isNull())
1742          {
1743              qDebug() << "Couldn't load and parse the JSON file for some reason.";
1744              return nullptr;
1745          }
1746          qDebug() << "Loaded JSON";
1747 
1748          QJsonObject jsonObj = jsonDoc.object();
1749          QJsonObject jsonMessages = jsonObj["messages"].toObject();
1750 
1751          QJsonObject::iterator iter;
1752          for (iter = jsonMessages.begin(); iter != jsonMessages.end(); iter++)
1753          {
1754              qDebug() << iter.key();
1755              DBC_MESSAGE msg;
1756              msg.ID = static_cast<uint32_t>(iter->toObject().find("message_id").value().toInt());
1757              msg.name = QString(iter.key().toUtf8());
1758              msg.len = static_cast<unsigned int>(iter->toObject().find("length_bytes").value().toInt());
1759              msg.sender = thisFile->findNodeByIdx(0);
1760              QString nodeName = iter->toObject().find("originNode").value().toString();
1761              msg.sender = thisFile->findNodeByName(nodeName);
1762              msg.bgColor = QColor(thisFile->findAttributeByName("GenMsgBackgroundColor")->defaultValue.toString());
1763              msg.fgColor = QColor(thisFile->findAttributeByName("GenMsgForegroundColor")->defaultValue.toString());
1764              if (!msg.sender && nodeName.length() > 1)
1765              {
1766                  DBC_NODE node;
1767                  node.name = nodeName;
1768                  thisFile->dbc_nodes.append(node);
1769                  msg.sender = thisFile->findNodeByName(nodeName);
1770              }
1771              if (nodeName.length() < 2) msg.sender = thisFile->findNodeByIdx(0);
1772              thisFile->messageHandler->addMessage(msg);
1773              pMsg = thisFile->messageHandler->findMsgByID(msg.ID);
1774              if (!pMsg)
1775              {
1776                  qDebug() << "pMsg was null... I have no idea how that is possible. DEBUG ME";
1777                  return nullptr;
1778              }
1779 
1780              QJsonObject jsonSigs = iter->toObject().find("signals").value().toObject();
1781              QJsonObject::iterator sigIter;
1782              for (sigIter = jsonSigs.begin(); sigIter != jsonSigs.end(); sigIter++)
1783              {
1784                 qDebug() << sigIter.key();
1785                 DBC_SIGNAL sig;
1786                 QJsonObject sigObj = sigIter->toObject();
1787                 if (sigObj.isEmpty())
1788                 {
1789                     qDebug() << "EMPTY!?";
1790                 }
1791                 sig.name = QString(sigIter.key().toUtf8());
1792                 sig.factor = sigObj.find("scale").value().toDouble();
1793                 sig.bias = sigObj.find("offset").value().toDouble();
1794                 sig.max = sigObj.find("max").value().toDouble();
1795                 sig.min = sigObj.find("min").value().toDouble();
1796                 sig.startBit = sigObj.find("start_position").value().toInt();
1797                 sig.unitName = sigObj.find("units").value().toString();
1798                 sig.signalSize = sigObj.find("width").value().toInt();
1799                 sig.isMultiplexed = false;
1800                 sig.isMultiplexor = false;
1801                 sig.parentMessage = pMsg;
1802                 if (!sigObj.find("mux_id")->isUndefined())
1803                 {
1804                     QJsonValue muxVal = sigObj.find("mux_id").value();
1805                     sig.multiplexLowValue = muxVal.toInt();
1806                     sig.multiplexHighValue = sig.multiplexLowValue;
1807                     sig.isMultiplexed = true;
1808                 }
1809                 else
1810                 {
1811                     sig.multiplexLowValue = 0;
1812                     sig.multiplexHighValue = 0;
1813                 }
1814                 QJsonValue muxerVal = sigObj.find("is_muxer").value();
1815                 if (!muxerVal.isNull())
1816                 {
1817                     if (muxerVal.toBool())
1818                     {
1819                         sig.isMultiplexor = true;
1820                     }
1821                 }
1822 
1823                 QJsonObject valuesObj = sigObj.find("value_description")->toObject();
1824                 if (!valuesObj.isEmpty())
1825                 {
1826                     QJsonObject::iterator valIter;
1827                     for (valIter = valuesObj.begin(); valIter != valuesObj.end(); valIter++)
1828                     {
1829                         DBC_VAL_ENUM_ENTRY valEnum;
1830                         valEnum.value = valIter.value().toInt();
1831                         valEnum.descript = valIter.key().toUtf8();
1832                         sig.valList.append(valEnum);
1833                     }
1834                 }
1835 
1836                 QJsonArray rxArray = sigObj.find("receivers")->toArray();
1837                 if (rxArray.size() < 1) sig.receiver = thisFile->findNodeByIdx(0);
1838                 else
1839                 {
1840                     qDebug() << rxArray[0].toString();
1841                     sig.receiver = thisFile->findNodeByName(rxArray[0].toString());
1842                     if (!sig.receiver && rxArray[0].toString().length() > 1)
1843                     {
1844                         DBC_NODE node;
1845                         node.name = rxArray[0].toString();
1846                         thisFile->dbc_nodes.append(node);
1847                         sig.receiver = thisFile->findNodeByName(rxArray[0].toString());
1848                     }
1849                     if (!sig.receiver) sig.receiver = thisFile->findNodeByIdx(0);
1850                 }
1851                 if (!sigObj.find("endianness").value().toString().compare("BIG"))
1852                 {
1853                     sig.intelByteOrder = false;
1854                 }
1855                 else sig.intelByteOrder = true;
1856 
1857                 if (!sigObj.find("signedness").value().toString().compare("UNSIGNED"))
1858                 {
1859                     sig.valType = DBC_SIG_VAL_TYPE::UNSIGNED_INT;
1860                 }
1861                 else sig.valType = DBC_SIG_VAL_TYPE::SIGNED_INT;
1862 
1863                 pMsg->sigHandler->addSignal(sig);
1864                 if (sig.isMultiplexor) //if this signal was the multiplexor then store that info
1865                 {
1866                     DBC_SIGNAL *pSig = pMsg->sigHandler->findSignalByName(sig.name);
1867                     if (pSig) pMsg->multiplexorSignal = pSig;
1868                 }
1869              }
1870          }
1871 
1872          for (int x = 0; x < thisFile->messageHandler->getCount(); x++)
1873          {
1874              DBC_MESSAGE *msg = thisFile->messageHandler->findMsgByIdx(x);
1875              for (int y = 0; y < msg->sigHandler->getCount(); y++)
1876              {
1877                  DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(y);
1878                  //if this doesn't have a multiplex parent set but is multiplexed then it must have used
1879                  //simple multiplexing instead of any extended specification. So, fill in the multiplexor signal here
1880                  //and also write the extended entry for it too.
1881                  if (sig->isMultiplexed && (sig->multiplexParent == nullptr) )
1882                  {
1883                      sig->multiplexParent = msg->multiplexorSignal;
1884                      msg->multiplexorSignal->multiplexedChildren.append(sig);
1885                  }
1886              }
1887          }
1888 
1889 
1890          thisFile->setDirtyFlag();
1891          return thisFile;
1892 }
1893 
removeDBCFile(int idx)1894 void DBCHandler::removeDBCFile(int idx)
1895 {
1896     if (loadedFiles.count() == 0) return;
1897     if (idx < 0) return;
1898     if (idx >= loadedFiles.count()) return;
1899     loadedFiles.removeAt(idx);
1900 }
1901 
removeAllFiles()1902 void DBCHandler::removeAllFiles()
1903 {
1904     loadedFiles.clear();
1905 }
1906 
swapFiles(int pos1,int pos2)1907 void DBCHandler::swapFiles(int pos1, int pos2)
1908 {
1909     if (loadedFiles.count() < 2) return;
1910     if (pos1 < 0) return;
1911     if (pos1 >= loadedFiles.count()) return;
1912     if (pos2 < 0) return;
1913     if (pos2 >= loadedFiles.count()) return;
1914 
1915     loadedFiles.swapItemsAt(pos1, pos2);
1916 }
1917 
1918 /*
1919  * Convenience function that encapsulates a whole lot of the details.
1920  * You give it a canbus frame and it'll tell you whether there is a loaded DBC file that can
1921  * interpret that frame for you.
1922  * Returns nullptr if there is no message definition that matches.
1923 */
findMessage(const CANFrame & frame)1924 DBC_MESSAGE* DBCHandler::findMessage(const CANFrame &frame)
1925 {
1926     for(int i = 0; i < loadedFiles.count(); i++)
1927     {
1928         if (loadedFiles[i].getAssocBus() == -1 || frame.bus == loadedFiles[i].getAssocBus())
1929         {
1930             DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(frame.frameId());
1931             if (msg != nullptr) return msg;
1932         }
1933     }
1934     return nullptr;
1935 }
1936 
findMessage(uint32_t id)1937 DBC_MESSAGE* DBCHandler::findMessage(uint32_t id)
1938 {
1939     for(int i = 0; i < loadedFiles.count(); i++)
1940     {
1941         DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(id);
1942         if (msg != nullptr)
1943         {
1944             return msg;
1945         }
1946     }
1947     return nullptr;
1948 }
1949 
1950 // 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
1951 // has filter labeling enabled.
1952 // Returns the found message as well as the matching criteria (exact/J1939/GMLAN)
1953 // Used for quickly populating the Frame Filtering section with interpreted values
findMessageForFilter(uint32_t id,MatchingCriteria_t * matchingCriteria)1954 DBC_MESSAGE* DBCHandler::findMessageForFilter(uint32_t id, MatchingCriteria_t * matchingCriteria)
1955 {
1956     for(int i = 0; i < loadedFiles.count(); i++)
1957     {
1958         if (loadedFiles[i].messageHandler->filterLabeling())
1959         {
1960             DBC_MESSAGE* msg = loadedFiles[i].messageHandler->findMsgByID(id);
1961             if (msg != nullptr)
1962             {
1963                 if (matchingCriteria) *matchingCriteria = loadedFiles[i].messageHandler->getMatchingCriteria();
1964                 return msg;
1965             }
1966         }
1967     }
1968     return nullptr;
1969 }
1970 
1971 
1972 /*
1973  * As above, a real shortcut function that searches all files in order to try to find a message with the given name
1974 */
findMessage(const QString msgName)1975 DBC_MESSAGE* DBCHandler::findMessage(const QString msgName)
1976 {
1977     DBC_MESSAGE *msg = nullptr;
1978     for(int i = 0; i < loadedFiles.count(); i++)
1979     {
1980         DBCFile * file = getFileByIdx(i);
1981         msg = file->messageHandler->findMsgByName(msgName);
1982         if (msg) return msg; //if it's not null then we have a match so return it
1983     }
1984     return nullptr; //no match, tough luck, return null
1985 }
1986 
getFileCount()1987 int DBCHandler::getFileCount()
1988 {
1989     return loadedFiles.count();
1990 }
1991 
getFileByIdx(int idx)1992 DBCFile* DBCHandler::getFileByIdx(int idx)
1993 {
1994     if (loadedFiles.count() == 0) return nullptr;
1995     if (idx < 0) return nullptr;
1996     if (idx >= loadedFiles.count()) return nullptr;
1997     return &loadedFiles[idx];
1998 }
1999 
getFileByName(QString name)2000 DBCFile* DBCHandler::getFileByName(QString name)
2001 {
2002     if (loadedFiles.count() == 0) return nullptr;
2003     for (int i = 0; i < loadedFiles.count(); i++)
2004     {
2005         if (loadedFiles[i].getFilename().compare(name, Qt::CaseInsensitive) == 0)
2006         {
2007             return &loadedFiles[i];
2008         }
2009     }
2010     return nullptr;
2011 }
2012 
DBCHandler()2013 DBCHandler::DBCHandler()
2014 {
2015     // Load previously saved DBC file settings
2016     QSettings settings;
2017     int filecount = settings.value("DBC/FileCount", 0).toInt();
2018     qDebug() << "Previously loaded DBC file count: " << filecount;
2019     for (int i=0; i<filecount; i++)
2020     {
2021         QString filename = settings.value("DBC/Filename_" + QString(i),"").toString();
2022         DBCFile * file = loadDBCFile(filename);
2023         if (file)
2024         {
2025             int bus = settings.value("DBC/AssocBus_" + QString(i),0).toInt();
2026             file->setAssocBus(bus);
2027 
2028             MatchingCriteria_t matchingCriteria = (MatchingCriteria_t)settings.value("DBC/MatchingCriteria_" + QString(i),0).toInt();
2029 
2030             DBC_ATTRIBUTE attr;
2031 
2032             attr.attrType = ATTR_TYPE_MESSAGE;
2033             attr.defaultValue = matchingCriteria;
2034             attr.enumVals.clear();
2035             attr.lower = 0;
2036             attr.upper = 0;
2037             attr.name = "matchingcriteria";
2038             attr.valType = ATTR_INT;
2039             file->dbc_attributes.append(attr);
2040             file->messageHandler->setMatchingCriteria(matchingCriteria);
2041 
2042             bool filterLabeling = settings.value("DBC/FilterLabeling_" + QString(i),0).toBool();
2043             attr.attrType = ATTR_TYPE_MESSAGE;
2044             attr.defaultValue = filterLabeling;
2045             attr.enumVals.clear();
2046             attr.lower = 0;
2047             attr.upper = 0;
2048             attr.name = "filterlabeling";
2049             attr.valType = ATTR_INT;
2050             file->dbc_attributes.append(attr);
2051             file->messageHandler->setFilterLabeling(filterLabeling);
2052 
2053             qInfo() << "Loaded DBC file" << filename << " (bus:" << bus
2054                 << ", Matching Criteria:" << (int)matchingCriteria << "Filter labeling: " << (filterLabeling?"enabled":"disabled") << ")";
2055         }
2056     }
2057 }
2058 
getReference()2059 DBCHandler* DBCHandler::getReference()
2060 {
2061     if (!instance) instance = new DBCHandler();
2062     return instance;
2063 }
2064