No version for distro humble. Known supported distros are highlighted in the buttons above.
No version for distro jazzy. Known supported distros are highlighted in the buttons above.
No version for distro rolling. Known supported distros are highlighted in the buttons above.

cyberdog_embed_protocol package from bridges repo

cyberdog_grpc cyberdog_embed_protocol params protocol

Package Summary

Tags No category tags.
Version 1.0.0
License Apache License, Version 2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Checkout URI https://github.com/miroboticslab/bridges.git
VCS Type git
VCS Version rolling
Last Updated 2024-01-17
Dev Status UNMAINTAINED
CI status No Continuous Integration
Released UNRELEASED
Tags No category tags.
Contributing Help Wanted (0)
Good First Issues (0)
Pull Requests to Review (0)

Package Description

Easier to use peripherals with toml description file

Additional Links

No additional links.

Maintainers

  • ZhengJunyuan

Authors

No additional authors.

EMBED_PROTOCOL

English Version

embed_protocol是一个通用的外设抽象类,可动态灵活的配置基于某种通信协议外设的数据解析方式,且所有通信协议都被抽象为一种描述文件,该文件通过外部加载的形式载入,可在不重新编译软件源代码的情况下进行更改,为防止改动出现错误,每一个描述文件都在初始编译态预置在了程序内部,当外部文件缺失或出现错误的情况下可自动载入内部预置文件(TBD)

实例通过载入不同描述文件以兼容不同的通信传输协议以及格式,简化了使用外设设备的方法,现在只需要关心消息的使用与指令数据的下发,从复杂的消息编码、解码、发送和接收中解放了出来,不同的通信传输协议也有了统一的通用接口


1.结构简介

整个功能包使用命名空间embed_protocol

功能目录结构如下:

include
├── embed_protocol
│   ├── can_protocol.hpp
│   ├── embed_protocol.hpp
│   ├── common.hpp
│   └── protocol_base.hpp
└── embed_parser
    └── can_parser.hpp

  • embed_protocol : 通用设备,用于存放主体代码
    • common.hpp : 通用及工具代码
    • embed_protocol.hpp : 对外统一接口
    • protocol_base.hpp : 不同协议的基类接口
    • [实现] can_protocol.hpp : CAN协议传输的功能实现,从protocol_base派生
  • embed_parser : 通用解析器,用于存放协议解析代码
    • [实现] can_parser.hpp : CAN协议传输的解析实现

描述文件存放目录见 : cyberdog_bridges/README.md规定

Class Protocol

该类用于对外的主要接口:

#define XNAME(x) (#x)
#define LINK_VAR(var) LinkVar( \
    XNAME(var), \
    cyberdog::embed::ProtocolData(sizeof((var)), static_cast<void *>(&(var))))

namespace embed
{
template<typename TDataClass>
class Protocol
{
public:
  explicit Protocol(const std::string & protocol_toml_path);

  std::shared_ptr<TDataClass> GetData();

  // please use "#define LINK_VAR(var)" instead
  void LinkVar(const std::string origin_name, const ProtocolData & var);

  bool Operate(const std::string & CMD, const std::vector<uint8_t> & data = std::vector<uint8_t>());

  bool SendSelfData();

  void SetDataCallback(std::function<void(std::shared_ptr<TDataClass>)> callback);

  StateCollector & GetErrorCollector();
};  // class Protocol
}  // namespace embed

构造函数,通过外部描述文件创建实例对象

explicit Protocol(const std::string & protocol_toml_path, bool for_send = false);

  • protocol_toml_path : 描述文件地址
  • for_send : 通过toml描述文件发送,或通过toml描述文件接收(默认为接收)

获取自定TDataClass类型数据 : 返回指向内部数据的shared_ptr

std::shared_ptr<TDataClass> GetData();

Note : 一般用于LINK_DATAfor_send设备的数据装填,接收请使用回调函数方式

链接TDataClass数据,以便通过各种传输协议解析出变量到TDataClass中

void LinkVar(const std::string origin_name, const ProtocolData & var);

Note : 一般使用宏LINK_VAR(var)代替,详细用法见下方example

设备操作函数,下发指令及数据 : 返回是否完整发送成功

bool Operate(const std::string & CMD, const std::vector<uint8_t> & data = std::vector<uint8_t>());

  • CMD : 指令字符串(描述文件中规定)
  • data : 指令所携带数据

GetData()修改的内部数据通过解析协议发送出去 : 返回是否完整发送成功

bool SendSelfData();

Note : 如设备不为发送模式(即for_send=false),设备同样可以进行发送,但不建议在非测试时使用

设置接收回调函数

void SetDataCallback(std::function<void(std::shared_ptr<TDataClass>)> callback);

  • callback : 设置的回调函数

获取接收方式是否超时 : 返回是否超时

bool IsRxTimeout()

Note : 当成功接收后将清空超时记录

获取发送方式是否超时 : 返回是否超时

bool IsTxTimeout()

Note : 当成功发送后将清空超时记录

获取接收方式是否出错 : 返回是否出错

bool IsRxError()

Note : 当成功完整接收描述文件内数据将清空出错记录

获取错误收集器 : 返回是搜集器引用

StateCollector & GetErrorCollector()

Note : 具体错误代码在common.hpp中

使用示例:

class Acc
{
public:
  float x;
  float y;
  float z;
};

void callback(std::shared_ptr<Acc> data)
{
  printf("callback, Acc.x=%f, Acc.y=%f, Acc.z=%f\n", data->x, data->y, data->z);
}

int main(int argc, char ** argv)
{
  UNUSED_VAR(argc);
  UNUSED_VAR(argv);

  // receive-operate mode
  cyberdog::embed::Protocol<Acc> protocol_1("parser/can/acc_protocol/acc_1.toml");
  protocol_1.LINK_VAR(protocol_1.GetData()->x);
  protocol_1.LINK_VAR(protocol_1.GetData()->y);
  protocol_1.LINK_VAR(protocol_1.GetData()->z);
  protocol_1.SetDataCallback(callback);

  std::vector<uint8_t> data = std::vector<uint8_t>(6);
  protocol_1.Operate("start", data);
  
  // for_send mode
  cyberdog::embed::Protocol<Acc> protocol_2("parser/can/acc_protocol/acc_2.toml");
  protocol_2.LINK_VAR(protocol_2.GetData()->x);
  protocol_2.LINK_VAR(protocol_2.GetData()->y);
  protocol_2.LINK_VAR(protocol_2.GetData()->z);

  std::shared_ptr acc_data = protocol_2.GetData();
  acc_data->x = 1.2;
  acc_data->y = 3.4;
  acc_data->z = 5.6;
  protocol_2.SendSelfData();

  return 0;
}


2.通信描述文件

CAN通信描述文件使用

示例:

# -- embed params -- #
# protocol = "can"             (string)
# name = "can_protocol_name"     (string)
# [delete] for_send = false  (true / false)
protocol = "can"
name = "can_protocol_1"

# -- can params -- #
# can_interface = "can0"             (string)
# [optional] extended_frame = false  (true / false)
# [optional] canfd_enable = false    (true / false)
# [optional] timeout_us = 3'000'000  (int64)
can_interface = "can0"
extended_frame = true
canfd_enable = false
timeout_us = 1000000

# -- data_var -- #
# [[var]]
# can_id = "0x0300 00 00" / "0x0300'00'00"  (string-HEX)
# var_name = "var_name_1"                   (string)
# var_type = "float"                      
#            (float / double / i64 / i32 / i16 / i8 / u64 / u32 / u16 / u8 / bool)
# parser_param = [0, 3]                     (array<uint8>[2] / array<uint8>[3])
# [optional] var_zoom = 1.0                 (float)
# [optional] parser_type = "auto"           (bit / var / auto)
# [optional] description = ""               (string)

[[var]]
can_id = "0x0300 00 00"
var_name = "example_var_1"
var_type = "double"
parser_param = [0, 7]
var_zoom = 0.0001
description = "this is example named example_var_1"

[[var]]
can_id = "0x0300'00'01"
var_name = "example_var_2"
var_type = "uint8"
parser_param = [0, 7, 4]
parser_type = "bit"
description = "this is example named example_var_2"

# -- data_array -- #
# [[array]]
# can_package_num = 8          (size_t)
# can_id = ["0x200", "0x207"]  (array<string-HEX>[2] / array<string-HEX>[can_package_num])
# array_name = "array_name_1"  (string)
# [optional] description = ""  (string)

[[array]]
can_package_num = 8
can_id = ["0x200", "0x207"]
array_name = "example_array_1"
description = "this is example named example_array_1"

[[array]]
can_package_num = 4
can_id = ["0x200", "0x201", "0x202", "0x203"]
array_name = "example_array_2"
description = "this is example named example_array_2"

# -- cmd -- #
# [[cmd]]
# cmd_name = "cmd_name_1"      (string)
# can_id = "0x02"              (string-HEX)
# [optional] ctrl_len = 0      (uint8)
# [optional] ctrl_data = []    (array<string-HEX>[x] : where x <= ctrl_len)
# [optional] description = ""  (string)

[[cmd]]
cmd_name = "example_cmd_1"
can_id = "0x02"
ctrl_len = 2
ctrl_data = ["0x06", "0x13"]
description = "this is example named example_cmd_1"

解析:

  • embed params : 通用参数
    • protocol : 该描述文件所规定的通信协议
    • name : 该设备embed_protocol的名称,在log中会使用该值来提示
    • [删除] for_send : 该设备从TDataClass接收或发送,如为false则开启接收线程从CAN总线接收数据到TDataClass; 如为true则关闭接收线程,使用SendSelfData()函数将TDataClass发送到CAN总线(改为在class embed_protocol中传入,以实现收发使用同一份描述文件)
  • can params : CAN协议参数
    • can_interface : 使用的CAN总线,一般为"can0", "can1""can2"
    • [可选] extended_frame : 是否使用扩展帧(主要针对发送,接收是全兼容的),默认缺省值为 : false
    • [可选] canfd_enable : 是否使用CAN_FD(需要系统设置及CAN收发器硬件支持),默认缺省值为 : false
    • [可选] timeout_us : 接收或发送超时时间(微秒),主要用于判断接收掉线和防止析构时卡在接收或发送函数中无法进行,有效值为1'000(1ms)3'000'000(3s),默认缺省值 : 3'000'000(3s)
  • data_var : CAN协议变量解析规则
    • can_id : 需要接收的ID,以"0x""0X"开头的十六进制字符串,可使用符号“’”(单引号)“ ”(空格)进行任意分割以方便阅读和书写,如 : 0x1FF'12'340x123 45 67
    • var_name : 需要解析到的变量名称(即在代码中使用LINK_VAR(var)链接的变量名称)
    • var_type :
      • 需要解析的变量类型(即在代码中使用LINK_VAR(var)链接的变量类型),在及其特殊情况下可使用不同类型(如将u8解析到代码中的u16),但size_of的大小不能超过代码中的大小(即不可将u64解析到代码中的u8)
      • 支持解析格式 : float / double / i64 / i32 / i16 / i8 / u64 / u32 / u16 / u8 / bool
    • parser_param : 解析参数,根据不同解析类型会存在不同长度
      • 以变量形式解析(即parser_type = "var") :
        • parser_paramarray<uint8>[2],即长度为2的uint8数组,且满足0 <= parser_param[0] <= parser_param[1] < MAX_CAN_LEN,其中MAX_CAN_LENSTD_CAN中为8,FD_CAN中为64
        • 解析时优先以高位优先按二进制解析原则将can_data[parser_param[0]]can_data[parser_param[1]]以二进制形式合并为一个变量
        • floatdouble在特殊情况下先以高位优先按二进制的解析形式合并,再以小数点精度缩放的解析形式生成为一个变量
        • 详细解析见下方示例表格(例1例3)
      • 以位形式解析(即parser_type = "bit") :
        • parser_paramarray<uint8>[3],即长度为3的uint8数组,且满足0 <= parser_param[0] < MAX_CAN_LEN && 8 > parser_param[1] >= parser_param[2] >= 0,其中MAX_CAN_LENSTD_CAN中为8,FD_CAN中为64
        • 解析时通过移位和位与解析原则将can_data[parser_param[0]]中高parser_param[1]位到低parser_param[2]位中的数据取出并右移parser_param[2]
        • 详细解析见下方示例表格(例4)
    • [可选] var_zoom : 缩放参数,以乘法形式与变量合并,仅floatdouble有效,默认缺省值为 : 1.0
    • [可选] parser_type :
      • 解析类型,可手动指定也可根据parser_param.length()自动推断
      • 支持解析类型 : var / bit / auto(默认缺省值)
    • [可选] description : 注释及使用描述
  • data_array : CAN协议数组解析规则
    • can_package_num : 预期接收的CAN数据帧数量
    • can_id : 预期接收作为数组值的CAN_ID数组
      • 手动指定所有CAN_ID(即can_id.length() == can_package_num) :
        • can_idarray<string-HEX>[can_package_num],即长度为can_package_num十六进制字符串
        • 所有数据将依照所指定的CAN_ID顺序,以数组index顺序装填,即通过CAN_ID查询在can_id中的index,按array[index * 8]为基准进行装填
        • 详细解析见下方示例表格(例5)
      • 自动指定连续CAN_ID(即can_id.length() != can_package_num && can_id.length() == 2 && can_id[0] < can_id[1])
        • can_idarray<string-HEX>[2],即长度为2的十六进制字符串
        • 所有数据将依照所指定的CAN_ID顺序,以数组index顺序装填,即通过CAN_ID查询在can_id中的index,按array[index * 8]为基准进行装填
        • 详细解析见下方示例表格(例6)
    • array_name : 需要解析到的数组名称(即在代码中使用LINK_VAR(var)链接的数组名称)
    • [可选] description : 注释及使用描述
  • cmd : CAN协议下发指令解析规则(例7)
    • cmd_name : 指令名称(即在代码中使用Operate()函数进行操作的指令)
    • can_id : 指令传输所用CAN_ID
    • [可选] ctrl_len : 控制段长度,需满足0 <= ctrl_len < MAX_CAN_LEN,其中MAX_CAN_LENSTD_CAN中为8,FD_CAN中为64,默认缺省值为 : 0
    • [可选] ctrl_data :
      • 控制段数据,为十六进制字符串数组(十六进制字符串不超过十六进制的两位(即u8)),满足0 <= ctrl_data.length() <= ctrl_len,默认缺省值为 : []
    • [可选] description : 注释及使用描述

示例集:

例1:

解析规则 数据
var_name = “example_1” can_data[0] = 0x12
var_type = “u16” can_data[1] = 0x34
parser_param = [1, 2] can_data[2] = 0x56

解析后等效代码为:

uint16 example_1 = 0x3456;

该示例展示了以参数[1, 2],即can_data数组index12共2个u8以高位优先按二进制的解析形式合并为u16类型变量example_1的过程

例2:

解析规则 数据
var_name = “example_2” can_data[0] = 0x12
var_type = “float” can_data[1] = 0x34
parser_param = [0, 3] can_data[2] = 0x56
var_zoom = 0.0001 can_data[3] = 0x78

解析后等效代码为:

uint32 tmp_2 = 0x12345678;
float example_2 = *((float *)(&tmp_2)) * 0.0001;

该示例展示了以参数[0, 3],即can_data数组index0123共4个u8以高位优先按二进制的解析形式合并为float类型变量example_2的过程

例3:

解析规则 数据
var_name = “example_3” can_data[0] = 0x12
var_type = “double” can_data[1] = 0x34
parser_param = [0, 3] can_data[2] = 0x56
var_zoom = 0.0001 can_data[3] = 0x78

解析后等效代码为:

uint32 tmp_2 = 0x12345678;
double example_2 = (double)(tmp_2) * 0.0001;

该示例展示了以参数[0, 3],即can_data数组index0123共4个u8以高位优先按二进制的解析形式合并为u32,再以小数点精度缩放的解析形式,缩放小数点得到double类型变量example_3的过程 *** :在var_typefloatdouble中,当parser_param规定的解析u8数量不等于flaotdoublesize且为size的偶数分之一(1/2, 1/4),将使用以高位优先按二进制合并,再使用小数点精度缩放的解析形式:

  • floatsize为4个u8,当parser_param规定的解析u8数量为2时
  • doublesize为8个u8,当parser_param规定的解析u8数量为4或2时

例4:

解析规则 数据
var_name = “example_4” can_data[0] = 0x12 (0b0001’0010)
var_type = “u8” can_data[1] = 0x34 (0b0011‘0100)
parser_param = [1, 5, 4] can_data[2] = 0x56 (0b0101’0110)

解析后等效代码为:

uint8 tmp_4 = 0x34;  // 0b0011'0100
uint8 example_4 = (uint8)((0b0011'0100 & 0b0011'0000) >> 4);
uint8 example_4 = 0b0000'0011;

该示例展示了以参数[1, 5, 4],即can_data数组index1,取高5位到低4位(&= 0b0011’0000)后右移4

例5:

解析规则 0x200 0x201 0x2FF
can_package_num = 3 can_data[0] = 0xA0 can_data[0] = 0xB0 can_data[0] = 0xC0
can_id = [“0x201”, “0x2FF”, “0x200”] can_data[1] = 0xA1 can_data[1] = 0xB1 can_data[1] = 0xC1
array_name = example_5 can_data[2] = 0xA2 can_data[2] = 0xB2 can_data[2] = 0xC2
  … = 0xA3 … = 0xB3 … = 0xC3
  … = 0xA4 … = 0xB4 … = 0xC4
  … = 0xA5 … = 0xB5 … = 0xC5
  … = 0xA6 … = 0xB6 … = 0xC6
  … = 0xA7 … = 0xB7 … = 0xC7

解析后等效代码为:

uint8 example_5[24] = {
  0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
  0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
  0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7
};

该示例展示了以参数["0x201", "0x2FF", "0x200"],即按照0x2010x2FF0x200的顺序依次解析并装填入数组example_5

例6:

解析规则 0x200 0x201 0x202
can_package_num = 3 can_data[0] = 0xA0 can_data[0] = 0xB0 can_data[0] = 0xC0
can_id = [“0x200”, “0x202”] can_data[1] = 0xA1 can_data[1] = 0xB1 can_data[1] = 0xC1
array_name = example_6 can_data[2] = 0xA2 can_data[2] = 0xB2 can_data[2] = 0xC2
  … = 0xA3 … = 0xB3 … = 0xC3
  … = 0xA4 … = 0xB4 … = 0xC4
  … = 0xA5 … = 0xB5 … = 0xC5
  … = 0xA6 … = 0xB6 … = 0xC6
  … = 0xA7 … = 0xB7 … = 0xC7

解析后等效代码为:

uint8 example_6[24] = {
  0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
  0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
  0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7
};

该示例展示了以参数["0x200", "0x202"],即按照从0x2000x202的顺序依次解析并装填入数组example_6

例7:

解析规则 数据
cmd_name = “cmd_1” cmd_data[0] = 0x12
can_id = “0x1F2” cmd_data[1] = 0x34
ctrl_len = 4 cmd_data[2] = 0x56
ctrl_data = [“0xA1”, “0xA2”] cmd_data[3] = 0x78

使用方式:

can_protocol_1.Operate("cmd_1", cmd_data);

使用后等效代码为:

struct can_frame tx_frame;
tx_frame.can_id = 0x1F2;
tx_frame.can_dlc = 8;
tx_frame.data[0] = 0xA1;
tx_frame.data[1] = 0xA2;

tx_frame.data[4] = 0x12;
tx_frame.data[5] = 0x34;
tx_frame.data[6] = 0x56;
tx_frame.data[7] = 0x78;
can_send(tx_frame);

该示例展示了以cmd_1携带cmd_data下发指令,即按照ctrl_len规定前4个u8都为控制段数据并装填入ctrl_data,但因数据不足后2个u8留空,剩下4个u8按传入参数cmd_data填入

CHANGELOG
No CHANGELOG found.

Wiki Tutorials

This package does not provide any links to tutorials in it's rosindex metadata. You can check on the ROS Wiki Tutorials page for the package.

Package Dependencies

System Dependencies

No direct system dependencies.

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged cyberdog_embed_protocol at Robotics Stack Exchange

No version for distro noetic. Known supported distros are highlighted in the buttons above.
No version for distro ardent. Known supported distros are highlighted in the buttons above.
No version for distro bouncy. Known supported distros are highlighted in the buttons above.
No version for distro crystal. Known supported distros are highlighted in the buttons above.
No version for distro eloquent. Known supported distros are highlighted in the buttons above.
No version for distro dashing. Known supported distros are highlighted in the buttons above.
No version for distro galactic. Known supported distros are highlighted in the buttons above.
No version for distro foxy. Known supported distros are highlighted in the buttons above.
No version for distro iron. Known supported distros are highlighted in the buttons above.
No version for distro lunar. Known supported distros are highlighted in the buttons above.
No version for distro jade. Known supported distros are highlighted in the buttons above.
No version for distro indigo. Known supported distros are highlighted in the buttons above.
No version for distro hydro. Known supported distros are highlighted in the buttons above.
No version for distro kinetic. Known supported distros are highlighted in the buttons above.
No version for distro melodic. Known supported distros are highlighted in the buttons above.