Files
ESPC3-wireless/components/system/source/shell/sh_vset.c

1531 lines
48 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file sh_vset.h.c
* @author LokLiang
* @brief sh_vset.h 模块源码
* @version 0.1
* @date 2023-09-22
*
* @copyright Copyright (c) 2023
*
*/
#include "sh_vset.h"
#include <string.h>
#undef CONFIG_SYS_LOG_LEVEL
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF
#define SYS_LOG_DOMAIN "VSET"
#define CONS_ABORT()
#include "sys_log.h"
#define __ARR_SIZE(ARR) (sizeof(ARR) / sizeof((ARR)[0]))
#define _TYPE_FLAG_UNSIGNED (1 << 0)
#define _TYPE_FLAG_INTEGER (1 << 1)
#define _TYPE_FLAG_FLOAT (1 << 2)
#define _TYPE_FLAG_STRING (1 << 3)
#define _PARAM_CB() \
do \
{ \
if (cb) \
cb(s_sh_hdl, &value); \
} while (0)
static sh_t *s_sh_hdl;
static vset_global_cb s_global_cb;
static bool _is_in_option(const char *input, const sh_vset_param_t *p, unsigned size);
static const sh_vset_param_t *_find_option(const char *input, const sh_vset_param_t *p, unsigned size);
static bool _do_completing_option(sh_t *sh_hdl, int argc, const char *argv[], const sh_vset_param_t *p, unsigned size);
static bool _do_completing_enum(const char *option, const sh_vset_param_t *p, unsigned size);
static int _set_dest_unsigned(void *dest, __type_attr_t attr, vset_cb cb, unsigned int value, unsigned int low, unsigned int high);
static int _set_dest_integer(void *dest, __type_attr_t attr, vset_cb cb, signed int value, signed int low, signed int high);
static int _set_dest_float(void *dest, __type_attr_t attr, vset_cb cb, float value, float low, float high);
static int _set_dest_string(void *dest, __type_attr_t attr, vset_cb cb, const char *str, unsigned bufsize);
static int _set_dest_enum(void *dest, __type_attr_t attr, vset_cb cb, const char *value, int type_flag);
static unsigned int _get_unsigned(void *dest, __type_attr_t attr);
static signed int _get_integer(void *dest, __type_attr_t attr);
static float _get_float(void *dest, __type_attr_t attr);
static char *_get_string(void *dest, __type_attr_t attr);
static int _enum_format(char *enum_buf, int buf_len, const char *enum_str);
static int _get_enum_by_key(const char *key, char **match_value, char *enum_buf);
static int _get_enum_by_param(void *dest, __type_attr_t attr, char **match_key, char **match_value, char *enum_buf, bool print_list, bool print_cp);
/**
* @brief s_attr_to_type_tbl
* 由变量的具体类型转换到可用于参数解析的范围
*/
static int const s_attr_to_type_tbl[] = {
[__TYPE_ATTR_CHR] = _PARSE_TYPE_INTEGER,
[__TYPE_ATTR_BOOL] = _PARSE_TYPE_INTEGER,
[__TYPE_ATTR_U8] = _PARSE_TYPE_UNSIGNED,
[__TYPE_ATTR_S8] = _PARSE_TYPE_INTEGER,
[__TYPE_ATTR_U16] = _PARSE_TYPE_UNSIGNED,
[__TYPE_ATTR_S16] = _PARSE_TYPE_INTEGER,
[__TYPE_ATTR_U32] = _PARSE_TYPE_UNSIGNED,
[__TYPE_ATTR_S32] = _PARSE_TYPE_INTEGER,
[__TYPE_ATTR_U64] = _PARSE_TYPE_UNSIGNED,
[__TYPE_ATTR_S64] = _PARSE_TYPE_INTEGER,
[__TYPE_ATTR_FLOAT] = _PARSE_TYPE_FLOAT,
[__TYPE_ATTR_DOUBLE] = _PARSE_TYPE_FLOAT,
[__TYPE_ATTR_STR] = _PARSE_TYPE_STRING,
};
/**
* @brief 初始化设置
*
* @param sh_hdl 设置 shell 的句柄
* @param global_cb 当任意一个值被修改时回调此函数,可设为 NULL
*/
void vset_init(sh_t *sh_hdl, vset_global_cb global_cb)
{
s_sh_hdl = sh_hdl;
s_global_cb = global_cb;
}
/**
* @brief 直接执行一次 vset_init() 所定义的回调函数
*
*/
void vset_force_cb(void)
{
vset_global_cb global_cb = s_global_cb;
if (global_cb)
{
global_cb(s_sh_hdl);
}
}
/**
* @brief 一次性根据选项结构 p 解析 argv, 自动选择对应的设置函数进行对变量的设置。
* 这将导致输入的参数 argc 和 argv 被更新。
*
* @details
* 这将一将性解析全部符合 p 内部符合的参数,并执行对应的处理函数,成功设置的 argc 和 argv 会被删除。
* 注意如果有任何处理失败的函数不会立即返回,已成功设置的值也不会被恢复,直到全部 argv 被解析过一次后返回。
*
* @param sh_hdl 原参数
* @param argc[i/o] 指向原参数,可能被更新
* @param argv[i/o] 原参数指针值,可能被更新
* @param p 选项描述结构体
* @param size 选项描述结构体的长度
* @retval 0 所有存在于 sh_vset_param_t::option 的选项被正确设置
* @retval != 0 首个设置错误的 sh_vset_param_t::option
*/
int vset_option_set(sh_t *sh_hdl, int *argc, const char *argv[], const sh_vset_param_t *p, unsigned size)
{
int argc_old = *argc;
const char *err_option = NULL;
/* 遍历输入参数 argv 在 p 中找到匹配的 p[i].option 对应的 p[i].set_func 并执行 */
for (int i = 0; i < argc_old; i++)
{
for (int j = 0; j < size / sizeof(*p); j++)
{
const sh_vset_param_t *pset = &p[j];
if (strcmp(argv[i], pset->option) == 0) // 匹配到选项
{
if (pset->set_func) // 该选有对应的设置函数
{
bool flag = false;
argv[i] = NULL;
const char *str[] = {
"",
"",
};
if (i + 1 < argc_old)
{
++i;
str[0] = argv[i];
argv[i] = NULL;
if (strcmp("?", str[0]) == 0)
{
flag = true;
sh_echo(s_sh_hdl, "选项: %s\r\n", pset->option);
if (pset->help)
{
sh_echo(s_sh_hdl, "说明: %s\r\n", pset->help);
}
}
}
int ret = pset->set_func(str);
if (flag)
{
sh_echo(s_sh_hdl, "========\r\n");
}
if (ret != 0)
{
if (err_option == NULL)
{
err_option = pset->option;
}
sh_echo(s_sh_hdl, "( 选项 %s )\r\n", pset->option);
sh_echo(s_sh_hdl, "========\r\n");
}
}
break;
}
}
}
/* 更新已被处理的输入参数 argc 和 argv */
int argc_new = 0;
for (int i = 0; i < argc_old; i++)
{
if (argv[i] && *argv[i] != '\0')
{
argv[argc_new++] = argv[i]; // 更新 argv
}
}
*argc = argc_new; // 更新 argc
while (argc_new < argc_old)
{
argv[argc_new++] = NULL;
}
return (int)err_option;
}
/**
* @brief 为 cp_fn 执行与 set_func 对应的参数补全动作
*
* @param sh_hdl 原参数
* @param argc 原参数
* @param argv 原参数指针值,可能被更新
* @param flag 原参数
* @param p 选项描述结构体
* @param size 选项描述结构体的长度
* @retval true 有补全动作, cp_fn 函数不需要进行任何动作
* @retval false 未补全动作, cp_fn 函数可自行执行补全
*/
bool vset_option_cp(sh_t *sh_hdl, int argc, const char *argv[], bool flag, const sh_vset_param_t *p, unsigned size)
{
if (flag == false) // 一个参数正在输入
{
if (argc > 0 && _is_in_option(argv[argc - 1], p, size)) // 前正在输入的参数有对应存在的候选选项
{
_do_completing_option(sh_hdl, argc, argv, p, size); // 执行对选项的补全
return true;
}
else if (argc > 1)
{
return _do_completing_enum(argv[argc - 2], p, size); // 执行对 SET_ENUM 中的 ENUM_STR 的补全
}
else
{
return false;
}
}
else // 当前参数未有任何输入
{
const sh_vset_param_t *pset;
if (argc > 0 && (pset = _find_option(argv[argc - 1], p, size)) != NULL) // 找到上一个输入的参数的对应选项
{
vset_cp_enum(1, false, pset->enum_str);
return true;
}
else
{
_do_completing_option(sh_hdl, argc, argv, p, size); // 执行对选项的补全
return true;
}
}
}
/**
* @brief 对应 SET_VAR 所设置的变量的可用自动补全函数,默认补全一个 '?' 号
*
* @param sh_hdl 原参数
* @param argc 原参数
* @param argv 原参数指针值,可能被更新
* @param flag 原参数
*/
void vset_cp_hint(sh_t *sh_hdl, int argc, const char *argv[], bool flag)
{
if (argc + flag == 1)
{
sh_completion_resource(sh_hdl, NULL, "? ", NULL);
}
}
int vset_unsigned(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], unsigned int low, unsigned int high)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_UNSIGNED, "待设置的参数类型不是正整数");
if (s_sh_hdl == NULL)
{
return -1;
}
if (attr >= __ARR_SIZE(s_attr_to_type_tbl))
{
sh_echo(s_sh_hdl, "attr 的值不在 __type_attr_t 所定义的范围\r\n");
return -1;
}
if (s_attr_to_type_tbl[attr] != _PARSE_TYPE_UNSIGNED)
{
sh_echo(s_sh_hdl, "待设置的参数类型不是正整数\r\n");
return -1;
}
if (argv[0] == NULL || *argv[0] == '\0')
{
sh_echo(s_sh_hdl, "缺少参数\r\n");
return -1;
}
if (strcmp(argv[0], "?") == 0)
{
if (high < low)
{
low ^= high;
high ^= low;
low ^= high;
}
sh_echo(s_sh_hdl, "有效范围: %d .. %d\r\n", low, high);
sh_echo(s_sh_hdl, "当前值: %u\r\n", _get_unsigned(dest, attr));
return 0;
}
sh_parse_t pv = sh_parse_value(argv[0]);
switch (pv.type)
{
case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型
return _set_dest_unsigned(dest, attr, cb, pv.value.val_unsigned, low, high);
default:
sh_echo(s_sh_hdl, "参数 '%s' 格式错误, 请输入正整数\r\n", argv[0]);
return -1;
}
}
int vset_integer(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], signed int low, signed int high)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_INTEGER, "待设置的参数类型不是带符号整数");
if (s_sh_hdl == NULL)
{
return -1;
}
if (attr >= __ARR_SIZE(s_attr_to_type_tbl))
{
sh_echo(s_sh_hdl, "attr 的值不在 __type_attr_t 所定义的范围\r\n");
return -1;
}
if (s_attr_to_type_tbl[attr] != _PARSE_TYPE_INTEGER)
{
sh_echo(s_sh_hdl, "待设置的参数类型不是带符号整数\r\n");
return -1;
}
if (argv[0] == NULL || *argv[0] == '\0')
{
sh_echo(s_sh_hdl, "缺少参数\r\n");
return -1;
}
if (strcmp(argv[0], "?") == 0)
{
if (high < low)
{
low ^= high;
high ^= low;
low ^= high;
}
sh_echo(s_sh_hdl, "有效范围: %d .. %d\r\n", low, high);
sh_echo(s_sh_hdl, "当前值: %d\r\n", _get_integer(dest, attr));
return 0;
}
sh_parse_t pv = sh_parse_value(argv[0]);
switch (pv.type)
{
case _PARSE_TYPE_INTEGER: // 解析出的参数格式是带符号整型
return _set_dest_integer(dest, attr, cb, pv.value.val_integer, low, high);
case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型
if (pv.value.val_unsigned > (1u << (sizeof(pv.value.val_unsigned) * 8 - 1)) - 1)
{
sh_echo(s_sh_hdl, "参数 %u 溢出\r\n", pv.value.val_unsigned);
return -1;
}
return _set_dest_integer(dest, attr, cb, pv.value.val_unsigned, low, high);
default:
sh_echo(s_sh_hdl, "参数 '%s' 格式错误, 请输入整数\r\n", argv[0]);
return -1;
}
}
int vset_float(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], float low, float high)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_FLOAT, "待设置的参数类型不是浮点数");
if (s_sh_hdl == NULL)
{
return -1;
}
if (attr >= __ARR_SIZE(s_attr_to_type_tbl))
{
sh_echo(s_sh_hdl, "attr 的值不在 __type_attr_t 所定义的范围\r\n");
return -1;
}
if (s_attr_to_type_tbl[attr] != _PARSE_TYPE_FLOAT)
{
sh_echo(s_sh_hdl, "待设置的参数类型不是浮点数\r\n");
return -1;
}
if (argv[0] == NULL || *argv[0] == '\0')
{
sh_echo(s_sh_hdl, "缺少参数\r\n");
return -1;
}
if (strcmp(argv[0], "?") == 0)
{
if (high < low)
{
float tmp = low;
low = high;
high = tmp;
}
sh_echo(s_sh_hdl, "有效范围: %f .. %f\r\n", low, high);
sh_echo(s_sh_hdl, "当前值: %f\r\n", _get_float(dest, attr));
return 0;
}
sh_parse_t pv = sh_parse_value(argv[0]);
switch (pv.type)
{
case _PARSE_TYPE_INTEGER: // 解析出的参数格式是带符号整型
return _set_dest_float(dest, attr, cb, pv.value.val_integer, low, high);
case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型
return _set_dest_float(dest, attr, cb, pv.value.val_unsigned, low, high);
case _PARSE_TYPE_FLOAT: // 解析出的参数格式是浮点数
return _set_dest_float(dest, attr, cb, pv.value.val_float, low, high);
default:
sh_echo(s_sh_hdl, "参数 '%s' 格式错误, 请输入数值\r\n", argv[0]);
return -1;
}
}
int vset_str(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], unsigned bufsize)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_STRING, "待设置的参数类型不是字符串");
if (s_sh_hdl == NULL)
{
return -1;
}
if (attr >= __ARR_SIZE(s_attr_to_type_tbl))
{
sh_echo(s_sh_hdl, "attr 的值不在 __type_attr_t 所定义的范围\r\n");
return -1;
}
if (s_attr_to_type_tbl[attr] != _PARSE_TYPE_STRING)
{
sh_echo(s_sh_hdl, "待设置的参数类型不是字符串\r\n");
return -1;
}
if (argv[0] == NULL || *argv[0] == '\0')
{
sh_echo(s_sh_hdl, "缺少参数\r\n");
return -1;
}
if (strcmp(argv[0], "?") == 0)
{
sh_echo(s_sh_hdl, "最大长度: %d\r\n", bufsize - 1);
sh_echo(s_sh_hdl, "当前值: %s\r\n", _get_string(dest, attr));
return 0;
}
if (argv[1] && *argv[1] != '\0')
{
sh_echo(s_sh_hdl, "参数中有空格。如果目标设备名中包含空格,请使用引号表示\r\n");
return -1;
}
return _set_dest_string(dest, attr, cb, argv[0], bufsize);
}
int vset_enum(void *dest, __type_attr_t attr, vset_cb cb, const char *argv[], const char *enum_str)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
if (s_sh_hdl == NULL)
{
return -1;
}
if (attr >= __ARR_SIZE(s_attr_to_type_tbl))
{
sh_echo(s_sh_hdl, "attr 的值不在 __type_attr_t 所定义的范围\r\n");
return -1;
}
if (argv[0] == NULL || *argv[0] == '\0')
{
sh_echo(s_sh_hdl, "缺少参数\r\n");
return -1;
}
/* 格式化 enum_str 到 enum_buf */
char enum_buf[CONFIG_SH_MAX_LINE_LEN];
int enum_len = _enum_format(enum_buf, __ARR_SIZE(enum_buf), enum_str);
if (enum_len < 0)
{
return -1;
}
char *match_key = NULL;
char *match_value = NULL;
int type_flag = 0;
if (strcmp(argv[0], "?") == 0)
{
sh_echo(s_sh_hdl, "可选值:\r\n");
_get_enum_by_param(dest, attr, &match_key, &match_value, enum_buf, true, false);
if (match_value)
{
sh_echo(s_sh_hdl, "当前值:\t%s\r\n", match_key);
}
return 0;
}
type_flag = _get_enum_by_key(argv[0], &match_value, enum_buf);
if (type_flag == 0)
{
sh_echo(s_sh_hdl, "选项为空\r\n");
return -1;
}
if (match_value == NULL)
{
sh_echo(s_sh_hdl, "'%s': 非法的取值\r\n", argv[0]);
return -1;
}
return _set_dest_enum(dest, attr, cb, match_value, type_flag);
}
void vset_cp_enum(int argc, bool flag, const char *enum_str)
{
if (argc + flag == 1)
{
if (enum_str != NULL)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
if (s_sh_hdl == NULL)
{
return;
}
/* 格式化 enum_str 到 enum_buf */
char enum_buf[CONFIG_SH_MAX_LINE_LEN];
int enum_len = _enum_format(enum_buf, __ARR_SIZE(enum_buf), enum_str);
if (enum_len < 0)
{
return;
}
/* 分割字符串 str_buf 并穷举匹配到的值,输出 match_key, match_value, type_flag */
char *match_key = NULL;
char *match_value = NULL;
_get_enum_by_param(enum_buf, __GENERIC_ATTR(enum_buf), &match_key, &match_value, enum_buf, false, true);
}
else
{
sh_completion_resource(s_sh_hdl, NULL, "? ", NULL);
}
}
}
/**
* @brief 内部函数,由 vset_option_cp() 专用,测试前正在输入的参数有对应存在的候选选项
*
* @param input 当前正在输入的参数
* @param p 选项描述结构体
* @param size 选项描述结构体的长度
* @retval true 当前正在输入的参数存在于候选选项中
* @retval false 当前正在输入的参数不在候选选项中
*/
static bool _is_in_option(const char *input, const sh_vset_param_t *p, unsigned size)
{
int len = strlen(input);
for (int i = 0; i < size / sizeof(*p); i++)
{
if (strncmp(input, p[i].option, len) == 0)
{
return true;
}
}
return false;
}
/**
* @brief 内部函数,由 vset_option_cp() 专用,查找输入的参数的对应选项
* @brief
*
* @param input
* @param p
* @param size
* @return const sh_vset_param_t*
*/
static const sh_vset_param_t *_find_option(const char *input, const sh_vset_param_t *p, unsigned size)
{
for (int i = 0; i < size / sizeof(*p); i++)
{
if (strcmp(input, p[i].option) == 0)
{
return &p[i];
}
}
return NULL;
}
/**
* @brief 内部函数,由 vset_option_cp() 专用,执行对选项的补全
*
* @param sh_hdl 原参数
* @param argc 原参数
* @param argv 原参数指针值,可能被更新
* @param p 选项描述结构体
* @param size 选项描述结构体的长度
* @retval true
*/
static bool _do_completing_option(sh_t *sh_hdl, int argc, const char *argv[], const sh_vset_param_t *p, unsigned size)
{
bool ret = false;
for (int i = 0; i < size / sizeof(*p); i++) // 遍历 p[i].option 分析
{
/* 遍历已输入的参数确认当前的 p[i].option 未曾出现过 */
bool repeat_flag = false;
for (int j = 0; j < argc - 1; j++)
{
if (strcmp(p[i].option, argv[j]) == 0)
{
repeat_flag = true;
break;
}
}
if (repeat_flag == false)
{
/* 执行 sh_completion_resource() */
char option_cpy[0x100];
int len = 0;
for (len = 0; len < sizeof(option_cpy) - 2; len++)
{
option_cpy[len] = p[i].option[len];
if (option_cpy[len] == '\0')
{
break;
}
}
option_cpy[len++] = ' ';
option_cpy[len] = '\0';
sh_completion_resource(sh_hdl, NULL, option_cpy, p[i].help);
ret = true;
}
}
return ret;
}
/**
* @brief 内部函数,由 vset_option_cp() 专用,执行对 SET_ENUM 中的 ENUM_STR 的补全
*
* @param option 已知的选项
* @param p 选项描述结构体
* @param size 选项描述结构体的长度
*/
static bool _do_completing_enum(const char *option, const sh_vset_param_t *p, unsigned size)
{
/* 根据上一个参数,遍历 p 找到对应的成员 */
if (option)
{
for (int i = 0; i < size / sizeof(*p); i++)
{
if (strcmp(option, p[i].option) == 0)
{
if (p[i].set_func)
{
vset_cp_enum(1, false, p[i].enum_str);
}
return true;
}
}
}
return false;
}
static int _set_dest_unsigned(void *dest, __type_attr_t attr, vset_cb cb, unsigned int value, unsigned int low, unsigned int high)
{
vset_global_cb global_cb = NULL;
int ret = -1;
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_UNSIGNED, "待设置的参数类型不是正整数");
if (value < low)
{
sh_echo(s_sh_hdl, "设置失败: %d < %d(min)\r\n", value, low);
return -1;
}
if (value > high)
{
sh_echo(s_sh_hdl, "设置失败: %d > %d(max)\r\n", value, high);
return -1;
}
switch (attr)
{
case __TYPE_ATTR_U8:
{
SYS_ASSERT((low & (~0u << 8)) == 0 && (high & (~0u << 8)) == 0, "范围值溢出");
if ((low & (~0u << 8)) || (high & (~0u << 8)))
{
sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n");
return -1;
}
if (*((uint8_t *)dest) != value)
{
_PARAM_CB();
*((uint8_t *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_U16:
{
SYS_ASSERT((low & (~0u << 16)) == 0 && (high & (~0u << 16)) == 0, "范围值溢出");
if ((low & (~0u << 16)) || (high & (~0u << 16)))
{
sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n");
return -1;
}
if (*((uint16_t *)dest) != value)
{
_PARAM_CB();
*((uint16_t *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_U32:
{
if (*((uint32_t *)dest) != value)
{
_PARAM_CB();
*((uint32_t *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_U64:
{
if (*((uint64_t *)dest) != value)
{
_PARAM_CB();
*((uint64_t *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
default:
return -1;
}
if (global_cb)
{
global_cb(s_sh_hdl);
}
return ret;
}
static int _set_dest_integer(void *dest, __type_attr_t attr, vset_cb cb, signed int value, signed int low, signed int high)
{
#define _MAX_S8 127
#define _MIN_S8 -128
#define _MAX_S16 32767
#define _MIN_S16 -32768
vset_global_cb global_cb = NULL;
int ret = -1;
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_INTEGER, "待设置的参数类型不是带符号整数");
if (value < low)
{
sh_echo(s_sh_hdl, "设置失败: %d < %d(min)\r\n", value, low);
return -1;
}
if (value > high)
{
sh_echo(s_sh_hdl, "设置失败: %d > %d(max)\r\n", value, high);
return -1;
}
switch (attr)
{
case __TYPE_ATTR_CHR:
{
char data = value;
if (data != value)
{
sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n");
return -1;
}
if (*((char *)dest) != data)
{
_PARAM_CB();
*((char *)dest) = data;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_BOOL:
{
bool data = value;
if (data != value)
{
sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n");
return -1;
}
if (*((bool *)dest) != data)
{
_PARAM_CB();
*((bool *)dest) = data;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_S8:
{
SYS_ASSERT(low >= _MIN_S8 && low <= _MAX_S8, "范围值溢出");
SYS_ASSERT(high >= _MIN_S8 && high <= _MAX_S8, "范围值溢出");
if (low < _MIN_S8 || low > _MAX_S8 ||
high < _MIN_S8 || high > _MAX_S8)
{
sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n");
return -1;
}
if (*((int8_t *)dest) != value)
{
_PARAM_CB();
*((int8_t *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_S16:
{
SYS_ASSERT(low >= _MIN_S16 && low <= _MAX_S16, "范围值溢出");
SYS_ASSERT(high >= _MIN_S16 && high <= _MAX_S16, "范围值溢出");
if (low < _MIN_S16 || low > _MAX_S16 ||
high < _MIN_S16 || high > _MAX_S16)
{
sh_echo(s_sh_hdl, "设置失败: 设置值溢出\r\n");
return -1;
}
if (*((int16_t *)dest) != value)
{
_PARAM_CB();
*((int16_t *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_S32:
{
if (*((int32_t *)dest) != value)
{
_PARAM_CB();
*((int32_t *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_S64:
{
if (*((int64_t *)dest) != value)
{
_PARAM_CB();
*((int64_t *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
default:
return -1;
}
if (global_cb)
{
global_cb(s_sh_hdl);
}
return ret;
}
static int _set_dest_float(void *dest, __type_attr_t attr, vset_cb cb, float value, float low, float high)
{
vset_global_cb global_cb = NULL;
int ret = -1;
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_FLOAT, "待设置的参数类型不是浮点数");
if (value < low)
{
sh_echo(s_sh_hdl, "设置失败: %f < %f(min)\r\n", value, low);
return -1;
}
if (value > high)
{
sh_echo(s_sh_hdl, "设置失败: %f > %f(max)\r\n", value, high);
return -1;
}
switch (attr)
{
case __TYPE_ATTR_FLOAT:
{
if (*((float *)dest) != value)
{
_PARAM_CB();
*((float *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
case __TYPE_ATTR_DOUBLE:
{
if (*((double *)dest) != value)
{
_PARAM_CB();
*((double *)dest) = value;
global_cb = s_global_cb;
}
ret = 0;
break;
}
default:
return -1;
}
if (global_cb)
{
global_cb(s_sh_hdl);
}
return ret;
}
static int _set_dest_string(void *dest, __type_attr_t attr, vset_cb cb, const char *str, unsigned bufsize)
{
int str_size = strlen(str) + 1;
vset_global_cb global_cb = NULL;
int ret = -1;
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_STRING, "待设置的参数类型不是字符串");
if (str_size > bufsize)
{
sh_echo(s_sh_hdl, "注意: '%s' 大于最大长度 %d\r\n", str, bufsize - 1);
str_size = bufsize;
}
switch (attr)
{
case __TYPE_ATTR_STR:
if (memcmp(dest, str, str_size))
{
if (cb)
{
char value[str_size + 1];
memset(value, 0, str_size + 1);
memcpy(value, str, str_size);
_PARAM_CB();
}
memset(dest, 0, bufsize);
memcpy(dest, str, str_size);
global_cb = s_global_cb;
}
ret = 0;
break;
default:
return -1;
}
if (global_cb)
{
global_cb(s_sh_hdl);
}
return ret;
}
static int _set_dest_enum(void *dest, __type_attr_t attr, vset_cb cb, const char *value, int type_flag)
{
sh_parse_t pv = sh_parse_value(value);
switch (s_attr_to_type_tbl[attr])
{
case _PARSE_TYPE_UNSIGNED: // 待设置的参数格式是无符号整型
{
if (type_flag & (_TYPE_FLAG_INTEGER | _TYPE_FLAG_FLOAT | _TYPE_FLAG_STRING))
{
sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n");
return -1;
}
switch (pv.type)
{
case _PARSE_TYPE_UNSIGNED: // 当前匹配的参数格式是无符号整型
return _set_dest_unsigned(dest, attr, cb, pv.value.val_unsigned, pv.value.val_unsigned, pv.value.val_unsigned);
default:
return -1;
}
}
break;
case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型
{
if (type_flag & (_TYPE_FLAG_FLOAT | _TYPE_FLAG_STRING))
{
sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n");
return -1;
}
switch (pv.type)
{
case _PARSE_TYPE_INTEGER: // 当前匹配的参数格式是带符号整型
return _set_dest_integer(dest, attr, cb, pv.value.val_integer, pv.value.val_integer, pv.value.val_integer);
case _PARSE_TYPE_UNSIGNED: // 当前匹配的参数格式是无符号整型
return _set_dest_integer(dest, attr, cb, pv.value.val_unsigned, pv.value.val_unsigned, pv.value.val_unsigned);
default:
return -1;
}
}
break;
case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数
{
if (type_flag & (_TYPE_FLAG_STRING))
{
sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n");
return -1;
}
switch (pv.type)
{
case _PARSE_TYPE_INTEGER: // 当前匹配的参数格式是带符号整型
return _set_dest_float(dest, attr, cb, pv.value.val_integer, pv.value.val_integer, pv.value.val_integer);
case _PARSE_TYPE_UNSIGNED: // 当前匹配的参数格式是无符号整型
return _set_dest_float(dest, attr, cb, pv.value.val_unsigned, pv.value.val_unsigned, pv.value.val_unsigned);
case _PARSE_TYPE_FLOAT: // 当前匹配的参数格式是浮点数
return _set_dest_float(dest, attr, cb, pv.value.val_float, pv.value.val_float, pv.value.val_float);
default:
return -1;
}
}
break;
case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串
{
return _set_dest_string(dest, attr, cb, value, strlen(value) + 1);
}
break;
default:
sh_echo(s_sh_hdl, "attr 的值不在 __type_attr_t 所定义的范围\r\n");
return -1;
}
return -1;
}
static signed int _get_integer(void *dest, __type_attr_t attr)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_INTEGER, "待设置的参数类型不是带符号整数");
switch (attr)
{
case __TYPE_ATTR_CHR:
return *((char *)dest);
case __TYPE_ATTR_BOOL:
return *((bool *)dest);
case __TYPE_ATTR_S8:
return *((int8_t *)dest);
case __TYPE_ATTR_S16:
return *((int16_t *)dest);
case __TYPE_ATTR_S32:
return *((int32_t *)dest);
case __TYPE_ATTR_S64:
return *((int64_t *)dest);
default:
return -1;
}
}
static float _get_float(void *dest, __type_attr_t attr)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_FLOAT, "待设置的参数类型不是浮点数");
switch (attr)
{
case __TYPE_ATTR_FLOAT:
return *((float *)dest);
case __TYPE_ATTR_DOUBLE:
return *((double *)dest);
default:
return -1;
}
}
static char *_get_string(void *dest, __type_attr_t attr)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_STRING, "待设置的参数类型不是字符串");
return dest;
}
static unsigned int _get_unsigned(void *dest, __type_attr_t attr)
{
SYS_ASSERT(s_sh_hdl, "未使用 vset_init() 初始化");
SYS_ASSERT(attr < __ARR_SIZE(s_attr_to_type_tbl), "attr 的值不在 __type_attr_t 所定义的范围");
SYS_ASSERT(s_attr_to_type_tbl[attr] == _PARSE_TYPE_UNSIGNED, "待设置的参数类型不是正整数");
switch (attr)
{
case __TYPE_ATTR_U8:
return *((uint8_t *)dest);
case __TYPE_ATTR_U16:
return *((uint16_t *)dest);
case __TYPE_ATTR_U32:
return *((uint32_t *)dest);
case __TYPE_ATTR_U64:
return *((uint64_t *)dest);
default:
return -1;
}
}
/**
* @brief 格式化原始的 enum_str 并输出到 enum_buf
*
* @param enum_buf[out] 复制 enum_str 并格式化的需要的缓存
* @param buf_len enum_buf 的长度
* @param enum_str 原始的选项格式
* @retval < 0 表示 enum_str 的格式错误(键值或数值不能包含空格)
* @retval >= 0 表示输出的 enum_buf 中的字符数(不包含 '\0'
*/
static int _enum_format(char *enum_buf, int buf_len, const char *enum_str)
{
/* 复制 enum_str 到 str_buf 并移除空格 */
if (strlen(enum_str) >= buf_len)
{
sh_echo(s_sh_hdl, "注意: str_buff 长度不足\r\n");
}
strncpy(enum_buf, enum_str, buf_len - 1);
enum_buf[buf_len - 1] = '\0';
/* 去除可能的多余空格 */
buf_len = strlen(enum_buf);
int cid = 0;
int flag1 = 0;
int flag2 = 1;
for (int i = 0; i < buf_len; i++)
{
if (enum_buf[i] > ' ')
{
enum_buf[cid] = enum_str[i];
cid++;
if (enum_buf[i] != ',' && enum_buf[i] != '=')
{
flag1 += flag2;
flag1 += !flag1;
if (flag1 > 1)
{
break;
}
}
else
{
flag1 = 0;
}
flag2 = 0;
}
else
{
flag2 = 1;
}
}
if (flag1 > 1)
{
sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 键值或数值不能包含空格\r\n");
return -1;
}
enum_buf[cid] = '\0';
return cid; // 返回字符串长度(不包含结束符)
}
/**
* @brief 根据给出的 key 在 enum_buf 查找对应的 value
*
* @param key 给出的被搜索的 key
* @param match_value[out] 与 key 中的值完全匹配的 value 位置,该位置在 enum_buf 中,如果结果为 NULL 表示在 enum_buf 中不存在与 param 的值完全匹配的项
* @param enum_buf 通过 _enum_format() 格式化的缓存
* @retval int 按位表示在 enum_buf 中出现过的 value 的数据类型。
* _TYPE_FLAG_UNSIGNED 掩码表示在 enum_buf 中包含无符号整型
* _TYPE_FLAG_INTEGER 掩码表示在 enum_buf 中包含有符号整型
* _TYPE_FLAG_FLOAT 掩码表示在 enum_buf 中包含浮点数
* _TYPE_FLAG_STRING 掩码表示在 enum_buf 中包含字符串
*/
static int _get_enum_by_key(const char *key, char **match_value, char *enum_buf)
{
int type_flag = 0; // 在 match_value 中出现过的数据类型
/* 分割字符串 enum_buf 并穷举匹配到的值 */
*match_value = NULL;
char *save_token;
char *token = strtok_r(enum_buf, ",", &save_token);
while (token)
{
char *enum_value;
char *enum_key = strtok_r(token, "=", &enum_value);
if (enum_key == NULL || *enum_key == '\0' || *enum_value == '\0')
{
sh_echo(s_sh_hdl, "'%s' 的格式错误, 请使用如下格式表示: 'key1=value,key2=value,...'\r\n", token);
return 0;
}
sh_parse_t pv = sh_parse_value(enum_value);
switch (pv.type)
{
case _PARSE_TYPE_INTEGER: // 解析出的参数格式是带符号整型
type_flag |= _TYPE_FLAG_INTEGER;
break;
case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型
type_flag |= _TYPE_FLAG_UNSIGNED;
break;
case _PARSE_TYPE_FLOAT: // 解析出的参数格式是浮点数
type_flag |= _TYPE_FLAG_FLOAT;
break;
case _PARSE_TYPE_STRING: // 解析出的参数格式是字符串
type_flag |= _TYPE_FLAG_STRING;
break;
default:
break;
}
if (*match_value == NULL)
{
if (strcmp(enum_key, key) == 0)
{
*match_value = enum_value;
}
}
token = strtok_r(NULL, ",", &save_token);
}
return type_flag;
}
/**
* @brief 根据给出的 param 在 enum_buf 查找对应的 key 和 value
* 1. 分析 enum_buf 中存在的所有 value 项的数据类型并作为返回值
* 2. 根据 param 的值,在格式化数据中搜索与该值完全匹配的 key 和 value 并输出到 *match_key 和 *match_value
* 2. 可选打印 enum_buf 中的 key 和 value
*
* @param param 指向待设置的变量属性的结构体
* @param match_key[out] 与 param 中的值完全匹配的 key 位置,该位置在 enum_buf 中,如果结果为 NULL 表示在 enum_buf 中不存在与 param 的值完全匹配的项
* @param match_value[out] 与 param 中的值完全匹配的 value 位置,该位置在 enum_buf 中,如果结果为 NULL 表示在 enum_buf 中不存在与 param 的值完全匹配的项
* @param enum_buf 通过 _enum_format() 格式化的缓存
* @param print_list 是否打印所有 enum_buf 中的项目
* @param print_cp 是否执行自动补全
* @retval int 按位表示在 enum_buf 中出现过的 value 的数据类型。
* _TYPE_FLAG_UNSIGNED 掩码表示在 enum_buf 中包含无符号整型
* _TYPE_FLAG_INTEGER 掩码表示在 enum_buf 中包含有符号整型
* _TYPE_FLAG_FLOAT 掩码表示在 enum_buf 中包含浮点数
* _TYPE_FLAG_STRING 掩码表示在 enum_buf 中包含字符串
*/
static int _get_enum_by_param(void *dest, __type_attr_t attr, char **match_key, char **match_value, char *enum_buf, bool print_list, bool print_cp)
{
char param_value_str[0x100];
int type_flag = 0; // 在 match_value 中出现过的数据类型
if (attr >= __ARR_SIZE(s_attr_to_type_tbl))
{
sh_echo(s_sh_hdl, "attr 的值不在 __type_attr_t 所定义的范围\r\n");
return -1;
}
switch (s_attr_to_type_tbl[attr])
{
case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型
SYS_SNPRINT(param_value_str, sizeof(param_value_str), "%d", _get_integer(dest, attr));
break;
case _PARSE_TYPE_UNSIGNED: // 待设置的参数格式是无符号整型
SYS_SNPRINT(param_value_str, sizeof(param_value_str), "%u", _get_unsigned(dest, attr));
break;
case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数
SYS_SNPRINT(param_value_str, sizeof(param_value_str), "%f", _get_float(dest, attr));
break;
case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串
SYS_SNPRINT(param_value_str, sizeof(param_value_str), "%s", _get_string(dest, attr));
break;
default:
sh_echo(s_sh_hdl, "attr 的值不在 __type_attr_t 所定义的范围\r\n");
return -1;
}
param_value_str[__ARR_SIZE(param_value_str) - 1] = '\0';
/* 分割字符串 enum_buf 并穷举匹配到的值 */
*match_key = NULL;
*match_value = NULL;
char *save_token;
char *token = strtok_r(enum_buf, ",", &save_token);
while (token)
{
char *enum_value;
char *enum_key = strtok_r(token, "=", &enum_value);
char key_value_str[__ARR_SIZE(param_value_str)];
if (*enum_key == '\0' || *enum_value == '\0')
{
sh_echo(s_sh_hdl, "'%s' 的格式错误, 请使用如下格式表示: 'key1=value,key2=value,...'\r\n", enum_key);
return 0;
}
key_value_str[0] = '\0';
sh_parse_t pv = sh_parse_value(enum_value);
switch (pv.type)
{
case _PARSE_TYPE_UNSIGNED: // 解析出的参数格式是无符号整型
type_flag |= _TYPE_FLAG_UNSIGNED;
if (*match_key == NULL)
{
switch (s_attr_to_type_tbl[attr])
{
case _PARSE_TYPE_UNSIGNED: // 待设置的参数格式是无符号整型
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%u", pv.value.val_unsigned);
break;
case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%d", pv.value.val_unsigned);
break;
case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%f", (float)pv.value.val_unsigned);
break;
case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%s", enum_value);
break;
default:
break;
}
}
break;
case _PARSE_TYPE_INTEGER: // 解析出的参数格式是带符号整型
type_flag |= _TYPE_FLAG_INTEGER;
if (*match_key == NULL)
{
switch (s_attr_to_type_tbl[attr])
{
case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%d", pv.value.val_integer);
break;
case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%f", (float)pv.value.val_integer);
break;
case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%s", enum_value);
break;
default:
break;
}
}
break;
case _PARSE_TYPE_FLOAT: // 解析出的参数格式是浮点数
type_flag |= _TYPE_FLAG_FLOAT;
if (*match_key == NULL)
{
switch (s_attr_to_type_tbl[attr])
{
case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%f", (float)pv.value.val_float);
break;
case _PARSE_TYPE_STRING: // 待设置的参数格式是字符串
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%s", enum_value);
break;
default:
break;
}
}
break;
case _PARSE_TYPE_STRING: // 解析出的参数格式是字符串
type_flag |= _TYPE_FLAG_STRING;
if (*match_key == NULL)
SYS_SNPRINT(key_value_str, sizeof(key_value_str), "%s", enum_value);
break;
default:
break;
}
if (*match_key == NULL)
{
if (strcmp(key_value_str, param_value_str) == 0)
{
*match_key = enum_key;
*match_value = enum_value;
}
}
if (print_list)
{
sh_echo(s_sh_hdl, "\t%s (%s)\r\n", enum_key, enum_value);
}
if (print_cp)
{
char key_cpy[0x100];
int len = 0;
for (len = 0; len < sizeof(key_cpy) - 2; len++)
{
key_cpy[len] = enum_key[len];
if (key_cpy[len] == '\0')
{
break;
}
}
key_cpy[len++] = ' ';
key_cpy[len] = '\0';
sh_completion_resource(s_sh_hdl, NULL, key_cpy, NULL);
}
token = strtok_r(NULL, ",", &save_token);
}
switch (s_attr_to_type_tbl[attr])
{
case _PARSE_TYPE_UNSIGNED: // 待设置的参数格式是无符号整型
{
if (type_flag & (_TYPE_FLAG_INTEGER | _TYPE_FLAG_FLOAT | _TYPE_FLAG_STRING))
{
sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n");
return -1;
}
}
break;
case _PARSE_TYPE_INTEGER: // 待设置的参数格式是带符号整型
{
if (type_flag & (_TYPE_FLAG_FLOAT | _TYPE_FLAG_STRING))
{
sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n");
return -1;
}
}
break;
case _PARSE_TYPE_FLOAT: // 待设置的参数格式是浮点数
{
if (type_flag & (_TYPE_FLAG_STRING))
{
sh_echo(s_sh_hdl, "ENUM_STR 格式错误, 包含非法的 value 类型\r\n");
return -1;
}
}
break;
default:
break;
}
return type_flag;
}