Zephyr shell系统使用指南-动态命令

Creative Commons
本作品采用知识共享署名

本文说明如何向shell系统添加动态命令。

顾名思义动态命令即是可以动态变化的命令,Zephyr的shell系统提供一种机制,可以在运行时根据不同的情况提供给shell系统不同的子命令,已满足在不同场景下shell交互界面提供出不同命令。例如当网络没有连接时shell界面中只能看到connect的命令,连接成功后只能看到disconnect的命令。

使用方法

动态子命令由SHELL_DYNAMIC_CMD_CREATE生成

1
SHELL_DYNAMIC_CMD_CREATE(name, get)

  • name[in] 动态命令入口.

  • get[in] 一个typedef void (*shell_dynamic_get)(size_t idx, struct shell_static_entry *entry)类型的函数,根据idx返回不同的struct shell_static_entry类型的命令入口参数。

    struct shell_static_entrySHELL_CMD_ARG指定的静态命令格式一样,指定命令符号,帮助信息,子命令入口,命令函数,和参数个数

    1
    2
    3
    4
    struct shell_static_args {
    uint8_t mandatory; /*!< 必选参数个数. */
    uint8_t optional; /*!< 可选参数个数. */
    };
1
2
3
4
5
6
7
struct shell_static_entry {
const char *syntax; /*!< 命令符号字符串. */
const char *help; /*!< 帮助信息字符串. */
const struct shell_cmd_entry *subcmd; /*!< 子命令入口. */
shell_cmd_handler handler; /*!< 命令函数. */
struct shell_static_args args; /*!< 命令参数个数. */
};

从上面看到get函数返回的shell命令入口和静态命令对应的参数一模一样, 参考Zephyr shell系统使用指南-添加命令

示例

如下代码片段演示了如何添加一个动态子命令:

  1. 准备一个typedef void (*shell_dynamic_get)(size_t idx, struct shell_static_entry *entry)类型的动态命令获取函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    static void dynamic_cmd_get(size_t idx, struct shell_static_entry *entry)
    {
    if(idx<dynamic_cmd_num){
    entry->syntax = cmd_name_flag? change_name[idx]:dynamic_entrys[idx].syntax;
    entry->handler = dynamic_entrys[idx].handler;
    entry->subcmd = dynamic_entrys[idx].subcmd;
    entry->help = dynamic_entrys[idx].help;
    entry->args.mandatory = dynamic_entrys[idx].args.mandatory;
    entry->args.optional = dynamic_entrys[idx].args.optional;
    }else{
    entry->syntax = NULL;
    }
    }
  2. 生成为动态子命令dynamic_set

    1
    SHELL_DYNAMIC_CMD_CREATE(dynamic_set, dynamic_cmd_get);
  3. dynamic_set注册到根命令中

    1
    2
    SHELL_CMD_REGISTER(shell_dynamic, &dynamic_set,
    "Sample dynamic command usage.", NULL);

根命令shell_dynamic的子命令集为dynamic_set,子命令集中有哪些子命令是由dynamic_cmd_get的输出决定。shell系统在遍历根命令shell_dynamic的子命令时,传入参数idx从0开始每次加一的循环调用dynamic_cmd_get,直到输出的entry->syntax为空。在上面的示例代码中动态子命令的个数由dynamic_cmd_num决定,默认情况下就是dynamic_entrys[]中的命令入口数量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/* 动态命令数组 */
static struct shell_static_entry dynamic_entrys[]=
{
/* 改变动态命令总数 */
{
.syntax = "total",
.handler = cmd_dynamic_total,
.subcmd = NULL,
.help = "Set total cmd number, must more than 1.",
.args.mandatory = 0,
.args.optional = 0,
},

/* 使用默认子命令名 */
{
.syntax = "org_name",
.handler = cmd_dynamic_org_name,
.subcmd = NULL,
.help = "Change to org cmd name.",
.args.mandatory = 0,
.args.optional = 0,
},

/* 使用新的子命令名 */
{
.syntax = "new_name",
.handler = cmd_dynamic_new_name,
.subcmd = NULL,
.help = "Change to new cmd name.",
.args.mandatory = 0,
.args.optional = 0,
},

/* 动态子命令的子命令演示 */
{
.syntax = "subcmd",
.handler = NULL,
.subcmd = &shell_sample,
.help = "Show dynamic sub cmd.",
.args.mandatory = 0,
.args.optional = 0,
},
{
.syntax = "cmd1",
.handler = NULL,
.subcmd = NULL,
.help = "Show dynamic command cmd1.",
.args.mandatory = 0,
.args.optional = 0,
},
{
.syntax = "cmd2",
.handler = NULL,
.subcmd = NULL,
.help = "Show dynamic command cmd2.",
.args.mandatory = 0,
.args.optional = 0,
},
{
.syntax = NULL,
}
};

static uint32_t dynamic_cmd_num = sizeof(dynamic_entrys)/sizeof(struct shell_static_entry);

开机后在shell中输入shell_dynamic后按tab后可以看到所有的子命令

1
2
uart:~$ shell_dynamic
total org_name new_name subcmd cmd1 cmd2

可以在运行时改变dynamic_cmd_num的大小达到改变shell_dynamic的子命令数量,例如total子命令就可以达到这一目的,它对应的命令函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static int cmd_dynamic_total(const struct shell *sh, size_t argc, char **argv)
{
if(argc < 2){
shell_help(sh);
return SHELL_CMD_HELP_PRINTED;
}

uint32_t num = (uint32_t)atoi(argv[1]);
if(num<1 && num > sizeof(dynamic_entrys)/sizeof(struct shell_static_entry)){
shell_error(sh, "total set fail, must in 1~%d", sizeof(dynamic_entrys)/sizeof(struct shell_static_entry));
return -ENOEXEC;
}

/* 改变动态命令的数量 */
shell_print(sh, "set total cmd num %d", num);
dynamic_cmd_num = num;

return 0;
}

如果执行shell命令shell_dynamic total 1cmd_dynamic_total被调用将dynamic_cmd_num修改为3,此时在shell中输入shell_dynamic后按tab后就只能看到只剩3个子命令:

1
2
uart:~$ shell_dynamic
total org_name new_name

也可以在运行时通过改变cmd_name_flag变量的值,让动态子命令的syntax发生改变,达到动态改变动态子命令符号的目的。例如执行shell_dynamic new_namecmd_name_flag被修改为1,dynamic_cmd_get输出的syntax将使用change_name的内容

1
2
3
4
5
6
7
8
char * change_name[] = {
"total_new",
"org_name_new",
"new_name_new",
"cmd1_new",
"cmd2_new",
"cmd3_new",
};

此时在shell中输入shell_dynamic后按tab后,看到的就是新的子命令名称

1
2
uart:~$ shell_dynamic
total_new org_name_new new_name_new

当然也可以修改dynamic_entrys的内容达到添加和删除动态子命令的目的,或者是修改其中的子命令函数达到修改子命令功能的目的。

参考

https://docs.zephyrproject.org/latest/services/shell/index.html