DBC parser initial commit.
This commit is contained in:
18
dbc.pro
Normal file
18
dbc.pro
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += console c++17
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
CONFIG -= qt
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
dbc_parser.cpp \
|
||||||
|
dbc_tree_builder.cpp \
|
||||||
|
main.cpp \
|
||||||
|
tree_node.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
dbc_database.h \
|
||||||
|
dbc_parser.h \
|
||||||
|
dbc_tree_builder.h \
|
||||||
|
frame_info.h \
|
||||||
|
signal_info.h \
|
||||||
|
tree_node.h
|
||||||
15
dbc_database.h
Normal file
15
dbc_database.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef DBC_DATABASE_H
|
||||||
|
#define DBC_DATABASE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "frame_info.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parsed DBC content stored in a simple internal form.
|
||||||
|
*/
|
||||||
|
struct DbcDatabase {
|
||||||
|
std::vector<FrameInfo> frames; /**< All frames found in DBC file. */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DBC_DATABASE_H */
|
||||||
315
dbc_parser.cpp
Normal file
315
dbc_parser.cpp
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
#include "dbc_parser.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/**
|
||||||
|
* @brief Remove leading and trailing spaces.
|
||||||
|
* @param text Input text.
|
||||||
|
* @return Trimmed text.
|
||||||
|
*/
|
||||||
|
std::string TrimText (const std::string &text) {
|
||||||
|
std::string::size_type begin = 0U;
|
||||||
|
while ((begin < text.size()) && std::isspace (static_cast<unsigned char> (text[begin])))
|
||||||
|
++begin;
|
||||||
|
|
||||||
|
std::string::size_type end = text.size();
|
||||||
|
while ((end > begin) && std::isspace (static_cast<unsigned char> (text[end - 1U])))
|
||||||
|
--end;
|
||||||
|
|
||||||
|
return text.substr (begin, end - begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DbcDatabase DbcParser::ParseFile (const std::string &filePath) const {
|
||||||
|
std::ifstream input (filePath.c_str());
|
||||||
|
if (!input.is_open())
|
||||||
|
throw std::runtime_error ("Failed to open DBC file: " + filePath);
|
||||||
|
|
||||||
|
DbcDatabase database;
|
||||||
|
FrameInfo *currentFrame = nullptr;
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline (input, line)) {
|
||||||
|
line = Trim (line);
|
||||||
|
if (line.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IsFrameLine (line)) {
|
||||||
|
FrameInfo frame = ParseFrameLine (line);
|
||||||
|
database.frames.push_back (frame);
|
||||||
|
currentFrame = &database.frames.back();
|
||||||
|
} else if (IsSignalLine (line)) {
|
||||||
|
if (currentFrame == nullptr)
|
||||||
|
throw std::runtime_error ("Signal found before any frame definition.");
|
||||||
|
|
||||||
|
SignalInfo signal = ParseSignalLine (line);
|
||||||
|
currentFrame->signals.push_back (signal);
|
||||||
|
} else if (IsCommentLine (line))
|
||||||
|
ParseCommentLine (line, database);
|
||||||
|
}
|
||||||
|
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DbcParser::IsFrameLine (const std::string &line) {
|
||||||
|
return (line.size() >= 4U) && (line.compare (0U, 4U, "BO_ ") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DbcParser::IsSignalLine (const std::string &line) {
|
||||||
|
return (line.size() >= 4U) && (line.compare (0U, 4U, "SG_ ") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DbcParser::IsCommentLine (const std::string &line) {
|
||||||
|
return (line.size() >= 4U) && (line.compare (0U, 4U, "CM_ ") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DbcParser::Trim (const std::string &text) {
|
||||||
|
return TrimText (text);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> DbcParser::SplitReceivers (const std::string &text) {
|
||||||
|
std::vector<std::string> receivers;
|
||||||
|
std::string token;
|
||||||
|
std::istringstream stream (text);
|
||||||
|
|
||||||
|
while (std::getline (stream, token, ',')) {
|
||||||
|
token = TrimText (token);
|
||||||
|
if (!token.empty())
|
||||||
|
receivers.push_back (token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return receivers;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t DbcParser::TryExtractPgn (std::uint32_t canId, bool &hasPgn) {
|
||||||
|
hasPgn = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Very simplified J1939 PGN extraction.
|
||||||
|
* Assumes 29-bit identifier.
|
||||||
|
*/
|
||||||
|
if ((canId & 0x1FFFFFFFU) != canId)
|
||||||
|
return 0U;
|
||||||
|
|
||||||
|
const std::uint32_t pf = (canId >> 16U) & 0xFFU;
|
||||||
|
const std::uint32_t ps = (canId >> 8U) & 0xFFU;
|
||||||
|
const std::uint32_t dp = (canId >> 24U) & 0x01U;
|
||||||
|
|
||||||
|
std::uint32_t pgn = 0U;
|
||||||
|
|
||||||
|
if (pf < 240U)
|
||||||
|
pgn = (dp << 16U) | (pf << 8U);
|
||||||
|
else
|
||||||
|
pgn = (dp << 16U) | (pf << 8U) | ps;
|
||||||
|
|
||||||
|
hasPgn = true;
|
||||||
|
return pgn;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameInfo DbcParser::ParseFrameLine (const std::string &line) {
|
||||||
|
/*
|
||||||
|
* Example:
|
||||||
|
* BO_ 256 EngineData: 8 Vector__XXX
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::istringstream stream (line);
|
||||||
|
std::string token;
|
||||||
|
FrameInfo frame;
|
||||||
|
|
||||||
|
stream >> token;
|
||||||
|
if (token != "BO_")
|
||||||
|
throw std::runtime_error ("Invalid frame line: " + line);
|
||||||
|
|
||||||
|
stream >> frame.canId;
|
||||||
|
stream >> token;
|
||||||
|
|
||||||
|
if (token.empty())
|
||||||
|
throw std::runtime_error ("Missing frame name: " + line);
|
||||||
|
|
||||||
|
if (token[token.size() - 1U] == ':')
|
||||||
|
token.erase (token.size() - 1U, 1U);
|
||||||
|
|
||||||
|
frame.name = token;
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned int dlcValue = 0U;
|
||||||
|
stream >> dlcValue;
|
||||||
|
frame.dlc = static_cast<std::uint8_t> (dlcValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream >> frame.transmitter;
|
||||||
|
frame.pgn = TryExtractPgn (frame.canId, frame.hasPgn);
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalInfo DbcParser::ParseSignalLine (const std::string &line) {
|
||||||
|
/*
|
||||||
|
* Example:
|
||||||
|
* SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8000] "rpm" Vector__XXX
|
||||||
|
*/
|
||||||
|
|
||||||
|
SignalInfo signal;
|
||||||
|
|
||||||
|
std::string work = TrimText (line);
|
||||||
|
if (work.compare (0U, 4U, "SG_ ") != 0)
|
||||||
|
throw std::runtime_error ("Invalid signal line: " + line);
|
||||||
|
|
||||||
|
work.erase (0U, 4U);
|
||||||
|
|
||||||
|
const std::string::size_type colonPos = work.find (':');
|
||||||
|
if (colonPos == std::string::npos)
|
||||||
|
throw std::runtime_error ("Signal line missing ':' : " + line);
|
||||||
|
|
||||||
|
signal.name = TrimText (work.substr (0U, colonPos));
|
||||||
|
std::string rest = TrimText (work.substr (colonPos + 1U));
|
||||||
|
|
||||||
|
const std::string::size_type pipePos = rest.find ('|');
|
||||||
|
const std::string::size_type atPos = rest.find ('@');
|
||||||
|
const std::string::size_type signPos = rest.find_first_of ("+-", atPos);
|
||||||
|
const std::string::size_type factorBegin = rest.find ('(');
|
||||||
|
const std::string::size_type factorComma = rest.find (',', factorBegin);
|
||||||
|
const std::string::size_type factorEnd = rest.find (')', factorComma);
|
||||||
|
const std::string::size_type rangeBegin = rest.find ('[');
|
||||||
|
const std::string::size_type rangeSep = rest.find ('|', rangeBegin);
|
||||||
|
const std::string::size_type rangeEnd = rest.find (']', rangeSep);
|
||||||
|
const std::string::size_type unitBegin = rest.find ('"', rangeEnd);
|
||||||
|
const std::string::size_type unitEnd = rest.find ('"', unitBegin + 1U);
|
||||||
|
|
||||||
|
if ((pipePos == std::string::npos) ||
|
||||||
|
(atPos == std::string::npos) ||
|
||||||
|
(signPos == std::string::npos) ||
|
||||||
|
(factorBegin == std::string::npos) ||
|
||||||
|
(factorComma == std::string::npos) ||
|
||||||
|
(factorEnd == std::string::npos) ||
|
||||||
|
(rangeBegin == std::string::npos) ||
|
||||||
|
(rangeSep == std::string::npos) ||
|
||||||
|
(rangeEnd == std::string::npos) ||
|
||||||
|
(unitBegin == std::string::npos) ||
|
||||||
|
(unitEnd == std::string::npos))
|
||||||
|
throw std::runtime_error ("Unsupported signal syntax: " + line);
|
||||||
|
|
||||||
|
signal.startBit = static_cast<std::uint32_t> (
|
||||||
|
std::stoul (TrimText (rest.substr (0U, pipePos)))
|
||||||
|
);
|
||||||
|
|
||||||
|
signal.length = static_cast<std::uint32_t> (
|
||||||
|
std::stoul (TrimText (rest.substr (pipePos + 1U, atPos - pipePos - 1U)))
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
if ((atPos + 1U) >= rest.size())
|
||||||
|
throw std::runtime_error ("Invalid endianness in signal: " + line);
|
||||||
|
|
||||||
|
const char endianChar = rest[atPos + 1U];
|
||||||
|
signal.isLittleEndian = (endianChar == '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const char signChar = rest[signPos];
|
||||||
|
signal.isSigned = (signChar == '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
signal.factor = std::stod (
|
||||||
|
TrimText (rest.substr (factorBegin + 1U, factorComma - factorBegin - 1U))
|
||||||
|
);
|
||||||
|
|
||||||
|
signal.offset = std::stod (
|
||||||
|
TrimText (rest.substr (factorComma + 1U, factorEnd - factorComma - 1U))
|
||||||
|
);
|
||||||
|
|
||||||
|
signal.minimum = std::stod (
|
||||||
|
TrimText (rest.substr (rangeBegin + 1U, rangeSep - rangeBegin - 1U))
|
||||||
|
);
|
||||||
|
|
||||||
|
signal.maximum = std::stod (
|
||||||
|
TrimText (rest.substr (rangeSep + 1U, rangeEnd - rangeSep - 1U))
|
||||||
|
);
|
||||||
|
|
||||||
|
signal.unit = rest.substr (unitBegin + 1U, unitEnd - unitBegin - 1U);
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string receiversText = TrimText (rest.substr (unitEnd + 1U));
|
||||||
|
signal.receivers = SplitReceivers (receiversText);
|
||||||
|
}
|
||||||
|
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DbcParser::ParseCommentLine (const std::string &line, DbcDatabase &database) {
|
||||||
|
/*
|
||||||
|
* Examples:
|
||||||
|
* CM_ BO_ 256 "Frame comment";
|
||||||
|
* CM_ SG_ 256 EngineSpeed "Signal comment";
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::istringstream stream (line);
|
||||||
|
std::string token;
|
||||||
|
stream >> token;
|
||||||
|
|
||||||
|
if (token != "CM_")
|
||||||
|
return;
|
||||||
|
|
||||||
|
stream >> token;
|
||||||
|
|
||||||
|
if (token == "BO_") {
|
||||||
|
std::uint32_t canId = 0U;
|
||||||
|
stream >> canId;
|
||||||
|
|
||||||
|
const std::string::size_type quoteBegin = line.find ('"');
|
||||||
|
const std::string::size_type quoteEnd = line.rfind ('"');
|
||||||
|
|
||||||
|
if ((quoteBegin == std::string::npos) ||
|
||||||
|
(quoteEnd == std::string::npos) ||
|
||||||
|
(quoteEnd <= quoteBegin))
|
||||||
|
return;
|
||||||
|
|
||||||
|
FrameInfo *frame = FindFrameById (database, canId);
|
||||||
|
if (frame != nullptr)
|
||||||
|
frame->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
|
||||||
|
} else if (token == "SG_") {
|
||||||
|
std::uint32_t canId = 0U;
|
||||||
|
std::string signalName;
|
||||||
|
|
||||||
|
stream >> canId;
|
||||||
|
stream >> signalName;
|
||||||
|
|
||||||
|
const std::string::size_type quoteBegin = line.find ('"');
|
||||||
|
const std::string::size_type quoteEnd = line.rfind ('"');
|
||||||
|
|
||||||
|
if ((quoteBegin == std::string::npos) ||
|
||||||
|
(quoteEnd == std::string::npos) ||
|
||||||
|
(quoteEnd <= quoteBegin))
|
||||||
|
return;
|
||||||
|
|
||||||
|
FrameInfo *frame = FindFrameById (database, canId);
|
||||||
|
if (frame != nullptr) {
|
||||||
|
SignalInfo *signal = FindSignalByName (*frame, signalName);
|
||||||
|
if (signal != nullptr)
|
||||||
|
signal->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameInfo *DbcParser::FindFrameById (DbcDatabase &database, std::uint32_t canId) {
|
||||||
|
for (std::size_t index = 0U; index < database.frames.size(); ++index) {
|
||||||
|
if (database.frames[index].canId == canId)
|
||||||
|
return &database.frames[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalInfo *DbcParser::FindSignalByName (FrameInfo &frame, const std::string &signalName) {
|
||||||
|
for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
|
||||||
|
if (frame.signals[index].name == signalName)
|
||||||
|
return &frame.signals[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
51
dbc_parser.h
Normal file
51
dbc_parser.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef DBC_PARSER_H
|
||||||
|
#define DBC_PARSER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "dbc_database.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Minimal DBC parser.
|
||||||
|
*
|
||||||
|
* Supports:
|
||||||
|
* - BO_
|
||||||
|
* - SG_
|
||||||
|
* - CM_ BO_
|
||||||
|
* - CM_ SG_
|
||||||
|
*
|
||||||
|
* Ignores:
|
||||||
|
* - attributes
|
||||||
|
* - multiplexing
|
||||||
|
* - value tables
|
||||||
|
*/
|
||||||
|
class DbcParser {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Parse DBC file.
|
||||||
|
* @param filePath Path to input file.
|
||||||
|
* @return Parsed database.
|
||||||
|
* @throws std::runtime_error on file or parse errors.
|
||||||
|
*/
|
||||||
|
DbcDatabase ParseFile (const std::string &filePath) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool IsFrameLine (const std::string &line);
|
||||||
|
static bool IsSignalLine (const std::string &line);
|
||||||
|
static bool IsCommentLine (const std::string &line);
|
||||||
|
static std::string Trim (const std::string &text);
|
||||||
|
static std::vector<std::string> SplitReceivers (const std::string &text);
|
||||||
|
static std::uint32_t TryExtractPgn (std::uint32_t canId, bool &hasPgn);
|
||||||
|
|
||||||
|
static FrameInfo ParseFrameLine (const std::string &line);
|
||||||
|
static SignalInfo ParseSignalLine (const std::string &line);
|
||||||
|
|
||||||
|
static void ParseCommentLine (const std::string &line, DbcDatabase &database);
|
||||||
|
|
||||||
|
static FrameInfo *FindFrameById (DbcDatabase &database, std::uint32_t canId);
|
||||||
|
static SignalInfo *FindSignalByName (FrameInfo &frame, const std::string &signalName);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DBC_PARSER_H */
|
||||||
20
dbc_tree_builder.cpp
Normal file
20
dbc_tree_builder.cpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#include "dbc_tree_builder.h"
|
||||||
|
|
||||||
|
std::unique_ptr<TreeNode> DbcTreeBuilder::Build (const DbcDatabase &database) const {
|
||||||
|
std::unique_ptr<TreeNode> root (new TreeNode());
|
||||||
|
|
||||||
|
for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
|
||||||
|
const FrameInfo &frame = database.frames[frameIndex];
|
||||||
|
std::unique_ptr<TreeNode> frameNode (new TreeNode (frame));
|
||||||
|
|
||||||
|
for (std::size_t signalIndex = 0U; signalIndex < frame.signals.size(); ++signalIndex) {
|
||||||
|
const SignalInfo &signal = frame.signals[signalIndex];
|
||||||
|
std::unique_ptr<TreeNode> signalNode (new TreeNode (signal));
|
||||||
|
frameNode->AddChild (std::move (signalNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
root->AddChild (std::move (frameNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
22
dbc_tree_builder.h
Normal file
22
dbc_tree_builder.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef DBC_TREE_BUILDER_H
|
||||||
|
#define DBC_TREE_BUILDER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "dbc_database.h"
|
||||||
|
#include "tree_node.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Builds a simple tree from parsed DBC database.
|
||||||
|
*/
|
||||||
|
class DbcTreeBuilder {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Build tree representation of parsed DBC data.
|
||||||
|
* @param database Parsed database.
|
||||||
|
* @return Root node of the tree.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<TreeNode> Build (const DbcDatabase &database) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DBC_TREE_BUILDER_H */
|
||||||
35
frame_info.h
Normal file
35
frame_info.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef FRAME_INFO_H
|
||||||
|
#define FRAME_INFO_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "signal_info.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes one CAN frame from a DBC file.
|
||||||
|
*/
|
||||||
|
struct FrameInfo {
|
||||||
|
std::string name; /**< Frame name. */
|
||||||
|
std::uint32_t canId; /**< Raw CAN identifier from DBC. */
|
||||||
|
std::uint32_t pgn; /**< J1939 PGN if applicable. */
|
||||||
|
bool hasPgn; /**< true if PGN was derived. */
|
||||||
|
std::uint8_t dlc; /**< Frame payload length. */
|
||||||
|
std::string transmitter; /**< Transmitter ECU name. */
|
||||||
|
std::string comment; /**< Optional frame comment. */
|
||||||
|
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
|
||||||
|
|
||||||
|
FrameInfo()
|
||||||
|
: name()
|
||||||
|
, canId (0U)
|
||||||
|
, pgn (0U)
|
||||||
|
, hasPgn (false)
|
||||||
|
, dlc (0U)
|
||||||
|
, transmitter()
|
||||||
|
, comment()
|
||||||
|
, signals() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FRAME_INFO_H */
|
||||||
99
main.cpp
Normal file
99
main.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "dbc_parser.h"
|
||||||
|
#include "dbc_tree_builder.h"
|
||||||
|
#include "tree_node.h"
|
||||||
|
|
||||||
|
static void PrintReceivers (const std::vector<std::string> &receivers) {
|
||||||
|
for (std::size_t index = 0U; index < receivers.size(); ++index) {
|
||||||
|
if (index != 0U)
|
||||||
|
std::cout << ",";
|
||||||
|
|
||||||
|
std::cout << receivers[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintTree (const TreeNode *node, int indent) {
|
||||||
|
if (node == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < indent; ++i)
|
||||||
|
std::cout << " ";
|
||||||
|
|
||||||
|
switch (node->GetType()) {
|
||||||
|
case NodeType::Root:
|
||||||
|
std::cout << "[root] " << node->GetName() << "\n";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeType::Frame: {
|
||||||
|
const FrameInfo *frame = node->GetFrame();
|
||||||
|
std::cout << "[frame] " << node->GetName();
|
||||||
|
|
||||||
|
if (frame != nullptr) {
|
||||||
|
std::cout << " id=" << frame->canId
|
||||||
|
<< " dlc=" << static_cast<unsigned int> (frame->dlc)
|
||||||
|
<< " tx=" << frame->transmitter;
|
||||||
|
|
||||||
|
if (frame->hasPgn)
|
||||||
|
std::cout << " pgn=" << frame->pgn;
|
||||||
|
|
||||||
|
if (!frame->comment.empty())
|
||||||
|
std::cout << " comment=\"" << frame->comment << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NodeType::Signal: {
|
||||||
|
const SignalInfo *signal = node->GetSignal();
|
||||||
|
std::cout << "[signal] " << node->GetName();
|
||||||
|
|
||||||
|
if (signal != nullptr) {
|
||||||
|
std::cout << " start=" << signal->startBit
|
||||||
|
<< " len=" << signal->length
|
||||||
|
<< " unit=" << signal->unit
|
||||||
|
<< " rx=";
|
||||||
|
|
||||||
|
PrintReceivers (signal->receivers);
|
||||||
|
|
||||||
|
if (!signal->comment.empty())
|
||||||
|
std::cout << " comment=\"" << signal->comment << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
std::cout << "[unknown]\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0U; i < node->GetChildCount(); ++i)
|
||||||
|
PrintTree (node->GetChild (i), indent + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char *argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cerr << "Usage: dbc_demo <file.dbc>\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DbcParser parser;
|
||||||
|
DbcDatabase database = parser.ParseFile (argv[1]);
|
||||||
|
|
||||||
|
DbcTreeBuilder builder;
|
||||||
|
std::unique_ptr<TreeNode> root = builder.Build (database);
|
||||||
|
|
||||||
|
PrintTree (root.get(), 0);
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
std::cerr << "Error: " << ex.what() << "\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
41
signal_info.h
Normal file
41
signal_info.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef SIGNAL_INFO_H
|
||||||
|
#define SIGNAL_INFO_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes one signal inside a DBC frame.
|
||||||
|
*/
|
||||||
|
struct SignalInfo {
|
||||||
|
std::string name; /**< Signal name. */
|
||||||
|
std::uint32_t startBit; /**< Start bit in DBC notation. */
|
||||||
|
std::uint32_t length; /**< Signal length in bits. */
|
||||||
|
bool isLittleEndian; /**< true for Intel, false for Motorola. */
|
||||||
|
bool isSigned; /**< true if signal is signed. */
|
||||||
|
double factor; /**< Scaling factor. */
|
||||||
|
double offset; /**< Physical offset. */
|
||||||
|
double minimum; /**< Minimum physical value. */
|
||||||
|
double maximum; /**< Maximum physical value. */
|
||||||
|
std::string unit; /**< Physical unit. */
|
||||||
|
std::vector<std::string> receivers; /**< Receivers of this signal. */
|
||||||
|
std::string comment; /**< Optional signal comment. */
|
||||||
|
|
||||||
|
SignalInfo()
|
||||||
|
: name()
|
||||||
|
, startBit (0U)
|
||||||
|
, length (0U)
|
||||||
|
, isLittleEndian (true)
|
||||||
|
, isSigned (false)
|
||||||
|
, factor (1.0)
|
||||||
|
, offset (0.0)
|
||||||
|
, minimum (0.0)
|
||||||
|
, maximum (0.0)
|
||||||
|
, unit()
|
||||||
|
, receivers()
|
||||||
|
, comment() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SIGNAL_INFO_H */
|
||||||
64
tree_node.cpp
Normal file
64
tree_node.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include "tree_node.h"
|
||||||
|
|
||||||
|
TreeNode::TreeNode()
|
||||||
|
: m_type (NodeType::Root)
|
||||||
|
, m_name ("dbc")
|
||||||
|
, m_children()
|
||||||
|
, m_frame()
|
||||||
|
, m_signal() {
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode::TreeNode (const FrameInfo &frame)
|
||||||
|
: m_type (NodeType::Frame)
|
||||||
|
, m_name (frame.name)
|
||||||
|
, m_children()
|
||||||
|
, m_frame (new FrameInfo (frame))
|
||||||
|
, m_signal() {
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode::TreeNode (const SignalInfo &signal)
|
||||||
|
: m_type (NodeType::Signal)
|
||||||
|
, m_name (signal.name)
|
||||||
|
, m_children()
|
||||||
|
, m_frame()
|
||||||
|
, m_signal (new SignalInfo (signal)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void TreeNode::AddChild (std::unique_ptr<TreeNode> child) {
|
||||||
|
if (child)
|
||||||
|
m_children.push_back (std::move (child));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t TreeNode::GetChildCount() const {
|
||||||
|
return m_children.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const TreeNode *TreeNode::GetChild (std::size_t index) const {
|
||||||
|
if (index >= m_children.size())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return m_children[index].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode *TreeNode::GetChild (std::size_t index) {
|
||||||
|
if (index >= m_children.size())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return m_children[index].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeType TreeNode::GetType() const {
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &TreeNode::GetName() const {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FrameInfo *TreeNode::GetFrame() const {
|
||||||
|
return m_frame.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SignalInfo *TreeNode::GetSignal() const {
|
||||||
|
return m_signal.get();
|
||||||
|
}
|
||||||
109
tree_node.h
Normal file
109
tree_node.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#ifndef TREE_NODE_H
|
||||||
|
#define TREE_NODE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "frame_info.h"
|
||||||
|
#include "signal_info.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Type of a tree node.
|
||||||
|
*/
|
||||||
|
enum class NodeType {
|
||||||
|
Root,
|
||||||
|
Frame,
|
||||||
|
Signal
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tree node for later use in model/view or other hierarchy consumers.
|
||||||
|
*/
|
||||||
|
class TreeNode {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Create root node.
|
||||||
|
*/
|
||||||
|
TreeNode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create frame node.
|
||||||
|
* @param frame Frame payload.
|
||||||
|
*/
|
||||||
|
explicit TreeNode (const FrameInfo &frame);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create signal node.
|
||||||
|
* @param signal Signal payload.
|
||||||
|
*/
|
||||||
|
explicit TreeNode (const SignalInfo &signal);
|
||||||
|
|
||||||
|
~TreeNode() = default;
|
||||||
|
|
||||||
|
TreeNode (const TreeNode &) = delete;
|
||||||
|
TreeNode &operator= (const TreeNode &) = delete;
|
||||||
|
|
||||||
|
TreeNode (TreeNode &&) = default;
|
||||||
|
TreeNode &operator= (TreeNode &&) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add child node.
|
||||||
|
* @param child Child node to add.
|
||||||
|
*/
|
||||||
|
void AddChild (std::unique_ptr<TreeNode> child);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get child count.
|
||||||
|
* @return Number of children.
|
||||||
|
*/
|
||||||
|
std::size_t GetChildCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get child by index.
|
||||||
|
* @param index Child index.
|
||||||
|
* @return Child pointer or nullptr if index is invalid.
|
||||||
|
*/
|
||||||
|
const TreeNode *GetChild (std::size_t index) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get mutable child by index.
|
||||||
|
* @param index Child index.
|
||||||
|
* @return Child pointer or nullptr if index is invalid.
|
||||||
|
*/
|
||||||
|
TreeNode *GetChild (std::size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get node type.
|
||||||
|
* @return Node type.
|
||||||
|
*/
|
||||||
|
NodeType GetType() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get display name.
|
||||||
|
* @return Node name.
|
||||||
|
*/
|
||||||
|
const std::string &GetName() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get frame payload if node is frame.
|
||||||
|
* @return Pointer to frame info or nullptr.
|
||||||
|
*/
|
||||||
|
const FrameInfo *GetFrame() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get signal payload if node is signal.
|
||||||
|
* @return Pointer to signal info or nullptr.
|
||||||
|
*/
|
||||||
|
const SignalInfo *GetSignal() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NodeType m_type;
|
||||||
|
std::string m_name;
|
||||||
|
std::vector<std::unique_ptr<TreeNode> > m_children;
|
||||||
|
std::unique_ptr<FrameInfo> m_frame;
|
||||||
|
std::unique_ptr<SignalInfo> m_signal;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* TREE_NODE_H */
|
||||||
Reference in New Issue
Block a user