Skip to content

Commit 305db9a

Browse files
authored
Merge pull request #1 from xatavian/database-analysis
New functionnalities for the CLI and layout checking capabilities for can-parse
2 parents 626a02d + 2a34a9d commit 305db9a

File tree

10 files changed

+489
-88
lines changed

10 files changed

+489
-88
lines changed

CMakeLists.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ set(CPPPARSER_INCLUDE_DIRECTORY
1212
set(CPPPARSER_SRC_FILES
1313
src/models/CANDatabase.cpp
1414
src/models/CANFrame.cpp
15-
src/models/CANSignal.cpp
15+
src/models/CANSignal.cpp
1616
src/parsing/DBCParser.cpp
17-
src/parsing/Tokenizer.cpp)
17+
src/parsing/Tokenizer.cpp
18+
src/analysis/CANFrameAnalysis.cpp)
1819

1920
add_library(cpp_can_parser STATIC ${CPPPARSER_SRC_FILES})
2021
target_include_directories(cpp_can_parser
2122
PUBLIC ${CPPPARSER_INCLUDE_DIRECTORY})
2223

2324
add_executable(can-parse
24-
utils/can-parse.cpp)
25+
utils/can-parse/can-parse.cpp
26+
utils/can-parse/print-frame.cpp
27+
utils/can-parse/check-frame.cpp)
2528
target_link_libraries(can-parse cpp_can_parser)

include/CANDatabase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class CANDatabase {
7171
* Note: the source database is passed-by-value for RVO
7272
* (see https://stackoverflow.com/a/3279550/8147455 for more info)
7373
*/
74-
CANDatabase& operator=(CANDatabase);
74+
CANDatabase& operator=(const CANDatabase&);
7575

7676
/**
7777
* @brief Moves a CANDatabase object. The CANFrame objects are NOT deep copied.

include/CANDatabaseAnalysis.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef CANDATABASE_ANALYSIS_H
2+
#define CANDATABASE_ANALYSIS_H
3+
4+
#include "CANFrame.h"
5+
6+
namespace CppCAN {
7+
namespace analysis {
8+
/**
9+
* @brief Analyses the frame signals to see if some signals are overlapping
10+
* @return true if no overlapping is detected, false otherwise
11+
*/
12+
bool is_frame_layout_ok(const CANFrame& src);
13+
14+
/**
15+
* @brief Like is_frame_layout_ok() but throws a CANDatabaseException if the layout is ill-formed
16+
*/
17+
void assert_frame_layout();
18+
}
19+
} // namespace CppCAN
20+
21+
#endif

src/analysis/CANFrameAnalysis.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#include "CANDatabaseAnalysis.h"
2+
#include <algorithm>
3+
#include <cmath>
4+
#include <tuple>
5+
6+
// For each entry: (byte, lr start bit, lr end bit)
7+
struct SignalRange {
8+
uint8_t byte;
9+
uint8_t lr_start_bit;
10+
uint8_t lr_end_bit;
11+
};
12+
13+
using SignalRanges = std::vector<SignalRange>;
14+
15+
/**
16+
* Few explanations about the attributes:
17+
* lr_start_bit: Start bit position reading from left to right (ie bit 7 of byte 1 is 0, bit 7 of byte 2 is 9, ...)
18+
* lr_end_bit: same but for the end bit.
19+
*/
20+
struct SignalLayoutEntry {
21+
SignalLayoutEntry() = delete;
22+
SignalLayoutEntry(const CANSignal* src, SignalRanges&& r)
23+
: src_signal(src), ranges(r) {
24+
25+
}
26+
27+
SignalLayoutEntry(const SignalLayoutEntry&) = default;
28+
29+
const CANSignal* src_signal;
30+
SignalRanges ranges;
31+
};
32+
33+
SignalRanges big_endian_ranges(const CANSignal& src) {
34+
SignalRanges result;
35+
36+
// For BigEndian signals, the start bit already represents the left mostbit
37+
// of the signal. Therefore, it is only required to transform it into a "human-readable"
38+
// value, ie. when looking at the frame from left to right.
39+
// ----------------- -----------------
40+
// |*|*|*|*|*|*|*|*| |*|*|*|*|*|*|*|*|
41+
// ----------------- -----------------
42+
// 0 7 instead of 7 0
43+
//
44+
// Example: Start bit 4 becomes LR start bit 3
45+
unsigned bitsAnalyzed = 0;
46+
bool is_start_byte = 0;
47+
48+
for(unsigned current_byte = src.start_bit() / 8; bitsAnalyzed < src.length(); current_byte++, is_start_byte = false) {
49+
unsigned lbit = is_start_byte ? (7 - src.start_bit() % 8) : 0;
50+
unsigned rbit = std::min(7u, src.start_bit() - bitsAnalyzed);
51+
52+
// The static_cast are not "necessary" but it removes some warnings
53+
result.push_back({ static_cast<uint8_t>(current_byte),
54+
static_cast<uint8_t>(lbit),
55+
static_cast<uint8_t>(rbit) });
56+
}
57+
58+
return result;
59+
}
60+
61+
SignalRanges little_endian_ranges(const CANSignal& src) {
62+
SignalRanges result;
63+
64+
// For LittleEndian signals, the start bit represents the LSB of the signal
65+
// which does not really represent anything in terms of layout. So we need
66+
// to compute the ranges for each byte individually anyway.
67+
68+
unsigned byteAnalyzed = 0;
69+
bool is_start_byte = 0;
70+
71+
return result;
72+
}
73+
74+
std::vector<SignalLayoutEntry> compute_layout(const CANFrame& src) {
75+
std::vector<SignalLayoutEntry> result;
76+
77+
for(const auto& signal: src) {
78+
const CANSignal& sig = *signal.second;
79+
int lr_start_bit, lr_end_bit;
80+
81+
if(sig.endianness() == CANSignal::BigEndian) {
82+
auto ranges = big_endian_ranges(sig);
83+
result.emplace_back(&sig, std::move(ranges));
84+
}
85+
else {
86+
auto ranges = little_endian_ranges(sig);
87+
result.emplace_back(&sig, std::move(ranges));
88+
}
89+
}
90+
91+
return result;
92+
}
93+
94+
bool CppCAN::analysis::is_frame_layout_ok(const CANFrame& src) {
95+
auto layout = compute_layout(src);
96+
97+
auto overlap = [](const SignalLayoutEntry& e1, const SignalLayoutEntry& e2) -> bool {
98+
return std::any_of(e1.ranges.begin(), e1.ranges.end(), [&e2](const SignalRange& r1) {
99+
// Find if r2 shares a SignalRange with the same byte with r1
100+
auto r2 = std::find_if(e2.ranges.begin(), e2.ranges.end(), [&r1](const SignalRange& e_range) {
101+
return r1.byte == e_range.byte;
102+
});
103+
104+
// The signals are on completely different bytes
105+
if(r2 == e2.ranges.end())
106+
return false;
107+
108+
// ordered.first is the leftmost SignalRange in the byte
109+
// ordered.second is the rightmost SignalRange in the byte
110+
auto ordered = std::minmax(r1, *r2, [](const SignalRange& r, const SignalRange& rr) {
111+
return r.lr_start_bit < rr.lr_start_bit;
112+
});
113+
114+
// No overlapping if the last bit of the leftmost is before the first
115+
// bit of the rightmost.
116+
return ordered.first.lr_end_bit < ordered.second.lr_start_bit;
117+
});
118+
};
119+
120+
for(size_t i = 0; i < layout.size(); i++) {
121+
const SignalLayoutEntry& e = layout[i];
122+
123+
for(size_t j = i + 1; j < layout.size(); j++) {
124+
if(overlap(layout[i], layout[j]))
125+
return false;
126+
}
127+
}
128+
129+
return true;
130+
}

src/models/CANDatabase.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ CANDatabase::CANDatabase(const CANDatabase& src):
1616
}
1717
}
1818

19-
CANDatabase & CANDatabase::operator=(CANDatabase src) {
20-
swap(*this, src);
19+
CANDatabase & CANDatabase::operator=(const CANDatabase& src) {
20+
CANDatabase temp(src);
21+
swap(temp, *this);
2122
return *this;
2223
}
2324

utils/can-parse.cpp

Lines changed: 0 additions & 82 deletions
This file was deleted.

0 commit comments

Comments
 (0)