一、cJson库
1. 功能简述
- 处理json数据,支持创建、解析以及操作json数据,适用于嵌入式系统
- 特点:轻量、易用、跨平台
- 代码地址
https://github.com/DaveGamble/cJSON.git,使用时只需要包含cJSON.c和cJSON.h
2. API介绍和使用
cJSON* cJSON_CreateObject(void); // 创建一个空的cJSON对象
cJSON* cJSON_CreateArray(void); // 创建一个空的cJSON数组
cJSON* cJSON_CreateString(const char *string); // 创建一个字符串cJSON对象
cJSON* cJSON_CreateNumber(double num); // 创建一个数字cJSON对象
cJSON_bool cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); //
cJSON* cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); //
3. 使用示例
// 创建JSON示例
cJSON *root = cJSON_CreateObject(); // 创建一个根节点
cJSON_AddStringToObject(root, "name", "John"); // 添加字符串对象到根节点中
cJSON_AddNumberToObject(root, "age", 30); // 添加数字对象到根节点中
cJSON *hobbies = cJSON_AddArrayToObject(root, "hobbies"); // 添加数组对象到根节点中
if (hobbies != NULL)
{
cJSON_AddItemToArray(hobbies, cJSON_CreateString("reading")); // 往cJSON节点中添加字符串属性
cJSON_AddItemToArray(hobbies, cJSON_CreateString("swimming")); // 往cJSON节点中添加字符串属性
cJSON_AddItemToArray(hobbies, cJSON_CreateString("coding")); // 往cJSON节点中添加字符串属性
}
cJSON *friends = cJSON_AddArrayToObject(root, "friends"); // 添加数组对象到根节点中
if (friends != NULL)
{
// 第一个朋友
cJSON *friend1 = cJSON_CreateObject();
cJSON_AddStringToObject(friend1, "name", "Alice");
cJSON_AddNumberToObject(friend1, "age", 28);
cJSON_AddItemToArray(friends, friend1); // 嵌套添加节点
// 第二个朋友
cJSON *friend2 = cJSON_CreateObject();
cJSON_AddStringToObject(friend2, "name", "Bob");
cJSON_AddNumberToObject(friend2, "age", 32);
cJSON_AddItemToArray(friends, friend2); // 嵌套添加节点
}
char *json_string = cJSON_Print(root); // 生成一个格式化的字符串带空格
printf("%s\n", json_string);
cJSON_free(json_string); // 对应cJSON_Print的释放函数
char *json_string1 = cJSON_PrintUnformatted(root); // 生成一个紧凑的字符串不带空格
printf("%s\n", json_string1);
cJSON_free(json_string1); // 对应cJSON_PrintUnformatted的释放函数
cJSON_Delete(root); // 对应cJSON_CreateObject的释放函数
// 解析JSON示例
cJSON *json = cJSON_Parse(json_string); // 输入json字符串生成cJSON根节点
if (root == NULL) { // 错误检查
printf("解析错误\n");
return;
}
cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name"); // 从cJSON节点中根据名称获得对应的cJSON对象
if (name != NULL && cJSON_IsString(name))
{
const char *name_str = cJSON_GetStringValue(name); // 处理字符串
printf("姓名: %s\n", name_str);
}
cJSON_Delete(json); // 释放cJSON根节点
4. 主要数据结构与API实现方式
// 关键结构体
typedef struct cJSON
{
struct cJSON *next;
struct cJSON *prev; // 双向链表组织cJSON结构体
struct cJSON *child; // json结构的子节点
int type; // json对象的类型
char *valuestring; // 字符串,类型是cJSON_String或cJSON_Raw时该值有效
int valueint; // 数值,整数值,被valuedouble替代
double valuedouble; // 数值,浮点数,类型是cJSON_Number时该值有效
char *string; // 名称
} cJSON;
// 内存分配和释放函数
malloc、free、realloc分别对应global_hooks结构体的三个成员
void cJSON_InitHooks(cJSON_Hooks* hooks); // 该函数可以自定义内存的分配和释放函数
cJSON *cJSON_New_Item(const internal_hooks * const hooks); // 该函数调用malloc函数返回一个cJSON的结构体并返回指针
unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks); // 字符串复制函数,调用内存分配并memcpy
cJSON *cJSON_CreateObject(void); // 调用cJSON_New_Item获得一个新的cJSON对象,并设置type为cJSON_Object
cJSON *cJSON_CreateArray(void); // 调用cJSON_New_Item获得一个新的cJSON对象,并设置type为cJSON_Array
cJSON *cJSON_CreateNumber(double num); // 调用cJSON_New_Item获得一个新的cJSON对象,并设置type为cJSON_Number,并设置valuedouble成员
cJSON *cJSON_CreateRaw(const char *raw); // 调用cJSON_New_Item获得一个新的cJSON对象,并设置type为cJSON_Raw,并调用cJSON_strdup完成字符串
cJSON *cJSON_CreateString(const char *string); // 同上,区别是type是cJSON_String
cJSON *cJSON_CreateIntArray(const int *numbers, int count); // 数组表示一个节点,child成员是第一个数字节点,所有的数字节点使用双向链表连接
cJSON *cJSON_CreateFloatArray(const float *numbers, int count); // 同上
cJSON *cJSON_CreateDoubleArray(const double *numbers, int count); // 同上
cJSON *cJSON_CreateStringArray(const char *const *strings, int count); // 同上
cJSON_bool cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); // 添加元素到cJSON对象中
cJSON_bool cJSON_AddItemToArray(cJSON *array, cJSON *item); // 添加元素到数组类型的cJSON对象中
cJSON_AddxxToObject; //创建一个xx,并将xx放入Object中
char *cJSON_Print(const cJSON *item); // 格式化输出json为字符串
char *JSON_PrintUnformatted(const cJSON *item); // 压缩输出json为字符串
cJSON *cJSON_Parse(const char *value); // 输入json字符串,输出JSON根节点
cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name"); // 从cJSON节点中根据名称获得对应的cJSON对象
cJSON_IsString是否是string
cJSON_IsNumber是否是number
cJSON_IsBool是否是布尔
cJSON_IsArray是否是数组,之后使用宏cJSON_ArrayForEach进行范围for循环,实际是一个for循环的封装
cJSON_GetStringValue返回json的字符串,类型不匹配返回NULL
cJSON_GetNumberValue返回json的数值,类型不匹配返回NAN
5. 需要关注的点
cJSON_CreateRaw中的字符串是原始的,不进行转义
cJSON_CreateIntArray创建了n+1个节点,其中一个节点作为父节点表示数组,其他的n个节点存储数据并通过双向链表连接
cJSON_ArrayForEach看起来像是范围for循环,实际是for循环的封装
- 关注的有数值、字符串、数组、嵌套
cJSON_CreateObject与cJSON_Delete对应
- 使用
cJSON_Print和cJSON_PrintUnformatted的返回值需要使用cJSON_free释放
- 使用
cJSON_AddxxToObject将json节点添加到Object,xx节点就不需要自己释放,释放Object的时候会释放掉xx
cJSON_Parse与cJSON_Delete对应
二、来自Zephyr的SMF
1. 概述
- 核心文件
smf.h和smf.c以及单独拿出这两个文件需要的配置文件,实现了简单的状态机
- 状态:每个状态有三个函数,
entry、run和exit分别对应状态进入、状态执行和状态退出执行的函数
- 状态转换发生的时候,SMF会自动调用响应状态的
entry和exit函数
- 结构体
smf_ctx保存了状态机的运行状态,例如当前状态、先前状态和终止标志
- entry函数进入状态触发,执行一次,做初始化和资源分配操作
- run函数,调用
smf_run_state会执行,可以执行多次,用于处理事件、检查条件和执行状态内逻辑操作
- exit函数离开状态触发,执行一次,用作清理资源、保存状态
- 使用时,一般将结构体
smf_ctx放到自定义结构体的首位,这样使用宏SMF_CTX(o)可以将状态结构体中函数的传参obj强制转换为smf_ctx结构体指针
2. 关键结构体
// 状态结构体
struct smf_state {
const state_method entry; // 进入状态时执行的函数
const state_execution run; // 状态运行时执行的函数
const state_method exit; // 退出状态时执行的函数
// ... 其他可选字段(层次状态机相关)
};
typedef void (*state_method)(void *obj); // 状态进入和退出函数参数是void*(一般是包含结构体smf_ctx的结构体),返回值void
typedef enum smf_state_result (*state_execution)(void *obj); // 状态运行函数参数是void*(同上),返回值是一个枚举
enum smf_state_result 一种是SMF_EVENT_HANDLED(事件已处理不需要向上传播)另一种是SMF_EVENT_PROPAGATE(事件未处理需要向上传播)
SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) // 宏用来创建一个结构体smf_state,参数都可以是NULL
// 状态机上下文结构体
struct smf_ctx {
const struct smf_state *current; // 当前状态
const struct smf_state *previous; // 先前状态
const struct smf_state *executing; // 正在执行的状态(层次状态机)
int32_t terminate_val; // 终止值
uint32_t internal; // 内部状态(框架使用)
};
// 宏
#define SMF_CTX(o) ((struct smf_ctx *)o) // 实现了结构体指针的强制转换
3. API介绍
void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state); // 初始化状态机并设置一个初始状态
该函数执行了初始状态的entry函数,并设置内部标志位,如果启用层次状态机,则自动处理初始化转换连
void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state); // 状态转换为new_state
该函数分三步,第一步递归执行从当前状态到公共祖先状态的exit函数,第二步更新状态机上下文的状态变量,第三步执行从公共祖先到目标状态的entry函数
int32_t smf_run_state(struct smf_ctx *ctx); // 调用run函数进行事件处理
该函数调用当前状态的run函数,并且如果是层次状态机之后还会依次调用父对象的run函数
void smf_set_terminate(struct smf_ctx *ctx, int32_t val); // 终止状态机,并设置一个终止值
该函数执行后再次执行smf_run_state会返回刚才设置的val值
const struct smf_state *smf_get_current_leaf_state(const struct smf_ctx *const ctx); // 获得当前叶子状态
const struct smf_state *smf_get_current_executing_state(const struct smf_ctx *const ctx); // 获得当前执行状态
4. 示例
/* 从状态 A 转换到状态 B */
smf_set_state(&ctx, &states[STATE_B]);
/* 不涉及层次状态机的执行顺序:
* 1. states[STATE_A].exit() (如果存在)
* 2. 更新 ctx->previous = &states[STATE_A]
* 3. 更新 ctx->current = &states[STATE_B]
* 4. states[STATE_B].entry() (如果存在)
*/
/* 典型的状态机执行模型 */
void state_machine_loop(void) {
struct smf_ctx ctx;
// 1. 初始化
smf_set_initial(&ctx, &states[INITIAL_STATE]);
// 2. 事件循环
while (1) {
// 处理输入或事件
process_events();
// 运行状态机
int32_t ret = smf_run_state(&ctx); // run函数中可能调用smf_set_state进行状态转换、也可能调用smf_set_terminate终止状态
// 检查是否终止
if (ret != 0) { // 返回0表示状态机正常可以继续转换,非0需要状态机终止
break;
}
// 延时或其他操作
delay_ms(10);
}
}