Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/libdbc/dbc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <libdbc/message.hpp>
#include <regex>
#include <string>
#include <unordered_map>
#include <vector>

namespace Libdbc {
Expand All @@ -32,6 +33,7 @@ class DbcParser : public Parser {
std::vector<Libdbc::Message> get_messages() const;

Message::ParseSignalsStatus parse_message(uint32_t message_id, const std::vector<uint8_t>& data, std::vector<double>& out_values);
Message::ParseSignalsStatus parse_message(uint32_t message_id, const std::vector<uint8_t>& data, std::unordered_map<std::string, double>& out_values);

std::vector<std::string> unused_lines() const;

Expand Down
5 changes: 5 additions & 0 deletions include/libdbc/message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <iostream>
#include <libdbc/signal.hpp>
#include <string>
#include <unordered_map>
#include <vector>

namespace Libdbc {
Expand All @@ -21,7 +22,11 @@ struct Message {
ErrorInvalidConversion,
};

// Decodes the signals present in data, in signal order. For multiplexed messages only the multiplexor,
// the non multiplexed signals and the signals matching the current multiplexer value are present.
ParseSignalsStatus parse_signals(const std::vector<uint8_t>& data, std::vector<double>& values) const;
// Same as above but keyed by signal name, so multiplexed messages can be decoded without tracking which signals are present.
ParseSignalsStatus parse_signals(const std::vector<uint8_t>& data, std::unordered_map<std::string, double>& values) const;

void append_signal(const Signal& signal);
std::vector<Signal> get_signals() const;
Expand Down
4 changes: 3 additions & 1 deletion include/libdbc/signal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ struct Signal {
};

std::string name;
bool is_multiplexed;
bool is_multiplexed; // Signal is only present for a specific multiplexer value (mN)
bool is_multiplexor = false; // Signal is the multiplexer switch of its message (M)
uint32_t multiplexer_value = 0; // Multiplexer value this signal is present for. Only valid if is_multiplexed
uint32_t start_bit;
uint32_t size;
bool is_bigendian;
Expand Down
49 changes: 33 additions & 16 deletions src/dbc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <libdbc/utils/utils.hpp>
#include <regex>
#include <string>
#include <unordered_map>
#include <vector>

namespace Libdbc {
Expand All @@ -17,6 +18,7 @@ const auto floatPattern = "(-?\\d+\\.?(\\d+)?)"; // Can be negative

const auto signalIdentifierPattern = "(SG_)";
const auto namePattern = "(\\w+)";
const auto multiplexerPattern = "(?:\\s(M|m\\d+M?))?"; // Optional: M = multiplexor, mN = multiplexed by value N, mNM = both
const auto bitStartPattern = "(\\d+)"; // Cannot be negative
const auto lengthPattern = "(\\d+)"; // Cannot be negative
const auto byteOrderPattern = "([0-1])";
Expand All @@ -34,16 +36,17 @@ const auto receiverPattern = "([\\w\\,]+|Vector__XXX)*";
const auto whiteSpace = "\\s";

constexpr unsigned SIGNAL_NAME_GROUP = 2;
constexpr unsigned SIGNAL_START_BIT_GROUP = 3;
constexpr unsigned SIGNAL_SIZE_GROUP = 4;
constexpr unsigned SIGNAL_ENDIAN_GROUP = 5;
constexpr unsigned SIGNAL_SIGNED_GROUP = 6;
constexpr unsigned SIGNAL_FACTOR_GROUP = 7;
constexpr unsigned SIGNAL_OFFSET_GROUP = 9;
constexpr unsigned SIGNAL_MIN_GROUP = 11;
constexpr unsigned SIGNAL_MAX_GROUP = 13;
constexpr unsigned SIGNAL_UNIT_GROUP = 15;
constexpr unsigned SIGNAL_RECIEVER_GROUP = 16;
constexpr unsigned SIGNAL_MULTIPLEXER_GROUP = 3;
constexpr unsigned SIGNAL_START_BIT_GROUP = 4;
constexpr unsigned SIGNAL_SIZE_GROUP = 5;
constexpr unsigned SIGNAL_ENDIAN_GROUP = 6;
constexpr unsigned SIGNAL_SIGNED_GROUP = 7;
constexpr unsigned SIGNAL_FACTOR_GROUP = 8;
constexpr unsigned SIGNAL_OFFSET_GROUP = 10;
constexpr unsigned SIGNAL_MIN_GROUP = 12;
constexpr unsigned SIGNAL_MAX_GROUP = 14;
constexpr unsigned SIGNAL_UNIT_GROUP = 16;
constexpr unsigned SIGNAL_RECIEVER_GROUP = 17;

constexpr unsigned MESSAGE_ID_GROUP = 2;
constexpr unsigned MESSAGE_NAME_GROUP = 3;
Expand All @@ -63,11 +66,9 @@ DbcParser::DbcParser()
, node_re("^(BU_:)\\s?((?:[\\w]+?\\s?)*)?")
, message_re("^(BO_)\\s(\\d+)\\s(\\w+)\\:\\s(\\d+)\\s(\\w+|Vector__XXX)")
, value_re("^(VAL_)\\s(\\d+)\\s(\\w+)((?:\\s(\\d+)\\s\"([^\"]*)\")+)\\s;$")
,
// NOTE: No multiplex support yet
signal_re(std::string("^") + whiteSpace + signalIdentifierPattern + whiteSpace + namePattern + whiteSpace + "\\:" + whiteSpace + bitStartPattern + "\\|"
+ lengthPattern + "\\@" + byteOrderPattern + signPattern + whiteSpace + offsetScalePattern + whiteSpace + minMaxPattern + whiteSpace + unitPattern
+ whiteSpace + receiverPattern) {
, signal_re(std::string("^") + whiteSpace + signalIdentifierPattern + whiteSpace + namePattern + multiplexerPattern + whiteSpace + "\\:" + whiteSpace
+ bitStartPattern + "\\|" + lengthPattern + "\\@" + byteOrderPattern + signPattern + whiteSpace + offsetScalePattern + whiteSpace
+ minMaxPattern + whiteSpace + unitPattern + whiteSpace + receiverPattern) {
}

void DbcParser::parse_file(std::istream& stream) {
Expand Down Expand Up @@ -128,6 +129,16 @@ Message::ParseSignalsStatus DbcParser::parse_message(const uint32_t message_id,
return Message::ParseSignalsStatus::ErrorUnknownID;
}

Message::ParseSignalsStatus
DbcParser::parse_message(const uint32_t message_id, const std::vector<uint8_t>& data, std::unordered_map<std::string, double>& out_values) {
for (const auto& message : messages) {
if (message.id() == message_id) {
return message.parse_signals(data, out_values);
}
}
return Message::ParseSignalsStatus::ErrorUnknownID;
}

void DbcParser::parse_dbc_header(std::istream& file_stream) {
std::string line;
std::smatch match;
Expand Down Expand Up @@ -183,7 +194,11 @@ void DbcParser::parse_dbc_messages(const std::vector<std::string>& lines) {

if (std::regex_search(line, match, signal_re) && !messages.empty()) {
std::string name = match.str(SIGNAL_NAME_GROUP);
bool is_multiplexed = false; // No support yet
// Multiplexer indicator: "M", "mN" or "mNM" (extended multiplexing)
std::string multiplexer = match.str(SIGNAL_MULTIPLEXER_GROUP);
bool is_multiplexed = !multiplexer.empty() && multiplexer.front() == 'm';
bool is_multiplexor = !multiplexer.empty() && multiplexer.back() == 'M';
uint32_t multiplexer_value = is_multiplexed ? static_cast<uint32_t>(std::stoul(multiplexer.substr(1))) : 0;
uint32_t start_bit = static_cast<uint32_t>(std::stoul(match.str(SIGNAL_START_BIT_GROUP)));
uint32_t size = static_cast<uint32_t>(std::stoul(match.str(SIGNAL_SIZE_GROUP)));
bool is_bigendian = (std::stoul(match.str(SIGNAL_ENDIAN_GROUP)) == 0);
Expand All @@ -200,6 +215,8 @@ void DbcParser::parse_dbc_messages(const std::vector<std::string>& lines) {
Utils::String::split(match.str(SIGNAL_RECIEVER_GROUP), receivers, ',');

Signal sig(name, is_multiplexed, start_bit, size, is_bigendian, is_signed, factor, offset, min, max, unit, receivers);
sig.is_multiplexor = is_multiplexor;
sig.multiplexer_value = multiplexer_value;
messages.back().append_signal(sig);
continue;
}
Expand Down
121 changes: 79 additions & 42 deletions src/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <libdbc/signal.hpp>
#include <ostream>
#include <string>
#include <unordered_map>
#include <vector>

namespace Libdbc {
Expand All @@ -26,10 +27,55 @@ bool Message::operator==(const Message& rhs) const {
return (m_id == rhs.id()) && (m_name == rhs.m_name) && (m_size == rhs.m_size) && (m_node == rhs.m_node);
}

Message::ParseSignalsStatus Message::parse_signals(const std::vector<uint8_t>& data, std::vector<double>& values) const {
namespace {

// Extracts the raw (unscaled) bits of a signal. Bits above signal.size are not masked off.
uint64_t extract_raw(const Signal& signal, uint64_t data_little_endian, uint64_t data_big_endian, std::size_t len) {
if (signal.is_bigendian) {
uint32_t start_bit = ONE_BYTE * (signal.start_bit / ONE_BYTE) + (SEVEN_BITS - (signal.start_bit % ONE_BYTE)); // Calculation taken from python CAN
uint64_t value = data_big_endian << start_bit;
return value >> (len - signal.size);
}
return data_little_endian >> signal.start_bit;
}

double to_physical(const Signal& signal, uint64_t value) {
if (signal.is_signed && signal.size > 1) {
switch (signal.size) {
case ONE_BYTE:
return static_cast<int8_t>(value) * signal.factor + signal.offset;
case TWO_BYTES:
return static_cast<int16_t>(value) * signal.factor + signal.offset;
case FOUR_BYTES:
return static_cast<int32_t>(value) * signal.factor + signal.offset;
case EIGHT_BYTES:
return static_cast<double>(value) * signal.factor + signal.offset;
default: {
// 2 complement -> decimal
const bool is_negative = (value & (1ULL << (signal.size - 1))) != 0;
int64_t nativeInt = 0;
if (is_negative) {
nativeInt = static_cast<int64_t>(value | ~((1ULL << signal.size) - 1)); // invert all bits above signal.size
} else {
nativeInt = static_cast<int64_t>(value & ((1ULL << signal.size) - 1)); // masking
}
return static_cast<double>(nativeInt) * signal.factor + signal.offset;
}
}
}
// use only the relevant bits
value = value & ((1 << signal.size) - 1); // masking
return static_cast<double>(value) * signal.factor + signal.offset;
}

// Decodes every signal that is present in the frame and hands it to on_value(signal, value).
// Multiplexed signals are only present when the multiplexor's raw value matches their multiplexer value.
// NOTE: Extended multiplexing (SG_MUL_VAL_) is not supported. All multiplexed signals are switched by the top level multiplexor.
template<typename OnValue>
Message::ParseSignalsStatus decode_signals(const std::vector<Signal>& signals, const std::vector<uint8_t>& data, OnValue on_value) {
auto size = data.size();
if (size > ONE_BYTE) {
return ParseSignalsStatus::ErrorMessageToLong; // not supported yet
return Message::ParseSignalsStatus::ErrorMessageToLong; // not supported yet
}

uint64_t data_little_endian = 0;
Expand All @@ -42,50 +88,41 @@ Message::ParseSignalsStatus Message::parse_signals(const std::vector<uint8_t>& d
// TODO: does this also work on a big endian machine?

const auto len = size * 8;
uint64_t value = 0;
for (const auto& signal : m_signals) {
if (signal.is_bigendian) {
uint32_t start_bit = ONE_BYTE * (signal.start_bit / ONE_BYTE) + (SEVEN_BITS - (signal.start_bit % ONE_BYTE)); // Calculation taken from python CAN
value = data_big_endian << start_bit;
value = value >> (len - signal.size);
} else {
value = data_little_endian >> signal.start_bit;
}

if (signal.is_signed && signal.size > 1) {
switch (signal.size) {
case ONE_BYTE:
values.push_back(static_cast<int8_t>(value) * signal.factor + signal.offset);
break;
case TWO_BYTES:
values.push_back(static_cast<int16_t>(value) * signal.factor + signal.offset);
break;
case FOUR_BYTES:
values.push_back(static_cast<int32_t>(value) * signal.factor + signal.offset);
break;
case EIGHT_BYTES:
values.push_back(static_cast<double>(value) * signal.factor + signal.offset);
break;
default: {
// 2 complement -> decimal
const bool is_negative = (value & (1ULL << (signal.size - 1))) != 0;
int64_t nativeInt = 0;
if (is_negative) {
nativeInt = static_cast<int64_t>(value | ~((1ULL << signal.size) - 1)); // invert all bits above signal.size
} else {
nativeInt = static_cast<int64_t>(value & ((1ULL << signal.size) - 1)); // masking
}
values.push_back(static_cast<double>(nativeInt) * signal.factor + signal.offset);
break;
bool has_multiplexor = false;
uint64_t multiplexer_value = 0;
for (const auto& signal : signals) {
if (signal.is_multiplexor && !signal.is_multiplexed) {
multiplexer_value = extract_raw(signal, data_little_endian, data_big_endian, len);
if (signal.size < EIGHT_BYTES) {
multiplexer_value &= (1ULL << signal.size) - 1;
}
}
} else {
// use only the relevant bits
value = value & ((1 << signal.size) - 1); // masking
values.push_back(static_cast<double>(value) * signal.factor + signal.offset);
has_multiplexor = true;
break;
}
}

for (const auto& signal : signals) {
if (signal.is_multiplexed && (!has_multiplexor || signal.multiplexer_value != multiplexer_value)) {
continue;
}
on_value(signal, to_physical(signal, extract_raw(signal, data_little_endian, data_big_endian, len)));
}
return ParseSignalsStatus::Success;
return Message::ParseSignalsStatus::Success;
}

}

Message::ParseSignalsStatus Message::parse_signals(const std::vector<uint8_t>& data, std::vector<double>& values) const {
return decode_signals(m_signals, data, [&values](const Signal&, double value) {
values.push_back(value);
});
}

Message::ParseSignalsStatus Message::parse_signals(const std::vector<uint8_t>& data, std::unordered_map<std::string, double>& values) const {
return decode_signals(m_signals, data, [&values](const Signal& signal, double value) {
values[signal.name] = value;
});
}

void Message::append_signal(const Signal& signal) {
Expand Down
7 changes: 6 additions & 1 deletion src/signal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ Signal::Signal(std::string name,
}

bool Signal::operator==(const Signal& rhs) const {
return (this->name == rhs.name) && (this->is_multiplexed == rhs.is_multiplexed) && (this->start_bit == rhs.start_bit) && (this->size == rhs.size)
return (this->name == rhs.name) && (this->is_multiplexed == rhs.is_multiplexed) && (this->is_multiplexor == rhs.is_multiplexor)
&& (this->multiplexer_value == rhs.multiplexer_value) && (this->start_bit == rhs.start_bit) && (this->size == rhs.size)
&& (this->is_bigendian == rhs.is_bigendian) && (this->is_signed == rhs.is_signed) && (this->offset == rhs.offset) && (this->min == rhs.min)
&& (this->max == rhs.max) && (this->unit == rhs.unit) && (this->receivers == rhs.receivers);
}
Expand All @@ -44,6 +45,10 @@ bool Signal::operator<(const Signal& rhs) const {
std::ostream& operator<<(std::ostream& out, const Signal& sig) {
out << "Signal {name: " << sig.name << ", ";
out << "Multiplexed: " << (sig.is_multiplexed ? "True" : "False") << ", ";
if (sig.is_multiplexed) {
out << "Multiplexer value: " << sig.multiplexer_value << ", ";
}
out << "Multiplexor: " << (sig.is_multiplexor ? "True" : "False") << ", ";
out << "Start bit: " << sig.start_bit << ", ";
out << "Size: " << sig.size << ", ";
out << "Endianness: " << (sig.is_bigendian ? "Big endian" : "Little endian") << ", ";
Expand Down
Loading
Loading