Closes #7
This commit is contained in:
1 parent
8d82b13dad
commit
b91cf4aae5
21 files changed
+1165
-1146
No files matched your search
@@ -0,0 +1,65 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: None
|
||||||
|
AlignOperands: Align
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: Always
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Always
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
ColumnLimit: 0
|
||||||
|
CompactNamespaces: false
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 4
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MaxEmptyLinesToKeep: 2
|
||||||
|
NamespaceIndentation: All
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 0
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
+1
-1
@@ -20,7 +20,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 the DBC file. */
|
std::vector<FrameInfo> frames; /**< All frames found in the DBC file. */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DBC_DATABASE_H */
|
#endif /* DBC_DATABASE_H */
|
||||||
+35
-35
@@ -11,46 +11,46 @@
|
|||||||
|
|
||||||
#include "dbc_decode_builder.h"
|
#include "dbc_decode_builder.h"
|
||||||
|
|
||||||
DecodeDatabase DbcDecodeBuilder::Build (const DbcDatabase &source) const {
|
DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const {
|
||||||
DecodeDatabase result;
|
DecodeDatabase result;
|
||||||
|
|
||||||
for (std::size_t frameIndex = 0U; frameIndex < source.frames.size(); ++frameIndex) {
|
for (std::size_t frameIndex = 0U; frameIndex < source.frames.size(); ++frameIndex) {
|
||||||
const FrameInfo &sourceFrame = source.frames[frameIndex];
|
const FrameInfo &sourceFrame = source.frames[frameIndex];
|
||||||
DecodeFrame targetFrame;
|
DecodeFrame targetFrame;
|
||||||
|
|
||||||
targetFrame.name = sourceFrame.name;
|
targetFrame.name = sourceFrame.name;
|
||||||
targetFrame.canId = sourceFrame.canId;
|
targetFrame.canId = sourceFrame.canId;
|
||||||
targetFrame.isExtended = sourceFrame.isExtended;
|
targetFrame.isExtended = sourceFrame.isExtended;
|
||||||
targetFrame.dlc = sourceFrame.dlc;
|
targetFrame.dlc = sourceFrame.dlc;
|
||||||
targetFrame.pgn = sourceFrame.pgn;
|
targetFrame.pgn = sourceFrame.pgn;
|
||||||
targetFrame.hasPgn = sourceFrame.hasPgn;
|
targetFrame.hasPgn = sourceFrame.hasPgn;
|
||||||
targetFrame.transmitter = sourceFrame.transmitter;
|
targetFrame.transmitter = sourceFrame.transmitter;
|
||||||
targetFrame.comment = sourceFrame.comment;
|
targetFrame.comment = sourceFrame.comment;
|
||||||
|
|
||||||
for (std::size_t signalIndex = 0U; signalIndex < sourceFrame.signals.size(); ++signalIndex) {
|
for (std::size_t signalIndex = 0U; signalIndex < sourceFrame.signals.size(); ++signalIndex) {
|
||||||
const SignalInfo &sourceSignal = sourceFrame.signals[signalIndex];
|
const SignalInfo &sourceSignal = sourceFrame.signals[signalIndex];
|
||||||
DecodeSignal targetSignal;
|
DecodeSignal targetSignal;
|
||||||
|
|
||||||
targetSignal.name = sourceSignal.name;
|
targetSignal.name = sourceSignal.name;
|
||||||
targetSignal.startBit = sourceSignal.startBit;
|
targetSignal.startBit = sourceSignal.startBit;
|
||||||
targetSignal.length = sourceSignal.length;
|
targetSignal.length = sourceSignal.length;
|
||||||
targetSignal.byteOrder = sourceSignal.isLittleEndian ? ByteOrder::Intel : ByteOrder::Motorola;
|
targetSignal.byteOrder = sourceSignal.isLittleEndian ? ByteOrder::Intel : ByteOrder::Motorola;
|
||||||
targetSignal.valueType = sourceSignal.isSigned ? ValueType::Signed : ValueType::Unsigned;
|
targetSignal.valueType = sourceSignal.isSigned ? ValueType::Signed : ValueType::Unsigned;
|
||||||
targetSignal.factor = sourceSignal.factor;
|
targetSignal.factor = sourceSignal.factor;
|
||||||
targetSignal.offset = sourceSignal.offset;
|
targetSignal.offset = sourceSignal.offset;
|
||||||
targetSignal.minimum = sourceSignal.minimum;
|
targetSignal.minimum = sourceSignal.minimum;
|
||||||
targetSignal.maximum = sourceSignal.maximum;
|
targetSignal.maximum = sourceSignal.maximum;
|
||||||
targetSignal.unit = sourceSignal.unit;
|
targetSignal.unit = sourceSignal.unit;
|
||||||
targetSignal.receivers = sourceSignal.receivers;
|
targetSignal.receivers = sourceSignal.receivers;
|
||||||
targetSignal.comment = sourceSignal.comment;
|
targetSignal.comment = sourceSignal.comment;
|
||||||
|
|
||||||
targetFrame.signals.push_back (targetSignal);
|
targetFrame.signals.push_back(targetSignal);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.frames.push_back(targetFrame);
|
||||||
|
result.frameIndexByKey[FrameKey(targetFrame.canId, targetFrame.isExtended)] =
|
||||||
|
result.frames.size() - 1U;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.frames.push_back (targetFrame);
|
return result;
|
||||||
result.frameIndexByKey[FrameKey (targetFrame.canId, targetFrame.isExtended)] =
|
|
||||||
result.frames.size() - 1U;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
@@ -19,13 +19,13 @@
|
|||||||
* @brief Converts parsed DBC data into runtime decode database.
|
* @brief Converts parsed DBC data into runtime decode database.
|
||||||
*/
|
*/
|
||||||
class DbcDecodeBuilder {
|
class DbcDecodeBuilder {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Build runtime decode database.
|
* @brief Build runtime decode database.
|
||||||
* @param source Parsed DBC database.
|
* @param source Parsed DBC database.
|
||||||
* @return Runtime-ready decode database.
|
* @return Runtime-ready decode database.
|
||||||
*/
|
*/
|
||||||
DecodeDatabase Build (const DbcDatabase &source) const;
|
DecodeDatabase Build(const DbcDatabase &source) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DBC_DECODE_BUILDER_H */
|
#endif /* DBC_DECODE_BUILDER_H */
|
||||||
+101
-99
@@ -11,104 +11,105 @@
|
|||||||
|
|
||||||
#include "dbc_decoder.h"
|
#include "dbc_decoder.h"
|
||||||
|
|
||||||
const DecodeFrame *DbcDecoder::FindFrame (const DecodeDatabase &database,
|
const DecodeFrame *DbcDecoder::FindFrame(const DecodeDatabase &database,
|
||||||
std::uint32_t canId,
|
std::uint32_t canId,
|
||||||
bool isExtended) const {
|
bool isExtended) const {
|
||||||
const FrameKey key (canId, isExtended);
|
const FrameKey key(canId, isExtended);
|
||||||
const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it =
|
const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it =
|
||||||
database.frameIndexByKey.find (key);
|
database.frameIndexByKey.find(key);
|
||||||
|
|
||||||
if (it == database.frameIndexByKey.end())
|
if (it == database.frameIndexByKey.end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const std::size_t index = it->second;
|
const std::size_t index = it->second;
|
||||||
if (index >= database.frames.size())
|
if (index >= database.frames.size())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return &database.frames[index];
|
return &database.frames[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
DecodedFrameValue DbcDecoder::Decode (const DecodeDatabase &database,
|
DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
|
||||||
const RawCanFrame &frame) const {
|
const RawCanFrame &frame) const {
|
||||||
DecodedFrameValue result;
|
DecodedFrameValue result;
|
||||||
const DecodeFrame *definition = FindFrame (database, frame.canId, frame.isExtended);
|
const DecodeFrame *definition = FindFrame(database, frame.canId, frame.isExtended);
|
||||||
|
|
||||||
if (definition == nullptr)
|
if (definition == nullptr)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
result.definition = definition;
|
result.definition = definition;
|
||||||
result.valid = true;
|
result.valid = true;
|
||||||
|
|
||||||
for (std::size_t signalIndex = 0U; signalIndex < definition->signals.size(); ++signalIndex) {
|
for (std::size_t signalIndex = 0U; signalIndex < definition->signals.size(); ++signalIndex) {
|
||||||
const DecodeSignal &signal = definition->signals[signalIndex];
|
const DecodeSignal &signal = definition->signals[signalIndex];
|
||||||
DecodedSignalValue decoded;
|
DecodedSignalValue decoded;
|
||||||
std::uint64_t unsignedValue = 0U;
|
std::uint64_t unsignedValue = 0U;
|
||||||
|
|
||||||
decoded.definition = &signal;
|
decoded.definition = &signal;
|
||||||
|
|
||||||
if (!ExtractUnsigned (frame.data, signal, unsignedValue)) {
|
if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
|
||||||
decoded.valid = false;
|
decoded.valid = false;
|
||||||
result.signals.push_back (decoded);
|
result.signals.push_back(decoded);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal.valueType == ValueType::Signed)
|
||||||
|
decoded.rawValue = SignExtend(unsignedValue, signal.length);
|
||||||
|
else
|
||||||
|
decoded.rawValue = unsignedValue;
|
||||||
|
|
||||||
|
decoded.physicalValue =
|
||||||
|
std::visit([&signal](auto rawValue) {
|
||||||
|
return (static_cast<double>(rawValue) * signal.factor) + signal.offset;
|
||||||
|
},
|
||||||
|
decoded.rawValue);
|
||||||
|
decoded.valid = true;
|
||||||
|
|
||||||
|
result.signals.push_back(decoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signal.valueType == ValueType::Signed)
|
return result;
|
||||||
decoded.rawValue = SignExtend (unsignedValue, signal.length);
|
|
||||||
else
|
|
||||||
decoded.rawValue = unsignedValue;
|
|
||||||
|
|
||||||
decoded.physicalValue =
|
|
||||||
std::visit ([&signal] (auto rawValue) {
|
|
||||||
return (static_cast<double> (rawValue) * signal.factor) + signal.offset;
|
|
||||||
}, decoded.rawValue);
|
|
||||||
decoded.valid = true;
|
|
||||||
|
|
||||||
result.signals.push_back (decoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DbcDecoder::ExtractUnsigned (const std::vector<std::uint8_t> &data,
|
bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data,
|
||||||
const DecodeSignal &signal,
|
const DecodeSignal &signal,
|
||||||
std::uint64_t &value) {
|
std::uint64_t &value) {
|
||||||
if ((signal.length == 0U) || (signal.length > 64U))
|
if ((signal.length == 0U) || (signal.length > 64U))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (signal.byteOrder == ByteOrder::Intel)
|
if (signal.byteOrder == ByteOrder::Intel)
|
||||||
return ExtractIntel (data, signal.startBit, signal.length, value);
|
return ExtractIntel(data, signal.startBit, signal.length, value);
|
||||||
|
|
||||||
return ExtractMotorola (data, signal.startBit, signal.length, value);
|
return ExtractMotorola(data, signal.startBit, signal.length, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DbcDecoder::ExtractIntel (const std::vector<std::uint8_t> &data,
|
bool DbcDecoder::ExtractIntel(const std::vector<std::uint8_t> &data,
|
||||||
std::uint32_t startBit,
|
std::uint32_t startBit,
|
||||||
std::uint32_t length,
|
std::uint32_t length,
|
||||||
std::uint64_t &value) {
|
std::uint64_t &value) {
|
||||||
value = 0U;
|
value = 0U;
|
||||||
|
|
||||||
for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
|
for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
|
||||||
const std::uint32_t absoluteBit = startBit + bitIndex;
|
const std::uint32_t absoluteBit = startBit + bitIndex;
|
||||||
const std::uint32_t byteIndex = absoluteBit / 8U;
|
const std::uint32_t byteIndex = absoluteBit / 8U;
|
||||||
const std::uint32_t bitInByte = absoluteBit % 8U;
|
const std::uint32_t bitInByte = absoluteBit % 8U;
|
||||||
|
|
||||||
if (byteIndex >= data.size())
|
if (byteIndex >= data.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const std::uint64_t bitValue =
|
const std::uint64_t bitValue =
|
||||||
(static_cast<std::uint64_t> ((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
|
(static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
|
||||||
|
|
||||||
value |= bitValue;
|
value |= bitValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DbcDecoder::ExtractMotorola (const std::vector<std::uint8_t> &data,
|
bool DbcDecoder::ExtractMotorola(const std::vector<std::uint8_t> &data,
|
||||||
std::uint32_t startBit,
|
std::uint32_t startBit,
|
||||||
std::uint32_t length,
|
std::uint32_t length,
|
||||||
std::uint64_t &value) {
|
std::uint64_t &value) {
|
||||||
/*
|
/*
|
||||||
* DBC Motorola bit numbering:
|
* DBC Motorola bit numbering:
|
||||||
* - startBit points to the most significant bit of the signal
|
* - startBit points to the most significant bit of the signal
|
||||||
* - inside a byte, bit numbering goes 7..0
|
* - inside a byte, bit numbering goes 7..0
|
||||||
@@ -116,40 +117,41 @@ bool DbcDecoder::ExtractMotorola (const std::vector<std::uint8_t> &data,
|
|||||||
* Example: a signal starting at 7 follows 7..0, 15..8, 23..16.
|
* Example: a signal starting at 7 follows 7..0, 15..8, 23..16.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
value = 0U;
|
value = 0U;
|
||||||
|
|
||||||
std::size_t byteIndex = startBit / 8U;
|
std::size_t byteIndex = startBit / 8U;
|
||||||
std::uint32_t bitInByte = startBit % 8U;
|
std::uint32_t bitInByte = startBit % 8U;
|
||||||
|
|
||||||
for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
|
for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
|
||||||
if (byteIndex >= data.size())
|
if (byteIndex >= data.size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
value <<= 1U;
|
value <<= 1U;
|
||||||
value |= static_cast<std::uint64_t> ((data[byteIndex] >> bitInByte) & 0x01U);
|
value |= static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U);
|
||||||
|
|
||||||
if (bitInByte == 0U) {
|
if (bitInByte == 0U) {
|
||||||
++byteIndex;
|
++byteIndex;
|
||||||
bitInByte = 7U;
|
bitInByte = 7U;
|
||||||
} else
|
} else
|
||||||
--bitInByte;
|
--bitInByte;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::int64_t DbcDecoder::SignExtend (std::uint64_t value, std::uint32_t bitLength) {
|
std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
|
||||||
if ((bitLength == 0U) || (bitLength > 64U))
|
if ((bitLength == 0U) || (bitLength > 64U))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
const std::uint64_t signMask = std::uint64_t (1U) << (bitLength - 1U);
|
const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
|
||||||
const std::uint64_t valueMask = (bitLength == 64U)
|
const std::uint64_t valueMask = (bitLength == 64U)
|
||||||
? ~std::uint64_t (0U) : (std::uint64_t (1U) << bitLength) - 1U;
|
? ~std::uint64_t(0U)
|
||||||
value &= valueMask;
|
: (std::uint64_t(1U) << bitLength) - 1U;
|
||||||
|
value &= valueMask;
|
||||||
|
|
||||||
if ((value & signMask) == 0U)
|
if ((value & signMask) == 0U)
|
||||||
return static_cast<std::int64_t> (value);
|
return static_cast<std::int64_t>(value);
|
||||||
|
|
||||||
// The complemented magnitude fits int64_t, including for INT64_MIN.
|
// The complemented magnitude fits int64_t, including for INT64_MIN.
|
||||||
return -1 - static_cast<std::int64_t> ((~value) & valueMask);
|
return -1 - static_cast<std::int64_t>((~value) & valueMask);
|
||||||
}
|
}
|
||||||
+39
-46
@@ -12,9 +12,9 @@
|
|||||||
#ifndef DBC_DECODER_H
|
#ifndef DBC_DECODER_H
|
||||||
#define DBC_DECODER_H
|
#define DBC_DECODER_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "decode_database.h"
|
#include "decode_database.h"
|
||||||
|
|
||||||
@@ -22,54 +22,47 @@
|
|||||||
* @brief Raw CAN frame used for runtime or trace decoding.
|
* @brief Raw CAN frame used for runtime or trace decoding.
|
||||||
*/
|
*/
|
||||||
struct RawCanFrame {
|
struct RawCanFrame {
|
||||||
std::uint32_t canId; /**< Normalized CAN ID. */
|
std::uint32_t canId; /**< Normalized CAN ID. */
|
||||||
bool isExtended; /**< true for extended frame. */
|
bool isExtended; /**< true for extended frame. */
|
||||||
std::vector<std::uint8_t> data; /**< Payload bytes. */
|
std::vector<std::uint8_t> data; /**< Payload bytes. */
|
||||||
|
|
||||||
RawCanFrame()
|
RawCanFrame()
|
||||||
: canId (0U)
|
: canId(0U), isExtended(false), data() {
|
||||||
, isExtended (false)
|
}
|
||||||
, data() {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief One decoded signal value.
|
* @brief One decoded signal value.
|
||||||
*/
|
*/
|
||||||
struct DecodedSignalValue {
|
struct DecodedSignalValue {
|
||||||
const DecodeSignal *definition; /**< Signal definition. */
|
const DecodeSignal *definition; /**< Signal definition. */
|
||||||
std::variant<std::int64_t, std::uint64_t> rawValue; /**< Exact signed or unsigned raw value. */
|
std::variant<std::int64_t, std::uint64_t> rawValue; /**< Exact signed or unsigned raw value. */
|
||||||
double physicalValue; /**< Converted physical value. */
|
double physicalValue; /**< Converted physical value. */
|
||||||
bool valid; /**< true if decoding succeeded. */
|
bool valid; /**< true if decoding succeeded. */
|
||||||
|
|
||||||
DecodedSignalValue()
|
DecodedSignalValue()
|
||||||
: definition (nullptr)
|
: definition(nullptr), rawValue(std::uint64_t(0U)), physicalValue(0.0), valid(false) {
|
||||||
, rawValue (std::uint64_t (0U))
|
}
|
||||||
, physicalValue (0.0)
|
|
||||||
, valid (false) {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Fully decoded frame.
|
* @brief Fully decoded frame.
|
||||||
*/
|
*/
|
||||||
struct DecodedFrameValue {
|
struct DecodedFrameValue {
|
||||||
const DecodeFrame *definition; /**< Frame definition. */
|
const DecodeFrame *definition; /**< Frame definition. */
|
||||||
std::vector<DecodedSignalValue> signals; /**< Decoded signal values. */
|
std::vector<DecodedSignalValue> signals; /**< Decoded signal values. */
|
||||||
bool valid; /**< true if frame was matched. */
|
bool valid; /**< true if frame was matched. */
|
||||||
|
|
||||||
DecodedFrameValue()
|
DecodedFrameValue()
|
||||||
: definition (nullptr)
|
: definition(nullptr), signals(), valid(false) {
|
||||||
, signals()
|
}
|
||||||
, valid (false) {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Runtime CAN decoder using prebuilt decode database.
|
* @brief Runtime CAN decoder using prebuilt decode database.
|
||||||
*/
|
*/
|
||||||
class DbcDecoder {
|
class DbcDecoder {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Find frame definition by CAN ID.
|
* @brief Find frame definition by CAN ID.
|
||||||
* @param database Runtime decode database.
|
* @param database Runtime decode database.
|
||||||
@@ -77,9 +70,9 @@ class DbcDecoder {
|
|||||||
* @param isExtended true for extended frame.
|
* @param isExtended true for extended frame.
|
||||||
* @return Pointer to frame definition or nullptr.
|
* @return Pointer to frame definition or nullptr.
|
||||||
*/
|
*/
|
||||||
const DecodeFrame *FindFrame (const DecodeDatabase &database,
|
const DecodeFrame *FindFrame(const DecodeDatabase &database,
|
||||||
std::uint32_t canId,
|
std::uint32_t canId,
|
||||||
bool isExtended) const;
|
bool isExtended) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Decode one raw CAN frame.
|
* @brief Decode one raw CAN frame.
|
||||||
@@ -87,25 +80,25 @@ class DbcDecoder {
|
|||||||
* @param frame Raw CAN frame.
|
* @param frame Raw CAN frame.
|
||||||
* @return Decoded frame value.
|
* @return Decoded frame value.
|
||||||
*/
|
*/
|
||||||
DecodedFrameValue Decode (const DecodeDatabase &database,
|
DecodedFrameValue Decode(const DecodeDatabase &database,
|
||||||
const RawCanFrame &frame) const;
|
const RawCanFrame &frame) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool ExtractUnsigned (const std::vector<std::uint8_t> &data,
|
static bool ExtractUnsigned(const std::vector<std::uint8_t> &data,
|
||||||
const DecodeSignal &signal,
|
const DecodeSignal &signal,
|
||||||
std::uint64_t &value);
|
std::uint64_t &value);
|
||||||
|
|
||||||
static bool ExtractIntel (const std::vector<std::uint8_t> &data,
|
static bool ExtractIntel(const std::vector<std::uint8_t> &data,
|
||||||
std::uint32_t startBit,
|
std::uint32_t startBit,
|
||||||
std::uint32_t length,
|
std::uint32_t length,
|
||||||
std::uint64_t &value);
|
std::uint64_t &value);
|
||||||
|
|
||||||
static bool ExtractMotorola (const std::vector<std::uint8_t> &data,
|
static bool ExtractMotorola(const std::vector<std::uint8_t> &data,
|
||||||
std::uint32_t startBit,
|
std::uint32_t startBit,
|
||||||
std::uint32_t length,
|
std::uint32_t length,
|
||||||
std::uint64_t &value);
|
std::uint64_t &value);
|
||||||
|
|
||||||
static std::int64_t SignExtend (std::uint64_t value, std::uint32_t bitLength);
|
static std::int64_t SignExtend(std::uint64_t value, std::uint32_t bitLength);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DBC_DECODER_H */
|
#endif /* DBC_DECODER_H */
|
||||||
+41
-41
@@ -12,59 +12,59 @@
|
|||||||
#include "dbc_parser.h"
|
#include "dbc_parser.h"
|
||||||
#include "dbc_statement_handler.h"
|
#include "dbc_statement_handler.h"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
/**
|
/**
|
||||||
* @brief Remove leading and trailing spaces.
|
* @brief Remove leading and trailing spaces.
|
||||||
* @param text Input text.
|
* @param text Input text.
|
||||||
* @return Trimmed text.
|
* @return Trimmed text.
|
||||||
*/
|
*/
|
||||||
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()) &&
|
while ((begin < text.size()) &&
|
||||||
std::isspace (static_cast<unsigned char> (text[begin])))
|
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) &&
|
while ((end > begin) &&
|
||||||
std::isspace (static_cast<unsigned char> (text[end - 1U])))
|
std::isspace(static_cast<unsigned char>(text[end - 1U])))
|
||||||
--end;
|
--end;
|
||||||
|
|
||||||
return text.substr (begin, end - begin);
|
return text.substr(begin, end - begin);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DbcDatabase DbcParser::ParseFile (const std::string &filePath) const {
|
|
||||||
std::ifstream input (filePath.c_str());
|
|
||||||
if (!input.is_open())
|
|
||||||
throw std::runtime_error ("Failed to open DBC file: " + filePath);
|
|
||||||
|
|
||||||
DbcDatabase database;
|
|
||||||
DbcParseContext context {database, nullptr};
|
|
||||||
const DbcStatementDispatcher dispatcher;
|
|
||||||
|
|
||||||
std::size_t lineNumber = 0U;
|
|
||||||
std::string line;
|
|
||||||
while (std::getline (input, line)) {
|
|
||||||
++lineNumber;
|
|
||||||
line = Trim (line);
|
|
||||||
if (line.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
dispatcher.Dispatch (line, context);
|
|
||||||
} catch (const std::exception &error) {
|
|
||||||
throw std::runtime_error (filePath + ":" + std::to_string (lineNumber) +
|
|
||||||
": " + error.what());
|
|
||||||
}
|
}
|
||||||
}
|
}// namespace
|
||||||
|
|
||||||
return database;
|
DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
|
||||||
|
std::ifstream input(filePath.c_str());
|
||||||
|
if (!input.is_open())
|
||||||
|
throw std::runtime_error("Failed to open DBC file: " + filePath);
|
||||||
|
|
||||||
|
DbcDatabase database;
|
||||||
|
DbcParseContext context{database, nullptr};
|
||||||
|
const DbcStatementDispatcher dispatcher;
|
||||||
|
|
||||||
|
std::size_t lineNumber = 0U;
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(input, line)) {
|
||||||
|
++lineNumber;
|
||||||
|
line = Trim(line);
|
||||||
|
if (line.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatcher.Dispatch(line, context);
|
||||||
|
} catch (const std::exception &error) {
|
||||||
|
throw std::runtime_error(filePath + ":" + std::to_string(lineNumber) +
|
||||||
|
": " + error.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return database;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DbcParser::Trim (const std::string &text) {
|
std::string DbcParser::Trim(const std::string &text) {
|
||||||
return TrimText (text);
|
return TrimText(text);
|
||||||
}
|
}
|
||||||
+4
-4
@@ -31,7 +31,7 @@
|
|||||||
* - value tables
|
* - value tables
|
||||||
*/
|
*/
|
||||||
class DbcParser {
|
class DbcParser {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Parse DBC file.
|
* @brief Parse DBC file.
|
||||||
* @param filePath Path to input file.
|
* @param filePath Path to input file.
|
||||||
@@ -39,10 +39,10 @@ class DbcParser {
|
|||||||
* @throws std::runtime_error on file or parse errors.
|
* @throws std::runtime_error on file or parse errors.
|
||||||
* Line parse errors include the file path and one-based line number.
|
* Line parse errors include the file path and one-based line number.
|
||||||
*/
|
*/
|
||||||
DbcDatabase ParseFile (const std::string &filePath) const;
|
DbcDatabase ParseFile(const std::string &filePath) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::string Trim (const std::string &text);
|
static std::string Trim(const std::string &text);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DBC_PARSER_H */
|
#endif /* DBC_PARSER_H */
|
||||||
+242
-242
@@ -1,323 +1,323 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
#include "dbc_statement_handler.h"
|
#include "dbc_statement_handler.h"
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
#include <cmath>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <cmath>
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::uint32_t ParseDecimal (const std::string &text,
|
std::uint32_t ParseDecimal(const std::string &text,
|
||||||
std::uint32_t maximum,
|
std::uint32_t maximum,
|
||||||
const char *field) {
|
const char *field) {
|
||||||
std::uint32_t value = 0U;
|
std::uint32_t value = 0U;
|
||||||
const auto parsed = std::from_chars (text.data(), text.data() + text.size(), value);
|
const auto parsed = std::from_chars(text.data(), text.data() + text.size(), value);
|
||||||
if (text.empty() || parsed.ec != std::errc() ||
|
if (text.empty() || parsed.ec != std::errc() ||
|
||||||
parsed.ptr != text.data() + text.size() || value > maximum)
|
parsed.ptr != text.data() + text.size() || value > maximum)
|
||||||
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text);
|
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove leading and trailing spaces.
|
* @brief Remove leading and trailing spaces.
|
||||||
* @param text Input text.
|
* @param text Input text.
|
||||||
* @return Trimmed text.
|
* @return Trimmed text.
|
||||||
*/
|
*/
|
||||||
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()) &&
|
while ((begin < text.size()) &&
|
||||||
std::isspace (static_cast<unsigned char> (text[begin])))
|
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) &&
|
while ((end > begin) &&
|
||||||
std::isspace (static_cast<unsigned char> (text[end - 1U])))
|
std::isspace(static_cast<unsigned char>(text[end - 1U])))
|
||||||
--end;
|
--end;
|
||||||
|
|
||||||
return text.substr (begin, end - begin);
|
return text.substr(begin, end - begin);
|
||||||
}
|
}
|
||||||
FrameInfo *FindFrameById (DbcDatabase &, std::uint32_t, bool);
|
FrameInfo *FindFrameById(DbcDatabase &, std::uint32_t, bool);
|
||||||
SignalInfo *FindSignalByName (FrameInfo &, const std::string &);
|
SignalInfo *FindSignalByName(FrameInfo &, const std::string &);
|
||||||
}
|
}// namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::vector<std::string> SplitReceivers (const std::string &text) {
|
std::vector<std::string> SplitReceivers(const std::string &text) {
|
||||||
std::vector<std::string> receivers;
|
std::vector<std::string> receivers;
|
||||||
std::string token;
|
std::string token;
|
||||||
std::istringstream stream (text);
|
std::istringstream stream(text);
|
||||||
|
|
||||||
while (std::getline (stream, token, ',')) {
|
while (std::getline(stream, token, ',')) {
|
||||||
token = TrimText (token);
|
token = TrimText(token);
|
||||||
if (!token.empty())
|
if (!token.empty())
|
||||||
receivers.push_back (token);
|
receivers.push_back(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return receivers;
|
return receivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NormalizeCanId (std::uint32_t rawCanId,
|
void NormalizeCanId(std::uint32_t rawCanId,
|
||||||
std::uint32_t &normalizedCanId,
|
std::uint32_t &normalizedCanId,
|
||||||
bool &isExtended) {
|
bool &isExtended) {
|
||||||
/*
|
/*
|
||||||
* DBC commonly stores extended identifiers with bit 31 set.
|
* DBC commonly stores extended identifiers with bit 31 set.
|
||||||
* Example:
|
* Example:
|
||||||
* raw id = 0x80000000 | actual_29_bit_id
|
* raw id = 0x80000000 | actual_29_bit_id
|
||||||
*/
|
*/
|
||||||
if ((rawCanId & 0x80000000U) != 0U) {
|
if ((rawCanId & 0x80000000U) != 0U) {
|
||||||
isExtended = true;
|
isExtended = true;
|
||||||
normalizedCanId = (rawCanId & 0x1FFFFFFFU);
|
normalizedCanId = (rawCanId & 0x1FFFFFFFU);
|
||||||
} else {
|
} else {
|
||||||
isExtended = (rawCanId > 0x7FFU);
|
isExtended = (rawCanId > 0x7FFU);
|
||||||
normalizedCanId = rawCanId;
|
normalizedCanId = rawCanId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t TryExtractPgn (std::uint32_t canId, bool isExtended, bool &hasPgn) {
|
std::uint32_t TryExtractPgn(std::uint32_t canId, bool isExtended, bool &hasPgn) {
|
||||||
hasPgn = false;
|
hasPgn = false;
|
||||||
|
|
||||||
if (!isExtended)
|
if (!isExtended)
|
||||||
return 0U;
|
return 0U;
|
||||||
|
|
||||||
if ((canId & 0x1FFFFFFFU) != canId)
|
if ((canId & 0x1FFFFFFFU) != canId)
|
||||||
return 0U;
|
return 0U;
|
||||||
|
|
||||||
const std::uint32_t pf = (canId >> 16U) & 0xFFU;
|
const std::uint32_t pf = (canId >> 16U) & 0xFFU;
|
||||||
const std::uint32_t ps = (canId >> 8U) & 0xFFU;
|
const std::uint32_t ps = (canId >> 8U) & 0xFFU;
|
||||||
const std::uint32_t dp = (canId >> 24U) & 0x01U;
|
const std::uint32_t dp = (canId >> 24U) & 0x01U;
|
||||||
|
|
||||||
std::uint32_t pgn = 0U;
|
std::uint32_t pgn = 0U;
|
||||||
|
|
||||||
if (pf < 240U)
|
if (pf < 240U)
|
||||||
pgn = (dp << 16U) | (pf << 8U);
|
pgn = (dp << 16U) | (pf << 8U);
|
||||||
else
|
else
|
||||||
pgn = (dp << 16U) | (pf << 8U) | ps;
|
pgn = (dp << 16U) | (pf << 8U) | ps;
|
||||||
|
|
||||||
hasPgn = true;
|
hasPgn = true;
|
||||||
return pgn;
|
return pgn;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}// namespace
|
||||||
|
|
||||||
FrameInfo DbcFrameHandler::ParseFrameLine (const std::string &line) {
|
FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
|
||||||
/*
|
/*
|
||||||
* Example:
|
* Example:
|
||||||
* BO_ 256 EngineData: 8 EEC1
|
* BO_ 256 EngineData: 8 EEC1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::istringstream stream (line);
|
std::istringstream stream(line);
|
||||||
std::string keyword;
|
std::string keyword;
|
||||||
std::string idText;
|
std::string idText;
|
||||||
if (!(stream >> keyword >> idText) || keyword != "BO_")
|
if (!(stream >> keyword >> idText) || keyword != "BO_")
|
||||||
throw std::runtime_error ("Missing CAN ID in frame definition.");
|
throw std::runtime_error("Missing CAN ID in frame definition.");
|
||||||
|
|
||||||
const std::uint32_t rawCanId = ParseDecimal (
|
const std::uint32_t rawCanId = ParseDecimal(
|
||||||
idText, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
|
idText, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
|
||||||
// Only bit 31 may accompany the actual 29-bit identifier.
|
// Only bit 31 may accompany the actual 29-bit identifier.
|
||||||
if ((rawCanId & 0x60000000U) != 0U)
|
if ((rawCanId & 0x60000000U) != 0U)
|
||||||
throw std::runtime_error ("CAN ID exceeds the 29-bit identifier range.");
|
throw std::runtime_error("CAN ID exceeds the 29-bit identifier range.");
|
||||||
|
|
||||||
std::string nameText;
|
std::string nameText;
|
||||||
if (!std::getline (stream, nameText, ':') || stream.eof())
|
if (!std::getline(stream, nameText, ':') || stream.eof())
|
||||||
throw std::runtime_error ("Missing ':' in frame definition.");
|
throw std::runtime_error("Missing ':' in frame definition.");
|
||||||
std::istringstream nameStream (nameText);
|
std::istringstream nameStream(nameText);
|
||||||
FrameInfo frame;
|
FrameInfo frame;
|
||||||
std::string extra;
|
std::string extra;
|
||||||
if (!(nameStream >> frame.name) || (nameStream >> extra))
|
if (!(nameStream >> frame.name) || (nameStream >> extra))
|
||||||
throw std::runtime_error ("Missing or invalid frame name.");
|
throw std::runtime_error("Missing or invalid frame name.");
|
||||||
|
|
||||||
std::string lengthText;
|
std::string lengthText;
|
||||||
if (!(stream >> lengthText))
|
if (!(stream >> lengthText))
|
||||||
throw std::runtime_error ("Missing payload length in frame definition.");
|
throw std::runtime_error("Missing payload length in frame definition.");
|
||||||
frame.dlc = static_cast<std::uint8_t> (ParseDecimal (
|
frame.dlc = static_cast<std::uint8_t>(ParseDecimal(
|
||||||
lengthText, std::numeric_limits<std::uint8_t>::max(), "payload length"));
|
lengthText, std::numeric_limits<std::uint8_t>::max(), "payload length"));
|
||||||
if (!(stream >> frame.transmitter))
|
if (!(stream >> frame.transmitter))
|
||||||
throw std::runtime_error ("Missing transmitter in frame definition.");
|
throw std::runtime_error("Missing transmitter in frame definition.");
|
||||||
if (stream >> extra)
|
if (stream >> extra)
|
||||||
throw std::runtime_error ("Unexpected trailing field in frame definition: " + extra);
|
throw std::runtime_error("Unexpected trailing field in frame definition: " + extra);
|
||||||
|
|
||||||
NormalizeCanId (rawCanId, frame.canId, frame.isExtended);
|
NormalizeCanId(rawCanId, frame.canId, frame.isExtended);
|
||||||
frame.pgn = TryExtractPgn (frame.canId, frame.isExtended, frame.hasPgn);
|
frame.pgn = TryExtractPgn(frame.canId, frame.isExtended, frame.hasPgn);
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalInfo DbcSignalHandler::ParseSignalLine (const std::string &line) {
|
SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
|
||||||
// Match the complete statement before converting any numeric fields.
|
// Match the complete statement before converting any numeric fields.
|
||||||
static const std::regex syntax (
|
static const std::regex syntax(
|
||||||
R"dbc(^SG_[ \t]+([^:]+):[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*"([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
|
R"dbc(^SG_[ \t]+([^:]+):[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*"([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
|
||||||
std::smatch fields;
|
std::smatch fields;
|
||||||
if (!std::regex_match (line, fields, syntax))
|
if (!std::regex_match(line, fields, syntax))
|
||||||
throw std::runtime_error ("Malformed or unsupported SG_ signal definition.");
|
throw std::runtime_error("Malformed or unsupported SG_ signal definition.");
|
||||||
|
|
||||||
SignalInfo signal;
|
SignalInfo signal;
|
||||||
signal.name = TrimText (fields[1].str());
|
signal.name = TrimText(fields[1].str());
|
||||||
if (signal.name.empty() || signal.name.find_first_of (" \t\r\n") != std::string::npos)
|
if (signal.name.empty() || signal.name.find_first_of(" \t\r\n") != std::string::npos)
|
||||||
throw std::runtime_error ("Invalid signal name or unsupported multiplexing syntax.");
|
throw std::runtime_error("Invalid signal name or unsupported multiplexing syntax.");
|
||||||
|
|
||||||
signal.startBit = ParseDecimal (fields[2].str(),
|
signal.startBit = ParseDecimal(fields[2].str(),
|
||||||
std::numeric_limits<std::uint32_t>::max(), "signal start bit");
|
std::numeric_limits<std::uint32_t>::max(), "signal start bit");
|
||||||
signal.length = ParseDecimal (fields[3].str(), 64U, "signal length");
|
signal.length = ParseDecimal(fields[3].str(), 64U, "signal length");
|
||||||
if (signal.length == 0U)
|
if (signal.length == 0U)
|
||||||
throw std::runtime_error ("Signal length must be within 1..64 bits.");
|
throw std::runtime_error("Signal length must be within 1..64 bits.");
|
||||||
signal.isLittleEndian = fields[4].str() == "1";
|
signal.isLittleEndian = fields[4].str() == "1";
|
||||||
signal.isSigned = fields[5].str() == "-";
|
signal.isSigned = fields[5].str() == "-";
|
||||||
|
|
||||||
const auto parsePhysical = [] (const std::string &text, const char *field) {
|
const auto parsePhysical = [](const std::string &text, const char *field) {
|
||||||
static const std::regex number (
|
static const std::regex number(
|
||||||
R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
|
R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
|
||||||
if (!std::regex_match (text, number))
|
if (!std::regex_match(text, number))
|
||||||
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text);
|
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
||||||
std::size_t consumed = 0U;
|
std::size_t consumed = 0U;
|
||||||
double value;
|
double value;
|
||||||
try {
|
try {
|
||||||
value = std::stod (text, &consumed);
|
value = std::stod(text, &consumed);
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text);
|
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
||||||
}
|
}
|
||||||
if (consumed != text.size() || !std::isfinite (value))
|
if (consumed != text.size() || !std::isfinite(value))
|
||||||
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text);
|
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
signal.factor = parsePhysical (fields[6].str(), "signal factor");
|
signal.factor = parsePhysical(fields[6].str(), "signal factor");
|
||||||
signal.offset = parsePhysical (fields[7].str(), "signal offset");
|
signal.offset = parsePhysical(fields[7].str(), "signal offset");
|
||||||
signal.minimum = parsePhysical (fields[8].str(), "signal minimum");
|
signal.minimum = parsePhysical(fields[8].str(), "signal minimum");
|
||||||
signal.maximum = parsePhysical (fields[9].str(), "signal maximum");
|
signal.maximum = parsePhysical(fields[9].str(), "signal maximum");
|
||||||
if (signal.minimum > signal.maximum)
|
if (signal.minimum > signal.maximum)
|
||||||
throw std::runtime_error ("Signal minimum exceeds maximum.");
|
throw std::runtime_error("Signal minimum exceeds maximum.");
|
||||||
signal.unit = fields[10].str();
|
signal.unit = fields[10].str();
|
||||||
signal.receivers = SplitReceivers (fields[11].str());
|
signal.receivers = SplitReceivers(fields[11].str());
|
||||||
return signal;
|
return signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbcCommentHandler::ParseCommentLine (const std::string &line, DbcDatabase &database) {
|
void DbcCommentHandler::ParseCommentLine(const std::string &line, DbcDatabase &database) {
|
||||||
/*
|
/*
|
||||||
* Examples:
|
* Examples:
|
||||||
* CM_ BO_ 256 "Frame comment";
|
* CM_ BO_ 256 "Frame comment";
|
||||||
* CM_ SG_ 256 EngineSpeed "Signal comment";
|
* CM_ SG_ 256 EngineSpeed "Signal comment";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::istringstream stream (line);
|
std::istringstream stream(line);
|
||||||
std::string token;
|
std::string token;
|
||||||
stream >> token;
|
stream >> token;
|
||||||
|
|
||||||
if (token != "CM_")
|
if (token != "CM_")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
stream >> token;
|
stream >> token;
|
||||||
|
|
||||||
if (token == "BO_") {
|
if (token == "BO_") {
|
||||||
std::uint32_t rawCanId = 0U;
|
std::uint32_t rawCanId = 0U;
|
||||||
std::uint32_t canId = 0U;
|
std::uint32_t canId = 0U;
|
||||||
bool isExtended = false;
|
bool isExtended = false;
|
||||||
|
|
||||||
stream >> rawCanId;
|
stream >> rawCanId;
|
||||||
NormalizeCanId (rawCanId, canId, isExtended);
|
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('"');
|
||||||
|
|
||||||
if ((quoteBegin == std::string::npos) ||
|
if ((quoteBegin == std::string::npos) ||
|
||||||
(quoteEnd == std::string::npos) ||
|
(quoteEnd == std::string::npos) ||
|
||||||
(quoteEnd <= quoteBegin))
|
(quoteEnd <= quoteBegin))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FrameInfo *frame = FindFrameById (database, canId, isExtended);
|
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 rawCanId = 0U;
|
||||||
std::uint32_t canId = 0U;
|
std::uint32_t canId = 0U;
|
||||||
bool isExtended = false;
|
bool isExtended = false;
|
||||||
std::string signalName;
|
std::string signalName;
|
||||||
|
|
||||||
stream >> rawCanId;
|
stream >> rawCanId;
|
||||||
stream >> signalName;
|
stream >> signalName;
|
||||||
|
|
||||||
NormalizeCanId (rawCanId, canId, isExtended);
|
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('"');
|
||||||
|
|
||||||
if ((quoteBegin == std::string::npos) ||
|
if ((quoteBegin == std::string::npos) ||
|
||||||
(quoteEnd == std::string::npos) ||
|
(quoteEnd == std::string::npos) ||
|
||||||
(quoteEnd <= quoteBegin))
|
(quoteEnd <= quoteBegin))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FrameInfo *frame = FindFrameById (database, canId, isExtended);
|
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)
|
||||||
signal->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
|
signal->comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
FrameInfo *FindFrameById (DbcDatabase &database,
|
FrameInfo *FindFrameById(DbcDatabase &database,
|
||||||
std::uint32_t canId,
|
std::uint32_t canId,
|
||||||
bool isExtended) {
|
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))
|
(database.frames[index].isExtended == isExtended))
|
||||||
return &database.frames[index];
|
return &database.frames[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalInfo *FindSignalByName(FrameInfo &frame, const std::string &signalName) {
|
||||||
|
for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
|
||||||
|
if (frame.signals[index].name == signalName)
|
||||||
|
return &frame.signals[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
void DbcFrameHandler::Handle(const std::string &line, DbcParseContext &context) const {
|
||||||
|
context.database.frames.push_back(ParseFrameLine(line));
|
||||||
|
context.currentFrame = &context.database.frames.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalInfo *FindSignalByName (FrameInfo &frame, const std::string &signalName) {
|
void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context) const {
|
||||||
for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
|
if (context.currentFrame == nullptr)
|
||||||
if (frame.signals[index].name == signalName)
|
throw std::runtime_error("Signal found before any frame definition.");
|
||||||
return &frame.signals[index];
|
SignalInfo signal = ParseSignalLine(line);
|
||||||
}
|
const std::uint64_t payloadBits = static_cast<std::uint64_t>(context.currentFrame->dlc) * 8U;
|
||||||
|
std::uint64_t bit = signal.startBit;
|
||||||
return nullptr;
|
for (std::uint32_t index = 0U; index < signal.length; ++index) {
|
||||||
}
|
if (bit >= payloadBits)
|
||||||
|
throw std::runtime_error("Signal exceeds the frame payload length.");
|
||||||
|
if (signal.isLittleEndian)
|
||||||
|
++bit;
|
||||||
|
else
|
||||||
|
bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
|
||||||
|
}
|
||||||
|
context.currentFrame->signals.push_back(std::move(signal));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DbcFrameHandler::Handle (const std::string &line, DbcParseContext &context) const {
|
void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context) const {
|
||||||
context.database.frames.push_back (ParseFrameLine (line));
|
if (line.compare(0U, 4U, "CM_ ") == 0)
|
||||||
context.currentFrame = &context.database.frames.back();
|
ParseCommentLine(line, context.database);
|
||||||
}
|
|
||||||
|
|
||||||
void DbcSignalHandler::Handle (const std::string &line, DbcParseContext &context) const {
|
|
||||||
if (context.currentFrame == nullptr)
|
|
||||||
throw std::runtime_error ("Signal found before any frame definition.");
|
|
||||||
SignalInfo signal = ParseSignalLine (line);
|
|
||||||
const std::uint64_t payloadBits = static_cast<std::uint64_t> (context.currentFrame->dlc) * 8U;
|
|
||||||
std::uint64_t bit = signal.startBit;
|
|
||||||
for (std::uint32_t index = 0U; index < signal.length; ++index) {
|
|
||||||
if (bit >= payloadBits)
|
|
||||||
throw std::runtime_error ("Signal exceeds the frame payload length.");
|
|
||||||
if (signal.isLittleEndian)
|
|
||||||
++bit;
|
|
||||||
else
|
|
||||||
bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
|
|
||||||
}
|
|
||||||
context.currentFrame->signals.push_back (std::move (signal));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DbcCommentHandler::Handle (const std::string &line, DbcParseContext &context) const {
|
|
||||||
if (line.compare (0U, 4U, "CM_ ") == 0)
|
|
||||||
ParseCommentLine (line, context.database);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DbcStatementDispatcher::DbcStatementDispatcher() {
|
DbcStatementDispatcher::DbcStatementDispatcher() {
|
||||||
RegisterType<DbcFrameHandler> ("BO_");
|
RegisterType<DbcFrameHandler>("BO_");
|
||||||
RegisterType<DbcSignalHandler> ("SG_");
|
RegisterType<DbcSignalHandler>("SG_");
|
||||||
RegisterType<DbcCommentHandler> ("CM_");
|
RegisterType<DbcCommentHandler>("CM_");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DbcStatementDispatcher::Dispatch (const std::string &line,
|
bool DbcStatementDispatcher::Dispatch(const std::string &line,
|
||||||
DbcParseContext &context) const {
|
DbcParseContext &context) const {
|
||||||
std::istringstream stream (line);
|
std::istringstream stream(line);
|
||||||
std::string keyword;
|
std::string keyword;
|
||||||
stream >> keyword;
|
stream >> keyword;
|
||||||
const auto handler = m_handlers.find (keyword);
|
const auto handler = m_handlers.find(keyword);
|
||||||
if (handler == m_handlers.end())
|
if (handler == m_handlers.end())
|
||||||
return false;
|
return false;
|
||||||
handler->second->Handle (line, context);
|
handler->second->Handle(line, context);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
+27
-24
@@ -9,54 +9,57 @@
|
|||||||
|
|
||||||
/** Mutable state belonging to one file parse. Handlers do not retain it. */
|
/** Mutable state belonging to one file parse. Handlers do not retain it. */
|
||||||
struct DbcParseContext {
|
struct DbcParseContext {
|
||||||
DbcDatabase &database;
|
DbcDatabase &database;
|
||||||
FrameInfo *currentFrame = nullptr;
|
FrameInfo *currentFrame = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DbcStatementHandler {
|
class DbcStatementHandler {
|
||||||
public:
|
public:
|
||||||
virtual ~DbcStatementHandler() = default;
|
virtual ~DbcStatementHandler() = default;
|
||||||
virtual void Handle (const std::string &line, DbcParseContext &context) const = 0;
|
virtual void Handle(const std::string &line, DbcParseContext &context) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DbcFrameHandler : public DbcStatementHandler {
|
class DbcFrameHandler : public DbcStatementHandler {
|
||||||
public:
|
public:
|
||||||
void Handle (const std::string &line, DbcParseContext &context) const override;
|
void Handle(const std::string &line, DbcParseContext &context) const override;
|
||||||
private:
|
|
||||||
static FrameInfo ParseFrameLine (const std::string &line);
|
private:
|
||||||
|
static FrameInfo ParseFrameLine(const std::string &line);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DbcSignalHandler : public DbcStatementHandler {
|
class DbcSignalHandler : public DbcStatementHandler {
|
||||||
public:
|
public:
|
||||||
void Handle (const std::string &line, DbcParseContext &context) const override;
|
void Handle(const std::string &line, DbcParseContext &context) const override;
|
||||||
private:
|
|
||||||
static SignalInfo ParseSignalLine (const std::string &line);
|
private:
|
||||||
|
static SignalInfo ParseSignalLine(const std::string &line);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DbcCommentHandler : public DbcStatementHandler {
|
class DbcCommentHandler : public DbcStatementHandler {
|
||||||
public:
|
public:
|
||||||
void Handle (const std::string &line, DbcParseContext &context) const override;
|
void Handle(const std::string &line, DbcParseContext &context) const override;
|
||||||
private:
|
|
||||||
static void ParseCommentLine (const std::string &line, DbcDatabase &database);
|
private:
|
||||||
|
static void ParseCommentLine(const std::string &line, DbcDatabase &database);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Exact-key dispatch to handlers created once per registration via TypeFactory. */
|
/** Exact-key dispatch to handlers created once per registration via TypeFactory. */
|
||||||
class DbcStatementDispatcher {
|
class DbcStatementDispatcher {
|
||||||
public:
|
public:
|
||||||
DbcStatementDispatcher();
|
DbcStatementDispatcher();
|
||||||
|
|
||||||
template <class Handler>
|
template<class Handler>
|
||||||
void RegisterType (const std::string &keyword) {
|
void RegisterType(const std::string &keyword) {
|
||||||
m_factory.registerType<Handler> (keyword);
|
m_factory.registerType<Handler>(keyword);
|
||||||
m_handlers.insert_or_assign (keyword, m_factory.create (keyword));
|
m_handlers.insert_or_assign(keyword, m_factory.create(keyword));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns false for an unregistered keyword; input must be trimmed. */
|
/** Returns false for an unregistered keyword; input must be trimmed. */
|
||||||
bool Dispatch (const std::string &line, DbcParseContext &context) const;
|
bool Dispatch(const std::string &line, DbcParseContext &context) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TypeFactory<std::string, DbcStatementHandler> m_factory;
|
TypeFactory<std::string, DbcStatementHandler> m_factory;
|
||||||
std::unordered_map<std::string, std::shared_ptr<DbcStatementHandler> > m_handlers;
|
std::unordered_map<std::string, std::shared_ptr<DbcStatementHandler>> m_handlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DBC_STATEMENT_HANDLER_H */
|
#endif /* DBC_STATEMENT_HANDLER_H */
|
||||||
+13
-13
@@ -11,21 +11,21 @@
|
|||||||
|
|
||||||
#include "dbc_tree_builder.h"
|
#include "dbc_tree_builder.h"
|
||||||
|
|
||||||
std::unique_ptr<TreeNode> DbcTreeBuilder::Build (const DbcDatabase &database) const {
|
std::unique_ptr<TreeNode> DbcTreeBuilder::Build(const DbcDatabase &database) const {
|
||||||
std::unique_ptr<TreeNode> root (new TreeNode());
|
std::unique_ptr<TreeNode> root(new TreeNode());
|
||||||
|
|
||||||
for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
|
for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
|
||||||
const FrameInfo &frame = database.frames[frameIndex];
|
const FrameInfo &frame = database.frames[frameIndex];
|
||||||
std::unique_ptr<TreeNode> frameNode (new TreeNode (frame));
|
std::unique_ptr<TreeNode> frameNode(new TreeNode(frame));
|
||||||
|
|
||||||
for (std::size_t signalIndex = 0U; signalIndex < frame.signals.size(); ++signalIndex) {
|
for (std::size_t signalIndex = 0U; signalIndex < frame.signals.size(); ++signalIndex) {
|
||||||
const SignalInfo &signal = frame.signals[signalIndex];
|
const SignalInfo &signal = frame.signals[signalIndex];
|
||||||
std::unique_ptr<TreeNode> signalNode (new TreeNode (signal));
|
std::unique_ptr<TreeNode> signalNode(new TreeNode(signal));
|
||||||
frameNode->AddChild (std::move (signalNode));
|
frameNode->AddChild(std::move(signalNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
root->AddChild(std::move(frameNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
root->AddChild (std::move (frameNode));
|
return root;
|
||||||
}
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
}
|
||||||
+2
-2
@@ -21,13 +21,13 @@
|
|||||||
* @brief Builds a simple tree from parsed DBC database.
|
* @brief Builds a simple tree from parsed DBC database.
|
||||||
*/
|
*/
|
||||||
class DbcTreeBuilder {
|
class DbcTreeBuilder {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Build tree representation of parsed DBC data.
|
* @brief Build tree representation of parsed DBC data.
|
||||||
* @param database Parsed database.
|
* @param database Parsed database.
|
||||||
* @return Root node of the tree.
|
* @return Root node of the tree.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<TreeNode> Build (const DbcDatabase &database) const;
|
std::unique_ptr<TreeNode> Build(const DbcDatabase &database) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DBC_TREE_BUILDER_H */
|
#endif /* DBC_TREE_BUILDER_H */
|
||||||
+52
-73
@@ -12,126 +12,105 @@
|
|||||||
#ifndef DECODE_DATABASE_H
|
#ifndef DECODE_DATABASE_H
|
||||||
#define DECODE_DATABASE_H
|
#define DECODE_DATABASE_H
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signal byte order used for runtime decoding.
|
* @brief Signal byte order used for runtime decoding.
|
||||||
*/
|
*/
|
||||||
enum class ByteOrder {
|
enum class ByteOrder {
|
||||||
Intel,
|
Intel,
|
||||||
Motorola
|
Motorola
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signal numeric type.
|
* @brief Signal numeric type.
|
||||||
*/
|
*/
|
||||||
enum class ValueType {
|
enum class ValueType {
|
||||||
Unsigned,
|
Unsigned,
|
||||||
Signed
|
Signed
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Runtime-ready signal definition.
|
* @brief Runtime-ready signal definition.
|
||||||
*/
|
*/
|
||||||
struct DecodeSignal {
|
struct DecodeSignal {
|
||||||
std::string name; /**< Signal name. */
|
std::string name; /**< Signal name. */
|
||||||
std::uint32_t startBit; /**< DBC start bit. */
|
std::uint32_t startBit; /**< DBC start bit. */
|
||||||
std::uint32_t length; /**< Signal length in bits. */
|
std::uint32_t length; /**< Signal length in bits. */
|
||||||
ByteOrder byteOrder; /**< Intel or Motorola. */
|
ByteOrder byteOrder; /**< Intel or Motorola. */
|
||||||
ValueType valueType; /**< Signed or unsigned. */
|
ValueType valueType; /**< Signed or unsigned. */
|
||||||
double factor; /**< Scaling factor. */
|
double factor; /**< Scaling factor. */
|
||||||
double offset; /**< Physical offset. */
|
double offset; /**< Physical offset. */
|
||||||
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. */
|
std::vector<std::string> receivers; /**< Receivers. */
|
||||||
std::string comment; /**< Comment. */
|
std::string comment; /**< Comment. */
|
||||||
|
|
||||||
DecodeSignal()
|
DecodeSignal()
|
||||||
: name()
|
: 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() {
|
||||||
, 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.
|
* @brief Runtime-ready frame definition.
|
||||||
*/
|
*/
|
||||||
struct DecodeFrame {
|
struct DecodeFrame {
|
||||||
std::string name; /**< Frame name. */
|
std::string name; /**< Frame name. */
|
||||||
std::uint32_t canId; /**< Normalized CAN ID. */
|
std::uint32_t canId; /**< Normalized CAN ID. */
|
||||||
bool isExtended; /**< true for extended frame. */
|
bool isExtended; /**< true for extended frame. */
|
||||||
std::uint8_t dlc; /**< Payload length. */
|
std::uint8_t dlc; /**< Payload length. */
|
||||||
std::uint32_t pgn; /**< PGN if available. */
|
std::uint32_t pgn; /**< PGN if available. */
|
||||||
bool hasPgn; /**< true if PGN is valid. */
|
bool hasPgn; /**< true if PGN is valid. */
|
||||||
std::string transmitter; /**< Transmitter ECU. */
|
std::string transmitter; /**< Transmitter ECU. */
|
||||||
std::string comment; /**< Frame comment. */
|
std::string comment; /**< Frame comment. */
|
||||||
std::vector<DecodeSignal> signals; /**< Signal definitions. */
|
std::vector<DecodeSignal> signals; /**< Signal definitions. */
|
||||||
|
|
||||||
DecodeFrame()
|
DecodeFrame()
|
||||||
: name()
|
: name(), canId(0U), isExtended(false), dlc(0U), pgn(0U), hasPgn(false), transmitter(), comment(), signals() {
|
||||||
, canId (0U)
|
}
|
||||||
, isExtended (false)
|
|
||||||
, dlc (0U)
|
|
||||||
, pgn (0U)
|
|
||||||
, hasPgn (false)
|
|
||||||
, transmitter()
|
|
||||||
, comment()
|
|
||||||
, signals() {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Key for fast frame lookup.
|
* @brief Key for fast frame lookup.
|
||||||
*/
|
*/
|
||||||
struct FrameKey {
|
struct FrameKey {
|
||||||
std::uint32_t canId;
|
std::uint32_t canId;
|
||||||
bool isExtended;
|
bool isExtended;
|
||||||
|
|
||||||
FrameKey()
|
FrameKey()
|
||||||
: canId (0U)
|
: canId(0U), isExtended(false) {
|
||||||
, isExtended (false) {
|
}
|
||||||
}
|
|
||||||
|
|
||||||
FrameKey (std::uint32_t id, bool extended)
|
FrameKey(std::uint32_t id, bool extended)
|
||||||
: canId (id)
|
: canId(id), isExtended(extended) {
|
||||||
, isExtended (extended) {
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool operator== (const FrameKey &other) const {
|
bool operator==(const FrameKey &other) const {
|
||||||
return (canId == other.canId) && (isExtended == other.isExtended);
|
return (canId == other.canId) && (isExtended == other.isExtended);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Hasher for frame key.
|
* @brief Hasher for frame key.
|
||||||
*/
|
*/
|
||||||
struct FrameKeyHasher {
|
struct FrameKeyHasher {
|
||||||
std::size_t operator() (const FrameKey &key) const {
|
std::size_t operator()(const FrameKey &key) const {
|
||||||
const std::size_t a = static_cast<std::size_t> (key.canId);
|
const std::size_t a = static_cast<std::size_t>(key.canId);
|
||||||
const std::size_t b = key.isExtended ? 1U : 0U;
|
const std::size_t b = key.isExtended ? 1U : 0U;
|
||||||
return (a * 1315423911U) ^ b;
|
return (a * 1315423911U) ^ b;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Runtime decode database with fast lookup by CAN ID.
|
* @brief Runtime decode database with fast lookup by CAN ID.
|
||||||
*/
|
*/
|
||||||
struct DecodeDatabase {
|
struct DecodeDatabase {
|
||||||
std::vector<DecodeFrame> frames;
|
std::vector<DecodeFrame> frames;
|
||||||
std::unordered_map<FrameKey, std::size_t, FrameKeyHasher> frameIndexByKey;
|
std::unordered_map<FrameKey, std::size_t, FrameKeyHasher> frameIndexByKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DECODE_DATABASE_H */
|
#endif /* DECODE_DATABASE_H */
|
||||||
+13
-21
@@ -12,9 +12,9 @@
|
|||||||
#ifndef FRAME_INFO_H
|
#ifndef FRAME_INFO_H
|
||||||
#define FRAME_INFO_H
|
#define FRAME_INFO_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "signal_info.h"
|
#include "signal_info.h"
|
||||||
|
|
||||||
@@ -22,27 +22,19 @@
|
|||||||
* @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; /**< Normalized CAN identifier. */
|
std::uint32_t canId; /**< Normalized CAN identifier. */
|
||||||
bool isExtended; /**< true for extended frame. */
|
bool isExtended; /**< true for extended frame. */
|
||||||
std::uint32_t pgn; /**< J1939 PGN if applicable. */
|
std::uint32_t pgn; /**< J1939 PGN if applicable. */
|
||||||
bool hasPgn; /**< true if PGN was derived. */
|
bool hasPgn; /**< true if PGN was derived. */
|
||||||
std::uint8_t dlc; /**< Frame payload length. */
|
std::uint8_t dlc; /**< Frame payload length. */
|
||||||
std::string transmitter; /**< Transmitter ECU name. */
|
std::string transmitter; /**< Transmitter ECU name. */
|
||||||
std::string comment; /**< Optional frame comment. */
|
std::string comment; /**< Optional frame comment. */
|
||||||
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
|
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
|
||||||
|
|
||||||
FrameInfo()
|
FrameInfo()
|
||||||
: name()
|
: name(), canId(0U), isExtended(false), pgn(0U), hasPgn(false), dlc(0U), transmitter(), comment(), signals() {
|
||||||
, canId (0U)
|
}
|
||||||
, isExtended (false)
|
|
||||||
, pgn (0U)
|
|
||||||
, hasPgn (false)
|
|
||||||
, dlc (0U)
|
|
||||||
, transmitter()
|
|
||||||
, comment()
|
|
||||||
, signals() {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* FRAME_INFO_H */
|
#endif /* FRAME_INFO_H */
|
||||||
@@ -9,129 +9,129 @@
|
|||||||
* See LICENSE file in the project root for full license text.
|
* See LICENSE file in the project root for full license text.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iostream>
|
#include <cstddef>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "dbc_parser.h"
|
|
||||||
#include "dbc_tree_builder.h"
|
|
||||||
#include "dbc_decode_builder.h"
|
#include "dbc_decode_builder.h"
|
||||||
#include "dbc_decoder.h"
|
#include "dbc_decoder.h"
|
||||||
|
#include "dbc_parser.h"
|
||||||
|
#include "dbc_tree_builder.h"
|
||||||
|
|
||||||
static void PrintTree (const TreeNode *node, int indent) {
|
static void PrintTree(const TreeNode *node, int indent) {
|
||||||
if (node == nullptr)
|
if (node == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < indent; ++i)
|
for (int i = 0; i < indent; ++i)
|
||||||
std::cout << " ";
|
std::cout << " ";
|
||||||
|
|
||||||
switch (node->GetType()) {
|
switch (node->GetType()) {
|
||||||
case NodeType::Root:
|
case NodeType::Root:
|
||||||
std::cout << "[root] " << node->GetName() << "\n";
|
std::cout << "[root] " << node->GetName() << "\n";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeType::Frame: {
|
case NodeType::Frame: {
|
||||||
const FrameInfo *frame = node->GetFrame();
|
const FrameInfo *frame = node->GetFrame();
|
||||||
std::cout << "[frame] " << node->GetName();
|
std::cout << "[frame] " << node->GetName();
|
||||||
|
|
||||||
if (frame != nullptr) {
|
if (frame != nullptr) {
|
||||||
std::cout << " id=0x" << std::hex << frame->canId << std::dec
|
std::cout << " id=0x" << std::hex << frame->canId << std::dec
|
||||||
<< " ext=" << (frame->isExtended ? "yes" : "no")
|
<< " ext=" << (frame->isExtended ? "yes" : "no")
|
||||||
<< " dlc=" << static_cast<unsigned int> (frame->dlc);
|
<< " dlc=" << static_cast<unsigned int>(frame->dlc);
|
||||||
|
|
||||||
if (frame->hasPgn)
|
if (frame->hasPgn)
|
||||||
std::cout << " pgn=" << frame->pgn;
|
std::cout << " pgn=" << frame->pgn;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "\n";
|
std::cout << "\n";
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NodeType::Signal: {
|
||||||
|
const SignalInfo *signal = node->GetSignal();
|
||||||
|
std::cout << "[signal] " << node->GetName();
|
||||||
|
|
||||||
|
if (signal != nullptr) {
|
||||||
|
std::cout << " start=" << signal->startBit
|
||||||
|
<< " len=" << signal->length
|
||||||
|
<< " unit=" << signal->unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
std::cout << "[unknown]\n";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case NodeType::Signal: {
|
for (std::size_t i = 0U; i < node->GetChildCount(); ++i)
|
||||||
const SignalInfo *signal = node->GetSignal();
|
PrintTree(node->GetChild(i), indent + 1);
|
||||||
std::cout << "[signal] " << node->GetName();
|
}
|
||||||
|
|
||||||
if (signal != nullptr) {
|
static void PrintDecodedFrame(const DecodedFrameValue &decoded) {
|
||||||
std::cout << " start=" << signal->startBit
|
if (!decoded.valid || (decoded.definition == nullptr)) {
|
||||||
<< " len=" << signal->length
|
std::cout << "No frame definition found.\n";
|
||||||
<< " unit=" << signal->unit;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "\n";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
std::cout << "Decoded frame: " << decoded.definition->name << "\n";
|
||||||
std::cout << "[unknown]\n";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::size_t i = 0U; i < node->GetChildCount(); ++i)
|
for (std::size_t index = 0U; index < decoded.signals.size(); ++index) {
|
||||||
PrintTree (node->GetChild (i), indent + 1);
|
const DecodedSignalValue &signal = decoded.signals[index];
|
||||||
|
|
||||||
|
if ((signal.definition == nullptr) || !signal.valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::cout << " " << signal.definition->name
|
||||||
|
<< " raw=";
|
||||||
|
std::visit([](auto rawValue) { std::cout << rawValue; }, signal.rawValue);
|
||||||
|
std::cout << " physical=" << signal.physicalValue
|
||||||
|
<< " " << signal.definition->unit
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintDecodedFrame (const DecodedFrameValue &decoded) {
|
int main(int argc, char *argv[]) {
|
||||||
if (!decoded.valid || (decoded.definition == nullptr)) {
|
if (argc < 2) {
|
||||||
std::cout << "No frame definition found.\n";
|
std::cerr << "Usage: dbc_demo <file.dbc>\n";
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Decoded frame: " << decoded.definition->name << "\n";
|
try {
|
||||||
|
DbcParser parser;
|
||||||
|
DbcDatabase database = parser.ParseFile(argv[1]);
|
||||||
|
|
||||||
for (std::size_t index = 0U; index < decoded.signals.size(); ++index) {
|
DbcTreeBuilder treeBuilder;
|
||||||
const DecodedSignalValue &signal = decoded.signals[index];
|
std::unique_ptr<TreeNode> root = treeBuilder.Build(database);
|
||||||
|
|
||||||
if ((signal.definition == nullptr) || !signal.valid)
|
std::cout << "=== Parsed tree ===\n";
|
||||||
continue;
|
PrintTree(root.get(), 0);
|
||||||
|
|
||||||
std::cout << " " << signal.definition->name
|
DbcDecodeBuilder decodeBuilder;
|
||||||
<< " raw=";
|
DecodeDatabase decodeDatabase = decodeBuilder.Build(database);
|
||||||
std::visit ([] (auto rawValue) { std::cout << rawValue; }, signal.rawValue);
|
|
||||||
std::cout << " physical=" << signal.physicalValue
|
|
||||||
<< " " << signal.definition->unit
|
|
||||||
<< "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char *argv[]) {
|
/*
|
||||||
if (argc < 2) {
|
|
||||||
std::cerr << "Usage: dbc_demo <file.dbc>\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
DbcParser parser;
|
|
||||||
DbcDatabase database = parser.ParseFile (argv[1]);
|
|
||||||
|
|
||||||
DbcTreeBuilder treeBuilder;
|
|
||||||
std::unique_ptr<TreeNode> root = treeBuilder.Build (database);
|
|
||||||
|
|
||||||
std::cout << "=== Parsed tree ===\n";
|
|
||||||
PrintTree (root.get(), 0);
|
|
||||||
|
|
||||||
DbcDecodeBuilder decodeBuilder;
|
|
||||||
DecodeDatabase decodeDatabase = decodeBuilder.Build (database);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Example raw frame.
|
* Example raw frame.
|
||||||
* Replace with live CAN frame or trace record later.
|
* Replace with live CAN frame or trace record later.
|
||||||
*/
|
*/
|
||||||
RawCanFrame rawFrame;
|
RawCanFrame rawFrame;
|
||||||
rawFrame.canId = decodeDatabase.frames.empty() ? 0U : decodeDatabase.frames[0].canId;
|
rawFrame.canId = decodeDatabase.frames.empty() ? 0U : decodeDatabase.frames[0].canId;
|
||||||
rawFrame.isExtended = decodeDatabase.frames.empty() ? false : decodeDatabase.frames[0].isExtended;
|
rawFrame.isExtended = decodeDatabase.frames.empty() ? false : decodeDatabase.frames[0].isExtended;
|
||||||
rawFrame.data.resize (8U, 0U);
|
rawFrame.data.resize(8U, 0U);
|
||||||
|
|
||||||
DbcDecoder decoder;
|
DbcDecoder decoder;
|
||||||
DecodedFrameValue decoded = decoder.Decode (decodeDatabase, rawFrame);
|
DecodedFrameValue decoded = decoder.Decode(decodeDatabase, rawFrame);
|
||||||
|
|
||||||
std::cout << "\n=== Decoded frame ===\n";
|
std::cout << "\n=== Decoded frame ===\n";
|
||||||
PrintDecodedFrame (decoded);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
+16
-27
@@ -12,41 +12,30 @@
|
|||||||
#ifndef SIGNAL_INFO_H
|
#ifndef SIGNAL_INFO_H
|
||||||
#define SIGNAL_INFO_H
|
#define SIGNAL_INFO_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Describes one signal inside a DBC frame.
|
* @brief Describes one signal inside a DBC frame.
|
||||||
*/
|
*/
|
||||||
struct SignalInfo {
|
struct SignalInfo {
|
||||||
std::string name; /**< Signal name. */
|
std::string name; /**< Signal name. */
|
||||||
std::uint32_t startBit; /**< Start bit in DBC notation. */
|
std::uint32_t startBit; /**< Start bit in DBC notation. */
|
||||||
std::uint32_t length; /**< Signal length in bits. */
|
std::uint32_t length; /**< Signal length in bits. */
|
||||||
bool isLittleEndian; /**< true for Intel, false for Motorola. */
|
bool isLittleEndian; /**< true for Intel, false for Motorola. */
|
||||||
bool isSigned; /**< true if signal is signed. */
|
bool isSigned; /**< true if signal is signed. */
|
||||||
double factor; /**< Scaling factor. */
|
double factor; /**< Scaling factor. */
|
||||||
double offset; /**< Physical offset. */
|
double offset; /**< Physical offset. */
|
||||||
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; /**< Signal receivers. */
|
std::vector<std::string> receivers; /**< Signal receivers. */
|
||||||
std::string comment; /**< Optional signal comment. */
|
std::string comment; /**< Optional signal comment. */
|
||||||
|
|
||||||
SignalInfo()
|
SignalInfo()
|
||||||
: name()
|
: name(), startBit(0U), length(0U), isLittleEndian(true), isSigned(false), factor(1.0), offset(0.0), minimum(0.0), maximum(0.0), unit(), receivers(), comment() {
|
||||||
, startBit (0U)
|
}
|
||||||
, length (0U)
|
|
||||||
, isLittleEndian (true)
|
|
||||||
, isSigned (false)
|
|
||||||
, factor (1.0)
|
|
||||||
, offset (0.0)
|
|
||||||
, minimum (0.0)
|
|
||||||
, maximum (0.0)
|
|
||||||
, unit()
|
|
||||||
, receivers()
|
|
||||||
, comment() {
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* SIGNAL_INFO_H */
|
#endif /* SIGNAL_INFO_H */
|
||||||
+123
-138
@@ -5,150 +5,135 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
struct DecodeCase {
|
struct DecodeCase {
|
||||||
const char *name;
|
const char *name;
|
||||||
ByteOrder byteOrder;
|
ByteOrder byteOrder;
|
||||||
std::uint32_t startBit;
|
std::uint32_t startBit;
|
||||||
std::uint32_t length;
|
std::uint32_t length;
|
||||||
ValueType valueType;
|
ValueType valueType;
|
||||||
std::vector<std::uint8_t> data;
|
std::vector<std::uint8_t> data;
|
||||||
bool valid;
|
bool valid;
|
||||||
std::int64_t rawValue;
|
std::int64_t rawValue;
|
||||||
double factor;
|
double factor;
|
||||||
double offset;
|
double offset;
|
||||||
double physicalValue;
|
double physicalValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
const DecodeCase cases[] = {
|
const DecodeCase cases[] = {
|
||||||
{"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned,
|
{"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
||||||
{0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
{"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned, {0xAC}, true, 11, 1.0, 0.0, 11.0},
|
||||||
{"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned,
|
{"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned, {0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0},
|
||||||
{0xAC}, true, 11, 1.0, 0.0, 11.0},
|
{"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned, {0x01, 0x80}, true, 3, 1.0, 0.0, 3.0},
|
||||||
{"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned,
|
{"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned, {0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
||||||
{0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0},
|
{"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed, {0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0},
|
||||||
{"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned,
|
{"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
||||||
{0x01, 0x80}, true, 3, 1.0, 0.0, 3.0},
|
{"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL, 1.0, 0.0, 4611686018427387904.0},
|
||||||
{"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned,
|
{"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0},
|
||||||
{0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
{"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned, {}, false, 0, 1.0, 0.0, 0.0},
|
||||||
{"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed,
|
{"Motorola large start", ByteOrder::Motorola, std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
|
||||||
{0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0},
|
{"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
|
||||||
{"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned,
|
{"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned,
|
||||||
{0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
std::vector<std::uint8_t>(9U, 0xFF), false, 0, 1.0, 0.0, 0.0},
|
||||||
{"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned,
|
{"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0},
|
||||||
{0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL,
|
{"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
|
||||||
1.0, 0.0, 4611686018427387904.0},
|
{"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}};
|
||||||
{"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned,
|
|
||||||
{0x12}, false, 0, 1.0, 0.0, 0.0},
|
|
||||||
{"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned,
|
|
||||||
{}, false, 0, 1.0, 0.0, 0.0},
|
|
||||||
{"Motorola large start", ByteOrder::Motorola,
|
|
||||||
std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned,
|
|
||||||
{0xFF}, false, 0, 1.0, 0.0, 0.0},
|
|
||||||
{"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned,
|
|
||||||
{0xFF}, false, 0, 1.0, 0.0, 0.0},
|
|
||||||
{"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned,
|
|
||||||
std::vector<std::uint8_t> (9U, 0xFF), false, 0, 1.0, 0.0, 0.0},
|
|
||||||
{"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned,
|
|
||||||
{0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0},
|
|
||||||
{"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed,
|
|
||||||
{0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
|
|
||||||
{"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned,
|
|
||||||
{0x12}, false, 0, 1.0, 0.0, 0.0}
|
|
||||||
};
|
|
||||||
|
|
||||||
int failures = 0;
|
int failures = 0;
|
||||||
for (const DecodeCase &test : cases) {
|
for (const DecodeCase &test: cases) {
|
||||||
DecodeSignal signal;
|
|
||||||
signal.name = test.name;
|
|
||||||
signal.byteOrder = test.byteOrder;
|
|
||||||
signal.startBit = test.startBit;
|
|
||||||
signal.length = test.length;
|
|
||||||
signal.valueType = test.valueType;
|
|
||||||
signal.factor = test.factor;
|
|
||||||
signal.offset = test.offset;
|
|
||||||
|
|
||||||
DecodeFrame definition;
|
|
||||||
definition.canId = 0x123U;
|
|
||||||
definition.dlc = static_cast<std::uint8_t> (test.data.size());
|
|
||||||
definition.signals.push_back (signal);
|
|
||||||
DecodeDatabase database;
|
|
||||||
database.frames.push_back (definition);
|
|
||||||
database.frameIndexByKey.emplace (FrameKey (definition.canId, false), 0U);
|
|
||||||
|
|
||||||
RawCanFrame frame;
|
|
||||||
frame.canId = definition.canId;
|
|
||||||
frame.data = test.data;
|
|
||||||
const DecodedFrameValue result = DbcDecoder().Decode (database, frame);
|
|
||||||
const bool passed = result.valid &&
|
|
||||||
(result.definition == &database.frames[0]) &&
|
|
||||||
(result.signals.size() == 1U) &&
|
|
||||||
(result.signals[0].definition == &database.frames[0].signals[0]) &&
|
|
||||||
(result.signals[0].valid == test.valid) &&
|
|
||||||
(!test.valid || (((test.valueType == ValueType::Signed)
|
|
||||||
? (std::get_if<std::int64_t> (&result.signals[0].rawValue) &&
|
|
||||||
std::get<std::int64_t> (result.signals[0].rawValue) == test.rawValue)
|
|
||||||
: (std::get_if<std::uint64_t> (&result.signals[0].rawValue) &&
|
|
||||||
std::get<std::uint64_t> (result.signals[0].rawValue) ==
|
|
||||||
static_cast<std::uint64_t> (test.rawValue))) &&
|
|
||||||
(result.signals[0].physicalValue == test.physicalValue)));
|
|
||||||
if (!passed) {
|
|
||||||
std::cerr << "FAIL: " << test.name << '\n';
|
|
||||||
++failures;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::size_t boundaryCases = 0U;
|
|
||||||
for (ByteOrder order : {ByteOrder::Intel, ByteOrder::Motorola}) {
|
|
||||||
for (ValueType type : {ValueType::Unsigned, ValueType::Signed}) {
|
|
||||||
for (std::uint64_t bits : {std::uint64_t (0U), std::uint64_t (0x7FFFFFFFFFFFFFFFULL),
|
|
||||||
std::uint64_t (0x8000000000000000ULL),
|
|
||||||
std::uint64_t (0x8000000000000001ULL),
|
|
||||||
std::uint64_t (0xFFFFFFFFFFFFFFFFULL)}) {
|
|
||||||
DecodeDatabase database;
|
|
||||||
DecodeFrame definition;
|
|
||||||
DecodeSignal signal;
|
DecodeSignal signal;
|
||||||
signal.startBit = order == ByteOrder::Intel ? 0U : 7U;
|
signal.name = test.name;
|
||||||
signal.length = 64U;
|
signal.byteOrder = test.byteOrder;
|
||||||
signal.byteOrder = order;
|
signal.startBit = test.startBit;
|
||||||
signal.valueType = type;
|
signal.length = test.length;
|
||||||
signal.factor = 0.5;
|
signal.valueType = test.valueType;
|
||||||
signal.offset = 10.0;
|
signal.factor = test.factor;
|
||||||
definition.signals.push_back (signal);
|
signal.offset = test.offset;
|
||||||
database.frames.push_back (definition);
|
|
||||||
database.frameIndexByKey.emplace (FrameKey (0U, false), 0U);
|
DecodeFrame definition;
|
||||||
|
definition.canId = 0x123U;
|
||||||
|
definition.dlc = static_cast<std::uint8_t>(test.data.size());
|
||||||
|
definition.signals.push_back(signal);
|
||||||
|
DecodeDatabase database;
|
||||||
|
database.frames.push_back(definition);
|
||||||
|
database.frameIndexByKey.emplace(FrameKey(definition.canId, false), 0U);
|
||||||
|
|
||||||
RawCanFrame frame;
|
RawCanFrame frame;
|
||||||
for (unsigned byte = 0U; byte < 8U; ++byte) {
|
frame.canId = definition.canId;
|
||||||
const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U;
|
frame.data = test.data;
|
||||||
frame.data.push_back (static_cast<std::uint8_t> (bits >> shift));
|
const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
|
||||||
|
const bool passed = result.valid &&
|
||||||
|
(result.definition == &database.frames[0]) &&
|
||||||
|
(result.signals.size() == 1U) &&
|
||||||
|
(result.signals[0].definition == &database.frames[0].signals[0]) &&
|
||||||
|
(result.signals[0].valid == test.valid) &&
|
||||||
|
(!test.valid || (((test.valueType == ValueType::Signed)
|
||||||
|
? (std::get_if<std::int64_t>(&result.signals[0].rawValue) &&
|
||||||
|
std::get<std::int64_t>(result.signals[0].rawValue) == test.rawValue)
|
||||||
|
: (std::get_if<std::uint64_t>(&result.signals[0].rawValue) &&
|
||||||
|
std::get<std::uint64_t>(result.signals[0].rawValue) ==
|
||||||
|
static_cast<std::uint64_t>(test.rawValue))) &&
|
||||||
|
(result.signals[0].physicalValue == test.physicalValue)));
|
||||||
|
if (!passed) {
|
||||||
|
std::cerr << "FAIL: " << test.name << '\n';
|
||||||
|
++failures;
|
||||||
}
|
}
|
||||||
const auto result = DbcDecoder().Decode (database, frame);
|
|
||||||
const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL
|
|
||||||
? static_cast<std::int64_t> (bits)
|
|
||||||
: std::numeric_limits<std::int64_t>::min() +
|
|
||||||
static_cast<std::int64_t> (bits - 0x8000000000000000ULL);
|
|
||||||
const auto &decoded = result.signals.at (0U);
|
|
||||||
const auto expected = type == ValueType::Unsigned
|
|
||||||
? std::variant<std::int64_t, std::uint64_t> (bits)
|
|
||||||
: std::variant<std::int64_t, std::uint64_t> (signedExpected);
|
|
||||||
const double physical = (type == ValueType::Unsigned
|
|
||||||
? static_cast<double> (bits) : static_cast<double> (signedExpected)) * 0.5 + 10.0;
|
|
||||||
std::ostringstream actualText;
|
|
||||||
std::ostringstream expectedText;
|
|
||||||
std::visit ([&actualText] (auto raw) { actualText << raw; }, decoded.rawValue);
|
|
||||||
if (type == ValueType::Unsigned)
|
|
||||||
expectedText << bits;
|
|
||||||
else
|
|
||||||
expectedText << signedExpected;
|
|
||||||
if (!decoded.valid || decoded.rawValue != expected ||
|
|
||||||
decoded.physicalValue != physical || actualText.str() != expectedText.str()) {
|
|
||||||
std::cerr << "FAIL: 64-bit boundary " << bits << '\n';
|
|
||||||
++failures;
|
|
||||||
}
|
|
||||||
++boundaryCases;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
std::size_t boundaryCases = 0U;
|
||||||
if (failures != 0)
|
for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
|
||||||
return 1;
|
for (ValueType type: {ValueType::Unsigned, ValueType::Signed}) {
|
||||||
std::cout << "All " << sizeof (cases) / sizeof (cases[0]) + boundaryCases << " decoder cases passed.\n";
|
for (std::uint64_t bits: {std::uint64_t(0U), std::uint64_t(0x7FFFFFFFFFFFFFFFULL),
|
||||||
return 0;
|
std::uint64_t(0x8000000000000000ULL),
|
||||||
|
std::uint64_t(0x8000000000000001ULL),
|
||||||
|
std::uint64_t(0xFFFFFFFFFFFFFFFFULL)}) {
|
||||||
|
DecodeDatabase database;
|
||||||
|
DecodeFrame definition;
|
||||||
|
DecodeSignal signal;
|
||||||
|
signal.startBit = order == ByteOrder::Intel ? 0U : 7U;
|
||||||
|
signal.length = 64U;
|
||||||
|
signal.byteOrder = order;
|
||||||
|
signal.valueType = type;
|
||||||
|
signal.factor = 0.5;
|
||||||
|
signal.offset = 10.0;
|
||||||
|
definition.signals.push_back(signal);
|
||||||
|
database.frames.push_back(definition);
|
||||||
|
database.frameIndexByKey.emplace(FrameKey(0U, false), 0U);
|
||||||
|
RawCanFrame frame;
|
||||||
|
for (unsigned byte = 0U; byte < 8U; ++byte) {
|
||||||
|
const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U;
|
||||||
|
frame.data.push_back(static_cast<std::uint8_t>(bits >> shift));
|
||||||
|
}
|
||||||
|
const auto result = DbcDecoder().Decode(database, frame);
|
||||||
|
const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL
|
||||||
|
? static_cast<std::int64_t>(bits)
|
||||||
|
: std::numeric_limits<std::int64_t>::min() +
|
||||||
|
static_cast<std::int64_t>(bits - 0x8000000000000000ULL);
|
||||||
|
const auto &decoded = result.signals.at(0U);
|
||||||
|
const auto expected = type == ValueType::Unsigned
|
||||||
|
? std::variant<std::int64_t, std::uint64_t>(bits)
|
||||||
|
: std::variant<std::int64_t, std::uint64_t>(signedExpected);
|
||||||
|
const double physical = (type == ValueType::Unsigned
|
||||||
|
? static_cast<double>(bits)
|
||||||
|
: static_cast<double>(signedExpected)) *
|
||||||
|
0.5 +
|
||||||
|
10.0;
|
||||||
|
std::ostringstream actualText;
|
||||||
|
std::ostringstream expectedText;
|
||||||
|
std::visit([&actualText](auto raw) { actualText << raw; }, decoded.rawValue);
|
||||||
|
if (type == ValueType::Unsigned)
|
||||||
|
expectedText << bits;
|
||||||
|
else
|
||||||
|
expectedText << signedExpected;
|
||||||
|
if (!decoded.valid || decoded.rawValue != expected ||
|
||||||
|
decoded.physicalValue != physical || actualText.str() != expectedText.str()) {
|
||||||
|
std::cerr << "FAIL: 64-bit boundary " << bits << '\n';
|
||||||
|
++failures;
|
||||||
|
}
|
||||||
|
++boundaryCases;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (failures != 0)
|
||||||
|
return 1;
|
||||||
|
std::cout << "All " << sizeof(cases) / sizeof(cases[0]) + boundaryCases << " decoder cases passed.\n";
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
+225
-202
@@ -8,223 +8,246 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
struct Fixture {
|
struct Fixture {
|
||||||
std::filesystem::path path = std::filesystem::temp_directory_path() /
|
std::filesystem::path path = std::filesystem::temp_directory_path() /
|
||||||
("dbc_parser_test_" + std::to_string (
|
("dbc_parser_test_" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + ".dbc");
|
||||||
std::chrono::steady_clock::now().time_since_epoch().count()) + ".dbc");
|
~Fixture() {
|
||||||
~Fixture() { std::error_code error; std::filesystem::remove (path, error); }
|
std::error_code error;
|
||||||
void Write (const std::string &text) const {
|
std::filesystem::remove(path, error);
|
||||||
std::ofstream output (path);
|
}
|
||||||
output << text;
|
void Write(const std::string &text) const {
|
||||||
if (!output)
|
std::ofstream output(path);
|
||||||
throw std::runtime_error ("Cannot write test fixture.");
|
output << text;
|
||||||
}
|
if (!output)
|
||||||
|
throw std::runtime_error("Cannot write test fixture.");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CountingHandler : public DbcStatementHandler {
|
class CountingHandler : public DbcStatementHandler {
|
||||||
public:
|
public:
|
||||||
inline static int instances = 0;
|
inline static int instances = 0;
|
||||||
inline static int calls = 0;
|
inline static int calls = 0;
|
||||||
CountingHandler() { ++instances; }
|
CountingHandler() { ++instances; }
|
||||||
void Handle (const std::string &, DbcParseContext &context) const override {
|
void Handle(const std::string &, DbcParseContext &context) const override {
|
||||||
++calls;
|
++calls;
|
||||||
context.database.frames.emplace_back();
|
context.database.frames.emplace_back();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Fixture fixture;
|
Fixture fixture;
|
||||||
int failures = 0;
|
int failures = 0;
|
||||||
std::size_t count = 0U;
|
std::size_t count = 0U;
|
||||||
const auto check = [&] (bool passed, const std::string &name) {
|
const auto check = [&](bool passed, const std::string &name) {
|
||||||
++count;
|
++count;
|
||||||
if (!passed) { ++failures; std::cerr << "FAIL: " << name << '\n'; }
|
if (!passed) {
|
||||||
};
|
++failures;
|
||||||
struct ValidCase { const char *line; std::uint32_t id; bool extended; unsigned length; };
|
std::cerr << "FAIL: " << name << '\n';
|
||||||
const ValidCase valid[] = {
|
}
|
||||||
{"BO_ 0 Frame: 0 ECU", 0U, false, 0U},
|
};
|
||||||
{"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U},
|
struct ValidCase {
|
||||||
{"BO_ 2048 Frame: 64 ECU", 2048U, true, 64U},
|
const char *line;
|
||||||
{"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U},
|
std::uint32_t id;
|
||||||
{"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
|
bool extended;
|
||||||
{"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
|
unsigned length;
|
||||||
{" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}
|
};
|
||||||
};
|
const ValidCase valid[] = {
|
||||||
for (const auto &test : valid) {
|
{"BO_ 0 Frame: 0 ECU", 0U, false, 0U},
|
||||||
fixture.Write (std::string (test.line) + '\n');
|
{"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U},
|
||||||
try {
|
{"BO_ 2048 Frame: 64 ECU", 2048U, true, 64U},
|
||||||
const auto database = DbcParser().ParseFile (fixture.path.string());
|
{"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U},
|
||||||
check (database.frames.size() == 1U && database.frames[0].name == "Frame" &&
|
{"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
|
||||||
database.frames[0].canId == test.id &&
|
{"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
|
||||||
database.frames[0].isExtended == test.extended &&
|
{" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}};
|
||||||
database.frames[0].dlc == test.length &&
|
for (const auto &test: valid) {
|
||||||
database.frames[0].transmitter == "ECU", test.line);
|
fixture.Write(std::string(test.line) + '\n');
|
||||||
} catch (const std::exception &error) { check (false, error.what()); }
|
try {
|
||||||
}
|
const auto database = DbcParser().ParseFile(fixture.path.string());
|
||||||
const char *invalid[] = {
|
check(database.frames.size() == 1U && database.frames[0].name == "Frame" &&
|
||||||
"BO_ invalid", "BO_", "BO_ 1", "BO_ 1 : 8 ECU", "BO_ 1 Frame 8 ECU",
|
database.frames[0].canId == test.id &&
|
||||||
"BO_ 1 Frame:", "BO_ 1 Frame: 8", "BO_ 1 Frame: 8 ECU extra",
|
database.frames[0].isExtended == test.extended &&
|
||||||
"BO_ 1 Two Names: 8 ECU", "BO_ -1 Frame: 8 ECU", "BO_ +1 Frame: 8 ECU",
|
database.frames[0].dlc == test.length &&
|
||||||
"BO_ 1x Frame: 8 ECU", "BO_ 0x123 Frame: 8 ECU", "BO_ 1.5 Frame: 8 ECU",
|
database.frames[0].transmitter == "ECU",
|
||||||
"BO_ 4294967296 Frame: 8 ECU", "BO_ 999999999999999999999 Frame: 8 ECU",
|
test.line);
|
||||||
"BO_ 536870912 Frame: 8 ECU", "BO_ 1073741824 Frame: 8 ECU",
|
} catch (const std::exception &error) { check(false, error.what()); }
|
||||||
"BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU",
|
|
||||||
"BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
|
|
||||||
"BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
|
|
||||||
"BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"
|
|
||||||
};
|
|
||||||
for (const auto *line : invalid) {
|
|
||||||
fixture.Write (std::string ("VERSION \"test\"\n\n") + line + '\n');
|
|
||||||
try {
|
|
||||||
DbcParser().ParseFile (fixture.path.string());
|
|
||||||
check (false, line);
|
|
||||||
} catch (const std::runtime_error &error) {
|
|
||||||
const std::string prefix = fixture.path.string() + ":3: ";
|
|
||||||
check (std::string (error.what()).find (prefix) == 0U &&
|
|
||||||
std::string (error.what()).size() > prefix.size(), line);
|
|
||||||
}
|
}
|
||||||
}
|
const char *invalid[] = {
|
||||||
fixture.Write (
|
"BO_ invalid", "BO_", "BO_ 1", "BO_ 1 : 8 ECU", "BO_ 1 Frame 8 ECU",
|
||||||
"BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n"
|
"BO_ 1 Frame:", "BO_ 1 Frame: 8", "BO_ 1 Frame: 8 ECU extra",
|
||||||
" SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
|
"BO_ 1 Two Names: 8 ECU", "BO_ -1 Frame: 8 ECU", "BO_ +1 Frame: 8 ECU",
|
||||||
"CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
|
"BO_ 1x Frame: 8 ECU", "BO_ 0x123 Frame: 8 ECU", "BO_ 1.5 Frame: 8 ECU",
|
||||||
"BO_ 257 Second: 8 ECU\n");
|
"BO_ 4294967296 Frame: 8 ECU", "BO_ 999999999999999999999 Frame: 8 ECU",
|
||||||
const auto database = DbcParser().ParseFile (fixture.path.string());
|
"BO_ 536870912 Frame: 8 ECU", "BO_ 1073741824 Frame: 8 ECU",
|
||||||
check (database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
|
"BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU",
|
||||||
database.frames[0].comment == "Frame comment" &&
|
"BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
|
||||||
database.frames[0].signals[0].comment == "Signal comment", "Integration");
|
"BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
|
||||||
fixture.Write ("\nSG_ Speed : invalid\n");
|
"BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"};
|
||||||
try {
|
for (const auto *line: invalid) {
|
||||||
DbcParser().ParseFile (fixture.path.string());
|
fixture.Write(std::string("VERSION \"test\"\n\n") + line + '\n');
|
||||||
check (false, "Signal error location");
|
try {
|
||||||
} catch (const std::runtime_error &error) {
|
DbcParser().ParseFile(fixture.path.string());
|
||||||
check (std::string (error.what()).find (fixture.path.string() + ":2: ") == 0U,
|
check(false, line);
|
||||||
"Signal error location");
|
} catch (const std::runtime_error &error) {
|
||||||
}
|
const std::string prefix = fixture.path.string() + ":3: ";
|
||||||
const DbcParser signalParser;
|
check(std::string(error.what()).find(prefix) == 0U &&
|
||||||
const auto signalLine = [] (const std::string &start, const std::string &length,
|
std::string(error.what()).size() > prefix.size(),
|
||||||
|
line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixture.Write(
|
||||||
|
"BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n"
|
||||||
|
" SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
|
||||||
|
"CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
|
||||||
|
"BO_ 257 Second: 8 ECU\n");
|
||||||
|
const auto database = DbcParser().ParseFile(fixture.path.string());
|
||||||
|
check(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
|
||||||
|
database.frames[0].comment == "Frame comment" &&
|
||||||
|
database.frames[0].signals[0].comment == "Signal comment",
|
||||||
|
"Integration");
|
||||||
|
fixture.Write("\nSG_ Speed : invalid\n");
|
||||||
|
try {
|
||||||
|
DbcParser().ParseFile(fixture.path.string());
|
||||||
|
check(false, "Signal error location");
|
||||||
|
} catch (const std::runtime_error &error) {
|
||||||
|
check(std::string(error.what()).find(fixture.path.string() + ":2: ") == 0U,
|
||||||
|
"Signal error location");
|
||||||
|
}
|
||||||
|
const DbcParser signalParser;
|
||||||
|
const auto signalLine = [](const std::string &start, const std::string &length,
|
||||||
const std::string &order, const std::string &factor,
|
const std::string &order, const std::string &factor,
|
||||||
const std::string &offset, const std::string &minimum,
|
const std::string &offset, const std::string &minimum,
|
||||||
const std::string &maximum) {
|
const std::string &maximum) {
|
||||||
return "SG_ Value : " + start + "|" + length + "@" + order +
|
return "SG_ Value : " + start + "|" + length + "@" + order +
|
||||||
" (" + factor + "," + offset + ") [" + minimum + "|" + maximum + "] \"\" ECU";
|
" (" + factor + "," + offset + ") [" + minimum + "|" + maximum + "] \"\" ECU";
|
||||||
};
|
};
|
||||||
const auto rejectSignal = [&] (const std::string &line, unsigned payload = 8U) {
|
const auto rejectSignal = [&](const std::string &line, unsigned payload = 8U) {
|
||||||
fixture.Write ("BO_ 1 Frame: " + std::to_string (payload) + " ECU\n\n" + line + "\n");
|
fixture.Write("BO_ 1 Frame: " + std::to_string(payload) + " ECU\n\n" + line + "\n");
|
||||||
try {
|
try {
|
||||||
signalParser.ParseFile (fixture.path.string());
|
signalParser.ParseFile(fixture.path.string());
|
||||||
check (false, "Reject " + line);
|
check(false, "Reject " + line);
|
||||||
} catch (const std::runtime_error &error) {
|
} catch (const std::runtime_error &error) {
|
||||||
const std::string prefix = fixture.path.string() + ":3: ";
|
const std::string prefix = fixture.path.string() + ":3: ";
|
||||||
check (std::string (error.what()).find (prefix) == 0U &&
|
check(std::string(error.what()).find(prefix) == 0U &&
|
||||||
std::string (error.what()).size() > prefix.size(), "Reject " + line);
|
std::string(error.what()).size() > prefix.size(),
|
||||||
|
"Reject " + line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const std::string ordinary = signalLine("0", "8", "1+", "1", "0", "0", "255");
|
||||||
|
for (const std::string value: {"-1", "+1", "0junk", "0x10", "1.5",
|
||||||
|
"4294967296", "999999999999999999999"}) {
|
||||||
|
rejectSignal(signalLine(value, "8", "1+", "1", "0", "0", "255"));
|
||||||
|
rejectSignal(signalLine("0", value, "1+", "1", "0", "0", "255"));
|
||||||
}
|
}
|
||||||
};
|
for (const std::string length: {"0", "65"})
|
||||||
const std::string ordinary = signalLine ("0", "8", "1+", "1", "0", "0", "255");
|
rejectSignal(signalLine("0", length, "1+", "1", "0", "0", "255"));
|
||||||
for (const std::string value : {"-1", "+1", "0junk", "0x10", "1.5",
|
for (const std::string order: {"2+", "9-", "1", "1x", "1++", "01+"})
|
||||||
"4294967296", "999999999999999999999"}) {
|
rejectSignal(signalLine("0", "8", order, "1", "0", "0", "255"));
|
||||||
rejectSignal (signalLine (value, "8", "1+", "1", "0", "0", "255"));
|
for (const std::string value: {"1junk", "nan", "NaN", "inf", "-inf", "1e999", "1e-999", "0x1p2", "", "1e", "1.2.3"}) {
|
||||||
rejectSignal (signalLine ("0", value, "1+", "1", "0", "0", "255"));
|
rejectSignal(signalLine("0", "8", "1+", value, "0", "0", "255"));
|
||||||
}
|
rejectSignal(signalLine("0", "8", "1+", "1", value, "0", "255"));
|
||||||
for (const std::string length : {"0", "65"})
|
rejectSignal(signalLine("0", "8", "1+", "1", "0", value, "255"));
|
||||||
rejectSignal (signalLine ("0", length, "1+", "1", "0", "0", "255"));
|
rejectSignal(signalLine("0", "8", "1+", "1", "0", "0", value));
|
||||||
for (const std::string order : {"2+", "9-", "1", "1x", "1++", "01+"})
|
}
|
||||||
rejectSignal (signalLine ("0", "8", order, "1", "0", "0", "255"));
|
rejectSignal(signalLine("0", "8", "1+", "1", "0", "2", "1"));
|
||||||
for (const std::string value : {"1junk", "nan", "NaN", "inf", "-inf", "1e999", "1e-999", "0x1p2", "", "1e", "1.2.3"}) {
|
for (const std::string name: {"", "Two Names", "Value M", "Value m1", "Value m1M"}) {
|
||||||
rejectSignal (signalLine ("0", "8", "1+", value, "0", "0", "255"));
|
std::string line = ordinary;
|
||||||
rejectSignal (signalLine ("0", "8", "1+", "1", value, "0", "255"));
|
line.replace(4U, 5U, name);
|
||||||
rejectSignal (signalLine ("0", "8", "1+", "1", "0", value, "255"));
|
rejectSignal(line);
|
||||||
rejectSignal (signalLine ("0", "8", "1+", "1", "0", "0", value));
|
}
|
||||||
}
|
for (const std::string broken: {
|
||||||
rejectSignal (signalLine ("0", "8", "1+", "1", "0", "2", "1"));
|
"SG_", "SG_ Value", "SG_ Value : 0|8@1+ (1,0) [0|255] ECU",
|
||||||
for (const std::string name : {"", "Two Names", "Value M", "Value m1", "Value m1M"}) {
|
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\"",
|
||||||
std::string line = ordinary;
|
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
|
||||||
line.replace (4U, 5U, name);
|
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
|
||||||
rejectSignal (line);
|
rejectSignal(broken);
|
||||||
}
|
rejectSignal(signalLine("64", "1", "1+", "1", "0", "0", "1"));
|
||||||
for (const std::string broken : {
|
rejectSignal(signalLine("63", "2", "1+", "1", "0", "0", "3"));
|
||||||
"SG_", "SG_ Value", "SG_ Value : 0|8@1+ (1,0) [0|255] ECU",
|
rejectSignal(signalLine("56", "2", "0+", "1", "0", "0", "3"));
|
||||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\"",
|
rejectSignal(signalLine("4294967295", "64", "1+", "1", "0", "0", "1"));
|
||||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
|
rejectSignal(ordinary, 0U);
|
||||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
|
for (const char delimiter: {'|', '@', '(', ')', '[', ']', '"'}) {
|
||||||
rejectSignal (broken);
|
std::string line = ordinary;
|
||||||
rejectSignal (signalLine ("64", "1", "1+", "1", "0", "0", "1"));
|
line.erase(line.find(delimiter), 1U);
|
||||||
rejectSignal (signalLine ("63", "2", "1+", "1", "0", "0", "3"));
|
rejectSignal(line);
|
||||||
rejectSignal (signalLine ("56", "2", "0+", "1", "0", "0", "3"));
|
}
|
||||||
rejectSignal (signalLine ("4294967295", "64", "1+", "1", "0", "0", "1"));
|
fixture.Write("BO_ 1 Frame: 8 ECU\n" +
|
||||||
rejectSignal (ordinary, 0U);
|
signalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" +
|
||||||
for (const char delimiter : {'|', '@', '(', ')', '[', ']', '"'}) {
|
signalLine("8", "8", "1-", "-1", "0", "-255", "0") + "\n");
|
||||||
std::string line = ordinary;
|
const auto factors = signalParser.ParseFile(fixture.path.string());
|
||||||
line.erase (line.find (delimiter), 1U);
|
check(factors.frames[0].signals.size() == 2U &&
|
||||||
rejectSignal (line);
|
factors.frames[0].signals[0].factor == 0.0 &&
|
||||||
}
|
factors.frames[0].signals[1].factor == -1.0,
|
||||||
fixture.Write ("BO_ 1 Frame: 8 ECU\n" +
|
"Zero and negative factors and equal range boundaries");
|
||||||
signalLine ("0", "8", "1+", "0", "0", "0", "0") + "\n" +
|
|
||||||
signalLine ("8", "8", "1-", "-1", "0", "-255", "0") + "\n");
|
|
||||||
const auto factors = signalParser.ParseFile (fixture.path.string());
|
|
||||||
check (factors.frames[0].signals.size() == 2U &&
|
|
||||||
factors.frames[0].signals[0].factor == 0.0 &&
|
|
||||||
factors.frames[0].signals[1].factor == -1.0,
|
|
||||||
"Zero and negative factors and equal range boundaries");
|
|
||||||
|
|
||||||
struct SignalCase { const char *start; const char *length; const char *order; unsigned payload; };
|
struct SignalCase {
|
||||||
const SignalCase validSignals[] = {
|
const char *start;
|
||||||
{"0", "1", "1+", 1U}, {"7", "1", "0-", 1U},
|
const char *length;
|
||||||
{"0", "64", "1+", 8U}, {"7", "64", "0-", 8U},
|
const char *order;
|
||||||
{"63", "1", "1+", 8U}, {"56", "1", "0+", 8U},
|
unsigned payload;
|
||||||
{"3", "12", "0+", 2U}, {"2039", "1", "1+", 255U}
|
};
|
||||||
};
|
const SignalCase validSignals[] = {
|
||||||
for (const auto &test : validSignals) {
|
{"0", "1", "1+", 1U},
|
||||||
fixture.Write ("BO_ 1 Frame: " + std::to_string (test.payload) + " ECU\n" +
|
{"7", "1", "0-", 1U},
|
||||||
signalLine (test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n");
|
{"0", "64", "1+", 8U},
|
||||||
|
{"7", "64", "0-", 8U},
|
||||||
|
{"63", "1", "1+", 8U},
|
||||||
|
{"56", "1", "0+", 8U},
|
||||||
|
{"3", "12", "0+", 2U},
|
||||||
|
{"2039", "1", "1+", 255U}};
|
||||||
|
for (const auto &test: validSignals) {
|
||||||
|
fixture.Write("BO_ 1 Frame: " + std::to_string(test.payload) + " ECU\n" +
|
||||||
|
signalLine(test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n");
|
||||||
|
try {
|
||||||
|
const auto parsed = signalParser.ParseFile(fixture.path.string());
|
||||||
|
const auto &signal = parsed.frames.at(0).signals.at(0);
|
||||||
|
check(signal.startBit == std::stoul(test.start) &&
|
||||||
|
signal.length == std::stoul(test.length) &&
|
||||||
|
signal.isLittleEndian == (test.order[0] == '1') &&
|
||||||
|
signal.isSigned == (test.order[1] == '-') &&
|
||||||
|
signal.factor == 5.0 && signal.offset == -0.25 &&
|
||||||
|
signal.minimum == -1.0 && signal.maximum == 200.0,
|
||||||
|
"Valid signal boundary and numeric metadata");
|
||||||
|
} catch (const std::exception &error) { check(false, error.what()); }
|
||||||
|
}
|
||||||
|
fixture.Write("BO_ 1 Frame: 8 ECU\n"
|
||||||
|
"SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
|
||||||
try {
|
try {
|
||||||
const auto parsed = signalParser.ParseFile (fixture.path.string());
|
const auto parsed = signalParser.ParseFile(fixture.path.string());
|
||||||
const auto &signal = parsed.frames.at (0).signals.at (0);
|
const auto &signal = parsed.frames.at(0).signals.at(0);
|
||||||
check (signal.startBit == std::stoul (test.start) &&
|
check(signal.name == "Value" && signal.unit == "rpm" &&
|
||||||
signal.length == std::stoul (test.length) &&
|
signal.receivers == std::vector<std::string>({"ECU1", "ECU2"}),
|
||||||
signal.isLittleEndian == (test.order[0] == '1') &&
|
"Signal tabs, spaces, and receiver list");
|
||||||
signal.isSigned == (test.order[1] == '-') &&
|
} catch (const std::exception &error) { check(false, error.what()); }
|
||||||
signal.factor == 5.0 && signal.offset == -0.25 &&
|
DbcDatabase dispatchDatabase;
|
||||||
signal.minimum == -1.0 && signal.maximum == 200.0,
|
DbcParseContext context{dispatchDatabase, nullptr};
|
||||||
"Valid signal boundary and numeric metadata");
|
DbcStatementDispatcher dispatcher;
|
||||||
} catch (const std::exception &error) { check (false, error.what()); }
|
dispatcher.RegisterType<CountingHandler>("TEST_");
|
||||||
}
|
check(dispatcher.Dispatch("TEST_ first", context) &&
|
||||||
fixture.Write ("BO_ 1 Frame: 8 ECU\n"
|
dispatcher.Dispatch("TEST_ second", context) &&
|
||||||
"SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
|
CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
|
||||||
try {
|
dispatchDatabase.frames.size() == 2U,
|
||||||
const auto parsed = signalParser.ParseFile (fixture.path.string());
|
"Factory dispatch and handler reuse");
|
||||||
const auto &signal = parsed.frames.at (0).signals.at (0);
|
check(!dispatcher.Dispatch("TEST_OTHER data", context) &&
|
||||||
check (signal.name == "Value" && signal.unit == "rpm" &&
|
!dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) &&
|
||||||
signal.receivers == std::vector<std::string> ({"ECU1", "ECU2"}),
|
!dispatcher.Dispatch("VAL_ 1 Speed 0 \"Stopped\";", context) &&
|
||||||
"Signal tabs, spaces, and receiver list");
|
CountingHandler::calls == 2,
|
||||||
} catch (const std::exception &error) { check (false, error.what()); }
|
"Exact and unknown keyword dispatch");
|
||||||
DbcDatabase dispatchDatabase;
|
const DbcParser parser;
|
||||||
DbcParseContext context {dispatchDatabase, nullptr};
|
fixture.Write("BO_ 1 First: 8 ECU\n");
|
||||||
DbcStatementDispatcher dispatcher;
|
check(parser.ParseFile(fixture.path.string()).frames.size() == 1U, "First parse");
|
||||||
dispatcher.RegisterType<CountingHandler> ("TEST_");
|
fixture.Write("SG_ Speed : invalid\n");
|
||||||
check (dispatcher.Dispatch ("TEST_ first", context) &&
|
try {
|
||||||
dispatcher.Dispatch ("TEST_ second", context) &&
|
parser.ParseFile(fixture.path.string());
|
||||||
CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
|
check(false, "Independent parse context");
|
||||||
dispatchDatabase.frames.size() == 2U, "Factory dispatch and handler reuse");
|
} catch (const std::runtime_error &error) {
|
||||||
check (!dispatcher.Dispatch ("TEST_OTHER data", context) &&
|
check(std::string(error.what()).find("Signal found before any frame") !=
|
||||||
!dispatcher.Dispatch ("BO_TX_BU_ 1 : ECU;", context) &&
|
std::string::npos,
|
||||||
!dispatcher.Dispatch ("VAL_ 1 Speed 0 \"Stopped\";", context) &&
|
"Independent parse context");
|
||||||
CountingHandler::calls == 2, "Exact and unknown keyword dispatch");
|
}
|
||||||
const DbcParser parser;
|
fixture.Write("BO_ 2 Second: 8 ECU\n");
|
||||||
fixture.Write ("BO_ 1 First: 8 ECU\n");
|
const auto recovered = parser.ParseFile(fixture.path.string());
|
||||||
check (parser.ParseFile (fixture.path.string()).frames.size() == 1U, "First parse");
|
check(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
|
||||||
fixture.Write ("SG_ Speed : invalid\n");
|
"Fresh parse after error");
|
||||||
try {
|
if (failures != 0) return 1;
|
||||||
parser.ParseFile (fixture.path.string());
|
std::cout << "All " << count << " parser cases passed.\n";
|
||||||
check (false, "Independent parse context");
|
return 0;
|
||||||
} catch (const std::runtime_error &error) {
|
|
||||||
check (std::string (error.what()).find ("Signal found before any frame") !=
|
|
||||||
std::string::npos, "Independent parse context");
|
|
||||||
}
|
|
||||||
fixture.Write ("BO_ 2 Second: 8 ECU\n");
|
|
||||||
const auto recovered = parser.ParseFile (fixture.path.string());
|
|
||||||
check (recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
|
|
||||||
"Fresh parse after error");
|
|
||||||
if (failures != 0) return 1;
|
|
||||||
std::cout << "All " << count << " parser cases passed.\n";
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
+21
-33
@@ -12,64 +12,52 @@
|
|||||||
#include "tree_node.h"
|
#include "tree_node.h"
|
||||||
|
|
||||||
TreeNode::TreeNode()
|
TreeNode::TreeNode()
|
||||||
: m_type (NodeType::Root)
|
: m_type(NodeType::Root), m_name("dbc"), m_children(), m_frame(), m_signal() {
|
||||||
, m_name ("dbc")
|
|
||||||
, m_children()
|
|
||||||
, m_frame()
|
|
||||||
, m_signal() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeNode::TreeNode (const FrameInfo &frame)
|
TreeNode::TreeNode(const FrameInfo &frame)
|
||||||
: m_type (NodeType::Frame)
|
: m_type(NodeType::Frame), m_name(frame.name), m_children(), m_frame(new FrameInfo(frame)), m_signal() {
|
||||||
, m_name (frame.name)
|
|
||||||
, m_children()
|
|
||||||
, m_frame (new FrameInfo (frame))
|
|
||||||
, m_signal() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeNode::TreeNode (const SignalInfo &signal)
|
TreeNode::TreeNode(const SignalInfo &signal)
|
||||||
: m_type (NodeType::Signal)
|
: m_type(NodeType::Signal), m_name(signal.name), m_children(), m_frame(), m_signal(new SignalInfo(signal)) {
|
||||||
, m_name (signal.name)
|
|
||||||
, m_children()
|
|
||||||
, m_frame()
|
|
||||||
, m_signal (new SignalInfo (signal)) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreeNode::AddChild (std::unique_ptr<TreeNode> child) {
|
void TreeNode::AddChild(std::unique_ptr<TreeNode> child) {
|
||||||
if (child)
|
if (child)
|
||||||
m_children.push_back (std::move (child));
|
m_children.push_back(std::move(child));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t TreeNode::GetChildCount() const {
|
std::size_t TreeNode::GetChildCount() const {
|
||||||
return m_children.size();
|
return m_children.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const TreeNode *TreeNode::GetChild (std::size_t index) const {
|
const TreeNode *TreeNode::GetChild(std::size_t index) const {
|
||||||
if (index >= m_children.size())
|
if (index >= m_children.size())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return m_children[index].get();
|
return m_children[index].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeNode *TreeNode::GetChild (std::size_t index) {
|
TreeNode *TreeNode::GetChild(std::size_t index) {
|
||||||
if (index >= m_children.size())
|
if (index >= m_children.size())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return m_children[index].get();
|
return m_children[index].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeType TreeNode::GetType() const {
|
NodeType TreeNode::GetType() const {
|
||||||
return m_type;
|
return m_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &TreeNode::GetName() const {
|
const std::string &TreeNode::GetName() const {
|
||||||
return m_name;
|
return m_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FrameInfo *TreeNode::GetFrame() const {
|
const FrameInfo *TreeNode::GetFrame() const {
|
||||||
return m_frame.get();
|
return m_frame.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
const SignalInfo *TreeNode::GetSignal() const {
|
const SignalInfo *TreeNode::GetSignal() const {
|
||||||
return m_signal.get();
|
return m_signal.get();
|
||||||
}
|
}
|
||||||
+17
-17
@@ -12,10 +12,10 @@
|
|||||||
#ifndef TREE_NODE_H
|
#ifndef TREE_NODE_H
|
||||||
#define TREE_NODE_H
|
#define TREE_NODE_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "frame_info.h"
|
#include "frame_info.h"
|
||||||
#include "signal_info.h"
|
#include "signal_info.h"
|
||||||
@@ -24,16 +24,16 @@
|
|||||||
* @brief Type of a tree node.
|
* @brief Type of a tree node.
|
||||||
*/
|
*/
|
||||||
enum class NodeType {
|
enum class NodeType {
|
||||||
Root,
|
Root,
|
||||||
Frame,
|
Frame,
|
||||||
Signal
|
Signal
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Tree node for later use in model/view or other hierarchy consumers.
|
* @brief Tree node for later use in model/view or other hierarchy consumers.
|
||||||
*/
|
*/
|
||||||
class TreeNode {
|
class TreeNode {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Create root node.
|
* @brief Create root node.
|
||||||
*/
|
*/
|
||||||
@@ -43,27 +43,27 @@ class TreeNode {
|
|||||||
* @brief Create frame node.
|
* @brief Create frame node.
|
||||||
* @param frame Frame payload.
|
* @param frame Frame payload.
|
||||||
*/
|
*/
|
||||||
explicit TreeNode (const FrameInfo &frame);
|
explicit TreeNode(const FrameInfo &frame);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create signal node.
|
* @brief Create signal node.
|
||||||
* @param signal Signal payload.
|
* @param signal Signal payload.
|
||||||
*/
|
*/
|
||||||
explicit TreeNode (const SignalInfo &signal);
|
explicit TreeNode(const SignalInfo &signal);
|
||||||
|
|
||||||
~TreeNode() = default;
|
~TreeNode() = default;
|
||||||
|
|
||||||
TreeNode (const TreeNode &) = delete;
|
TreeNode(const TreeNode &) = delete;
|
||||||
TreeNode &operator= (const TreeNode &) = delete;
|
TreeNode &operator=(const TreeNode &) = delete;
|
||||||
|
|
||||||
TreeNode (TreeNode &&) = default;
|
TreeNode(TreeNode &&) = default;
|
||||||
TreeNode &operator= (TreeNode &&) = default;
|
TreeNode &operator=(TreeNode &&) = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add child node.
|
* @brief Add child node.
|
||||||
* @param child Child node to add.
|
* @param child Child node to add.
|
||||||
*/
|
*/
|
||||||
void AddChild (std::unique_ptr<TreeNode> child);
|
void AddChild(std::unique_ptr<TreeNode> child);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get child count.
|
* @brief Get child count.
|
||||||
@@ -76,14 +76,14 @@ class TreeNode {
|
|||||||
* @param index Child index.
|
* @param index Child index.
|
||||||
* @return Child pointer or nullptr if index is invalid.
|
* @return Child pointer or nullptr if index is invalid.
|
||||||
*/
|
*/
|
||||||
const TreeNode *GetChild (std::size_t index) const;
|
const TreeNode *GetChild(std::size_t index) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get mutable child by index.
|
* @brief Get mutable child by index.
|
||||||
* @param index Child index.
|
* @param index Child index.
|
||||||
* @return Child pointer or nullptr if index is invalid.
|
* @return Child pointer or nullptr if index is invalid.
|
||||||
*/
|
*/
|
||||||
TreeNode *GetChild (std::size_t index);
|
TreeNode *GetChild(std::size_t index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get node type.
|
* @brief Get node type.
|
||||||
@@ -109,10 +109,10 @@ class TreeNode {
|
|||||||
*/
|
*/
|
||||||
const SignalInfo *GetSignal() const;
|
const SignalInfo *GetSignal() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NodeType m_type;
|
NodeType m_type;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::vector<std::unique_ptr<TreeNode> > m_children;
|
std::vector<std::unique_ptr<TreeNode>> m_children;
|
||||||
std::unique_ptr<FrameInfo> m_frame;
|
std::unique_ptr<FrameInfo> m_frame;
|
||||||
std::unique_ptr<SignalInfo> m_signal;
|
std::unique_ptr<SignalInfo> m_signal;
|
||||||
};
|
};
|
||||||
|
|||||||
+33
-33
@@ -7,7 +7,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility> // std::move
|
#include <utility>// std::move
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Registry-based factory for creating objects by runtime identifier.
|
* @brief Registry-based factory for creating objects by runtime identifier.
|
||||||
@@ -55,15 +55,15 @@
|
|||||||
* (void)b->get();
|
* (void)b->get();
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
template <class ClassId, class BaseClass, class... Args>
|
template<class ClassId, class BaseClass, class... Args>
|
||||||
class TypeFactory {
|
class TypeFactory {
|
||||||
public:
|
public:
|
||||||
using BasePtr = std::shared_ptr<BaseClass>;
|
using BasePtr = std::shared_ptr<BaseClass>;
|
||||||
using CreatorFn = BasePtr (*)(Args...);
|
using CreatorFn = BasePtr (*)(Args...);
|
||||||
|
|
||||||
TypeFactory() = default;
|
TypeFactory() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Registers a Derived type under the given id.
|
* @brief Registers a Derived type under the given id.
|
||||||
*
|
*
|
||||||
* Requirements (checked at compile-time):
|
* Requirements (checked at compile-time):
|
||||||
@@ -72,47 +72,47 @@ public:
|
|||||||
*
|
*
|
||||||
* Re-registering the same id overwrites the previous entry ("last wins").
|
* Re-registering the same id overwrites the previous entry ("last wins").
|
||||||
*/
|
*/
|
||||||
template <class Derived>
|
template<class Derived>
|
||||||
void registerType(const ClassId& id) {
|
void registerType(const ClassId &id) {
|
||||||
static_assert(std::is_base_of_v<BaseClass, Derived>,
|
static_assert(std::is_base_of_v<BaseClass, Derived>,
|
||||||
"Derived must inherit from BaseClass");
|
"Derived must inherit from BaseClass");
|
||||||
static_assert(std::is_constructible_v<Derived, Args...>,
|
static_assert(std::is_constructible_v<Derived, Args...>,
|
||||||
"Derived must be constructible from Args...");
|
"Derived must be constructible from Args...");
|
||||||
|
|
||||||
classes.insert_or_assign(id, &instantiate<Derived>);
|
classes.insert_or_assign(id, &instantiate<Derived>);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns creator function for the given id.
|
* @brief Returns creator function for the given id.
|
||||||
* @throws std::out_of_range when id is not registered.
|
* @throws std::out_of_range when id is not registered.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] CreatorFn creator(const ClassId& id) const {
|
[[nodiscard]] CreatorFn creator(const ClassId &id) const {
|
||||||
return classes.at(id);
|
return classes.at(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates an instance for the given id using provided arguments.
|
* @brief Creates an instance for the given id using provided arguments.
|
||||||
* @throws std::out_of_range when id is not registered.
|
* @throws std::out_of_range when id is not registered.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] BasePtr create(const ClassId& id, Args... args) const {
|
[[nodiscard]] BasePtr create(const ClassId &id, Args... args) const {
|
||||||
return creator(id)(std::move(args)...);
|
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 {
|
[[nodiscard]] bool contains(const ClassId &id) const {
|
||||||
return classes.find(id) != classes.end();
|
return classes.find(id) != classes.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Protected by design: derived factories may extend/inspect the registry.
|
// Protected by design: derived factories may extend/inspect the registry.
|
||||||
std::unordered_map<ClassId, CreatorFn> classes;
|
std::unordered_map<ClassId, CreatorFn> classes;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <class Derived>
|
template<class Derived>
|
||||||
static BasePtr instantiate(Args... args) {
|
static BasePtr instantiate(Args... args) {
|
||||||
return std::make_shared<Derived>(std::move(args)...);
|
return std::make_shared<Derived>(std::move(args)...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif // TYPEFACTORY_H
|
#endif// TYPEFACTORY_H
|
||||||
Reference in new issue
Block a user