Repository navigation
Expand file tree
/
Copy pathCommandRouter.cpp
More file actions
141 lines (122 loc) · 4.63 KB
/
Copy pathCommandRouter.cpp
File metadata and controls
141 lines (122 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include "CommandRouter.h"
#include "ErrorCounters.h"
#include <string.h>
#include <vector>
namespace CommandRouter {
// global state for command router
#define MAX_CMD_LEN 1024
uint8_t command_buffer[MAX_CMD_LEN];
size_t command_buffer_pos;
bool escaped; // was the last character an escape char
std::vector<command> commands;
// demo command to test connectivity
void ping(const char *args) {
CommsSerial.println("pong");
CommsSerial.print("args: ");
CommsSerial.println(args == nullptr ? "null" : args);
}
void begin() {
escaped = false;
command_buffer_pos = 0;
commands.clear();
add(help, "help", "lists all commands or provides help for an individual command");
add(ping, "ping", "prints pong");
}
void call_cmd(uint8_t *buffer, size_t buffer_len) {
// first we need to split the command and args
size_t command_len = buffer_len;
uint8_t *arg_buffer = buffer;
size_t arg_len = 0;
for (int i = 0; i < buffer_len; i++) {
if (buffer[i] == ' ') {
buffer[i] = '\0'; // null terminate so we can strcmp this buffer
command_len = i;
arg_buffer = &buffer[command_len + 1]; // +1 b/c of the space
arg_len = buffer_len - command_len - 1; // -1 b/c of the space
break;
}
}
if (command_len == buffer_len) { // if we never found a space, make sure argbuffer is pointed at the null terminator
arg_buffer = &buffer[command_len];
}
for (const command &c : commands) {
if (strcmp(c.name, (char *)buffer) == 0) {
c.f(arg_buffer, arg_len); // call the function
return;
}
}
CommsSerial.println("Command not found.");
}
// Call this function in a loop to handle incoming serial messages and process commands
void receive_byte(uint8_t c) {
if (c == END_CHAR && !escaped) { // true end
command_buffer[command_buffer_pos] = '\0'; // null terminate to let us use string tools on this buffer
int buf_len = command_buffer_pos;
command_buffer_pos = 0; // allows for nesting commands
call_cmd(command_buffer, buf_len);
} else if (c == CR_CHAR && !escaped) { // true carriage return
// do nothing - we aren't a typewriter, no need to carriage return
} else if (c == BACKSPACE_CHAR && !escaped) { // true backspace
if (command_buffer_pos > 0) {
command_buffer_pos -= 1;
}
} else if (c == ESCAPE_CHAR && !escaped) { // true escape start
escaped = true;
} else { // literally write the character
command_buffer[command_buffer_pos] = c;
command_buffer_pos++;
escaped = false;
if (command_buffer_pos == MAX_CMD_LEN) { // we need an extra byte at the end to guarantee a safe null-terminate
// something went wrong and we never saw a non-escaped END_CHAR so just go back to 0 and try again
command_buffer_pos = 0;
ErrorCounters::increment(ErrorCounters::cmd_buf_overflow);
}
}
}
// Prints help message for command / lists all commands
void help(const char *command_name) {
// user just typed "help\n" - we list all commands
if (strlen(command_name) == 0) {
CommsSerial.println("All commands: ");
for (command &c : commands) {
CommsSerial.println(c.name);
}
return;
}
// user typed "help command"
for (const command &c : commands) {
if (strcmp(c.name, command_name) == 0) {
CommsSerial.println(c.help);
return;
}
}
// no command found, so print all commands that start with what user entered
CommsSerial.println("Similar commands: ");
for (const command &c : commands) {
if (strncmp(c.name, command_name, strlen(command_name)) == 0) {
CommsSerial.println(c.name);
}
}
}
// register a function that takes a buffer and a len
void add(std::function<void(const uint8_t *, size_t)> f, const char *name, const char *help) {
commands.push_back({f, name, help});
}
// register a function that takes a null terminated buffer
void add(std::function<void(const char *)> fstr, const char *name, const char *help) {
std::function<void(const uint8_t *, size_t)> f = [fcap = fstr](const uint8_t *str, size_t) {
fcap((const char *)str);
};
commands.push_back({f, name, help});
}
// register a function that takes a function with no args
void add(std::function<void()> fvoid, const char *name, const char *help) {
std::function<void(const uint8_t *, size_t)> f = [fcap = fvoid](const uint8_t *str, size_t) { fcap(); };
commands.push_back({f, name, help});
}
// add a flag that will be set to true when the command is called
void add_flag(bool *flag, const char *name, const char *help) {
std::function<void(const uint8_t *, size_t)> f = [flagcap = flag](const uint8_t *str, size_t) { *flagcap = true; };
commands.push_back({f, name, help});
}
} // namespace CommandRouter