Andy Little
/
userMenu
Simple Menu for user config at startup etc
userMenu.cpp@0:694155408133, 2020-03-29 (annotated)
- Committer:
- skyscraper
- Date:
- Sun Mar 29 16:00:36 2020 +0000
- Revision:
- 0:694155408133
Initial commit
Who changed what in which revision?
User | Revision | Line number | New 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 | } |