DBC Framework
DBC parsing and CAN signal decoding framework
dbc_parser.cpp
Go to the documentation of this file.
1
12#include "dbc_parser.h"
13
14#include <fstream>
15#include <sstream>
16#include <stdexcept>
17#include <cctype>
18
19namespace {
25 std::string TrimText (const std::string &text) {
26 std::string::size_type begin = 0U;
27 while ((begin < text.size()) &&
28 std::isspace (static_cast<unsigned char> (text[begin])))
29 ++begin;
30
31 std::string::size_type end = text.size();
32 while ((end > begin) &&
33 std::isspace (static_cast<unsigned char> (text[end - 1U])))
34 --end;
35
36 return text.substr (begin, end - begin);
37 }
38}
39
40DbcDatabase DbcParser::ParseFile (const std::string &filePath) const {
41 std::ifstream input (filePath.c_str());
42 if (!input.is_open())
43 throw std::runtime_error ("Failed to open DBC file: " + filePath);
44
45 DbcDatabase database;
46 FrameInfo *currentFrame = nullptr;
47
48 std::string line;
49 while (std::getline (input, line)) {
50 line = Trim (line);
51 if (line.empty())
52 continue;
53
54 if (IsFrameLine (line)) {
55 FrameInfo frame = ParseFrameLine (line);
56 database.frames.push_back (frame);
57 currentFrame = &database.frames.back();
58 } else if (IsSignalLine (line)) {
59 if (currentFrame == nullptr)
60 throw std::runtime_error ("Signal found before any frame definition.");
61
62 SignalInfo signal = ParseSignalLine (line);
63 currentFrame->signals.push_back (signal);
64 } else if (IsCommentLine (line))
65 ParseCommentLine (line, database);
66 }
67
68 return database;
69}
70
71bool DbcParser::IsFrameLine (const std::string &line) {
72 return (line.size() >= 4U) && (line.compare (0U, 4U, "BO_ ") == 0);
73}
74
75bool DbcParser::IsSignalLine (const std::string &line) {
76 return (line.size() >= 4U) && (line.compare (0U, 4U, "SG_ ") == 0);
77}
78
79bool DbcParser::IsCommentLine (const std::string &line) {
80 return (line.size() >= 4U) && (line.compare (0U, 4U, "CM_ ") == 0);
81}
82
83std::string DbcParser::Trim (const std::string &text) {
84 return TrimText (text);
85}
86
87std::vector<std::string> DbcParser::SplitReceivers (const std::string &text) {
88 std::vector<std::string> receivers;
89 std::string token;
90 std::istringstream stream (text);
91
92 while (std::getline (stream, token, ',')) {
93 token = TrimText (token);
94 if (!token.empty())
95 receivers.push_back (token);
96 }
97
98 return receivers;
99}
100
101void DbcParser::NormalizeCanId (std::uint32_t rawCanId,
102 std::uint32_t &normalizedCanId,
103 bool &isExtended) {
104 /*
105 * DBC commonly stores extended identifiers with bit 31 set.
106 * Example:
107 * raw id = 0x80000000 | actual_29_bit_id
108 */
109 if ((rawCanId & 0x80000000U) != 0U) {
110 isExtended = true;
111 normalizedCanId = (rawCanId & 0x1FFFFFFFU);
112 } else {
113 isExtended = (rawCanId > 0x7FFU);
114 normalizedCanId = rawCanId;
115 }
116}
117
118std::uint32_t DbcParser::TryExtractPgn (std::uint32_t canId, bool isExtended, bool &hasPgn) {
119 hasPgn = false;
120
121 if (!isExtended)
122 return 0U;
123
124 if ((canId & 0x1FFFFFFFU) != canId)
125 return 0U;
126
127 const std::uint32_t pf = (canId >> 16U) & 0xFFU;
128 const std::uint32_t ps = (canId >> 8U) & 0xFFU;
129 const std::uint32_t dp = (canId >> 24U) & 0x01U;
130
131 std::uint32_t pgn = 0U;
132
133 if (pf < 240U)
134 pgn = (dp << 16U) | (pf << 8U);
135 else
136 pgn = (dp << 16U) | (pf << 8U) | ps;
137
138 hasPgn = true;
139 return pgn;
140}
141
142FrameInfo DbcParser::ParseFrameLine (const std::string &line) {
143 /*
144 * Example:
145 * BO_ 256 EngineData: 8 EEC1
146 */
147
148 std::istringstream stream (line);
149 std::string token;
150 FrameInfo frame;
151 std::uint32_t rawCanId = 0U;
152
153 stream >> token;
154 if (token != "BO_")
155 throw std::runtime_error ("Invalid frame line: " + line);
156
157 stream >> rawCanId;
158 NormalizeCanId (rawCanId, frame.canId, frame.isExtended);
159
160 stream >> token;
161 if (token.empty())
162 throw std::runtime_error ("Missing frame name: " + line);
163
164 if (token[token.size() - 1U] == ':')
165 token.erase (token.size() - 1U, 1U);
166
167 frame.name = token;
168
169 {
170 unsigned int dlcValue = 0U;
171 stream >> dlcValue;
172 frame.dlc = static_cast<std::uint8_t> (dlcValue);
173 }
174
175 stream >> frame.transmitter;
176 frame.pgn = TryExtractPgn (frame.canId, frame.isExtended, frame.hasPgn);
177
178 return frame;
179}
180
181SignalInfo DbcParser::ParseSignalLine (const std::string &line) {
182 /*
183 * Example:
184 * SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8000] "rpm" ECU1,ECU2
185 */
186
187 SignalInfo signal;
188
189 std::string work = TrimText (line);
190 if (work.compare (0U, 4U, "SG_ ") != 0)
191 throw std::runtime_error ("Invalid signal line: " + line);
192
193 work.erase (0U, 4U);
194
195 const std::string::size_type colonPos = work.find (':');
196 if (colonPos == std::string::npos)
197 throw std::runtime_error ("Signal line missing ':' : " + line);
198
199 signal.name = TrimText (work.substr (0U, colonPos));
200 std::string rest = TrimText (work.substr (colonPos + 1U));
201
202 const std::string::size_type pipePos = rest.find ('|');
203 const std::string::size_type atPos = rest.find ('@');
204 const std::string::size_type signPos = rest.find_first_of ("+-", atPos);
205 const std::string::size_type factorBegin = rest.find ('(');
206 const std::string::size_type factorComma = rest.find (',', factorBegin);
207 const std::string::size_type factorEnd = rest.find (')', factorComma);
208 const std::string::size_type rangeBegin = rest.find ('[');
209 const std::string::size_type rangeSep = rest.find ('|', rangeBegin);
210 const std::string::size_type rangeEnd = rest.find (']', rangeSep);
211 const std::string::size_type unitBegin = rest.find ('"', rangeEnd);
212 const std::string::size_type unitEnd = rest.find ('"', unitBegin + 1U);
213
214 if ((pipePos == std::string::npos) ||
215 (atPos == std::string::npos) ||
216 (signPos == std::string::npos) ||
217 (factorBegin == std::string::npos) ||
218 (factorComma == std::string::npos) ||
219 (factorEnd == std::string::npos) ||
220 (rangeBegin == std::string::npos) ||
221 (rangeSep == std::string::npos) ||
222 (rangeEnd == std::string::npos) ||
223 (unitBegin == std::string::npos) ||
224 (unitEnd == std::string::npos))
225 throw std::runtime_error ("Unsupported signal syntax: " + line);
226
227 signal.startBit = static_cast<std::uint32_t> (
228 std::stoul (TrimText (rest.substr (0U, pipePos)))
229 );
230
231 signal.length = static_cast<std::uint32_t> (
232 std::stoul (TrimText (rest.substr (pipePos + 1U, atPos - pipePos - 1U)))
233 );
234
235 {
236 if ((atPos + 1U) >= rest.size())
237 throw std::runtime_error ("Invalid endianness in signal: " + line);
238
239 const char endianChar = rest[atPos + 1U];
240 signal.isLittleEndian = (endianChar == '1');
241 }
242
243 {
244 const char signChar = rest[signPos];
245 signal.isSigned = (signChar == '-');
246 }
247
248 signal.factor = std::stod (
249 TrimText (rest.substr (factorBegin + 1U, factorComma - factorBegin - 1U))
250 );
251
252 signal.offset = std::stod (
253 TrimText (rest.substr (factorComma + 1U, factorEnd - factorComma - 1U))
254 );
255
256 signal.minimum = std::stod (
257 TrimText (rest.substr (rangeBegin + 1U, rangeSep - rangeBegin - 1U))
258 );
259
260 signal.maximum = std::stod (
261 TrimText (rest.substr (rangeSep + 1U, rangeEnd - rangeSep - 1U))
262 );
263
264 signal.unit = rest.substr (unitBegin + 1U, unitEnd - unitBegin - 1U);
265
266 {
267 const std::string receiversText = TrimText (rest.substr (unitEnd + 1U));
268 signal.receivers = SplitReceivers (receiversText);
269 }
270
271 return signal;
272}
273
274void DbcParser::ParseCommentLine (const std::string &line, DbcDatabase &database) {
275 /*
276 * Examples:
277 * CM_ BO_ 256 "Frame comment";
278 * CM_ SG_ 256 EngineSpeed "Signal comment";
279 */
280
281 std::istringstream stream (line);
282 std::string token;
283 stream >> token;
284
285 if (token != "CM_")
286 return;
287
288 stream >> token;
289
290 if (token == "BO_") {
291 std::uint32_t rawCanId = 0U;
292 std::uint32_t canId = 0U;
293 bool isExtended = false;
294
295 stream >> rawCanId;
296 NormalizeCanId (rawCanId, canId, isExtended);
297
298 const std::string::size_type quoteBegin = line.find ('"');
299 const std::string::size_type quoteEnd = line.rfind ('"');
300
301 if ((quoteBegin == std::string::npos) ||
302 (quoteEnd == std::string::npos) ||
303 (quoteEnd <= quoteBegin))
304 return;
305
306 FrameInfo *frame = FindFrameById (database, canId, isExtended);
307 if (frame != nullptr)
308 frame->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
309 } else if (token == "SG_") {
310 std::uint32_t rawCanId = 0U;
311 std::uint32_t canId = 0U;
312 bool isExtended = false;
313 std::string signalName;
314
315 stream >> rawCanId;
316 stream >> signalName;
317
318 NormalizeCanId (rawCanId, canId, isExtended);
319
320 const std::string::size_type quoteBegin = line.find ('"');
321 const std::string::size_type quoteEnd = line.rfind ('"');
322
323 if ((quoteBegin == std::string::npos) ||
324 (quoteEnd == std::string::npos) ||
325 (quoteEnd <= quoteBegin))
326 return;
327
328 FrameInfo *frame = FindFrameById (database, canId, isExtended);
329 if (frame != nullptr) {
330 SignalInfo *signal = FindSignalByName (*frame, signalName);
331 if (signal != nullptr)
332 signal->comment = line.substr (quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
333 }
334 }
335}
336
337FrameInfo *DbcParser::FindFrameById (DbcDatabase &database,
338 std::uint32_t canId,
339 bool isExtended) {
340 for (std::size_t index = 0U; index < database.frames.size(); ++index) {
341 if ((database.frames[index].canId == canId) &&
342 (database.frames[index].isExtended == isExtended))
343 return &database.frames[index];
344 }
345
346 return nullptr;
347}
348
349SignalInfo *DbcParser::FindSignalByName (FrameInfo &frame, const std::string &signalName) {
350 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
351 if (frame.signals[index].name == signalName)
352 return &frame.signals[index];
353 }
354
355 return nullptr;
356}
DbcDatabase ParseFile(const std::string &filePath) const
Parse DBC file.
Definition: dbc_parser.cpp:40
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Parsed DBC content stored in a simple internal form.
Definition: dbc_database.h:22
std::vector< FrameInfo > frames
Definition: dbc_database.h:23
Describes one CAN frame from a DBC file.
Definition: frame_info.h:24
bool isExtended
Definition: frame_info.h:27
bool hasPgn
Definition: frame_info.h:29
std::string transmitter
Definition: frame_info.h:31
std::uint8_t dlc
Definition: frame_info.h:30
std::string name
Definition: frame_info.h:25
std::string comment
Definition: frame_info.h:32
std::uint32_t pgn
Definition: frame_info.h:28
std::uint32_t canId
Definition: frame_info.h:26
Describes one signal inside a DBC frame.
Definition: signal_info.h:22
std::uint32_t length
Definition: signal_info.h:25
std::string unit
Definition: signal_info.h:32
std::vector< std::string > receivers
Definition: signal_info.h:33
std::string name
Definition: signal_info.h:23
bool isSigned
Definition: signal_info.h:27
double offset
Definition: signal_info.h:29
std::string comment
Definition: signal_info.h:34
std::uint32_t startBit
Definition: signal_info.h:24
double minimum
Definition: signal_info.h:30
double maximum
Definition: signal_info.h:31
double factor
Definition: signal_info.h:28
bool isLittleEndian
Definition: signal_info.h:26