Skip to content

Commit 079a46b

Browse files
committed
initial commit to parse Messages
1 parent 55ff2d2 commit 079a46b

File tree

10 files changed

+1048
-9
lines changed

10 files changed

+1048
-9
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ list(APPEND SOURCE ${PROJECT_SOURCE_DIR}/src/utils.cpp
2929
${PROJECT_SOURCE_DIR}/src/signal.cpp
3030
${PROJECT_SOURCE_DIR}/src/dbc.cpp)
3131

32+
add_subdirectory(third_party/bitstream)
33+
3234
include_directories(src)
3335
include_directories(include)
3436

@@ -40,6 +42,7 @@ endif()
4042
add_subdirectory(doc)
4143

4244
add_library(${PROJECT_NAME} STATIC ${SOURCE})
45+
target_link_libraries(${PROJECT_NAME} bitstream)
4346

4447
add_custom_target(release
4548
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}

include/libdbc/dbc.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace libdbc {
2424

2525
class DbcParser : public Parser {
2626
public:
27-
DbcParser();
27+
DbcParser(bool sortSignals = false);
2828

2929
virtual ~DbcParser() = default;
3030

@@ -34,6 +34,8 @@ namespace libdbc {
3434
std::vector<std::string> get_nodes() const;
3535
std::vector<libdbc::Message> get_messages() const;
3636

37+
bool parseMessage(const uint32_t id, const std::vector<uint8_t>& data, std::vector<double>& out_values);
38+
3739
private:
3840
std::string version;
3941
std::vector<std::string> nodes;
@@ -46,6 +48,8 @@ namespace libdbc {
4648
const std::regex message_re;
4749
const std::regex signal_re;
4850

51+
bool sortSignals{false};
52+
4953
void parse_dbc_header(std::istream& file_stream);
5054
void parse_dbc_nodes(std::istream& file_stream);
5155
void parse_dbc_messages(const std::vector<std::string>& lines);

include/libdbc/message.hpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,26 @@ namespace libdbc {
1717
Message() = delete;
1818
explicit Message(uint32_t id, const std::string& name, uint8_t size, const std::string& node);
1919

20-
virtual bool operator==(const Message& rhs) const;
20+
bool parseSignals(const std::vector<uint8_t> data, std::vector<double> &values) const;
21+
22+
/*!
23+
* \brief prepareMessage
24+
* Preparing message to be able to parse signals afterwards. This speeds up parsing
25+
*/
26+
void prepareMessage();
27+
28+
virtual bool operator==(const Message& rhs) const;
29+
30+
private:
31+
bool prepared{false}; // indicates if the message is prepared for parsing signals
32+
struct BitStruct {
33+
BitStruct(const std::string& name, uint32_t size, bool padding): name(name), size(size), padding(padding) {}
34+
BitStruct() = delete;
35+
std::string name;
36+
uint32_t size;
37+
bool padding;
38+
};
39+
std::vector<BitStruct> bitstruct;
2140
};
2241

2342
std::ostream& operator<< (std::ostream &out, const Message& msg);

src/dbc.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const auto whiteSpace = "\\s";
2828

2929
namespace libdbc {
3030

31-
DbcParser::DbcParser() : version(""), nodes(),
31+
DbcParser::DbcParser(bool sortSignals) : version(""), nodes(),
3232
version_re("^(VERSION)\\s\"(.*)\""), bit_timing_re("^(BS_:)"),
3333
name_space_re("^(NS_)\\s\\:"), node_re("^(BU_:)\\s((?:[\\w]+?\\s?)*)"),
3434
message_re("^(BO_)\\s(\\d+)\\s(\\w+)\\:\\s(\\d+)\\s(\\w+|Vector__XXX)"),
@@ -53,7 +53,8 @@ namespace libdbc {
5353
whiteSpace +
5454
unitPattern +
5555
whiteSpace +
56-
receiverPattern) {
56+
receiverPattern),
57+
sortSignals(sortSignals) {
5758

5859
}
5960

@@ -73,6 +74,11 @@ namespace libdbc {
7374

7475
parse_dbc_messages(lines);
7576

77+
if (sortSignals) {
78+
for (auto& message: messages) {
79+
message.prepareMessage();
80+
}
81+
}
7682
}
7783

7884
std::string DbcParser::get_version() const {
@@ -87,6 +93,14 @@ namespace libdbc {
8793
return messages;
8894
}
8995

96+
bool DbcParser::parseMessage(const uint32_t id, const std::vector<uint8_t>& data, std::vector<double>& out_values) {
97+
for (const auto& message: messages) {
98+
if (message.id == id)
99+
return message.parseSignals(data, out_values);
100+
}
101+
return false;
102+
}
103+
90104

91105
void DbcParser::parse_dbc_header(std::istream& file_stream) {
92106
std::string line;

src/message.cpp

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,158 @@
1+
#include <algorithm>
12
#include <libdbc/message.hpp>
23

4+
#include <bitstream.h>
5+
36
namespace libdbc {
47
Message::Message(uint32_t id, const std::string& name, uint8_t size, const std::string& node) :
58
id(id), name(name), size(size), node(node) {}
69

710
bool Message::operator==(const Message& rhs) const {
811
return (this->id == rhs.id) && (this->name == rhs.name) &&
912
(this->size == rhs.size) && (this->node == rhs.node);
10-
}
13+
}
14+
15+
bool Message::parseSignals(const std::vector<uint8_t> data, std::vector<double>& values) const {
16+
if (!prepared)
17+
return false;
18+
19+
bitstream_reader_t reader;
20+
bitstream_reader_init(&reader, data.data());
21+
22+
double v;
23+
for (const auto& bs: bitstruct) {
24+
if (bs.padding) {
25+
for (uint32_t i=0; i < bs.size; i++)
26+
bitstream_reader_read_bit(&reader);
27+
} else {
28+
for(const auto& signal: signals) {
29+
if (bs.name.compare(signal.name) != 0) {
30+
continue;
31+
}
32+
33+
if (signal.is_bigendian) {
34+
35+
switch (bs.size) {
36+
case 1: v = static_cast<double>(bitstream_reader_read_bit(&reader)); break;
37+
case 8: v = static_cast<double>(bitstream_reader_read_u8(&reader)); break;
38+
case 16: {
39+
uint16_t tmp = bitstream_reader_read_u8(&reader);
40+
tmp |= (uint16_t)bitstream_reader_read_u8(&reader) << 8;
41+
if (signal.is_signed)
42+
v = static_cast<double>(static_cast<int16_t>(tmp));
43+
else
44+
v = static_cast<double>(tmp);
45+
break;
46+
}
47+
case 32: {
48+
uint32_t tmp = bitstream_reader_read_u8(&reader);
49+
tmp |= (uint32_t)bitstream_reader_read_u8(&reader) << 8;
50+
tmp |= (uint32_t)bitstream_reader_read_u8(&reader) << 16;
51+
tmp |= (uint32_t)bitstream_reader_read_u8(&reader) << 24;
52+
if (signal.is_signed)
53+
v = static_cast<double>(static_cast<int32_t>(tmp));
54+
else
55+
v = static_cast<double>(tmp);
56+
break;
57+
}
58+
case 64: {
59+
uint64_t tmp = bitstream_reader_read_u8(&reader);
60+
tmp |= (uint64_t)bitstream_reader_read_u8(&reader) << 8;
61+
tmp |= (uint64_t)bitstream_reader_read_u8(&reader) << 16;
62+
tmp |= (uint64_t)bitstream_reader_read_u8(&reader) << 24;
63+
tmp |= (uint64_t)bitstream_reader_read_u8(&reader) << 32;
64+
tmp |= (uint64_t)bitstream_reader_read_u8(&reader) << 40;
65+
tmp |= (uint64_t)bitstream_reader_read_u8(&reader) << 48;
66+
tmp |= (uint64_t)bitstream_reader_read_u8(&reader) << 56;
67+
if (signal.is_signed)
68+
v = static_cast<double>(static_cast<int64_t>(tmp));
69+
else
70+
v = static_cast<double>(tmp);
71+
break;
72+
}
73+
default: {
74+
// TODO: implement sign?
75+
// TODO: implement big endian
76+
uint64_t value = 0;
77+
for (uint32_t i=0; i < bs.size; i++) {
78+
value |= bitstream_reader_read_bit(&reader) << i;
79+
}
80+
v = static_cast<double>(value);
81+
break;
82+
}
83+
}
84+
} else {
85+
// little endian
86+
switch (bs.size) {
87+
case 1:
88+
v = static_cast<double>(bitstream_reader_read_bit(&reader)); break;
89+
case 8:
90+
if (signal.is_signed)
91+
v = static_cast<double>(static_cast<int8_t>(bitstream_reader_read_u8(&reader)));
92+
else
93+
v = static_cast<double>(bitstream_reader_read_u8(&reader));
94+
break;
95+
case 16:
96+
if (signal.is_signed)
97+
v = static_cast<double>(static_cast<int16_t>(bitstream_reader_read_u16(&reader)));
98+
else
99+
v = static_cast<double>(bitstream_reader_read_u16(&reader));
100+
break;
101+
case 32:
102+
if (signal.is_signed)
103+
v = static_cast<double>(static_cast<int32_t>(bitstream_reader_read_u32(&reader)));
104+
else
105+
v = static_cast<double>(bitstream_reader_read_u32(&reader));
106+
break;
107+
case 64:
108+
if (signal.is_signed)
109+
v = static_cast<double>(static_cast<int64_t>(bitstream_reader_read_u64(&reader)));
110+
else
111+
v = static_cast<double>(bitstream_reader_read_u64(&reader));
112+
break;
113+
default: {
114+
// TODO: implement sign?
115+
// TODO: implement big endian
116+
uint64_t value = 0;
117+
for (uint32_t i=0; i < bs.size; i++) {
118+
value |= bitstream_reader_read_bit(&reader) << i;
119+
}
120+
v = static_cast<double>(value);
121+
break;
122+
}
123+
}
124+
}
125+
126+
values.push_back(v * signal.factor + signal.offset);
127+
break;
128+
}
129+
}
130+
}
131+
return true;
132+
}
133+
134+
void Message::prepareMessage() {
135+
prepared = false;
136+
std::sort(signals.begin(), signals.end());
137+
138+
uint32_t curr_bit = 0;
139+
for (const auto& signal: signals) {
140+
if (signal.is_multiplexed /*|| signal.is_bigendian*/)
141+
break; // Not supported yet
142+
143+
if (curr_bit < signal.start_bit) {
144+
// padding needed
145+
bitstruct.push_back(BitStruct("", signal.start_bit - curr_bit, true));
146+
}
147+
bitstruct.push_back(BitStruct(signal.name, signal.size, false));
148+
curr_bit = signal.start_bit + signal.size;
149+
}
150+
if (curr_bit > size * 8)
151+
return;
152+
153+
prepared = true;
154+
155+
}
11156

12157
std::ostream& operator<< (std::ostream &out, const Message& msg) {
13158
out << "Message: {id: " << msg.id << ", ";
@@ -16,4 +161,4 @@ namespace libdbc {
16161
out << "node: " << msg.node << "}";
17162
return out;
18163
}
19-
}
164+
}

test/test_dbc.cpp

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include <catch2/catch_test_macros.hpp>
2+
#include <catch2/catch_approx.hpp>
3+
#include <catch2/matchers/catch_matchers.hpp>
24
#include "defines.hpp"
35
#include <libdbc/dbc.hpp>
46

@@ -101,7 +103,7 @@ TEST_CASE("Testing negative values") {
101103
SG_ Sig4 : 7|16@0- (1,-10) [0|32767] "" Vector__XXX)");
102104

103105
auto parser = libdbc::DbcParser();
104-
parser.parse_file(std::string(filename));
106+
parser.parse_file(filename);
105107

106108
REQUIRE(parser.get_messages().size() == 1);
107109
REQUIRE(parser.get_messages().at(0).signals.size() == 4);
@@ -136,7 +138,6 @@ TEST_CASE("Testing negative values") {
136138
}
137139
}
138140

139-
140141
TEST_CASE("Special characters in unit") {
141142
const auto* filename = std::tmpnam(NULL);
142143

@@ -145,7 +146,7 @@ TEST_CASE("Special characters in unit") {
145146

146147

147148
auto parser = libdbc::DbcParser();
148-
parser.parse_file(std::string(filename));
149+
parser.parse_file(filename);
149150

150151
REQUIRE(parser.get_messages().size() == 1);
151152
REQUIRE(parser.get_messages().at(0).signals.size() == 1);
@@ -154,3 +155,58 @@ TEST_CASE("Special characters in unit") {
154155
REQUIRE(signal.unit.compare("Km/h") == 0);
155156
}
156157
}
158+
159+
TEST_CASE("Parse Message little endian") {
160+
const auto* filename = std::tmpnam(NULL);
161+
162+
auto* file = std::fopen(filename, "w");
163+
CHECK(file);
164+
165+
std::fputs(PRIMITIVE_DBC.c_str(), file);
166+
std::fputs(R"(BO_ 541 STATUS: 8 DEVICE1
167+
SG_ Temperature : 48|16@1+ (0.01,-40) [-40|125] "C" DEVICE1
168+
SG_ SOH : 0|16@1+ (0.01,0) [0|100] "%" DEVICE1
169+
SG_ SOE : 32|16@1+ (0.01,0) [0|100] "%" DEVICE1
170+
SG_ SOC : 16|16@1+ (0.01,0) [0|100] "%" DEVICE1)", file);
171+
std::fclose(file);
172+
173+
libdbc::DbcParser p(true);
174+
p.parse_file(filename);
175+
176+
std::vector<uint8_t> data{0x27, 0x08, 0x22, 0xa3, 0x1f, 0xe5, 0x14, 0x45}; // little endian
177+
std::vector<double> result_values;
178+
REQUIRE(p.parseMessage(0x21d, data, result_values) == true);
179+
REQUIRE(result_values.size() == 4);
180+
REQUIRE(Catch::Approx(result_values.at(0)) == 99.92);
181+
REQUIRE(Catch::Approx(result_values.at(1)) == 88.67);
182+
REQUIRE(Catch::Approx(result_values.at(2)) == 81.65);
183+
REQUIRE(Catch::Approx(result_values.at(3)) == 11.89);
184+
185+
}
186+
187+
TEST_CASE("Parse Message big endian") {
188+
const auto* filename = std::tmpnam(NULL);
189+
190+
auto* file = std::fopen(filename, "w");
191+
CHECK(file);
192+
193+
std::fputs(PRIMITIVE_DBC.c_str(), file);
194+
std::fputs(R"(BO_ 541 STATUS: 8 DEVICE1
195+
SG_ Temperature : 48|16@0+ (0.01,-40) [-40|125] "C" DEVICE1
196+
SG_ SOH : 0|16@0+ (0.01,0) [0|100] "%" DEVICE1
197+
SG_ SOE : 32|16@0+ (0.01,0) [0|100] "%" DEVICE1
198+
SG_ SOC : 16|16@0+ (0.01,0) [0|100] "%" DEVICE1)", file);
199+
std::fclose(file);
200+
201+
libdbc::DbcParser p(true);
202+
p.parse_file(filename);
203+
204+
std::vector<uint8_t> data{0x08, 0x27, 0xa3, 0x22, 0xe5, 0x1f, 0x45, 0x14}; // big endian
205+
std::vector<double> result_values;
206+
REQUIRE(p.parseMessage(0x21d, data, result_values) == true);
207+
REQUIRE(result_values.size() == 4);
208+
REQUIRE(Catch::Approx(result_values.at(0)) == 99.92);
209+
REQUIRE(Catch::Approx(result_values.at(1)) == 88.67);
210+
REQUIRE(Catch::Approx(result_values.at(2)) == 81.65);
211+
REQUIRE(Catch::Approx(result_values.at(3)) == 11.89);
212+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
cmake_minimum_required(VERSION 3.23)
2+
# FILE_SET needs cmake 3.23
3+
4+
project(bitstream)
5+
6+
add_library(${PROJECT_NAME} STATIC bitstream.c)
7+
target_sources(${PROJECT_NAME} INTERFACE FILE_SET HEADERS
8+
TYPE HEADERS
9+
BASE_DIRS ${PROJECT_SOURCE_DIR}
10+
FILES bitstream.h)

third_party/bitstream/Readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Bitstream reader.
2+
Files copied from https://github.com/eerimoq/bitstruct/tree/master/bitstruct

0 commit comments

Comments
 (0)