/* Copyright (c) 2015, 2016 Hubert Denkmair This file is part of cangaroo. cangaroo is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. cangaroo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with cangaroo. If not, see . */ #include "DbcParser.h" #include #include #include #include #include #include "DbcTokens.h" DbcParser::DbcParser() : _errorLine(0), _errorColumn(0) { } bool DbcParser::parseFile(QFile *file, CanDb &candb) { DbcTokenList tokens; if (tokenize(file, tokens) != err_ok) { QString msg = QString("error parsing dbc file %1").arg(file->fileName()); if (_errorLine) { msg += QString(" at line %1, column %2").arg(_errorLine).arg(_errorColumn); } log_error(msg); return false; } candb.setPath(file->fileName()); return parse(candb, tokens); } DbcToken *DbcParser::createNewToken(QChar ch, int line, int column) { static const QString acceptableIdStartChars("ABCDEFGHIKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"); static const QRegExp numberRegExp("^(\\d+(\\.\\d*)?(E[-+]?\\d*)?)$"); if (ch.isSpace()) { return new DbcWhitespaceToken(line, column); } else if (ch.isDigit()) { return new DbcRegExpToken(line, column, dbc_tok_number, numberRegExp); } else if (ch == '"') { return new DbcStringToken(line, column); } else if (ch == ':') { return new DbcSingleCharToken(line, column, dbc_tok_colon, ':'); } else if (ch == '|') { return new DbcSingleCharToken(line, column, dbc_tok_pipe, '|'); } else if (ch == '@') { return new DbcSingleCharToken(line, column, dbc_tok_at, '@'); } else if (ch == '+') { return new DbcSingleCharToken(line, column, dbc_tok_plus, '+'); } else if (ch == '-') { return new DbcSingleCharToken(line, column, dbc_tok_minus, '-'); } else if (ch == '(') { return new DbcSingleCharToken(line, column, dbc_tok_parenth_open, '('); } else if (ch == ')') { return new DbcSingleCharToken(line, column, dbc_tok_parenth_close, ')'); } else if (ch == '[') { return new DbcSingleCharToken(line, column, dbc_tok_bracket_open, '['); } else if (ch == ']') { return new DbcSingleCharToken(line, column, dbc_tok_bracket_close, ']'); } else if (ch == ',') { return new DbcSingleCharToken(line, column, dbc_tok_comma, ','); } else if (ch == ';') { return new DbcSingleCharToken(line, column, dbc_tok_semicolon, ';'); } else if (acceptableIdStartChars.contains(ch)) { return new DbcIdentifierToken(line, column); } else { return 0; } } DbcParser::error_t DbcParser::tokenize(QFile *file, DbcParser::DbcTokenList &tokens) { if (!file->open(QIODevice::ReadOnly)) { // TODO raise cannot open file exception return err_cannot_open_file; } DbcToken *currentToken = 0; int line = 1; int column = 0; error_t retval = err_ok; QTextStream in(file); in.setCodec("ISO 8859-1"); while (true) { QString s = in.read(1); if (s.isEmpty()) { break; } QChar ch = s[0]; if (ch=='\n') { line++; column=1; } else { column++; } if (currentToken) { if (!currentToken->appendChar(ch)) { tokens.append(currentToken); currentToken = 0; } } if (!currentToken) { currentToken = createNewToken(ch, line, column); if (currentToken) { currentToken->appendChar(ch); } else { retval = err_tokenize_error; _errorColumn = column; _errorLine = line; break; } } } file->close(); return retval; } bool DbcParser::isSectionEnding(DbcToken *token, bool newLineIsSectionEnding) { if (!token) { return true; } else { int numNewLinesForEnding = newLineIsSectionEnding ? 1 : 2; dbc_token_type_t type = token->getType(); return ( (type==dbc_tok_semicolon) || ( (type==dbc_tok_whitespace) && (token->countLineBreaks()>=numNewLinesForEnding))); } } DbcToken *DbcParser::readToken(DbcParser::DbcTokenList &tokens, int typeMask, bool skipWhitespace, bool skipSectionEnding, bool newLineIsSectionEnding) { while (true) { if (tokens.isEmpty()) { return 0; } DbcToken *token = tokens.first(); dbc_token_type_t type = token->getType(); if (type & typeMask) { tokens.pop_front(); return token; } else if (isSectionEnding(token, newLineIsSectionEnding)) { if (skipSectionEnding) { tokens.pop_front(); free(token); continue; } else { return 0; } } else if (skipWhitespace && (type==dbc_tok_whitespace)) { tokens.pop_front(); free(token); continue; } else { return 0; } } return 0; } bool DbcParser::expectSectionEnding(DbcTokenList &tokens, bool newLineIsSectionEnding) { if (tokens.isEmpty()) { return true; } DbcToken *token = readToken(tokens, dbc_tok_whitespace|dbc_tok_semicolon); if (!token) { return false; } if (!isSectionEnding(token, newLineIsSectionEnding)) { free(token); return false; } else { free(token); return true; } } bool DbcParser::expectLineBreak(DbcParser::DbcTokenList &tokens) { bool found_line_break; DbcToken *token = readToken(tokens, dbc_tok_whitespace); if (token) { found_line_break = token->countLineBreaks()>0; free(token); } else { found_line_break = false; } return found_line_break; } bool DbcParser::expectAndSkipToken(DbcTokenList &tokens, dbc_token_type_t type, bool skipWhitespace, bool skipSectionEnding) { DbcToken *token = readToken(tokens, type, skipWhitespace, skipSectionEnding); if (!token) { return false; } else { free(token); return true; } } bool DbcParser::expectData(DbcParser::DbcTokenList &tokens, dbc_token_type_t type, QString *data, bool skipWhitespace, bool skipSectionEnding, bool newLineIsSectionEnding) { DbcToken *token; if (!(token = readToken(tokens, type, skipWhitespace, skipSectionEnding, newLineIsSectionEnding))) { return false; } if (data) { data->clear(); data->append(token->getData()); } free(token); return true; } bool DbcParser::expectIdentifier(DbcParser::DbcTokenList &tokens, QString *id, bool skipWhitespace, bool skipSectionEnding, bool newLineIsSectionEnding) { return expectData(tokens, dbc_tok_identifier, id, skipWhitespace, skipSectionEnding, newLineIsSectionEnding); } bool DbcParser::expectString(DbcParser::DbcTokenList &tokens, QString *str, bool skipWhitespace) { QString quotedStr; bool ok = expectData(tokens, dbc_tok_string, "edStr, skipWhitespace); if (ok && quotedStr.length()>=2) { *str = quotedStr.mid(1, quotedStr.length()-2); return true; } else { return false; } } bool DbcParser::expectNumber(DbcParser::DbcTokenList &tokens, QString *str, bool skipWhitespace) { QString data; if (expectAndSkipToken(tokens, dbc_tok_minus, skipWhitespace)) { *str = "-"; } else if (expectAndSkipToken(tokens, dbc_tok_plus, skipWhitespace)) { *str = "+"; } if (expectData(tokens, dbc_tok_number, &data, skipWhitespace)) { *str += data; return true; } else { return false; } } bool DbcParser::expectInt(DbcParser::DbcTokenList &tokens, int *i, int base, bool skipWhitespace) { QString data; if (!expectNumber(tokens, &data, skipWhitespace)) { return false; } bool convert_ok; *i = data.toInt(&convert_ok, base); return convert_ok; } bool DbcParser::expectLongLong(DbcTokenList &tokens, long long *i, int base, bool skipWhitespace) { QString data; if (!expectNumber(tokens, &data, skipWhitespace)) { return false; } bool convert_ok; *i = data.toLongLong(&convert_ok, base); return convert_ok; } bool DbcParser::expectDouble(DbcTokenList &tokens, double *df, bool skipWhitespace) { QString data; if (!expectNumber(tokens, &data, skipWhitespace)) { return false; } bool convert_ok; *df = data.toDouble(&convert_ok); return convert_ok; } void DbcParser::skipUntilSectionEnding(DbcTokenList &tokens) { while (!tokens.isEmpty()) { DbcToken *token = readToken(tokens, dbc_tok_ALL, false, false); if (!token) { return; } if (isSectionEnding(token)) { free(token); return; } else { free(token); } } } bool DbcParser::parseIdentifierList(DbcTokenList &tokens, QStringList *list, bool newLineIsSectionEnding) { if (!expectAndSkipToken(tokens, dbc_tok_colon)) { return false; } QString id; while (expectIdentifier(tokens, &id, true, false, newLineIsSectionEnding)) { if (list) { list->append(id); } } return expectSectionEnding(tokens, newLineIsSectionEnding); } bool DbcParser::parse(CanDb &candb, DbcTokenList &tokens) { _dbcVersion.clear(); _nsEntries.clear(); _buEntries.clear(); while (!tokens.isEmpty()) { if (!parseSection(candb, tokens)) { return false; } } return true; } bool DbcParser::parseSection(CanDb &candb, DbcTokenList &tokens) { bool retval = true; QString sectionName; QStringList strings; while (retval) { if (tokens.isEmpty()) { break; } if (expectIdentifier(tokens, §ionName, true, true)) { if (sectionName == "VERSION") { retval &= parseSectionVersion(candb, tokens); } else if (sectionName == "NS_") { strings.clear(); retval &= parseIdentifierList(tokens, &strings); } else if (sectionName == "BS_") { retval &= parseSectionBs(tokens); } else if (sectionName == "BU_") { retval &= parseSectionBu(candb, tokens); } else if (sectionName == "BO_") { retval &= parseSectionBo(candb, tokens); } else if (sectionName == "CM_") { retval &= parseSectionCm(candb, tokens); } else if (sectionName == "VAL_") { retval &= parseSectionVal(candb, tokens); } else { skipUntilSectionEnding(tokens); } } else { retval = false; } } if (!retval) { log_error("dbc parse error"); } return retval; /* if (sectionName == "BA_") { return tokSectionBa; } if (sectionName == "BA_REL_") { return tokSectionBaRel; } if (sectionName == "BA_DEF_") { return tokSectionBaDef; } if (sectionName == "BA_DEF_DEF_") { return tokSectionBaDefDef; } if (sectionName == "BA_DEF_DEF_REL_") { return tokSectionBaDefDefRel; } */ } bool DbcParser::parseSectionVersion(CanDb &candb, DbcTokenList &tokens) { QString version; if (!expectString(tokens, &version)) { return false; } candb.setVersion(version); return expectSectionEnding(tokens); } bool DbcParser::parseSectionBs(DbcParser::DbcTokenList &tokens) { if (!expectAndSkipToken(tokens, dbc_tok_colon)) { return false; } return expectSectionEnding(tokens); } bool DbcParser::parseSectionBu(CanDb &candb, DbcParser::DbcTokenList &tokens) { QStringList strings; QString s; if (!parseIdentifierList(tokens, &strings, true)) { return false; } foreach(s, strings) { candb.getOrCreateNode(s); } return true; } bool DbcParser::parseSectionBo(CanDb &candb, DbcTokenList &tokens) { long long can_id; int dlc; QString msg_name; QString sender; if (!expectLongLong(tokens, &can_id)) { return false; } if (!expectIdentifier(tokens, &msg_name)) { return false; } if (!expectAndSkipToken(tokens, dbc_tok_colon)) { return false; } if (!expectInt(tokens, &dlc)) { return false; } if (!expectIdentifier(tokens, &sender)) { return false; } CanDbMessage *msg = new CanDbMessage(&candb); msg->setRaw_id(can_id); msg->setName(msg_name); msg->setDlc(dlc); msg->setSender(candb.getOrCreateNode(sender)); candb.addMessage(msg); QString subsect; while (true) { if (expectSectionEnding(tokens)) { return true; } else { if (!expectIdentifier(tokens, &subsect)) { return false; } if (subsect!="SG_") { return false; } if (!parseSectionBoSg(candb, msg, tokens)) { return false; } } } } bool DbcParser::parseSectionBoSg(CanDb &candb, CanDbMessage *msg, DbcTokenList &tokens) { (void)candb; QString signal_name; QString mux_indicator; int start_bit = 0; int length = 0; int byte_order = 0; double factor = 1; double offset = 0; double minimum = 0; double maximum = 0; QString unit; QString receiver; QStringList receivers; CanDbSignal *signal = new CanDbSignal(msg); msg->addSignal(signal); if (!expectIdentifier(tokens, &signal_name)) { return false; } signal->setName(signal_name); if (expectIdentifier(tokens, &mux_indicator)) { if (mux_indicator=="M") { signal->setIsMuxer(true); msg->setMuxer(signal); } else if (mux_indicator.startsWith('m')) { signal->setIsMuxed(true); bool ok; signal->setMuxValue(mux_indicator.mid(1).toUInt(&ok)); if (!ok) { return false; } } else { return false; } } if (!expectAndSkipToken(tokens, dbc_tok_colon)) { return false; } if (!expectInt(tokens, &start_bit)) { return false; } signal->setStartBit(start_bit); if (!expectAndSkipToken(tokens, dbc_tok_pipe)) { return false; } if (!expectInt(tokens, &length)) { return false; } signal->setLength(length); if (!expectAndSkipToken(tokens, dbc_tok_at)) { return false; } if (!expectInt(tokens, &byte_order)) { return false; } signal->setIsBigEndian(byte_order==0); // If the signal is big endian, convert the start bit to the Intel-style start bit for further parsing if(signal->isBigEndian()) { // This will be the number of 8-bit rows above the message uint8_t row_position = signal->startBit() >> 3; // Bit position in current row (0-7) uint8_t column_position = signal->startBit() & 0b111; // Calcualte the normalized start bit position (bit index starting at 0) uint8_t normalized_position = (row_position * 8) + (7 - column_position); signal->setStartBit(normalized_position); } if (expectAndSkipToken(tokens, dbc_tok_plus)) { signal->setUnsigned(true); } else { if (expectAndSkipToken(tokens, dbc_tok_minus)) { signal->setUnsigned(false); } else { return false; } } if (!expectAndSkipToken(tokens, dbc_tok_parenth_open)) { return false; } if (!expectDouble(tokens, &factor)) { return false; } signal->setFactor(factor); if (!expectAndSkipToken(tokens, dbc_tok_comma)) { return false; } if (!expectDouble(tokens, &offset)) { return false; } signal->setOffset(offset); if (!expectAndSkipToken(tokens, dbc_tok_parenth_close)) { return false; } if (!expectAndSkipToken(tokens, dbc_tok_bracket_open)) { return false; } if (!expectDouble(tokens, &minimum)) { return false; } signal->setMinimumValue(minimum); if (!expectAndSkipToken(tokens, dbc_tok_pipe)) { return false; } if (!expectDouble(tokens, &maximum)) { return false; } signal->setMaximumValue(maximum); if (!expectAndSkipToken(tokens, dbc_tok_bracket_close)) { return false; } if (!expectString(tokens, &unit)) { return false; } signal->setUnit(unit); if (!expectIdentifier(tokens, &receiver)) { return false; } receivers.append(receiver); while (expectAndSkipToken(tokens, dbc_tok_comma, false, false)) { if (!expectIdentifier(tokens, &receiver)) { return false; } receivers.append(receiver); } return true; } bool DbcParser::parseSectionCm(CanDb &candb, DbcParser::DbcTokenList &tokens) { QString s; QString idtype; QString id; long long ll; if (expectString(tokens, &s)) { // DBC file comment candb.setComment(s); return true; } if (!expectIdentifier(tokens, &idtype)) { return false; } if (idtype=="BU_") { if (!expectIdentifier(tokens, &id)) { return false; } if (!expectString(tokens, &s)) { return false; } candb.getOrCreateNode(id)->setComment(s); return expectSectionEnding(tokens); } else if (idtype=="BO_") { if (!expectLongLong(tokens, &ll)) { return false; } if (!expectString(tokens, &s)) { return false; } CanDbMessage *msg = candb.getMessageById(ll); if (!msg) { return false; } msg->setComment(s); return expectSectionEnding(tokens); } else if (idtype=="SG_") { if (!expectLongLong(tokens, &ll)) { return false; } CanDbMessage *msg = candb.getMessageById(ll); if (!msg) { return false; } if (!expectIdentifier(tokens, &id)) { return false; } CanDbSignal *signal = msg->getSignalByName(id); if (!signal) { return false; } if (!expectString(tokens, &s)) { return false; } signal->setComment(s); return expectSectionEnding(tokens); } else { return false; } } bool DbcParser::parseSectionVal(CanDb &candb, DbcParser::DbcTokenList &tokens) { long long can_id; QString signal_id; long long value; QString name; if (!expectLongLong(tokens, &can_id)) { return false; } CanDbMessage *msg = candb.getMessageById(can_id); if (!msg) { return false; } if (!expectIdentifier(tokens, &signal_id)) { return false; } CanDbSignal *signal = msg->getSignalByName(signal_id); if (!signal) { return false; } while (!expectAndSkipToken(tokens, dbc_tok_semicolon)) { if (!expectLongLong(tokens, &value)) { return false; } if (!expectString(tokens, &name)) { return false; } signal->setValueName(value, name); } return true; }