2010-09-24 02:50:02 -03:00
|
|
|
//
|
|
|
|
// Simple commandline menu system.
|
2015-12-23 10:50:17 -04:00
|
|
|
#include "AP_Menu.h"
|
2010-09-24 02:50:02 -03:00
|
|
|
|
|
|
|
#include <ctype.h>
|
2015-12-23 10:50:17 -04:00
|
|
|
#include <stdlib.h>
|
2010-09-24 02:50:02 -03:00
|
|
|
#include <string.h>
|
|
|
|
|
2015-12-23 10:50:17 -04:00
|
|
|
#include <AP_Common/AP_Common.h>
|
|
|
|
#include <AP_HAL/AP_HAL.h>
|
2010-09-24 02:50:02 -03:00
|
|
|
|
2012-11-12 17:12:40 -04:00
|
|
|
extern const AP_HAL::HAL& hal;
|
|
|
|
|
2010-09-24 02:50:02 -03:00
|
|
|
// statics
|
2013-11-05 18:10:31 -04:00
|
|
|
char *Menu::_inbuf;
|
|
|
|
Menu::arg *Menu::_argv;
|
2012-11-12 17:12:40 -04:00
|
|
|
AP_HAL::BetterStream *Menu::_port;
|
2012-11-21 01:17:16 -04:00
|
|
|
|
2010-09-24 02:50:02 -03:00
|
|
|
|
|
|
|
// constructor
|
2015-10-26 08:25:44 -03:00
|
|
|
Menu::Menu(const char *prompt, const Menu::command *commands, uint8_t entries, preprompt ppfunc) :
|
2011-10-28 15:43:43 -03:00
|
|
|
_prompt(prompt),
|
|
|
|
_commands(commands),
|
|
|
|
_entries(entries),
|
2013-11-05 18:10:31 -04:00
|
|
|
_ppfunc(ppfunc),
|
|
|
|
_commandline_max(MENU_COMMANDLINE_MAX),
|
|
|
|
_args_max(MENU_ARGS_MAX)
|
2010-09-24 02:50:02 -03:00
|
|
|
{
|
2016-10-30 02:24:21 -03:00
|
|
|
// the buffers are initially nullptr, then they are allocated on
|
2013-11-05 18:10:31 -04:00
|
|
|
// first use
|
2016-10-30 02:24:21 -03:00
|
|
|
_inbuf = nullptr;
|
|
|
|
_argv = nullptr;
|
2010-09-24 02:50:02 -03:00
|
|
|
}
|
|
|
|
|
2013-11-05 18:37:21 -04:00
|
|
|
/**
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-11-05 19:16:14 -04:00
|
|
|
// display the prompt
|
|
|
|
void
|
|
|
|
Menu::_display_prompt(void)
|
|
|
|
{
|
2015-10-26 10:08:19 -03:00
|
|
|
_port->printf("%s] ", _prompt);
|
2013-11-05 19:16:14 -04:00
|
|
|
}
|
2013-11-05 18:37:21 -04:00
|
|
|
|
2010-09-24 02:50:02 -03:00
|
|
|
// run the menu
|
2013-11-05 18:37:21 -04:00
|
|
|
bool
|
2013-11-05 19:16:14 -04:00
|
|
|
Menu::_run_command(bool prompt_on_enter)
|
2010-09-24 02:50:02 -03:00
|
|
|
{
|
2012-08-17 03:18:11 -03:00
|
|
|
int8_t ret;
|
2013-11-05 18:37:21 -04:00
|
|
|
uint8_t i;
|
2012-08-17 03:18:11 -03:00
|
|
|
uint8_t argc;
|
2016-10-30 02:24:21 -03:00
|
|
|
char *s = nullptr;
|
2011-10-28 15:43:43 -03:00
|
|
|
|
2013-11-05 18:37:21 -04:00
|
|
|
_input_len = 0;
|
|
|
|
|
|
|
|
// split the input line into tokens
|
|
|
|
argc = 0;
|
2016-10-30 02:24:21 -03:00
|
|
|
s = nullptr;
|
2013-11-05 18:37:21 -04:00
|
|
|
_argv[argc++].str = strtok_r(_inbuf, " ", &s);
|
|
|
|
|
|
|
|
// XXX should an empty line by itself back out of the current menu?
|
|
|
|
while (argc <= _args_max) {
|
2016-10-30 02:24:21 -03:00
|
|
|
_argv[argc].str = strtok_r(nullptr, " ", &s);
|
|
|
|
if (_argv[argc].str == nullptr || '\0' == _argv[argc].str[0])
|
2013-11-05 18:37:21 -04:00
|
|
|
break;
|
|
|
|
_argv[argc].i = atol(_argv[argc].str);
|
2019-10-27 17:37:36 -03:00
|
|
|
_argv[argc].f = strtof(_argv[argc].str, NULL);
|
2013-11-05 18:37:21 -04:00
|
|
|
argc++;
|
|
|
|
}
|
|
|
|
|
2016-10-30 02:24:21 -03:00
|
|
|
if (_argv[0].str == nullptr) {
|
2013-11-05 18:37:21 -04:00
|
|
|
// we got a blank line, re-display the prompt
|
2013-11-05 19:16:14 -04:00
|
|
|
if (prompt_on_enter) {
|
|
|
|
_display_prompt();
|
|
|
|
}
|
2013-11-05 18:37:21 -04:00
|
|
|
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++) {
|
2015-10-25 13:22:16 -03:00
|
|
|
if (!strcasecmp(_argv[0].str, _commands[i].command)) {
|
2013-11-05 18:37:21 -04:00
|
|
|
ret = _call(i, argc);
|
|
|
|
cmd_found=true;
|
|
|
|
if (-2 == ret)
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// implicit commands
|
|
|
|
if (i == _entries) {
|
2015-10-25 13:22:16 -03:00
|
|
|
if (!strcmp(_argv[0].str, "?") || (!strcasecmp(_argv[0].str, "help"))) {
|
2013-11-05 18:37:21 -04:00
|
|
|
_help();
|
|
|
|
cmd_found=true;
|
2015-10-25 13:22:16 -03:00
|
|
|
} else if (!strcasecmp(_argv[0].str, "exit")) {
|
2013-11-05 18:37:21 -04:00
|
|
|
// exit the menu
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd_found==false)
|
|
|
|
{
|
2017-01-21 00:56:01 -04:00
|
|
|
_port->printf("Invalid command, type 'help'\n");
|
2013-11-05 18:37:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// run the menu
|
|
|
|
void
|
|
|
|
Menu::run(void)
|
|
|
|
{
|
2016-10-30 02:24:21 -03:00
|
|
|
if (_port == nullptr) {
|
2012-11-21 01:17:16 -04:00
|
|
|
// default to main serial port
|
2012-11-12 17:12:40 -04:00
|
|
|
_port = hal.console;
|
2012-11-21 01:17:16 -04:00
|
|
|
}
|
|
|
|
|
2013-11-05 18:10:31 -04:00
|
|
|
_allocate_buffers();
|
|
|
|
|
2013-11-05 19:16:14 -04:00
|
|
|
_display_prompt();
|
|
|
|
|
2011-10-28 15:43:43 -03:00
|
|
|
// loop performing commands
|
2012-11-12 17:12:40 -04:00
|
|
|
for (;;) {
|
2011-10-28 15:43:43 -03:00
|
|
|
|
|
|
|
// run the pre-prompt function, if one is defined
|
2015-05-24 07:41:02 -03:00
|
|
|
if (_ppfunc) {
|
2013-11-05 19:16:14 -04:00
|
|
|
if (!_ppfunc())
|
|
|
|
return;
|
|
|
|
_display_prompt();
|
|
|
|
}
|
2011-10-28 15:43:43 -03:00
|
|
|
|
|
|
|
// loop reading characters from the input
|
2013-11-05 18:37:21 -04:00
|
|
|
_input_len = 0;
|
|
|
|
|
2012-08-17 03:18:11 -03:00
|
|
|
for (;; ) {
|
2013-11-05 18:37:21 -04:00
|
|
|
if (_check_for_input()) {
|
2011-10-28 15:43:43 -03:00
|
|
|
break;
|
|
|
|
}
|
2013-11-05 18:37:21 -04:00
|
|
|
hal.scheduler->delay(20);
|
2011-10-28 15:43:43 -03:00
|
|
|
}
|
|
|
|
|
2013-11-05 18:37:21 -04:00
|
|
|
// we have a full command to run
|
2013-11-05 19:16:14 -04:00
|
|
|
if (_run_command(false)) break;
|
|
|
|
|
|
|
|
_display_prompt();
|
2013-11-05 18:37:21 -04:00
|
|
|
}
|
|
|
|
}
|
2011-10-28 15:43:43 -03:00
|
|
|
|
2013-11-05 18:37:21 -04:00
|
|
|
// check for new user input
|
|
|
|
bool
|
|
|
|
Menu::check_input(void)
|
|
|
|
{
|
2016-10-30 02:24:21 -03:00
|
|
|
if (_port == nullptr) {
|
2013-11-05 18:37:21 -04:00
|
|
|
// default to main serial port
|
|
|
|
_port = hal.console;
|
|
|
|
}
|
2011-10-28 15:43:43 -03:00
|
|
|
|
2013-11-05 18:37:21 -04:00
|
|
|
_allocate_buffers();
|
2012-01-11 03:41:20 -04:00
|
|
|
|
2013-11-05 18:37:21 -04:00
|
|
|
if (_check_for_input()) {
|
2013-11-05 19:16:14 -04:00
|
|
|
return _run_command(true);
|
2013-11-05 18:37:21 -04:00
|
|
|
}
|
2012-01-11 03:41:20 -04:00
|
|
|
|
2013-11-05 18:37:21 -04:00
|
|
|
return false;
|
2010-09-24 02:50:02 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
// display the list of commands in response to the 'help' command
|
|
|
|
void
|
|
|
|
Menu::_help(void)
|
|
|
|
{
|
2012-08-17 03:18:11 -03:00
|
|
|
int i;
|
2011-10-28 15:43:43 -03:00
|
|
|
|
2017-01-21 00:56:01 -04:00
|
|
|
_port->printf("Commands:\n");
|
2012-11-21 01:17:16 -04:00
|
|
|
for (i = 0; i < _entries; i++) {
|
2012-11-12 17:12:40 -04:00
|
|
|
hal.scheduler->delay(10);
|
2015-10-26 10:08:19 -03:00
|
|
|
_port->printf(" %s\n", _commands[i].command);
|
2012-11-21 01:17:16 -04:00
|
|
|
}
|
2010-09-24 02:50:02 -03:00
|
|
|
}
|
2010-09-24 03:18:59 -03:00
|
|
|
|
|
|
|
// run the n'th command in the menu
|
2010-09-25 15:02:41 -03:00
|
|
|
int8_t
|
2010-09-24 03:18:59 -03:00
|
|
|
Menu::_call(uint8_t n, uint8_t argc)
|
|
|
|
{
|
2015-12-23 10:50:17 -04:00
|
|
|
return _commands[n].func(argc, &_argv[0]);
|
2010-09-24 03:18:59 -03:00
|
|
|
}
|
2013-11-05 18:10:31 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
set limits on max args and command line length
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
Menu::set_limits(uint8_t commandline_max, uint8_t args_max)
|
|
|
|
{
|
2016-10-30 02:24:21 -03:00
|
|
|
if (_inbuf != nullptr) {
|
2013-11-05 18:10:31 -04:00
|
|
|
delete[] _inbuf;
|
2016-10-30 02:24:21 -03:00
|
|
|
_inbuf = nullptr;
|
2013-11-05 18:10:31 -04:00
|
|
|
}
|
2016-10-30 02:24:21 -03:00
|
|
|
if (_argv != nullptr) {
|
2013-11-05 18:10:31 -04:00
|
|
|
delete[] _argv;
|
2016-10-30 02:24:21 -03:00
|
|
|
_argv = nullptr;
|
2013-11-05 18:10:31 -04:00
|
|
|
}
|
|
|
|
// remember limits, the buffers will be allocated by allocate_buffers()
|
|
|
|
_commandline_max = commandline_max;
|
|
|
|
_args_max = args_max;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Menu::_allocate_buffers(void)
|
|
|
|
{
|
2016-10-30 02:24:21 -03:00
|
|
|
/* only allocate if the buffers are nullptr */
|
|
|
|
if (_inbuf == nullptr) {
|
2013-11-05 18:10:31 -04:00
|
|
|
_inbuf = new char[_commandline_max];
|
2013-11-05 18:12:00 -04:00
|
|
|
memset(_inbuf, 0, _commandline_max);
|
2013-11-05 18:10:31 -04:00
|
|
|
}
|
2016-10-30 02:24:21 -03:00
|
|
|
if (_argv == nullptr) {
|
2013-11-05 18:10:31 -04:00
|
|
|
_argv = new arg[_args_max+1];
|
2013-11-05 18:12:00 -04:00
|
|
|
memset(_argv, 0, (_args_max+1) * sizeof(_argv[0]));
|
2013-11-05 18:10:31 -04:00
|
|
|
}
|
|
|
|
}
|