/* 命令行的 JSON 两头:**只认一层扁平对象**的解析器 + 往定长缓冲里拼的写入器(A12,需求 §10.8)。 §10.8 定的分帧是「一条命令 = 一行 JSON」。请求这一侧永远是一层扁平对象: {"seq":17,"cmd":"key","rail":3,"keys":"pwr+vdn","ms":2000} 所以这里**不引 cJSON**,自己写一个只认这个形状的小解析器,理由两条: · cJSON 是 ESP-IDF 的组件,宿主机上要另外 vendor 一份;它还要 malloc, 而命令处理器跑在执行器任务里(cmd.h),那里不该有堆分配; · 请求形状越窄,越界输入的面就越小:嵌套、数组、转义、null、浮点**一律当解析错**, 上位机本来就不该发这些。 ---- 刻意不支持的东西 ---------------------------------------------------- · **字符串转义**(`\"`、`\n`、`\u....`):命令里的字符串只有命令名和键名这类 ASCII 词, 遇到反斜杠直接判解析错,而不是「尽量理解」—— 半懂不懂地解出来比拒绝危险。 · **浮点**:电压用整数 mV、时长用整数 ms。`3.9` 是解析错,不会被截成 3。 · **重复的键**:`{"mv":3900,"mv":4300}` 是解析错。按「后者覆盖」理解的实现不止一种, 而这正是有人拿来绕过校验的那种写法。 */ #ifndef CMD_JSON_H #define CMD_JSON_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define CMD_JSON_MAX_FIELDS 12 #define CMD_JSON_KEY_MAX 16 /* 含结尾 0 */ #define CMD_JSON_STR_MAX 65 /* 含结尾 0。64 = WPA2 口令上限 63 与 broker 地址(A14) */ typedef enum { CJ_INT = 1, CJ_STR, CJ_BOOL, } cj_type_t; typedef struct { char key[CMD_JSON_KEY_MAX]; cj_type_t type; int64_t i; /* CJ_INT / CJ_BOOL(0/1) */ char s[CMD_JSON_STR_MAX]; /* CJ_STR */ } cj_field_t; typedef struct { cj_field_t f[CMD_JSON_MAX_FIELDS]; int n; } cj_obj_t; /* 解析一行。`line` 可以带结尾的 `\r` / `\n`(当空白吞掉)。成功返回 true。 整数只收 18 位以内(绝对值 < 10^18),超了是解析错 —— 不做回绕。 */ bool cj_parse(const char *line, cj_obj_t *o); /* 三个取值器的返回值:1 = 有且类型对,0 = 没这个键,-1 = 有但类型不对。 「类型不对」与「没有」要分开:`"mv":"3900"` 不是「没给电压」,是上位机的 bug。 */ int cj_get_int(const cj_obj_t *o, const char *key, int64_t *v); int cj_get_str(const cj_obj_t *o, const char *key, const char **s); int cj_get_bool(const cj_obj_t *o, const char *key, bool *v); /* ---- 写入器:往定长缓冲里拼,写满了只置标志、不越界 ---------------------- */ typedef struct { char *buf; size_t cap; size_t pos; bool overflow; /* 某一次没写下 ⇒ 整行作废(cmd.c 会换成一条 overflow 错误) */ } cj_writer_t; void cj_w_init(cj_writer_t *w, char *buf, size_t cap); #if defined(__GNUC__) __attribute__((format(printf, 2, 3))) #endif void cj_w(cj_writer_t *w, const char *fmt, ...); #ifdef __cplusplus } #endif #endif /* CMD_JSON_H */