├── README.md ├── cantest.pro ├── dbc ├── dbc_classes.cpp ├── dbc_classes.h ├── dbchandler.cpp ├── dbchandler.h ├── utility.cpp └── utility.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h └── mainwindow.ui /README.md: -------------------------------------------------------------------------------- 1 | # usage 2 | 3 | Usage: ./cantest [options] input 4 | DBC data with UDP network emulator 5 | 6 | Options: 7 | -h, --help Displays this help. 8 | -v, --version Displays version information. 9 | -t, --timer inteval timer inteval. ms,default is 1000ms 10 | -u, --use time use massage timer inteval. ms 11 | -m, --message name message name. string,default is BCU_04 12 | -I, --message id message ID. int,default is 91 13 | -s, --signal idx signal idx. int,default is 0 14 | -n, --no run do not increment,default is 0 15 | -i, --ip UDP host IP. string,default is 127.0.0.1 16 | -p, --port

UDP host port,int. default is 11000 17 | -V, --verbose Verbose mode. Prints out more information. 18 | 19 | Arguments: 20 | input DBC file. 21 | 22 | # push default message to a specified UDP socket 23 | ./cantest ~/Downloads/V6_3.dbc -i 10.11.153.15 -p 11100 24 | 25 | # push default message with a specified time inteval 26 | ./cantest ~/Downloads/V6_3.dbc -t 100 27 | ./cantest ~/Downloads/V6_3.dbc 100 28 | 29 | # push message with a specified name 30 | ./cantest ~/Downloads/V6_3.dbc -m WLC_CDC_NFC 31 | 32 | 33 | # push message with a specified ID 34 | ./cantest ~/Downloads/V6_3.dbc -I 1198 35 | 36 | # push message with the time inteval specified in the DBC file [GenMsgCycleTime] 37 | ./cantest ~/Downloads/V6_3.dbc -I 1198 -u 1 38 | 39 | # push message with signal value specified 40 | ./cantest ~/Downloads/V6_3.dbc -I 1198 -s 24,25,26,27,28 41 | 42 | # do not increment the value 43 | ./cantest ~/Downloads/V6_3.dbc -I 1198 -s 24,25,26,27,28 -n 1 44 | 45 | $${\frac{1+x}{x^2+1}}$$ 46 | -------------------------------------------------------------------------------- /cantest.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2017-12-02T19:05:12 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui network 8 | CONFIG += c++11 warn_on 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = cantest 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which has been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | 26 | SOURCES += \ 27 | main.cpp \ 28 | mainwindow.cpp \ 29 | dbc/dbc_classes.cpp \ 30 | dbc/dbchandler.cpp \ 31 | dbc/utility.cpp 32 | 33 | HEADERS += \ 34 | mainwindow.h \ 35 | dbc/dbc_classes.h \ 36 | dbc/dbchandler.h \ 37 | dbc/utility.h 38 | 39 | FORMS += \ 40 | mainwindow.ui 41 | -------------------------------------------------------------------------------- /dbc/dbc_classes.cpp: -------------------------------------------------------------------------------- 1 | #include "dbc_classes.h" 2 | #include "dbchandler.h" 3 | 4 | DBC_MESSAGE::DBC_MESSAGE() 5 | { 6 | sigHandler = new DBCSignalHandler; 7 | } 8 | 9 | /* 10 | The way that the DBC file format works is kind of weird... For intel format signals you count up 11 | from the start bit to the end bit which is (startbit + signallength - 1). At each point 12 | bits are numbered in a sawtooth manner. What that means is that the very first bit is 0 and you count up 13 | from there all of the way to 63 with each byte being 8 bits so bit 0 is the lowest bit in the first byte 14 | and 8 is the lowest bit in the next byte up. The whole thing looks like this: 15 | Bits 16 | 7 6 5 4 3 2 1 0 17 | 18 | 0 7 6 5 4 3 2 1 0 19 | b 1 15 14 13 12 11 10 9 8 20 | y 2 23 22 21 20 19 18 17 16 21 | t 3 31 30 29 28 27 26 25 24 22 | e 4 39 38 37 36 35 34 33 32 23 | s 5 47 46 45 44 43 42 41 40 24 | 6 55 54 53 52 51 50 49 48 25 | 7 63 62 61 60 59 58 57 56 26 | 27 | For intel format you start at the start bit and keep counting up. If you have a signal size of 8 28 | and start at bit 12 then the bits are 12, 13, 14, 15, 16, 17, 18, 19 which spans across two bytes. 29 | In this format each bit is worth twice as much as the last and you just keep counting up. 30 | Bit 12 is worth 1, 13 is worth 2, 14 is worth 4, etc all of the way to bit 19 is worth 128. 31 | 32 | Motorola format turns most everything on its head. You count backward from the start bit but 33 | only within the current byte. If you are about to exit the current byte you go one higher and then keep 34 | going backward as before. Using the same example as for intel, start bit of 12 and a signal length of 8. 35 | So, the bits are 12, 11, 10, 9, 8, 23, 22, 21. Yes, that's confusing. They now go in reverse value order too. 36 | Bit 12 is worth 128, 11 is worth 64, etc until bit 21 is worth 1. 37 | */ 38 | 39 | DBC_ATTRIBUTE_VALUE *DBC_SIGNAL::findAttrValByName(QString name) 40 | { 41 | if (attributes.length() == 0) return NULL; 42 | for (int i = 0; i < attributes.length(); i++) 43 | { 44 | if (attributes[i].attrName.compare(name, Qt::CaseInsensitive) == 0) 45 | { 46 | return &attributes[i]; 47 | } 48 | } 49 | return NULL; 50 | } 51 | 52 | DBC_ATTRIBUTE_VALUE *DBC_SIGNAL::findAttrValByIdx(int idx) 53 | { 54 | if (idx < 0) return NULL; 55 | if (idx >= attributes.count()) return NULL; 56 | return &attributes[idx]; 57 | } 58 | 59 | DBC_ATTRIBUTE_VALUE *DBC_MESSAGE::findAttrValByName(QString name) 60 | { 61 | if (attributes.length() == 0) return NULL; 62 | for (int i = 0; i < attributes.length(); i++) 63 | { 64 | if (attributes[i].attrName.compare(name, Qt::CaseInsensitive) == 0) 65 | { 66 | return &attributes[i]; 67 | } 68 | } 69 | return NULL; 70 | } 71 | 72 | DBC_ATTRIBUTE_VALUE *DBC_MESSAGE::findAttrValByIdx(int idx) 73 | { 74 | if (idx < 0) return NULL; 75 | if (idx >= attributes.count()) return NULL; 76 | return &attributes[idx]; 77 | } 78 | 79 | DBC_ATTRIBUTE_VALUE *DBC_NODE::findAttrValByName(QString name) 80 | { 81 | if (attributes.length() == 0) return NULL; 82 | for (int i = 0; i < attributes.length(); i++) 83 | { 84 | if (attributes[i].attrName.compare(name, Qt::CaseInsensitive) == 0) 85 | { 86 | return &attributes[i]; 87 | } 88 | } 89 | return NULL; 90 | } 91 | 92 | DBC_ATTRIBUTE_VALUE *DBC_NODE::findAttrValByIdx(int idx) 93 | { 94 | if (idx < 0) return NULL; 95 | if (idx >= attributes.count()) return NULL; 96 | return &attributes[idx]; 97 | } 98 | -------------------------------------------------------------------------------- /dbc/dbc_classes.h: -------------------------------------------------------------------------------- 1 | #ifndef DBC_CLASSES_H 2 | #define DBC_CLASSES_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /*classes to encapsulate data from a DBC file. Really, the stuff of interest 10 | are the nodes, messages, signals, attributes, and comments. 11 | 12 | These things sort of form a hierarchy. Nodes send and receive messages. 13 | Messages are comprised of signals. Nodes, signals, and messages potentially have attribute values. 14 | All of them can have comments. 15 | */ 16 | 17 | enum DBC_SIG_VAL_TYPE 18 | { 19 | UNSIGNED_INT, 20 | SIGNED_INT, 21 | SP_FLOAT, 22 | DP_FLOAT, 23 | STRING 24 | }; 25 | 26 | enum DBC_ATTRIBUTE_VAL_TYPE 27 | { 28 | QINT, 29 | QFLOAT, 30 | QSTRING, 31 | ENUM 32 | }; 33 | 34 | enum DBC_ATTRIBUTE_TYPE 35 | { 36 | GENERAL, 37 | NODE, 38 | MESSAGE, 39 | SIG 40 | }; 41 | 42 | class DBC_ATTRIBUTE 43 | { 44 | public: 45 | QString name; 46 | DBC_ATTRIBUTE_VAL_TYPE valType; 47 | DBC_ATTRIBUTE_TYPE attrType; 48 | double upper, lower; 49 | QStringList enumVals; 50 | QVariant defaultValue; 51 | }; 52 | 53 | class DBC_ATTRIBUTE_VALUE 54 | { 55 | public: 56 | QString attrName; 57 | QVariant value; 58 | }; 59 | 60 | class DBC_VAL_ENUM_ENTRY 61 | { 62 | public: 63 | int value; 64 | QString descript; 65 | }; 66 | 67 | class DBC_NODE 68 | { 69 | public: 70 | QString name; 71 | QString comment; 72 | QList attributes; 73 | 74 | DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name); 75 | DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx); 76 | }; 77 | 78 | class DBC_MESSAGE; //forward reference so that DBC_SIGNAL can compile before we get to real definition of DBC_MESSAGE 79 | 80 | class DBC_SIGNAL 81 | { 82 | public: //TODO: this is sloppy. It shouldn't all be public! 83 | QString name; 84 | int startBit; 85 | int signalSize; 86 | bool intelByteOrder; //true is obviously little endian. False is big endian 87 | bool isMultiplexor; 88 | bool isMultiplexed; 89 | int multiplexValue; 90 | DBC_SIG_VAL_TYPE valType; 91 | double factor; 92 | double bias; 93 | double min; 94 | double max; 95 | DBC_NODE *receiver; //it is fast to have a pointer but dangerous... Make sure to walk the whole tree and delete everything so nobody has stale references. 96 | DBC_MESSAGE *parentMessage; 97 | QString unitName; 98 | QString comment; 99 | QList attributes; 100 | QList valList; 101 | 102 | DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name); 103 | DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx); 104 | }; 105 | 106 | class DBCSignalHandler; //forward declaration to keep from having to include dbchandler.h in this file and thus create a loop 107 | 108 | class DBC_MESSAGE 109 | { 110 | public: 111 | DBC_MESSAGE(); 112 | 113 | uint32_t ID; 114 | QString name; 115 | QString comment; 116 | unsigned int len; 117 | DBC_NODE *sender; 118 | QColor bgColor; 119 | QColor fgColor; 120 | QList attributes; 121 | DBCSignalHandler *sigHandler; 122 | DBC_SIGNAL* multiplexorSignal; 123 | 124 | DBC_ATTRIBUTE_VALUE *findAttrValByName(QString name); 125 | DBC_ATTRIBUTE_VALUE *findAttrValByIdx(int idx); 126 | }; 127 | 128 | 129 | #endif // DBC_CLASSES_H 130 | 131 | -------------------------------------------------------------------------------- /dbc/dbchandler.cpp: -------------------------------------------------------------------------------- 1 | #include "dbchandler.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "utility.h" 11 | 12 | DBCHandler* DBCHandler::instance = NULL; 13 | 14 | DBC_SIGNAL* DBCSignalHandler::findSignalByIdx(int idx) 15 | { 16 | if (sigs.count() == 0) return NULL; 17 | if (idx < 0) return NULL; 18 | if (idx >= sigs.count()) return NULL; 19 | 20 | return &sigs[idx]; 21 | } 22 | 23 | DBC_SIGNAL* DBCSignalHandler::findSignalByName(QString name) 24 | { 25 | if (sigs.count() == 0) return NULL; 26 | for (int i = 0; i < sigs.count(); i++) 27 | { 28 | if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0) 29 | { 30 | return &sigs[i]; 31 | } 32 | } 33 | return NULL; 34 | } 35 | 36 | bool DBCSignalHandler::addSignal(DBC_SIGNAL &sig) 37 | { 38 | sigs.append(sig); 39 | return true; 40 | } 41 | 42 | bool DBCSignalHandler::removeSignal(DBC_SIGNAL *sig) 43 | { 44 | Q_UNUSED(sig); 45 | //if (sigs.removeAll(*sig) > 0) return true; 46 | return false; 47 | } 48 | 49 | bool DBCSignalHandler::removeSignal(int idx) 50 | { 51 | if (sigs.count() == 0) return false; 52 | if (idx < 0) return false; 53 | if (idx >= sigs.count()) return false; 54 | sigs.removeAt(idx); 55 | return true; 56 | } 57 | 58 | bool DBCSignalHandler::removeSignal(QString name) 59 | { 60 | bool foundSome = false; 61 | if (sigs.count() == 0) return false; 62 | for (int i = sigs.count() - 1; i >= 0; i--) 63 | { 64 | if (sigs[i].name.compare(name, Qt::CaseInsensitive) == 0) 65 | { 66 | sigs.removeAt(i); 67 | foundSome = true; 68 | } 69 | } 70 | return foundSome; 71 | } 72 | 73 | void DBCSignalHandler::removeAllSignals() 74 | { 75 | sigs.clear(); 76 | } 77 | 78 | int DBCSignalHandler::getCount() 79 | { 80 | return sigs.count(); 81 | } 82 | 83 | DBC_MESSAGE* DBCMessageHandler::findMsgByID(uint32_t id) 84 | { 85 | if (messages.count() == 0) return NULL; 86 | for (int i = 0; i < messages.count(); i++) 87 | { 88 | if (isJ1939Handler) 89 | { 90 | // include data page and extended data page in the pgn 91 | uint32_t pgn = (id & 0x3FFFF00) >> 8; 92 | if ( (pgn & 0xFF00) <= 0xEF00 ) 93 | { 94 | // PDU1 format 95 | pgn &= 0x3FF00; 96 | if ((messages[i].ID & 0x3FF0000) == (pgn << 8)) 97 | { 98 | return &messages[i]; 99 | } 100 | } 101 | else 102 | { 103 | // PDU2 format 104 | if ((messages[i].ID & 0x3FFFF00) == (pgn << 8)) 105 | { 106 | return &messages[i]; 107 | } 108 | } 109 | } 110 | else 111 | { 112 | if ( messages[i].ID == id ) 113 | { 114 | return &messages[i]; 115 | } 116 | } 117 | } 118 | return NULL; 119 | } 120 | 121 | DBC_MESSAGE* DBCMessageHandler::findMsgByIdx(int idx) 122 | { 123 | if (messages.count() == 0) return NULL; 124 | if (idx < 0) return NULL; 125 | if (idx >= messages.count()) return NULL; 126 | return &messages[idx]; 127 | } 128 | 129 | DBC_MESSAGE* DBCMessageHandler::findMsgByName(QString name) 130 | { 131 | if (messages.count() == 0) return NULL; 132 | for (int i = 0; i < messages.count(); i++) 133 | { 134 | if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0) 135 | { 136 | return &messages[i]; 137 | } 138 | } 139 | return NULL; 140 | } 141 | 142 | bool DBCMessageHandler::addMessage(DBC_MESSAGE &msg) 143 | { 144 | messages.append(msg); 145 | return true; 146 | } 147 | 148 | bool DBCMessageHandler::removeMessage(DBC_MESSAGE *msg) 149 | { 150 | Q_UNUSED(msg); 151 | //if (messages.removeAll(*msg) > 0) return true; 152 | return false; 153 | } 154 | 155 | bool DBCMessageHandler::removeMessageByIndex(int idx) 156 | { 157 | if (messages.count() == 0) return false; 158 | if (idx < 0) return false; 159 | if (idx >= messages.count()) return false; 160 | messages.removeAt(idx); 161 | return true; 162 | } 163 | 164 | bool DBCMessageHandler::removeMessage(uint32_t ID) 165 | { 166 | bool foundSome = false; 167 | if (messages.count() == 0) return false; 168 | for (int i = messages.count() - 1; i >= 0; i--) 169 | { 170 | if (messages[i].ID == ID) 171 | { 172 | messages.removeAt(i); 173 | foundSome = true; 174 | } 175 | } 176 | return foundSome; 177 | } 178 | 179 | bool DBCMessageHandler::removeMessage(QString name) 180 | { 181 | bool foundSome = false; 182 | if (messages.count() == 0) return false; 183 | for (int i = messages.count() - 1; i >= 0; i--) 184 | { 185 | if (messages[i].name.compare(name, Qt::CaseInsensitive) == 0) 186 | { 187 | messages.removeAt(i); 188 | foundSome = true; 189 | } 190 | } 191 | return foundSome; 192 | } 193 | 194 | void DBCMessageHandler::removeAllMessages() 195 | { 196 | messages.clear(); 197 | } 198 | 199 | int DBCMessageHandler::getCount() 200 | { 201 | return messages.count(); 202 | } 203 | 204 | bool DBCMessageHandler::isJ1939() 205 | { 206 | return isJ1939Handler; 207 | } 208 | 209 | void DBCMessageHandler::setJ1939(bool j1939) 210 | { 211 | isJ1939Handler = j1939; 212 | } 213 | 214 | DBCFile::DBCFile() 215 | { 216 | messageHandler = new DBCMessageHandler; 217 | messageHandler->setJ1939(false); 218 | } 219 | 220 | DBCFile::DBCFile(const DBCFile& cpy) 221 | { 222 | messageHandler = new DBCMessageHandler; 223 | for (int i = 0 ; i < cpy.messageHandler->getCount() ; i++) 224 | messageHandler->addMessage(*cpy.messageHandler->findMsgByIdx(i)); 225 | 226 | messageHandler->setJ1939(cpy.messageHandler->isJ1939()); 227 | fileName = cpy.fileName; 228 | filePath = cpy.filePath; 229 | assocBuses = cpy.assocBuses; 230 | dbc_nodes.clear(); 231 | dbc_nodes.append(cpy.dbc_nodes); 232 | dbc_attributes.clear(); 233 | dbc_attributes.append(cpy.dbc_attributes); 234 | } 235 | 236 | DBCFile& DBCFile::operator=(const DBCFile& cpy) 237 | { 238 | if (this != &cpy) // protect against invalid self-assignment 239 | { 240 | messageHandler = cpy.messageHandler; 241 | fileName = cpy.fileName; 242 | filePath = cpy.filePath; 243 | assocBuses = cpy.assocBuses; 244 | dbc_nodes.clear(); 245 | dbc_nodes.append(cpy.dbc_nodes); 246 | dbc_attributes.clear(); 247 | dbc_attributes.append(cpy.dbc_attributes); 248 | } 249 | return *this; 250 | } 251 | 252 | DBC_NODE* DBCFile::findNodeByIdx(int idx) 253 | { 254 | if (idx < 0) return NULL; 255 | if (idx >= dbc_nodes.count()) return NULL; 256 | return &dbc_nodes[idx]; 257 | } 258 | 259 | DBC_NODE* DBCFile::findNodeByName(QString name) 260 | { 261 | if (dbc_nodes.length() == 0) return NULL; 262 | for (int i = 0; i < dbc_nodes.length(); i++) 263 | { 264 | if (dbc_nodes[i].name.compare(name, Qt::CaseInsensitive) == 0) 265 | { 266 | return &dbc_nodes[i]; 267 | } 268 | } 269 | return NULL; 270 | } 271 | 272 | QString DBCFile::getFullFilename() 273 | { 274 | return filePath + fileName; 275 | } 276 | 277 | QString DBCFile::getFilename() 278 | { 279 | return fileName; 280 | } 281 | 282 | QString DBCFile::getPath() 283 | { 284 | return filePath; 285 | } 286 | 287 | int DBCFile::getAssocBus() 288 | { 289 | return assocBuses; 290 | } 291 | 292 | void DBCFile::setAssocBus(int bus) 293 | { 294 | if (bus < -1) return; 295 | if (bus > 1) return; 296 | assocBuses = bus; 297 | } 298 | 299 | DBC_ATTRIBUTE *DBCFile::findAttributeByName(QString name) 300 | { 301 | if (dbc_attributes.length() == 0) return NULL; 302 | for (int i = 0; i < dbc_attributes.length(); i++) 303 | { 304 | if (dbc_attributes[i].name.compare(name, Qt::CaseInsensitive) == 0) 305 | { 306 | return &dbc_attributes[i]; 307 | } 308 | } 309 | return NULL; 310 | } 311 | 312 | DBC_ATTRIBUTE *DBCFile::findAttributeByIdx(int idx) 313 | { 314 | if (idx < 0) return NULL; 315 | if (idx >= dbc_attributes.count()) return NULL; 316 | return &dbc_attributes[idx]; 317 | } 318 | 319 | void DBCFile::findAttributesByType(DBC_ATTRIBUTE_TYPE typ, QList *list) 320 | { 321 | if (!list) return; 322 | list->clear(); 323 | foreach (DBC_ATTRIBUTE attr, dbc_attributes) 324 | { 325 | if (attr.attrType == typ) list->append(attr); 326 | } 327 | } 328 | 329 | void DBCFile::loadFile(QString fileName) 330 | { 331 | QFile *inFile = new QFile(fileName); 332 | QString line; 333 | QRegularExpression regex; 334 | QRegularExpressionMatch match; 335 | DBC_MESSAGE *currentMessage = NULL; 336 | DBC_ATTRIBUTE attr; 337 | int numSigFaults = 0, numMsgFaults = 0; 338 | 339 | qDebug() << "DBC File: " << fileName; 340 | 341 | if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text)) 342 | { 343 | delete inFile; 344 | return; 345 | } 346 | 347 | qDebug() << "Starting DBC load"; 348 | dbc_nodes.clear(); 349 | messageHandler->removeAllMessages(); 350 | messageHandler->setJ1939(false); 351 | 352 | DBC_NODE falseNode; 353 | falseNode.name = "Vector__XXX"; 354 | falseNode.comment = "Default node if none specified"; 355 | dbc_nodes.append(falseNode); 356 | 357 | while (!inFile->atEnd()) { 358 | line = QString(inFile->readLine().simplified()); 359 | if (line.startsWith("BO_ ")) //defines a message 360 | { 361 | qDebug() << "Found a BO line"; 362 | regex.setPattern("^BO\\_ (\\w+) (\\w+) *: (\\w+) (\\w+)"); 363 | match = regex.match(line); 364 | //captured 1 = the ID in decimal 365 | //captured 2 = The message name 366 | //captured 3 = the message length 367 | //captured 4 = the NODE responsible for this message 368 | if (match.hasMatch()) 369 | { 370 | DBC_MESSAGE msg; 371 | msg.ID = match.captured(1).toULong() & 0x7FFFFFFFul; //the ID is always stored in decimal format 372 | msg.name = match.captured(2); 373 | msg.len = match.captured(3).toInt(); 374 | msg.sender = findNodeByName(match.captured(4)); 375 | messageHandler->addMessage(msg); 376 | currentMessage = messageHandler->findMsgByID(msg.ID); 377 | } 378 | else numMsgFaults++; 379 | } 380 | if (line.startsWith("SG_ ")) //defines a signal 381 | { 382 | int offset = 0; 383 | bool isMultiplexor = false; 384 | //bool isMultiplexed = false; 385 | DBC_SIGNAL sig; 386 | 387 | sig.multiplexValue = 0; 388 | sig.isMultiplexed = false; 389 | sig.isMultiplexor = false; 390 | 391 | qDebug() << "Found a SG line"; 392 | regex.setPattern("^SG\\_ *(\\w+) +M *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)"); 393 | 394 | match = regex.match(line); 395 | if (match.hasMatch()) 396 | { 397 | qDebug() << "Multiplexor signal"; 398 | isMultiplexor = true; 399 | sig.isMultiplexor = true; 400 | } 401 | else 402 | { 403 | regex.setPattern("^SG\\_ *(\\w+) +m(\\d+) *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)"); 404 | match = regex.match(line); 405 | if (match.hasMatch()) 406 | { 407 | qDebug() << "Multiplexed signal"; 408 | //isMultiplexed = true; 409 | sig.isMultiplexed = true; 410 | sig.multiplexValue = match.captured(2).toInt(); 411 | offset = 1; 412 | } 413 | else 414 | { 415 | qDebug() << "standard signal"; 416 | regex.setPattern("^SG\\_ *(\\w+) *: *(\\d+)\\|(\\d+)@(\\d+)([\\+|\\-]) \\(([0-9.+\\-eE]+),([0-9.+\\-eE]+)\\) \\[([0-9.+\\-eE]+)\\|([0-9.+\\-eE]+)\\] \\\"(.*)\\\" (.*)"); 417 | match = regex.match(line); 418 | sig.isMultiplexed = false; 419 | sig.isMultiplexor = false; 420 | } 421 | } 422 | 423 | //captured 1 is the signal name 424 | //captured 2 would be multiplex value if this is a multiplex signal. Then offset the rest of these by 1 425 | //captured 2 is the starting bit 426 | //captured 3 is the length in bits 427 | //captured 4 is the byte order / value type 428 | //captured 5 specifies signed/unsigned for ints 429 | //captured 6 is the scaling factor 430 | //captured 7 is the offset 431 | //captured 8 is the minimum value 432 | //captured 9 is the maximum value 433 | //captured 10 is the unit 434 | //captured 11 is the receiving node 435 | 436 | if (match.hasMatch()) 437 | { 438 | sig.name = match.captured(1); 439 | sig.startBit = match.captured(2 + offset).toInt(); 440 | sig.signalSize = match.captured(3 + offset).toInt(); 441 | int val = match.captured(4 + offset).toInt(); 442 | if (val < 2) 443 | { 444 | if (match.captured(5 + offset) == "+") sig.valType = UNSIGNED_INT; 445 | else sig.valType = SIGNED_INT; 446 | } 447 | switch (val) 448 | { 449 | case 0: //big endian mode 450 | sig.intelByteOrder = false; 451 | break; 452 | case 1: //little endian mode 453 | sig.intelByteOrder = true; 454 | break; 455 | case 2: 456 | sig.valType = SP_FLOAT; 457 | break; 458 | case 3: 459 | sig.valType = DP_FLOAT; 460 | break; 461 | case 4: 462 | sig.valType = STRING; 463 | break; 464 | } 465 | sig.factor = match.captured(6 + offset).toDouble(); 466 | sig.bias = match.captured(7 + offset).toDouble(); 467 | sig.min = match.captured(8 + offset).toDouble(); 468 | sig.max = match.captured(9 + offset).toDouble(); 469 | sig.unitName = match.captured(10 + offset); 470 | if (match.captured(11 + offset).contains(',')) 471 | { 472 | QString tmp = match.captured(11 + offset).split(',')[0]; 473 | sig.receiver = findNodeByName(tmp); 474 | } 475 | else sig.receiver = findNodeByName(match.captured(11 + offset)); 476 | sig.parentMessage = currentMessage; 477 | currentMessage->sigHandler->addSignal(sig); 478 | if (isMultiplexor) currentMessage->multiplexorSignal = currentMessage->sigHandler->findSignalByName(sig.name); 479 | } 480 | else numSigFaults++; 481 | } 482 | if (line.startsWith("BU_:")) //line specifies the nodes on this canbus 483 | { 484 | qDebug() << "Found a BU line"; 485 | regex.setPattern("^BU\\_\\:(.*)"); 486 | match = regex.match(line); 487 | //captured 1 = a list of node names separated by spaces. No idea how many yet 488 | if (match.hasMatch()) 489 | { 490 | QStringList nodeStrings = match.captured(1).split(' '); 491 | qDebug() << "Found " << nodeStrings.count() << " node names"; 492 | for (int i = 0; i < nodeStrings.count(); i++) 493 | { 494 | //qDebug() << nodeStrings[i]; 495 | if (nodeStrings[i].length() > 1) 496 | { 497 | DBC_NODE node; 498 | node.name = nodeStrings[i]; 499 | dbc_nodes.append(node); 500 | } 501 | } 502 | } 503 | } 504 | if (line.startsWith("CM_ SG_ ")) 505 | { 506 | qDebug() << "Found an SG comment line"; 507 | regex.setPattern("^CM\\_ SG\\_ *(\\w+) *(\\w+) *\\\"(.*)\\\";"); 508 | match = regex.match(line); 509 | //captured 1 is the ID to match against to get to the message 510 | //captured 2 is the signal name from that message 511 | //captured 3 is the comment itself 512 | if (match.hasMatch()) 513 | { 514 | //qDebug() << "Comment was: " << match.captured(3); 515 | DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); 516 | if (msg != NULL) 517 | { 518 | DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2)); 519 | if (sig != NULL) 520 | { 521 | sig->comment = match.captured(3); 522 | } 523 | } 524 | } 525 | } 526 | if (line.startsWith("CM_ BO_ ")) 527 | { 528 | qDebug() << "Found a BO comment line"; 529 | regex.setPattern("^CM\\_ BO\\_ *(\\w+) *\\\"(.*)\\\";"); 530 | match = regex.match(line); 531 | //captured 1 is the ID to match against to get to the message 532 | //captured 2 is the comment itself 533 | if (match.hasMatch()) 534 | { 535 | //qDebug() << "Comment was: " << match.captured(2); 536 | DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); 537 | if (msg != NULL) 538 | { 539 | msg->comment = match.captured(2); 540 | } 541 | } 542 | } 543 | if (line.startsWith("CM_ BU_ ")) 544 | { 545 | qDebug() << "Found a BU comment line"; 546 | regex.setPattern("^CM\\_ BU\\_ *(\\w+) *\\\"(.*)\\\";"); 547 | match = regex.match(line); 548 | //captured 1 is the Node name 549 | //captured 2 is the comment itself 550 | if (match.hasMatch()) 551 | { 552 | //qDebug() << "Comment was: " << match.captured(2); 553 | DBC_NODE *node = findNodeByName(match.captured(1)); 554 | if (node != NULL) 555 | { 556 | node->comment = match.captured(2); 557 | } 558 | } 559 | } 560 | //VAL_ (1090) (VCUPresentParkLightOC) (1 "Error present" 0 "Error not present") ; 561 | if (line.startsWith("VAL_ ")) 562 | { 563 | qDebug() << "Found a value definition line"; 564 | regex.setPattern("^VAL\\_ (\\w+) (\\w+) (.*);"); 565 | match = regex.match(line); 566 | //captured 1 is the ID to match against 567 | //captured 2 is the signal name to match against 568 | //captured 3 is a series of values in the form (number "text") that is, all sep'd by spaces 569 | if (match.hasMatch()) 570 | { 571 | //qDebug() << "Data was: " << match.captured(3); 572 | DBC_MESSAGE *msg = messageHandler->findMsgByID(match.captured(1).toInt()); 573 | if (msg != NULL) 574 | { 575 | DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(match.captured(2)); 576 | if (sig != NULL) 577 | { 578 | QString tokenString = match.captured(3); 579 | DBC_VAL_ENUM_ENTRY val; 580 | while (tokenString.length() > 2) 581 | { 582 | regex.setPattern("(\\d+) \\\"(.*?)\\\"(.*)"); 583 | match = regex.match(tokenString); 584 | if (match.hasMatch()) 585 | { 586 | val.value = match.captured(1).toInt(); 587 | val.descript = match.captured(2); 588 | //qDebug() << "sig val " << val.value << " desc " <valList.append(val); 590 | int rightSize = tokenString.length() - match.captured(1).length() - match.captured(2).length() - 4; 591 | if (rightSize > 0) tokenString = tokenString.right(rightSize); 592 | else tokenString = ""; 593 | //qDebug() << "New token string: " << tokenString; 594 | } 595 | else tokenString = ""; 596 | } 597 | } 598 | } 599 | } 600 | } 601 | 602 | if (line.startsWith("BA_DEF_ SG_ ")) 603 | { 604 | qDebug() << "Found a SG attribute line"; 605 | 606 | if (parseAttribute(line.right(line.length() - 12), attr)) 607 | { 608 | qDebug() << "Success"; 609 | attr.attrType = SIG; 610 | dbc_attributes.append(attr); 611 | } 612 | } 613 | if (line.startsWith("BA_DEF_ BO_ ")) //definition of a message attribute 614 | { 615 | qDebug() << "Found a BO attribute line"; 616 | 617 | if (parseAttribute(line.right(line.length() - 12), attr)) 618 | { 619 | qDebug() << "Success"; 620 | attr.attrType = MESSAGE; 621 | dbc_attributes.append(attr); 622 | } 623 | } 624 | if (line.startsWith("BA_DEF_ BU_ ")) //definition of a node attribute 625 | { 626 | qDebug() << "Found a BU attribute line"; 627 | 628 | if (parseAttribute(line.right(line.length() - 12), attr)) 629 | { 630 | qDebug() << "Success"; 631 | attr.attrType = NODE; 632 | dbc_attributes.append(attr); 633 | } 634 | } 635 | 636 | if (line.startsWith("BA_DEF_DEF_ ")) //definition of default value for an attribute 637 | { 638 | regex.setPattern("^BA\\_DEF\\_DEF\\_ \\\"*(\\w+)\\\"* \\\"*([#\\w]*)\\\"*"); 639 | match = regex.match(line); 640 | //captured 1 is the name of the attribute 641 | //captured 2 is the default value for that attribute 642 | if (match.hasMatch()) 643 | { 644 | qDebug() << "Found an attribute default value line, searching for an attribute named " << match.captured(1) << "with data " << match.captured(2); 645 | DBC_ATTRIBUTE *found = findAttributeByName(match.captured(1)); 646 | if (found) 647 | { 648 | switch (found->valType) 649 | { 650 | case QSTRING: 651 | found->defaultValue = match.captured(2); 652 | break; 653 | case QFLOAT: 654 | found->defaultValue = match.captured(2).toFloat(); 655 | break; 656 | case QINT: 657 | found->defaultValue = match.captured(2).toInt(); 658 | break; 659 | case ENUM: 660 | QString temp = match.captured(2); 661 | found->defaultValue = 0; 662 | for (int x = 0; x < found->enumVals.count(); x++) 663 | { 664 | if (!found->enumVals[x].compare(temp, Qt::CaseInsensitive)) 665 | { 666 | found->defaultValue = x; 667 | break; 668 | } 669 | } 670 | } 671 | qDebug() << "Matched an attribute. Setting default value to " << found->defaultValue; 672 | } 673 | } 674 | } 675 | 676 | //BA_ "GenMsgCycleTime" BO_ 101 100; 677 | if (line.startsWith("BA_ ")) //set value of attribute 678 | { 679 | regex.setPattern("^BA\\_ \\\"*(\\w+)\\\"* BO\\_ (\\d+) \\\"*([#\\w]+)\\\"*"); 680 | match = regex.match(line); 681 | //captured 1 is the attribute name 682 | //captured 2 is the message ID number (frame ID) 683 | //captured 3 is the attribute value 684 | if (match.hasMatch()) 685 | { 686 | qDebug() << "Found an attribute setting line for a message"; 687 | DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1)); 688 | if (foundAttr) 689 | { 690 | qDebug() << "That attribute does exist"; 691 | DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toInt()); 692 | if (foundMsg) 693 | { 694 | qDebug() << "It references a valid, registered message"; 695 | DBC_ATTRIBUTE_VALUE *foundAttrVal = foundMsg->findAttrValByName(match.captured(1)); 696 | if (foundAttrVal) { 697 | foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType); 698 | } 699 | else 700 | { 701 | DBC_ATTRIBUTE_VALUE val; 702 | val.attrName = match.captured(1); 703 | val.value = processAttributeVal(match.captured(3), foundAttr->valType); 704 | foundMsg->attributes.append(val); 705 | } 706 | } 707 | } 708 | } 709 | 710 | regex.setPattern("^BA\\_ \\\"*(\\w+)\\\"* SG\\_ (\\d+) \\\"*(\\w+)\\\"* \\\"*([#\\w]+)\\\"*"); 711 | match = regex.match(line); 712 | //captured 1 is the attribute name 713 | //captured 2 is the message ID number (frame ID) 714 | //captured 3 is the signal name to bind to 715 | //captured 4 is the attribute value 716 | if (match.hasMatch()) 717 | { 718 | qDebug() << "Found an attribute setting line for a signal"; 719 | DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1)); 720 | if (foundAttr) 721 | { 722 | qDebug() << "That attribute does exist"; 723 | DBC_MESSAGE *foundMsg = messageHandler->findMsgByID(match.captured(2).toInt()); 724 | if (foundMsg) 725 | { 726 | qDebug() << "It references a valid, registered message"; 727 | DBC_SIGNAL *foundSig = foundMsg->sigHandler->findSignalByName(match.captured(3)); 728 | if (foundSig) 729 | { 730 | DBC_ATTRIBUTE_VALUE *foundAttrVal = foundSig->findAttrValByName(match.captured(1)); 731 | if (foundAttrVal) foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType); 732 | else 733 | { 734 | DBC_ATTRIBUTE_VALUE val; 735 | val.attrName = match.captured(1); 736 | val.value = processAttributeVal(match.captured(3), foundAttr->valType); 737 | foundSig->attributes.append(val); 738 | } 739 | } 740 | } 741 | } 742 | } 743 | 744 | regex.setPattern("^BA\\_ \\\"*(\\w+)\\\"* BU\\_ \\\"*(\\w+)\\\"* \\\"*([#\\w]+)\\\"*"); 745 | match = regex.match(line); 746 | //captured 1 is the attribute name 747 | //captured 2 is the name of the node 748 | //captured 3 is the attribute value 749 | if (match.hasMatch()) 750 | { 751 | qDebug() << "Found an attribute setting line for a node"; 752 | DBC_ATTRIBUTE *foundAttr = findAttributeByName(match.captured(1)); 753 | if (foundAttr) 754 | { 755 | qDebug() << "That attribute does exist"; 756 | DBC_NODE *foundNode = findNodeByName(match.captured(2)); 757 | if (foundNode) 758 | { 759 | qDebug() << "References a valid node name"; 760 | DBC_ATTRIBUTE_VALUE *foundAttrVal = foundNode->findAttrValByName(match.captured(1)); 761 | if (foundAttrVal) foundAttrVal->value = processAttributeVal(match.captured(3), foundAttr->valType); 762 | else 763 | { 764 | DBC_ATTRIBUTE_VALUE val; 765 | val.attrName = match.captured(1); 766 | val.value = processAttributeVal(match.captured(3), foundAttr->valType); 767 | foundNode->attributes.append(val); 768 | } 769 | } 770 | } 771 | 772 | } 773 | } 774 | } 775 | 776 | //upon loading the file add our custom foreground and background color attributes if they don't exist already 777 | DBC_ATTRIBUTE *bgAttr = findAttributeByName("GenMsgBackgroundColor"); 778 | if (!bgAttr) 779 | { 780 | attr.attrType = MESSAGE; 781 | attr.defaultValue = QColor(Qt::white).name(); 782 | attr.enumVals.clear(); 783 | attr.lower = 0; 784 | attr.upper = 0; 785 | attr.name = "GenMsgBackgroundColor"; 786 | attr.valType = QSTRING; 787 | dbc_attributes.append(attr); 788 | bgAttr = findAttributeByName("GenMsgBackgroundColor"); 789 | } 790 | 791 | DBC_ATTRIBUTE *fgAttr = findAttributeByName("GenMsgForegroundColor"); 792 | if (!fgAttr) 793 | { 794 | attr.attrType = MESSAGE; 795 | attr.defaultValue = QApplication::palette().color(QPalette::WindowText).name(); 796 | attr.enumVals.clear(); 797 | attr.lower = 0; 798 | attr.upper = 0; 799 | attr.name = "GenMsgForegroundColor"; 800 | attr.valType = QSTRING; 801 | dbc_attributes.append(attr); 802 | fgAttr = findAttributeByName("GenMsgForegroundColor"); 803 | } 804 | 805 | DBC_ATTRIBUTE *j1939attr = findAttributeByName("isj1939dbc"); 806 | if (j1939attr) 807 | { 808 | messageHandler->setJ1939(j1939attr->defaultValue > 0); 809 | } 810 | else 811 | { 812 | messageHandler->setJ1939(false); 813 | } 814 | 815 | QColor DefaultBG = QColor(bgAttr->defaultValue.toString()); 816 | QColor DefaultFG = QColor(fgAttr->defaultValue.toString()); 817 | 818 | DBC_ATTRIBUTE_VALUE *thisBG; 819 | DBC_ATTRIBUTE_VALUE *thisFG; 820 | 821 | 822 | 823 | for (int x = 0; x < messageHandler->getCount(); x++) 824 | { 825 | DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x); 826 | msg->bgColor = DefaultBG; 827 | msg->fgColor = DefaultFG; 828 | thisBG = msg->findAttrValByName("GenMsgBackgroundColor"); 829 | thisFG = msg->findAttrValByName("GenMsgForegroundColor"); 830 | if (thisBG) msg->bgColor = QColor(thisBG->value.toString()); 831 | if (thisFG) msg->fgColor = QColor(thisFG->value.toString()); 832 | } 833 | 834 | if (numSigFaults > 0 || numMsgFaults > 0) 835 | { 836 | QMessageBox msgBox; 837 | QString msg = "DBC file loaded with errors!\n"; 838 | msg += "Number of faulty message entries: " + QString::number(numMsgFaults) + "\n"; 839 | msg += "Number of faulty signal entries: " + QString::number(numSigFaults) + "\n\n"; 840 | msg += "Faulty entries have not been loaded.\n\n"; 841 | msg += "All other entries are, however, loaded."; 842 | msgBox.setText(msg); 843 | msgBox.exec(); 844 | } 845 | inFile->close(); 846 | delete inFile; 847 | QStringList fileList = fileName.split('/'); 848 | this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function. 849 | filePath = fileName.left(fileName.length() - this->fileName.length()); 850 | assocBuses = -1; 851 | } 852 | 853 | QVariant DBCFile::processAttributeVal(QString input, DBC_ATTRIBUTE_VAL_TYPE typ) 854 | { 855 | QVariant out; 856 | switch (typ) 857 | { 858 | case QSTRING: 859 | out = input; 860 | break; 861 | case QFLOAT: 862 | out = input.toFloat(); 863 | break; 864 | case QINT: 865 | case ENUM: 866 | out = input.toInt(); 867 | break; 868 | } 869 | return out; 870 | } 871 | 872 | bool DBCFile::parseAttribute(QString inpString, DBC_ATTRIBUTE &attr) 873 | { 874 | bool goodAttr = false; 875 | QRegularExpression regex; 876 | QRegularExpressionMatch match; 877 | 878 | regex.setPattern("\\\"*(\\w+)\\\"* \\\"*(\\w+)\\\"* (\\d+) (\\d+)"); 879 | match = regex.match(inpString); 880 | //captured 1 is the name of the attribute to set up 881 | //captured 2 is the type of signal attribute to create. 882 | //captured 3 is the lower bound value for this attribute 883 | //captured 4 is the upper bound value for this attribute 884 | if (match.hasMatch()) 885 | { 886 | qDebug() << "Parsing an attribute with low/high values"; 887 | attr.name = match.captured(1); 888 | QString typ = match.captured(2); 889 | attr.attrType = SIG; 890 | attr.lower = 0; 891 | attr.upper = 0; 892 | attr.valType = QSTRING; 893 | if (!typ.compare("INT", Qt::CaseInsensitive)) 894 | { 895 | qDebug() << "INT attribute named " << attr.name; 896 | attr.valType = QINT; 897 | attr.lower = match.captured(3).toInt(); 898 | attr.upper = match.captured(4).toInt(); 899 | goodAttr = true; 900 | } 901 | if (!typ.compare("FLOAT", Qt::CaseInsensitive)) 902 | { 903 | qDebug() << "FLOAT attribute named " << attr.name; 904 | attr.valType = QFLOAT; 905 | attr.lower = match.captured(3).toDouble(); 906 | attr.upper = match.captured(4).toDouble(); 907 | goodAttr = true; 908 | } 909 | if (!typ.compare("STRING", Qt::CaseInsensitive)) 910 | { 911 | qDebug() << "STRING attribute named " << attr.name; 912 | attr.valType = QSTRING; 913 | goodAttr = true; 914 | } 915 | } 916 | else 917 | { 918 | regex.setPattern("\\\"*(\\w+)\\\"* \\\"*(\\w+)\\\"* (.*)"); 919 | match = regex.match(inpString); 920 | //Same as above but no upper/lower bound values. 921 | if (match.hasMatch()) 922 | { 923 | qDebug() << "Parsing an attribute without boundaries"; 924 | attr.name = match.captured(1); 925 | QString typ = match.captured(2); 926 | attr.lower = 0; 927 | attr.upper = 0; 928 | attr.attrType = SIG; 929 | 930 | if (!typ.compare("INT", Qt::CaseInsensitive)) 931 | { 932 | qDebug() << "INT attribute named " << attr.name; 933 | attr.valType = QINT; 934 | goodAttr = true; 935 | } 936 | 937 | if (!typ.compare("FLOAT", Qt::CaseInsensitive)) 938 | { 939 | qDebug() << "FLOAT attribute named " << attr.name; 940 | attr.valType = QFLOAT; 941 | goodAttr = true; 942 | } 943 | 944 | if (!typ.compare("STRING", Qt::CaseInsensitive)) 945 | { 946 | qDebug() << "STRING attribute named " << attr.name; 947 | attr.valType = QSTRING; 948 | goodAttr = true; 949 | } 950 | 951 | if (!typ.compare("ENUM", Qt::CaseInsensitive)) 952 | { 953 | qDebug() << "ENUM attribute named " << attr.name; 954 | QStringList enumLst = match.captured(3).split(','); 955 | foreach (QString enumStr, enumLst) 956 | { 957 | attr.enumVals.append(Utility::unQuote(enumStr)); 958 | qDebug() << "Enum value: " << enumStr; 959 | } 960 | attr.valType = ENUM; 961 | goodAttr = true; 962 | } 963 | } 964 | } 965 | return goodAttr; 966 | } 967 | 968 | void DBCFile::saveFile(QString fileName) 969 | { 970 | QFile *outFile = new QFile(fileName); 971 | QString nodesOutput, msgOutput, commentsOutput, valuesOutput; 972 | QString defaultsOutput, attrValOutput; 973 | 974 | if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text)) 975 | { 976 | delete outFile; 977 | return; 978 | } 979 | 980 | //right now it outputs a standard hard coded boilerplate 981 | outFile->write("VERSION \"\"\n"); 982 | outFile->write("\n"); 983 | outFile->write("\n"); 984 | outFile->write("NS_ :\n"); 985 | outFile->write(" NS_DESC_\n"); 986 | outFile->write(" CM_\n"); 987 | outFile->write(" BA_DEF_\n"); 988 | outFile->write(" BA_\n"); 989 | outFile->write(" VAL_\n"); 990 | outFile->write(" CAT_DEF_\n"); 991 | outFile->write(" CAT_\n"); 992 | outFile->write(" FILTER\n"); 993 | outFile->write(" BA_DEF_DEF_\n"); 994 | outFile->write(" EV_DATA_\n"); 995 | outFile->write(" ENVVAR_DATA_\n"); 996 | outFile->write(" SGTYPE_\n"); 997 | outFile->write(" SGTYPE_VAL_\n"); 998 | outFile->write(" BA_DEF_SGTYPE_\n"); 999 | outFile->write(" BA_SGTYPE_\n"); 1000 | outFile->write(" SIG_TYPE_REF_\n"); 1001 | outFile->write(" VAL_TABLE_\n"); 1002 | outFile->write(" SIG_GROUP_\n"); 1003 | outFile->write(" SIG_VALTYPE_\n"); 1004 | outFile->write(" SIGTYPE_VALTYPE_\n"); 1005 | outFile->write(" BO_TX_BU_\n"); 1006 | outFile->write(" BA_DEF_REL_\n"); 1007 | outFile->write(" BA_REL_\n"); 1008 | outFile->write(" BA_DEF_DEF_REL_\n"); 1009 | outFile->write(" BU_SG_REL_\n"); 1010 | outFile->write(" BU_EV_REL_\n"); 1011 | outFile->write(" BU_BO_REL_\n"); 1012 | outFile->write(" SG_MUL_VAL_\n"); 1013 | outFile->write("\n"); 1014 | outFile->write("BS_: \n"); 1015 | 1016 | //Build list of nodes line 1017 | nodesOutput.append("BU_: "); 1018 | for (int x = 0; x < dbc_nodes.count(); x++) 1019 | { 1020 | DBC_NODE node = dbc_nodes[x]; 1021 | if (node.name.compare("Vector__XXX", Qt::CaseInsensitive) != 0) 1022 | { 1023 | nodesOutput.append(node.name + " "); 1024 | if (node.comment.length() > 0) 1025 | { 1026 | commentsOutput.append("CM_ BU_ " + node.name + " \"" + node.comment + "\";\n"); 1027 | } 1028 | if (node.attributes.count() > 0) 1029 | { 1030 | foreach (DBC_ATTRIBUTE_VALUE val, node.attributes) { 1031 | attrValOutput.append("BA_ \"" + val.attrName + "\" BU_ "); 1032 | switch (val.value.type()) 1033 | { 1034 | case QMetaType::QString: 1035 | attrValOutput.append("\"" + val.value.toString() + "\";\n"); 1036 | break; 1037 | default: 1038 | attrValOutput.append(val.value.toString() + ";\n"); 1039 | break; 1040 | } 1041 | } 1042 | } 1043 | } 1044 | } 1045 | nodesOutput.append("\n"); 1046 | outFile->write(nodesOutput.toUtf8()); 1047 | 1048 | //Go through all messages one at at time issuing the message line then all signals in there too. 1049 | for (int x = 0; x < messageHandler->getCount(); x++) 1050 | { 1051 | DBC_MESSAGE *msg = messageHandler->findMsgByIdx(x); 1052 | 1053 | msgOutput.append("BO_ " + QString::number(msg->ID) + " " + msg->name + ": " + QString::number(msg->len) + 1054 | " " + msg->sender->name + "\n"); 1055 | if (msg->comment.length() > 0) 1056 | { 1057 | commentsOutput.append("CM_ BO_ " + QString::number(msg->ID) + " \"" + msg->comment + "\";\n"); 1058 | } 1059 | 1060 | //If this message has attributes then compile them into attributes list to output later on. 1061 | if (msg->attributes.count() > 0) 1062 | { 1063 | foreach (DBC_ATTRIBUTE_VALUE val, msg->attributes) { 1064 | attrValOutput.append("BA_ \"" + val.attrName + "\" BO_ " + QString::number(msg->ID) + " "); 1065 | switch (val.value.type()) 1066 | { 1067 | case QMetaType::QString: 1068 | attrValOutput.append("\"" + val.value.toString() + "\";\n"); 1069 | break; 1070 | default: 1071 | attrValOutput.append(val.value.toString() + ";\n"); 1072 | break; 1073 | } 1074 | } 1075 | } 1076 | 1077 | for (int s = 0; s < msg->sigHandler->getCount(); s++) 1078 | { 1079 | DBC_SIGNAL *sig = msg->sigHandler->findSignalByIdx(s); 1080 | msgOutput.append(" SG_ " + sig->name); 1081 | 1082 | if (sig->isMultiplexor) msgOutput.append(" M"); 1083 | if (sig->isMultiplexed) 1084 | { 1085 | msgOutput.append(" m" + QString::number(sig->multiplexValue)); 1086 | } 1087 | 1088 | msgOutput.append(" : " + QString::number(sig->startBit) + "|" + QString::number(sig->signalSize) + "@"); 1089 | 1090 | switch (sig->valType) 1091 | { 1092 | case UNSIGNED_INT: 1093 | if (sig->intelByteOrder) msgOutput.append("1+"); 1094 | else msgOutput.append("0+"); 1095 | break; 1096 | case SIGNED_INT: 1097 | if (sig->intelByteOrder) msgOutput.append("1-"); 1098 | else msgOutput.append("0-"); 1099 | break; 1100 | case SP_FLOAT: 1101 | msgOutput.append("2-"); 1102 | break; 1103 | case DP_FLOAT: 1104 | msgOutput.append("3-"); 1105 | break; 1106 | case STRING: 1107 | msgOutput.append("4-"); 1108 | break; 1109 | default: 1110 | msgOutput.append("0-"); 1111 | break; 1112 | } 1113 | msgOutput.append(" (" + QString::number(sig->factor) + "," + QString::number(sig->bias) + ") [" + 1114 | QString::number(sig->min) + "|" + QString::number(sig->max) + "] \"" + sig->unitName 1115 | + "\" " + sig->receiver->name + "\n"); 1116 | if (sig->comment.length() > 0) 1117 | { 1118 | commentsOutput.append("CM_ SG_ " + QString::number(msg->ID) + " " + sig->name + " \"" + sig->comment + "\";\n"); 1119 | } 1120 | 1121 | //if this signal has attributes then compile them in a special list of attributes 1122 | if (sig->attributes.count() > 0) 1123 | { 1124 | foreach (DBC_ATTRIBUTE_VALUE val, sig->attributes) { 1125 | attrValOutput.append("BA_ \"" + val.attrName + "\" SG_ " + QString::number(msg->ID) + " " + sig->name + " "); 1126 | switch (val.value.type()) 1127 | { 1128 | case QMetaType::QString: 1129 | attrValOutput.append("\"" + val.value.toString() + "\";\n"); 1130 | break; 1131 | default: 1132 | attrValOutput.append(val.value.toString() + ";\n"); 1133 | break; 1134 | } 1135 | } 1136 | } 1137 | 1138 | if (sig->valList.count() > 0) 1139 | { 1140 | valuesOutput.append("VAL_ " + QString::number(msg->ID) + " " + sig->name); 1141 | for (int v = 0; v < sig->valList.count(); v++) 1142 | { 1143 | DBC_VAL_ENUM_ENTRY val = sig->valList[v]; 1144 | valuesOutput.append(" " + QString::number(val.value) + " \"" + val.descript +"\""); 1145 | } 1146 | valuesOutput.append(";\n"); 1147 | } 1148 | } 1149 | msgOutput.append("\n"); 1150 | //write it out every message so the string doesn't end up too huge 1151 | outFile->write(msgOutput.toUtf8()); 1152 | msgOutput.clear(); //got to reset it after writing 1153 | } 1154 | 1155 | //Now dump out all of the stored attributes 1156 | for (int x = 0; x < dbc_attributes.count(); x++) 1157 | { 1158 | msgOutput.append("BA_DEF_ "); 1159 | switch (dbc_attributes[x].attrType) 1160 | { 1161 | case GENERAL: 1162 | break; 1163 | case NODE: 1164 | msgOutput.append("BU_ "); 1165 | break; 1166 | case MESSAGE: 1167 | msgOutput.append("BO_ "); 1168 | break; 1169 | case SIG: 1170 | msgOutput.append("SG_ "); 1171 | break; 1172 | } 1173 | 1174 | msgOutput.append("\"" + dbc_attributes[x].name + "\" "); 1175 | 1176 | switch (dbc_attributes[x].valType) 1177 | { 1178 | case QINT: 1179 | msgOutput.append("INT " + QString::number(dbc_attributes[x].lower) + " " + QString::number(dbc_attributes[x].upper)); 1180 | break; 1181 | case QFLOAT: 1182 | msgOutput.append("FLOAT " + QString::number(dbc_attributes[x].lower) + " " + QString::number(dbc_attributes[x].upper)); 1183 | break; 1184 | case QSTRING: 1185 | msgOutput.append("STRING "); 1186 | break; 1187 | case ENUM: 1188 | msgOutput.append("ENUM "); 1189 | foreach (QString str, dbc_attributes[x].enumVals) 1190 | { 1191 | msgOutput.append("\"" + str + "\","); 1192 | } 1193 | msgOutput.truncate(msgOutput.length() - 1); //remove trailing , 1194 | break; 1195 | } 1196 | 1197 | msgOutput.append(";\n"); 1198 | 1199 | outFile->write(msgOutput.toUtf8()); 1200 | msgOutput.clear(); //got to reset it after writing 1201 | 1202 | if (dbc_attributes[x].defaultValue.isValid()) 1203 | { 1204 | defaultsOutput.append("BA_DEF_DEF_ \"" + dbc_attributes[x].name + "\" "); 1205 | switch (dbc_attributes[x].valType) 1206 | { 1207 | case QSTRING: 1208 | defaultsOutput.append("\"" + dbc_attributes[x].defaultValue.toString() + "\";\n"); 1209 | break; 1210 | case ENUM: 1211 | defaultsOutput.append("\"" + dbc_attributes[x].enumVals[dbc_attributes[x].defaultValue.toInt()] + "\";\n"); 1212 | break; 1213 | case QINT: 1214 | case QFLOAT: 1215 | defaultsOutput.append(dbc_attributes[x].defaultValue.toString() + ";\n"); 1216 | break; 1217 | } 1218 | } 1219 | } 1220 | 1221 | //now write out all of the accumulated comments and value tables from above 1222 | outFile->write(attrValOutput.toUtf8()); 1223 | outFile->write(defaultsOutput.toUtf8()); 1224 | outFile->write(commentsOutput.toUtf8()); 1225 | outFile->write(valuesOutput.toUtf8()); 1226 | 1227 | attrValOutput.clear(); 1228 | defaultsOutput.clear(); 1229 | commentsOutput.clear(); 1230 | valuesOutput.clear(); 1231 | 1232 | outFile->close(); 1233 | delete outFile; 1234 | 1235 | QStringList fileList = fileName.split('/'); 1236 | this->fileName = fileList[fileList.length() - 1]; //whoops... same name as parameter in this function. 1237 | filePath = fileName.left(fileName.length() - this->fileName.length()); 1238 | } 1239 | 1240 | void DBCHandler::saveDBCFile(int idx) 1241 | { 1242 | if (loadedFiles.count() == 0) return; 1243 | if (idx < 0) return; 1244 | if (idx >= loadedFiles.count()) return; 1245 | 1246 | QString filename; 1247 | QFileDialog dialog; 1248 | 1249 | QStringList filters; 1250 | filters.append(QString(tr("DBC File (*.dbc)"))); 1251 | 1252 | dialog.setFileMode(QFileDialog::AnyFile); 1253 | dialog.setNameFilters(filters); 1254 | dialog.setViewMode(QFileDialog::Detail); 1255 | dialog.setAcceptMode(QFileDialog::AcceptSave); 1256 | dialog.selectFile(loadedFiles[idx].getFullFilename()); 1257 | 1258 | if (dialog.exec() == QDialog::Accepted) 1259 | { 1260 | filename = dialog.selectedFiles()[0]; 1261 | if (!filename.contains('.')) filename += ".dbc"; 1262 | loadedFiles[idx].saveFile(filename); 1263 | } 1264 | } 1265 | 1266 | int DBCHandler::createBlankFile() 1267 | { 1268 | DBCFile newFile; 1269 | DBC_ATTRIBUTE attr; 1270 | 1271 | //add our custom attributes to the new file so that we know they're already there. 1272 | attr.attrType = MESSAGE; 1273 | attr.defaultValue = QColor(Qt::white).name(); 1274 | attr.enumVals.clear(); 1275 | attr.lower = 0; 1276 | attr.upper = 0; 1277 | attr.name = "GenMsgBackgroundColor"; 1278 | attr.valType = QSTRING; 1279 | newFile.dbc_attributes.append(attr); 1280 | 1281 | attr.attrType = MESSAGE; 1282 | attr.defaultValue = QApplication::palette().color(QPalette::WindowText).name(); 1283 | attr.enumVals.clear(); 1284 | attr.lower = 0; 1285 | attr.upper = 0; 1286 | attr.name = "GenMsgForegroundColor"; 1287 | attr.valType = QSTRING; 1288 | newFile.dbc_attributes.append(attr); 1289 | 1290 | attr.attrType = MESSAGE; 1291 | attr.defaultValue = 0; 1292 | attr.enumVals.clear(); 1293 | attr.lower = 0; 1294 | attr.upper = 0; 1295 | attr.name = "isj1939dbc"; 1296 | attr.valType = QINT; 1297 | newFile.dbc_attributes.append(attr); 1298 | 1299 | loadedFiles.append(newFile); 1300 | return loadedFiles.count(); 1301 | } 1302 | 1303 | //the only reason to even bother sending the index is to see if 1304 | //the user wants to replace an already loaded DBC. 1305 | //Otherwise add a new one. Well, always add a new one. 1306 | //If a valid index is passed we'll remove that one and then commence 1307 | //adding. Otherwise, just go straight to adding. 1308 | DBCFile* DBCHandler::loadDBCFile(int idx) 1309 | { 1310 | if (idx > -1 && idx < loadedFiles.count()) removeDBCFile(idx); 1311 | 1312 | QString filename; 1313 | QFileDialog dialog; 1314 | 1315 | QStringList filters; 1316 | filters.append(QString(tr("DBC File (*.dbc)"))); 1317 | 1318 | dialog.setFileMode(QFileDialog::ExistingFile); 1319 | dialog.setNameFilters(filters); 1320 | dialog.setViewMode(QFileDialog::Detail); 1321 | 1322 | if (dialog.exec() == QDialog::Accepted) 1323 | { 1324 | filename = dialog.selectedFiles()[0]; 1325 | //right now there is only one file type that can be loaded here so just do it. 1326 | DBCFile newFile; 1327 | newFile.loadFile(filename); 1328 | loadedFiles.append(newFile); 1329 | 1330 | return &loadedFiles.last(); 1331 | } 1332 | 1333 | return NULL; 1334 | } 1335 | 1336 | void DBCHandler::removeDBCFile(int idx) 1337 | { 1338 | if (loadedFiles.count() == 0) return; 1339 | if (idx < 0) return; 1340 | if (idx >= loadedFiles.count()) return; 1341 | loadedFiles.removeAt(idx); 1342 | } 1343 | 1344 | void DBCHandler::removeAllFiles() 1345 | { 1346 | loadedFiles.clear(); 1347 | } 1348 | 1349 | void DBCHandler::swapFiles(int pos1, int pos2) 1350 | { 1351 | if (loadedFiles.count() < 2) return; 1352 | if (pos1 < 0) return; 1353 | if (pos1 >= loadedFiles.count()) return; 1354 | if (pos2 < 0) return; 1355 | if (pos2 >= loadedFiles.count()) return; 1356 | 1357 | loadedFiles.swap(pos1, pos2); 1358 | } 1359 | 1360 | /* 1361 | * Convenience function that encapsulates a whole lot of the details. 1362 | * You give it a canbus frame and it'll tell you whether there is a loaded DBC file that can 1363 | * interpret that frame for you. 1364 | * Returns NULL if there is no message definition that matches. 1365 | */ 1366 | 1367 | int DBCHandler::getFileCount() 1368 | { 1369 | return loadedFiles.count(); 1370 | } 1371 | 1372 | DBCFile* DBCHandler::getFileByIdx(int idx) 1373 | { 1374 | if (loadedFiles.count() == 0) return NULL; 1375 | if (idx < 0) return NULL; 1376 | if (idx >= loadedFiles.count()) return NULL; 1377 | return &loadedFiles[idx]; 1378 | } 1379 | 1380 | DBCFile* DBCHandler::getFileByName(QString name) 1381 | { 1382 | if (loadedFiles.count() == 0) return NULL; 1383 | for (int i = 0; i < loadedFiles.count(); i++) 1384 | { 1385 | if (loadedFiles[i].getFilename().compare(name, Qt::CaseInsensitive) == 0) 1386 | { 1387 | return &loadedFiles[i]; 1388 | } 1389 | } 1390 | return NULL; 1391 | } 1392 | 1393 | DBCHandler::DBCHandler() 1394 | { 1395 | 1396 | } 1397 | 1398 | DBCHandler* DBCHandler::getReference() 1399 | { 1400 | if (!instance) instance = new DBCHandler(); 1401 | return instance; 1402 | } 1403 | -------------------------------------------------------------------------------- /dbc/dbchandler.h: -------------------------------------------------------------------------------- 1 | #ifndef DBCHANDLER_H 2 | #define DBCHANDLER_H 3 | 4 | #include 5 | #include "dbc_classes.h" 6 | 7 | /* 8 | * TODO: 9 | * Finish coding up the decoupled design 10 | * 11 | */ 12 | class DBCSignalHandler: public QObject 13 | { 14 | Q_OBJECT 15 | public: 16 | DBC_SIGNAL *findSignalByName(QString name); 17 | DBC_SIGNAL *findSignalByIdx(int idx); 18 | bool addSignal(DBC_SIGNAL &sig); 19 | bool removeSignal(DBC_SIGNAL *sig); 20 | bool removeSignal(int idx); 21 | bool removeSignal(QString name); 22 | void removeAllSignals(); 23 | int getCount(); 24 | private: 25 | QList sigs; //signals is a reserved word or I'd have used that 26 | }; 27 | 28 | class DBCMessageHandler: public QObject 29 | { 30 | Q_OBJECT 31 | public: 32 | DBC_MESSAGE *findMsgByID(uint32_t id); 33 | DBC_MESSAGE *findMsgByIdx(int idx); 34 | DBC_MESSAGE *findMsgByName(QString name); 35 | bool addMessage(DBC_MESSAGE &msg); 36 | bool removeMessage(DBC_MESSAGE *msg); 37 | bool removeMessageByIndex(int idx); 38 | bool removeMessage(uint32_t ID); 39 | bool removeMessage(QString name); 40 | void removeAllMessages(); 41 | int getCount(); 42 | bool isJ1939(); 43 | void setJ1939(bool j1939); 44 | private: 45 | QList messages; 46 | bool isJ1939Handler; 47 | }; 48 | 49 | //technically there should be a node handler too but I'm sort of treating nodes as second class 50 | //citizens since they aren't really all that important (to me anyway) 51 | class DBCFile: public QObject 52 | { 53 | Q_OBJECT 54 | public: 55 | DBCFile(); 56 | DBCFile(const DBCFile& cpy); 57 | DBCFile& operator=(const DBCFile& cpy); 58 | DBC_NODE *findNodeByName(QString name); 59 | DBC_NODE *findNodeByIdx(int idx); 60 | DBC_ATTRIBUTE *findAttributeByName(QString name); 61 | DBC_ATTRIBUTE *findAttributeByIdx(int idx); 62 | void findAttributesByType(DBC_ATTRIBUTE_TYPE typ, QList *list); 63 | void saveFile(QString); 64 | void loadFile(QString); 65 | QString getFullFilename(); 66 | QString getFilename(); 67 | QString getPath(); 68 | int getAssocBus(); 69 | void setAssocBus(int bus); 70 | 71 | DBCMessageHandler *messageHandler; 72 | QList dbc_nodes; 73 | QList dbc_attributes; 74 | private: 75 | QString fileName; 76 | QString filePath; 77 | int assocBuses; //-1 = all buses, 0 = first bus, 1 = second bus, etc. 78 | 79 | bool parseAttribute(QString inpString, DBC_ATTRIBUTE &attr); 80 | QVariant processAttributeVal(QString input, DBC_ATTRIBUTE_VAL_TYPE typ); 81 | }; 82 | 83 | class DBCHandler: public QObject 84 | { 85 | Q_OBJECT 86 | public: 87 | DBCFile* loadDBCFile(int); 88 | void saveDBCFile(int); 89 | void removeDBCFile(int); 90 | void removeAllFiles(); 91 | void swapFiles(int pos1, int pos2); 92 | int getFileCount(); 93 | DBCFile* getFileByIdx(int idx); 94 | DBCFile* getFileByName(QString name); 95 | int createBlankFile(); 96 | static DBCHandler *getReference(); 97 | 98 | private: 99 | QList loadedFiles; 100 | 101 | DBCHandler(); 102 | static DBCHandler *instance; 103 | }; 104 | 105 | #endif // DBCHANDLER_H 106 | -------------------------------------------------------------------------------- /dbc/utility.cpp: -------------------------------------------------------------------------------- 1 | #include "utility.h" 2 | 3 | bool Utility::decimalMode = false; 4 | bool Utility::secondsMode = true; 5 | bool Utility::sysTimeMode = false; 6 | QString Utility::timeFormat = "MMM-dd HH:mm:ss.zzz"; 7 | -------------------------------------------------------------------------------- /dbc/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_H 2 | #define UTILITY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Utility 11 | { 12 | public: 13 | 14 | static bool decimalMode; 15 | static bool secondsMode; 16 | static bool sysTimeMode; 17 | static QString timeFormat; 18 | 19 | static QString unQuote(QString inStr) 20 | { 21 | QStringList temp; 22 | temp = inStr.split('\"'); 23 | if (temp.length() >= 3) 24 | return temp[1]; 25 | return inStr; 26 | } 27 | 28 | static uint64_t ParseStringToNum(QByteArray input) 29 | { 30 | uint64_t temp = 0; 31 | 32 | input = input.toUpper(); 33 | if (input.startsWith("0X") || input.startsWith("X")) //hex number 34 | { 35 | if (input.length() < 3) temp = 0; 36 | else temp = input.right(input.size() - 2).toLongLong(NULL, 16); 37 | } 38 | else if (input.startsWith("0B") || input.startsWith("B")) //binary number 39 | { 40 | input = input.right(input.size() - 1); //remove the B 41 | for (int i = 0; i < input.length(); i++) 42 | { 43 | if (input[i] == '1') temp += (uint64_t)1 << (input.length() - i - 1); 44 | } 45 | } 46 | else //decimal number 47 | { 48 | temp = input.toLongLong(); 49 | } 50 | 51 | return temp; 52 | } 53 | 54 | static uint64_t ParseStringToNum(QString input) 55 | { 56 | return ParseStringToNum(input.toUtf8()); 57 | } 58 | 59 | static uint ParseStringToNum2(QString pInput, bool* pOk_p = NULL) 60 | { 61 | if(pInput.startsWith("0b")) 62 | { 63 | pInput.remove(0, 2); 64 | return pInput.toUInt(pOk_p, 2); 65 | } 66 | 67 | return pInput.toUInt(pOk_p, 0); 68 | } 69 | 70 | static long GetTimeMS() 71 | { 72 | QDateTime stamp = QDateTime::currentDateTime(); 73 | return (long)(((stamp.time().hour() * 3600) + (stamp.time().minute() * 60) + (stamp.time().second()) * 1000) + stamp.time().msec()); 74 | } 75 | 76 | //prints hex numbers in uppercase with 0's filling out the number depending 77 | //on the size needed. Promotes hex numbers to either 2, 4, or 8 digits 78 | static QString formatHexNum(uint64_t input) 79 | { 80 | if (input < 256) 81 | return "0x" + QString::number(input, 16).toUpper().rightJustified(2,'0'); 82 | if (input < 65536) 83 | return "0x" + QString::number(input, 16).toUpper().rightJustified(4,'0'); 84 | if (input < 4294967296) 85 | return "0x" + QString::number(input, 16).toUpper().rightJustified(8,'0'); 86 | return "0x" + QString::number(input, 16).toUpper().rightJustified(16,'0'); 87 | } 88 | 89 | //uses decimalMode to see if it should show value as decimal or hex 90 | static QString formatNumber(uint64_t value) 91 | { 92 | if (decimalMode) 93 | { 94 | return QString::number(value, 10); 95 | } 96 | else return formatHexNum(value); 97 | } 98 | 99 | static QString formatCANID(uint64_t id, bool extended) 100 | { 101 | if (decimalMode) return QString::number(id, 10); 102 | 103 | if (extended) 104 | { 105 | return "0x" + QString::number(id, 16).toUpper().rightJustified(8,'0'); 106 | } 107 | else 108 | { 109 | id = id & 0x7FF; 110 | return "0x" + QString::number(id, 16).toUpper().rightJustified(3,'0'); 111 | } 112 | } 113 | 114 | static QString formatCANID(uint64_t id) 115 | { 116 | if (id < 0x800) return formatCANID(id, false); 117 | return formatCANID(id, true); 118 | } 119 | 120 | static QString formatByteAsBinary(uint8_t value) 121 | { 122 | QString output; 123 | for (int b = 7; b >= 0; b--) 124 | { 125 | if (value & (1 << b)) output += "1"; 126 | else output += "0"; 127 | } 128 | return output; 129 | } 130 | 131 | static QString formatTimestamp(uint64_t timestamp) 132 | { 133 | if (!sysTimeMode) { 134 | if (!secondsMode) return QString::number(timestamp); 135 | else return QString::number((double)timestamp / 1000000.0, 'f', 6); 136 | } 137 | else return QDateTime::fromMSecsSinceEpoch(timestamp / 1000).toString(timeFormat); 138 | } 139 | 140 | //parses the input string to grab as much of it as possible while staying alpha numeric 141 | static QString grabAlphaNumeric(QString &input) 142 | { 143 | QString builder; 144 | QChar thisChar; 145 | for (int i = 0; i < input.length(); i++) 146 | { 147 | thisChar = input[i]; 148 | if (thisChar.isLetterOrNumber() || thisChar == ':' || thisChar == '~') builder.append(input[i]); 149 | else 150 | { 151 | //qDebug() << "i: "<< i << " len: " << input.length(); 152 | if (i < (input.length() - 1)) input = input.right(input.length() - i); 153 | else input = ""; 154 | return builder; 155 | } 156 | } 157 | //qDebug() << "Reached end of string in grabAlphaNumeric"; 158 | input = ""; 159 | return builder; 160 | } 161 | 162 | static QString grabOperation(QString &input) 163 | { 164 | QString builder; 165 | QChar thisChar = input[0]; 166 | 167 | if (thisChar == '+' || thisChar == '-' || thisChar == '*' || thisChar == '/' || thisChar == '^' || thisChar == '&' || thisChar == '|' || thisChar == '=' || thisChar == '%') 168 | { 169 | input = input.right(input.length() - 1); 170 | builder = thisChar; 171 | } 172 | return builder; 173 | } 174 | 175 | //simple linear interpolation between value1 and value2. sample point is 0.0 to 1.0 176 | static double Lerp(double value1, double value2, double samplePoint) 177 | { 178 | return (value1 * (1.0 - samplePoint)) + (value2 * samplePoint); 179 | } 180 | 181 | static int64_t processIntegerSignal(const uint8_t *data, int startBit, int sigSize, bool littleEndian, bool isSigned) 182 | { 183 | 184 | int64_t result = 0; 185 | int bit; 186 | 187 | if (littleEndian) 188 | { 189 | bit = startBit; 190 | for (int bitpos = 0; bitpos < sigSize; bitpos++) 191 | { 192 | if (data[bit / 8] & (1 << (bit % 8))) 193 | result += (1ULL << bitpos); 194 | bit++; 195 | } 196 | } 197 | else //motorola / big endian mode 198 | { 199 | bit = startBit; 200 | for (int bitpos = 0; bitpos < sigSize; bitpos++) 201 | { 202 | if (data[bit / 8] & (1 << (bit % 8))) 203 | result += (1ULL << (sigSize - bitpos - 1)); 204 | 205 | if ((bit % 8) == 0) 206 | bit += 15; 207 | else bit--; 208 | 209 | } 210 | } 211 | 212 | if (isSigned) 213 | { 214 | int64_t mask = (1ULL << (sigSize - 1)); 215 | if ((result & mask) == mask) //is the highest bit possible for this signal size set? 216 | { 217 | /* 218 | * if so we need to also set every bit higher in the result int too. 219 | * This leads to the below two lines that are nasty. Here's the theory behind that... 220 | * If the value is signed and the highest bit is set then it is negative. To create 221 | * a negative value out of this even though the variable result is 64 bit we have to 222 | * run 1's all of the way up to bit 63 in result. -1 is all ones for whatever size integer 223 | * you have. So, it's 64 1's in this case. 224 | * signedMask is done this way: 225 | * first you take the signal size and shift 1 up that far. Then subtract one. Lets 226 | * see that for a 16 bit signal: 227 | * (1 << 16) - 1 = the first 16 bits set as 1's. So far so good. We then negate the whole 228 | * thing which flips all bits. Thus signedMask ends up with 1's everwhere that the signal 229 | * doesn't take up in the 64 bit signed integer result. Then, result has an OR operation on 230 | * it with the old value and -1 masked so that the the 1 bits from -1 don't overwrite bits from the 231 | * actual signal. This extends the sign bits out so that the integer result reads as the proper negative 232 | * value. We dont need to do any of this if the sign bit wasn't set. 233 | */ 234 | uint64_t signedMask = ~((1ULL << sigSize) - 1); 235 | result = (-1LL & signedMask) | result; 236 | } 237 | } 238 | 239 | return result; 240 | } 241 | }; 242 | 243 | #endif // UTILITY_H 244 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "dbc/dbchandler.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define STAMP "yyyyMMdd HH:mm:ss.zzz" 12 | #define DEFAULT_MESSAGE "BCU_04" 13 | int main(int argc, char *argv[]) 14 | { 15 | QApplication app(argc, argv); 16 | 17 | app.setApplicationVersion(QDateTime::currentDateTime().toString(STAMP)); 18 | QCommandLineParser p; 19 | p.setApplicationDescription("DBC data with UDP network emulator"); 20 | p.addHelpOption(); 21 | p.addVersionOption(); 22 | p.addOption({{"t", "timer inteval"}, "timer inteval. ms,default is 1000ms","t","1000"}); 23 | p.addOption({{"u", "use time"}, "use massage timer inteval. ms","u",""}); 24 | p.addOption({{"m", "message name"}, "message name. string,default is " DEFAULT_MESSAGE,"m",DEFAULT_MESSAGE}); 25 | p.addOption({{"I", "message id"}, "message ID. int,default is 91","I","91"}); 26 | p.addOption({{"s", "signal idx"}, "signal idx. int,default is 0","s"}); 27 | p.addOption({{"n", "no run"}, "do not increment,default is 0","n","0"}); 28 | p.addOption({{"i", "ip"}, "UDP host IP. string,default is 127.0.0.1","i","127.0.0.1"}); 29 | p.addOption({{"p", "port"}, "UDP host port,int. default is 11000","p","11000"}); 30 | p.addOption({{"V","verbose"}, "Verbose mode. Prints out more information."}); 31 | p.addPositionalArgument("input", "DBC file.", "input"); 32 | p.process(app); 33 | 34 | QScopedPointer win; 35 | 36 | QStringList arglist = p.positionalArguments(); 37 | if(arglist.size()>0){ 38 | QString dbcfile = arglist.at(0); 39 | DBCFile newFile; 40 | newFile.loadFile(dbcfile); 41 | 42 | DBCMessageHandler *messageHandler = newFile.messageHandler; 43 | DBC_MESSAGE *message = messageHandler->findMsgByName(DEFAULT_MESSAGE); 44 | { 45 | DBC_MESSAGE *a = messageHandler->findMsgByName(p.value("m")); 46 | DBC_MESSAGE *b = messageHandler->findMsgByID(quint32(p.value("I").toInt())); 47 | if(p.isSet("m")&& a) message = a; 48 | if(p.isSet("I")&& b) message = b; 49 | } 50 | 51 | int cycletime = p.value("t").toInt(); 52 | if(arglist.size()>1){ 53 | cycletime = arglist.at(1).toInt(); 54 | } 55 | if(p.isSet("u")&&p.value("u").toInt()>0){ 56 | if(message){ 57 | DBC_ATTRIBUTE_VALUE *attr = message->findAttrValByName("GenMsgCycleTime"); 58 | if(attr){ 59 | cycletime = attr->value.toInt(); 60 | } 61 | } 62 | } 63 | 64 | /////////////////////////////////////////////////// 65 | for(int m=0;mgetCount();++m){ 66 | DBC_MESSAGE *msg = messageHandler->findMsgByIdx(m); 67 | DBCSignalHandler *sigHandler = msg->sigHandler; 68 | for(int i=0;igetCount();++i){ 69 | DBC_SIGNAL* sig = sigHandler->findSignalByIdx(i); 70 | qInfo() <parentMessage->ID<parentMessage->len << sig->name<startBit<signalSize; 71 | } 72 | qInfo()<< msg->ID; 73 | } 74 | 75 | QTimer *timer = new QTimer(&app); 76 | QObject::connect(timer,&QTimer::timeout,[&](){ 77 | if(not message or message->len>8) return ; 78 | static quint64 cnt = 0; 79 | static QStringList lst = p.value("s").split(QRegExp("\\W+"),QString::SkipEmptyParts); 80 | 81 | std::bitset<64> payload; 82 | if(p.value("n").toInt()<=0){ 83 | DBCSignalHandler *sigHandler = message->sigHandler; 84 | // for(int i=0;igetCount();++i){ 85 | payload = std::bitset<64>(++cnt); 86 | // } 87 | } 88 | for(auto s:lst) payload.set(s.toInt()); 89 | 90 | QUdpSocket sock; 91 | QNetworkDatagram gram; 92 | QByteArray upacket; 93 | QDataStream stream(&upacket, QIODevice::WriteOnly); 94 | stream.setByteOrder(QDataStream::BigEndian); 95 | stream << quint32(message->ID); 96 | stream << quint8(message->len); 97 | quint64 payl = payload.to_ullong(); 98 | stream.writeRawData(reinterpret_cast(&payl),int(message->len)); 99 | gram.setData(upacket); 100 | gram.setDestination(QHostAddress(p.value("i")),quint16(p.value("p").toInt())); 101 | qInfo()<name<<"ID:"<ID<<"payload:"<len<<"total:"<start(cycletime); 105 | 106 | } else { 107 | win.reset(new MainWindow); 108 | win->show(); 109 | } 110 | 111 | return app.exec(); 112 | } 113 | -------------------------------------------------------------------------------- /mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include 4 | MainWindow::MainWindow(QWidget *parent) : 5 | QMainWindow(parent), 6 | ui(new Ui::MainWindow) 7 | { 8 | ui->setupUi(this); 9 | 10 | } 11 | 12 | MainWindow::~MainWindow() 13 | { 14 | delete ui; 15 | } 16 | -------------------------------------------------------------------------------- /mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class MainWindow; 8 | } 9 | 10 | class MainWindow : public QMainWindow 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit MainWindow(QWidget *parent = 0); 16 | ~MainWindow(); 17 | 18 | private: 19 | Ui::MainWindow *ui; 20 | }; 21 | 22 | #endif // MAINWINDOW_H 23 | -------------------------------------------------------------------------------- /mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | MainWindow 3 | 4 | 5 | 6 | 0 7 | 0 8 | 400 9 | 300 10 | 11 | 12 | 13 | MainWindow 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | --------------------------------------------------------------------------------