This CLI (Command Line Interface) is based mbed-os. Both NNN50 and NQ620 are supported.

Fork of NNN40_CLI by Delta

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers command-interpreter.cpp Source File

command-interpreter.cpp

00001 /**
00002  * File: command-interpreter.c
00003  * Description: processes commands incoming over the serial port.
00004  *
00005  * Copyright 2014 by CYNTEC Corporation.  All rights reserved.
00006  */
00007 
00008 #include <stdint.h>
00009 #include <string.h>
00010 #include "mbed.h"
00011 #include "command-interpreter.h"
00012 
00013 extern Serial console;
00014 
00015 // Command parsing state
00016 typedef struct {
00017 
00018   // Finite-state machine's current state. 
00019   uint8_t state;
00020 
00021   // The command line is stored in this buffer.
00022   uint8_t buffer[CYNTEC_COMMAND_BUFFER_LENGTH];
00023 
00024   // Indices of the tokens (command(s) and arguments) in the above buffer.
00025   uint8_t tokenIndices[MAX_TOKEN_COUNT];
00026 
00027   // The number of tokens read in, including the command(s). 
00028   uint8_t tokenCount;
00029 
00030   // Used while reading in the command line.
00031   uint8_t index;
00032 
00033   // First error found in this command. 
00034   uint8_t error;
00035 
00036   // The token number of the first true argument after possible nested commands.
00037   uint8_t argOffset;
00038   
00039   //gill
00040   uint8_t totalBuffer[CYNTEC_COMMAND_BUFFER_LENGTH];
00041   uint8_t totalIndex;
00042 
00043 } CyntecCommandState;
00044 
00045 static CyntecCommandState commandState;
00046 
00047 // Remember the previous character seen by emberProcessCommandString() to ignore
00048 // an LF following a CR.
00049 static uint8_t previousCharacter = 0;
00050 CyntecCommandEntry *cyntecCurrentCommand;
00051 
00052 enum {
00053   CMD_AWAITING_ARGUMENT,
00054   CMD_READING_ARGUMENT,
00055   CMD_READING_TO_EOL                   // clean up after error
00056 };
00057 
00058 
00059 
00060 const char* cyntecCommandErrorNames[] =
00061   {
00062     "",
00063     "No such command;",
00064     "Wrong number of arguments;",
00065     "Argument out of range;",
00066     "Argument syntax error;",       
00067     "No matched argument;",
00068     "Wrong command order;",
00069     "Invalid state to perform operation;",
00070     "Function call fail;"
00071   };
00072 
00073     /**
00074 *   @brief Converts a character representation of a hex to real value.
00075 *   @param c is the hex value in char format
00076 *   @return the value of the hex otherwise INVALID_HEX_CHARACTER
00077 */
00078 
00079 uint8_t cyntecAtoi(uint8_t *str, uint8_t len)
00080 {
00081     uint8_t result = 0;
00082     uint8_t i = 0;
00083     
00084     while( *str != '\0' && i < len)
00085     {
00086         result *= 10;
00087         result = result + ( *str - '0' );
00088         str++;
00089         i++;
00090     }
00091     
00092     return result;
00093 }
00094 int cyntecAtoInt(uint8_t *str)
00095 {
00096     int result = 0;
00097     uint8_t i = 0;
00098     bool is_negative = false;
00099     
00100     if (*str == '-') {
00101         is_negative = true;
00102         str++;
00103     }
00104     while( *str != '\0')
00105     {
00106         result *= 10;
00107         result = result + ( *str - '0' );
00108         str++;
00109         i++;
00110     }
00111     
00112     if (is_negative)
00113         return -result;
00114     else
00115         return result;
00116 }
00117 uint8_t cyntecArgToUint8(uint8_t *str, uint8_t len)
00118 {
00119     uint8_t result = 0;
00120     uint8_t num[2];
00121     uint8_t i;
00122     
00123     if ( len != 2 )
00124     {
00125         return 0;
00126     }
00127     
00128     for ( i = 0 ; i < 2 ; i++ )
00129     {
00130         if ('0' <= str[i] && str[i] <= '9')
00131             num[i] = str[i] - '0';
00132         else if ('a' <= str[i] && str[i] <= 'f')
00133             num[i] = str[i] - 'a' + 10;
00134         else if ('A' <= str[i] && str[i] <= 'F')
00135             num[i] = str[i] - 'A' + 10;
00136         else
00137             return 0;
00138     }
00139     
00140     result |= num[0] << 4;
00141     result |= num[1] << 0;
00142     
00143     return result;
00144 }
00145 
00146 uint16_t cyntecAtoiUint16(uint8_t *str, uint8_t len)
00147 {
00148     uint16_t result = 0;
00149     uint16_t i = 0;
00150     
00151     while( *str != '\0' && i < len)
00152     {
00153         result *= 10;
00154         result = result + ( *str - '0' );
00155         str++;
00156         i++;
00157     }
00158     
00159     return result;
00160 }
00161 
00162 uint16_t cyntecArgToUint16(uint8_t *str, uint8_t len)
00163 {
00164     uint16_t result = 0;
00165     uint8_t num[4];
00166     uint8_t i;
00167     
00168     if ( len != 4 )
00169     {
00170         return 0;
00171     }
00172     
00173     for ( i = 0 ; i < 4 ; i++ )
00174     {
00175         if ('0' <= str[i] && str[i] <= '9')
00176             num[i] = str[i] - '0';
00177         else if ('a' <= str[i] && str[i] <= 'f')
00178             num[i] = str[i] - 'a' + 10;
00179         else if ('A' <= str[i] && str[i] <= 'F')
00180             num[i] = str[i] - 'A' + 10;
00181         else
00182             return 0;
00183     }
00184     
00185     result |= num[0] << 12;
00186     result |= num[1] << 8;
00187     result |= num[2] << 4;
00188     result |= num[3] << 0;
00189     
00190     return result;
00191 }
00192 
00193 //gill 20150918
00194 uint32_t cyntecHexToUint32(uint8_t *str, uint8_t len)
00195 {
00196     if (len > 8)
00197         return 0;
00198     uint32_t result = 0;
00199     uint16_t i = 0;
00200     
00201     while( *str != '\0' && i < len)
00202     {
00203         result *= 16;
00204         result = result + ( *str - '0' );
00205         str++;
00206         i++;
00207     }
00208     
00209     return result;
00210 }
00211 
00212 
00213 
00214 
00215 uint8_t cyntecStrCmp(uint8_t *src, uint8_t *dst, uint8_t len)
00216 {
00217     uint8_t i = 0;
00218     
00219     while ( *src != '\0' && *dst != '\0' && i < len ) 
00220     {
00221         if ( *src != *dst )
00222             return 0;
00223         i++;
00224         src++;
00225         dst++;
00226     }
00227     
00228     return 1;
00229 }
00230     
00231 // Initialize the state machine.
00232 void cyntecCommandReaderInit(void)
00233 {
00234   commandState.state = CMD_AWAITING_ARGUMENT;
00235   commandState.index = 0;
00236   commandState.tokenIndices[0] = 0;
00237   commandState.tokenCount = 0;
00238   commandState.error = CYNTEC_CMD_SUCCESS;
00239   commandState.argOffset = 0;
00240     cyntecCurrentCommand = NULL;
00241     commandState.totalIndex = 0; //gill
00242 }
00243 
00244 static uint8_t tokenLength(uint8_t num)
00245 {
00246   return (commandState.tokenIndices[num + 1] 
00247           - commandState.tokenIndices[num]);
00248 }
00249 
00250 static uint8_t *tokenPointer(uint8_t tokenNum)
00251 {
00252   return (commandState.buffer + commandState.tokenIndices[tokenNum]);
00253 }
00254 
00255 void cyntecCommandActionHandler(const CommandAction action)
00256 {
00257   (*action)();
00258     clearBuffer();
00259 }
00260 
00261 static bool getNestedCommand(CyntecCommandEntry *entry,
00262                                 CyntecCommandEntry **nestedCommand)
00263 {
00264   if ( entry -> action == NULL ) {
00265     *nestedCommand = (CyntecCommandEntry*)entry->subMenu;
00266     return true;
00267   } else {
00268     return false;
00269   }
00270 }
00271 
00272 static void cyntecPrintCommandUsage(CyntecCommandEntry *entry) 
00273 {
00274   CyntecCommandEntry *commandFinger;
00275 
00276     if (entry == NULL) {
00277         entry = commandFinger = cyntecCommandTable;
00278     } else {
00279         getNestedCommand(entry, &commandFinger);
00280         
00281         console.printf("%s-%s\r\n",entry->name,entry->description);
00282     }
00283     
00284   if ( commandFinger != NULL ) {
00285     for (; commandFinger->name != NULL; commandFinger++) {
00286             console.printf("%s - %s\r\n",commandFinger->name,commandFinger->description);
00287     }   
00288   }
00289     
00290 }
00291 
00292 void cyntecCommandErrorHandler(uint8_t status)
00293 {
00294         //Silvia 20161111 modify
00295         console.printf("\r\nERROR;%s\r\n\r\n", cyntecCommandErrorNames[status]);
00296         cyntecPrintCommandUsage(cyntecCurrentCommand);
00297 }
00298 
00299 static CyntecCommandEntry *commandLookup(CyntecCommandEntry *commandFinger, 
00300                                         uint8_t tokenNum)
00301 {
00302   uint8_t *inputCommand = tokenPointer(tokenNum);
00303   uint8_t inputLength = tokenLength(tokenNum);
00304 
00305   for (; commandFinger->name != NULL; commandFinger++) {
00306     const char *entryFinger = commandFinger->name;
00307     uint8_t *inputFinger = inputCommand;
00308     for (;; entryFinger++, inputFinger++) {
00309       bool endInput = (inputFinger - inputCommand == inputLength);
00310       bool endEntry = (*entryFinger == 0);
00311       if (endInput && endEntry) {
00312         return commandFinger;  // Exact match.
00313       } else if ((*inputFinger) != (*entryFinger)) {
00314         break;
00315       }
00316     }
00317   }
00318   return NULL;
00319 }
00320 
00321 void callCommandAction(void)
00322 {
00323     CyntecCommandEntry *commandFinger = cyntecCommandTable;
00324     uint8_t tokenNum = 0;
00325     
00326     if (commandState.tokenCount == 0) {
00327     cyntecCommandReaderInit();
00328         return;
00329   }
00330     
00331     // Lookup the command.
00332   while (true) {
00333         commandFinger = commandLookup(commandFinger, tokenNum);
00334         if (commandFinger == NULL) {
00335       commandState.error = CYNTEC_CMD_ERR_NO_SUCH_COMMAND;
00336             break;
00337     } else {
00338             cyntecCurrentCommand = commandFinger;
00339       tokenNum += 1;
00340       commandState.argOffset += 1;
00341 
00342       if ( getNestedCommand(commandFinger, &commandFinger) ) {
00343         if (tokenNum >= commandState.tokenCount) {
00344           commandState.error = CYNTEC_CMD_ERR_WRONG_NUMBER_OF_ARGUMENTS;
00345                     break;
00346         }
00347       } else {
00348         break;
00349       }
00350     }
00351     }
00352 
00353   if (commandState.error == CYNTEC_CMD_SUCCESS) {
00354     cyntecCommandActionHandler(commandFinger->action);
00355   } else {
00356         cyntecCommandErrorHandler(commandState.error);;
00357   } 
00358     
00359     cyntecCommandReaderInit();
00360 }
00361 
00362 /*
00363  * 
00364  */
00365 void cyntecEndArgument(uint8_t input)
00366 {
00367   if (commandState.tokenCount == MAX_TOKEN_COUNT) {
00368         commandState.error = CYNTEC_CMD_ERR_WRONG_NUMBER_OF_ARGUMENTS;
00369     commandState.state = CMD_READING_TO_EOL;
00370     }
00371     
00372     commandState.tokenCount += 1;
00373     commandState.tokenIndices[commandState.tokenCount] = commandState.index;
00374     commandState.state = CMD_AWAITING_ARGUMENT;
00375     
00376   if (input == '\r' || input == '\n') {
00377         callCommandAction();
00378   }
00379 }
00380 
00381 /*
00382  * 
00383  */
00384 void cyntecWriteToBuffer(uint8_t input)
00385 {
00386     if (commandState.index == CYNTEC_COMMAND_BUFFER_LENGTH) {
00387         commandState.error = CYNTEC_CMD_ERR_WRONG_NUMBER_OF_ARGUMENTS;
00388         commandState.state = CMD_READING_TO_EOL;
00389     } else {
00390         commandState.buffer[commandState.index] = input;
00391         commandState.index += 1;
00392     }
00393 }
00394 
00395 
00396 /*
00397  * Process the given char as a command.
00398  */
00399 void cyntecProcessCommandInput(uint8_t input) {
00400     
00401     bool isEol = false;
00402   bool isSpace = false;
00403 
00404     if (previousCharacter == '\r' && input == '\n') {
00405         previousCharacter = input;
00406     return;
00407   }
00408     
00409   previousCharacter = input;    
00410   isEol = ((input == '\r') || (input == '\n'));
00411   isSpace = (input == ' ');
00412     
00413     switch (commandState.state) {
00414     case CMD_AWAITING_ARGUMENT:
00415       if (!isEol)
00416         cyntecWriteToTotalBuffer(input); // total buffer including space
00417       if (isEol) {
00418         callCommandAction();
00419       } else if (! isSpace) {
00420         commandState.state = CMD_READING_ARGUMENT;
00421         cyntecWriteToBuffer(input);
00422       }
00423       break;
00424     case CMD_READING_ARGUMENT:
00425       if (!isEol)
00426         cyntecWriteToTotalBuffer(input);
00427       if (isEol || isSpace) {
00428         cyntecEndArgument(input);
00429       } else {
00430         cyntecWriteToBuffer(input);
00431       }         
00432             break;
00433     case CMD_READING_TO_EOL:
00434       if (isEol) {
00435         if (commandState.error != CYNTEC_CMD_SUCCESS) {
00436           cyntecCommandErrorHandler(commandState.error);
00437         }
00438         cyntecCommandReaderInit();
00439       }
00440       break;            
00441     }
00442 }
00443 
00444 
00445 /** Retrieves unsigned integer arguments. */
00446 uint8_t *cyntecGetCommandArgument(uint8_t argNum, uint8_t *length)
00447 {
00448   uint8_t tokenNum = argNum + commandState.argOffset;
00449 
00450   if (length != NULL) {
00451     *length = tokenLength(tokenNum);
00452   }
00453   return tokenPointer(tokenNum);
00454 }
00455 
00456 void clearBuffer(void)
00457 {
00458     uint16_t i;
00459     for (i=0;i<CYNTEC_COMMAND_BUFFER_LENGTH;i++)
00460     {
00461         commandState.buffer[i] = NULL;
00462     }
00463 }
00464 
00465 /** Retrieves the token count. */
00466 uint8_t cyntecGetCommandTokenCnt()
00467 {
00468     return commandState.tokenCount;
00469 }
00470 
00471 /*
00472 gill add for accept blank in name 20150904
00473 uint8_t *cyntecGetCommandBuffer()
00474 void cyntecWriteToTotalBuffer(uint8_t input)
00475 uint8_t *cyntecGetCommandTotalBuffer(void)
00476 uint8_t cyntecGetTotalIndex(void)
00477 */
00478 
00479     
00480 void cyntecWriteToTotalBuffer(uint8_t input)
00481 {
00482     if (commandState.totalIndex == CYNTEC_COMMAND_BUFFER_LENGTH) {
00483         commandState.error = CYNTEC_CMD_ERR_WRONG_NUMBER_OF_ARGUMENTS;
00484         commandState.state = CMD_READING_TO_EOL;
00485     } else {
00486         commandState.totalBuffer[commandState.totalIndex] = input;
00487         commandState.totalIndex += 1;
00488     }
00489 }
00490 
00491 uint8_t *cyntecGetCommandTotalBuffer(void)
00492 {
00493     return commandState.totalBuffer;
00494 }
00495 
00496 uint8_t cyntecGetTotalIndex(void)
00497 {
00498     return commandState.totalIndex;
00499 }
00500 
00501