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
5
6CommandLine::CommandLine(Stream * UART) : UART(UART) {
7}
8
10 help();
11 UART->print("CL> ");
12}
13
14
15void CommandLine::readInput() { //NOLINT(readability-function-cognitive-complexity)
16
17 while (UART->available() > 0) {
18 char incomingByte = static_cast<char>(UART->read());
19
20 // Handle the Up Arrow key (ASCII 27 + [ + ([A] || [B]))
21 if (incomingByte == static_cast<char>(AsciiKey::Escape)) { // ASCII ESC (start of escape sequence)
22 // Read the escape sequence buffer
23 delay(2);
24 incomingByte = static_cast<char>(UART->read());
25 if(incomingByte == '[')
26 {
27 delay(2);
28 incomingByte = static_cast<char>(UART->read());
29 // Check if the full sequence matches an arrow key
30 if (incomingByte == 'A') {
31 // Up Arrow
32 if (historyIndex > 0) {
33 historyIndex--;
34 }
35 displayCommandFromHistory();
36 } else if (incomingByte == 'B') {
37 // Down Arrow
38 if (historyIndex < MAX_HISTORY) {
39 historyIndex++;
40 }
41 displayCommandFromHistory();
42 } else {
43 // Handle other escape sequences or ignore unknown ones
44 UART->println("Unknown escape sequence: ");
45 }
46 }
47 }
48 else if (incomingByte == static_cast<char>(AsciiKey::Backspace) || incomingByte == static_cast<char>(AsciiKey::Delete)) // 8 = Backspace, 127 = Delete
49 {
50 if (!fullLine.empty()) {
51 fullLine.pop_back(); // Remove the last character from the buffer
52 UART->print("\b \b"); // Move cursor back, print a space, and move cursor back again
53
54 if(!inputBuffer.empty()){
55 inputBuffer.pop_back();
56 }
57 //TODO(mvari) FIX: If you back space on a history command it will not erase
58 //the command or argument buffer, so if you press enter it will
59 // return prevous commmand
60 }
61 }
62 else if (incomingByte == ' ')
63 {
64 UART->print(incomingByte);
65 fullLine += incomingByte;
66
67 if (!inputBuffer.empty()) {
68 if (!isCommandParsed) {
69 command = inputBuffer;
70 isCommandParsed = true;
71 } else {
72 if(argSize[historyIndex] < MAX_ARGUMENTS){
73 argumentQueue.push(inputBuffer); // Push subsequent words as arguments
74 argSize[historyIndex]++;
75 }
76 }
77 }
78 inputBuffer.clear();
79 }
80 else if (incomingByte == '\n' || incomingByte == '\r')
81 {
82 UART->println();
83 int idx= 0;
84 // If it's a newline, process the current buffer and add to history
85 if (!inputBuffer.empty()) {
86 if(isCommandParsed || !command.empty()) //NOLINT(bugprone-branch-clone)
87 {
88 argumentQueue.push(inputBuffer);
89 argSize[historyIndex]++;
90 }
91 else
92 {
93 command = inputBuffer; // Set the command based on the input buffer
94 }
95 }
96
97 if(!command.empty() || !argumentQueue.empty())
98 {
99 executeCommand(command, argumentQueue);
100 commandHistory[historyIndex] = command;
101 fullLineHistory[historyIndex] = fullLine;
102
103
104 while (!argumentQueue.empty()) { //NOLINT(altera-unroll-loops)
105 argumentHistory[historyIndex][idx] = argumentQueue.front();
106 argumentQueue.pop(); // Clear the argument queue
107 idx++;
108 }
109 historyIndex++;
110 }
111
112 inputBuffer.clear(); // Clear the buffer for the next input
113 command.clear();
114 fullLine.clear();
115 isCommandParsed = false;
116 UART->print("CL> ");
117
118
119 } else {
120 // Add the incoming byte to the buffer, and update the UART display
121 if (inputBuffer.length() < UART_BUFFER_SIZE - 1) { // Ensure we don't overflow the buffer
122 inputBuffer += incomingByte;
123 fullLine += incomingByte;
124 UART->print(incomingByte); // Echo the character to the terminal
125
126 } else {
127 UART->println("Buffer overflow, input ignored.");
128 }
129 }
130
131 }
132}
133
134void CommandLine::displayCommandFromHistory() {
135
136 if (historyIndex >= MAX_HISTORY) {
137 // Reset the history index to 0 (wrap around) and clear the history buffer
138 historyIndex = 0; // Reset to the beginning of history
139
140 // Optionally clear the history buffer or handle how you want to reset the entries
141 // For example, clearing all argumentHistory buffers:
142 #pragma unroll
143 for (int i = 0; i < MAX_HISTORY; i++) {
144
145 #pragma unroll
146 for (int idx = 0; idx < MAX_ARGUMENTS; idx++) {
147 argumentHistory[i][idx] = "";
148 }
149 commandHistory[i] = "";
150 fullLineHistory[i] = "";
151 }
152 }
153
154 if (historyIndex >= 0 && historyIndex < MAX_HISTORY) {
155 // Clear current input and display the previous/next command from history
156 inputBuffer.clear();
157 command.clear();
158 fullLine.clear();
159
160 #pragma unroll
161 while (!argumentQueue.empty()) {
162 argumentQueue.front();
163 argumentQueue.pop();
164 }
165
166 fullLine = fullLineHistory[historyIndex];
167 command = commandHistory[historyIndex];
168
169 #pragma unroll
170 for (int idx = 0; idx < argSize[historyIndex]; idx++) {
171 argumentQueue.push(argumentHistory[historyIndex][idx]);
172 }
173
174 UART->print("\r"); // Move the cursor to the beginning of the line
175
176 #pragma unroll
177 for(int len = 0; len < MAX_ROW_LENGTH; len++){
178 UART->print(" "); // Clear the current line (clear any previous characters)
179 }
180
181
182 UART->print("\r"); // Move back to the beginning of the line
183 UART->print("CL> ");
184 UART->print(fullLine.c_str()); // Print the command from history
185 }
186
187
188}
189
190// Add a command with its long name, short name, and function pointer
191void 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)
192 Command newCommand{ longName, shortName, funcPtr };
193 commands.push_back(newCommand);
194}
195
196// Execute a command based on its long name or short name
197void CommandLine::executeCommand(const std::string& command, std::queue<std::string> arguments) {
198 // Check if the user entered "help" or "?"
199 if (command == "help" || command == "?") {
200 help();
201 return;
202 }
203
204 std::string response;
205 for (const auto& cmd : commands) {
206 if (cmd.longName == command || cmd.shortName == command) {
207
208 cmd.funcPtr(arguments, response);
209 return;
210 }
211 }
212
213 UART->println("Command not found");
214
215}
216
217// Help function to list all commands with their long and short names
218void CommandLine::help(){
219 if (commands.empty()) {
220 UART->println("No commands available.");
221 UART->println("help<?>");
222 return;
223 }
224
225 for (const auto& cmd : commands) {
226 UART->println(String(cmd.longName.c_str()) + "<" + String(cmd.shortName.c_str()) + ">");
227 }
228 UART->println("help<?>");
229
230}
231
232void CommandLine::trimSpaces(std::string& str) { //NOLINT(readability-convert-member-functions-to-static)
233 // Remove leading spaces
234 size_t start = str.find_first_not_of(" "); //NOLINT(cppcoreguidelines-init-variables)
235 // If there's any non-space character
236 if (start != std::string::npos) {
237 // Remove trailing spaces
238 size_t end = str.find_last_not_of(" "); //NOLINT(cppcoreguidelines-init-variables)
239 str = str.substr(start, end - start + 1);
240 } else {
241 // If there are only spaces, clear the string
242 str.clear();
243 }
244}
constexpr int MAX_ARGUMENTS
constexpr int UART_BUFFER_SIZE
constexpr int MAX_ROW_LENGTH
constexpr int MAX_HISTORY
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)