1
1
#include " CANDatabaseAnalysis.h"
2
+ #include " CANDatabaseException.h"
2
3
#include < algorithm>
3
4
#include < cmath>
4
5
#include < tuple>
6
+ #include < set>
5
7
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
+ */
7
14
struct SignalRange {
8
15
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;
11
18
};
12
19
13
20
using SignalRanges = std::vector<SignalRange>;
14
21
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
+
20
31
struct SignalLayoutEntry {
21
32
SignalLayoutEntry () = delete ;
22
33
SignalLayoutEntry (const CANSignal* src, SignalRanges&& r)
@@ -35,52 +46,56 @@ struct SignalLayoutEntry {
35
46
36
47
SignalRanges big_endian_ranges (const CANSignal& src) {
37
48
SignalRanges result;
38
-
49
+
39
50
// 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 ();
50
58
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 );
54
62
55
63
// The static_cast are not "necessary" but it removes some warnings
56
64
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);
59
69
}
60
70
61
71
return result;
62
72
}
63
73
64
74
SignalRanges 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.
65
82
SignalRanges result;
66
83
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
+
71
87
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 );
77
92
78
93
// The static_cast are not "necessary" but it removes some warnings
79
94
result.push_back ({ static_cast <uint8_t >(current_byte),
80
- static_cast <uint8_t >(lbit),
81
- static_cast <uint8_t >(rbit) });
95
+ lbit, rbit });
82
96
83
- bitsLeft -= (rbit - lbit);
97
+ bitsLeft -= rbit - lbit;
98
+ currentPos += rbit - lbit;
84
99
}
85
100
86
101
return result;
@@ -91,14 +106,14 @@ std::vector<SignalLayoutEntry> compute_layout(const CANFrame& src) {
91
106
92
107
for (const auto & signal : src) {
93
108
const CANSignal& sig = signal .second ;
94
- int lr_start_bit, lr_end_bit;
95
109
96
110
if (sig.endianness () == CANSignal::BigEndian) {
97
111
auto ranges = big_endian_ranges (sig);
98
112
result.emplace_back (&sig, std::move (ranges));
99
113
}
100
114
else {
101
115
auto ranges = little_endian_ranges (sig);
116
+ convert_to_big_endian_layout (ranges);
102
117
result.emplace_back (&sig, std::move (ranges));
103
118
}
104
119
}
@@ -118,7 +133,7 @@ bool overlap(const SignalLayoutEntry& e1, const SignalLayoutEntry& e2) {
118
133
// ordered.first is the leftmost SignalRange in the byte
119
134
// ordered.second is the rightmost SignalRange in the byte
120
135
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 ;
122
137
});
123
138
124
139
// 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) {
135
150
auto layout = compute_layout (src);
136
151
137
152
for (size_t i = 0 ; i < layout.size (); i++) {
138
- const SignalLayoutEntry& e = layout[i];
139
-
140
153
for (size_t j = i + 1 ; j < layout.size (); j++) {
141
- if (overlap (layout[i], layout[j]))
154
+ if (overlap (layout[i], layout[j])) {
142
155
return false ;
156
+ }
143
157
}
144
158
}
145
159
146
160
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
+ }
147
192
}
0 commit comments