Giter Club home page Giter Club logo

dbc_parser_cpp's Introduction

C++ DBC Parser

This is to provide a library header only file to read in DBC files. I was looking around and couldn't find a simple library that didn't have dependencies. So here we are making one. I got some inspiration from the python dbc library here: https://pypi.org/project/cantools/

Building

I am using Cmake to be able to build the tests and the lib. I plan on doing more with it but this is what it is for now. This doesn't mean that IDEs aren't welcome but the build process might not be suited for this. You will need to modify it for your needs. Feel free to submit changes so the building process will be more robust.

Here are the steps to get started:

# Release Build
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild -H.

# Debug Build
cmake -DCMAKE_BUILD_TYPE=Debug -Bbuild -H.

# Run the build
cmake --build build

Listing Build Options

You can check the latest build options with cmake. After you configure cmake you can run this.

cd build

# List this projects options
cmake -LH .. | grep -B1 "DBC_"

# To see all the included project cache variables and options
cmake -LAH ..

Creating a Single Header File

It requires you have cargo installed from rust. See these instructions if you don't have that https://www.rust-lang.org/tools/install. It uses the https://github.com/Felerius/cpp-amalgamate crate to do the single header file creation.

The output will be generated in the build/single_header/libdbc/ folder. You can run a cmake command to build this as well as other targets.

To just build the single header you can simply run the target:

cmake -Bbuild -H. -DDBC_GENERATE_SINGLE_HEADER=ON

cmake --build build --parallel `nproc` --target single_header

Testing

I am trying to always make sure that this is very well tested code. I am using Catch2 to do this testing and if you aren't familiar here is the documentation: https://github.com/catchorg/Catch2/blob/master/docs/Readme.md#top

There is one option you will want for testing: DBC_TEST_LOCALE_INDEPENDENCE. This requires the de_DE.UTF-8 locale installed to test. It is for checking we don't rely on locale to convert floats. i.e. 1.23 vs 1,23

You will need to configure the project to enable this: cmake -DCMAKE_BUILD_TYPE=Release -DDBC_TEST_LOCALE_INDEPENDENCE=ON -Bbuild -H.. You will get a warning if it isn't enabled because it isn't enabled by default.

To run the tests locally you can use the following. Assuming you have built the project you should get a test executable.

ctest --output-on-failure --test-dir build

Scripts

To use the scripts in scripts/ you will need to install the requirements from the top level directory.

pip install -r script-requirements.txt

Contributing

I welcome all help! Please feel free to fork and start some pull requests! You can see the issues sections for some ideas what might need to be done.

dbc_parser_cpp's People

Contributors

gerlachs avatar linuxdevon avatar murmele avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

dbc_parser_cpp's Issues

parseSignals should fail if frame data lenght is too small

Hello. Doing some test I got what I consider unexpected results as I was sending incomplete frames; i.e. frames where the data buffer was not large enough to actually contain signal data..

Here is a draft of a patch I would like you to consider. Not certain if the check is correct but it does appear to "patch" the issue, preventing unexpected parsing success. I certainly would like to have your point of view on this.

dbc_parser_cpp$ git diff | sed 's/^M//g'
diff --git a/include/libdbc/message.hpp b/include/libdbc/message.hpp
index e903fed..b31b960 100644
--- a/include/libdbc/message.hpp
+++ b/include/libdbc/message.hpp
@@ -19,6 +19,7 @@ struct Message {
                ErrorBigEndian,
                ErrorUnknownID,
                ErrorInvalidConversion,
+               ErrorMessageTooShort, // or something...
        };

        /*!
diff --git a/src/message.cpp b/src/message.cpp
index 443526a..97233c0 100644
--- a/src/message.cpp
+++ b/src/message.cpp
@@ -31,6 +31,8 @@ Message::ParseSignalsStatus Message::parseSignals(const std::vector<uint8_t>& da
        const auto len = size * 8;
        uint64_t v = 0;
        for (const auto& signal : m_signals) {
+               if (signal.size > len)
+                       return ParseSignalsStatus::ErrorMessageTooShort;
                if (signal.is_bigendian) {
                        uint32_t start_bit = 8 * (signal.start_bit / 8) + (7 - (signal.start_bit % 8)); // Calculation taken from python CAN
                        v = data_big_endian << start_bit;

Feature: Better error messages

Currently only a validity_error() is thrown if the dbc file is not correct. Maybe introducing an enum which will be returned with the error to indicate where the error is located and a function to convert the enum to a string error message.

dbc_parser_cpp/src/dbc.cpp

Lines 204 to 250 in 753119d

Message::ParseSignalsStatus DbcParser::parseMessage(const uint32_t id, const std::vector<uint8_t>& data, std::vector<double>& out_values) {
for (const auto& message : messages) {
if (message.id() == id) {
return message.parseSignals(data, out_values);
}
}
return Message::ParseSignalsStatus::ErrorUnknownID;
}
void DbcParser::parse_dbc_header(std::istream& file_stream) {
std::string line;
std::smatch match;
utils::StreamHandler::get_line(file_stream, line);
if (!std::regex_search(line, match, version_re)) {
throw validity_error();
}
version = match.str(2);
utils::StreamHandler::get_next_non_blank_line(file_stream, line);
utils::StreamHandler::skip_to_next_blank_line(file_stream, line);
utils::StreamHandler::get_next_non_blank_line(file_stream, line);
if (!std::regex_search(line, match, bit_timing_re)) {
throw validity_error();
}
}
void DbcParser::parse_dbc_nodes(std::istream& file_stream) {
std::string line;
std::smatch match;
utils::StreamHandler::get_next_non_blank_line(file_stream, line);
if (!std::regex_search(line, match, node_re)) {
throw validity_error();
}
if (match.length() > 2) {
std::string n = match.str(2);
utils::String::split(n, nodes);
}
}
void DbcParser::parse_dbc_messages(const std::vector<std::string>& lines) {

unhandled error on invalid DBC content

Building my own tests - inspired by yours - I came across a case that crashes the library.
I think adding a unit test with DBC content below should reproduce the issue on your side.
Notice the frame id on a new line after BO_

VERSION "1.0.0"

NS_ :

BS_:

BU_: DBG DRIVER IO MOTOR SENSOR

BO_
234 MSG1: 8 Vector__XXX
 SG_ State1 : 0|8@1+ (1,0) [0|200] "Km/h"  DEVICE1,DEVICE2,DEVICE3
 SG_ State2 : 0|8@1+ (1,0) [0|204] "" DEVICE1,DEVICE2,DEVICE3
VAL_ 234 State1 123 "Description 1" 0 "Description 2" 90903489 "Big value and special characters &$§())!" ;

For completness, here is the relevant callstack:

Program received signal SIGSEGV, Segmentation fault.
0x00005555556b1fc0 in std::vector<libdbc::Signal, std::allocator<libdbc::Signal> >::push_back(libdbc::Signal const&) ()
(gdb) where
#0  0x00005555556b1fc0 in std::vector<libdbc::Signal, std::allocator<libdbc::Signal> >::push_back(libdbc::Signal const&) ()
#1  0x00005555556b1c29 in libdbc::Message::appendSignal(libdbc::Signal const&) ()
#2  0x00005555556844cb in libdbc::DbcParser::parse_dbc_messages(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&) ()
#3  0x00005555556836b6 in libdbc::DbcParser::parse_file(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ()

parse dbc with istream instead of filename

Hi Devon! Thanks for the great lib! I selected it because of its simplicity.

void DbcParser::parse_file(const std::string& file) {
	std::ifstream s(file.c_str());
(...)

The file argument is only needed for the ifstream .
I would suggest to expose an std::istream overload in which case hitting the filesystem would not be required.
This looks like a low hanging fruit while the current limitation causes my current project to depend on writable storage which is highly inconvenient.

Thanks in advance!
Also, reading the announcement, you can make it a c++17 project if you ask me :)

Feature: Implement empty BU_

BU_ field is available, but is empty. According to the documentation there is nothing against an empty BU_ field
http://mcu.so/Microcontroller/Automotive/dbc-file-format-documentation_compress.pdf

VERSION ""


NS_ : 

BS_:

BU_:


BO_ 293 Msg1: 2 Vector__XXX
 SG_ Wert7 : 0|16@1- (1,0) [0|0] "" Vector__XXX

BO_ 292 Msg2: 8 Vector__XXX
 SG_ Wert8 : 56|8@1- (1,0) [0|0] "" Vector__XXX
 SG_ Wert6 : 32|24@1- (1,0) [0|0] "km/h" Vector__XXX
 SG_ Wert5 : 24|8@1- (1,0) [0|0] "" Vector__XXX
 SG_ Wert4 : 0|24@1- (1,0) [0|0] "km/h" Vector__XXX

BO_ 291 Msg3: 8 Vector__XXX
 SG_ Wert3 : 48|16@1- (1,0) [0|0] "" Vector__XXX
 SG_ Wert2 : 24|8@1- (1,0) [0|0] "" Vector__XXX
 SG_ Wert1 : 32|16@1- (1,0) [0|0] "" Vector__XXX
 SG_ Speed : 0|24@1- (1,0) [0|0] "km/h" Vector__XXX

CMake minimum required version issue

Building with CMake version 3.16 up to 3.22 fails. CMakeLists.txt uses FILE_SET which requires CMake 3.23 but minimum required CMake version is set to 3.16.

what() methods are private

Why the what methods are private?

	class exception : public std::exception {
		const char * what() const throw() {
			return "libdbc exception occurred";
		}
	};

	class validity_error : public exception {
		const char * what() const throw() {
			return "Invalid DBC file...";
		}
	};

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.