Avionics
Core avionics package for CURE flight computers
Loading...
Searching...
No Matches
UARTCommandHandler.cpp
Go to the documentation of this file.
2#include "ArduinoHAL.h"
3#include <algorithm>
4
5constexpr int COMMAND_CHARS_ASCII_END = 31; // ASCII control characters end at 31, so we can ignore those in input
6
7
8CommandLine::CommandLine(Stream * UART) : UART(UART) {
9}
10
12 help();
13 UART->print(SHELL_PROMPT);
14}
15
16static bool isBackspace_(char receivedChar) {
17 return receivedChar == static_cast<char>(AsciiKey::Backspace) ||
18 receivedChar == static_cast<char>(AsciiKey::Delete);
19}
20
21static bool isNewline_(char receivedChar) {
22 return receivedChar == '\n' || receivedChar == '\r';
23}
24
25namespace {
26// Small helper: split on spaces/tabs (no quoting support)
27// Parses a line into a command and arguments, e.g. "foo bar baz" -> cmd="foo", args={"bar", "baz"}
28void tokenizeWhitespace(const std::string& line,
29 std::string& outCmd,
30 std::queue<std::string>& outArgs)
31{
32 outCmd.clear();
33 while (!outArgs.empty()) {
34 outArgs.pop();
35 }
36
37 std::string token;
38 auto flush = [&]() {
39 if (token.empty()) {return;}
40 if (outCmd.empty()) {outCmd = token;}
41 else {outArgs.push(token);}
42 token.clear();
43 };
44
45 for (char ch : line) {
46 if (ch == ' ' || ch == '\t') {flush();}
47 else {token += ch;}
48 }
49 flush();
50}
51} // namespace
52
53void CommandLine::readInput() { // NOLINT(readability-function-cognitive-complexity)
54 while (UART->available() > 0) {
55 const char receivedChar = static_cast<char>(UART->read());
56
57 if (isBackspace_(receivedChar)) {
58 lastWasCR_ = false;
59 handleBackspace_();
60 } else if (isNewline_(receivedChar)) {
61 if (lastWasCR_ && receivedChar == '\n') {
62 lastWasCR_ = false; // Treat CRLF as a single newline event.
63 continue;
64 }
65 lastWasCR_ = (receivedChar == '\r');
66 handleNewline_();
67 } else {
68 lastWasCR_ = false;
69 handleChar_(receivedChar);
70 }
71 }
72}
73
74void CommandLine::handleBackspace_() {
75 if (fullLine.empty()) {return;}
76 fullLine.pop_back();
77 UART->print("\b \b"); // erase last char on terminal
78}
79
80void CommandLine::handleNewline_() {
81 UART->println();
82
83 std::string line = fullLine;
84 fullLine.clear();
85
86 trimSpaces(line);
87 if (line.empty()) {
88 UART->print(SHELL_PROMPT);
89 return;
90 }
91
92 std::string cmd = {""};
93 std::queue<std::string> args = {}; //NOLINT(cppcoreguidelines-init-variables)
94 tokenizeWhitespace(line, cmd, args);
95
96 if (!cmd.empty()) {
97 executeCommand(cmd, args);
98 }
99
100 UART->print(SHELL_PROMPT);
101}
102
103void CommandLine::handleChar_(char receivedChar) {
104 // Optional: ignore other control chars (keep tab if you want)
105 if (static_cast<unsigned char>(receivedChar) <= COMMAND_CHARS_ASCII_END && receivedChar != '\t') {return;}
106
107 if (fullLine.length() >= UART_BUFFER_SIZE - 1) {
108 UART->println();
109 UART->println("Buffer overflow, input ignored.");
110 fullLine.clear();
111 UART->print(SHELL_PROMPT);
112 return;
113 }
114
115 fullLine += receivedChar;
116 UART->print(receivedChar);
117}
118
119
120// Add a command with its long name, short name, and function pointer
121void CommandLine::addCommand(const std::string& longName, const std::string& shortName, std::function<void(std::queue<std::string>, std::string&)> funcPtr) { //NOLINT(readability-convert-member-functions-to-static)
122 Command newCommand{ longName, shortName, funcPtr };
123 commands.push_back(newCommand);
124}
125
126// Execute a command based on its long name or short name
127void CommandLine::executeCommand(const std::string& command, std::queue<std::string> arguments) {
128 // Check if the user entered "help" or "?"
129 if (command == "help" || command == "?") {
130 help();
131 return;
132 }
133
134 std::string response;
135 for (const auto& cmd : commands) {
136 if (cmd.longName == command || cmd.shortName == command) {
137
138 cmd.funcPtr(arguments, response);
139 return;
140 }
141 }
142
143 // Print the name of the command that was not found, and suggest using "help" to see available commands
144 UART->println("Command not found: " + String(command.c_str()));
145 UART->println("Type 'help' or '?' to see available commands.");
146}
147
148// Help function to list all commands with their long and short names
149void CommandLine::help(){
150 if (commands.empty()) {
151 UART->println("No commands available.");
152 UART->println("help<?>");
153 return;
154 }
155
156 for (const auto& cmd : commands) {
157 UART->println(String(cmd.longName.c_str()) + "<" + String(cmd.shortName.c_str()) + ">");
158 }
159 UART->println("help<?>");
160
161}
162
163void CommandLine::trimSpaces(std::string& str) { //NOLINT(readability-convert-member-functions-to-static)
164 // Remove leading spaces
165 size_t start = str.find_first_not_of(" "); //NOLINT(cppcoreguidelines-init-variables)
166 // If there's any non-space character
167 if (start != std::string::npos) {
168 // Remove trailing spaces
169 size_t end = str.find_last_not_of(" "); //NOLINT(cppcoreguidelines-init-variables)
170 str = str.substr(start, end - start + 1);
171 } else {
172 // If there are only spaces, clear the string
173 str.clear();
174 }
175}
constexpr int COMMAND_CHARS_ASCII_END
constexpr int UART_BUFFER_SIZE
#define SHELL_PROMPT
void executeCommand(const std::string &command, std::queue< std::string > arugments)
void addCommand(const std::string &longName, const std::string &shortName, std::function< void(std::queue< std::string > argumentQueue, std::string &)> funcPtr)
CommandLine(Stream *UART)
void println(const T &message)