本项目是一个数据交互模块,以实现多模块间解耦,模块之间可以通过广播模式或者远程函数模式进行数据交互
softbus.c/h
stdint.h
、string.h
、stdlib.h
vector.c/h
:通用类型变长数组
广播模式是一种多对多的数据传输的模式,提供了不同模块之间的数据传输通道
广播的发送者可以广播一系列消息,广播接收者可以订阅自己感兴趣的广播,以监听在该广播上所发布的所有消息
如上图所示,每个广播发送者/接收者就是一个模块,一个广播名就是一个字符串,每个广播名可以视作一个通信管道,发送者通过"send"
操作向管道中写入数据,订阅该管道的接收者就可以收到数据
相较于传统方式中模块间直接调用对方的函数来传递数据,该模式使用字符串话题来标记所传输数据的意义,使得模块间不产生函数依赖和类型依赖,从而不产生文件依赖(无需相互引用对方的文件),以此实现模块间的松耦合
注:虽然广播模式支持多对多通信,但为降低模块间连接复杂度,建议尽可能只进行一对多通信,即同一广播名只由一个模块发出
远程函数模式是一种多对一数据传输模式,当一个模块将一个函数注册为远程函数后,即可被其他模块通过软总线进行调用
与广播模式相比,远程函数的主要意义在于可以向调用者返回数据,如下图所示:
需要注意的是,如果一个远程函数名下尝试注册多个回调函数,只有第一次注册的回调函数会被成功绑定,后续的注册操作会被忽略。
例如:订阅者可以订阅一次"broadcast1"广播,此时绑定一个数据 A,然后再次订阅"broadcast1",并绑定一个数据 B,那么当该广播发送时,该订阅者可以收到两次广播,分别附带有所绑定的 A 和 B
广播
远程函数
性能表现:在 168MHz 主频的 STM32F407 中,本模块有以下性能表现
注:1. 最简情况指仅在单个话题上注册单个空回调函数;2. 若回调内逻辑增加,广播的两种方式的发布频率都会明显下降
选用原则:基于可读性和效率的衡量,我们对两种模式及其发布方式的选用有以下建议
注:此处仅展示基础用法,若要获取更多说明请查看项目文件中代码注释
发送广播
/* 普通发布方式 */
uint8_t value1 = 0x01; //要发布的第一个值
float value2 = 1.0f; //要发布的第二个值
Bus_PublishTopic("topic1", {
{"key1", {.U8 = value1}},
{"key2", {.U8 = value2}}
}); //向总线广播一个映射表数据帧
/* 快速发布方式 */
//创建快速句柄(只在程序初始化时创建一次)
SoftBusReceiverHandle handle = Bus_GetFastTopicHandle("topic2");
//发布数据帧
uint16_t value = 0x201; //要发布的第一个值
uint8_t array[2] = {0x20, 0x01}; //要发布的第二个值
Bus_PublishTopicFast(handle, {{.U16 = value}, {array}}); //向总线快速广播一个列表数据帧
接收广播
//定义软总线回调函数,收到数据时会自动调用
//也可定义为 BUS_TOPICENDPOINT(Callback)
void Callback(const char* name, SoftBusFrame* frame, void* bindData)
{
if(strcmp(name, "topic1") == 0)
{
if(!Bus_CheckMapKeysExist(frame, {"key1", "key2"})) //确保数据帧中存在所需字段
return;
uint8_t value1 = Bus_GetMapValue(frame, "key1").U8; //读取key1字段值
float value2 = Bus_GetMapValue(frame, "key2").F32; //读取key2字段值
/* ...其他处理逻辑 */
}
}
//为提高总线效率,一般使用单独回调函数订阅快速发布的话题
//也可定义为 BUS_TOPICENDPOINT(FastCallback)
void FastCallback(const char* name, SoftBusFrame* frame, void* bindData)
{
uint16_t value = Bus_GetListValue(frame, 0).U16; //获取第一个值
uint8_t* array = (uint8_t*)Bus_GetListValue(frame, 1).Ptr; //获取第二个值
/* ...其他处理逻辑 */
}
//订阅话题
Bus_SubscribeTopic(NULL, Callback, "topic1");
Bus_SubscribeTopic(NULL, Fastcallback, "topic2");
编写和注册远程函数
//定义远程函数回调函数,其他模块请求调用时会自动调用
//该远程函数接收四个参数,arg1:uint8_t, arg2:float, ret1:float, ret2:uint8_t
//前两个参数为输入参数,后两个为输出参数(返回值)
//也可定义为 BUS_REMOTEFUNC(Callback)
bool Callback(const char* name, SoftBusFrame* frame, void* bindData)
{
if(!Bus_CheckMapKeysExist(frame, {"arg1", "arg2","ret1", "ret2"})) //确保数据帧中存在所需所有参数
return false;
uint8_t value1 = Bus_GetMapValue(frame, "arg1").U8; //读取arg1参数值
float value2 = Bus_GetMapValue(frame, "arg2").F32; //读取arg2参数值
float* result1 = (float*)Bus_GetMapValue(frame, "ret1").Ptr; //读取ret1参数值
uint8_t* result2 = (uint8_t*)Bus_GetMapValue(frame, "ret2").Ptr; //读取ret2参数值
//...其他处理逻辑
//返回数据
*result1 = /* ... */;
*result2 = /* ... */;
return true;
}
//向软总线注册该回调函数
Bus_RemoteFuncRegister(NULL, Callback, "fun1")
调用远程函数
uint8_t value1 = 0x01; //要传入的第一个参数
float value2 = 1.0f; //要传入的第二个参数
float result1; //用于接收远程函数第一个返回值
uint8_t result2; //用于接收远程函数第二个返回值
Bus_RemoteFuncCall("fun1", {
{"arg1", {.U8 = &value1}},
{"arg2", {.F32 = &value2}},
{"ret1", {&result1}},
{"ret2", {&result2}}
}); //调用远程函数fun1
注:用
Bus_PublishTopic
或Bus_PublishTopicFast
函数发布时,只有当订阅了该 name 的所有回调函数执行结束后,该发布函数才会退出
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。