11#include " CANDatabaseAnalysis.h"
2+ #include " CANDatabaseException.h"
23#include < algorithm>
34#include < cmath>
45#include < tuple>
6+ #include < set>
57
6- // For each entry: (byte, lr start bit, lr end bit)
8+ /* *
9+ * For each entry: byte, [lr_start_bit, lr_end_bit)
10+ * Few explanations about the attributes:
11+ * lr_start_bit: Start bit position in the byte (included)
12+ * lr_end_bit: End bit position in the byte (excluded)
13+ */
714struct SignalRange {
815 uint8_t byte;
9- uint8_t lr_start_bit;
10- uint8_t lr_end_bit;
16+ char lr_start_bit;
17+ char lr_end_bit;
1118};
1219
1320using SignalRanges = std::vector<SignalRange>;
1421
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- */
22+ static void
23+ convert_to_big_endian_layout (SignalRanges& src) {
24+ // src is assumed to be LittleEndian
25+ for (SignalRange& r : src) {
26+ r.lr_start_bit = 7 - r.lr_start_bit ;
27+ r.lr_end_bit = 7 - r.lr_end_bit ;
28+ }
29+ }
30+
2031struct SignalLayoutEntry {
2132 SignalLayoutEntry () = delete ;
2233 SignalLayoutEntry (const CANSignal* src, SignalRanges&& r)
@@ -35,52 +46,56 @@ struct SignalLayoutEntry {
3546
3647SignalRanges big_endian_ranges (const CANSignal& src) {
3748 SignalRanges result;
38-
49+
3950 // For BigEndian signals, the start bit already represents the left mostbit
40- // of the signal. Therefore, it is only required to transform it into a "human-readable"
41- // value, ie. when looking at the frame from left to right.
42- // ----------------- -----------------
43- // |*|*|*|*|*|*|*|*| |*|*|*|*|*|*|*|*|
44- // ----------------- -----------------
45- // 0 7 instead of 7 0
46- //
47- // Example: Start bit 4 becomes LR start bit 3
48- unsigned bitsAnalyzed = 0 ;
49- bool is_start_byte = 0 ;
51+ // ----------------- -----------------
52+ // |*|*|*|*|*|*|*|*| |*|*|*|*|*|*|*|*|
53+ // ----------------- -----------------
54+ // 7 0 15 8
55+
56+ unsigned bitsLeft = src.length ();
57+ unsigned currentPos = src.start_bit ();
5058
51- for (unsigned current_byte = src.start_bit () / 8 ; bitsAnalyzed < src. length () ; current_byte++, is_start_byte = false ) {
52- unsigned lbit = is_start_byte ? ( 7 - src. start_bit () % 8 ) : 0 ;
53- unsigned rbit = std::min ( 7u , src. start_bit () - bitsAnalyzed );
59+ for (unsigned current_byte = src.start_bit () / 8 ; bitsLeft > 0 ; current_byte++) {
60+ char lbit = currentPos % 8 ;
61+ char rbit = std::max< char >(- 1 , lbit - bitsLeft );
5462
5563 // The static_cast are not "necessary" but it removes some warnings
5664 result.push_back ({ static_cast <uint8_t >(current_byte),
57- static_cast <uint8_t >(lbit),
58- static_cast <uint8_t >(rbit) });
65+ lbit, rbit });
66+
67+ bitsLeft -= lbit - rbit;
68+ currentPos += (lbit - rbit);
5969 }
6070
6171 return result;
6272}
6373
6474SignalRanges little_endian_ranges (const CANSignal& src) {
75+ // For LittleEndian signals, act like the bits are reversed in the byte:
76+ // ----------------- -----------------
77+ // |*|*|*|*|*|*|*|*| |*|*|*|*|*|*|*|*|
78+ // ----------------- -----------------
79+ // 0 7 8 15
80+ //
81+ // The signal can be found from the start bit + read to the right.
6582 SignalRanges result;
6683
67- // For LittleEndian signals, the start bit represents the LSB of the signal
68- // which does not really represent anything in terms of layout. So we need
69- // to compute the ranges for each byte individually anyway.
70-
84+ if (src.length () == 0 ) // length is 0, we return an empty result.
85+ return result;
86+
7187 unsigned bitsLeft = src.length ();
72- bool is_start_byte = 0 ;
73-
74- for (unsigned current_byte = src.start_bit () / 8 ; bitsLeft > 0 ; current_byte++, is_start_byte = false ) {
75- unsigned lbit = 7 -( src.start_bit () + bitsLeft);
76- unsigned rbit = 7 - src.start_bit ();
88+ unsigned currentPos = src.start_bit ();
89+ for (unsigned current_byte = src.start_bit () / 8 ; bitsLeft > 0 ; current_byte++) {
90+ char lbit = currentPos % 8 ;
91+ char rbit = std::min<char >(lbit + bitsLeft, 8 );
7792
7893 // The static_cast are not "necessary" but it removes some warnings
7994 result.push_back ({ static_cast <uint8_t >(current_byte),
80- static_cast <uint8_t >(lbit),
81- static_cast <uint8_t >(rbit) });
95+ lbit, rbit });
8296
83- bitsLeft -= (rbit - lbit);
97+ bitsLeft -= rbit - lbit;
98+ currentPos += rbit - lbit;
8499 }
85100
86101 return result;
@@ -91,14 +106,14 @@ std::vector<SignalLayoutEntry> compute_layout(const CANFrame& src) {
91106
92107 for (const auto & signal: src) {
93108 const CANSignal& sig = signal.second ;
94- int lr_start_bit, lr_end_bit;
95109
96110 if (sig.endianness () == CANSignal::BigEndian) {
97111 auto ranges = big_endian_ranges (sig);
98112 result.emplace_back (&sig, std::move (ranges));
99113 }
100114 else {
101115 auto ranges = little_endian_ranges (sig);
116+ convert_to_big_endian_layout (ranges);
102117 result.emplace_back (&sig, std::move (ranges));
103118 }
104119 }
@@ -118,7 +133,7 @@ bool overlap(const SignalLayoutEntry& e1, const SignalLayoutEntry& e2) {
118133 // ordered.first is the leftmost SignalRange in the byte
119134 // ordered.second is the rightmost SignalRange in the byte
120135 auto ordered = std::minmax (r1, r2, [](const SignalRange& r, const SignalRange& rr) {
121- return r.lr_start_bit < rr.lr_start_bit ;
136+ return r.lr_start_bit > rr.lr_start_bit ;
122137 });
123138
124139 // No overlapping if the last bit of the leftmost is before the first
@@ -135,13 +150,43 @@ bool CppCAN::analysis::is_frame_layout_ok(const CANFrame& src) {
135150 auto layout = compute_layout (src);
136151
137152 for (size_t i = 0 ; i < layout.size (); i++) {
138- const SignalLayoutEntry& e = layout[i];
139-
140153 for (size_t j = i + 1 ; j < layout.size (); j++) {
141- if (overlap (layout[i], layout[j]))
154+ if (overlap (layout[i], layout[j])) {
142155 return false ;
156+ }
143157 }
144158 }
145159
146160 return true ;
161+ }
162+
163+ bool CppCAN::analysis::is_frame_layout_ok (const CANFrame& src, std::vector<std::string>& diagnosis) {
164+ auto layout = compute_layout (src);
165+ diagnosis.clear ();
166+
167+ std::set<size_t > diagnosis_indices;
168+ auto report_issue = [&diagnosis, &diagnosis_indices](size_t idx, const CANSignal& sig) {
169+ if (diagnosis_indices.count (idx) == 0 ) {
170+ diagnosis_indices.insert (idx);
171+ diagnosis.push_back (sig.name ());
172+ }
173+ };
174+
175+ for (size_t i = 0 ; i < layout.size (); i++) {
176+ for (size_t j = i + 1 ; j < layout.size (); j++) {
177+ if (overlap (layout[i], layout[j])) {
178+ report_issue (i, *layout[i].src_signal );
179+ report_issue (j, *layout[j].src_signal );
180+ }
181+ }
182+ }
183+
184+ return diagnosis_indices.size () == 0 ;
185+ }
186+
187+ void CppCAN::analysis::assert_frame_layout (const CANFrame& src) {
188+ if (!is_frame_layout_ok (src)) {
189+ std::string text = " assert_frame_layout() failed for frame \" " + src.name () + " \" " ;
190+ throw CANDatabaseException (text);
191+ }
147192}
0 commit comments