AF_SYNC通信架构
- 前言
af_sync数据同步框架,旨在解决复杂通信问题;在强度稍微高一点的项目,可能有五个模块甚至更多,你可能使用modbus协议来解决各模块的通信问题,但是这只适用于主从场景,在需要双向通信的场景就没那么适用了,这是这个框架诞生的意义。
- 应对场景
场景一:A->B; A->C; B->A->C; C->A->B
场景二:A->B->C->A
场景三:A->B->C; C->B->A; B->A;
主要就是这三种场景,其他的场景都是基于这三个场景组合扩展出来更复杂的通信架构,这个架构的优点在于:能规范通信代码架构,方便后期维护;减少程序员开发时间,迅速构建通信方案;只需进行简单配置即可实现复杂的通信;通过地址和字符串绑定数据,方便快速添加数据源,非常灵活。缺点:列表形式进行轮询比对,会损失一部分效率;地址只是一个标识符,不是物理地址,与modbus有本质区别,不支持一条指令写多个地址,但是一个地址可自由定义长度;容易出现通信环路;这些都是待解决优化的地方,下面也会对应进行说明。
- 源码介绍
代码由三部分构成,发送,赋值,接收;发送部分主要是轮询地址列表发送数据请求至外部设备;赋值部分提供了变量名赋值和地址赋值接口;接收部分主要是负责接收外部设备的数据请求。支持线程安全,也可以裸机运行。
af_sync.c
/*
*Copyright(C) -
*FileName: af_sync.c
*Author: 我不是阿沸
*Version: 9.0.1
*Date: 2023.08.13
*Description: 数据同步框架,目前接口功能比较完善,如遇问题可联系作者
*Others: 使用接口前一定调用初始化接口,仅适配rt_thread,如需其他操作系统需修改部分代码
*History:
1.Date:2023/08/03
Author:我不是阿沸
适配迪文屏
2.Date:2023/08/16
Author:我不是阿沸
适配can
3.Date:2023/09/18
Author:我不是阿沸
增加相关接口以及超时机制
4.Date:2023/10/01
Author:我不是阿沸
修复无限制发送bug
5.Date:2023/10/31
Author:我不是阿沸
增加在线离线事件回调接口以及心跳处理
6.Date:2023/11/24
Author:我不是阿沸
修改心跳机制,心跳包与其他数据包处理逻辑和普通数据包一样,同步超时调用离线接口
7.Date:2023/11/28
Author:我不是阿沸
合并心跳计数与更新检测处理函数
8.Date:2023/12/05
Author:我不是阿沸
修复数据列表第一位加入心跳类型会无限重发bug
9.Date:2023/12/25
Author:我不是阿沸
修复在未连接状态继续进行数据上传操作bug
10.Date:2024/01/30
Author:我不是阿沸
1.)xxxx提供绑定地址接口xxxx(已撤消)
2.)修改相关接受逻辑
3.)uForm与uRow类型不再适配diwen屏
11.Date:2024/01/31
Author:我不是阿沸
1.)增加uVoid类型
2.)增加无感通信接口
3.)修复读指令无响应问题,这里说明:读指令中的长度位在本框架中无作用,不作任何处理,
任何一条读指令,返回的字节数在上位协议中已经约定好,此框架无法一次读取多个地址
或者一次写多个地址,不同于modbus等协议
12.Date:2024/02/3
Author:我不是阿沸
1.)更改row类型编码逻辑与void类型一致
2.)修复心跳机制不调用离线接口bug
3.)暂存问题:ustring类型目前传输为小端模式,后期将全面修改
13.Date:2024/02/4
Author:我不是阿沸
1.)完善form类型相关代码
2.)加入大小端模式配置,需要注意的是,大端模式是在小端操作系统上而言的
3.)uString修改为大端模式
4.)修复强制类型转换出错宕机bug
14.Date:2024/03/4
Author:我不是阿沸
1.)增加上电离线回调
15.Date:2024/04/09
Author:我不是阿沸
1.)修补Button类型存在的问题:增加Button类型的无感通信,修改Button的更新策略
2.)增加nonind通信环路保护
16.Date:2024/04/10
Author:我不是阿沸
1.)修改strcmp为strncmp,降低风险
17.Date:2024/04/25
Author:我不是阿沸
1.)修改af_name_list_set接口耗时问题
2.)增加af_sync_reconnect_init接口代替af_name_list_set,提供跟快的刷新速率
3.)无感通信由原来的一个节点扩增到n个节点,定义最大元素为50
4.)af_error接口增加对应错误元素索引的传递
18.Date:2024/06/20
Author:我不是阿沸
1.)修复void类型,string类型,form/row类型无感传递死机问题
19.Date:2024/07/03
Author:我不是阿沸
1.)修复string类型无感传递死机问题
20.Date:2024/07/18
Author:我不是阿沸
1.)修复无感传递初始化时未找到对象赋值为0, 导致在0位置的心跳包无限制发送问题
21.Date:2024/08/13
Author:我不是阿沸
1.)大版本迭代,本次迭代移除了一些无必要的类型定义,修改了更新数据的机制解决上一版本数据蜂拥造成的拥堵问题
2.)删除框架中关于类型的定义,只留下其他类型,心跳类型,按键类型
3.)修改缓存区为静态分配,一次性分配整张表,避免了缓冲区的冗余定义
4.)表中增加长度字段,避免冗余的类型定义
5.)修改心跳的机制,以及心跳时间的初始化方式
6.)修改更新数据为时间片的模式,能避免数据拥堵
7.)删除互斥接口,不再有互斥保护,互斥极大的影响了框架的性能,所以移除,目前未发现有线程安全的bug
8.)每个时间片可自由设置更新数据的条数,修改.h文件中的 TICK_UPDATE_NUMBER
9.)其他若干修改与上一版本不再兼容,请注意版本兼容问题
22.Date:2024/08/19
Author:我不是阿沸
1.)修复回调返回FALSE,无感通信依然传递新数据问题
23.Date:2024/10/31
Author:我不是阿沸
1.)修复回调函数强转指针概率性丢失数据的问题
2.)修复无感通信主机端存在该参数,透传端不存在造成的通信死机问题
/
include "af_sync.h"
/**
* @brief 通过名字超找单元,并返回单元索引
* @param 变量名
* @param 变量值
* @retval None
*/
int af_uint_name_find(af_sync_admin af, afu8 name)
{
AF_DATA_TYPE index = -1;
for(int i = 0; i < af->number; i++)
{
if(strncmp((const char )name, (const char )(af->body_list[i].name), AF_NAME_LEN) == 0)
{
index = i;
break;
}
}
return index;
}
/**
* @brief 框架必须存在写请求,写回复,读请求,读回复
* @param 变量名
* @param 变量值
* @retval None
*/
static void af_sync_send_buffer(af_agree_body body, unsigned short addr, unsigned char cmd, unsigned char data, int len)
{
int send_len = 0;
if(body == NULL) return;
switch(cmd)
{
case AF_SYNC_WRITE_REQUST:
send_len = body->write_requst(body->send_buffer, addr, data, len);
break;
case AF_SYNC_WRITE_REPORT:
send_len = body->write_report(body->send_buffer, addr);
break;
case AF_SYNC_READ_REQUST:
send_len = body->read_requse(body->send_buffer, addr, len);
break;
case AF_SYNC_READ_REPORT:
send_len = body->read_report(body->send_buffer, addr, data, len);
break;
}
body->send(body->send_buffer, send_len);
}
/**
* @brief 通过变量名置位向外读取数据标志
* @param 变量名
* @param 变量值
* @retval None
*/
void af_uint_name_get(af_sync_admin af, afu8 name)
{
int index = af_uint_name_find(af, name);
if(index > -1)
{
af->body_list[index].rush_flag = AF_READ_FLAG;
}
return;
}
/**
* @brief 通过名字同步两个同步管理机的数据
* @param 变量名
* @param 变量值
* @retval None
*/
void af_struct_name_sync(af_sync_admin dec, af_sync_admin src, afu8 *name)
{
void *buffer = 0;
afu8 flag = 0;
for(int i = 0; i < src->number; i++)
{
if(strncmp((const char )name, (const char )(src->body_list[i].name), AF_NAME_LEN) == 0)
{
buffer = src->body_list[i].ptr;
flag = 1;
break;
}
}
if(flag == 0) return;
for(int i = 0; i < dec->number; i++)
{
if(strncmp((const char )name, (const char )(dec->body_list[i].name), AF_NAME_LEN) == 0)
{
af_unit_body *body = dec->body_list + i;
if(memcmp((void )body->ptr, (void )buffer, body->len) != 0)
{
memcpy((void )(body->ptr), (const void )buffer, body->len);
body->rush_flag = AF_WRITE_FLAG;
}
break;
}
}
return;
}
/**
* @brief 通过变量名置位向外读取数据标志
* @param 变量名
* @param 变量值
* @retval None
*/
void af_uint_name_read(af_sync_admin af, afu8 *name)
{
int index = af_uint_name_find(af, name);
if(index > -1)
{
return af->body_list[index].ptr;
}
return NULL;
}
/**
* @brief 通过地址同步两个同步管理机的数据
* @param 变量名
* @param 变量值
* @retval None
*/
void af_struct_addr_sync(af_sync_admin dec, af_sync_admin src, afu16 addr)
{
void *buffer = NULL;
afu8 flag = 0;
for(int i = 0; i < src->number; i++)
{
if(addr == src->body_list[i].addr)
{
buffer = src->body_list[i].ptr;
flag = 1;
break;
}
}
if(flag == 0) return;
for(int i = 0; i < dec->number; i++)
{
if(addr == dec->body_list[i].addr)
{
af_unit_body *body = dec->body_list + i;
if(memcmp((void )body->ptr, (void )buffer, body->len) != 0)
{
memcpy((void )(body->ptr), (const void )buffer, body->len);
body->rush_flag = AF_WRITE_FLAG;
}
break;
}
}
return;
}
/**
* @brief 通过变量地址置位向外写数据标志
* @param 变量地址
* @param 变量值
* @retval None
*/
void af_uint_addr_set(af_sync_admin af, afu16 addr, void buffer)
{
for(int i = 0; i < af->number; i++)
{
if(addr == af->body_list[i].addr)
{
af_unit_body *body = af->body_list + i;
if(memcmp((void )body->ptr, (void )buffer, body->len) != 0)
{
memcpy((void )(body->ptr), (const void )buffer, body->len);
body->rush_flag = AF_WRITE_FLAG;
}
break;
}
}
}
/**
* @brief 通过变量地址置位向外写数据标志
* @param 变量地址
* @param 变量值
* @retval None
*/
void af_uint_name_set_int(af_sync_admin af, void name, int buffer)
{
for(int i = 0; i < af->number; i++)
{
if(strncmp((const char )name, (const char )(af->body_list[i].name), AF_NAME_LEN) == 0)
{
af_unit_body *body = af->body_list + i;
if(memcmp((void )body->ptr, (void )&buffer, body->len) != 0)
{
memcpy((void )(body->ptr), (const void )&buffer, body->len);
body->rush_flag = AF_WRITE_FLAG;
}
break;
}
}
}
/**
* @brief 通过变量名置位向外写数据标志
* @param 变量地址
* @param 变量值
* @retval None
*/
void af_uint_name_set(af_sync_admin af, afu8 name, void *buffer)
{
for(int i = 0; i < af->number; i++)
{
if(strncmp((const char )name, (const char )(af->body_list[i].name), AF_NAME_LEN) == 0)
{
af_unit_body *body = af->body_list + i;
if(memcmp((void )body->ptr, (void )buffer, body->len) != 0)
{
memcpy((void )(body->ptr), (const void )buffer, body->len);
body->rush_flag = AF_WRITE_FLAG;
}
break;
}
}
}
/**
* @brief 通过变量名置位强制向外写数据标志
* @param 变量地址
* @param 变量值
* @retval None
*/
void af_uint_name_force_set(af_sync_admin af, afu8 name, void *buffer)
{
for(int i = 0; i < af->number; i++)
{
if(strncmp((const char )name, (const char )(af->body_list[i].name), AF_NAME_LEN) == 0)
{
af_unit_body *body = af->body_list + i;
memcpy(body->ptr, (const void *)buffer, body->len);
body->rush_flag = AF_WRITE_FLAG;
break;
}
}
}
/**
* @brief 通过变量名置位强制向外写数据标志
* @param 变量地址
* @param 变量值
* @retval None
*/
void af_uint_name_force_set_int(af_sync_admin af, afu8 name, afu32 data)
{
for(int i = 0; i < af->number; i++)
{
if(strncmp((const char )name, (const char )(af->body_list[i].name), AF_NAME_LEN) == 0)
{
af_unit_body *body = af->body_list + i;
memcpy(body->ptr, (const void *)&data, body->len);
body->rush_flag = AF_WRITE_FLAG;
break;
}
}
}
/**
* @brief 断线重连更新
* @param 需要重新刷新的数据名列表
* @param 列表长度
* @param 刷新标志,是读还是写
* @retval None
*/
void af_name_list_set(af_sync_admin *af, af_clist list, afu32 len, af_sync_flag flag)
{
for(int n = 0; n < len; n++)
{
for(int i = 0; i < af->number; i++)
{
if(strncmp((const char )list[n], (const char )(af->body_list[i].name), AF_NAME_LEN) == 0)
{
af_unit_body *body = af->body_list + i;
body->rush_flag = flag;
break;
}
}
}
}
/**
* @brief 同步管理机的消息发送
* @param 同步管理机
* @param 同步的地址
* @param 同步的数据
* @param 同步的类型
* @param 消息的类型、模式 读回复、写回复
* @retval None
*/
void af_sync_message_transmit(af_sync_admin af, afu16 addr, void ptr, int len, af_message_mode mode)
{
afu8 *buffer = af->send_buffer;
switch(mode)
{
case AF_SYNC_WRITE_REQUST:
case AF_SYNC_READ_REPORT:
{
if(len > AF_MAX_TRBUFF) len = AF_MAX_TRBUFF;
memcpy(buffer, (const void *)ptr, len);
if(af->alignment == AF_ALIGNMENT_MAX)
{
af_agree_buffer_toggle(buffer, len);
}
if(AF_SYNC_WRITE_REQUST == mode) af_sync_send_buffer(af->agree, addr, AF_SYNC_WRITE_REQUST, buffer, len);
else if(AF_SYNC_READ_REPORT == mode) af_sync_send_buffer(af->agree, addr, AF_SYNC_READ_REPORT, buffer, len);
}
break;
case AF_SYNC_READ_REQUST:
af_sync_send_buffer(af->agree, addr, AF_SYNC_READ_REQUST, NULL, 0);
break;
case AF_SYNC_WRITE_REPORT:
af_sync_send_buffer(af->agree, addr, AF_SYNC_WRITE_REPORT, NULL, 0);
break;
default:
break;
}
}
/**
* @brief 视图更新线程
* @param 同步管理机
* @retval None
*/
void af_bodys_updata_handle(af_sync_admin *af)
{
af_sync_cfg *sync_cfg = NULL;
af_unit_body *body = NULL;
af->tick++;
for(int i = 0; i < TICK_UPDATE_NUMBER; )
{
if(af->cur_index < af->number)
{
body = af->body_list + af->cur_index;
if(body->type == uHeart)
{
if(body->rush_flag == AF_NONE_FLAG)
{
if(af->tick > af->heart_time)
{
body->rush_flag = AF_WRITE_FLAG;
af->tick = 0;
}
}
}
if(af->cfg_list != NULL)
{
sync_cfg = af->cfg_list + af->cur_index;
}
if(body->rush_flag == AF_WRITE_FLAG)
{
if(af->reg_flag == AF_ONLINE && body->type != uHeart)
{
af_sync_message_transmit(af, body->addr, body->ptr, body->len, AF_SYNC_WRITE_REQUST);
if(sync_cfg != NULL)
{
sync_cfg->rush_state = AF_WRITE_FLAG;
sync_cfg->cur_rett = 0;
sync_cfg->cur_time = 0;
}
}
else if(body->type == uHeart)
{
af_sync_message_transmit(af, body->addr, body->ptr, body->len, AF_SYNC_WRITE_REQUST);
if(sync_cfg != NULL)
{
sync_cfg->rush_state = AF_WRITE_FLAG;
}
}
i++;
body->rush_flag = AF_NONE_FLAG;
}
else if(body->rush_flag == AF_READ_FLAG)
{
if(af->reg_flag == AF_ONLINE && body->type != uHeart)
{
af_sync_message_transmit(af, body->addr, body->ptr, body->len, AF_SYNC_READ_REQUST);
if(sync_cfg != NULL)
{
sync_cfg->rush_state = AF_READ_FLAG;
sync_cfg->cur_rett = 0;
sync_cfg->cur_time = 0;
}
}
i++;
body->rush_flag = AF_NONE_FLAG;
}
af->cur_index++;
}
else
{
af->cur_index = 0;
break;
}
}
if(af->cfg_list == NULL) return;
for(int i = 0; i < af->number; i++)
{
sync_cfg = af->cfg_list + i;
body = af->body_list + i;
if(sync_cfg->rush_state == AF_WRITE_FLAG || sync_cfg->rush_state == AF_READ_FLAG)
{
sync_cfg->cur_time++;
if(sync_cfg->cur_time >= sync_cfg->out_time)
{
sync_cfg->cur_rett++;
sync_cfg->cur_time = 0;
if(sync_cfg->rush_state == AF_WRITE_FLAG)
{
af_sync_message_transmit(af, body->addr, body->ptr, body->len, AF_SYNC_WRITE_REQUST);
}
else if(sync_cfg->rush_state == AF_READ_FLAG)
{
af_sync_message_transmit(af, body->addr, body->ptr, body->len, AF_SYNC_READ_REQUST);
}
}
if(sync_cfg->cur_rett >= sync_cfg->retry_time)
{
if(body->type == uHeart && af->offline != NULL )
{
if(af->reg_flag == AF_ONLINE)
{
if(af->offline != NULL)
{
af->offline();
}
}
af->reg_flag = AF_OFFLINE;
}
else if(af->error != NULL && af->reg_flag == AF_ONLINE)
{
af->error(AF_ERROR_OUTTIME, body->name);
}
sync_cfg->cur_rett = 0;
sync_cfg->cur_time = 0;
sync_cfg->rush_state = AF_NONE_FLAG;
}
}
}
}
/**
* @brief 数据同步管理者初始化,初始化不包含重发机制的初始化,记得调用 af_sync_repeat_init
* @param 视图列表
* @param 视图列表条目
* @retval None
*/
int af_sync_admin_init(
af_sync_admin *af,
af_unit_body *list,
int number,
af_agree_body *agree,
void *buffer,
int len,
af_error error)
{
int i = 0, index = 0;
af->body_list = list;
af->cfg_list = NULL;
af->tick = 1000;
af->agree = agree;
af->number = number;
af->malloc_buffer = buffer;
af->malloc_len = len;
for(; i < number ; i++)
{
list[i].ptr = (afu8*)buffer + index;
if(list[i].len / 4 == 1) index += 4;
else if(list[i].len / 4 == 0) index += 4;
else if(list[i].len % 4 == 0) index += (4 * list[i].len / 4);
else index += (4 * list[i].len / 4 + 4);
if(index > len) return -1;
}
af->error = error;
af->reg_flag = AF_ONLINE;
af->offline = NULL;
af->online = NULL;
af->nonind_number = 0;
af->alignment = AF_ALIGNMENT_MAX;
af->reconnect_flag = AF_NONE_FLAG;
af->reconnect_number = 0;
af->cur_index = 0;
return len - index;
}
/**
* @brief 同步初始化
* @param 视图列表
* @param 视图列表条目
* @retval None
*/
void af_sync_repeat_init(af_sync_admin af, af_sync_cfg cfg, afu16 out_time, afu16 retry_time)
{
if(cfg == NULL) return;
for(int i = 0; i < af->number; i++)
{
(cfg + i)->out_time = out_time;
(cfg + i)->retry_time = retry_time;
(cfg + i)->cur_rett = 0;
(cfg + i)->cur_time = 0;
(cfg + i)->rush_state = 0;
}
af->reg_flag = AF_OFFLINE;
if(af->offline != NULL)
{
af->offline();
}
af->cfg_list = cfg;
return;
}
/**
* @brief 断线重连更新
* @param 需要重新刷新的数据名列表
* @param 列表长度
* @param 刷新标志,是读还是写
* @retval None
*/
void af_sync_reconnect_init(af_sync_admin *af, af_clist list, afu32 len, af_sync_flag flag)
{
for(int n = 0; n < len && n < AF_MAX_VOID; n++)
{
for(int i = 0; i < af->number; i++)
{
if(strncmp((const char )list[n], (const char )(af->body_list[i].name), AF_NAME_LEN) == 0)
{
af_unit_body *body = af->body_list + i;
af->reconnect_list[n] = i;
break;
}
else
{
af->reconnect_list[n] = -1;
}
}
}
af->reconnect_flag = flag;
af->reconnect_number = len;
}
/**
* @brief 无感同步初始化
* @param 视图列表
* @param 视图列表条目
* @retval None
*/
void af_sync_nonind_init(af_sync_admin af, af_nonind nonind, afu8 number)
{
if(!af) return;
if(!nonind) return;
af->nonind = nonind;
af->nonind_number = number;
for(int n = 0; n < number; n++)
{
for(int i = 0; i < (nonind + n)->number && i < AF_MAX_VOID;i++)
{
for(int m = 0; m < af->number; m++)
{
if(strncmp((const char )((nonind + n)->list[i]), (const char )(af->body_list[m].name), AF_NAME_LEN) == 0)
{
(nonind + n)->map[i].key = m;
break;
}
else
{
(nonind + n)->map[i].key = -1;
}
}
for(int m = 0; m < (nonind + n)->dec->number; m++)
{
if(strncmp((const char )((nonind + n)->list[i]), (const char )((nonind + n)->dec->body_list[m].name), AF_NAME_LEN) == 0)
{
(nonind + n)->map[i].val = m;
break;
}
else
{
(nonind + n)->map[i].val = -1;
}
}
if((nonind + n)->map[i].key == -1 || (nonind + n)->map[i].val == -1)
{
(nonind + n)->map[i].key = -1;
(nonind + n)->map[i].val = -1;
}
}
}
return;
}
/**
* @brief 注册初始化,用于设备的在线和离线
* @param 注册事件回调
* @param 离线事件回调
* @retval None
*/
void af_sync_register_init(af_sync_admin * af, int heart_time, af_register reg, af_register off)
{
if(af == NULL) return;
af->heart_time = heart_time;
af->reg_flag = AF_OFFLINE;
af->online = reg;
af->offline = off;
return;
}
/**
* @brief 设置框架的大小端模式
* @retval None
*/
void af_sync_aligment_init(af_sync_admin *af, af_alignment_mode mode)
{
if(af == NULL) return;
af->alignment = mode;
return;
}
/**
* @brief 用于接受外部发来的数据同步消息处理
* @param 同步管理机
* @param 事件
* @param 数据,大端模式
* @param 数据长度(字节),uString 数据类型的子类型为u8,所以u8为一个字, uRow子类型如果是u16 那u16就是一个字,以此类推
* @retval None
*/
void af_sync_recv_handle(af_sync_admin af, af_agree_pare async)
{
af_unit_body *body = NULL;
af_sync_cfg *sync_cfg = NULL;
afu16 addr = async->addr;
afu32 len = async->len;
afu16 cmd = async->cmd;
if(async->len > AF_MAX_TRBUFF) async->len = AF_MAX_TRBUFF;
memcpy(af->recv_buffer, async->data, async->len);
afu8 buffer = (afu8 )af->recv_buffer;
for(int i = 0; i < af->number; i++)
{
if(addr == af->body_list[i].addr)
{
body = af->body_list + i;
if(af->cfg_list != NULL)
{
sync_cfg = af->cfg_list + i;
}
if(af->alignment == AF_ALIGNMENT_MAX)
{
af_agree_buffer_toggle(buffer, len);
}
switch(cmd)
{
//写请求和读回复,一个需要回复,一个不需要回复
case AF_SYNC_WRITE_REQUST:
case AF_SYNC_READ_REPORT:
{
if(len > body->len) len = body->len;
if(body->handle != NULL)
{
afbool flag = body->handle(buffer, len);
if(flag)
{
memcpy((void *)(body->ptr), buffer, len);
}
}
else
{
memcpy((void *)(body->ptr), buffer, len);
}
if(body->type == uButton)
{
body->rush_flag = AF_WRITE_FLAG;
}
//如果存在无感通信
for(int num = 0; num < af->nonind_number; num++)
{
if(af->nonind == NULL) break;
for(int n = 0; n < af->nonind[num].number; n++)
{
if(af->nonind[num].map[n].key == i && af->nonind[num].map[n].val != -1)
{
af_sync_admin *dec_admin = af->nonind[num].dec;
af_unit_body *dec_body = dec_admin->body_list + af->nonind[num].map[n].val;
//强制更新
ifdef NONIND_PROTECT
if(memcmp((const char )dec_body->data, (const char )buffer, len) != 0)
endif
{
memcpy((void )(dec_body->ptr), (const void )body->ptr, dec_body->len);
dec_body->rush_flag = AF_WRITE_FLAG;
}
break;
}
}
}
if(cmd == AF_SYNC_WRITE_REQUST) //写请求
{
af_sync_send_buffer(af->agree, addr, AF_SYNC_WRITE_REPORT, NULL, 0);
}
else if(cmd == AF_SYNC_READ_REPORT)//读回复,需要清空标志位
{
if(sync_cfg != NULL)
{
sync_cfg->rush_state = AF_NONE_FLAG;
sync_cfg->cur_rett = 0;
sync_cfg->cur_time = 0;
}
}
}
break;
//写回复,不许要回复,清空标志位就可
case AF_SYNC_WRITE_REPORT:
{
if(body->type == uHeart && af->reg_flag == AF_OFFLINE)
{
af->reg_flag = AF_ONLINE;
if(af->reconnect_flag != AF_NONE_FLAG)
{
for(int i = 0; i < af->reconnect_number; i++)
{
int index = af->reconnect_list[i];
if(index > 0)
af->body_list[index].rush_flag = af->reconnect_flag;
}
}
if(af->online != NULL)
{
af->online();
}
}
if(sync_cfg != NULL)
{
sync_cfg->rush_state = AF_NONE_FLAG;
sync_cfg->cur_rett = 0;
sync_cfg->cur_time = 0;
}
}
break;
//读请求,需要回复,与写请求的发送类似
case AF_SYNC_READ_REQUST:
{
af_sync_message_transmit(af, addr, body->ptr, body->len, AF_SYNC_READ_REPORT);
}
break;
}
break;
}
}
}
af_sync.h
#ifndef __AF_SYNC_H
#define __AF_SYNC_H
#include "af_define.h"
#include "af_agree.h"
#define AF_DATA_TYPE afu32
#define AF_NAME_LEN 30
#define AF_MAX_TRBUFF 256
#define AF_MAX_VOID 80
#define AF_MAX_RECON_NUMBER 100
#define TICK_UPDATE_NUMBER 6
//该宏定义,只为保护在使用无感通信时造成的通信环路;
//当有出现通信环路(a->b->c->a 无限循环)开启宏可进行保护,当数据经过一轮循环返回给a时,数据会被截断不再转发,不开启则会出现环路,需要程序手动处理
//#define NONIND_PROTECT
//#define AF_SYNC_WAIT_MODE
typedef char (*af_clist)[AF_NAME_LEN];
typedef afbool (*application)(void * ptr, int len);
typedef enum{
uOther, //自定义类型
uButton,//uButton 类型会回发写类型数据
uHeart //心跳类型
}af_type;
typedef struct
{
afu8 rush_state; //刷新状态 0:刷新成功 1:刷新失败
afu16 out_time; //超时时间
afu16 retry_time;//重试次数
afu16 cur_time; //内部计数用,无需配置
afu16 cur_rett; //内部计数用,无需配置
}af_sync_cfg; //超时判断机制
typedef struct
{
afu8 name[AF_NAME_LEN]; //地址名
afu16 addr; //地址
af_type type; //数据类型
char len;
void * ptr; //数据缓冲区,如果是数组类型,存储的就是buffer首地址,其他就是对应的具体值
afu8 rush_flag; //刷新标志
application handle; //外部数据同步回调,可用于处理发来的数据以及其他操作,这里特别注意,u32、u16、ubutton 与 其他类型使用方法不一样,详见附件说明
}af_unit_body;
typedef enum
{
AF_SYNC_NONE = 0,
AF_SYNC_WRITE_REQUST = 0x82, //写请求
AF_SYNC_WRITE_REPORT = 0x28, //写回复
AF_SYNC_READ_REQUST = 0x83, //读请求
AF_SYNC_READ_REPORT = 0x38 //读回复
}af_message_mode;
typedef enum
{
AF_NONE,
AF_ERROR_OUTTIME,
AF_ERROR_MALLOC,
}af_error_type; //错误码
typedef enum
{
AF_NONE_FLAG = 0,
AF_WRITE_FLAG,
AF_READ_FLAG,
}af_sync_flag; //同步码
enum
{
AF_OFFLINE = 0,
AF_ONLINE,
};
typedef enum
{
AF_ALIGNMENT_MAX = 0,
AF_ALIGNMENT_MIN,
}af_alignment_mode;
typedef void (* af_write_data)(afu16 addr, afu8 cmd, afu8 * buffer, int len); //外部通信接口
typedef void (* af_read_data)(afu16 addr, int len); //外部通信接口
typedef void (*af_error)(af_error_type error, afu8 * name); //错误处理接口
typedef void (*af_register)(void); //设备注册回调
typedef struct af_nonind_admin af_nonind;
typedef struct{
af_unit_body * body_list;
af_sync_cfg * cfg_list;
af_agree_body * agree;
afu32 cur_index;
void * malloc_buffer;
int malloc_len;
afu16 tick;
int number;//body_list 数量
af_error error;//错误处理接口
afu8 reg_flag;//注册标志
af_register online;
af_register offline;
int heart_time;//心跳时间
af_nonind * nonind;//无感列表
afu8 nonind_number;//无感数量
afu8 alignment;//大小端
afu16 reconnect_flag;
int reconnect_list[AF_MAX_RECON_NUMBER];
afu8 reconnect_number;
afu8 send_buffer[AF_MAX_TRBUFF];
afu32 recv_buffer[AF_MAX_TRBUFF/4];
}af_sync_admin;//管理机
struct af_nonind_admin
{
af_sync_admin * dec;
afumap map[AF_MAX_RECON_NUMBER];
af_clist list;
afu32 number;
};
int af_uint_name_find(af_sync_admin *af, afu8 *name);
void af_uint_name_get(af_sync_admin *af, afu8 *name);
void af_uint_name_force_set_int(af_sync_admin *af, afu8 *name, afu32 data);
void af_struct_name_sync(af_sync_admin *dec, af_sync_admin *src, afu8 *name);
void af_struct_addr_sync(af_sync_admin *dec, af_sync_admin *src, afu16 addr);
void af_uint_addr_set(af_sync_admin *af, afu16 addr, void * buffer);
void af_uint_name_set(af_sync_admin *af, afu8 *name, void * buffer);
void af_uint_name_set_int(af_sync_admin *af, void * name, int buffer);
void * af_uint_name_read(af_sync_admin *af, afu8 *name);
void af_uint_name_force_set(af_sync_admin *af, afu8 *name, void * buffer);
void af_name_list_set(af_sync_admin *af, af_clist list, afu32 len, af_sync_flag flag);
void af_sync_message_transmit(af_sync_admin *af, afu16 addr, void * ptr, int len, af_message_mode mode);
void af_bodys_updata_handle(af_sync_admin *af);
int af_sync_state_get(af_sync_admin *af);
int af_sync_admin_init(af_sync_admin *af,af_unit_body *list,int number,af_agree_body *agree,void * buffer,int len,af_error error);
void af_sync_repeat_init(af_sync_admin *af, af_sync_cfg *cfg, afu16 out_time, afu16 retry_time);
void af_sync_reconnect_init(af_sync_admin *af, af_clist list, afu32 len, af_sync_flag flag);
void af_sync_nonind_init(af_sync_admin *af, af_nonind *nonind, afu8 number);
void af_sync_register_init(af_sync_admin * af, int heart_time, af_register reg, af_register off);
void af_sync_aligment_init(af_sync_admin *af, af_alignment_mode mode);
void af_sync_recv_handle(af_sync_admin *af, af_agree_pare * async);
#endif
1.几个重要的宏定义
#define AF_NAME_LEN 20
#define AF_MAX_TRBUFF 100
AF_NAME_LEN:数据名最大长度。
AF_MAX_TRBUFF:数据发送最大缓冲区
- 支持传输的数据类型
数据类型 | 说明 | 长度 |
uInt16 | 16位无符号短整型变量 | 2字节 |
uInt32 | 32位无符号短整型变量 | 4字节 |
uString | 8位无符号数组 | 自定义长度 |
uVoid | 自定义类型 | 自定义长度 |
uButton | 按键类型,16位无符号短整型变量,收到写请求,会回发写请求,可用于更新上位机操作状态。 | 2字节 |
uForm | 表格类型,支持16位无符号短整型变量和32位无符号短整型变量 | 自定义长度 |
uRow | 数组行类型,支持16位无符号短整型变量和32位无符号短整型变量 | 自定义长度 |
uHeart | 心跳类型,16位无符号短整型变量 | 2字节 |
- 支持的请求
AF_SYNC_WRITE_REQUEST | 写请求 | 0x82 |
AF_SYNC_WRITE_REPORT | 写回复 | 0x28 |
AF_SYNC_READ_REQUEST | 读请求 | 0x83 |
AF_SYNC_READ_REPORT | 读回复 | 0x38 |
- 类型结构体说明
结构体名称 | 描述 | 成员变量 |
af_type_form | 用于描述单元格的位置和数据类型。 | - horizontal: 单元格的横坐标<br>- vertical: 单元格的纵坐标<br>- type: 自定义类型 af_type,表示单元格的数据类型 |
af_type_void | 用于描述自定义类型的长度。 | - len: 自定义类型的长度 |
af_type_row | 用于描述行的位置和数据类型。 | - horizontal: 行的横坐标<br>- type: 自定义类型 af_type,表示行的数据类型 |
af_type_string | 用于描述字符串的长度。 | - len: 字符串的长度 |
af_type_heart | 用于描述心跳数据。 | - time: 心跳时间<br>- count: 心跳次数 |
af_type_cfg | 用于描述数据类型和相关配置。 | - type: 自定义类型 af_type,表示数据的类型<br>- cfg: 指针,指向与该类型相关的配置信息 |
- 以下是对 af_sync_admin管理机 结构体的说明
成员变量 | 描述 | 成员变量 |
body_list | 指向 af_unit_body 结构体数组的指针。 | body_list |
cfg_list | 指向 af_sync_cfg 结构体数组的指针。 | cfg_list |
out_time | 用于指定超时时间的 16 位无符号整数。 | out_time |
retry_time | 用于指定重试时间的 16 位无符号整数。 | retry_time |
number | body_list 数组的元素数量。 | number |
rt_mutex_t | 用于数据安全的实时线程互斥锁。 | rt_mutex_t |
write | 外部通信接口。 | write |
error | 错误处理接口。 | error |
reg_flag | 注册标志,可能是 8 位无符号整数。 | reg_flag |
online | 在线注册接口。 | online |
offline | 离线注册接口。 | offline |
nonind | 指向 af_nonind 结构体的指针。 | nonind |
alignment | 对齐标志,可能是 8 位无符号整数。 | alignment |
af_sync_admin 结构体用于管理通信机制,它包含了各种用于数据同步和通信的配置、接口以及处理方法。
- 以下是对 af_sync_cfg 结构体的说明
成员变量 | 描述 | 成员变量 |
rush_state | 刷新状态。0 表示刷新成功,1 表示刷新失败。 | rush_state |
out_time | 超时时间,用于指定超时时长的 16 位无符号整数。 | out_time |
retry_time | 重试次数,用于指定重试次数的 16 位无符号整数。 | retry_time |
cur_time | 内部计数器,用于内部计数,无需配置。 | cur_time |
af_sync_cfg 结构体用于定义超时判断机制的配置参数,包括刷新状态、超时时间和重试次数。cur_time 和 cur_rett 是内部计数器,不需要外部配置,可能用于内部计数或其他用途。
- 接口说明
函数名 | 描述 |
void af_sync_aligment_init(af_sync_admin * af, af_alignment_mode mode); | 初始化同步管理器的对齐模式。 |
void af_sync_register_init(af_sync_admin * af, af_register reg, af_offline off); | 初始化同步管理器的注册和离线状态。 |
void af_sync_repeat_init(af_sync_admin * af, af_sync_cfg * cfg, afu16 out_time, afu16 retry_time); | 初始化同步管理器的重复操作配置,包括超时时间和重试次数。 |
void af_sync_recv_handle(af_sync_admin * af, afu8 cmd, afu16 addr, afu8 * buffer, afu16 len); | 处理接收到的同步数据。 |
void af_sync_admin_init(af_sync_admin * af, af_unit_body * list, int number, af_write_data write, af_error error); | 初始化同步管理器,包括单元体列表、数量、写入数据函数和错误处理函数。 |
void af_sync_nonind_init(af_sync_admin * af, af_nonind * nonind); | 初始化非指示符。 |
void af_bodys_updata_handle(af_sync_admin * af); | 处理单元体更新。 |
void af_uint_addr_receive(af_sync_admin * af, afu16 addr, AF_DATA_TYPE val); | 接收地址类型的数据。 |
void af_uint_name_receive(af_sync_admin * af, afu8 * name, AF_DATA_TYPE val); | 接收名称类型的数据。 |
void af_struct_name_sync(af_sync_admin * dec, af_sync_admin * src, afu8 * name); | 同步结构体中指定名称的成员。 |
void af_struct_addr_sync(af_sync_admin * dec, af_sync_admin * src, afu16 addr); | 同步结构体中指定地址的成员。 |
void af_uint_addr_set(af_sync_admin * af, afu16 addr, AF_DATA_TYPE val); | 设置地址类型的数据。 |
void af_uint_name_set(af_sync_admin * af, afu8* name, AF_DATA_TYPE val); | 设置名称类型的数据。 |
void af_uint_name_update(af_sync_admin * af, afu8 * name); | 更新名称类型的数据。 |
void af_heart_handle(af_sync_admin * af); | 处理心跳数据。 |
AF_DATA_TYPE af_uint_name_get(af_sync_admin * af, afu8 * name); | 获取名称类型的数据。 |
void af_uint_name_force_set(af_sync_admin * af, afu8* name, AF_DATA_TYPE val); | 强制设置名称类型的数据。 |
void af_name_list_set(af_sync_admin * af, af_clist list, afu32 len, af_sync_flag flag); | 设置名称列表。 |
- 使用范例
首先注册一个需要同步的数据表:数据名,数据地址, 数据类型, 初始值,是否上电同步, 数据同步事件回调,这张表双端都是一样的,执行端如果有需要就配置相应的回调。
af_unit_body can1_list[] =
{
"type", 0xFFFF, &u32_can, Rf, 0, NULL,
"r_im2", 0x2006, &u32_can, 0, 0, NULL,
"r_im3", 0x2008, &u32_can, 0, 0, NULL,
"ctr_cqm1", 0x2002, &u32_can, 100000, 0, NULL,
"ctr_cqm2", 0x2004, &u32_can, 100000, 0, NULL,
"ad_cqm1", 0x4012, &u32_can, 0, 0, NULL,
"ad_cqm2", 0x4014, &u32_can, 0, 0, NULL,
"cqm_en", 0x4018, &u32_can, 0, 0, NULL,
"gnd_en", 0x4016, &u32_can, 0, 0, NULL,
"temp", 0x1010, &u32_can, 0, 0, NULL,
"outv", 0x4000, &u32_can, 0, 0, NULL,
"outi", 0x4002, &u32_can, 0, 0, NULL,
"m_outr", 0x4004, &u32_can, 0, 0, NULL,
"outw", 0x4006, &u32_can, 0, 0, NULL,
"ini", 0x4008, &u32_can, 0, 0, NULL,
"inv", 0x400a, &u32_can, 0, 0, NULL,
"inw", 0x400e, &u32_can, 0, 0, NULL,
"vm3", 0x400c, &u32_can, 0, 0, NULL,
"vm4", 0x401e, &u32_can, 0, 0, NULL,
};
#include "sync.h"
#include "after.h"
#include "serial.h"
#include "gather.h"
#include "serial.h"
#include "handle.h"
typedef enum
{
Measure,
Rf,
PowerSupply,
HandShank
}Module_Type;
typedef struct
{
unsigned short id;
Module_Type type;
}af_module;
#define MASTER_ID 0x56
af_module af_module_pool[20];
void af_data_send(uint8_t * data, int len)
{
can1_transmit(data, len);
}
/**
* @brief 封装32位变量数据
* @param 需要发送的ID,如果需要发送至多个ID,这个ID为板载类型ID,每种类型的板子都会有一个独特的ID,设置为0xff
* @param 变量值
* @retval None
*/
void af_write_reg_requst(unsigned short addr, unsigned int data)
{
uint8_t buffer[20];
buffer[0] = AF_SYNC_WRITE_REQUST;
buffer[1] = MASTER_ID;
buffer[2] = addr / 256;
buffer[3] = addr % 256;
buffer[4] = data >> 24;
buffer[5] = data >> 16;
buffer[6] = data >> 8;
buffer[7] = data & 0xff;
af_data_send(buffer, 8);
}
/**
* @brief 封装32位变量数据
* @param 变量地址
* @param 变量值
* @retval None
*/
void af_write_reg_reply(unsigned short addr)
{
uint8_t buffer[20];
buffer[0] = AF_SYNC_WRITE_REPORT;
buffer[1] = MASTER_ID;
buffer[2] = addr / 256;
buffer[3] = addr % 256;
buffer[4] = 0;
buffer[5] = 0;
buffer[6] = 0;
buffer[7] = 0;
af_data_send(buffer, 8);
}
/**
* @brief 封装32位变量数据
* @param 需要发送的ID,如果需要发送至多个ID,这个ID为板载类型ID,每种类型的板子都会有一个独特的ID,设置为0xff
* @param 变量值
* @retval None
*/
void af_read_reg_requst(unsigned short addr)
{
uint8_t buffer[20];
buffer[0] = AF_SYNC_READ_REQUST;
buffer[1] = MASTER_ID;
buffer[2] = addr / 256;
buffer[3] = addr % 256;
buffer[4] = 0xff;
buffer[5] = 0xff;
buffer[6] = 0xff;
buffer[7] = 0xff;
af_data_send(buffer, 8);
}
/**
* @brief 封装32位变量数据
* @param 变量地址
* @param 变量值
* @retval None
*/
void af_read_reg_reply(unsigned char id, unsigned short addr, unsigned int data)
{
uint8_t buffer[20];
buffer[0] = AF_SYNC_READ_REPORT;
buffer[1] = id;
buffer[2] = addr / 256;
buffer[3] = addr % 256;
buffer[4] = data >> 24;
buffer[5] = data >> 16;
buffer[6] = data >> 8;
buffer[7] = data & 0xff;
af_data_send(buffer, 8);
}
void can1_tran_buffer(unsigned short addr, unsigned char cmd, unsigned char * data, int len)
{
switch(cmd)
{
case AF_SYNC_WRITE_REQUST:
{
afu32 val = afu32_buffer_max(data);
af_write_reg_requst(addr, val);
}
break;
case AF_SYNC_WRITE_REPORT:
af_write_reg_reply(addr);
break;
}
}
af_type_cfg u16_can = {uInt16, NULL};
AfScny afCan = {0, 0, 100, 3, 0, 0};
af_type_cfg u32_can = {uInt32, NULL};
af_unit_body can1_list[] =
{
"type", 0xFFFF, &u32_can, Rf, 0, NULL,
"r_im2", 0x2006, &u32_can, 0, 0, NULL,
"r_im3", 0x2008, &u32_can, 0, 0, NULL,
"ctr_cqm1", 0x2002, &u32_can, 100000, 0, NULL,
"ctr_cqm2", 0x2004, &u32_can, 100000, 0, NULL,
"ad_cqm1", 0x4012, &u32_can, 0, 0, NULL,
"ad_cqm2", 0x4014, &u32_can, 0, 0, NULL,
"cqm_en", 0x4018, &u32_can, 0, 0, NULL,
"gnd_en", 0x4016, &u32_can, 0, 0, NULL,
"temp", 0x1010, &u32_can, 0, 0, NULL,
"outv", 0x4000, &u32_can, 0, 0, NULL,
"outi", 0x4002, &u32_can, 0, 0, NULL,
"m_outr", 0x4004, &u32_can, 0, 0, NULL,
"outw", 0x4006, &u32_can, 0, 0, NULL,
"ini", 0x4008, &u32_can, 0, 0, NULL,
"inv", 0x400a, &u32_can, 0, 0, NULL,
"inw", 0x400e, &u32_can, 0, 0, NULL,
"vm3", 0x400c, &u32_can, 0, 0, NULL,
"vm4", 0x401e, &u32_can, 0, 0, NULL,
};
af_sync_admin can1_admin;
af_sync_cfg can1_sync_cfgs[sizeof(can1_list) / sizeof(af_unit_body)];
void can1_error(af_error_type error)
{
rt_kprintf("can out time\n");
}
void can1_sync_init(void)
{
///初始化数据同步管理者
af_sync_admin_init(&can1_admin, can1_list, sizeof(can1_list) / sizeof(af_unit_body), can1_tran_buffer, can1_error);
///初始化重发机制
af_sync_repeat_init(&can1_admin, can1_sync_cfgs, 600, 3);
}
void af_updata_task(void * p)
{
SYSTEMDELAY(3000);
while(1)
{
af_bodys_updata_handle(&can1_admin);
SYSTEMDELAY(1);
}
}
extern af_sync_admin dw_admin;
void af_logic_task(void * p)
{
while(1)
{
Electrical ele;
get_electrical(&ele);
ele.outi = af_uint_name_get(&can1_admin, (afu8*)"outi");
ele.outv = af_uint_name_get(&can1_admin, (afu8*)"outv");
ele.ini = af_uint_name_get(&can1_admin, (afu8*)"ini");
ele.inv = af_uint_name_get(&can1_admin, (afu8*)"inv");
ele.vm3 = af_uint_name_get(&can1_admin, (afu8*)"vm3");
ele.vm4 = af_uint_name_get(&can1_admin, (afu8*)"vm4");
af_struct_name_sync(&can1_admin, &dw_admin, (afu8*)"ctr_cqm1");
af_struct_name_sync(&can1_admin, &dw_admin, (afu8*)"ctr_cqm2");
af_struct_name_sync(&can1_admin, &dw_admin, (afu8*)"cqm_en");
af_struct_name_sync(&can1_admin, &dw_admin, (afu8*)"gnd_en");
af_uint_name_set(&dw_admin, (afu8*)"m_outi", (int)(ele.outi));
af_uint_name_set(&dw_admin, (afu8*)"m_outv",(int)(ele.outv));
af_uint_name_set(&dw_admin, (afu8*)"m_outw", (int)(ele.outi/1000*ele.outv));
af_struct_name_sync(&dw_admin, &can1_admin, (afu8*)"m_outr");
af_struct_name_sync(&dw_admin, &can1_admin, (afu8*)"ad_cqm1");
af_struct_name_sync(&dw_admin, &can1_admin, (afu8*)"ad_cqm2");
SYSTEMDELAY(50);
};
}
/**
* @brief 封装32位变量数据
* @param 变量地址
* @param 变量值
* @retval None
*/
void after_sync_rx(void * p)
{
uint16_t addr = 0; //地址缓存
uint32_t data = 0; //数据缓存
uint16_t CRC_U16=0;
uint8_t R_Crc_h,R_Crc_l;
uint8_t buffer[50];
int len = 0;
for(;;)
{
if(can1_receive(buffer, &len))
{
uint8_t cmd = buffer[0];
uint8_t id = buffer[1];
uint32_t addr = afu16_buffer_max((buffer+2));
uint32_t data = afu32_buffer_max((buffer+4));
// for(int i = 0; i < len; i++)
// {
// rt_kprintf("%02X ", buffer[i]);
// };
// rt_kprintf("\n");
switch(cmd)
{
case AF_SYNC_WRITE_REQUST:
//rt_kprintf("addr:%04X, data:%d\n", addr, data);
af_sync_recv_handle(&can1_admin, AF_SYNC_WRITE_REQUST, addr, buffer+4, 4);
break;
case AF_SYNC_WRITE_REPORT:
//rt_kprintf("AF_SYNC_WRITE_REPORT\n");
af_sync_recv_handle(&can1_admin, AF_SYNC_WRITE_REPORT, addr,NULL, 0);
break;
case AF_SYNC_READ_REQUST:
af_sync_recv_handle(&can1_admin, AF_SYNC_READ_REQUST, addr, NULL, 0);
break;
case AF_SYNC_READ_REPORT:
af_sync_recv_handle(&can1_admin, AF_SYNC_READ_REPORT, addr, buffer+4, 4);
break;
}
}
else
{
//rt_kprintf("error\n");
}
SYSTEMDELAY(1);
}
}
- 注意事项
- 版权说明 我不是阿沸: AF工具库,不断完善 (gitee.com)