Andy Little
/
userMenu
Simple Menu for user config at startup etc
Diff: userMenu.h
- Revision:
- 0:694155408133
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userMenu.h Sun Mar 29 16:00:36 2020 +0000 @@ -0,0 +1,151 @@ +/// +/// @file userMenu.h +/// @brief Simple commandline menu subsystem. +/// @discussion +/// Menu derived from old version of ArduPilot, used in ArduIMU.V3 +/// Originally from https://code.google.com/archive/p/ardu-imu/downloads +/// +/// The Menu class implements a simple CLI that accepts commands typed by +/// the user, and passes the arguments to those commands to a function +/// defined as handing the command. +/// +/// Commands are defined in an array of Menu::command structures passed +/// to the constructor. Each entry in the array defines one command. +/// +/// Arguments passed to the handler function are pre-converted to both +/// long and float for convenience. + +#ifndef SKYSCRAPER_MBED_USER_MENU_H_INCLUDED +#define SKYSCRAPER_MBED_USER_MENU_H_INCLUDED + +#include <inttypes.h> + +//#define MENU_COMMANDLINE_MAX 32 +//#define MENU_ARGS_MAX 4 +//#define MENU_COMMAND_MAX 14 + +/// Class defining and handling one menu tree +class Menu { +public: + + + static constexpr uint8_t commandlineLenMax = 32U; ///< maximum input line length + static constexpr uint8_t argsLenMax = 4U; ///< maximum number of arguments + static constexpr uint8_t commandNameLenMax = 14; ///< maximum length of a command name + /// argument passed to a menu function + /// + /// Space-delimited arguments are parsed from the commandline and + /// separated into these structures. + /// + /// If the argument cannot be parsed as a float or a long, the value + /// of f or i respectively is undefined. You should range-check + /// the inputs to your function. + /// + struct arg { + arg(): str{nullptr}, i{0},f{0.0}{} + 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 + /// + /// Functions called by menu array entries are expected to be of this + /// type. + /// + /// @param argc The number of valid arguments, including the + /// name of the command in argv[0]. Will never be + /// more than argsLenMax. + /// @param argv Pointer to an array of Menu::arg structures + /// detailing any optional arguments given to the + /// command. argv[0] is always the name of the + /// command, so that the same function can be used + /// to handle more than one command. + /// + typedef int8_t (*func)(uint8_t argc, const struct arg *argv); + + /// menu pre-prompt function + /// + /// Called immediately before waiting for the user to type a command; can be + /// used to display help text or status, for example. + /// + /// If this function returns false, the menu exits. + /// + typedef bool (*preprompt)(void); + + /// menu command description + /// + struct command { + /// Name of the command, as typed or received. + /// Command names are limited in size to keep this structure compact. + /// + const char command[commandNameLenMax]; + + /// The function to call when the command is received. + /// + /// The argc argument will be at least 1, and no more than + /// argsLenMax. The argv array will be populated with + /// arguments typed/received up to argsLenMax. The command + /// name will always be in argv[0]. + /// + /// Commands may return -2 to cause the menu itself to exit. + /// The "?", "help" and "exit" commands are always defined, but + /// can be overridden by explicit entries in the command array. + /// + int8_t (*func)(uint8_t argc, const struct arg *argv); ///< callback function + }; + + /// constructor + /// + /// Note that you should normally not call the constructor directly. Use + /// the MENU and MENU2 macros defined below. + /// + /// @param prompt The prompt to be displayed with this menu. + /// @param commands An array of ::command structures in program memory (PROGMEM). + /// @param entries The number of entries in the menu. + /// + Menu(const char *prompt, const struct command *commands, uint8_t entries, preprompt ppfunc = 0); + + /// menu runner + void run(void); + +private: + /// Implements the default 'help' command. + /// + void _help(void); ///< implements the 'help' command + + /// calls the function for the n'th menu item + /// + /// @param n Index for the menu item to call + /// @param argc Number of arguments prepared for the menu item + /// + int8_t _call(uint8_t n, uint8_t argc); + + const char *_prompt; ///< prompt to display + const command *_commands; ///< array of commands + const uint8_t _entries; ///< size of the menu + const preprompt _ppfunc; ///< optional pre-prompt action + + static char _inbuf[commandlineLenMax]; ///< input buffer + static arg _argv[argsLenMax+ 1]; ///< arguments +}; + +/// Macros used to define a menu. +/// +/// The commands argument should be an arary of Menu::command structures, one +/// per command name. The array does not need to be terminated with any special +/// record. +/// +/// Use name.run() to run the menu. +/// +/// The MENU2 macro supports the optional pre-prompt printing function. +/// +#define MENU(name, prompt, commands) \ + static constexpr char __menu_name__ ##name[] = prompt; \ + static Menu name(__menu_name__ ##name, commands, sizeof(commands) / sizeof(commands[0])) + +#define MENU2(name, prompt, commands, preprompt) \ + static constexpr char __menu_name__ ##name[] = prompt; \ + static Menu name(__menu_name__ ##name, commands, sizeof(commands) / sizeof(commands[0]), preprompt) + +#endif