You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
5.9 KiB
279 lines
5.9 KiB
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- |
|
|
|
// |
|
// Simple commandline menu system. |
|
// |
|
|
|
#include <AP_Common/AP_Common.h> |
|
#include <AP_Progmem/AP_Progmem.h> |
|
#include <AP_HAL/AP_HAL.h> |
|
|
|
#include <stdlib.h> |
|
#include <ctype.h> |
|
#include <string.h> |
|
|
|
#include "AP_Menu.h" |
|
|
|
extern const AP_HAL::HAL& hal; |
|
|
|
// statics |
|
char *Menu::_inbuf; |
|
Menu::arg *Menu::_argv; |
|
AP_HAL::BetterStream *Menu::_port; |
|
|
|
|
|
// constructor |
|
Menu::Menu(const char *prompt, const Menu::command *commands, uint8_t entries, preprompt ppfunc) : |
|
_prompt(prompt), |
|
_commands(commands), |
|
_entries(entries), |
|
_ppfunc(ppfunc), |
|
_commandline_max(MENU_COMMANDLINE_MAX), |
|
_args_max(MENU_ARGS_MAX) |
|
{ |
|
// the buffers are initially NULL, then they are allocated on |
|
// first use |
|
_inbuf = NULL; |
|
_argv = NULL; |
|
} |
|
|
|
/** |
|
check for another input byte on the port and accumulate |
|
return true if we have a full line ready to process |
|
*/ |
|
bool |
|
Menu::_check_for_input(void) |
|
{ |
|
if (_port->available() <= 0) { |
|
return false; |
|
} |
|
|
|
// loop reading characters from the input |
|
int c = _port->read(); |
|
|
|
// carriage return -> process command |
|
if ('\r' == c || '\n' == c) { |
|
_inbuf[_input_len] = '\0'; |
|
_port->write('\r'); |
|
_port->write('\n'); |
|
// we have a full line to process |
|
return true; |
|
} |
|
|
|
// backspace |
|
if ('\b' == c) { |
|
if (_input_len > 0) { |
|
_input_len--; |
|
_port->write('\b'); |
|
_port->write(' '); |
|
_port->write('\b'); |
|
return false; |
|
} |
|
} |
|
|
|
// printable character |
|
if (isprint(c) && (_input_len < (_commandline_max - 1))) { |
|
_inbuf[_input_len++] = c; |
|
_port->write((char)c); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
// display the prompt |
|
void |
|
Menu::_display_prompt(void) |
|
{ |
|
_port->printf("%s] ", _prompt); |
|
} |
|
|
|
// run the menu |
|
bool |
|
Menu::_run_command(bool prompt_on_enter) |
|
{ |
|
int8_t ret; |
|
uint8_t i; |
|
uint8_t argc; |
|
char *s = NULL; |
|
|
|
_input_len = 0; |
|
|
|
// split the input line into tokens |
|
argc = 0; |
|
s = NULL; |
|
_argv[argc++].str = strtok_r(_inbuf, " ", &s); |
|
|
|
// XXX should an empty line by itself back out of the current menu? |
|
while (argc <= _args_max) { |
|
_argv[argc].str = strtok_r(NULL, " ", &s); |
|
if (_argv[argc].str == NULL || '\0' == _argv[argc].str[0]) |
|
break; |
|
_argv[argc].i = atol(_argv[argc].str); |
|
_argv[argc].f = atof(_argv[argc].str); // calls strtod, > 700B ! |
|
argc++; |
|
} |
|
|
|
if (_argv[0].str == NULL) { |
|
// we got a blank line, re-display the prompt |
|
if (prompt_on_enter) { |
|
_display_prompt(); |
|
} |
|
return false; |
|
} |
|
|
|
// populate arguments that have not been specified with "" and 0 |
|
// this is safer than NULL in the case where commands may look |
|
// without testing argc |
|
i = argc; |
|
while (i <= _args_max) { |
|
_argv[i].str = ""; |
|
_argv[i].i = 0; |
|
_argv[i].f = 0; |
|
i++; |
|
} |
|
|
|
bool cmd_found = false; |
|
// look for a command matching the first word (note that it may be empty) |
|
for (i = 0; i < _entries; i++) { |
|
if (!strcasecmp(_argv[0].str, _commands[i].command)) { |
|
ret = _call(i, argc); |
|
cmd_found=true; |
|
if (-2 == ret) |
|
return true; |
|
break; |
|
} |
|
} |
|
|
|
// implicit commands |
|
if (i == _entries) { |
|
if (!strcmp(_argv[0].str, "?") || (!strcasecmp(_argv[0].str, "help"))) { |
|
_help(); |
|
cmd_found=true; |
|
} else if (!strcasecmp(_argv[0].str, "exit")) { |
|
// exit the menu |
|
return true; |
|
} |
|
} |
|
|
|
if (cmd_found==false) |
|
{ |
|
_port->println("Invalid command, type 'help'"); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
// run the menu |
|
void |
|
Menu::run(void) |
|
{ |
|
if (_port == NULL) { |
|
// default to main serial port |
|
_port = hal.console; |
|
} |
|
|
|
_allocate_buffers(); |
|
|
|
_display_prompt(); |
|
|
|
// loop performing commands |
|
for (;;) { |
|
|
|
// run the pre-prompt function, if one is defined |
|
if (_ppfunc) { |
|
if (!_ppfunc()) |
|
return; |
|
_display_prompt(); |
|
} |
|
|
|
// loop reading characters from the input |
|
_input_len = 0; |
|
|
|
for (;; ) { |
|
if (_check_for_input()) { |
|
break; |
|
} |
|
hal.scheduler->delay(20); |
|
} |
|
|
|
// we have a full command to run |
|
if (_run_command(false)) break; |
|
|
|
_display_prompt(); |
|
} |
|
} |
|
|
|
// check for new user input |
|
bool |
|
Menu::check_input(void) |
|
{ |
|
if (_port == NULL) { |
|
// default to main serial port |
|
_port = hal.console; |
|
} |
|
|
|
_allocate_buffers(); |
|
|
|
if (_check_for_input()) { |
|
return _run_command(true); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
// display the list of commands in response to the 'help' command |
|
void |
|
Menu::_help(void) |
|
{ |
|
int i; |
|
|
|
_port->println("Commands:"); |
|
for (i = 0; i < _entries; i++) { |
|
hal.scheduler->delay(10); |
|
_port->printf(" %s\n", _commands[i].command); |
|
} |
|
} |
|
|
|
// run the n'th command in the menu |
|
int8_t |
|
Menu::_call(uint8_t n, uint8_t argc) |
|
{ |
|
func fn; |
|
|
|
pgm_read_block(&_commands[n].func, &fn, sizeof(fn)); |
|
return(fn(argc, &_argv[0])); |
|
} |
|
|
|
/** |
|
set limits on max args and command line length |
|
*/ |
|
void |
|
Menu::set_limits(uint8_t commandline_max, uint8_t args_max) |
|
{ |
|
if (_inbuf != NULL) { |
|
delete[] _inbuf; |
|
_inbuf = NULL; |
|
} |
|
if (_argv != NULL) { |
|
delete[] _argv; |
|
_argv = NULL; |
|
} |
|
// remember limits, the buffers will be allocated by allocate_buffers() |
|
_commandline_max = commandline_max; |
|
_args_max = args_max; |
|
} |
|
|
|
void |
|
Menu::_allocate_buffers(void) |
|
{ |
|
/* only allocate if the buffers are NULL */ |
|
if (_inbuf == NULL) { |
|
_inbuf = new char[_commandline_max]; |
|
memset(_inbuf, 0, _commandline_max); |
|
} |
|
if (_argv == NULL) { |
|
_argv = new arg[_args_max+1]; |
|
memset(_argv, 0, (_args_max+1) * sizeof(_argv[0])); |
|
} |
|
}
|
|
|