Simple Menu for user config at startup etc

Committer:
skyscraper
Date:
Sun Mar 29 16:00:36 2020 +0000
Revision:
0:694155408133
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
skyscraper 0:694155408133 1
skyscraper 0:694155408133 2
skyscraper 0:694155408133 3 //
skyscraper 0:694155408133 4 /// Simple commandline menu system.
skyscraper 0:694155408133 5 /// Originally from https://code.google.com/archive/p/ardu-imu/downloads
skyscraper 0:694155408133 6
skyscraper 0:694155408133 7
skyscraper 0:694155408133 8 #include <ctype.h>
skyscraper 0:694155408133 9 #include <string.h>
skyscraper 0:694155408133 10
skyscraper 0:694155408133 11 #include <iostream>
skyscraper 0:694155408133 12
skyscraper 0:694155408133 13 #include "userMenu.h"
skyscraper 0:694155408133 14
skyscraper 0:694155408133 15 // statics
skyscraper 0:694155408133 16 char Menu::_inbuf[commandlineLenMax];
skyscraper 0:694155408133 17 Menu::arg Menu::_argv[argsLenMax+ 1];
skyscraper 0:694155408133 18
skyscraper 0:694155408133 19 // constructor
skyscraper 0:694155408133 20 Menu::Menu(const char*prompt, const Menu::command *commands, uint8_t entries, preprompt ppfunc) :
skyscraper 0:694155408133 21 _prompt(prompt),
skyscraper 0:694155408133 22 _commands(commands),
skyscraper 0:694155408133 23 _entries(entries),
skyscraper 0:694155408133 24 _ppfunc(ppfunc)
skyscraper 0:694155408133 25 {
skyscraper 0:694155408133 26 }
skyscraper 0:694155408133 27 namespace {
skyscraper 0:694155408133 28 // could be a eeprom option
skyscraper 0:694155408133 29 // If terminal at PC end already echoes then don't echo here
skyscraper 0:694155408133 30 static constexpr bool serial_echo = false;
skyscraper 0:694155408133 31 }
skyscraper 0:694155408133 32
skyscraper 0:694155408133 33 // run the menu
skyscraper 0:694155408133 34 void
skyscraper 0:694155408133 35 Menu::run(void)
skyscraper 0:694155408133 36 {
skyscraper 0:694155408133 37 for (;;) {
skyscraper 0:694155408133 38 if(_ppfunc != nullptr){
skyscraper 0:694155408133 39 // run the included pre-prompt function
skyscraper 0:694155408133 40 if ( !_ppfunc()){
skyscraper 0:694155408133 41 return;
skyscraper 0:694155408133 42 }
skyscraper 0:694155408133 43 }
skyscraper 0:694155408133 44 // Serial.print(_prompt);
skyscraper 0:694155408133 45 // Serial.print(']');
skyscraper 0:694155408133 46 std::cout << _prompt << ']';
skyscraper 0:694155408133 47 for (uint8_t len = 0,done = false; done == false;) {
skyscraper 0:694155408133 48 // int c = -1;// = Serial.read();
skyscraper 0:694155408133 49 int c = std::cin.get();
skyscraper 0:694155408133 50 switch(c) {
skyscraper 0:694155408133 51 case -1:
skyscraper 0:694155408133 52 //eof
skyscraper 0:694155408133 53 break;
skyscraper 0:694155408133 54 case '\r':
skyscraper 0:694155408133 55 // carriage return -> process command
skyscraper 0:694155408133 56 _inbuf[len] = '\0';
skyscraper 0:694155408133 57 if ( serial_echo){
skyscraper 0:694155408133 58 // Serial.write('\r');
skyscraper 0:694155408133 59 // Serial.write('\n');
skyscraper 0:694155408133 60 std::cout << '\r';
skyscraper 0:694155408133 61 }
skyscraper 0:694155408133 62 std::cout << '\n';
skyscraper 0:694155408133 63 done = true;
skyscraper 0:694155408133 64 break;
skyscraper 0:694155408133 65 case '\b':
skyscraper 0:694155408133 66 // backspace
skyscraper 0:694155408133 67 if (len > 0) {
skyscraper 0:694155408133 68 len--;
skyscraper 0:694155408133 69 if ( serial_echo){
skyscraper 0:694155408133 70 // Serial.write('\b');
skyscraper 0:694155408133 71 std::cout << '\b';
skyscraper 0:694155408133 72 }
skyscraper 0:694155408133 73 // Serial.write(' ');
skyscraper 0:694155408133 74 // Serial.write('\b');
skyscraper 0:694155408133 75 std::cout << " \b";
skyscraper 0:694155408133 76 }
skyscraper 0:694155408133 77 break;
skyscraper 0:694155408133 78 default:
skyscraper 0:694155408133 79 // printable character
skyscraper 0:694155408133 80 if (isprint(c) && (len < (commandlineLenMax - 1))) {
skyscraper 0:694155408133 81 _inbuf[len++] = c;
skyscraper 0:694155408133 82 if(serial_echo){
skyscraper 0:694155408133 83 // Serial.write((char)c);
skyscraper 0:694155408133 84 std::cout << static_cast<char>(c);
skyscraper 0:694155408133 85 }
skyscraper 0:694155408133 86 }
skyscraper 0:694155408133 87 break;
skyscraper 0:694155408133 88 }
skyscraper 0:694155408133 89 }
skyscraper 0:694155408133 90 // split the input line into tokens
skyscraper 0:694155408133 91 uint8_t argc = 0;
skyscraper 0:694155408133 92 char *s = nullptr;
skyscraper 0:694155408133 93 _argv[argc++].str = strtok_r(_inbuf, " ", &s);
skyscraper 0:694155408133 94 // XXX should an empty line by itself back out of the current menu?
skyscraper 0:694155408133 95 for ( ;argc <= argsLenMax; ++argc) {
skyscraper 0:694155408133 96 _argv[argc].str = strtok_r(nullptr, " ", &s);
skyscraper 0:694155408133 97 if (_argv[argc].str != nullptr){
skyscraper 0:694155408133 98 _argv[argc].i = atol(_argv[argc].str);
skyscraper 0:694155408133 99 _argv[argc].f = atof(_argv[argc].str); // calls strtod, > 700B !
skyscraper 0:694155408133 100 }else{
skyscraper 0:694155408133 101 break;
skyscraper 0:694155408133 102 }
skyscraper 0:694155408133 103 }
skyscraper 0:694155408133 104
skyscraper 0:694155408133 105 if (_argv[0].str == nullptr) {
skyscraper 0:694155408133 106 continue;
skyscraper 0:694155408133 107 }
skyscraper 0:694155408133 108
skyscraper 0:694155408133 109 // populate arguments that have not been specified with "" and 0
skyscraper 0:694155408133 110 // this is safer than NULL in the case where commands may look
skyscraper 0:694155408133 111 // without testing argc
skyscraper 0:694155408133 112 for(int i = argc; i <= argsLenMax;++i){
skyscraper 0:694155408133 113 _argv[i].str = nullptr;
skyscraper 0:694155408133 114 _argv[i].i = 0;
skyscraper 0:694155408133 115 _argv[i].f = 0;
skyscraper 0:694155408133 116 }
skyscraper 0:694155408133 117
skyscraper 0:694155408133 118 bool cmd_found = false;
skyscraper 0:694155408133 119 // look for a command matching the first word (note that it may be empty)
skyscraper 0:694155408133 120 int i = 0;
skyscraper 0:694155408133 121 for (; i < _entries; ++i) {
skyscraper 0:694155408133 122 if (!strcmp(_argv[0].str, _commands[i].command)) {
skyscraper 0:694155408133 123 int8_t ret = _call(i, argc);
skyscraper 0:694155408133 124 cmd_found=true;
skyscraper 0:694155408133 125 if (-2 == ret){
skyscraper 0:694155408133 126 return;
skyscraper 0:694155408133 127 }
skyscraper 0:694155408133 128 break;
skyscraper 0:694155408133 129 }
skyscraper 0:694155408133 130 }
skyscraper 0:694155408133 131
skyscraper 0:694155408133 132 // implicit commands
skyscraper 0:694155408133 133 if (i == _entries) {
skyscraper 0:694155408133 134 if (!strcmp(_argv[0].str, "?") || (!strcmp(_argv[0].str, "help"))) {
skyscraper 0:694155408133 135 _help();
skyscraper 0:694155408133 136 cmd_found=true;
skyscraper 0:694155408133 137 } else if (!strcmp(_argv[0].str, "exit")) {
skyscraper 0:694155408133 138 return;
skyscraper 0:694155408133 139 }
skyscraper 0:694155408133 140 }
skyscraper 0:694155408133 141
skyscraper 0:694155408133 142 if (cmd_found == false){
skyscraper 0:694155408133 143 //Serial.println("Invalid command, type 'help'");
skyscraper 0:694155408133 144 std::cout << "Invalid command, type 'help'\n" ;
skyscraper 0:694155408133 145 }
skyscraper 0:694155408133 146 }
skyscraper 0:694155408133 147 }
skyscraper 0:694155408133 148
skyscraper 0:694155408133 149 // display the list of commands in response to the 'help' command
skyscraper 0:694155408133 150 void
skyscraper 0:694155408133 151 Menu::_help(void)
skyscraper 0:694155408133 152 {
skyscraper 0:694155408133 153 // Serial.println("Commands:");
skyscraper 0:694155408133 154 std::cout << "Commands:\n";
skyscraper 0:694155408133 155 for (int i = 0; i < _entries; i++){
skyscraper 0:694155408133 156 std::cout << " " << _commands[i].command << '\n';
skyscraper 0:694155408133 157 // Serial.print(" ");
skyscraper 0:694155408133 158 // Serial.print(_commands[i].command);
skyscraper 0:694155408133 159 // Serial.print('\n');
skyscraper 0:694155408133 160 }
skyscraper 0:694155408133 161 }
skyscraper 0:694155408133 162
skyscraper 0:694155408133 163 // run the n'th command in the menu
skyscraper 0:694155408133 164 int8_t
skyscraper 0:694155408133 165 Menu::_call(uint8_t n, uint8_t argc)
skyscraper 0:694155408133 166 {
skyscraper 0:694155408133 167 return _commands[n].func(argc, &_argv[0]);
skyscraper 0:694155408133 168 }