LingYi 发表于 2018-10-27 13:24:51

分享一个ESP32命令行解析框架

该命令行解析框架,可以轻松实现,读写HEX类型的数据,读写数值型数据,读写IP,MAC,字符串,运行函数,浮点型数据暂时不支持,带符号的数据暂时不支持。
命令行解析框架,使用链表将所有注册的信息串联起来,然后每个节点node包含了,keyword,help,数据类型描述,命令执行的回调函数,和参数存储的回调函数。主要的操作是,已知函数变量地址,类型,和地址宽度,然后强制转换后对变量读写操作。
命令节点结构如下typedef                structfun_node
{
        structfun_node *pNext;
        const                char        *pHelp;
        const                char        *pName;
        uint8_t               paraNum;
        FUN_CMD_BKRunFun;
        uint8_t               EnableState;
}ST_FUN_NODE;

typedef                enum
{
        emCmdDataType_Dec                =1,
        emCmdDataType_Hex                =2,
        emCmdDataType_Str                =3,
        emCmdDataType_IP                =4,
        emCmdDataType_MAC                =5,
       
}EM_CMD_DATA_TYPE;


typedef                struct   cmd_para
{
        struct   cmd_para*pNext;       
        const        char*pName;
        void               *pDataAddr;       
        uint16_t        dataLen;
        EM_CMD_DATA_TYPEdataType;
        uint8_t       EnableState;
}ST_CMD_PARA;


typedef                struct        cmd_node
{
        const        char         *pKeyword;
        const        char   *pHelp;
        ST_CMD_PARA*pData;
        ST_FUN_NODE       *pFuntion;
        FUN_STOR_BK   StorageFun;
}ST_CMD_NODE;

参数注册配置实例如下:

void       cla_parametersConfig(void)
{
                cla_init();
                cla_regsister("uuid","uuid <only read>");
                cla_addParameter("uuid","uuid ",(void *)&uuid,emCmdDataType_Str,                sizeof(uuid));

                cla_regsister("wifi","wifi or wifi ssid=xxx password=xxx");
                cla_addParameter("wifi","ssid",(void *)ssid,emCmdDataType_Str,                        sizeof(ssid));
                cla_addParameter("wifi","password",(void *)password,emCmdDataType_Str,        sizeof(password));

                cla_regsister("hex","hex or hex hex8=xx hex16=xx hex32=xx");
                cla_addParameter("hex","hex8",(void *)&hex8,emCmdDataType_Hex,                        sizeof(data8));
                cla_addParameter("hex","hex16",(void *)&hex16,emCmdDataType_Hex,                sizeof(data16));
                cla_addParameter("hex","hex32",(void *)&hex32,emCmdDataType_Hex,                sizeof(data32));
               
                cla_regsister("data","data or data data8=xx data16=xx data32=xx");
                cla_addParameter("data","data8",(void *)&data8,emCmdDataType_Dec,                sizeof(data8));
                cla_addParameter("data","data16",(void *)&data16,emCmdDataType_Dec,                sizeof(data16));
                cla_addParameter("data","data32",(void *)&data32,emCmdDataType_Dec,                sizeof(data32));

                cla_regsister("ipconfig","ipconfig or ipconfig ipaddr=192.168.1.1");
                cla_addParameter("ipconfig","ipaddr",(void *)IPaddr,emCmdDataType_IP,        sizeof(IPaddr));
                cla_addParameter("ipconfig","mask",(void *)maskIP,emCmdDataType_IP,                sizeof(maskIP));
                cla_addParameter("ipconfig","dns",(void *)dnsIP,emCmdDataType_IP,                sizeof(dnsIP));
                cla_addParameter("ipconfig","mac",(void *)Mac,emCmdDataType_MAC,                sizeof(Mac));

                cla_regsister("run","<funtions>");
                cla_addRunFun("run",ptest_3fun_help,"test",test_3fun,3);
                cla_addRunFun("run",ptest_2fun_help,"test2",test_2fun,2);
}


命令解析的进程如下:

void        cla_precess(uint8_t *pCmd,uint16_t cmdLen,FUN_PRINT_OUT printFun)
{
        uint16_t       i,outlen;
        FUN_STOR_BK                storageFun=NULL;
        BOOLEAN       res;
        static        ST_CMD_STR                cmdStr;
        static        char        outBuff;
        if((pCmd !=NULL)&&(cmdLen > 2))
        {
                        cla_cmdMake(&cmdStr,pCmd,cmdLen);       
                        if((cla_CheckIn(&cmdStr,printFun))&&(cmdStr.argCount >= 1))
                        {
                                for(i=0;i<CMD_NODE_TOTAL;i++)
                                {
                                       if(CmdNodeGroup.pKeyword != NULL)
                                       {                                               
                                                 if(strcmp((const char *)CmdNodeGroup.pKeyword,cmdStr.pArgValue) == 0)
                                                        {
                                                                        if(cmdStr.argCount== 1)//查询参数
                                                                        {
                                                                                        outlen =cla_sprintfPara(CmdNodeGroup.pData,outBuff,OUT_BUFF_MAX_LEN);
                                                                                        if(printFun !=NULL)
                                                                                        {
                                                                                               printFun(outBuff,outlen);
                                                                                        }
                                                                                        Debug("out done\n");
                                                                        }
                                                                        else if(strcmp("run",cmdStr.pArgValue) == 0)
                                                                        {
                                                                                        cmdStr.argPos                =1;
                                                                                        res        =cla_runFuntion(CmdNodeGroup.pFuntion,&cmdStr);
                                                                                        Debug("run done\n");                                                                                       
                                                                        }
                                                                        else
                                                                        {
                                                                                        cmdStr.argPos                =1;
                                                                                        res       =cla_scanfPara(CmdNodeGroup.pData,&cmdStr);
                                                                                        if(res)
                                                                                        {
                                                                                                        Debug("cfg done\n");
                                                                                                        storageFun =CmdNodeGroup.StorageFun;
                                                                                                        if(storageFun !=NULL)
                                                                                                        {
                                                                                                                        storageFun();
                                                                                                        }
                                                                                        }                                                                                       
                                                                        }
                                                                        return ;
                                                        }
                                                        else if(strcmp("help",cmdStr.pArgValue) == 0)
                                                        {
                                                                        cla_help(printFun,outBuff,OUT_BUFF_MAX_LEN);
                                                                        return ;
                                                        }
                                       }
                                }
                        }
        }       
}




代码可以直接下载下来,在ESP32上使用 TELNET 操作,也可以移植到其他地方,比如用串口操作



avr-arm 发表于 2018-10-27 13:40:42

赞,不错的东东

了无 发表于 2018-10-27 21:39:35

看着挺精简的,不过没看明白,先收藏慢慢看。

honami520 发表于 2018-10-27 21:58:02

不知道有啥用途

dukelec 发表于 2018-10-27 22:36:27

本帖最后由 dukelec 于 2018-10-27 23:07 编辑

我直接把 Linux 的红黑树拿到单片机用(不用修改就可以用),可以做成类似电脑 socket 编程的效果,可以裸跑,也可以上系统。
既支持被调用(做服务器),也支持主动查询(做客户端)。
而且,可以分散在不同 c 文件,不用集中 switch case, 更加干净。

譬如这个裸跑的小步进电机的代码 https://github.com/dukelec/stepper_motor_controller/blob/master/fw/usr/app_motor.c
库存放在:https://github.com/dukelec/cdnet , 其中还有很多好用的工具。

static cdnet_socket_t pos_sock = { .port = 20 };
static cdnet_socket_t speed_sock = { .port = 21 };


static void pos_mode_service_routine(void)
{
    cdnet_packet_t *pkt = cdnet_socket_recvfrom(&pos_sock);
    if (!pkt)
      return;

    // 处理接收的命令
    ....

    pkt->len = 2;
    pkt->dat = cmd ? 0x80 : 0x81; // 返回状态
    pkt->dat = cmd_pend_head.len; // 返回命令队列长度

    pkt->dst = pkt->src;
    cdnet_socket_sendto(&pos_sock, pkt);
}

void main(void)
{
    ....
    cdnet_socket_bind(&pos_sock, NULL);
    cdnet_socket_bind(&speed_sock, NULL);
    ....
    while (true) {
      ....
      pos_mode_service_routine();
      speed_mode_service_routine();
      ....
    }
}

同样,即便是字符串命令,也是建议通过红黑树做成高级语言的 dict 形式,普通 for 循环遍历链表或数组效率太差。

另外,其它需要用到链表的地方,也建议使用 Linux 的 container_of 的方式实现,这样能最优雅的实现代码共用。当然我也整理了一份在我的库里。

我还写过一个命令行解析的小程序,由于只是电脑程序启动时使用一次,所以不用考虑效率,是用链表实现的:
https://github.com/dukelec/cdipc/blob/master/cdipc/utils/cd_args.h

Pupil 发表于 2018-10-28 09:40:41

不错收藏学习

ronic 发表于 2018-10-28 10:02:54

感谢分享,学习一下

zqy517 发表于 2018-10-28 12:34:42

赞,不错的东东

ztg328 发表于 2018-10-28 15:13:20

mark mark

huangqi412 发表于 2018-10-28 19:46:31

这个是通用么

cloudboy 发表于 2018-10-28 22:57:45

ESP32感觉还是直接用里面的核比较好,双核够强的了

guxingganyue 发表于 2018-10-29 21:14:54

楼主有没有和RT-Thread AT组件比较呢,这个组件是专门解析AT指令,似乎很强大

maimaige 发表于 2018-10-30 08:13:19

mark 厉害

zsenbao 发表于 2018-10-30 08:28:46

不错,学习了

LingYi 发表于 2018-10-30 21:36:16

dukelec 发表于 2018-10-27 22:36
我直接把 Linux 的红黑树拿到单片机用(不用修改就可以用),可以做成类似电脑 socket 编程的效果,可以裸 ...

谢谢分享,最近比较忙,空了来研究一下!你这个应该效率高不少。

LingYi 发表于 2018-10-30 21:37:07

huangqi412 发表于 2018-10-28 19:46
这个是通用么

可以通用。

LingYi 发表于 2018-10-30 21:37:50

honami520 发表于 2018-10-27 21:58
不知道有啥用途

LINUX 命令解析,比如cd ...ls copy 等一些列命令都是这么做的

LingYi 发表于 2018-10-30 21:38:49

guxingganyue 发表于 2018-10-29 21:14
楼主有没有和RT-Thread AT组件比较呢,这个组件是专门解析AT指令,似乎很强大 ...

我有AT 指令命令框架,这个也能简单修改,适配AT指令

kinsno 发表于 2018-10-30 21:42:33

LingYi 发表于 2018-10-30 21:38
我有AT 指令命令框架,这个也能简单修改,适配AT指令

不明白你这个主题里的命令行是指?
给讲解一下呗,不晓得在哪里用的。。

sunbest80 发表于 2018-10-30 21:45:57

学习了

guxingganyue 发表于 2018-10-30 21:46:40

LingYi 发表于 2018-10-30 21:38
我有AT 指令命令框架,这个也能简单修改,适配AT指令

哦,你这个是ESP32的指令解析架构,记成AT指令架构了。。。

这2个差别不大吧?以前只解析过几个AT直接,解的非常麻烦,想找个通用的AT指令解析架构学习下

guxingganyue 发表于 2018-10-30 21:49:09

LingYi 发表于 2018-10-30 21:37
LINUX 命令解析,比如cd ...ls copy 等一些列命令都是这么做的

是的,linux里的命令行解析功能很强大,一直想找一个可以在单片机上跑的,一直没找到

LingYi 发表于 2018-10-30 21:52:30

guxingganyue 发表于 2018-10-30 21:49
是的,linux里的命令行解析功能很强大,一直想找一个可以在单片机上跑的,一直没找到 ...

这个已经实现了得差不多了,可以直接移植,非常容易实现的。

LingYi 发表于 2018-10-30 21:54:22

kinsno 发表于 2018-10-30 21:42
不明白你这个主题里的命令行是指?
给讲解一下呗,不晓得在哪里用的。。



linux的
cd ../
copy ./a ./b
reboot
这些没有用过嘛,都是这样实现的,只不过初始化的时候需要自己去注册函数,变量 等
linux 内核代码已经帮你注册了,所有你可以使用

LingYi 发表于 2018-10-30 22:01:34

dukelec 发表于 2018-10-27 22:36
我直接把 Linux 的红黑树拿到单片机用(不用修改就可以用),可以做成类似电脑 socket 编程的效果,可以裸 ...

用socket 的方式确实很高级,我明白中心思想了,哪个container_of 的方式,几年前我已经用过了,只是当时不知道,哈哈。优秀的代码还是要去linux里面抠。

kinsno 发表于 2018-10-30 22:02:02

LingYi 发表于 2018-10-30 21:54
linux的
cd ../
copy ./a ./b


我懂了。。。
以前玩过。。

guxingganyue 发表于 2018-10-30 22:06:26

LingYi 发表于 2018-10-30 21:52
这个已经实现了得差不多了,可以直接移植,非常容易实现的。

直接从linux的相关源码里面移植到mcu上?

makathy 发表于 2018-10-30 22:13:07

还是需要学linux啊

文艺小青年 发表于 2018-10-31 08:53:10

MARK,shell相关

leijiayou 发表于 2018-10-31 09:27:41

mark   shell   

sandman 发表于 2018-10-31 09:29:58

谢谢,很好的思路

LingYi 发表于 2018-11-6 09:51:36

更新一下,基于红黑树的 命令解析

lworldstar 发表于 2018-11-17 22:26:45

mark,移植用用,应该效率不错

RAMILE 发表于 2018-11-18 20:21:10

面试的时候,如果有人问我什么是红黑树,我扭头就走
不是因为这题太容易
而是老子真的不会啊

--段子

fengyunyu 发表于 2020-11-1 19:25:29

收藏,谢谢大神

oooios 发表于 2020-11-1 21:56:32

谢谢分享,{:smile:}{:smile:}{:smile:}

yangzi8000 发表于 2022-4-29 15:07:26

mark.................
页: [1]
查看完整版本: 分享一个ESP32命令行解析框架