This commit is contained in:
deeaitch committed 2026-10-20 13:38:21 -04:00
1 parent 849ce31e2f
commit a55d1c58ed
272 files changed
+41866 -261

No files matched your search

+31 -16
View File
@@ -9,6 +9,9 @@
#include <variant>
#include <vector>
/**
* @brief Target scope of an attribute definition or assignment.
*/
enum class AttributeScope {
Database,
Node,
@@ -17,6 +20,9 @@ enum class AttributeScope {
Environment
};
/**
* @brief DBC attribute storage type. HEX values use decimal integer syntax.
*/
enum class AttributeType {
Integer,
Hex,
@@ -25,29 +31,38 @@ enum class AttributeType {
Enum
};
/** @brief Typed attribute value; ENUM uses a zero-based integer index. */
using AttributeValue = std::variant<std::int64_t, double, std::string>;
/** @brief Effective attribute values indexed by definition name. */
using AttributeMap = std::map<std::string, AttributeValue>;
/**
* @brief Attribute type, permitted range, and optional default.
*/
struct AttributeDefinition {
AttributeScope scope = AttributeScope::Database;
AttributeType type = AttributeType::String;
std::int64_t integerMinimum = 0;
std::int64_t integerMaximum = 0;
double minimum = 0.0;
double maximum = 0.0;
std::vector<std::string> enumValues;
std::optional<AttributeValue> defaultValue;
AttributeScope scope = AttributeScope::Database; /**< Target scope; database when no scope marker is present. */
AttributeType type = AttributeType::String; /**< Type used to validate values. */
std::int64_t integerMinimum = 0; /**< Inclusive INT, HEX, or ENUM lower bound. */
std::int64_t integerMaximum = 0; /**< Inclusive INT, HEX, or ENUM upper bound. */
double minimum = 0.0; /**< Inclusive FLOAT lower bound. */
double maximum = 0.0; /**< Inclusive FLOAT upper bound. */
std::vector<std::string> enumValues; /**< ENUM labels in index order. */
std::optional<AttributeValue> defaultValue; /**< Validated default; empty when not declared. */
};
/** Explicit assignment; defaults remain on definitions. Named nodes/environment
* variables are retained without validating declarations, which are not parsed. */
/**
* @brief Explicit assignment; defaults remain on definitions.
*
* Named nodes/environment variables are retained without validating declarations,
* which are not parsed.
*/
struct AttributeAssignment {
std::string name;
AttributeScope scope = AttributeScope::Database;
std::uint32_t canId = 0U;
bool isExtended = false;
std::string targetName;
AttributeValue value;
std::string name; /**< Attribute definition name. */
AttributeScope scope = AttributeScope::Database; /**< Target scope; database when no scope marker is present. */
std::uint32_t canId = 0U; /**< Normalized frame ID for frame/signal assignments. */
bool isExtended = false; /**< Frame type for frame/signal assignments. */
std::string targetName; /**< Signal, node, or environment variable name. */
AttributeValue value; /**< Validated explicit value. */
};
#endif
+20 -29
View File
@@ -104,7 +104,8 @@ namespace {
throw std::runtime_error("Unsupported attribute scope: " + text);
}
std::int64_t Integer(const std::string &text) {
template<class Numeric>
Numeric ParseNumber(const std::string &text) {
const char *begin = text.data();
const char *end = begin + text.size();
if (begin != end && *begin == '+') {
@@ -112,22 +113,7 @@ namespace {
if (begin != end && (*begin == '-' || *begin == '+'))
throw std::runtime_error("Invalid attribute number: " + text);
}
std::int64_t value = 0;
const std::from_chars_result parsed = std::from_chars(begin, end, value);
if (begin == end || parsed.ec != std::errc() || parsed.ptr != end)
throw std::runtime_error("Invalid attribute integer: " + text);
return value;
}
double Number(const std::string &text) {
const char *begin = text.data();
const char *end = begin + text.size();
if (begin != end && *begin == '+') {
++begin;
if (begin != end && (*begin == '-' || *begin == '+'))
throw std::runtime_error("Invalid attribute number: " + text);
}
double value = 0.0;
Numeric value = 0;
const std::from_chars_result parsed = std::from_chars(begin, end, value);
if (begin == end || parsed.ec != std::errc() || parsed.ptr != end || !std::isfinite(value))
throw std::runtime_error("Invalid attribute number: " + text);
@@ -148,12 +134,12 @@ namespace {
}
if (token.quoted) throw std::runtime_error("Numeric attribute requires an unquoted number.");
if (definition.type == AttributeType::Float) {
const double value = Number(token.text);
const double value = ParseNumber<double>(token.text);
if (value < definition.minimum || value > definition.maximum)
throw std::runtime_error("Attribute FLOAT value outside definition range.");
return value;
}
const std::int64_t value = Integer(token.text);
const std::int64_t value = ParseNumber<std::int64_t>(token.text);
if (value < definition.integerMinimum || value > definition.integerMaximum)
throw std::runtime_error("Attribute integer/ENUM value outside definition range.");
return value;
@@ -171,14 +157,14 @@ namespace {
const std::string type = tokens.Word();
if (type == "INT" || type == "HEX") {
definition.type = type == "INT" ? AttributeType::Integer : AttributeType::Hex;
definition.integerMinimum = Integer(tokens.Word());
definition.integerMaximum = Integer(tokens.Word());
definition.integerMinimum = ParseNumber<std::int64_t>(tokens.Word());
definition.integerMaximum = ParseNumber<std::int64_t>(tokens.Word());
if (definition.integerMinimum > definition.integerMaximum)
throw std::runtime_error("Reversed attribute integer range.");
} else if (type == "FLOAT") {
definition.type = AttributeType::Float;
definition.minimum = Number(tokens.Word());
definition.maximum = Number(tokens.Word());
definition.minimum = ParseNumber<double>(tokens.Word());
definition.maximum = ParseNumber<double>(tokens.Word());
if (definition.minimum > definition.maximum)
throw std::runtime_error("Reversed attribute FLOAT range.");
} else if (type == "STRING") {
@@ -217,7 +203,7 @@ namespace {
}
void ParseId(const std::string &text, AttributeAssignment &assignment) {
const std::int64_t raw = Integer(text);
const std::int64_t raw = ParseNumber<std::int64_t>(text);
if (raw < 0 || raw > std::numeric_limits<std::uint32_t>::max() || (raw & 0x60000000U) != 0)
throw std::runtime_error("Invalid attribute CAN ID.");
const std::uint32_t id = static_cast<std::uint32_t>(raw);
@@ -247,7 +233,12 @@ namespace {
void ApplyDefault(DbcDatabase &database, const std::string &name, const AttributeDefinition &definition) {
if (!definition.defaultValue) return;
if (definition.scope == AttributeScope::Database) database.attributes[name] = *definition.defaultValue;
if (definition.scope == AttributeScope::Database) {
database.attributes[name] = *definition.defaultValue;
return;
}
if (definition.scope != AttributeScope::Frame && definition.scope != AttributeScope::Signal)
return;
for (FrameInfo &frame: database.frames) {
if (definition.scope == AttributeScope::Frame) frame.attributes[name] = *definition.defaultValue;
if (definition.scope == AttributeScope::Signal) {
@@ -266,20 +257,20 @@ void DbcParseContext::ResolveAttributes(const std::string &filePath, std::vector
std::vector<std::size_t> assignmentLines;
// Resolve each phase independently of source ordering.
const char *keywords[] = {"BA_DEF_", "BA_DEF_DEF_", "BA_"};
for (int phase = 0; phase < 3; ++phase) {
for (const char *phase: keywords) {
for (const PendingAttribute &pending: pendingAttributes) {
const std::string keyword = pending.statement.substr(0U, pending.statement.find_first_of(" \t"));
if (keyword != keywords[phase]) continue;
if (keyword != phase) continue;
try {
AttributeTokens tokens(pending.statement);
tokens.Word();
if (phase == 0) {
if (keyword == "BA_DEF_") {
Define(tokens, database);
continue;
}
const std::string name = tokens.Quoted();
AttributeDefinition &definition = FindDefinition(database, name);
if (phase == 1) {
if (keyword == "BA_DEF_DEF_") {
if (definition.defaultValue) throw std::runtime_error("Duplicate attribute default: " + name);
definition.defaultValue = Value(tokens.Take(), definition, true);
ValidateCycleTime(name, *definition.defaultValue);
+3 -3
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_database.h
* @brief
* @brief Parsed DBC database and attribute metadata.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
@@ -21,8 +21,8 @@
*/
struct DbcDatabase {
std::vector<FrameInfo> frames; /**< All frames found in the DBC file. */
std::map<std::string, AttributeDefinition> attributeDefinitions = {};
std::vector<AttributeAssignment> attributeAssignments = {};
std::map<std::string, AttributeDefinition> attributeDefinitions = {}; /**< Attribute definitions indexed by name. */
std::vector<AttributeAssignment> attributeAssignments = {}; /**< Explicit assignments in source order. */
AttributeMap attributes = {}; /**< Effective database attributes. */
};
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_decode_builder.cpp
* @brief
* @brief Conversion of parsed metadata to runtime definitions.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_decode_builder.h
* @brief
* @brief Runtime decode database builder.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
+2 -3
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_decoder.cpp
* @brief
* @brief CAN signal extraction and physical value conversion.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
@@ -99,8 +99,7 @@ DecodedFrameValue DbcDecoder::DecodeImpl(const DecodeDatabase &database,
}
bool DbcDecoder::IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc) {
if (signal.length == 0U || signal.length > 64U ||
(signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola) ||
if ((signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola) ||
(signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed) ||
(signal.multiplexing.role != MultiplexRole::Unconditional &&
signal.multiplexing.role != MultiplexRole::Selector &&
+3 -2
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_decoder.h
* @brief
* @brief CAN frame decoding API and result types.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
@@ -29,6 +29,7 @@ struct RawCanFrame {
RawCanFrame() = default;
};
/** @brief Outcome of signal extraction and physical conversion. */
enum class SignalDecodeStatus {
InvalidDefinition,
InsufficientPayload,
@@ -56,7 +57,7 @@ struct DecodedSignalValue {
RawSignalValue rawValue = std::uint64_t(0U); /**< Exact raw value for Valid or PhysicalConversionFailed. */
double physicalValue = 0.0; /**< Conversion result, possibly NaN/Inf on failure. */
bool valid = false; /**< true if decoding succeeded. */
SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition;
SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition; /**< Detailed signal outcome. */
/** Lookup using the raw integer; nullptr for invalid or unmapped values. */
const std::string *GetValueDescription() const {
+11 -7
View File
@@ -7,6 +7,7 @@
#include <string>
#include <vector>
/** @brief Warning or fatal parse error. */
enum class DbcDiagnosticSeverity {
Warning,
Error
@@ -24,18 +25,21 @@ enum class DbcDiagnosticCode {
UnsupportedAttribute
};
/** @brief Diagnostic message and physical source location. */
struct DbcDiagnostic {
DbcDiagnosticSeverity severity;
DbcDiagnosticCode code;
std::string filePath;
std::size_t lineNumber;// One-based physical line; zero for file-level errors.
std::string message;
DbcDiagnosticSeverity severity; /**< Message severity. */
DbcDiagnosticCode code; /**< Stable diagnostic category. */
std::string filePath; /**< Input file path. */
std::size_t lineNumber; /**< One-based physical line; zero for file-level errors. */
std::string message; /**< Human-readable diagnostic details. */
};
/** @brief Parsed database and diagnostics; the database is empty on error. */
struct DbcParseResult {
DbcDatabase database;
std::vector<DbcDiagnostic> diagnostics;
DbcDatabase database; /**< Parsed content, published only on success. */
std::vector<DbcDiagnostic> diagnostics; /**< Warnings and the first error, if any. */
/** @brief Return whether any diagnostic has error severity. */
bool HasErrors() const {
for (const DbcDiagnostic &diagnostic: diagnostics) {
if (diagnostic.severity == DbcDiagnosticSeverity::Error)
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_parser.cpp
* @brief
* @brief DBC file reading and diagnostic collection.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
+3 -2
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_parser.h
* @brief
* @brief DBC file parser and interpretation options.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
@@ -18,7 +18,7 @@
/** Explicit interpretation applied to extended frames in this file. */
struct DbcParserOptions {
bool enableJ1939 = false;
bool enableJ1939 = false; /**< Derive J1939 PGNs from extended CAN IDs. */
};
/**
@@ -40,6 +40,7 @@ struct DbcParserOptions {
*/
class DbcParser {
public:
/** @brief Create a parser with explicit extended-frame interpretation options. */
explicit DbcParser(DbcParserOptions options = {}) : options(options) {}
/**
+2 -7
View File
@@ -160,13 +160,8 @@ namespace {
* Example:
* raw id = 0x80000000 | actual_29_bit_id
*/
if ((rawCanId & 0x80000000U) != 0U) {
isExtended = true;
normalizedCanId = (rawCanId & 0x1FFFFFFFU);
} else {
isExtended = (rawCanId > 0x7FFU);
normalizedCanId = rawCanId;
}
isExtended = (rawCanId & 0x80000000U) != 0U || rawCanId > 0x7FFU;
normalizedCanId = rawCanId & 0x1FFFFFFFU;
}
} // namespace
+40 -22
View File
@@ -9,65 +9,76 @@
#include <string>
#include <unordered_map>
/** @brief Deferred raw value descriptions and their target. */
struct PendingValueDescriptions {
std::uint32_t canId;
bool isExtended;
std::string signalName;
ValueDescriptions descriptions;
std::size_t lineNumber;
std::uint32_t canId; /**< Normalized target frame identifier. */
bool isExtended; /**< Target frame type. */
std::string signalName; /**< Target signal name. */
ValueDescriptions descriptions; /**< Raw integer descriptions to merge. */
std::size_t lineNumber; /**< One-based physical statement line. */
};
/** @brief Deferred frame or signal comment and its source location. */
struct PendingComment {
std::uint32_t canId;
bool isExtended;
bool isSignal;
std::string signalName;
std::string comment;
std::size_t lineNumber;
std::uint32_t canId; /**< Normalized target frame identifier. */
bool isExtended; /**< Target frame type. */
bool isSignal; /**< Whether the target is a signal rather than a frame. */
std::string signalName; /**< Target signal name. */
std::string comment; /**< Unescaped comment text. */
std::size_t lineNumber; /**< One-based physical statement line. */
};
/** Carries deferred validation locations without parsing exception text. */
class DbcDiagnosticError : public std::runtime_error {
public:
/** @brief Create an exception with a structured source diagnostic. */
DbcDiagnosticError(DbcDiagnosticCode code, const std::string &filePath,
std::size_t lineNumber, const std::string &message)
: std::runtime_error(filePath + ":" + std::to_string(lineNumber) + ": " + message),
diagnostic{DbcDiagnosticSeverity::Error, code, filePath, lineNumber, message} {}
DbcDiagnostic diagnostic;
DbcDiagnostic diagnostic; /**< Structured error accompanying the exception message. */
};
/** @brief Attribute statement retained for ordered resolution. */
struct PendingAttribute {
std::string statement;
std::size_t lineNumber;
std::string statement; /**< Complete attribute statement. */
std::size_t lineNumber; /**< One-based physical statement line. */
};
/** Mutable state belonging to one file parse. Handlers do not retain it. */
struct DbcParseContext {
DbcDatabase &database;
FrameInfo *currentFrame = nullptr;
bool enableJ1939 = false;
std::size_t lineNumber = 0U;
std::vector<PendingValueDescriptions> pendingValues = {};
DbcDatabase &database; /**< Parsed metadata under construction. */
FrameInfo *currentFrame = nullptr; /**< Current frame for following signal definitions. */
bool enableJ1939 = false; /**< Explicit interpretation option for extended frames. */
std::size_t lineNumber = 0U; /**< Physical line of the current statement. */
std::vector<PendingValueDescriptions> pendingValues = {}; /**< Unresolved raw value descriptions. */
std::vector<std::vector<std::size_t>> signalLines = {};
std::vector<std::vector<std::size_t>> signalLines = {}; /**< Signal definition lines grouped by frame. */
/** @brief Validate selectors, branch ranges, and signal overlap. */
void ValidateMultiplexing(const std::string &filePath) const;
std::vector<PendingComment> pendingComments = {};
std::vector<PendingComment> pendingComments = {}; /**< Unresolved comments in source order. */
/** @brief Resolve deferred comments after all definitions are known. */
void ResolveComments(const std::string &filePath);
std::vector<PendingAttribute> pendingAttributes = {};
std::vector<PendingAttribute> pendingAttributes = {}; /**< Unresolved attributes in source order. */
/** @brief Resolve definitions, defaults, then assignments; append format warnings. */
void ResolveAttributes(const std::string &filePath, std::vector<DbcDiagnostic> &diagnostics);
/** @brief Resolve deferred raw descriptions against unique targets. */
void ResolveValueDescriptions(const std::string &filePath);
};
/** @brief Interface for processing a registered DBC statement. */
class DbcStatementHandler {
public:
virtual ~DbcStatementHandler() = default;
/** @brief Process a trimmed statement using state belonging to this parse. */
virtual void Handle(const std::string &line, DbcParseContext &context) const = 0;
};
/** @brief Parse and append frame definitions. */
class DbcFrameHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
@@ -76,6 +87,7 @@ private:
static FrameInfo ParseFrameLine(const std::string &line);
};
/** @brief Parse and append signals to the current frame. */
class DbcSignalHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
@@ -84,21 +96,25 @@ private:
static SignalInfo ParseSignalLine(const std::string &line);
};
/** @brief Defer frame and signal comments for target resolution. */
class DbcCommentHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
};
/** @brief Defer typed attribute statements for resolution. */
class DbcAttributeHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
};
/** @brief Defer raw value descriptions for target resolution. */
class DbcValueDescriptionHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
};
/** @brief Reject unsupported extended multiplexing statements. */
class DbcUnsupportedMultiplexingHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
@@ -113,8 +129,10 @@ public:
/** Exact-key dispatch to handlers created once per registration via TypeFactory. */
class DbcStatementDispatcher {
public:
/** @brief Register supported statement handlers through TypeFactory. */
DbcStatementDispatcher();
/** @brief Register or replace a handler and create its reusable instance. */
template<class Handler>
void RegisterType(const std::string &keyword) {
m_factory.registerType<Handler>(keyword);
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_tree_builder.cpp
* @brief
* @brief Construction of frame and signal metadata trees.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @file dbc_tree_builder.h
* @brief
* @brief Metadata tree builder.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
+10 -8
View File
@@ -1,6 +1,6 @@
/**
* @file decode_database.h
* @brief
* @brief Runtime definitions and immutable indexed database.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
@@ -42,7 +42,7 @@ enum class ValueType {
* @brief Runtime-ready signal definition.
*/
struct DecodeSignal {
Multiplexing multiplexing;
Multiplexing multiplexing; /**< Selector role and branch value. */
std::string name; /**< Signal name. */
std::uint32_t startBit = 0U; /**< DBC start bit. */
std::uint32_t length = 0U; /**< Signal length in bits. */
@@ -57,7 +57,7 @@ struct DecodeSignal {
ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */
std::string comment; /**< Comment. */
AttributeMap attributes;
AttributeMap attributes; /**< Effective attributes after defaults and overrides. */
DecodeSignal() = default;
};
@@ -76,12 +76,13 @@ struct DecodeFrame {
std::string comment; /**< Frame comment. */
std::vector<DecodeSignal> signals; /**< Signal definitions. */
AttributeMap attributes;
std::optional<std::string> frameFormat;
std::optional<std::uint32_t> cycleTimeMs;
AttributeMap attributes; /**< Effective attributes after defaults and overrides. */
std::optional<std::string> frameFormat; /**< VFrameFormat label, when declared. */
std::optional<std::uint32_t> cycleTimeMs; /**< GenMsgCycleTime in milliseconds, when declared. */
DecodeFrame() = default;
/** @brief Return the stable normalized frame identifier. */
FrameKey GetKey() const {
return FrameKey(canId, isExtended);
}
@@ -89,8 +90,8 @@ struct DecodeFrame {
/** A PGN lookup rejects multiple definitions rather than choosing by order. */
struct PgnLookupResult {
std::shared_ptr<const DecodeFrame> definition;
bool ambiguous = false;
std::shared_ptr<const DecodeFrame> definition; /**< Owning matched definition; empty if missing or ambiguous. */
bool ambiguous = false; /**< True when multiple definitions share the requested PGN. */
};
/**
@@ -113,6 +114,7 @@ public:
std::shared_ptr<const DecodeFrame> FindFrame(const FrameKey &key) const;
/** Only explicitly marked hasPgn frames participate; ambiguous results have no definition. */
PgnLookupResult FindFrameByPgn(std::uint32_t pgn) const;
/** @brief Return an owning signal handle, or an empty handle for a missing key. */
std::shared_ptr<const DecodeSignal> FindSignal(const SignalKey &key) const;
private:
+11 -4
View File
@@ -13,15 +13,17 @@
* @brief Stable frame identifier: normalized CAN ID and frame type.
*/
struct FrameKey {
std::uint32_t canId = 0U;
bool isExtended = false;
std::uint32_t canId = 0U; /**< Normalized CAN identifier. */
bool isExtended = false; /**< True for extended CAN frames. */
FrameKey() = default;
/** @brief Create a key from a normalized identifier and frame type. */
FrameKey(std::uint32_t id, bool extended)
: canId(id), isExtended(extended) {
}
/** @brief Compare all logical identity fields. */
bool operator==(const FrameKey &other) const {
return (canId == other.canId) && (isExtended == other.isExtended);
}
@@ -31,6 +33,7 @@ struct FrameKey {
* @brief Hasher for frame key.
*/
struct FrameKeyHasher {
/** @brief Hash the logical identity fields. */
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;
@@ -40,20 +43,24 @@ struct FrameKeyHasher {
/** Stable signal identifier within one logical DBC database. */
struct SignalKey {
FrameKey frame;
std::string name;
FrameKey frame; /**< Owning frame identity. */
std::string name; /**< Signal name within the frame. */
SignalKey() = default;
/** @brief Create a signal key from its frame identity and name. */
SignalKey(FrameKey frameKey, std::string signalName)
: frame(frameKey), name(std::move(signalName)) {
}
/** @brief Compare all logical identity fields. */
bool operator==(const SignalKey &other) const {
return frame == other.frame && name == other.name;
}
};
/** @brief Hasher for stable signal keys. */
struct SignalKeyHasher {
/** @brief Hash the logical identity fields. */
std::size_t operator()(const SignalKey &key) const {
return FrameKeyHasher()(key.frame) ^ (std::hash<std::string>()(key.name) << 1U);
}
+4 -4
View File
@@ -1,6 +1,6 @@
/**
* @file frame_info.h
* @brief
* @brief Parsed CAN frame metadata.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
@@ -33,10 +33,10 @@ struct FrameInfo {
std::string comment; /**< Optional frame comment. */
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
std::optional<std::string> frameFormat; /**< VFrameFormat enum label. */
std::optional<std::uint32_t> cycleTimeMs; /**< GenMsgCycleTime; zero means unspecified. */
std::optional<std::string> frameFormat; /**< VFrameFormat label, when declared. */ /**< VFrameFormat enum label. */
std::optional<std::uint32_t> cycleTimeMs; /**< GenMsgCycleTime in milliseconds, when declared. */ /**< GenMsgCycleTime; zero means unspecified. */
AttributeMap attributes; /**< Effective values, including defaults. */
AttributeMap attributes; /**< Effective attributes after defaults and overrides. */ /**< Effective values, including defaults. */
FrameInfo() = default;
};
+3 -2
View File
@@ -4,6 +4,7 @@
#include <cstdint>
/** @brief Unconditional signal, selector, or single-value branch. */
enum class MultiplexRole {
Unconditional,
Selector,
@@ -12,8 +13,8 @@ enum class MultiplexRole {
/** Basic multiplexing: one unsigned selector per frame, one raw value per branch. */
struct Multiplexing {
MultiplexRole role = MultiplexRole::Unconditional;
std::uint64_t value = 0U;
MultiplexRole role = MultiplexRole::Unconditional; /**< Signal participation in multiplexing. */
std::uint64_t value = 0U; /**< Required raw selector value; used only by branches. */
};
#endif
+3 -3
View File
@@ -1,6 +1,6 @@
/**
* @file signal_info.h
* @brief
* @brief Parsed CAN signal metadata.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
@@ -23,7 +23,7 @@
* @brief Describes one signal inside a DBC frame.
*/
struct SignalInfo {
Multiplexing multiplexing;
Multiplexing multiplexing; /**< Selector role and branch value. */
std::string name; /**< Signal name. */
std::uint32_t startBit = 0U; /**< Start bit in DBC notation. */
std::uint32_t length = 0U; /**< Signal length in bits. */
@@ -38,7 +38,7 @@ struct SignalInfo {
ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */
std::string comment; /**< Optional signal comment. */
AttributeMap attributes; /**< Effective values, including defaults. */
AttributeMap attributes; /**< Effective attributes after defaults and overrides. */ /**< Effective values, including defaults. */
SignalInfo() = default;
};
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @file tree_node.cpp
* @brief
* @brief Tree node ownership and navigation.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @file tree_node.h
* @brief
* @brief Qt-independent metadata tree nodes.
*
* Created: 2026-03-13
* Author: Deeaitch (Dim. Himro)
+22 -20
View File
@@ -19,9 +19,9 @@
* - Re-registering the same id overwrites the previous entry ("last wins").
* - ClassId must be hashable (std::hash<ClassId> specialization must exist).
*
* @tparam ClassId -Unique class identifier type (e.g. std::string, int, enum).
* @tparam BaseClass -Base type for all created objects.
* @tparam Arg - Constructor arguments forwarded to registered derived types (by value).
* @tparam ClassId Unique class identifier type (e.g. std::string, int, enum).
* @tparam BaseClass Base type for all created objects.
* @tparam Args Constructor arguments forwarded to registered derived types (by value).
*
* Usage example:
* @code
@@ -58,20 +58,22 @@
template<class ClassId, class BaseClass, class... Args>
class TypeFactory {
public:
/** @brief Shared ownership handle returned for created instances. */
using BasePtr = std::shared_ptr<BaseClass>;
/** @brief Registered creator function signature. */
using CreatorFn = BasePtr (*)(Args...);
TypeFactory() = default;
/**
* @brief Registers a Derived type under the given id.
*
* Requirements (checked at compile-time):
* - Derived must inherit from BaseClass
* - Derived must be constructible from Args...
*
* Re-registering the same id overwrites the previous entry ("last wins").
*/
* @brief Registers a Derived type under the given id.
*
* Requirements (checked at compile-time):
* - Derived must inherit from BaseClass
* - Derived must be constructible from Args...
*
* Re-registering the same id overwrites the previous entry ("last wins").
*/
template<class Derived>
void registerType(const ClassId &id) {
static_assert(std::is_base_of_v<BaseClass, Derived>,
@@ -83,30 +85,30 @@ public:
}
/**
* @brief Returns creator function for the given id.
* @throws std::out_of_range when id is not registered.
*/
* @brief Returns creator function for the given id.
* @throws std::out_of_range when id is not registered.
*/
[[nodiscard]] CreatorFn creator(const ClassId &id) const {
return classes.at(id);
}
/**
* @brief Creates an instance for the given id using provided arguments.
* @throws std::out_of_range when id is not registered.
*/
* @brief Creates an instance for the given id using provided arguments.
* @throws std::out_of_range when id is not registered.
*/
[[nodiscard]] BasePtr create(const ClassId &id, Args... args) const {
return creator(id)(std::move(args)...);
}
/**
* @brief Checks whether an id is registered.
*/
* @brief Checks whether an id is registered.
*/
[[nodiscard]] bool contains(const ClassId &id) const {
return classes.find(id) != classes.end();
}
protected:
// Protected by design: derived factories may extend/inspect the registry.
/** @brief Registered creators; accessible to derived factories. */
std::unordered_map<ClassId, CreatorFn> classes;
private: