Giter VIP home page Giter VIP logo

dbcc's Introduction

dbcc

DBC converter/compiler

This program turns a DBC file into a number of different formats.

CAN-FD IS CURRENTLY NOT SUPPORTED.

Please consider donating to the project if you find it useful. This project requires your support to continue. If you require paid support please contact mailto:[email protected].

As you can see by the big note above, CAN-FD is currently not supported, and likely will not (for a while). The problem is that CAN-FD allows for messages above 64-bits in length and this project and the code generated from it makes the assumption that 8-bits is all is intended a CAN message to have in it.

Introduction

dbcc is a program for converting a DBC file primarily into into C code that can serialize and deserialize CAN messages into structures that represent those messages and signals. It is also possible to print out the information contained in a structure.

Building, Licenses and Dependencies

See the license file for details of the license for this program, it is released under the MIT license. Dependencies, if linked against, may have their own license and their own set of restrictions if built against.

The sources file mpc.c and mpc.h originate from a parser combinator written in C called MPC and are licensed under the 3 Clause BSD license.

To build, you only need a C (C99) compiler and Make (probably GNU make, I make no effort to support other Make implementations). The dbcc program itself it written in what should be portable C with the only external dependency being your platforms C library.

You should be able to type:

make

To build, an executable called 'dbcc' is produced. To test run the tests, xmllint is required.

C Coding Standards

  • When in doubt, format with indent with the "-linux" option.
  • Use tabs, not spaces for formatting
  • Use assertions where possible (not for error checking, for checking pre/post conditions and invariants).
  • The tool should run on Windows and Linux with no modification. The project is written in pure C.
  • No external dependencies should brought into the project.

Notes on generated code

  • If you want a specific format in the generated code, integrate indent into you toolchain instead of trying to change the code generator.
  • The output of the generated code is generally not stable, it may change from commit to commit, download and maintain a specific version if you want stability.
  • That said, the -n option can be used to specify the output version which might give you access to previous behaviour if backwards compatibility has been implemented.

Generated output versions

You can specify the version to use on a command line with the -n option. The latest version will be used by default.

Version 1:

Legacy/original behaviour. Note that this still won't provide a stable output, but will have a better chance of not having breaking changes.

Version 2:

  • Latest version

  • Enum names are qualified with the CAN message name

  • encode/decode function names are also qualified with the message name

How to use the generated code

The code generator can make code to unpack a message (turn some bytes into a data structure), decode a message (apply a scaling/offset minimum and maximum values to the values in a data structure), and the inverse can be done (pack/encode).

You can look at the code generated from the DBC files within the project to get an understanding of how it should work.

If you want to process a CAN message that you have received you will need to call the 'unpack_message'. The code generate is agnostic to the CPUs byte order, it takes a 'uint64_t' value containing a single CAN packet (along with the CAN ID and the DLC for the that packet) and unpacks that into a structure it generates. The first byte of the CAN packet should be put in the least significant byte of the 'uint64_t'.

You can use the following functions to convert to/from a CAN message:

static uint64_t u64_from_can_msg(const uint8_t m[8]) {
	return ((uint64_t)m[7] << 56) | ((uint64_t)m[6] << 48) | ((uint64_t)m[5] << 40) | ((uint64_t)m[4] << 32) 
		| ((uint64_t)m[3] << 24) | ((uint64_t)m[2] << 16) | ((uint64_t)m[1] << 8) | ((uint64_t)m[0] << 0);
}

static void u64_to_can_msg(const uint64_t u, uint8_t m[8]) {
	m[7] = u >> 56;
	m[6] = u >> 48;
	m[5] = u >> 40;
	m[4] = u >> 32;
	m[3] = u >> 24;
	m[2] = u >> 16;
	m[1] = u >>  8;
	m[0] = u >>  0;
}

The code generator will make a structure based on the file name of the DBC file, so for the example DBC file 'ex1.dbc' a data structure called 'can_obj_ex1_h_t' is made. This structure contains all of the CAN message structures, which in turn contain all of the signals. Having all of the messages/signals in one structure has advantages and disadvantages, one of the things it makes easier is defining the data structures needed.

/* reminder of the 'unpack_message' prototype */
int unpack_message(can_obj_ex1_h_t *o, const unsigned long id, uint64_t data, uint8_t dlc);

static can_obj_ex1_h_t ex1;

uint8_t can_message_raw[8];
unsigned long id = 0;
uint8_t dlc = 0;
your_function_to_receive_a_can_message(can_message_raw, &id, &dlc);
if (unpack_message(&ex1, id, can_message_u64, dlc) < 0) {
	// Error Condition; something went wrong
	return -1;
}

'unpack_message' calls the correct unpack function for that ID, as an example for ID '0x020':

case 0x020: return unpack_can_0x020_MagicCanNode1RBootloaderAddress(&o->can_0x020_MagicCanNode1RBootloaderAddress, data, dlc);

The unpack function populates the message object in the 'can_obj_ex1_h_t' structure for that ID. The individual signals can then be decoded with the appropriate functions for that signal. For example:

uint16_t b = 0;
if (decode_can_0x020_MagicNode1R_BLAddy(o, &b)) {
	/* error */
}

To transmit a message, each signal has to be encoded, then the pack function will return a packed message.

Some other notes:

  • Asserts can be disabled with a command line option
  • An option to force the encode/decode function to only use the double width floating point type has been added, so different function types do not have to be dealt with by the programmer.
  • You can remove the message number from the functions and values generated, which is useful if your message numbers are changing a lot, however the names for each message and signal must then be unique.

DBC file specification

For a specification, as I understand it, of the DBC file format, see dbc.md. This is a work in progress.

DBC VIM syntax file

There is a Vim syntax file for DBC files in the project, called dbc.vim

XML Generation

As well as C, XML can be generated, the project contains an XSD and XSLT file for the generated XML.

BSM (beSTORM Module) Generation

An XML based file that can be imported into Beyond Security's beSTORM and used to test CAN BUS infrastructure.

  • Note: May be replaced with an XSLT in the future, deriving from the XML output. The BSM output is itself and XML file.

CSV Generation

A flat CSV file can be generated, which is easier to import into Excel.

JSON Generation

A JSON file can be generated, which is what all the cool kids use nowadays.

Operation

Consult the manual page for more information about the precise operation of the program.

Bugs / To Do

  • Generated manual page from this markdown "readme.md" file.
  • For versions going forward, especially versions that break the generated C code, it might be nice to have an option to generate previous versions of the code.
  • Support CAN-FD (big task).
  • Make definitions for message-ids and Data-Length-Codes so the user does not have to make them as either an enumeration or a define.
  • Make the bit-fields more useful
  • The floating point conversion routines assume your platform is using IEEE-754 floats. If it does not, then tough.
  • A lot of the DBC file format is not dealt with:
    • Special values
    • Timeouts
    • Error frames
    • ...
  • The generated code is not MISRA C compliant.
  • Integers that cannot be represented in a double width floating point number should be packed/unpacked correctly, however the encode/decode and printing functions will not as they use doubles for calculations (pack/unpack do not). This affects numbers larger than 2^53.
  • There are two pieces of information that are useful to any CAN stack for received messages; the time stamp of the received message, and the status (error CRC/timeout, message okay, or message never set). This information could be included in the generated C code.
  • Enumeration values could be checked for and only correct values should be decoded, and encoded. There is more stuff that can be done with the enumeration values, along with command line options to enumeration generation.
  • A mechanism for callbacks for custom code for floating point encoding and decoding, and other callbacks in general, could be added. Packing and unpacking floats is done in what should be a portable, but not fast, way.
  • A mechanism and system for error handling should be added, that is, a simple communications manager that does the following:
    • Each signal should have three values associated with it; Unknown (the signal has never been set), Valid, and Error (for any error).
    • If the time out for a message goes out, or a CRC check fails, then all of the messages child messages should get invalidated and set the error state.
    • If the signal contains an invalid value, it gets set to an Error state.
    • All signal access functions should check the Unknown/Error value, returning success only on a Valid signal. There are a few other details that would need to be sorted out, like how CRC and time outs could be calculated.
  • The code generator makes code for packing/encoding and unpacking/decoding, this could be done in one step to simplify the code and data structures, it means decoded/encoded values do not need to recalculated.

It would be possible to generate nice (ASCII ART) images that show how a message is structured, which helps in understanding the message in question, and is useful for documentation purposes, for example, something like:

Message Name: Example-1
Message ID: 0x10, 16
DLC: 1 (8-bits)
+-----+-----.-----.-----.-----+-----.-----+-----+
|     |                       |           |     |
|     |                       |           |     |
+-----+-----.-----.-----.-----+-----.-----+-----+
   0     1     2     3     4     5     6     7
Bit     0: Signal-Name-1, 1 bit signal, scalar 1.0, offset 0
Bits  1-2: Signal-Name-2, 4 bit signal, signed, Motorola, ...
... etcetera ...

Or something similar. This would be another output module.

<style type="text/css"> body { margin:40px auto;max-width:850px;line-height:1.6;font-size:16px;color:#444;padding:0 10px } h1,h2,h3 { line-height:1.2 } </style>

dbcc's People

Contributors

badicsalex avatar chentyjpm avatar howerj avatar martonmiklos avatar melonedo avatar vmacari avatar warrickw avatar xr3b0rn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dbcc's Issues

Removing Struct Parameter of encode/decode function?

What about removing the struct parameter of the encode and decode functions, as it should always be the same for the signal. Or is there anything I miss, why the possibilty should be given?

bool decode_can_0x015_SUFUB_CURRENT_MAIN_A(can_0x015_SUFUB_STATUS_t *record, double *out); bool encode_can_0x015_SUFUB_CURRENT_MAIN_A(can_0x015_SUFUB_STATUS_t *record, double in);

to

bool decode_can_0x015_SUFUB_CURRENT_MAIN_A(double *out); bool encode_can_0x015_SUFUB_CURRENT_MAIN_A(double in) ;

why sig->start_bit <= 64 ?

assert(r == 1 && sig->start_bit <= 64);
function:static signal_t *ast2signal(mpc_ast_t *top, mpc_ast_t *ast, unsigned can_id)
file:can.c

thanks!

suggest: about decode param type

Thanks for the work~~

i have a problem

when i use the latest version, it convent different type of value in different types which cause me a little problem.

When use Code C I can not detect the value type in a function param

so when use code generate .... ... this way of coding is failed

`
#define DEC_MOD(obj,srcid,matchid,fun,ID)
if(srcid == matchid)
{
double tmpval = 0;
fun(&obj, &tmpval);
setval(report, ID, tmpval);
}

...
if (unpack_message(&canobj, canid, candat, 8) == 0)
{
DEC_MOD(&canobj, canid, 0x9002fff4, decode_can_0x9002fff4_BAT_CU, GBID_FC_VELCUR)
// 0x9002fff4.BAT_CU->GBID_FC_VELCUR
DEC_MOD(&canobj, canid, 0x9002fff4, decode_can_0x9002fff4_BAT_VOL, GBID_FC_VELVOL)
// 0x9002fff4.BAT_VOL->GBID_FC_VELVOL
...
`

maybe there can be a switch to change decode function param to all is in double like this?
or switch decode function like this

`
//current
int encode_can_0x9827fff7_TEM_SEN_23(can_obj_can_h_t *o, double in);
int decode_can_0x98eaff00_PGN(const can_obj_can_h_t *o, uint32_t *out);

//add paramtype in function name
int encode_double_can_0x9827fff7_TEM_SEN_23(can_obj_can_h_t *o, double in);
int decode_uint32_can_0x98eaff00_PGN(const can_obj_can_h_t *o, uint32_t *out);

//or all param to double
int encode_can_0x9827fff7_TEM_SEN_23(can_obj_can_h_t *o, double in);
int decode_can_0x98eaff00_PGN(const can_obj_can_h_t *o, double *out);
`
so i can use script to detect the function param type and process

Translation to physical unit

This dbc signal:
SG_ SIGNAL : 12|12@1- (0.5,0) [-1023.5|1023.5] "Nm"
Leads to this code:

rval *= 2.000000;
if((rval >= -1023.500000) && (rval <= 1023.500000)) {

in terms of the translation to the physical unit.
In my case this is defentetly wrong. canntools is doing it right so the dbc File must be correct and this must be a bug.

Maybe there are two kinds of translation:

  • Translation by reciprocal
  • Translation by value

And for now the program handels only the first one? - I don't know.

For me I fixed this by replacing
fprintf(o, "\trval *= %f;\n", 1.0 / sig->scaling);
with:
fprintf(o, "\trval *= %f;\n", sig->scaling);
in 2c.c:237.

Long signal value generates invalid C code.

The code generated from this following signal value is broken:

VAL_ 2147483938 IVT_SleepAck 15 "reserved 15" 14 "reserved 14" 13 "reserved 13" 12 "reserved 12" 11 "reserved 11" 10 "reserved 10" 9 "reserved 9" 8 "reserved 8" 7 "reserved 7" 6 "reserved 6" 5 "reserved 5" 4 "reserved 4" 3 "reserved 3" 2 "reserved 2" 1 "Sleep request acknowledged" 0 "Sleep request not acknowledged" ;

The broken code (look at the whitespaces)

typedef enum {
	IVT_SleepAck_Sleep request not acknowledged_e = 0,
	IVT_SleepAck_Sleep request acknowledged_e = 1, 
	IVT_SleepAck_reserved 2_e = 2,
	IVT_SleepAck_reserved 3_e = 3,
	IVT_SleepAck_reserved 4_e = 4,
	IVT_SleepAck_reserved 5_e = 5,
	IVT_SleepAck_reserved 6_e = 6,
	IVT_SleepAck_reserved 7_e = 7,
	IVT_SleepAck_reserved 8_e = 8,
	IVT_SleepAck_reserved 9_e = 9,
	IVT_SleepAck_reserved 10_e = 10,
	IVT_SleepAck_reserved 11_e = 11,
	IVT_SleepAck_reserved 12_e = 12,
	IVT_SleepAck_reserved 13_e = 13,
	IVT_SleepAck_reserved 14_e = 14,
	IVT_SleepAck_reserved 15_e = 15,
} IVT_SleepAck_e;

Thanks for the tool.
Compiling and using it was straightforward and intuitive :)

C-Code generation signal range check

The C-Code generator generates wrong code for range check. This code is generated:
if((rval <= -1023.500000) && (rval >= 1023.500000)) {
But the genrator should generate:
if((rval >= -1023.500000) && (rval <= 1023.500000)) {

To fix this, just change
fprintf(o, "\tif((rval <= %f) && (rval >= %f)) {\n", sig->minimum, sig->maximum);
in file 2c.c:239 to:
fprintf(o, "\tif((rval >= %f) && (rval <= %f)) {\n", sig->minimum, sig->maximum);

Can't get CM_ string when it has \".

Hello;

could you help me with a small issue? I'm trying to parse a dbc file that has the \" scape sequence on comments. It's making parse to stop to process with no error, but all the following fields are ignored.

I've tried to change the regex string but could not reach a satisfactory solution.

A example of string to parse:
CM_ SG_ 2628452094 MF_SourcePathNameLength "Length of \"source\" path name string for Move File";

Utilize getopt.h for command line argument processing?

Hello @howerj

First of all many thanks for sharing this project, it looks like is going to be very helpful for me!

Currently I am planning to add some features to the tool like:

  • Parametrizable indentation of the output source (tabs/spaces, number of spaces)
  • I would like to be able to generate code without generating the CAN ID in the function names

These changes would require to add more command line arguments, and handling them with the current processing method seems to be cumbersome. My suggestion would be to refactor the argument processing to use getopt.h provided functions. If you are open to such a change I can came up with a PR before start implementing my improvements.

Hide breaking changes behind feature flag

Prompted by issue #40, which has now been merged onto the branch feature/better-enums it would be wise to hide changes behind feature flags for any new breaking changes from now on, by either specifying a version (to get that versions behaviour), or by enabling a specific flag. The details need working out.

cannot decode messages that share same address

for example:
SG_ Speed_ms : 39|16@0+ (0.00514444,0) [0|0] "m/s" Vector__XXX
SG_ Speed_mph : 39|16@0+ (0.0115078,0) [0|0] "mph" Vector__XXX
SG_ Speed_kmh : 39|16@0+ (0.01852,0) [0|0] "km/h" Vector__XXX

it will report:
warning: Too many signals, not enough bytes (DLC is too low, fix your DBC file)

Support for DBC float and double types?

Hi,

I was wondering if you were planning on adding support for float and double types?

dbcc currently treats float and double types as a signed value of the same size (ie. int{32,64}_t) when generating code, which results in incorrect values being encoded/decoded.

From a quick scan through the code, it seems like you already have an attribute on the struct allocated for floating-point types (is_floating), and the codegen code accounts for it, but it just isn't getting set anywhere when parsing the DBC.

This is indicated via the SIG_VALTYPE_ attribute in the DBC.

A float value is denoted via

SIG_VALTYPE_ <can-id> <signame> : 1;

And a double is denoted via

SIG_VALTYPE_ <can-id> <signame> : 2;

Thanks!

dbcc cannot process the byte order mixed package

VehCAN_GHAC.zip

hello:
howerj,
The attachment is VehCAN_GHAC.dbc .
I found a bug ,When i run the following command:
dbcc VehCAN_GHAC.dbc
It will generate tow files,VehCAN_GHAC.c and VehCAN_GHAC.h

then i found that a package with byte order mixed can not be generate c code right.
first error: <*data = reverse_byte_order(m)|> c code has a syntax error,
second error: << shift operation is error when i process real can package, I can not get right result.

int pack_can_0x450_EVCM_cEM(can_0x450_EVCM_cEM_t pack, uint64_t data)
{
register uint64_t x;
register uint64_t m = 0;
register uint64_t i = 0;
/
ElecMachineSpeed: start-bit 7, length 16, endianess motorola, scaling 0.500000, offset -15000.000000 /
x = (
(uint16_t
)(&pack->ElecMachineSpeed)) & 0xffff;
x <<= 63;
m |= x;
/* ElecTorqueRequest: start-bit 31, length 13, endianess motorola, scaling 0.100000, offset -250.000000 /
x = (
(uint16_t*)(&pack->ElecTorqueRequest)) & 0x1fff;
x <<= 39;
m |= x;
/* AccelPedalPos: start-bit 23, length 8, endianess motorola, scaling 1.000000, offset 0.000000 /
x = (
(uint8_t*)(&pack->AccelPedalPos)) & 0xff;
x <<= 47;
m |= x;
/* BrakePedalDepressed: start-bit 32, length 1, endianess intel, scaling 1.000000, offset 0.000000 /
x = (
(uint8_t*)(&pack->BrakePedalDepressed)) & 0x1;
x <<= 32;
i |= x;
*data = reverse_byte_order(m)|;
return 0;
}

note:My email is :[email protected]
you can send email to me to discuss this question.

C-Code generation multiplexing

The signal multiplexing is either leading to wrong code generation or the ast is build wrong.
I had not enought time to figured it out yet, a hint for me where useful so I may decide whether I hotfix it by my self and push it.

Here is the wrong generated code (the multply case 12: statements are leading to compile time errors):

int unpack_can_0x5e0(can_0x5e0_t *unpack, uint64_t data, uint8_t dlc)
{
	register uint64_t x;
	register uint64_t i = (data);
	if(dlc < 8)
		return -1;
	x = i & 0xff;
	unpack->ID2 = x;
	switch(unpack->ID2) {
	case 12:
	x = (i >> 8) & 0xffff;
	unpack->ID_FN_RQ = x;
	break;
	case 12:
	x = (i >> 24) & 0xff;
	unpack->ID_RQ = x;
	break;
	case 12:
	x = (i >> 32) & 0xf;
	unpack->ST_RQ = x;
	break;
	default: return -1;
	}
	return 0;
}

dbcc doesnt detect float if blank line above SIG_VALTYPE_ entry

Hi.

If there is a blank line above the first SIG_VALTYPE_ entry, dbcc appears to ignore all SIG_VALTYPE_ entries

i have 2 almost identical dbc files attached below, one with an extra line
broken.dbc.txt
working.dbc.txt

output of $ diff -u broken.dbc working.dbc :

--- working.dbc     2023-08-14 15:52:36.946093459 +1000
+++ broken.dbc      2023-08-14 15:56:00.614259724 +1000
@@ -44,5 +44,6 @@
 CM_ BU_ test "message1";
 CM_ SG_ 1536 InputVoltage "message2";
 CM_ SG_ 1536 InputCurrent "message3";
+
 SIG_VALTYPE_ 1536 InputVoltage : 1;
 SIG_VALTYPE_ 1536 InputCurrent : 1;

I ran ./bin/dbcc -o out -j *.dbc

compare the json files: diff -U 0 out/working.dbc.json out/broken.dbc.json

--- out/working.dbc.json        2023-08-14 15:59:52.178037864 +1000
+++ out/broken.dbc.json 2023-08-14 15:59:52.177037878 +1000
@@ -22 +22 @@
-                                       "floating" : 1,
+                                       "floating" : 0,
@@ -35 +35 @@
-                                       "floating" : 1,
+                                       "floating" : 0,

Feature request: Generating enum for Frame ID.

Hi!

I sometimes need to use CAN ID in my code.
Currently, the generated code doesn't provide any way to use the CAN ID.

Having at the top of the generated header file an enum would be useful.
Something like:

typedef enum {
    CAN_ID__XCP_Request_All = 0x200
    CAN_ID__XCP_Request_Ivt = 0x201
    CAN_ID__XCP_Response_Ivt = 0x202
} MyDBC_CAN_ID_t;

Thanks again for your work!

Unused code generation when message length is 0

I found this bug with the following entry in a .dbc (modified for simplicity and privacy)

BO_ 42 MESSAGE_NAME: 0 MESSAGE_SENDER
 SG_ Signal_Name : 0|3@0+ (1,0) [0|7] "" DBG

which generates the following code

int unpack_can_0xc0000000_MESSAGE_NAME(can_0xc0000000_MESSAGE_NAME_t *unpack, uint64_t data, uint8_t dlc)
{
	register uint64_t x;
	register uint64_t m = reverse_byte_order(data);
	
	if(dlc < 0)
		return -1;
...

Which, when compiled with -Wall -Werror -Wextra throws the following error

dbc_file.c:123:9: error: comparison is always false due to limited range of data type [-Werror=type-limits]
  if(dlc < 0)
         ^

because dlc is of type unsigned.

The bug is not mission critical, removing the two offending lines and adding (void)dbc; silences the compiler warnings.

I don't yet know why the .dbc file has the entry with the 0 length message, or what the significance of that is (I'm new to DBC/CAN myself)

Best way to get message length

I'm using sizeof(can_0x003_trim_manual_t) to get the frame size to write. Is this the best way to do this?

I wondered if the pack_message function could return the length of the relevant message, rather than just 0 for success... but obviously that's a breaking change. If doing a sizeof the relevant message struct is the best approach then I'll just keep on with that.

Missing infromation about CAN-FD on main page

Hi,

Great code. Idea of this tool is perfectly for me but unfortunatelly CAN-FD is not supported.
For my and probably not only mine purpose it cause that this tool is useles :(
Number of users which will have this problem will grow because CAN-FD enter to more places. In automotive currently most new projects which I know use CAN-FD.

Please add on main page information that CAN-FD isn't supported.

Result after call application with CAN-FD database:
D:\dbc>dbcc.exe CAN_FD_XXXXXXXXX.dbc
Assertion failed!

Program: D:\dbc\dbcc.exe
File: ../can.c, Line 130

Expression: r == 1 && sig->start_bit <= 64

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

I will try solve it on my own(with succes or not). If I will create something usefull I will inform you.

Add C code generation for value lists

Hello @howerj , thanks for dbcc.

I too would like to add C code generation of value lists as enums. This was originally proposed in #23. I made a minimal proof of concept:

driftregion@35f3bef

vals_ast amiss

When starting out, I found that the vals_ast returned here is NULL:

dbcc/can.c

Line 334 in 68139f7

mpc_ast_t *vals_ast = mpc_ast_get_child_lb(ast, "vals|>", 0);

example.dbc

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_: N1 N2

BO_ 690 N1_Event: 8 N1
 SG_ N1_Event M : 0|10@1+ (1,0) [0|0] ""  N2
 SG_ N1_Event000ecuReset m0 : 10|1@1+ (1,0) [0|0] "" N2
 SG_ N1_Event001bootFailureReason m1 : 10|4@1+ (1,0) [0|0] "" N2

CM_ SG_ 690 N1_Event001bootFailureReason "Why N1 failed to boot";
VAL_ 690 N1_Event 0 "N1_Event000ECURESET" 1 "N1_Event001FAILEDTOBOOT" ;
VAL_ 690 N1_Event001bootFailureReason 1 "BOOT_INFO_CRC_MISMATCH" 2 "APP_HEADER_CRC_MISMATCH" 3 "NODE_ENUM_MISMATCH" 4 "HW_VERSION_ENUM_MISMATCH" 5 "PROJECT_ENUM_MISMATCH" 6 "USAGE_ENUM_MISMATCH" 7 "APP_CRC_INVALID" ;

the AST shows that VAL_ is parsed as a types.

'
  types|> 
    ident|regex:46:1 'VAL_'
    s|regex:46:5 ' '
    whatever|integer|regex:46:6 '690'
    s|regex:46:9 ' '
    whatever|ident|regex:46:10 'N1_Event'
    s|regex:46:18 ' '
    whatever|integer|regex:46:19 '0'
    s|regex:46:20 ' '
    whatever|string|> 
      char:46:21 '"'
      regex:46:22 'N1_Event000ECURESET'
      char:46:41 '"'
    s|regex:46:42 ' '
    whatever|integer|regex:46:43 '1'
    s|regex:46:44 ' '
    whatever|string|> 
      char:46:45 '"'
      regex:46:46 'N1_Event001FAILEDTOBOOT'
      char:46:69 '"'
    s|regex:46:70 ' '
    char:46:71 ';'
    n|regex:46:72 '

what was once is no more

I found that behavior has changed since #23 was merged (ce968d8). On that commit, vals appears correctly in the AST.

'
  vals|> 
    val|> 
      string:44:1 'VAL_'
      s|regex:44:5 ' '
      id|integer|regex:44:6 '690'
      s|regex:44:9 ' '
      name|ident|regex:44:10 'N1_Event'
      s|regex:44:18 ' '
      val_item|> 
        integer|regex:44:19 '0'
        s|regex:44:20 ' '
        string|> 
          char:44:21 '"'
          regex:44:22 'N1_Event000ECURESET'
          char:44:41 '"'
        s|regex:44:42 ' '
      val_item|> 
        integer|regex:44:43 '1'
        s|regex:44:44 ' '
        string|> 
          char:44:45 '"'
          regex:44:46 'N1_Event001FAILEDTOBOOT'
          char:44:69 '"'
        s|regex:44:70 ' '
      char:44:71 ';'
      n|regex:44:72 '

the plucky git bisect run was given the following test

#! /usr/bin/env bash
make clean
make
./dbcc -vvvv -o tmp example.dbc > dbc.ast
grep vals dbc.ast

and found the key change in in 94f604b#diff-a07721cd062be25900bddb926de15fc103cf32ea2726d1fea286f6548b810c6a

dbc       : <version> <symbols> <bs> <ecus> <values>* <n>* <messages> (<n>|<sigval>|<types>)*  ; \n" ;

I don't know the reason for removing <attribute_definition>, <attribute_value>* from the grammar. It seems related to the parsing of SIG_VALTYPE_. Some background here would be very helpful. When I added those tags back in, parsing worked as expected.

" dbc       : <version> <symbols> <bs> <ecus> <values>* <n>* <messages> <comments> <sigval>* <attribute_definition>* <attribute_value>* <vals>  ; \n" ;

why we want this

  • to deduplicate DBC-defined value lists and user-application-defined enums (which may or may not exist)

design decisions

@howerj @martonmiklos , would appreciate your input

  • how to handle duplicate enumerator identifiers ?

    • error, require that input DBC has no duplicate val_list item names
    • name mangle, prefix enumerator (val_list->name) with enum name (val_list_items[i]->name)
    • generate C++ namespaces (kidding)
  • how to detect strings that are not valid C identifiers?

    • use the grammar (How?)
  • how to handle strings that are not valid C identifiers?

    • warn, omit offending enum declaration, still generate code
    • warn, omit all enum declarations, still generate code
    • error, do not generate code

It might be worth generating functions for encoding/decoding and validating that specific enumeration, and then using those functions in the pack/print/unpack/encode/decode routines.

  • pro: type checking, bounds checking
  • con: type checking

Having a single VAL_ inside the dbc will not generate enums

When there is only a single VAL_ in the dbc, the AST looks like this, notice particularly the first line,

  vals|val|> 
    string:81:1 'VAL_'
    s|regex:81:5 ' '
    id|integer|regex:81:6 '1'
    s|regex:81:7 ' '
    name|ident|regex:81:8 'state'
    val_item|> 
      s|regex:81:13 ' '
      integer|regex:81:14 '0'
      s|regex:81:15 ' '
      string|> 
        char:81:16 '"'
        regex:81:17 'Inactive'
        char:81:25 '"'
    val_item|> 
      s|regex:81:26 ' '
      integer|regex:81:27 '1'
      s|regex:81:28 ' '
      string|> 
        char:81:29 '"'
        regex:81:30 'Active'
        char:81:36 '"'
    char:81:37 ';'
    n|regex:81:38 '
'

which breaks the ast2dbc function in can.c since it searches for the string vals|>.

This means that in this particular case the corresponding enum is not generated. You can check this by just removing all VAL_ except one in enum.dbc.

If there are two or more VAL_ however the AST is correct

  vals|> 
    val|> 
      string:81:1 'VAL_'
      s|regex:81:5 ' '
      id|integer|regex:81:6 '1'
      s|regex:81:7 ' '
      name|ident|regex:81:8 'state'
      val_item|> 
        s|regex:81:13 ' '
        integer|regex:81:14 '0'
        s|regex:81:15 ' '
        string|> 
          char:81:16 '"'
          regex:81:17 'Inactive'
          char:81:25 '"'
      val_item|> 
        s|regex:81:26 ' '
        integer|regex:81:27 '1'
        s|regex:81:28 ' '
        string|> 
          char:81:29 '"'
          regex:81:30 'Active'
          char:81:36 '"'
      char:81:37 ';'
      n|regex:81:38 '
'
    val|> 
      string:82:1 'VAL_'
      s|regex:82:5 ' '
      id|integer|regex:82:6 '2'
      s|regex:82:7 ' '
      name|ident|regex:82:8 'state'
      val_item|> 
        s|regex:82:13 ' '
        integer|regex:82:14 '3'
        s|regex:82:15 ' '
        string|> 
          char:82:16 '"'
          regex:82:17 'OFF'
          char:82:20 '"'
      val_item|> 
        s|regex:82:21 ' '
        integer|regex:82:22 '4'
        s|regex:82:23 ' '
        string|> 
          char:82:24 '"'
          regex:82:25 'ON'
          char:82:27 '"'
      char:82:28 ';'
      n|regex:82:29 '
'

and the corresponding enums are generated.

fix_start_bit possible bug

Hi howerj, first of all thanks for your code.

static unsigned fix_start_bit(bool motorola, unsigned start)
{
if(motorola)
start = (8 * (7 - (start / 8))) + (start % 8);
return start;
}

What about this functions? Shouldn't it return start-(signal->bit_length)?

Decoding float signal: wrong type?

Hello,
Nice tool! I have just noticed a strange thing, that i do not know if it is intended...
When I do dbcc float_signal.dbc I get a function like this:

int decode_can_0x400_FloatSignal0(const can_obj_float_signal_h_t *o, int32_t *out) {
    assert(o);
    assert(out);
    int32_t rval = (int32_t)(o->can_0x400_NewMessage0.FloatSignal0);
    *out = rval;
    return 0;
}

Why should it return a int32_t? Why not returning a float?

Byte order

I am not sure, what to pass to unpack_can... functions.
Casting frame.data (the 8 bytes of the can frame) into a uint64_t causes byte order failures (power pc would interpret it different than intel). So it would be better to pass the char array, so the unpacking function have to ensure the correct byte order.

PS I also couldn't find something in your repo, where I may see how I shall use this function correctly. Fact is, the byte order gets messed up if I cast the frame.data array.

May be introduce a new dbcc flag for choosing the byte order.

I need more time to test it. But prehaps I will do a other push.

Documentation on Conversion of 8 bytes to uint64

Thank you for the creation of this library, I have found it very useful.

When examining the documentation of how to convert between an 8 byte array and a uint64 variable, unless I'm mistaken the 8th byte shift is incorrect:

static uint64_t u64_from_can_msg(const uint8_t m[8]) {
	return ((uint64_t)m[7] << 54) | ((uint64_t)m[6] << 48) | ((uint64_t)m[5] << 40) | ((uint64_t)m[4] << 32) 
		| ((uint64_t)m[3] << 24) | ((uint64_t)m[2] << 16) | ((uint64_t)m[1] << 8) | ((uint64_t)m[0] << 0);
}

static void u64_to_can_msg(const uint64_t u, uint8_t m[8]) {
	m[7] = u >> 54;
	m[6] = u >> 48;
	m[5] = u >> 40;
	m[4] = u >> 32;
	m[3] = u >> 24;
	m[2] = u >> 16;
	m[1] = u >>  8;
	m[0] = u >>  0;
}

It should be 56 (as in, 8 more than 48 and 8 less than 64) for both of these, ex:
(uint64_t)m[7] << 56
m[7] = u >> 56;

Generated code do not support extended messages

Hi,

In my dbc, I have a message with an ID of 0x100 in extended mode.

BO_ 2147483904 IVT_HVBattery: 8 IVT_30_Node

The problem is that instead of generating an id of 0x100 the code generates the following:

int unpack_message(can_obj_ivt30_h_t *o, const unsigned long id, uint64_t data, uint8_t dlc, dbcc_time_stamp_t time_stamp) {
	assert(o);
	assert(id < (1ul << 29)); /* 29-bit CAN ID is largest possible */
	assert(dlc <= 8);         /* Maximum of 8 bytes in a CAN packet */
	switch (id) {
	//...
	case 0x80000100: return unpack_can_0x80000100_IVT_HVBattery(o, data, dlc, time_stamp);
        // ...

This message ID is therefore impossible to use, because of the assert(id < (1ul << 29));.

I would suggest generating the following:

int unpack_message(can_obj_ivt30_h_t *o, const unsigned long id, uint64_t data, uint8_t dlc, dbcc_time_stamp_t time_stamp) {
	assert(o);
	assert(id < (1ul << 29)); /* 29-bit CAN ID is largest possible */
	assert(dlc <= 8);         /* Maximum of 8 bytes in a CAN packet */
	switch (id) {
	//...
	case 0x100: return unpack_can_0x100_IVT_HVBattery(o, data, dlc, time_stamp);
        // ...

Best regards,
Gabriel

dbcc.xsd not valid because of typo

A space character in <xs:maxInclusive value="3221225472 "/> leads xml schema parsers to throw an error. ( https://github.com/howerj/dbcc/blob/master/dbcc.xsd#L108 )

<xs:simpleType name="can-id">
	<xs:restriction base="xs:integer">
		<xs:minInclusive value="0"/>
		<xs:maxInclusive value="3221225472 "/>
		<!-- Maximum ID should be '536870911', some DBCs do not abide by this -->
	</xs:restriction>
</xs:simpleType>

Fixed by deleting the space behind "3221225472".

encode function have err

  1. maybe encode function shoud input a double value and point of dst struct to process and output result true/false?

  2. value should first add offset then * scale now is first scale then offset

for example

result is that

double decode_can_0x203_BattPackVoltage(can_0x203_Batt_Basic_Info_t *record)
{
double rval = (double)(record->BattPackVoltage);
rval *= 0.100000;
rval += -1000.000000;
return rval;
}

double encode_can_0x203_BattPackVoltage(can_0x203_Batt_Basic_Info_t *record)
{
double rval = (double)(record->BattPackVoltage);
rval *= 10.000000;
rval += 1000.000000;
return rval;
}

Too small offsets and scales generate wrong code

We have a CAN DBC, where scale is 1E-7. The code generated is something like
value *= 0.000000;
Which is obviously wrong. It should be
value *= 1e-7;
It is caused by using "%f" (always use decimal notation) instead of "%g" (always use the shortest notation) in the printf calls all over the code.

I can easily prepare a pull request where every "%f" is replaced with "%g" in the code base, if you want.

about decode and encode

wonderful work!

i have build it in codeblock and its work well

translate dbc in c with helpful depack

however

i have find many encoder and decoder function with only definition

i may make it work with writing down my implementation? or maybe implementation in feature?

Best regards

Plans for value lists?

Hi Richard,

I have seen this comment about the value list support:
https://github.com/howerj/dbcc/blob/master/parse.c#L85

What is your plan about the value list support?
I am thinking about generating an enum from these lists would be useful as well setting the value mapped fields to enum in the generated code.

If you have other plans please let me know, because I think I might be able to came up with an implementation.

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.