• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

menu.cpp

Go to the documentation of this file.
00001 // -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
00002 
00003 //
00004 // Simple commandline menu system.
00005 //
00006 
00007 #include <FastSerial.h>
00008 #include <AP_Common.h>
00009 
00010 #include <ctype.h>
00011 #include <string.h>
00012 #include <avr/pgmspace.h>
00013 
00014 #include "include/menu.h"
00015 
00016 // statics
00017 char Menu::_inbuf[MENU_COMMANDLINE_MAX];
00018 Menu::arg Menu::_argv[MENU_ARGS_MAX + 1];
00019 
00020 // constructor
00021 Menu::Menu(const prog_char *prompt, const Menu::command *commands, uint8_t entries, preprompt ppfunc) :
00022         _prompt(prompt),
00023         _commands(commands),
00024         _entries(entries),
00025         _ppfunc(ppfunc)
00026 {
00027 }
00028 
00029 // run the menu
00030 void
00031 Menu::run(void)
00032 {
00033         uint8_t         len, i, ret;
00034         uint8_t         argc;
00035         int                     c;
00036         char            *s;
00037                 
00038         // loop performing commands
00039         for (;;) {
00040 
00041                 // run the pre-prompt function, if one is defined
00042                 if ((NULL != _ppfunc) && !_ppfunc())
00043                         return;
00044 
00045                 // loop reading characters from the input
00046                 len = 0;
00047                 Serial.printf("%S] ", _prompt);
00048                 for (;;) {
00049                         c = Serial.read();
00050                         if (-1 == c)
00051                                 continue;
00052                         // carriage return -> process command
00053                         if ('\r' == c) {
00054                                 _inbuf[len] = '\0';
00055                                 Serial.write('\r');
00056                                 Serial.write('\n');
00057                                 break;
00058                         }
00059                         // backspace
00060                         if ('\b' == c) {
00061                                 if (len > 0) {
00062                                         len--;
00063                                         Serial.write('\b');
00064                                         Serial.write(' ');
00065                                         Serial.write('\b');
00066                                         continue;
00067                                 }
00068                         }
00069                         // printable character
00070                         if (isprint(c) && (len < (MENU_COMMANDLINE_MAX - 1))) {
00071                                 _inbuf[len++] = c;
00072                                 Serial.write((char)c);
00073                                 continue;
00074                         }
00075                 }
00076                 
00077                 // split the input line into tokens
00078                 argc = 0;
00079                 _argv[argc++].str = strtok_r(_inbuf, " ", &s);
00080                 // XXX should an empty line by itself back out of the current menu?
00081                 while (argc <= MENU_ARGS_MAX) {
00082                         _argv[argc].str = strtok_r(NULL, " ", &s);
00083                         if ('\0' == _argv[argc].str)
00084                                 break;
00085                         _argv[argc].i = atol(_argv[argc].str);
00086                         _argv[argc].f = atof(_argv[argc].str);  // calls strtod, > 700B !
00087                         argc++;
00088                 }
00089 
00090                 // populate arguments that have not been specified with "" and 0
00091                 // this is safer than NULL in the case where commands may look
00092                 // without testing argc
00093                 i = argc;
00094                 while (i <= MENU_ARGS_MAX) {
00095                         _argv[i].str = "";
00096                         _argv[i].i = 0;
00097                         _argv[i].f = 0;
00098                         i++;
00099                 }
00100                         
00101                 // look for a command matching the first word (note that it may be empty)
00102                 for (i = 0; i < _entries; i++) {
00103                         if (!strcasecmp_P(_argv[0].str, _commands[i].command)) {
00104                                 ret = _call(i, argc);
00105                                 if (-2 == ret)
00106                                         return;
00107                                 break;
00108                         }
00109                 }
00110 
00111                 // implicit commands
00112                 if (i == _entries) {
00113                         if (!strcmp(_argv[0].str, "?") || (!strcasecmp_P(_argv[0].str, PSTR("help")))) {
00114                                 _help();
00115                         } else if (!strcasecmp_P(_argv[0].str, PSTR("exit"))) {
00116                                 return;
00117                         }
00118                 }
00119         }                       
00120 }
00121 
00122 // display the list of commands in response to the 'help' command
00123 void
00124 Menu::_help(void)
00125 {
00126         int             i;
00127         
00128         Serial.println("Commands:");
00129         for (i = 0; i < _entries; i++)
00130                 Serial.printf("  %S\n", _commands[i].command);
00131 }
00132 
00133 // run the n'th command in the menu
00134 int8_t
00135 Menu::_call(uint8_t n, uint8_t argc)
00136 {
00137         func            fn;
00138 
00139         fn = (func)pgm_read_word(&_commands[n].func);
00140         return(fn(argc, &_argv[0]));
00141 }

Generated for ArduPilot Libraries by doxygen