udp_bridge_sender_component简要介绍
udp_bridge_sender_component的功能是订阅Apollo的话题,并将话题中的protobuf数据发送给目的IP地址。
使用方法
在modules/bridge/conf
文件夹下,可以找到对应的配置文件,修改其中的参数,定义目标IP地址,端口号,protobuf数据类型。若要添加新的数据类型可参考 modules/bridge/README.md
文件。
udp_bridge_sender_component简要分析
该文件定义了一个类UDPBridgeSenderComponent
,可以从头文件中看出,其继承于cyber::Component
,即该类由cyber RT框架动态加载。
其有两个主要的成员函数,UDPBridgeSenderComponent<T>::Init()
相当于main
函数,该函数较为简单,完成了从配置文件中读取目的IP地址、端口号等信息的功能。UDPBridgeSenderComponent<T>::Proc(const std::shared_ptr<T> &pb_msg)
相当于订阅者的回调函数,其形参pb_msg
为订阅得到的protobuf数据,实现的功能是将protobuf数据以UDP协议发送给目的IP地址。
数据格式
在进一步分析之前,首先需要了解一下Apollo的bridge模块UDP数据的格式。
Apollo的bridge模块通过UDP协议发送的基本数据被称为帧,其由首部和数据组成。在UDPBridgeSenderComponent<T>::Proc
函数当中,protobuf数据首先进行序列化,然后将其根据参数FRAME_SIZE
分割为一个个大小相同的数据块,为每个数据块添加首部后,就组成了一个个的帧,然后将所有的帧发送发送。
帧的格式如下:
- BRIDGE_HEADER_FLAG。用于标识bridge模块的数据
- hsize。通过
typedef
定义的数据类型,用于表示首部的大小 - 各类的首部信息,其格式为
HType:bsize:value\n
,分别表示首部信息的枚举类型HType
、该首部信息value
的大小bsize
、首部信息value
。枚举类型HType
定义如下:
1.Header_Ver
。版本
2.Msg_Name
。protobuf数据类型名,用于标识该protobuf数据的类型
3.Msg_ID
。protobuf数据ID,用于唯一标识一个protobuf数据
4.Msg_Size
。protobuf数据序列化后的总字节数,注意不包含首部
5.Msg_Frames
。该protobuf数据的总帧数
6.Frame_Size
。该帧的字节数,包含帧首部和数据
7. Frame_Pos。该帧的数据在序列化protobuf数据中的位置,用于将各个帧中的数据组装回protobuf数据
8.Frame_Index
。每个帧从零开始的序号,用于标识帧的顺序
9.Time_Stamp
。时间戳
10.Header_Tail
。用于表示枚举成员的个数(不包含该枚举成员)
数据发送
在UDPBridgeSenderComponent<T>::Proc
函数当中,会实例化一个BridgeProtoSerializedBuf
对象,调用成员函数Serialize
将protobuf数据进行序列化,通过计算和protobuf数据当中的一部分信息获得首部,得到一个个的帧,并将所有的帧保存到的数据成员当中,并通过for循环将其发送给目的地址,相关代码:
// 创建序列化对象,将protobuf序列化并分割为一个个带有首部信息的帧,并将其全部发送给目的地址
BridgeProtoSerializedBuf<T> proto_buf;
proto_buf.Serialize(pb_msg, proto_name_);
for (size_t j = 0; j < proto_buf.GetSerializedBufCount(); j++) {
ssize_t nbytes = send(sock_fd, proto_buf.GetSerializedBuf(j),
proto_buf.GetSerializedBufSize(j), 0);
// 若发生的数据和帧的大小不一致,则停止之后的发送
if (nbytes != static_cast<ssize_t>(proto_buf.GetSerializedBufSize(j))) {
break;
}
}
到此即完成了protobuf数据的发送。
由个人能力有限,如有错误,请在评论中指出,不胜感谢!