/// @file menu.h /// @brief Simple commandline menu subsystem. /// @discussion /// 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. #pragma once #include #include #define MENU_COMMANDLINE_MAX 32 ///< maximum input line length #define MENU_ARGS_MAX 3 ///< 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 /// /// 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 { 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 MENU_ARGS_MAX. /// @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. /// FUNCTOR_TYPEDEF(func, int8_t, uint8_t, const struct arg *); static void set_port(AP_HAL::BetterStream *port) { _port = port; } /// 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. /// FUNCTOR_TYPEDEF(preprompt, bool); /// 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[MENU_COMMAND_MAX]; /// The function to call when the command is received. /// /// The argc argument will be at least 1, and no more than /// MENU_ARGS_MAX. The argv array will be populated with /// arguments typed/received up to MENU_ARGS_MAX. 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. /// FUNCTOR_DECLARE(func, int8_t, uint8_t, const struct arg *); }; /// 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. /// @param entries The number of entries in the menu. /// Menu(const char *prompt, const struct command *commands, uint8_t entries, preprompt ppfunc = 0); /// set command line length limit void set_limits(uint8_t commandline_max, uint8_t args_max); /// menu runner void run(void); /// check for new input on the port. Can be used /// to allow for the menu to operate asynchronously /// this will return true if the user asked to exit the menu bool check_input(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; ///< input buffer static arg *_argv; ///< arguments uint8_t _commandline_max; uint8_t _args_max; // allocate inbuf and args buffers void _allocate_buffers(void); // number of characters read so far from the port uint8_t _input_len; // check for next input character bool _check_for_input(void); // run one full entered command. // return true if the menu loop should exit bool _run_command(bool prompt_on_enter); void _display_prompt(); // port to run on static AP_HAL::BetterStream *_port; }; /// 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 const char __menu_name__ ## name[] = prompt; \ static Menu name(__menu_name__ ## name, commands, ARRAY_SIZE(commands)) #define MENU2(name, prompt, commands, preprompt) \ static const char __menu_name__ ## name[] = prompt; \ static Menu name(__menu_name__ ## name, commands, ARRAY_SIZE(commands), preprompt)