Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1702 - 1
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
2
 
3
//
4
// Simple commandline menu system.
5
//
6
 
7
#include <FastSerial.h>
8
#include <AP_Common.h>
9
 
10
#include <ctype.h>
11
#include <string.h>
12
#include <avr/pgmspace.h>
13
 
14
#include "include/menu.h"
15
 
16
// statics
17
char Menu::_inbuf[MENU_COMMANDLINE_MAX];
18
Menu::arg Menu::_argv[MENU_ARGS_MAX + 1];
19
 
20
// constructor
21
Menu::Menu(const prog_char *prompt, const Menu::command *commands, uint8_t entries, preprompt ppfunc) :
22
    _prompt(prompt),
23
    _commands(commands),
24
    _entries(entries),
25
    _ppfunc(ppfunc)
26
{
27
}
28
 
29
// run the menu
30
void
31
Menu::run(void)
32
{
33
    int8_t              ret;
34
    uint8_t             len, i;
35
    uint8_t             argc;
36
    int                 c;
37
    char                *s;
38
 
39
    // loop performing commands
40
    for (;;) {
41
 
42
        // run the pre-prompt function, if one is defined
43
        if ((NULL != _ppfunc) && !_ppfunc())
44
            return;
45
 
46
        // loop reading characters from the input
47
        len = 0;
48
        Serial.printf("%S] ", FPSTR(_prompt));
49
        for (;;) {
50
            c = Serial.read();
51
            if (-1 == c)
52
                continue;
53
            // carriage return -> process command
54
            if ('\r' == c) {
55
                _inbuf[len] = '\0';
56
                Serial.write('\r');
57
                Serial.write('\n');
58
                break;
59
            }
60
            // backspace
61
            if ('\b' == c) {
62
                if (len > 0) {
63
                    len--;
64
                    Serial.write('\b');
65
                    Serial.write(' ');
66
                    Serial.write('\b');
67
                    continue;
68
                }
69
            }
70
            // printable character
71
            if (isprint(c) && (len < (MENU_COMMANDLINE_MAX - 1))) {
72
                _inbuf[len++] = c;
73
                Serial.write((char)c);
74
                continue;
75
            }
76
        }
77
 
78
        // split the input line into tokens
79
        argc = 0;
80
        _argv[argc++].str = strtok_r(_inbuf, " ", &s);
81
        // XXX should an empty line by itself back out of the current menu?
82
        while (argc <= MENU_ARGS_MAX) {
83
            _argv[argc].str = strtok_r(NULL, " ", &s);
84
            if ('\0' == _argv[argc].str)
85
                break;
86
            _argv[argc].i = atol(_argv[argc].str);
87
            _argv[argc].f = atof(_argv[argc].str);      // calls strtod, > 700B !
88
            argc++;
89
        }
90
 
91
                if (_argv[0].str == NULL) {
92
                        continue;
93
                }
94
 
95
        // populate arguments that have not been specified with "" and 0
96
        // this is safer than NULL in the case where commands may look
97
        // without testing argc
98
        i = argc;
99
        while (i <= MENU_ARGS_MAX) {
100
            _argv[i].str = "";
101
            _argv[i].i = 0;
102
            _argv[i].f = 0;
103
            i++;
104
        }
105
 
106
                bool cmd_found = false;
107
        // look for a command matching the first word (note that it may be empty)
108
        for (i = 0; i < _entries; i++) {
109
            if (!strcasecmp_P(_argv[0].str, _commands[i].command)) {
110
                ret = _call(i, argc);
111
                cmd_found=true;
112
                if (-2 == ret)
113
                    return;
114
                break;
115
            }
116
        }
117
 
118
        // implicit commands
119
        if (i == _entries) {
120
            if (!strcmp(_argv[0].str, "?") || (!strcasecmp_P(_argv[0].str, PSTR("help")))) {
121
                _help();
122
                cmd_found=true;
123
            } else if (!strcasecmp_P(_argv[0].str, PSTR("exit"))) {
124
                return;
125
            }
126
        }
127
 
128
        if (cmd_found==false)
129
        {
130
                Serial.println("Invalid command, type 'help'");
131
        }
132
 
133
    }
134
}
135
 
136
// display the list of commands in response to the 'help' command
137
void
138
Menu::_help(void)
139
{
140
    int         i;
141
 
142
    Serial.println("Commands:");
143
    for (i = 0; i < _entries; i++)
144
        Serial.printf("  %S\n", FPSTR(_commands[i].command));
145
}
146
 
147
// run the n'th command in the menu
148
int8_t
149
Menu::_call(uint8_t n, uint8_t argc)
150
{
151
    func                fn;
152
 
153
    fn = (func)pgm_read_pointer(&_commands[n].func);
154
    return(fn(argc, &_argv[0]));
155
}