Skip to content
Permalink
Browse files
First!
  • Loading branch information
David committed Nov 3, 2022
1 parent ea5931f commit f6c49bff76ee9aa4d1aa6cde598de54e87890da4
Show file tree
Hide file tree
Showing 9 changed files with 635 additions and 0 deletions.
@@ -9,3 +9,7 @@ install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps

bin/
src_autogen/
lib/
@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 17)

project(dbc_example C CXX)

# location of source code files
include_directories( include )

# tell cmake where to put the executables that it creates
file( MAKE_DIRECTORY bin )
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY bin )

# where to put the object files it creates
file( MAKE_DIRECTORY lib )
SET( LIBRARY_OUTPUT_PATH lib )

# autogenerate code from the DBC file
file( MAKE_DIRECTORY src_autogen )
add_custom_command( OUTPUT src_autogen/data.h src_autogen/data.c
COMMAND python3 -m cantools generate_c_source data.dbc -o src_autogen
MAIN_DEPENDENCY data.dbc
COMMENT "Autogenerate code from DBC file" )

# create shared library of the autogenerated code from the DBC file
add_library( data SHARED src_autogen/data.c )
target_include_directories( data PUBLIC src_autogen )

add_executable( demo_dbc src/demo_dbc.cpp )
target_link_libraries( demo_dbc PRIVATE data )

add_executable( demo src/demo.cpp )

add_executable( demo_raw_decode src/demo_raw_decode.cpp )

add_executable( demo_send src/demo_send.cpp )
@@ -0,0 +1,60 @@
VERSION ""


NS_ :
NS_DESC_
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_DEF_DEF_REL_
BU_SG_REL_
BU_EV_REL_
BU_BO_REL_
SG_MUL_VAL_

BS_:

BU_: Auto VCU
BU_: Auto DASH

BO_ 392 indicator_control: 4 DASH
SG_ Right_indicator : 1|1@1+ (1,0) [0|1] "" Vector__XXX
SG_ Left_indicator : 0|1@1+ (1,0) [0|1] "" Vector__XXX

VAL_ 392 Right_indicator 0 "OFF" 1 "ON";
VAL_ 392 Left_indicator 0 "OFF" 1 "ON";

BO_ 580 speed: 5 ECU
SG_ Speed : 31|16@0+ (1,0) [0|48280] "dam/h" Vector__XXX

CM_ "SG_ Speed : 31|16@0+ (1,0) [0|48280] \"dam/h\" Vector__XXX
Is an example using decameters per hour. This is the format of the raw
data actually sent over the CAN bus.
Dam/h is not, however, a commonly used unit of measurement and so the
following message specification might be preferable. The scale factor
is used to adjust the raw values.
SG_ Speed : 31|16@0+ (0.01,0) [0|482.8] \"km/h\" Vector__XXX";


BA_DEF_ "BusType" STRING ;
BA_DEF_DEF_ "BusType" "CAN";

@@ -0,0 +1,193 @@
#ifndef CAN_WRAP_H
#define CAN_WRAP_H

#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>

#include <linux/can.h>
#include <linux/can/raw.h>

#include <cstring>
#include <iomanip>
#include <iostream>
#include <stdexcept>

namespace can
{
/**
* @brief Opens socket for CAN bus communication via socketcan.
* Returns socket identifier which is used as an argument for
* future can::read(), can::write() and can::close() calls.
*
* @throws std::runtime_error If socket creation failed.
* @throws std::runtime_error If could not enable CANFD.
* @throws std::runtime_error If could not bind socket.
*
* @param fd Open as normal CAN or CANFD.
* @param channel Network channel to use, e.g. can0 or vcan0.
* @return const int Socket identifier.
*/
const int _connect( const bool fd, const std::string& channel )
{
const int canSocket = socket( PF_CAN, SOCK_RAW, CAN_RAW );
if( canSocket < 0 )
throw std::runtime_error( "Create socket failed" );

struct ifreq ifr;
std::strncpy( ifr.ifr_name, channel.c_str(), sizeof(ifr.ifr_name)-1 );
ioctl( canSocket, SIOCGIFINDEX, &ifr );

struct sockaddr_can addr;
std::memset( &addr, 0, sizeof(addr) );
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;

int enableCanFd = 1;
if( fd && setsockopt( canSocket, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
&enableCanFd, sizeof(enableCanFd) ) )
throw std::runtime_error( "Enable CAN FD failed" );

if( bind( canSocket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr) ) < 0 )
throw std::runtime_error( "Bind socket failed" );

return canSocket;
}

/**
* @brief Passes call to can::_connect with fd argument as false.
*/
inline const int connect( const std::string channel="vcan0" )
{
return _connect( false, channel );
}

/**
* @brief Passes call to can::_connect with fd argument as true.
*/
inline const int connectfd( const std::string channel="vcan0" )
{
return _connect( true, channel );
}

/**
* @brief Close specified socketcan socket.
*
* @throws std::runtime_error If close failed.
*
* @param socket Socket identifer as returned from can::connect().
*/
void close( const int socket )
{
if( ::close( socket ) < 0 )
throw std::runtime_error( "Socket close failed" );
}

/**
* @brief Read next frame from CAN bus.
*
* @warning This function will block until a frame is available.
*
* @throws std::runtime_error If read failed.
*
* @param socket Socket identifer as returned from can::connect().
* @return const can_frame
*/
template<typename FRAME>
const FRAME _read( const int socket )
{
FRAME frame;

if( read( socket, &frame, sizeof(frame) ) < 0 )
throw std::runtime_error( "Read failed" );

return frame;
}

/**
* @brief Passes call to can::_read<can_frame>() to read standard CAN frame.
*
* @param socket Socket identifer as returned from can::connect().
* @return const can_frame
*/
inline const can_frame read( const int socket )
{
return _read<can_frame>( socket );
}

/**
* @brief Passes call to can::_read<canfd_frame>() to read CANFD frame.
*
* @param socket Socket identifer as returned from can::connect().
* @return const canfd_frame
*/
inline const canfd_frame readfd( const int socket )
{
return _read<canfd_frame>( socket );
}

/**
* @brief Send CAN frame.
*
* @throws std::runtime_error If write failed.
*
* @tparam FRAME can_frame or canfd_frame
* @param socket Socket identifer as returned from can::connect().
* @param frame CAN or CANFD frame to be sent.
* @param numBytes Number of bytes to be written.
*/
template<typename FRAME>
void _write( const int socket, const FRAME& frame, const uint8_t numBytes )
{
if( write( socket, &frame, numBytes ) != numBytes )
std::runtime_error( "Write failed" );
}

/**
* @brief Passes call to can::_write<can_frame>() to write standard CAN frame.
*
* @param socket Socket identifer as returned from can::connect().
* @param frame CAN frame to be sent.
*/
void write( const int socket, const can_frame& frame )
{
// 8 bytes for the frame header and the rest for the actual data.
_write( socket, frame, sizeof(can_frame) );
}

/**
* @brief Passes call to can::_write<canfd_frame>() to write CANFD frame.
*
* @param socket Socket identifer as returned from can::connect().
* @param frame CANFD frame to be sent.
*/
void write( const int socket, const canfd_frame& frame )
{
// 8 bytes for the frame header and the rest for the actual data.
_write( socket, frame, sizeof(canfd_frame) );
}


std::ostream& operator<<( std::ostream &os, const can_frame& frame )
{
os << "0x" << std::setw(3) << frame.can_id <<
" [" << static_cast<int>(frame.can_dlc) << ']';
for( int i=0; i<frame.can_dlc; ++i )
os << ' ' << std::setw(2) << static_cast<int>(frame.data[i]);

return os;
}

std::ostream& operator<<( std::ostream &os, const canfd_frame& frame )
{
os << "0x" << std::setw(3) << frame.can_id <<
" [" << static_cast<int>(frame.len) << ']';
for( int i=0; i<frame.len; ++i )
os << ' ' << std::setw(2) << static_cast<int>(frame.data[i]);

return os;
}
}

#endif
@@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <iomanip>
#include <iostream>
#include <string>

#include "can_wrap.h"
using can::operator<<;

int main( int argc, char* argv[] )
{
const std::string canChannel = "vcan0";
const int canSocket = can::connect( canChannel );

std::cout << std::hex << std::uppercase << std::setfill('0');

for( int c=0; c<100; ++c )
{
auto frame = can::read( canSocket );

std::cout << frame << std::endl;
}

can::close( canSocket );

return 0;
}

0 comments on commit f6c49bf

Please sign in to comment.