Closes
This commit is contained in:
1 parent
65142fdfe3
commit
e5d818d9b2
14 files changed
+393
-39
No files matched your search
@@ -16,5 +16,6 @@ HEADERS += \
|
||||
$$PWD/decode_database.h \
|
||||
$$PWD/frame_info.h \
|
||||
$$PWD/signal_info.h \
|
||||
$$PWD/value_descriptions.h \
|
||||
$$PWD/tree_node.h \
|
||||
$$PWD/typefactory.h
|
||||
@@ -43,6 +43,7 @@ DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const {
|
||||
targetSignal.unit = sourceSignal.unit;
|
||||
targetSignal.receivers = sourceSignal.receivers;
|
||||
targetSignal.comment = sourceSignal.comment;
|
||||
targetSignal.valueDescriptions = sourceSignal.valueDescriptions;
|
||||
|
||||
targetFrame.signals.push_back(targetSignal);
|
||||
}
|
||||
|
||||
+9
-4
@@ -35,10 +35,15 @@ struct RawCanFrame {
|
||||
* @brief One decoded signal value.
|
||||
*/
|
||||
struct DecodedSignalValue {
|
||||
const DecodeSignal *definition; /**< Signal definition. */
|
||||
std::variant<std::int64_t, std::uint64_t> rawValue; /**< Exact signed or unsigned raw value. */
|
||||
double physicalValue; /**< Converted physical value. */
|
||||
bool valid; /**< true if decoding succeeded. */
|
||||
const DecodeSignal *definition; /**< Signal definition. */
|
||||
RawSignalValue rawValue; /**< Exact signed or unsigned raw value. */
|
||||
double physicalValue; /**< Converted physical value. */
|
||||
bool valid; /**< true if decoding succeeded. */
|
||||
|
||||
/** Lookup using the raw integer; nullptr for invalid or unmapped values. */
|
||||
const std::string *GetValueDescription() const {
|
||||
return valid && definition ? FindValueDescription(definition->valueDescriptions, rawValue) : nullptr;
|
||||
}
|
||||
|
||||
DecodedSignalValue()
|
||||
: definition(nullptr), rawValue(std::uint64_t(0U)), physicalValue(0.0), valid(false) {
|
||||
|
||||
@@ -55,6 +55,7 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
|
||||
continue;
|
||||
|
||||
try {
|
||||
context.lineNumber = lineNumber;
|
||||
dispatcher.Dispatch(line, context);
|
||||
} catch (const std::exception &error) {
|
||||
throw std::runtime_error(filePath + ":" + std::to_string(lineNumber) +
|
||||
@@ -62,6 +63,7 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
|
||||
}
|
||||
}
|
||||
|
||||
context.ResolveValueDescriptions(filePath);
|
||||
return database;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -24,11 +24,12 @@
|
||||
* - SG_
|
||||
* - CM_ BO_
|
||||
* - CM_ SG_
|
||||
* - VAL_ signal value descriptions
|
||||
*
|
||||
* Ignores:
|
||||
* - attributes
|
||||
* - multiplexing
|
||||
* - value tables
|
||||
* - named VAL_TABLE_ tables
|
||||
*/
|
||||
class DbcParser {
|
||||
public:
|
||||
|
||||
@@ -309,6 +309,7 @@ DbcStatementDispatcher::DbcStatementDispatcher() {
|
||||
RegisterType<DbcFrameHandler>("BO_");
|
||||
RegisterType<DbcSignalHandler>("SG_");
|
||||
RegisterType<DbcCommentHandler>("CM_");
|
||||
RegisterType<DbcValueDescriptionHandler>("VAL_");
|
||||
}
|
||||
|
||||
bool DbcStatementDispatcher::Dispatch(const std::string &line,
|
||||
@@ -322,3 +323,88 @@ bool DbcStatementDispatcher::Dispatch(const std::string &line,
|
||||
handler->second->Handle(line, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext &context) const {
|
||||
static const std::regex header(R"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
|
||||
static const std::regex pair(R"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
|
||||
std::smatch fields;
|
||||
if (!std::regex_match(line, fields, header))
|
||||
throw std::runtime_error("Malformed or unsupported VAL_ statement.");
|
||||
const auto rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(), "VAL_ CAN ID");
|
||||
if ((rawId & 0x60000000U) != 0U)
|
||||
throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range.");
|
||||
PendingValueDescriptions pending;
|
||||
NormalizeCanId(rawId, pending.canId, pending.isExtended);
|
||||
pending.signalName = fields[2].str();
|
||||
pending.lineNumber = context.lineNumber;
|
||||
std::string rest = fields[3].str();
|
||||
while (rest != ";") {
|
||||
if (!std::regex_search(rest, fields, pair))
|
||||
throw std::runtime_error("Malformed VAL_ value/description pair or missing semicolon.");
|
||||
const std::string number = fields[1].str();
|
||||
RawSignalValue key;
|
||||
if (!number.empty() && number[0] == '-') {
|
||||
std::int64_t value = 0;
|
||||
const auto parsed = std::from_chars(number.data(), number.data() + number.size(), value);
|
||||
if (parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
|
||||
throw std::runtime_error("Invalid VAL_ integer: " + number);
|
||||
key = NormalizeValueKey(RawSignalValue(value));
|
||||
} else {
|
||||
std::uint64_t value = 0;
|
||||
const auto parsed = std::from_chars(number.data(), number.data() + number.size(), value);
|
||||
if (number.empty() || parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
|
||||
throw std::runtime_error("Invalid VAL_ integer: " + number);
|
||||
key = value;
|
||||
}
|
||||
std::string description;
|
||||
const std::string quoted = fields[2].str();
|
||||
for (std::size_t i = 0; i < quoted.size(); ++i) {
|
||||
if (quoted[i] == '\\')
|
||||
++i;
|
||||
description += quoted[i];
|
||||
}
|
||||
if (!pending.descriptions.emplace(key, description).second)
|
||||
throw std::runtime_error("Duplicate VAL_ value key: " + number);
|
||||
const auto consumed = static_cast<std::size_t>(fields.length());
|
||||
rest.erase(0, consumed);
|
||||
if (!rest.empty() && rest[0] != ';' && rest[0] != ' ' && rest[0] != '\t')
|
||||
throw std::runtime_error("Missing whitespace between VAL_ pairs.");
|
||||
rest = TrimText(rest);
|
||||
}
|
||||
if (pending.descriptions.empty())
|
||||
throw std::runtime_error("VAL_ requires at least one value/description pair.");
|
||||
context.pendingValues.push_back(std::move(pending));
|
||||
}
|
||||
|
||||
void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
|
||||
for (const auto &pending: pendingValues) {
|
||||
try {
|
||||
FrameInfo *frame = nullptr;
|
||||
for (auto &candidate: database.frames) {
|
||||
if (candidate.canId == pending.canId && candidate.isExtended == pending.isExtended) {
|
||||
if (frame)
|
||||
throw std::runtime_error("Ambiguous VAL_ frame reference.");
|
||||
frame = &candidate;
|
||||
}
|
||||
}
|
||||
if (!frame)
|
||||
throw std::runtime_error("Unresolved VAL_ frame reference.");
|
||||
SignalInfo *signal = nullptr;
|
||||
for (auto &candidate: frame->signals) {
|
||||
if (candidate.name == pending.signalName) {
|
||||
if (signal)
|
||||
throw std::runtime_error("Ambiguous VAL_ signal reference: " + pending.signalName);
|
||||
signal = &candidate;
|
||||
}
|
||||
}
|
||||
if (!signal)
|
||||
throw std::runtime_error("Unresolved VAL_ signal reference: " + pending.signalName);
|
||||
for (const auto &entry: pending.descriptions) {
|
||||
if (!signal->valueDescriptions.insert(entry).second)
|
||||
throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
|
||||
}
|
||||
} catch (const std::exception &error) {
|
||||
throw std::runtime_error(filePath + ":" + std::to_string(pending.lineNumber) + ": " + error.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,22 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
struct PendingValueDescriptions {
|
||||
std::uint32_t canId;
|
||||
bool isExtended;
|
||||
std::string signalName;
|
||||
ValueDescriptions descriptions;
|
||||
std::size_t lineNumber;
|
||||
};
|
||||
|
||||
/** Mutable state belonging to one file parse. Handlers do not retain it. */
|
||||
struct DbcParseContext {
|
||||
DbcDatabase &database;
|
||||
FrameInfo *currentFrame = nullptr;
|
||||
std::size_t lineNumber = 0U;
|
||||
std::vector<PendingValueDescriptions> pendingValues = {};
|
||||
|
||||
void ResolveValueDescriptions(const std::string &filePath);
|
||||
};
|
||||
|
||||
class DbcStatementHandler {
|
||||
@@ -43,6 +55,11 @@ private:
|
||||
static void ParseCommentLine(const std::string &line, DbcDatabase &database);
|
||||
};
|
||||
|
||||
class DbcValueDescriptionHandler : public DbcStatementHandler {
|
||||
public:
|
||||
void Handle(const std::string &line, DbcParseContext &context) const override;
|
||||
};
|
||||
|
||||
/** Exact-key dispatch to handlers created once per registration via TypeFactory. */
|
||||
class DbcStatementDispatcher {
|
||||
public:
|
||||
|
||||
+14
-12
@@ -12,6 +12,7 @@
|
||||
#ifndef DECODE_DATABASE_H
|
||||
#define DECODE_DATABASE_H
|
||||
|
||||
#include "value_descriptions.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -37,18 +38,19 @@ enum class ValueType {
|
||||
* @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. */
|
||||
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. */
|
||||
ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */
|
||||
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() {
|
||||
|
||||
+14
-12
@@ -12,6 +12,7 @@
|
||||
#ifndef SIGNAL_INFO_H
|
||||
#define SIGNAL_INFO_H
|
||||
|
||||
#include "value_descriptions.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -20,18 +21,19 @@
|
||||
* @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; /**< Signal receivers. */
|
||||
std::string comment; /**< Optional signal comment. */
|
||||
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; /**< Signal receivers. */
|
||||
ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */
|
||||
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() {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#ifndef VALUE_DESCRIPTIONS_H
|
||||
#define VALUE_DESCRIPTIONS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
using RawSignalValue = std::variant<std::int64_t, std::uint64_t>;
|
||||
|
||||
/** Canonical keys: negative int64_t, nonnegative uint64_t. */
|
||||
inline RawSignalValue NormalizeValueKey(const RawSignalValue &value) {
|
||||
if (const auto *number = std::get_if<std::int64_t>(&value)) {
|
||||
if (*number >= 0)
|
||||
return static_cast<std::uint64_t>(*number);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
using ValueDescriptions = std::map<RawSignalValue, std::string>;
|
||||
|
||||
/** Returns a borrowed string, or nullptr; never converts integers to double. */
|
||||
inline const std::string *FindValueDescription(const ValueDescriptions &descriptions,
|
||||
const RawSignalValue &rawValue) {
|
||||
const auto found = descriptions.find(NormalizeValueKey(rawValue));
|
||||
return found == descriptions.end() ? nullptr : &found->second;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user