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