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

 

背景

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

 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 = 0; dwDbgMapIdx < (sizeof(gOmciDebugMap)/sizeof(CMD_USER_PROC)); dwDbgMapIdx++)
13     {
14         if(gOmciDebugMap[dwDbgMapIdx].dwArgCounts != ptCmdUserArg->arg_counts)
15             continue;
16 
17         for(dwChkArgIdx = 0; dwChkArgIdx < gOmciDebugMap[dwDbgMapIdx].dwCheckArgCounts; dwChkArgIdx++)
18         {
19             if(0 != strcmp(ptCmdUserArg->arg[dwChkArgIdx], gOmciDebugMap[dwDbgMapIdx].CmdStr[dwChkArgIdx]))
20             {   //若任一待检查命令参数不匹配,则认为整体不匹配
21                 break;
22             }
23         }
24 
25         if(dwChkArgIdx == gOmciDebugMap[dwDbgMapIdx].dwCheckArgCounts)
26         {
27             gOmciDebugMap[dwDbgMapIdx].procfun(ptCmdUserArg); //调用相应的调试函数
28             return ;
29         }
30     }
31 
32     OmciDebugHelp(ptCmdUserArg); //若未找到对应调试函数,则输出帮助信息
33 }
Command Parser

     可见,接收到的的命令必须满足以下条件才能正确解析:

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

     当命令解析失败时,调用全局帮助函数提示用户:

 1 INT32 OmciDebugHelp(CMD_USER_ARG * pCmdUserArg)
 2 {
 3     INT32U dwDbgMapIdx = 0, dwChkArgIdx = 0;
 4     CHAR szDbgBuf[MAX_CMDMSG_LEN] = {0};
 5 
 6     for(dwDbgMapIdx = 0; dwDbgMapIdx < (sizeof(gOmciDebugMap)/sizeof(CMD_USER_PROC)); dwDbgMapIdx++)
 7     {
 8         memset(szDbgBuf, 0, sizeof(szDbgBuf));
 9         for(dwChkArgIdx = 0; dwChkArgIdx < gOmciDebugMap[dwDbgMapIdx].dwCheckArgCounts; 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

     因此,当用户输入错误的命令时,终端将呈现如下的“救命稻草” :

 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

 

问题

     问题是,这根稻草真的能“救命”吗?设想:

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

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

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

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

         为什么终端还是吐出一根稻草?!Are u kidding???

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

 

改进

     经过缜密的调查,发现问题主要体现为:

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

     于是乎,接下来对症下药。

     首先,将模块内部定义的命令列表稍加改造,如下:

 1 //调测命令表项宏,用于简化书写
 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[] = {
 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 = (INT32U)(sizeof(gOmciDebugMap)/sizeof(CMD_USER_PROC) - 1/*End*/);
Improved Command Lists

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

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

     接着,命令解析函数改为:

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

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

     **:又不是账号密码,完全没必要区分大小写嘛~

     然后,全局帮助函数也稍作修改: 

 1 INT32 OmciDebugHelp(CMD_USER_ARG *pCmdUserArg)
 2 {
 3     INT32U dwDbgMapIdx = 0, dwChkArgIdx = 0;
 4     CHAR szDbgBuf[MAX_CMDMSG_LEN] = {0};
 5 
 6     printf("\n<Usage>: %s [Args...]. e.g.\n", pszOmciDbgHead);
 7     for(dwDbgMapIdx = 0; dwDbgMapIdx < OMCI_DEBUG_MAP_NUM; dwDbgMapIdx++)
 8     {
 9         memset(szDbgBuf, 0, sizeof(szDbgBuf));
10         for(dwChkArgIdx = 0; dwChkArgIdx < gOmciDebugMap[dwDbgMapIdx].dwCheckArgCounts; 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("<Note> [Args...] are case-insensative(i.e.ShowPwSrvInfo=showpwsrvinfo)!\n");
19     printf("       Press '?' or 'help' after the first [Arg] to get detailed usage.\n\n");
20 
21     return 0;
22 }
Improved Global Helper

     经过改造后,终端呈现的帮助提示如下所示:

 1 <Usage>: 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 <Note> [Args...] are case-insensative(i.e.ShowPwSrvInfo=showpwsrvinfo)!
12        Press '?' or 'help' after the first [Arg] to get detailed usage.
Improved Helper Prompts

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

     以“SetMeCtrl”命令为例,其处理函数实现如下:

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

     至此,用户A、B或C可以一起愉快地“玩耍”了:

1 sendcmd 132 omcidebug  SetMeCtrl ?
2 <Usage>: sendcmd 132 omcidebug SetMeCtrl [MeClass:Decimal][AutoCreated:BOOL]
3          Set MeClass=0 to Clear Me Create Ctrl File...
Happy Ending

 

 

posted @ 2014-05-22 15:06  clover_toeic  阅读(873)  评论(0编辑  收藏