在嵌入式市场中,为了更快地提高开发进度,更多都会选用模块应用开发,比如选用Ble、Wifi、Modem等模块,这一类型的模块一般都是AT指令通信,如何通用解析以及处理应答数据极其重要。
如下三条指令,分别为查询类型指令、执行类型指令、设置类型指令。常用的解析方法为调用strstr\sscanf等接口去解析数据,当然其他复杂的源数据还需要特殊的解析,如果在一套数据解析就可以灵活处理这些类型指令数据会大大加快开发进度,本文将讲述一种可以灵活解析数据的方法思路。
//查询类型指令
AT+VERSION?
+ver:AppR01A01V01
OK
//执行类型指令
AT+ACCESSON
OK
+accesson:sucess
//设置类型指令
AT+MODE=1
OK
解析思路
采用回调的方式解析数据,将解析完成的源数据传入回调中由开发者自行处理。当然也是兼容了查询、设置、执行类型的指令数据,parser_target_t结构体中元素包含了begin_str(起始字符串,可为NULL), end_str(结束字符串,可为NULL),is_cont(是否持续解析),cb_func(匹配成功回调函数);
//目前参数result尚未用到,预留
typedef int (*parser_result_callback)(int result, void *pvoid);
/**
- @brief 外部传入指定匹配字符串以及回调函数
- >> begin_str与end_str不能同时为NULL
- >> begin_str = NULL, end_str != NULL, end_str 之前的数据都传入回调
- >> begin_str != NULL, end_str = NULL, 匹配到begin_str就会回调
- >> begin_str != NULL, end_str != NULL, begin_str 与 end_str之间的数据传入回调
*/
typedef struct {
const char *begin_str; /* 匹配起始字符串 */
const char *end_str; /* 匹配结束字符串 */
bool is_cont; /* 在一次数据循环中是否持续匹配,
true:连续匹配,false:单次匹配,匹配成功则退出 */
parser_result_callback cb_func; /* 匹配成功回调函数 */
}parser_target_t;
- begin_str与end_str不能同时为NULL,既然要解析数据,所以必须要有解析对象,解析对象不能同时为NULL;
- 当begin_str与end_str不为NULL时,则解析到begin_str与end_str之间的数据然后传入回调函数中;
static int ver_parser_callback(int result, void *pdata)
{
printf("version:%s\r\n", (char *)pdata);
return 0;
}
static parser_target_t ver_parser_target = {
.begin_str = "+ver:", .end_str = "\r\n", .is_cont = false, .cb_func = ver_parser_callback
};
解析对象:
AT+VERSION?
+ver:AppR01A01V01
OK
回调执行输出:
version:AppR01A01V01
- 当begin_str不为NULL,end_str为NULL,则为匹配到begin_str则成功回调,并且传入数据为NULL,针对这类型的的匹配机制,可把参数cb_func回调设置为NULL,则无需回调直接返回操作成功;
//第一种解析方式
static int module_mode_cfg_status = 0;
static int mode_parser_callback(int result, void *pdata)
{
module_mode_cfg_status = 1;
return 0;
}
static parser_target_t mode_parser_target = {
.begin_str = "OK", .end_str = NULL, .is_cont = false, .cb_func = mode_parser_callback
};
//第二种解析方式
static parser_target_t mode_parser_target = {
.begin_str = "OK", .end_str = NULL, .is_cont = false, .cb_func = NULL
};
解析对象:
//匹配到OK则执行成功
AT+MODE=1
OK
- 当begin_str为NULL,end_str不为NULL,这时将接收到数据暂存缓存区知道匹配到end_str,则将数据传入回调函数,这种考虑到可能一条指令中需要解析为多个数据,则将整包数据传入回调,由回调函数中处理解析;
static int info_parser_callback(int result, void *pdata)
{
printf("info:%s\r\n", (char *)pdata);
return 0;
}
static parser_target_t info_parser_target = {
.begin_str = NULL, .end_str = "OK", .is_cont = false, .cb_func = info_parser_callback
};
解析对象:
AT+INFO?
+ver:AppR01A01V01
+freemem:512k
OK
回调执行输出:
info:
+ver:AppR01A01V01
+freemem:512k
注册接口
//内部结构体
typedef struct {
int step;
int data_pos;
int data_count;
int buffer_size;
unsigned int time_to_clr;
unsigned int clr_tick;
bool is_exist;
char *p_buffer;
parser_target_t target;
parser_target_t *update_target;
}parser_item_t;
typedef parser_item_t* parser_handler_t;
#define parser_clr(parser_handler) do { \
parser_handler->data_pos = 0; \
parser_handler->step = 0; \
parser_handler->data_count = 0; \
if (parser_handler->buffer_size) { \
memset(parser_handler->p_buffer, 0, parser_handler->buffer_size); \
} \
} while (0)
/**
* @brief 创建指定解析器,用于提取对应区间数据
* @param p_target - 解析对象结构体指针
* parser_buffer_size - 截取区间数据缓存区大小
* time_to_clr - 超时无数据,则清除匹配状态,恢复初始状态,单位:ms
* @retval 返回 parser_handler_t格式句柄,创建失败返回NULL
*/
parser_handler_t parser_target_create(parser_target_t *p_target, int parser_buffer_size, unsigned int time_to_clr)
{
parser_handler_t parser_handler = NULL;
if (p_target == NULL)
goto parser_end;
if ((p_target->begin_str == NULL) && (p_target->end_str == NULL))
goto parser_end;
parser_handler = (parser_handler_t)quec_malloc(sizeof(parser_item_t));
if (parser_handler == NULL)
goto parser_end;
if (parser_buffer_size == 0) { /* 单独匹配功能 */
parser_handler->buffer_size = 0;
parser_handler->p_buffer = NULL;
} else {
parser_handler->buffer_size = parser_buffer_size;
parser_handler->p_buffer = (char *)user_malloc(parser_buffer_size + 1);
if (parser_handler->p_buffer == NULL) {
user_free(parser_handler);
parser_handler = NULL;
goto parser_end;
}
}
if (time_to_clr == 0) {
time_to_clr = 200; /* 默认超时200ms */
}
parser_handler->time_to_clr = time_to_clr;
parser_handler->clr_tick = getms();
parser_handler->is_exist = true;
parser_handler->target = *p_target;
parser_handler->update_target = NULL;
parser_clr(parser_handler);
parser_end:
return parser_handler;
}
parser_handler_t parser_target_create(parser_target_t *p_target, int parser_buffer_size, unsigned int time_to_clr)
- p_target : 解析目标对象;
- parser_buffer_size:接收匹配数据缓存区长度,满足解析条件时才将数据存取缓存区中,单位:字节;
- time_to_clr:设置无数据交互时间,当无数据交互超过该时间则清除解析状态,单位:ms;
- 该注册接口执行成功后返回解析句柄。
解析接口
bool is_timeout(unsigned int start_tick, unsigned int timeout)
{
if ((getms() - start_tick) >= timeout)
return true;
else
return false;
}
/******************************************************************************
** 匹配指定指定单个字符串
** 参数 :pos[] - 字符串组匹配字符索引位置,由外部传入,初始化必须为0
** data - 查找的源数据缓存区
** size - 源数据缓存区大小
** ackstr- 匹配的字符串
** 返回值:
** < 0, 未匹配到指定字符串
** >= 0,返回包含指针数组中的下标次序
*******************************************************************************/
int libcom_single_string_match(int *pos, char *data, int size, const char *ackstr)
{
int i = 0;
for (i = 0; i < size; i++) {
if (data[i] != ackstr[pos[0]]) {
pos[0] = 0; /* 匹配失败则从当前字符开始重新匹配 */
}
if (data[i] == ackstr[pos[0]]) {
pos[0]++;
if (0 == ackstr[pos[0]]) { /* 匹配到完整的字符串 */
pos[0] = 0; /* 清除位置状态 */
return 0;
}
}
}
return -1;
}
/**
* @brief 指定解析处理进程
* @param parser_handler - 解析句柄
* data - 传入数据缓存区
* size - 数据缓存区大小
* @retval 0:解析成功, <0:未解析成功或等待解析
*/
int parser_target_process(parser_handler_t parser_handler, char *data, int size)
{
int i = 0;
int ret = -1;
if (parser_handler == NULL)
return -1;
if (parser_handler->is_exist == false)
return -1;
if (true == is_timeout(parser_handler->clr_tick, parser_handler->time_to_clr)) {
parser_clr(parser_handler);
}
if (size) {
parser_handler->clr_tick = getms();
}
if (parser_handler->update_target != NULL) {
os_enter_cirtical();
parser_handler->target = *(parser_handler->update_target);
user_free(parser_handler->update_target);
parser_handler->update_target = NULL;
parser_clr(parser_handler);
os_exit_cirtical();
}
for (i = 0; i < size; i++) {
if ((parser_handler->target.begin_str == NULL) || (parser_handler->step == 1)) { /* 接收匹配结束字符串前数据 */
if (parser_handler->buffer_size) {
parser_handler->p_buffer[parser_handler->data_count++] = data[i];
}
if (0 == libcom_single_string_match(&parser_handler->data_pos, &data[i], 1, parser_handler->target.end_str)) {
if (parser_handler->buffer_size) {
parser_handler->p_buffer[parser_handler->data_count - strlen(parser_handler->target.end_str)] = '\0';
}
if (parser_handler->target.cb_func != NULL) {
parser_handler->target.cb_func(DEV_RET_OK, (void *)parser_handler->p_buffer);
}
ret = 0;
parser_clr(parser_handler);
if (parser_handler->target.is_cont == false)
return 0; /* 成功结束 */
}
} else { /* 匹配起始字符串 */
if (0 == libcom_single_string_match(&parser_handler->data_pos, &data[i], 1, parser_handler->target.begin_str)) {
parser_clr(parser_handler);
if (parser_handler->target.end_str == NULL) { /* 结束字符串为NULL,则直接回调结束 */
if (parser_handler->target.cb_func != NULL) {
parser_handler->target.cb_func(DEV_RET_OK, NULL);
}
ret = 0;
if (parser_handler->target.is_cont == false)
return 0; /* 成功结束 */
} else { /* 进入下一步操作 */
parser_handler->step = 1;
}
}
}
if ((parser_handler->buffer_size) && (parser_handler->data_count >= parser_handler->buffer_size)) {
parser_clr(parser_handler);
}
}
return ret;
}
int parser_target_process(parser_handler_t parser_handler, char *data, int size)
- parser_handler:解析句柄;
- data:传入接收到的数据;
- size:数据大小,单位:字节;
- 该解析接口需要执行任务中调用,并传入接收到的全部源数据。
注销接口
/**
* @brief 解除指定解析器
* @param parser_handler - 解析句柄
* @retval 0:释放成功,-1:释放失败.
*/
int parser_target_delete(parser_handler_t parser_handler)
{
if (parser_handler == NULL)
return -1;
parser_handler->data_pos = 0;
parser_handler->step = 0;
parser_handler->data_count = 0;
parser_handler->target.begin_str = NULL;
parser_handler->target.end_str = NULL;
parser_handler->target.cb_func = NULL;
if (parser_handler->buffer_size) {
user_free(parser_handler->p_buffer);
}
parser_handler->buffer_size = 0;
parser_handler->p_buffer = NULL;
parser_handler->is_exist = false;
parser_handler->update_target = NULL;
user_free(parser_handler);
parser_handler = NULL;
return 0;
}
int parser_target_delete(parser_handler_t parser_handler)
- parser_handler:解析句柄;
- 解析结束后可调用注销接口释放资源。
更新解析目标接口
/**
* @brief 更新解析对象参数
* @param parser_handler - 解析句柄
* p_target - 更新对象
* @retval 0:成功,<0:失败.
*/
int parser_target_update_param(parser_handler_t parser_handler, parser_target_t *p_target)
{
if ((parser_handler == NULL) || (p_target == NULL))
return -EPERM;
os_enter_cirtical();
parser_handler->update_target = (parser_target_t*)quec_malloc(sizeof(parser_target_t));
if (parser_handler->update_target == NULL) {
os_exit_cirtical();
return -ENOMEM;
}
*(parser_handler->update_target) = *p_target;
os_exit_cirtical();
return 0;
}
int parser_target_update_param(parser_handler_t parser_handler, parser_target_t *p_target)
- parser_handler:解析句柄;
- p_target:更新后的解析对象;
- 创建解析对象句柄后,如中间需要匹配多条不同的解析对象,则调用该接口更新解析目标。
清除解析状态接口
/**
* @brief 解析对象状态恢复
* @param parser_handler - 解析句柄
* @retval 0:成功,<0:失败.
*/
int parser_target_state_restore(parser_handler_t parser_handler)
{
if (parser_handler == NULL)
return -1;
os_enter_cirtical();
parser_clr(parser_handler);
os_exit_cirtical();
return 0;
}
int parser_target_state_restore(parser_handler_t parser_handler)
- parser_handler:解析句柄;
- 针对多种解析对象时,调用该接口清除解析状态。
使用示例
parser_handler_t parser_target_create(parser_target_t *p_target, int parser_buffer_size, unsigned int time_to_clr);
int parser_target_delete(parser_handler_t parser_handler);
int parser_target_process(parser_handler_t parser_handler, char *data, int size);
int parser_target_update_param(parser_handler_t parser_handler, parser_target_t *p_target);
int parser_target_state_restore(parser_handler_t parser_handler);
int parser_callback(int result, void *pvoid)
{
printf("parser data = %s \r\n", (char *)pvoid);
return 0;
}
parser_handler_t parser_handler = NULL;
parser_target_t parser_target = { "begin", "end", false, parser_callback };
void parser_init(void)
{
parser_handler = parser_target_create(&parser_target, 64, 200);
if (parser_handler == NULL) {
printf("parser create failed.\r\n");
}
}
int read_data(char *buf, int size)
{
//add here...
return 0;
}
void parser_thread1(void)
{
int read_len = 0;
char read_buffer[64] = { 0 };
read_len = read_data(read_buffer, sizeof(read_buffer));
if (0 == parser_target_process(parser_handler, read_buffer, read_len )) {
printf("parser ok.\r\n");
parser_target_delete(parser_handler);
}
}
总结
这套解析接口可组合使用,视具体应用场景而定。