Add a simple commandline menu library.

The library has an interface suitable for both interactive and automated use (i.e. Arduino console or GCS operation).


git-svn-id: https://arducopter.googlecode.com/svn/trunk@544 f9c3cf11-9bcb-44bc-f272-b75c42450872
This commit is contained in:
DrZiplok@gmail.com 2010-09-24 05:50:02 +00:00
parent 7721e622aa
commit eb6113e685
4 changed files with 213 additions and 11 deletions

View File

@ -6,23 +6,18 @@
// your option) any later version.
//
#ifndef _AP_COMMON_H
#define _AP_COMMON_H
#include <stdint.h>
///
/// @file AP_Common.h
/// @brief Common definitions and utility routines for the ArduPilot
/// libraries.
///
/// @note For correct operation, all sketches and libraries should
/// include this header *before* any other. In
/// particular, this is critical for things like the
/// FastSerial library, which need the opportunity to
/// override parts of the Arduino infrastructure.
///
#ifndef _AP_COMMON_H
#define _AP_COMMON_H
#include <stdint.h>
#include "include/menu.h" /// simple menu subsystem
////////////////////////////////////////////////////////////////////////////////
/// @name Types

View File

@ -0,0 +1,38 @@
#include <FastSerial.h>
#include <AP_Common.h>
#include <avr/pgmspace.h>
FastSerialPort0(Serial);
int
menu_test(uint8_t argc, const Menu::arg *argv)
{
int i;
Serial.printf("This is a test with %d arguments\n", argc);
for (i = 1; i < argc; i++) {
Serial.printf("%d: int %ld float ", i, argv[i].i);
Serial.println(argv[i].f, 6); // gross
}
}
const struct Menu::command top_menu_commands[] PROGMEM = {
{"test", menu_test},
};
MENU(top, "menu", top_menu_commands);
void
setup(void)
{
Serial.begin(38400);
top.run();
}
void
loop(void)
{
}

View File

@ -0,0 +1,61 @@
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
/// @file menu.h
/// @brief Simple commandline menu subsystem.
#define MENU_COMMANDLINE_MAX 32 ///< maximum input line length
#define MENU_ARGS_MAX 4 ///< maximum number of arguments
#define MENU_COMMAND_MAX 14 ///< maximum size of a command name
/// Class defining and handling one menu tree
class Menu {
public:
/// argument passed to a menu function
struct arg {
const char *str; ///< string form of the argument
long i; ///< integer form of the argument (if a number)
float f; ///< floating point form of the argument (if a number)
};
/// menu command function
///
typedef int (*func)(uint8_t argc, const struct arg *argv);
/// menu command description
///
/// Note that the array of menu commands is expected to be in program
/// memory.
struct command {
const char command[MENU_COMMAND_MAX]; ///< name of the command
int (*func)(uint8_t argc, const struct arg *argv); ///< callback function
};
/// constructor
///
/// @param prompt The prompt to be displayed with this menu.
/// @param commands An array of ::command structures.
/// @param entries The number of entries in the menu.
///
Menu(const char *prompt, const struct command *commands, uint8_t entries);
/// menu runner
void run(void);
private:
void _help(void); ///< implements the 'help' command
const char *_prompt; ///< prompt to display
const command *_commands; ///< array of commands
const uint8_t _entries; ///< size of the menu
static char _inbuf[MENU_COMMANDLINE_MAX]; ///< input buffer
static arg _argv[MENU_ARGS_MAX + 1]; ///< arguments
};
/// Macro used to define a menu.
///
/// Use name.run() to run the menu.
///
#define MENU(name, prompt, commands) \
static const char __menu_name__ ##name[] PROGMEM = prompt; \
static Menu name(__menu_name__ ##name, commands, sizeof(commands) / sizeof(commands[0]))

View File

@ -0,0 +1,108 @@
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
//
// Simple commandline menu system.
//
#include <FastSerial.h>
#include <ctype.h>
#include <string.h>
#include <avr/pgmspace.h>
#include "include/menu.h"
// statics
char Menu::_inbuf[MENU_COMMANDLINE_MAX];
Menu::arg Menu::_argv[MENU_ARGS_MAX + 1];
// constructor
Menu::Menu(const prog_char *prompt, const Menu::command *commands, uint8_t entries) :
_prompt(prompt),
_commands(commands),
_entries(entries)
{
}
// run the menu
void
Menu::run(void)
{
uint8_t len, i, ret;
uint8_t argc;
int c;
func fn;
// loop performing commands
for (;;) {
// loop reading characters from the input
len = 0;
Serial.printf("%S] ", _prompt);
for (;;) {
c = Serial.read();
if (-1 == c)
continue;
// carriage return -> process command
if ('\r' == c) {
_inbuf[len] = '\0';
Serial.write('\r');
Serial.write('\n');
break;
}
// backspace
if ('\b' == c) {
if (len > 0) {
len--;
Serial.write('\b');
Serial.write(' ');
Serial.write('\b');
continue;
}
}
// printable character
if (isprint(c) && (len < (MENU_COMMANDLINE_MAX - 1))) {
_inbuf[len++] = c;
Serial.write((char)c);
continue;
}
}
// split the input line into tokens
argc = 0;
_argv[argc++].str = strtok(_inbuf, " ");
while (argc <= MENU_ARGS_MAX) {
_argv[argc].str = strtok(NULL, " ");
if ('\0' == _argv[argc].str)
break;
_argv[argc].i = atol(_argv[argc].str);
_argv[argc].f = atof(_argv[argc].str); // calls strtod, > 700B !
argc++;
}
// look for a command matching the first word (note that it may be empty)
for (i = 0; i < _entries; i++) {
if (!strcmp_P(_argv[0].str, _commands[i].command)) {
fn = (func)pgm_read_word(&_commands[i].func);
ret = fn(argc, &_argv[0]);
if (-2 == ret)
return;
}
}
// if the menu doesn't provide more comprehensive help, print the command list
if ((i == _entries) && (!strcmp(_argv[0].str, "?") || (!strcmp_P(_argv[0].str, PSTR("help")))))
_help();
}
}
// display the list of commands in response to the 'help' command
void
Menu::_help(void)
{
int i;
Serial.println("Commands:");
for (i = 0; i < _entries; i++)
Serial.printf(" %S\n", _commands[i].command);
}