# protocol_code_generate
**Repository Path**: eelog/protocol_code_generate
## Basic Information
- **Project Name**: protocol_code_generate
- **Description**: 利用通信协议生成解包和打包的程序,适用于can协议、sbus协议、232协议等等。
- **Primary Language**: C++
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-12-04
- **Last Updated**: 2025-04-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### 项目说明
用于将csv格式的协议文件自动生成协议打包和解包程序。本项目python脚本是基于python3开发的。
### 使用说明
下载项目:git clone https://gitee.com/eelog/protocol_code_generate.git
#### 1 适用范围
(1) 默认字节序为小端模式,即低字节在低地址,高字节在高地址
(2) 默认单个信号在数据包内最大长度不会超过64位
(3) 默认一个数据包不会超过512MB
#### 2 使用
step1:按照protocol目录下的协议模板(xxx.csv)填好协议,将协议复制到protocol文件夹下
step2:双击protocol_code_generate.exe,会在output文件夹下生成协议打包和解包程序(一个.csv协议文件会生成一个头文件和一个源文件)
step3: 将协议程序头文件和源文件复制到你自己的工作目录下便可以使用了
注意:请不要将字段命名为**编程语言关键字**
生成的程序使用举例:
**建议使用前用memset对相关结构体进行至0操作,如果不置0,且后续未赋值,那么变量的初始值可能会超出数值限制范围,造成赋值失败**。调用signalArrayTo$PROTOCOLPkg时,若返回0,则代表赋值成功,否则失败。
**API说明**
```c
// main.c
#include "$INCLUDE.h"
#include
int main()
{
$PROTOCOLSignal signal1,signal2;
$PROTOCOLPkgStruct pkg;
memset(&pkg,0x00,sizeof($PROTOCOLPkgStruct));
signal1.$SIGNAL1 = 0
signal1.$SIGNAL2 = 1
...
// 将信号打包
signalArrayTo$PROTOCOLPkg(&pkg, &signal1);
// 将包解析成信号
pkgTo$PROTOCOLSignalArray(&signal2, &pkg);
printf("%f",signal2.$SIGNAL1);
}
// 说明
/*
* $INCLUDE 请用协议文件名替换(全小写)
* $PROTOCOL请用协议文件名替换(首字母大写,其余字母小写)
* $SIGNAL1 $SIGNAL2请用协议里的信号名替换
*
*/
// 举例
// 比如协议名为can.csv
// 协议第一列有信号m1_enable
// 那么上面的程序模板可写成:
// #include "can.h"
// int main()
// {
// CanSignal signal1,signal2;
// CanPkgStruct pkg;
//
// memset(&pkg,0x00,sizeof(CanPkgStruct));
// signal1.m1_enable = 0
// // 将信号打包
// signalArrayToCanPkg(&pkg, &signal1);
//
// // 将包解析成信号
// pkgToCanSignalArray(&signal2, &pkg);
// printf("%f",signal2.m1_enable);
// }
```
生成的代码为c语言代码,当其中函数要被c++调用时,需用extern "C"{}来修饰代码。如下
```c++
// test.cpp
extern "C"
{
#include "xxx.h"
}
```
修改下面生成代码的min和max不会影响程序运行,程序是通过bit_length和data_type以及scale、offset计算真值范围的。
```c++
Head_state_fbSignalAttr head_state_fb_signal_attrs[SIGNAL_NUM]={
{.name="vel",.start_bit=0,.bit_length=16,.scale=0.01,.offset=0,.data_type=1,.min=-327.68,.max=327.67 },
{.name="head",.start_bit=16,.bit_length=16,.scale=0.01,.offset=0,.data_type=0,.min=0.0,.max=655.35 },
{.name="ad_sys_stat",.start_bit=32,.bit_length=4,.scale=1,.offset=0,.data_type=0,.min=0.0,.max=15.0 },
{.name="ad_start_stat",.start_bit=36,.bit_length=1,.scale=1,.offset=0,.data_type=0,.min=0.0,.max=1.0 },
{.name="reserve",.start_bit=37,.bit_length=15,.scale=1,.offset=0,.data_type=0,.min=-15000,.max=32767.0 },
{.name="beat",.start_bit=52,.bit_length=4,.scale=1,.offset=0,.data_type=0,.min=0.0,.max=15.0 },
{.name="check_sum",.start_bit=56,.bit_length=8,.scale=1,.offset=0,.data_type=0,.min=0.0,.max=255.0 },
};
```
#### 3 错误检查
step1:检查protocol文件夹、software_template文件夹是否存在,如果不存在,请在protocol_code_generate.exe所在的目录下创建这两个文件夹
step2:检查protocol下是否有协议文件,协议文件必须为.csv文件,且格式与模板一致。如果没有协议文件,请将协议文件复制到protocol文件夹下
step3:检查software_template文件夹是否存在include_template.h和source_template.c文件,如果
不存在,请将这两个文件复制到software_template文件夹下
#### 4 限制
- 使用时请用户自己算好数据范围,不要造成数据溢出,否则会造成数据传输错误,解析数据包无法得到正确的数据。
#### 5 注意
请不要更改software_template文件夹下的文件!!!
若非要修改,请注意移位操作的优先级,建议将所有的移位操作都用括号括起来。
有符号数与无符号数的转换比较麻烦。
**符号数只支持8位、12位、16位、32位、64位。**
#### 附录
```mermaid
graph LR
1[真值]-->2[总线值数据包]-->3[真值]
```
```mermaid
graph LR
1[float]-->2[Uint64]-->3[unsigned bit_length]-->4[Uint64]-->5[针对有符号数]
5-->7[uint64*scale+offset]
4-->6[针对无符号数]-->8[首位为1]-->10[int64_t value - int64_t 1<< bit_length-1 ]-->11[*scale+offset]
6-->9[首位为0]-->7
```
上图为项目的基本工作逻辑,即在发送端将数据真值打包成数据包发送,接收端接到数据包后,将数据包的真值解析出来。
**真值如何转换成数据包的呢?**
> [!IMPORTANT]
>
> 首先用户为信号赋值(真值),真值以**float**数据类型存储。
>
> 然后,将float类型的真值数据进行计算得到总线值**value**,得到的总线值**临时以uint64_t**数据类型存储。 总线值 = (真值-偏移量) / 精度。
>
> 之后,根据总线值在数据包的起始位和结束位,按low part、middle part、high part三部分向数据包数组赋值。
>
> 针对low part,运行value & (0xff)来保留低字节,并通过左移(8-low_part_length)再右移(8-low_part_length)来保证**把不属于low part的位清零**。
>
> 针对middle part,运行value>>low_part_length来清除low part,再右移动i*8(i=0...),再通过uint8_t类型强制转换截断前面的数据,**把不属于middle part的位清零**,得到单个字节,并赋值。
>
> 针对high part,运行value<<64-bit_length来将总线值有效数据首位移动到最高位,再右移64-bit_length实现对非总线有效位长的数据进行清零,再右移动bit_length-high_part_length实现**对非high part数据进行清零**。
>
> 注:每次向数据包赋值时,都是以uint8数据参与运算进行赋值。
**某真值赋值时数据溢出会不会影响到其他字段呢?**
不会,因为会对不属于总线值长度范围内的数据进行清零操作再赋值到数据包。
后续会把真值范围再引进来,真值范围超过限制区间,需要将状态返回给用户。
**通信协议里的精度和偏移量有什么含义呢?**
根据真值=总线值*精度+偏移量,因此得到总线值 = (真值-偏移量) / 精度,如果精度过大,比如为5,那么真值在0~4的范围内变化是无法体现在总线值上的。
偏移量代表相对真值的偏移,比如偏移量为10000,那么代表真值为10000时,总线值为0。