Added decoding support to runtime decode.
This commit is contained in:
5
dbc.pro
5
dbc.pro
@@ -4,6 +4,8 @@ CONFIG -= app_bundle
|
|||||||
CONFIG -= qt
|
CONFIG -= qt
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
dbc_decode_builder.cpp \
|
||||||
|
dbc_decoder.cpp \
|
||||||
dbc_parser.cpp \
|
dbc_parser.cpp \
|
||||||
dbc_tree_builder.cpp \
|
dbc_tree_builder.cpp \
|
||||||
main.cpp \
|
main.cpp \
|
||||||
@@ -11,8 +13,11 @@ SOURCES += \
|
|||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
dbc_database.h \
|
dbc_database.h \
|
||||||
|
dbc_decode_builder.h \
|
||||||
|
dbc_decoder.h \
|
||||||
dbc_parser.h \
|
dbc_parser.h \
|
||||||
dbc_tree_builder.h \
|
dbc_tree_builder.h \
|
||||||
|
decode_database.h \
|
||||||
frame_info.h \
|
frame_info.h \
|
||||||
signal_info.h \
|
signal_info.h \
|
||||||
tree_node.h
|
tree_node.h
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
* @brief Parsed DBC content stored in a simple internal form.
|
* @brief Parsed DBC content stored in a simple internal form.
|
||||||
*/
|
*/
|
||||||
struct DbcDatabase {
|
struct DbcDatabase {
|
||||||
std::vector<FrameInfo> frames; /**< All frames found in DBC file. */
|
std::vector<FrameInfo> frames; /**< All frames found in the DBC file. */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DBC_DATABASE_H */
|
#endif /* DBC_DATABASE_H */
|
||||||
|
|||||||
45
dbc_decode_builder.cpp
Normal file
45
dbc_decode_builder.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include "dbc_decode_builder.h"
|
||||||
|
|
||||||
|
DecodeDatabase DbcDecodeBuilder::Build (const DbcDatabase &source) const {
|
||||||
|
DecodeDatabase result;
|
||||||
|
|
||||||
|
for (std::size_t frameIndex = 0U; frameIndex < source.frames.size(); ++frameIndex) {
|
||||||
|
const FrameInfo &sourceFrame = source.frames[frameIndex];
|
||||||
|
DecodeFrame targetFrame;
|
||||||
|
|
||||||
|
targetFrame.name = sourceFrame.name;
|
||||||
|
targetFrame.canId = sourceFrame.canId;
|
||||||
|
targetFrame.isExtended = sourceFrame.isExtended;
|
||||||
|
targetFrame.dlc = sourceFrame.dlc;
|
||||||
|
targetFrame.pgn = sourceFrame.pgn;
|
||||||
|
targetFrame.hasPgn = sourceFrame.hasPgn;
|
||||||
|
targetFrame.transmitter = sourceFrame.transmitter;
|
||||||
|
targetFrame.comment = sourceFrame.comment;
|
||||||
|
|
||||||
|
for (std::size_t signalIndex = 0U; signalIndex < sourceFrame.signals.size(); ++signalIndex) {
|
||||||
|
const SignalInfo &sourceSignal = sourceFrame.signals[signalIndex];
|
||||||
|
DecodeSignal targetSignal;
|
||||||
|
|
||||||
|
targetSignal.name = sourceSignal.name;
|
||||||
|
targetSignal.startBit = sourceSignal.startBit;
|
||||||
|
targetSignal.length = sourceSignal.length;
|
||||||
|
targetSignal.byteOrder = sourceSignal.isLittleEndian ? ByteOrder::Intel : ByteOrder::Motorola;
|
||||||
|
targetSignal.valueType = sourceSignal.isSigned ? ValueType::Signed : ValueType::Unsigned;
|
||||||
|
targetSignal.factor = sourceSignal.factor;
|
||||||
|
targetSignal.offset = sourceSignal.offset;
|
||||||
|
targetSignal.minimum = sourceSignal.minimum;
|
||||||
|
targetSignal.maximum = sourceSignal.maximum;
|
||||||
|
targetSignal.unit = sourceSignal.unit;
|
||||||
|
targetSignal.receivers = sourceSignal.receivers;
|
||||||
|
targetSignal.comment = sourceSignal.comment;
|
||||||
|
|
||||||
|
targetFrame.signals.push_back (targetSignal);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.frames.push_back (targetFrame);
|
||||||
|
result.frameIndexByKey[FrameKey (targetFrame.canId, targetFrame.isExtended)] =
|
||||||
|
result.frames.size() - 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
20
dbc_decode_builder.h
Normal file
20
dbc_decode_builder.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef DBC_DECODE_BUILDER_H
|
||||||
|
#define DBC_DECODE_BUILDER_H
|
||||||
|
|
||||||
|
#include "dbc_database.h"
|
||||||
|
#include "decode_database.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts parsed DBC data into runtime decode database.
|
||||||
|
*/
|
||||||
|
class DbcDecodeBuilder {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Build runtime decode database.
|
||||||
|
* @param source Parsed DBC database.
|
||||||
|
* @return Runtime-ready decode database.
|
||||||
|
*/
|
||||||
|
DecodeDatabase Build (const DbcDatabase &source) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DBC_DECODE_BUILDER_H */
|
||||||
146
dbc_decoder.cpp
Normal file
146
dbc_decoder.cpp
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#include "dbc_decoder.h"
|
||||||
|
|
||||||
|
const DecodeFrame *DbcDecoder::FindFrame (const DecodeDatabase &database,
|
||||||
|
std::uint32_t canId,
|
||||||
|
bool isExtended) const {
|
||||||
|
const FrameKey key (canId, isExtended);
|
||||||
|
const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it =
|
||||||
|
database.frameIndexByKey.find (key);
|
||||||
|
|
||||||
|
if (it == database.frameIndexByKey.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const std::size_t index = it->second;
|
||||||
|
if (index >= database.frames.size())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return &database.frames[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
DecodedFrameValue DbcDecoder::Decode (const DecodeDatabase &database,
|
||||||
|
const RawCanFrame &frame) const {
|
||||||
|
DecodedFrameValue result;
|
||||||
|
const DecodeFrame *definition = FindFrame (database, frame.canId, frame.isExtended);
|
||||||
|
|
||||||
|
if (definition == nullptr)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result.definition = definition;
|
||||||
|
result.valid = true;
|
||||||
|
|
||||||
|
for (std::size_t signalIndex = 0U; signalIndex < definition->signals.size(); ++signalIndex) {
|
||||||
|
const DecodeSignal &signal = definition->signals[signalIndex];
|
||||||
|
DecodedSignalValue decoded;
|
||||||
|
std::uint64_t unsignedValue = 0U;
|
||||||
|
|
||||||
|
decoded.definition = &signal;
|
||||||
|
|
||||||
|
if (!ExtractUnsigned (frame.data, signal, unsignedValue)) {
|
||||||
|
decoded.valid = false;
|
||||||
|
result.signals.push_back (decoded);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.valueType == ValueType::Signed)
|
||||||
|
decoded.rawValue = SignExtend (unsignedValue, signal.length);
|
||||||
|
else
|
||||||
|
decoded.rawValue = static_cast<std::int64_t> (unsignedValue);
|
||||||
|
|
||||||
|
decoded.physicalValue =
|
||||||
|
(static_cast<double> (decoded.rawValue) * signal.factor) + signal.offset;
|
||||||
|
decoded.valid = true;
|
||||||
|
|
||||||
|
result.signals.push_back (decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DbcDecoder::ExtractUnsigned (const std::vector<std::uint8_t> &data,
|
||||||
|
const DecodeSignal &signal,
|
||||||
|
std::uint64_t &value) {
|
||||||
|
if ((signal.length == 0U) || (signal.length > 64U))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (signal.byteOrder == ByteOrder::Intel)
|
||||||
|
return ExtractIntel (data, signal.startBit, signal.length, value);
|
||||||
|
|
||||||
|
return ExtractMotorola (data, signal.startBit, signal.length, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DbcDecoder::ExtractIntel (const std::vector<std::uint8_t> &data,
|
||||||
|
std::uint32_t startBit,
|
||||||
|
std::uint32_t length,
|
||||||
|
std::uint64_t &value) {
|
||||||
|
value = 0U;
|
||||||
|
|
||||||
|
for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
|
||||||
|
const std::uint32_t absoluteBit = startBit + bitIndex;
|
||||||
|
const std::uint32_t byteIndex = absoluteBit / 8U;
|
||||||
|
const std::uint32_t bitInByte = absoluteBit % 8U;
|
||||||
|
|
||||||
|
if (byteIndex >= data.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::uint64_t bitValue =
|
||||||
|
(static_cast<std::uint64_t> ((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
|
||||||
|
|
||||||
|
value |= bitValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DbcDecoder::ExtractMotorola (const std::vector<std::uint8_t> &data,
|
||||||
|
std::uint32_t startBit,
|
||||||
|
std::uint32_t length,
|
||||||
|
std::uint64_t &value) {
|
||||||
|
/*
|
||||||
|
* DBC Motorola bit numbering:
|
||||||
|
* - startBit points to the most significant bit of the signal
|
||||||
|
* - inside a byte, bit numbering goes 7..0
|
||||||
|
* - crossing byte boundary moves to the next byte, bit 7
|
||||||
|
*/
|
||||||
|
|
||||||
|
value = 0U;
|
||||||
|
|
||||||
|
std::int32_t currentBit = static_cast<std::int32_t> (startBit);
|
||||||
|
|
||||||
|
for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
|
||||||
|
if (currentBit < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::uint32_t absoluteBit = static_cast<std::uint32_t> (currentBit);
|
||||||
|
const std::uint32_t byteIndex = absoluteBit / 8U;
|
||||||
|
const std::uint32_t bitFromMsb = absoluteBit % 8U;
|
||||||
|
const std::uint32_t bitInByte = 7U - bitFromMsb;
|
||||||
|
|
||||||
|
if (byteIndex >= data.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
value <<= 1U;
|
||||||
|
value |= static_cast<std::uint64_t> ((data[byteIndex] >> bitInByte) & 0x01U);
|
||||||
|
|
||||||
|
if ((absoluteBit % 8U) == 7U)
|
||||||
|
currentBit = static_cast<std::int32_t> ((byteIndex + 1U) * 8U);
|
||||||
|
else
|
||||||
|
--currentBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::int64_t DbcDecoder::SignExtend (std::uint64_t value, std::uint32_t bitLength) {
|
||||||
|
if ((bitLength == 0U) || (bitLength >= 64U))
|
||||||
|
return static_cast<std::int64_t> (value);
|
||||||
|
|
||||||
|
const std::uint64_t signMask = (static_cast<std::uint64_t> (1U) << (bitLength - 1U));
|
||||||
|
const std::uint64_t valueMask = (static_cast<std::uint64_t> (1U) << bitLength) - 1U;
|
||||||
|
|
||||||
|
value &= valueMask;
|
||||||
|
|
||||||
|
if ((value & signMask) == 0U)
|
||||||
|
return static_cast<std::int64_t> (value);
|
||||||
|
|
||||||
|
return static_cast<std::int64_t> (value | (~valueMask));
|
||||||
|
}
|
||||||
99
dbc_decoder.h
Normal file
99
dbc_decoder.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#ifndef DBC_DECODER_H
|
||||||
|
#define DBC_DECODER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "decode_database.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Raw CAN frame used for runtime or trace decoding.
|
||||||
|
*/
|
||||||
|
struct RawCanFrame {
|
||||||
|
std::uint32_t canId; /**< Normalized CAN ID. */
|
||||||
|
bool isExtended; /**< true for extended frame. */
|
||||||
|
std::vector<std::uint8_t> data; /**< Payload bytes. */
|
||||||
|
|
||||||
|
RawCanFrame()
|
||||||
|
: canId (0U)
|
||||||
|
, isExtended (false)
|
||||||
|
, data() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief One decoded signal value.
|
||||||
|
*/
|
||||||
|
struct DecodedSignalValue {
|
||||||
|
const DecodeSignal *definition; /**< Signal definition. */
|
||||||
|
std::int64_t rawValue; /**< Extracted raw integer value. */
|
||||||
|
double physicalValue; /**< Converted physical value. */
|
||||||
|
bool valid; /**< true if decoding succeeded. */
|
||||||
|
|
||||||
|
DecodedSignalValue()
|
||||||
|
: definition (nullptr)
|
||||||
|
, rawValue (0)
|
||||||
|
, physicalValue (0.0)
|
||||||
|
, valid (false) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fully decoded frame.
|
||||||
|
*/
|
||||||
|
struct DecodedFrameValue {
|
||||||
|
const DecodeFrame *definition; /**< Frame definition. */
|
||||||
|
std::vector<DecodedSignalValue> signals; /**< Decoded signal values. */
|
||||||
|
bool valid; /**< true if frame was matched. */
|
||||||
|
|
||||||
|
DecodedFrameValue()
|
||||||
|
: definition (nullptr)
|
||||||
|
, signals()
|
||||||
|
, valid (false) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runtime CAN decoder using prebuilt decode database.
|
||||||
|
*/
|
||||||
|
class DbcDecoder {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Find frame definition by CAN ID.
|
||||||
|
* @param database Runtime decode database.
|
||||||
|
* @param canId Normalized CAN ID.
|
||||||
|
* @param isExtended true for extended frame.
|
||||||
|
* @return Pointer to frame definition or nullptr.
|
||||||
|
*/
|
||||||
|
const DecodeFrame *FindFrame (const DecodeDatabase &database,
|
||||||
|
std::uint32_t canId,
|
||||||
|
bool isExtended) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode one raw CAN frame.
|
||||||
|
* @param database Runtime decode database.
|
||||||
|
* @param frame Raw CAN frame.
|
||||||
|
* @return Decoded frame value.
|
||||||
|
*/
|
||||||
|
DecodedFrameValue Decode (const DecodeDatabase &database,
|
||||||
|
const RawCanFrame &frame) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool ExtractUnsigned (const std::vector<std::uint8_t> &data,
|
||||||
|
const DecodeSignal &signal,
|
||||||
|
std::uint64_t &value);
|
||||||
|
|
||||||
|
static bool ExtractIntel (const std::vector<std::uint8_t> &data,
|
||||||
|
std::uint32_t startBit,
|
||||||
|
std::uint32_t length,
|
||||||
|
std::uint64_t &value);
|
||||||
|
|
||||||
|
static bool ExtractMotorola (const std::vector<std::uint8_t> &data,
|
||||||
|
std::uint32_t startBit,
|
||||||
|
std::uint32_t length,
|
||||||
|
std::uint64_t &value);
|
||||||
|
|
||||||
|
static std::int64_t SignExtend (std::uint64_t value, std::uint32_t bitLength);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DBC_DECODER_H */
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -14,11 +13,13 @@ namespace {
|
|||||||
*/
|
*/
|
||||||
std::string TrimText (const std::string &text) {
|
std::string TrimText (const std::string &text) {
|
||||||
std::string::size_type begin = 0U;
|
std::string::size_type begin = 0U;
|
||||||
while ((begin < text.size()) && std::isspace (static_cast<unsigned char> (text[begin])))
|
while ((begin < text.size()) &&
|
||||||
|
std::isspace (static_cast<unsigned char> (text[begin])))
|
||||||
++begin;
|
++begin;
|
||||||
|
|
||||||
std::string::size_type end = text.size();
|
std::string::size_type end = text.size();
|
||||||
while ((end > begin) && std::isspace (static_cast<unsigned char> (text[end - 1U])))
|
while ((end > begin) &&
|
||||||
|
std::isspace (static_cast<unsigned char> (text[end - 1U])))
|
||||||
--end;
|
--end;
|
||||||
|
|
||||||
return text.substr (begin, end - begin);
|
return text.substr (begin, end - begin);
|
||||||
@@ -86,13 +87,29 @@ std::vector<std::string> DbcParser::SplitReceivers (const std::string &text) {
|
|||||||
return receivers;
|
return receivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t DbcParser::TryExtractPgn (std::uint32_t canId, bool &hasPgn) {
|
void DbcParser::NormalizeCanId (std::uint32_t rawCanId,
|
||||||
|
std::uint32_t &normalizedCanId,
|
||||||
|
bool &isExtended) {
|
||||||
|
/*
|
||||||
|
* DBC commonly stores extended identifiers with bit 31 set.
|
||||||
|
* Example:
|
||||||
|
* raw id = 0x80000000 | actual_29_bit_id
|
||||||
|
*/
|
||||||
|
if ((rawCanId & 0x80000000U) != 0U) {
|
||||||
|
isExtended = true;
|
||||||
|
normalizedCanId = (rawCanId & 0x1FFFFFFFU);
|
||||||
|
} else {
|
||||||
|
isExtended = (rawCanId > 0x7FFU);
|
||||||
|
normalizedCanId = rawCanId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t DbcParser::TryExtractPgn (std::uint32_t canId, bool isExtended, bool &hasPgn) {
|
||||||
hasPgn = false;
|
hasPgn = false;
|
||||||
|
|
||||||
/*
|
if (!isExtended)
|
||||||
* Very simplified J1939 PGN extraction.
|
return 0U;
|
||||||
* Assumes 29-bit identifier.
|
|
||||||
*/
|
|
||||||
if ((canId & 0x1FFFFFFFU) != canId)
|
if ((canId & 0x1FFFFFFFU) != canId)
|
||||||
return 0U;
|
return 0U;
|
||||||
|
|
||||||
@@ -114,20 +131,22 @@ std::uint32_t DbcParser::TryExtractPgn (std::uint32_t canId, bool &hasPgn) {
|
|||||||
FrameInfo DbcParser::ParseFrameLine (const std::string &line) {
|
FrameInfo DbcParser::ParseFrameLine (const std::string &line) {
|
||||||
/*
|
/*
|
||||||
* Example:
|
* Example:
|
||||||
* BO_ 256 EngineData: 8 Vector__XXX
|
* BO_ 256 EngineData: 8 EEC1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::istringstream stream (line);
|
std::istringstream stream (line);
|
||||||
std::string token;
|
std::string token;
|
||||||
FrameInfo frame;
|
FrameInfo frame;
|
||||||
|
std::uint32_t rawCanId = 0U;
|
||||||
|
|
||||||
stream >> token;
|
stream >> token;
|
||||||
if (token != "BO_")
|
if (token != "BO_")
|
||||||
throw std::runtime_error ("Invalid frame line: " + line);
|
throw std::runtime_error ("Invalid frame line: " + line);
|
||||||
|
|
||||||
stream >> frame.canId;
|
stream >> rawCanId;
|
||||||
stream >> token;
|
NormalizeCanId (rawCanId, frame.canId, frame.isExtended);
|
||||||
|
|
||||||
|
stream >> token;
|
||||||
if (token.empty())
|
if (token.empty())
|
||||||
throw std::runtime_error ("Missing frame name: " + line);
|
throw std::runtime_error ("Missing frame name: " + line);
|
||||||
|
|
||||||
@@ -143,7 +162,7 @@ FrameInfo DbcParser::ParseFrameLine (const std::string &line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stream >> frame.transmitter;
|
stream >> frame.transmitter;
|
||||||
frame.pgn = TryExtractPgn (frame.canId, frame.hasPgn);
|
frame.pgn = TryExtractPgn (frame.canId, frame.isExtended, frame.hasPgn);
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
@@ -151,7 +170,7 @@ FrameInfo DbcParser::ParseFrameLine (const std::string &line) {
|
|||||||
SignalInfo DbcParser::ParseSignalLine (const std::string &line) {
|
SignalInfo DbcParser::ParseSignalLine (const std::string &line) {
|
||||||
/*
|
/*
|
||||||
* Example:
|
* Example:
|
||||||
* SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8000] "rpm" Vector__XXX
|
* SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8000] "rpm" ECU1,ECU2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SignalInfo signal;
|
SignalInfo signal;
|
||||||
@@ -258,8 +277,12 @@ void DbcParser::ParseCommentLine (const std::string &line, DbcDatabase &database
|
|||||||
stream >> token;
|
stream >> token;
|
||||||
|
|
||||||
if (token == "BO_") {
|
if (token == "BO_") {
|
||||||
|
std::uint32_t rawCanId = 0U;
|
||||||
std::uint32_t canId = 0U;
|
std::uint32_t canId = 0U;
|
||||||
stream >> canId;
|
bool isExtended = false;
|
||||||
|
|
||||||
|
stream >> rawCanId;
|
||||||
|
NormalizeCanId (rawCanId, canId, isExtended);
|
||||||
|
|
||||||
const std::string::size_type quoteBegin = line.find ('"');
|
const std::string::size_type quoteBegin = line.find ('"');
|
||||||
const std::string::size_type quoteEnd = line.rfind ('"');
|
const std::string::size_type quoteEnd = line.rfind ('"');
|
||||||
@@ -269,16 +292,20 @@ void DbcParser::ParseCommentLine (const std::string &line, DbcDatabase &database
|
|||||||
(quoteEnd <= quoteBegin))
|
(quoteEnd <= quoteBegin))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FrameInfo *frame = FindFrameById (database, canId);
|
FrameInfo *frame = FindFrameById (database, canId, isExtended);
|
||||||
if (frame != nullptr)
|
if (frame != nullptr)
|
||||||
frame->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
|
frame->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
|
||||||
} else if (token == "SG_") {
|
} else if (token == "SG_") {
|
||||||
|
std::uint32_t rawCanId = 0U;
|
||||||
std::uint32_t canId = 0U;
|
std::uint32_t canId = 0U;
|
||||||
|
bool isExtended = false;
|
||||||
std::string signalName;
|
std::string signalName;
|
||||||
|
|
||||||
stream >> canId;
|
stream >> rawCanId;
|
||||||
stream >> signalName;
|
stream >> signalName;
|
||||||
|
|
||||||
|
NormalizeCanId (rawCanId, canId, isExtended);
|
||||||
|
|
||||||
const std::string::size_type quoteBegin = line.find ('"');
|
const std::string::size_type quoteBegin = line.find ('"');
|
||||||
const std::string::size_type quoteEnd = line.rfind ('"');
|
const std::string::size_type quoteEnd = line.rfind ('"');
|
||||||
|
|
||||||
@@ -287,7 +314,7 @@ void DbcParser::ParseCommentLine (const std::string &line, DbcDatabase &database
|
|||||||
(quoteEnd <= quoteBegin))
|
(quoteEnd <= quoteBegin))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FrameInfo *frame = FindFrameById (database, canId);
|
FrameInfo *frame = FindFrameById (database, canId, isExtended);
|
||||||
if (frame != nullptr) {
|
if (frame != nullptr) {
|
||||||
SignalInfo *signal = FindSignalByName (*frame, signalName);
|
SignalInfo *signal = FindSignalByName (*frame, signalName);
|
||||||
if (signal != nullptr)
|
if (signal != nullptr)
|
||||||
@@ -296,9 +323,12 @@ void DbcParser::ParseCommentLine (const std::string &line, DbcDatabase &database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameInfo *DbcParser::FindFrameById (DbcDatabase &database, std::uint32_t canId) {
|
FrameInfo *DbcParser::FindFrameById (DbcDatabase &database,
|
||||||
|
std::uint32_t canId,
|
||||||
|
bool isExtended) {
|
||||||
for (std::size_t index = 0U; index < database.frames.size(); ++index) {
|
for (std::size_t index = 0U; index < database.frames.size(); ++index) {
|
||||||
if (database.frames[index].canId == canId)
|
if ((database.frames[index].canId == canId) &&
|
||||||
|
(database.frames[index].isExtended == isExtended))
|
||||||
return &database.frames[index];
|
return &database.frames[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
dbc_parser.h
11
dbc_parser.h
@@ -37,14 +37,21 @@ class DbcParser {
|
|||||||
static bool IsCommentLine (const std::string &line);
|
static bool IsCommentLine (const std::string &line);
|
||||||
static std::string Trim (const std::string &text);
|
static std::string Trim (const std::string &text);
|
||||||
static std::vector<std::string> SplitReceivers (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 std::uint32_t TryExtractPgn (std::uint32_t canId, bool isExtended, bool &hasPgn);
|
||||||
|
|
||||||
|
static void NormalizeCanId (std::uint32_t rawCanId,
|
||||||
|
std::uint32_t &normalizedCanId,
|
||||||
|
bool &isExtended);
|
||||||
|
|
||||||
static FrameInfo ParseFrameLine (const std::string &line);
|
static FrameInfo ParseFrameLine (const std::string &line);
|
||||||
static SignalInfo ParseSignalLine (const std::string &line);
|
static SignalInfo ParseSignalLine (const std::string &line);
|
||||||
|
|
||||||
static void ParseCommentLine (const std::string &line, DbcDatabase &database);
|
static void ParseCommentLine (const std::string &line, DbcDatabase &database);
|
||||||
|
|
||||||
static FrameInfo *FindFrameById (DbcDatabase &database, std::uint32_t canId);
|
static FrameInfo *FindFrameById (DbcDatabase &database,
|
||||||
|
std::uint32_t canId,
|
||||||
|
bool isExtended);
|
||||||
|
|
||||||
static SignalInfo *FindSignalByName (FrameInfo &frame, const std::string &signalName);
|
static SignalInfo *FindSignalByName (FrameInfo &frame, const std::string &signalName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
126
decode_database.h
Normal file
126
decode_database.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#ifndef DECODE_DATABASE_H
|
||||||
|
#define DECODE_DATABASE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal byte order used for runtime decoding.
|
||||||
|
*/
|
||||||
|
enum class ByteOrder {
|
||||||
|
Intel,
|
||||||
|
Motorola
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Signal numeric type.
|
||||||
|
*/
|
||||||
|
enum class ValueType {
|
||||||
|
Unsigned,
|
||||||
|
Signed
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runtime-ready signal definition.
|
||||||
|
*/
|
||||||
|
struct DecodeSignal {
|
||||||
|
std::string name; /**< Signal name. */
|
||||||
|
std::uint32_t startBit; /**< DBC start bit. */
|
||||||
|
std::uint32_t length; /**< Signal length in bits. */
|
||||||
|
ByteOrder byteOrder; /**< Intel or Motorola. */
|
||||||
|
ValueType valueType; /**< Signed or unsigned. */
|
||||||
|
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. */
|
||||||
|
std::string comment; /**< Comment. */
|
||||||
|
|
||||||
|
DecodeSignal()
|
||||||
|
: name()
|
||||||
|
, startBit (0U)
|
||||||
|
, length (0U)
|
||||||
|
, byteOrder (ByteOrder::Intel)
|
||||||
|
, valueType (ValueType::Unsigned)
|
||||||
|
, factor (1.0)
|
||||||
|
, offset (0.0)
|
||||||
|
, minimum (0.0)
|
||||||
|
, maximum (0.0)
|
||||||
|
, unit()
|
||||||
|
, receivers()
|
||||||
|
, comment() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runtime-ready frame definition.
|
||||||
|
*/
|
||||||
|
struct DecodeFrame {
|
||||||
|
std::string name; /**< Frame name. */
|
||||||
|
std::uint32_t canId; /**< Normalized CAN ID. */
|
||||||
|
bool isExtended; /**< true for extended frame. */
|
||||||
|
std::uint8_t dlc; /**< Payload length. */
|
||||||
|
std::uint32_t pgn; /**< PGN if available. */
|
||||||
|
bool hasPgn; /**< true if PGN is valid. */
|
||||||
|
std::string transmitter; /**< Transmitter ECU. */
|
||||||
|
std::string comment; /**< Frame comment. */
|
||||||
|
std::vector<DecodeSignal> signals; /**< Signal definitions. */
|
||||||
|
|
||||||
|
DecodeFrame()
|
||||||
|
: name()
|
||||||
|
, canId (0U)
|
||||||
|
, isExtended (false)
|
||||||
|
, dlc (0U)
|
||||||
|
, pgn (0U)
|
||||||
|
, hasPgn (false)
|
||||||
|
, transmitter()
|
||||||
|
, comment()
|
||||||
|
, signals() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Key for fast frame lookup.
|
||||||
|
*/
|
||||||
|
struct FrameKey {
|
||||||
|
std::uint32_t canId;
|
||||||
|
bool isExtended;
|
||||||
|
|
||||||
|
FrameKey()
|
||||||
|
: canId (0U)
|
||||||
|
, isExtended (false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameKey (std::uint32_t id, bool extended)
|
||||||
|
: canId (id)
|
||||||
|
, isExtended (extended) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator== (const FrameKey &other) const {
|
||||||
|
return (canId == other.canId) && (isExtended == other.isExtended);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hasher for frame key.
|
||||||
|
*/
|
||||||
|
struct FrameKeyHasher {
|
||||||
|
std::size_t operator() (const FrameKey &key) const {
|
||||||
|
const std::size_t a = static_cast<std::size_t> (key.canId);
|
||||||
|
const std::size_t b = key.isExtended ? 1U : 0U;
|
||||||
|
return (a * 1315423911U) ^ b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runtime decode database with fast lookup by CAN ID.
|
||||||
|
*/
|
||||||
|
struct DecodeDatabase {
|
||||||
|
std::vector<DecodeFrame> frames;
|
||||||
|
std::unordered_map<FrameKey, std::size_t, FrameKeyHasher> frameIndexByKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* DECODE_DATABASE_H */
|
||||||
18
frame_info.h
18
frame_info.h
@@ -11,18 +11,20 @@
|
|||||||
* @brief Describes one CAN frame from a DBC file.
|
* @brief Describes one CAN frame from a DBC file.
|
||||||
*/
|
*/
|
||||||
struct FrameInfo {
|
struct FrameInfo {
|
||||||
std::string name; /**< Frame name. */
|
std::string name; /**< Frame name. */
|
||||||
std::uint32_t canId; /**< Raw CAN identifier from DBC. */
|
std::uint32_t canId; /**< Normalized CAN identifier. */
|
||||||
std::uint32_t pgn; /**< J1939 PGN if applicable. */
|
bool isExtended; /**< true for extended frame. */
|
||||||
bool hasPgn; /**< true if PGN was derived. */
|
std::uint32_t pgn; /**< J1939 PGN if applicable. */
|
||||||
std::uint8_t dlc; /**< Frame payload length. */
|
bool hasPgn; /**< true if PGN was derived. */
|
||||||
std::string transmitter; /**< Transmitter ECU name. */
|
std::uint8_t dlc; /**< Frame payload length. */
|
||||||
std::string comment; /**< Optional frame comment. */
|
std::string transmitter; /**< Transmitter ECU name. */
|
||||||
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
|
std::string comment; /**< Optional frame comment. */
|
||||||
|
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
|
||||||
|
|
||||||
FrameInfo()
|
FrameInfo()
|
||||||
: name()
|
: name()
|
||||||
, canId (0U)
|
, canId (0U)
|
||||||
|
, isExtended (false)
|
||||||
, pgn (0U)
|
, pgn (0U)
|
||||||
, hasPgn (false)
|
, hasPgn (false)
|
||||||
, dlc (0U)
|
, dlc (0U)
|
||||||
|
|||||||
76
main.cpp
76
main.cpp
@@ -1,19 +1,13 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include "dbc_parser.h"
|
#include "dbc_parser.h"
|
||||||
#include "dbc_tree_builder.h"
|
#include "dbc_tree_builder.h"
|
||||||
#include "tree_node.h"
|
#include "dbc_decode_builder.h"
|
||||||
|
#include "dbc_decoder.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) {
|
static void PrintTree (const TreeNode *node, int indent) {
|
||||||
if (node == nullptr)
|
if (node == nullptr)
|
||||||
@@ -32,15 +26,12 @@ static void PrintTree (const TreeNode *node, int indent) {
|
|||||||
std::cout << "[frame] " << node->GetName();
|
std::cout << "[frame] " << node->GetName();
|
||||||
|
|
||||||
if (frame != nullptr) {
|
if (frame != nullptr) {
|
||||||
std::cout << " id=" << frame->canId
|
std::cout << " id=0x" << std::hex << frame->canId << std::dec
|
||||||
<< " dlc=" << static_cast<unsigned int> (frame->dlc)
|
<< " ext=" << (frame->isExtended ? "yes" : "no")
|
||||||
<< " tx=" << frame->transmitter;
|
<< " dlc=" << static_cast<unsigned int> (frame->dlc);
|
||||||
|
|
||||||
if (frame->hasPgn)
|
if (frame->hasPgn)
|
||||||
std::cout << " pgn=" << frame->pgn;
|
std::cout << " pgn=" << frame->pgn;
|
||||||
|
|
||||||
if (!frame->comment.empty())
|
|
||||||
std::cout << " comment=\"" << frame->comment << "\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "\n";
|
std::cout << "\n";
|
||||||
@@ -54,13 +45,7 @@ static void PrintTree (const TreeNode *node, int indent) {
|
|||||||
if (signal != nullptr) {
|
if (signal != nullptr) {
|
||||||
std::cout << " start=" << signal->startBit
|
std::cout << " start=" << signal->startBit
|
||||||
<< " len=" << signal->length
|
<< " len=" << signal->length
|
||||||
<< " unit=" << signal->unit
|
<< " unit=" << signal->unit;
|
||||||
<< " rx=";
|
|
||||||
|
|
||||||
PrintReceivers (signal->receivers);
|
|
||||||
|
|
||||||
if (!signal->comment.empty())
|
|
||||||
std::cout << " comment=\"" << signal->comment << "\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "\n";
|
std::cout << "\n";
|
||||||
@@ -76,6 +61,28 @@ static void PrintTree (const TreeNode *node, int indent) {
|
|||||||
PrintTree (node->GetChild (i), indent + 1);
|
PrintTree (node->GetChild (i), indent + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void PrintDecodedFrame (const DecodedFrameValue &decoded) {
|
||||||
|
if (!decoded.valid || (decoded.definition == nullptr)) {
|
||||||
|
std::cout << "No frame definition found.\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Decoded frame: " << decoded.definition->name << "\n";
|
||||||
|
|
||||||
|
for (std::size_t index = 0U; index < decoded.signals.size(); ++index) {
|
||||||
|
const DecodedSignalValue &signal = decoded.signals[index];
|
||||||
|
|
||||||
|
if ((signal.definition == nullptr) || !signal.valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::cout << " " << signal.definition->name
|
||||||
|
<< " raw=" << signal.rawValue
|
||||||
|
<< " physical=" << signal.physicalValue
|
||||||
|
<< " " << signal.definition->unit
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main (int argc, char *argv[]) {
|
int main (int argc, char *argv[]) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
std::cerr << "Usage: dbc_demo <file.dbc>\n";
|
std::cerr << "Usage: dbc_demo <file.dbc>\n";
|
||||||
@@ -86,10 +93,29 @@ int main (int argc, char *argv[]) {
|
|||||||
DbcParser parser;
|
DbcParser parser;
|
||||||
DbcDatabase database = parser.ParseFile (argv[1]);
|
DbcDatabase database = parser.ParseFile (argv[1]);
|
||||||
|
|
||||||
DbcTreeBuilder builder;
|
DbcTreeBuilder treeBuilder;
|
||||||
std::unique_ptr<TreeNode> root = builder.Build (database);
|
std::unique_ptr<TreeNode> root = treeBuilder.Build (database);
|
||||||
|
|
||||||
|
std::cout << "=== Parsed tree ===\n";
|
||||||
PrintTree (root.get(), 0);
|
PrintTree (root.get(), 0);
|
||||||
|
|
||||||
|
DbcDecodeBuilder decodeBuilder;
|
||||||
|
DecodeDatabase decodeDatabase = decodeBuilder.Build (database);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Example raw frame.
|
||||||
|
* Replace with live CAN frame or trace record later.
|
||||||
|
*/
|
||||||
|
RawCanFrame rawFrame;
|
||||||
|
rawFrame.canId = decodeDatabase.frames.empty() ? 0U : decodeDatabase.frames[0].canId;
|
||||||
|
rawFrame.isExtended = decodeDatabase.frames.empty() ? false : decodeDatabase.frames[0].isExtended;
|
||||||
|
rawFrame.data.resize (8U, 0U);
|
||||||
|
|
||||||
|
DbcDecoder decoder;
|
||||||
|
DecodedFrameValue decoded = decoder.Decode (decodeDatabase, rawFrame);
|
||||||
|
|
||||||
|
std::cout << "\n=== Decoded frame ===\n";
|
||||||
|
PrintDecodedFrame (decoded);
|
||||||
} catch (const std::exception &ex) {
|
} catch (const std::exception &ex) {
|
||||||
std::cerr << "Error: " << ex.what() << "\n";
|
std::cerr << "Error: " << ex.what() << "\n";
|
||||||
return 2;
|
return 2;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ struct SignalInfo {
|
|||||||
double minimum; /**< Minimum physical value. */
|
double minimum; /**< Minimum physical value. */
|
||||||
double maximum; /**< Maximum physical value. */
|
double maximum; /**< Maximum physical value. */
|
||||||
std::string unit; /**< Physical unit. */
|
std::string unit; /**< Physical unit. */
|
||||||
std::vector<std::string> receivers; /**< Receivers of this signal. */
|
std::vector<std::string> receivers; /**< Signal receivers. */
|
||||||
std::string comment; /**< Optional signal comment. */
|
std::string comment; /**< Optional signal comment. */
|
||||||
|
|
||||||
SignalInfo()
|
SignalInfo()
|
||||||
|
|||||||
Reference in New Issue
Block a user