热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

终端调测命令易用性的改进

背景某平台向上层应用模块提供调测命令支持,其接口结构定义如下:1#defineMAX_CMDMSG_LEN322#defineMAX_ARG_NUM73

 

背景

     某平台向上层应用模块提供调测命令支持,其接口结构定义如下:

1 #define MAX_CMDMSG_LEN 32
2 #define MAX_ARG_NUM 7
3 #define MAX_USERARG_NUM (MAX_ARG_NUM - 1)
4
5 typedef struct{
6 INT32U arg_counts; /* 二级进程调试命令带的参数个数 */
7 CHAR arg[MAX_USERARG_NUM][MAX_CMDMSG_LEN]; /* 调试命令的参数 */
8 }CMD_USER_ARG;
9
10 /* 用户处理命令函数*/
11 typedef INT32S (*CmdUserProcFun)(CMD_USER_ARG *pCmdUserArg);
12
13 /* 应用模块处理命令数据结构 */
14 typedef struct {
15 INT32U dwArgCounts; /* 该命令实际带的参数 */
16 INT32U dwCheckArgCounts; /* 统一比较的参数 */
17 CHAR *CmdStr[MAX_USERARG_NUM];
18 CmdUserProcFun procfun;
19 }CMD_USER_PROC;

Interface Structure

     某应用模块内部定义需要的命令列表,如下:

1 static CMD_USER_PROC gOmciDebugMap[] = {
2 {1, 1, {"help",0,0,0,0,0}, (CmdUserProcFun)OmciDebugHelp},
3 {1, 1, {"showmectrl",0,0,0,0,0}, (CmdUserProcFun)ShowMeCtrl},
4 {2, 1, {"showmeinfo",0,0,0,0,0}, (CmdUserProcFun)ShowMeInfo},
5 {1, 1, {"showmeminfo",0,0,0,0,0}, (CmdUserProcFun)ShowMemInfo},
6 {1, 1, {"showuniinfo",0,0,0,0,0}, (CmdUserProcFun)ShowUniInfo},
7 {3, 1, {"setmectrl",0,0,0,0,0}, (CmdUserProcFun)SetMeCtrl},
8 {2, 2, {"setmectrl","help",0,0,0,0}, (CmdUserProcFun)SetMeCtrlHelp},//注意此行!
9 {3, 1, {"log",0,0,0,0,0}, (CmdUserProcFun)SetOmciLog},
10 {4, 1, {"testbatch",0,0,0,0,0}, (CmdUserProcFun)TestBatch},
11 {1, 1, {"testendian",0,0,0,0,0}, (CmdUserProcFun)TestEndianOper}
12 };

Command Lists

     对应的调测命令形如“sendcmd [pid] [module] [args...]”。本模块将解析处理args部分的参数。

     用户在终端(如串口)输入的模块调测命令,经平台捕获后转发给该模块。模块接收到平台发来的调测命令消息后,调用如下函数进行解析:

1 VOID OmciDebugMsgProc(INT16U wEvent, VOID *lpMsg, INT16U wMsgLen)
2 {
3 INT32U dwDbgMapIdx = 0, dwChkArgIdx = 0;
4 CMD_USER_ARG *ptCmdUserArg = (CMD_USER_ARG *)lpMsg;
5
6 if((wMsgLen != sizeof(CMD_USER_ARG)) || (0 == ptCmdUserArg->arg_counts))
7 {
8 OmciDebugHelp(ptCmdUserArg);
9 return ;
10 }
11
12 for(dwDbgMapIdx &#61; 0; dwDbgMapIdx <(sizeof(gOmciDebugMap)/sizeof(CMD_USER_PROC)); dwDbgMapIdx&#43;&#43;)
13 {
14 if(gOmciDebugMap[dwDbgMapIdx].dwArgCounts !&#61; ptCmdUserArg->arg_counts)
15 continue;
16
17 for(dwChkArgIdx &#61; 0; dwChkArgIdx )
18 {
19 if(0 !&#61; strcmp(ptCmdUserArg->arg[dwChkArgIdx], gOmciDebugMap[dwDbgMapIdx].CmdStr[dwChkArgIdx]))
20 { //若任一待检查命令参数不匹配&#xff0c;则认为整体不匹配
21 break;
22 }
23 }
24
25 if(dwChkArgIdx &#61;&#61; gOmciDebugMap[dwDbgMapIdx].dwCheckArgCounts)
26 {
27 gOmciDebugMap[dwDbgMapIdx].procfun(ptCmdUserArg); //调用相应的调试函数
28 return ;
29 }
30 }
31
32 OmciDebugHelp(ptCmdUserArg); //若未找到对应调试函数&#xff0c;则输出帮助信息
33 }

Command Parser

     可见&#xff0c;接收到的的命令必须满足以下条件才能正确解析&#xff1a;

  • 命令携带的参数数目与命令列表中某条目预期一致
  • 命令中待检查的参数与命令列表中某条目精确匹配(包括大小写)

     当命令解析失败时&#xff0c;调用全局帮助函数提示用户&#xff1a;

1 INT32 OmciDebugHelp(CMD_USER_ARG * pCmdUserArg)
2 {
3 INT32U dwDbgMapIdx &#61; 0, dwChkArgIdx &#61; 0;
4 CHAR szDbgBuf[MAX_CMDMSG_LEN] &#61; {0};
5
6 for(dwDbgMapIdx &#61; 0; dwDbgMapIdx <(sizeof(gOmciDebugMap)/sizeof(CMD_USER_PROC)); dwDbgMapIdx&#43;&#43;)
7 {
8 memset(szDbgBuf, 0, sizeof(szDbgBuf));
9 for(dwChkArgIdx &#61; 0; dwChkArgIdx )
10 {
11 sprintf(szDbgBuf, "%s %s", szDbgBuf, gOmciDebugMap[dwDbgMapIdx].CmdStr[dwChkArgIdx]);
12 }
13 printf("sendcmd 132 omcidebug %s\n\r", szDbgBuf);
14 }
15 return 0;
16 }

Global Helper

     因此&#xff0c;当用户输入错误的命令时&#xff0c;终端将呈现如下的“救命稻草” &#xff1a;

1 sendcmd 132 omcidebug help
2 sendcmd 132 omcidebug showmectrl
3 sendcmd 132 omcidebug showmeinfo
4 sendcmd 132 omcidebug showmeminfo
5 sendcmd 132 omcidebug showuniinfo
6 sendcmd 132 omcidebug setmectrl
7 sendcmd 132 omcidebug setmectrl help
8 sendcmd 132 omcidebug log
9 sendcmd 132 omcidebug testbatch
10 sendcmd 132 omcidebug testendian

Helper Prompts

 

问题

     问题是&#xff0c;这根稻草真的能“救命”吗&#xff1f;设想&#xff1a;

     1) 用户A从未接触过该模块调测命令&#xff0c;试探性地输入“sendcmd 132 omcidebug  help”(简记为“help”命令&#xff0c;后面类似)。

         令人惊讶的是&#xff0c;除了“setmectrl help”命令似乎可以进一步提供些有用信息外&#xff0c;其他的命令只有一个不太友好的组合字符串(“testendian”何解&#xff1f;)……

     2) 用户B对命令提示的组合字符串含义有所领悟&#xff0c;愉快地输入“showmeinfo”命令——What&#xff1f;终端又吐出一根稻草&#xff01;

     3) 用户C曾亲睹旁人输入该模块调测命令&#xff0c;知道某些命令只提示了部分参数。于是自信地输入绝无隐藏参数的“showuninfo”命令&#xff0c;但是——

         为什么终端还是吐出一根稻草&#xff1f;&#xff01;Are u kidding???

     至此&#xff0c;用户A、B、C全部败下阵来&#xff0c;内牛满面。所幸&#xff0c;因新手故&#xff0c;A还不至于显得太狼狈。

 

改进

     经过缜密的调查&#xff0c;发现问题主要体现为&#xff1a;

  • 缺乏具体命令的帮助信息
  • 隐藏的参数未加以提示
  • 小写的组合字符串容易误读(用户C承认因老眼昏花将“showuniinfo”错看为“showuninfo”)

     于是乎&#xff0c;接下来对症下药。

     首先&#xff0c;将模块内部定义的命令列表稍加改造&#xff0c;如下&#xff1a;

1 //调测命令表项宏&#xff0c;用于简化书写
2 #define DEBUG_CMD_ENTRY(argNum, cmdStr, dbgFunc) {argNum, 1, {cmdStr}, dbgFunc}
3 #define DEBUG_CMD_ENTRY_END {0, 0, {NULL}, NULL}
4
5 //调测命令表
6 static CMD_USER_PROC gOmciDebugMap[] &#61; {
7 DEBUG_CMD_ENTRY(1, "Help", OmciDebugHelp),
8
9 DEBUG_CMD_ENTRY(1, "ShowMeCtrl", ShowMeCtrl),
10 DEBUG_CMD_ENTRY(2, "ShowMeInfo", ShowMeInfo),
11 DEBUG_CMD_ENTRY(1, "ShowMemInfo", ShowMemInfo),
12 DEBUG_CMD_ENTRY(1, "ShowUniInfo", ShowUniInfo),
13 DEBUG_CMD_ENTRY(3, "SetMeCtrl", SetMeCtrl),
14 DEBUG_CMD_ENTRY(3, "Log", SetOmciLog),
15 DEBUG_CMD_ENTRY(4, "TestBatch", TestBatch),
16 DEBUG_CMD_ENTRY(1, "TestEndian", TestEndianOper),
17
18 DEBUG_CMD_ENTRY_END
19 };
20 const INT32U OMCI_DEBUG_MAP_NUM &#61; (INT32U)(sizeof(gOmciDebugMap)/sizeof(CMD_USER_PROC) - 1/*End*/);

Improved Command Lists

     这里将“setmectrl help”与“setmectrl”命令合为一体&#xff0c;即命令帮助信息置于SetMeCtrl()函数内部*&#xff0c;不另设help函数。待检查参数定为单个字符串&#xff0c;且为PascalCase格式以增强可读性(“ShowUniInfo”比“showuniinfo”更易识别)。

     注*&#xff1a;若在全局帮助函数里提供各命令的详细帮助&#xff0c;则不利于代码的清晰化。

     接着&#xff0c;命令解析函数改为&#xff1a;

1 /******************************************************************************
2 * 函数名称: IsUserNeedHelp
3 * 功能说明: 判断用户是否需要调试帮助
4 若首个待检查命令参数后紧跟"?"、"help"等字符串(可扩充)&#xff0c;
5 则认为用户期望查看调试命令内部帮助信息。
6 * 输入参数: VOID *pvDbgArg : 首个待检查命令参数指针
7 * 输出参数: NA
8 * 返回值 : BOOL
9 ******************************************************************************/
10 static BOOL IsUserNeedHelp(VOID *pvDbgArg)
11 {
12 return ((0 &#61;&#61; strcmp(pvDbgArg, "?")) || (0 &#61;&#61; strcmp(pvDbgArg, "help")));
13 }
14
15
16 VOID OmciDebugMsgProc(INT16U wEvent, VOID *lpMsg, INT16U wMsgLen)
17 {
18 INT32U dwDbgMapIdx &#61; 0, dwChkArgIdx &#61; 0;
19 CMD_USER_ARG *ptCmdUserArg &#61; (CMD_USER_ARG *)lpMsg;
20
21 if((wMsgLen !&#61; sizeof(CMD_USER_ARG)) || (0 &#61;&#61; ptCmdUserArg->arg_counts))
22 {
23 OmciDebugHelp(ptCmdUserArg);
24 return ;
25 }
26
27 for(dwDbgMapIdx &#61; 0; dwDbgMapIdx )
28 {
29 if((gOmciDebugMap[dwDbgMapIdx].dwArgCounts !&#61; ptCmdUserArg->arg_counts) &&
30 (0 &#61;&#61; IsUserNeedHelp(ptCmdUserArg->arg[1])))
31 { //尽可能提供机会&#xff0c;以触发相应调试函数内部帮助信息
32 continue;
33 }
34
35 for(dwChkArgIdx &#61; 0; dwChkArgIdx )
36 {
37 if(0 !&#61; strcasecmp(ptCmdUserArg->arg[dwChkArgIdx], gOmciDebugMap[dwDbgMapIdx].CmdStr[dwChkArgIdx]))
38 { //若任一待检查命令参数不匹配(不区分大小写)&#xff0c;则认为整体不匹配
39 break;
40 }
41 }
42
43 if(dwChkArgIdx &#61;&#61; gOmciDebugMap[dwDbgMapIdx].dwCheckArgCounts)
44 {
45 gOmciDebugMap[dwDbgMapIdx].procfun(ptCmdUserArg); //调用相应的调试函数
46 return ;
47 }
48 }
49
50 OmciDebugHelp(ptCmdUserArg); //若未找到对应调试函数&#xff0c;则输出帮助信息
51 }

Improved Command Parser

     这样&#xff0c;用户在提示的参数后输入“?”或“help”&#xff0c;会显示命令的具体帮助信息。同时&#xff0c;因为不区分大小写**&#xff0c;用户可根据习惯输入全大写、全小写或PascalCase格式的参数。

     **&#xff1a;又不是账号密码&#xff0c;完全没必要区分大小写嘛~

     然后&#xff0c;全局帮助函数也稍作修改&#xff1a; 

1 INT32 OmciDebugHelp(CMD_USER_ARG *pCmdUserArg)
2 {
3 INT32U dwDbgMapIdx &#61; 0, dwChkArgIdx &#61; 0;
4 CHAR szDbgBuf[MAX_CMDMSG_LEN] &#61; {0};
5
6 printf("\n: %s [Args...]. e.g.\n", pszOmciDbgHead);
7 for(dwDbgMapIdx &#61; 0; dwDbgMapIdx )
8 {
9 memset(szDbgBuf, 0, sizeof(szDbgBuf));
10 for(dwChkArgIdx &#61; 0; dwChkArgIdx )
11 {
12 sprintf(szDbgBuf, "%s %s", szDbgBuf, gOmciDebugMap[dwDbgMapIdx].CmdStr[dwChkArgIdx]);
13 }
14 printf("%s %s%s\n", pszOmciDbgHead, szDbgBuf,
15 (gOmciDebugMap[dwDbgMapIdx].dwArgCounts>1) ? " ..." : "");
16 }
17
18 printf(" [Args...] are case-insensative(i.e.ShowPwSrvInfo&#61;showpwsrvinfo)!\n");
19 printf(" Press &#39;?&#39; or &#39;help&#39; after the first [Arg] to get detailed usage.\n\n");
20
21 return 0;
22 }

Improved Global Helper

     经过改造后&#xff0c;终端呈现的帮助提示如下所示&#xff1a;

1 : sendcmd 132 omcidebug [Args...]. e.g.
2 sendcmd 132 omcidebug Help
3 sendcmd 132 omcidebug ShowMeCtrl
4 sendcmd 132 omcidebug ShowMeInfo ...
5 sendcmd 132 omcidebug ShowMemInfo
6 sendcmd 132 omcidebug ShowUniInfo
7 sendcmd 132 omcidebug SetMeCtrl ...
8 sendcmd 132 omcidebug Log ...
9 sendcmd 132 omcidebug TestBatch ...
10 sendcmd 132 omcidebug TestEndian
11 [Args...] are case-insensative(i.e.ShowPwSrvInfo&#61;showpwsrvinfo)!
12 Press &#39;?&#39; or &#39;help&#39; after the first [Arg] to get detailed usage.

Improved Helper Prompts

     用户看到命令参数后携带“...”&#xff0c;就知道该命令有隐藏参数&#xff0c;再输入“?”或“help”即可获得具体的帮助信息。

     以“SetMeCtrl”命令为例&#xff0c;其处理函数实现如下&#xff1a;

1 static INT32 SetMeCtrl(CMD_USER_ARG *pCmdUserArg)
2 {
3 if(IsUserNeedHelp(pCmdUserArg->arg[1]) ||
4 IsUserNeedHelp(pCmdUserArg->arg[2]))
5 {
6 printf("\n: %s SetMeCtrl [MeClass:Decimal][AutoCreated:BOOL]\n", pszOmciDbgHead);
7 printf(" Set MeClass&#61;0 to Clear Me Create Ctrl File...\n\n");
8 return 0;
9 }
10
11 INT32U dwMeClass &#61; 0;
12 INT32U dwAutoCreated &#61; OMCI_TRUE;
13 StrToULong((CHAR *)pCmdUserArg->arg[1], &dwMeClass, 0);
14 StrToULong((CHAR *)pCmdUserArg->arg[2], &dwAutoCreated, 0);
15
16 dwAutoCreated &#61; (OMCI_TRUE &#61;&#61; dwAutoCreated);
17 OmciSetMeCreateCtrl((INT16U)dwMeClass, (BOOL)dwAutoCreated);
18 printf("\n");
19
20 OaSaveDbConfig();
21 return 0;
22 }

Specific Command Handler

     至此&#xff0c;用户A、B或C可以一起愉快地“玩耍”了&#xff1a;

1 sendcmd 132 omcidebug SetMeCtrl ?
2 : sendcmd 132 omcidebug SetMeCtrl [MeClass:Decimal][AutoCreated:BOOL]
3 Set MeClass&#61;0 to Clear Me Create Ctrl File...

Happy Ending

 

 



推荐阅读
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • 本文介绍了Sencha Touch的学习使用心得,主要包括搭建项目框架的过程。作者强调了使用MVC模式的重要性,并提供了一个干净的引用示例。文章还介绍了Index.html页面的作用,以及如何通过链接样式表来改变全局风格。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了在Windows系统上使用C语言命令行参数启动程序并传递参数的方法,包括接收参数程序的代码和bat文件的编写方法,同时给出了程序运行的结果。 ... [详细]
  • 本文介绍了PHP常量的定义和使用方法,包括常量的命名规则、大小写敏感性、全局范围和标量数据的限制。同时还提到了应尽量避免定义resource常量,并给出了使用define()函数定义常量的示例。 ... [详细]
author-avatar
c肀xc86_441
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有