-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
2,191 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#pragma once | ||
|
||
#include <array> | ||
#include <cstddef> | ||
#include <iostream> | ||
#include <memory> | ||
#include <Arduino.h> | ||
|
||
namespace serialcom { | ||
|
||
/// Adapted from https://en.cppreference.com/w/cpp/io/basic_streambuf/overflow's example | ||
template<std::size_t size, class CharT = char> | ||
struct ArrayedStreamBuffer : std::basic_streambuf<CharT> | ||
{ | ||
using Base = std::basic_streambuf<CharT>; | ||
using char_type = typename Base::char_type; | ||
using int_type = typename Base::int_type; | ||
|
||
bool canFlushOnOverflow = true; | ||
|
||
ArrayedStreamBuffer() | ||
{ | ||
// put area pointers to work with 'buffer' | ||
Base::setp(buffer.data(), buffer.data() + size); | ||
} | ||
|
||
~ArrayedStreamBuffer() | ||
{ | ||
if (this->stream && this->originalBuffer) | ||
this->stream->rdbuf(originalBuffer); | ||
} | ||
|
||
void installOnStream(std::ostream* stream) | ||
{ | ||
this->stream = stream; | ||
this->originalBuffer = this->stream->rdbuf(); | ||
this->stream->rdbuf(this); | ||
} | ||
|
||
void flushBuffer() | ||
{ | ||
if (!this->stream || !this->originalBuffer) | ||
return; | ||
this->stream->rdbuf(originalBuffer); | ||
this->stream->write(buffer.data(), this->pptr() - this->pbase()); | ||
this->stream->flush(); // Ensure it's flushed to output | ||
this->stream->rdbuf(this); | ||
buffer.fill(0); | ||
Base::setp(buffer.data(), buffer.data() + size); | ||
} | ||
|
||
void log(const std::string& log) | ||
{ | ||
this->buffer->write(log); | ||
} | ||
|
||
void directLog(const std::string& log, bool newLine) | ||
{ | ||
if (!this->stream || !this->originalBuffer) | ||
return; | ||
this->stream->rdbuf(originalBuffer); | ||
Serial.write(log.c_str(), log.size()); | ||
if (newLine) | ||
{ | ||
Serial.write("\r\n"); | ||
Serial.flush(); | ||
} | ||
this->stream->rdbuf(this); | ||
} | ||
|
||
std::streambuf* changeDefaultBuffer(std::streambuf* buffer) | ||
{ | ||
std::streambuf* oldBuffer = this->originalBuffer; | ||
this->originalBuffer = buffer; | ||
return oldBuffer; | ||
} | ||
|
||
void emptyBuffer() | ||
{ | ||
buffer.fill(0); | ||
Base::setp(buffer.data(), buffer.data() + size); | ||
} | ||
private: | ||
int_type overflow(int_type ch) override | ||
{ | ||
if (!this->stream) | ||
return Base::overflow(ch); | ||
|
||
// TODO: find a way to force flush on overflow without compromising an eventual command log. | ||
if (canFlushOnOverflow) | ||
flushBuffer(); | ||
else { | ||
this->buffer.fill(0); | ||
Base::setp(buffer.data(), buffer.data() + size); | ||
} | ||
|
||
*(this->stream) << (char)ch; | ||
return 1; // overflow management succeeded | ||
} | ||
|
||
std::array<char_type, size> buffer{}; // value-initialize buffer | ||
std::streambuf* originalBuffer; | ||
std::ostream* stream; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
#include "Command.hpp" | ||
#include "Commands/InfoCommand.hpp" | ||
#include "Commands/FileCommands.hpp" | ||
#include "Commands/ShellModeCommand.hpp" | ||
#include "Commands/ConsoleCommand.hpp" | ||
#include <iostream> | ||
|
||
namespace serialcom { | ||
Command::Command(char (&input)[INPUT_MAX_SIZE]) { | ||
bool shellMode = CommandsManager::defaultInstance->shellMode; | ||
|
||
// detect the command type knowing that the maximum size that the command type can have is MAX_COMMMAND_TYPE_SIZE | ||
char command_type[MAX_COMMMAND_TYPE_SIZE]; | ||
|
||
size_t command_size = MAX_COMMMAND_TYPE_SIZE; | ||
|
||
for (size_t i = 0; i < MAX_COMMMAND_TYPE_SIZE; i++) { | ||
if (input[i] == ' ' || input[i] == '\0') { | ||
command_type[i] = '\0'; | ||
command_size = i; | ||
break; | ||
} | ||
command_type[i] = input[i]; | ||
} | ||
|
||
// match the command_type with a loop with the CommandType enum but don't forget that the input is a 16 char array | ||
|
||
this->type = CommandType::unknown; | ||
for (auto const& [key, val] : command_types_raw_strings) { | ||
if (val.size() == 0) { | ||
continue; | ||
} | ||
if (MAX_COMMMAND_TYPE_SIZE < val.size()) { | ||
if (shellMode) { | ||
SerialManager::sharedInstance->commandLog("WARNING: COMMAND TYPE " + val + " HAS A GREATER SIZE THAN THE MAXIMUM COMMAND TYPE SIZE"); | ||
} else { | ||
SerialManager::sharedInstance->commandLog(NON_SHELL_MODE_ERROR_CODE); | ||
} | ||
continue; | ||
} | ||
|
||
bool broken = false; | ||
|
||
for(size_t i = 0; i < val.size(); i++) { | ||
if (command_type[i] != val[i]) { | ||
broken = true; | ||
break; | ||
} | ||
} | ||
if (!broken) | ||
{ | ||
this->type = key; | ||
break; | ||
} | ||
} | ||
|
||
if (type == CommandType::unknown) { | ||
if (shellMode) { | ||
SerialManager::sharedInstance->commandLog("ERROR: UNKONWN COMMAND TYPE"); | ||
} else { | ||
SerialManager::sharedInstance->commandLog(NON_SHELL_MODE_ERROR_CODE); | ||
} | ||
return; | ||
} | ||
|
||
size_t current_index = command_size; | ||
|
||
for (size_t argument_index = 0; argument_index < MAX_COMMAND_ARGUMENTS_COUNT; argument_index++) { | ||
// skip spaces | ||
while (input[current_index] == ' ' && current_index < INPUT_MAX_SIZE - 1) { | ||
current_index++; | ||
} | ||
|
||
// check if the input has ended | ||
if (input[current_index] == '\0') { | ||
break; | ||
} | ||
|
||
bool isUsingQuotes = false; | ||
|
||
if (input[current_index] == '"') { | ||
isUsingQuotes = true; | ||
current_index++; | ||
} | ||
|
||
bool isEscaping = false; | ||
size_t argument_char_index = 0; | ||
for (size_t i = current_index; i < current_index + MAX_ARGUMENT_SIZE; i++) | ||
{ | ||
if (input[i] == '\0' || input[i] == '\n') { | ||
if (isEscaping) { | ||
// escaping end of input | ||
isEscaping = false; | ||
this->arguments[argument_index][argument_char_index] = input[i]; | ||
current_index = i + 1; | ||
break; | ||
} else { | ||
// end of input | ||
this->arguments[argument_index][argument_char_index] = '\0'; | ||
current_index = i + 1; | ||
break; | ||
} | ||
} else if (input[i] == '\\') { | ||
if (isEscaping) { | ||
// escaping backslash | ||
isEscaping = false; | ||
this->arguments[argument_index][argument_char_index] = '\\'; | ||
argument_char_index++; | ||
} else { | ||
// start escaping | ||
isEscaping = true; | ||
} | ||
} else if (input[i] == ' ') { | ||
if (isUsingQuotes) { | ||
// space and is using quotes | ||
this->arguments[argument_index][argument_char_index] = input[i]; | ||
argument_char_index++; | ||
continue; | ||
} | ||
|
||
// not using quotes => need to escape space characters | ||
if (isEscaping) { | ||
// escaping space | ||
isEscaping = false; | ||
this->arguments[argument_index][argument_char_index] = ' '; | ||
argument_char_index++; | ||
continue; | ||
} else { | ||
// end of argument because of space | ||
this->arguments[argument_index][argument_char_index] = '\0'; | ||
current_index = i + 1; | ||
break; | ||
} | ||
} else if (input[i] == '"') { | ||
if (isUsingQuotes) { | ||
if (isEscaping) { | ||
// escaping quote | ||
isEscaping = false; | ||
this->arguments[argument_index][argument_char_index] = '"'; | ||
argument_char_index++; | ||
} else { | ||
// end of argument because of quote | ||
this->arguments[argument_index][argument_char_index] = '\0'; | ||
current_index = i + 1; | ||
break; | ||
} | ||
} else { | ||
if (isEscaping) { | ||
// escaping quote | ||
isEscaping = false; | ||
this->arguments[argument_index][argument_char_index] = '"'; | ||
argument_char_index++; | ||
} else { | ||
if (shellMode) { | ||
SerialManager::sharedInstance->commandLog("ERROR: UNEXPECTED QUOTE CHARACTER AT POSITION " + std::to_string(i)); | ||
} else { | ||
SerialManager::sharedInstance->commandLog(NON_SHELL_MODE_ERROR_CODE); | ||
} | ||
this->type = CommandType::unknown; | ||
for (size_t j = 0; j < MAX_COMMAND_ARGUMENTS_COUNT; j++) | ||
{ | ||
this->arguments[j][0] = '\0'; | ||
} | ||
return; | ||
} | ||
} | ||
} else { | ||
if (isEscaping) { | ||
// escaping character | ||
isEscaping = false; | ||
this->arguments[argument_index][argument_char_index] = '\\'; | ||
argument_char_index++; | ||
} | ||
this->arguments[argument_index][argument_char_index] = input[i]; | ||
argument_char_index++; | ||
} | ||
} | ||
} | ||
} | ||
|
||
std::unordered_map<Command::CommandType, std::string> Command::command_types_raw_strings { | ||
{Command::CommandType::sm, "sm"}, | ||
{Command::CommandType::console, "console"}, | ||
|
||
{Command::CommandType::info, "info"}, | ||
{Command::CommandType::echo, "echo"}, | ||
{Command::CommandType::apps, "apps"}, | ||
{Command::CommandType::files, "files"}, | ||
{Command::CommandType::elevate, "elevate"}, | ||
{Command::CommandType::lte, "lte"}, | ||
|
||
{Command::CommandType::ls, "ls"}, | ||
{Command::CommandType::touch, "touch"}, | ||
{Command::CommandType::mkdir, "mkdir"}, | ||
{Command::CommandType::rm, "rm"}, | ||
{Command::CommandType::cp, "cp"}, | ||
{Command::CommandType::mv, "mv"}, | ||
{Command::CommandType::cat, "cat"}, | ||
{Command::CommandType::download, "download"}, | ||
{Command::CommandType::upload, "upload"} | ||
}; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
#pragma once | ||
|
||
#include <cstddef> | ||
#include <unordered_map> | ||
#include <optional> | ||
#include <string> | ||
|
||
namespace serialcom { | ||
const size_t MAX_COMMMAND_TYPE_SIZE = 16; | ||
const size_t MAX_ARGUMENT_SIZE = 512; | ||
const size_t MAX_COMMAND_ARGUMENTS_COUNT = 3; | ||
constexpr size_t INPUT_MAX_SIZE = MAX_COMMMAND_TYPE_SIZE + MAX_COMMAND_ARGUMENTS_COUNT * MAX_ARGUMENT_SIZE; | ||
|
||
struct Command { | ||
enum class CommandType { | ||
sm, | ||
console, | ||
|
||
info, | ||
echo, | ||
apps, | ||
files, | ||
elevate, | ||
lte, | ||
|
||
ls, | ||
touch, | ||
mkdir, | ||
rm, | ||
cp, | ||
mv, | ||
cat, | ||
download, | ||
upload, | ||
|
||
unknown | ||
}; | ||
|
||
// a static unordered map between the raw strings of the command types and the CommandType enum | ||
|
||
static std::unordered_map<CommandType, std::string> command_types_raw_strings; | ||
|
||
CommandType type; | ||
|
||
Command(char (&input)[INPUT_MAX_SIZE]); | ||
|
||
char arguments[MAX_COMMAND_ARGUMENTS_COUNT][MAX_ARGUMENT_SIZE] = {'\0'}; | ||
}; | ||
} |
Oops, something went wrong.