diff --git a/include/serial/SerialBuffer.h b/include/serial/SerialBuffer.h new file mode 100644 index 00000000..dcd7283f --- /dev/null +++ b/include/serial/SerialBuffer.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include + +#include "Common.h" + +namespace OpenShock::Serial { + class SerialBuffer { + DISABLE_COPY(SerialBuffer); + DISABLE_MOVE(SerialBuffer); + + public: + constexpr SerialBuffer() + : m_data(nullptr) + , m_size(0) + , m_capacity(0) + { + } + SerialBuffer(size_t capacity) + : m_data(new char[capacity]) + , m_size(0) + , m_capacity(capacity) + { + } + ~SerialBuffer() { delete[] m_data; } + + constexpr char* data() { return m_data; } + constexpr size_t size() const { return m_size; } + constexpr size_t capacity() const { return m_capacity; } + constexpr bool empty() const { return m_size == 0; } + + constexpr void clear() { m_size = 0; } + void destroy() + { + delete[] m_data; + m_data = nullptr; + m_size = 0; + m_capacity = 0; + } + + void reserve(size_t size) + { + size = (size + 31) & ~31; // Align to 32 bytes + + if (size <= m_capacity) { + return; + } + + char* newData = new char[size]; + if (m_data != nullptr) { + memcpy(newData, m_data, m_size); + delete[] m_data; + } + + m_data = newData; + m_capacity = size; + } + + void push_back(char c) + { + if (m_size >= m_capacity) { + reserve(m_capacity + 16); + } + + m_data[m_size++] = c; + } + + constexpr void pop_back() + { + if (m_size > 0) { + --m_size; + } + } + + constexpr operator std::string_view() const { return std::string_view(m_data, m_size); } + + private: + char* m_data; + size_t m_size; + size_t m_capacity; + }; +} // namespace OpenShock::Serial diff --git a/include/serial/SerialInputHandler.h b/include/serial/SerialInputHandler.h index d47c2d78..6ac3ee23 100644 --- a/include/serial/SerialInputHandler.h +++ b/include/serial/SerialInputHandler.h @@ -8,6 +8,6 @@ namespace OpenShock::SerialInputHandler { bool SerialEchoEnabled(); void SetSerialEchoEnabled(bool enabled); - void PrintWelcomeHeader(); + void PrintBootInfo(); void PrintVersionInfo(); } // namespace OpenShock::SerialInputHandler diff --git a/include/serial/SerialReadResult.h b/include/serial/SerialReadResult.h new file mode 100644 index 00000000..23202618 --- /dev/null +++ b/include/serial/SerialReadResult.h @@ -0,0 +1,10 @@ +#pragma once + +namespace OpenShock::Serial { + enum class SerialReadResult { + NoData, + Data, + LineEnd, + AutoCompleteRequest, + }; +} // namespace OpenShock::Serial diff --git a/include/serial/command_handlers/CommandArgument.h b/include/serial/command_handlers/CommandArgument.h new file mode 100644 index 00000000..7c5c6d62 --- /dev/null +++ b/include/serial/command_handlers/CommandArgument.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace OpenShock::Serial { + class CommandArgument { + public: + std::string_view name; + std::string_view constraint; + std::string_view exampleValue; + std::vector constraintExtensions; + }; +} // namespace OpenShock::Serial diff --git a/include/serial/command_handlers/CommandEntry.h b/include/serial/command_handlers/CommandEntry.h index ced779ed..a3cf3527 100644 --- a/include/serial/command_handlers/CommandEntry.h +++ b/include/serial/command_handlers/CommandEntry.h @@ -1,19 +1,12 @@ #pragma once +#include "CommandArgument.h" +#include "CommandHandler.h" + #include #include namespace OpenShock::Serial { - typedef void (*CommandHandler)(std::string_view arg, bool isAutomated); - - class CommandArgument { - public: - std::string_view name; - std::string_view constraint; - std::string_view exampleValue; - std::vector constraintExtensions; - }; - class CommandEntry { public: CommandEntry(std::string_view description, CommandHandler commandHandler); @@ -32,24 +25,4 @@ namespace OpenShock::Serial { std::vector m_arguments; CommandHandler m_commandHandler; }; - - class CommandGroup { - public: - CommandGroup() = default; - CommandGroup(std::string_view name); - CommandGroup(CommandGroup&& other) = default; - CommandGroup(const CommandGroup& other) = default; - CommandGroup& operator=(CommandGroup&& other) = default; - CommandGroup& operator=(const CommandGroup& other) = default; - - inline std::string_view name() const { return m_name; } - inline const std::vector& commands() const { return m_commands; } - - CommandEntry& addCommand(std::string_view description, CommandHandler commandHandler); - CommandEntry& addCommand(std::string_view name, std::string_view description, CommandHandler commandHandler); - - private: - std::string_view m_name; - std::vector m_commands; - }; } // namespace OpenShock::Serial diff --git a/include/serial/command_handlers/CommandGroup.h b/include/serial/command_handlers/CommandGroup.h new file mode 100644 index 00000000..f39dad18 --- /dev/null +++ b/include/serial/command_handlers/CommandGroup.h @@ -0,0 +1,29 @@ +#pragma once + +#include "CommandEntry.h" +#include "CommandHandler.h" + +#include +#include + +namespace OpenShock::Serial { + class CommandGroup { + public: + CommandGroup() = default; + CommandGroup(std::string_view name); + CommandGroup(CommandGroup&& other) = default; + CommandGroup(const CommandGroup& other) = default; + CommandGroup& operator=(CommandGroup&& other) = default; + CommandGroup& operator=(const CommandGroup& other) = default; + + constexpr std::string_view name() const { return m_name; } + const std::vector& commands() const { return m_commands; } + + CommandEntry& addCommand(std::string_view description, CommandHandler commandHandler); + CommandEntry& addCommand(std::string_view name, std::string_view description, CommandHandler commandHandler); + + private: + std::string_view m_name; + std::vector m_commands; + }; +} // namespace OpenShock::Serial diff --git a/include/serial/command_handlers/CommandHandler.h b/include/serial/command_handlers/CommandHandler.h new file mode 100644 index 00000000..62e80f10 --- /dev/null +++ b/include/serial/command_handlers/CommandHandler.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace OpenShock::Serial { + typedef void (*CommandHandler)(std::string_view arg, bool isAutomated); +} // namespace OpenShock::Serial diff --git a/include/serial/command_handlers/index.h b/include/serial/command_handlers/index.h index a98c41ee..1bfd6c6f 100644 --- a/include/serial/command_handlers/index.h +++ b/include/serial/command_handlers/index.h @@ -1,6 +1,6 @@ #pragma once -#include "serial/command_handlers/CommandEntry.h" +#include "serial/command_handlers/CommandGroup.h" #include diff --git a/src/serial/SerialInputHandler.cpp b/src/serial/SerialInputHandler.cpp index 2e1a370f..1f746921 100644 --- a/src/serial/SerialInputHandler.cpp +++ b/src/serial/SerialInputHandler.cpp @@ -14,6 +14,8 @@ const char* const TAG = "SerialInputHandler"; #include "serial/command_handlers/CommandEntry.h" #include "serial/command_handlers/common.h" #include "serial/command_handlers/index.h" +#include "serial/SerialBuffer.h" +#include "serial/SerialReadResult.h" #include "serialization/JsonAPI.h" #include "serialization/JsonSerial.h" #include "Time.h" @@ -55,24 +57,41 @@ namespace std { }; } // namespace std -using namespace std::string_view_literals; +#define OPENSHOCK_WELCOME_HEADER_STR \ + "\n\ +============== OPENSHOCK ==============\n\ + Contribute @ github.com/OpenShock\n\ + Discuss @ discord.gg/OpenShock\n\ + Type 'help' for available commands\n\ +=======================================\n" -using namespace OpenShock; +#define OPENSHOCK_VERSION_INFO_STR \ + "\ + Version: " OPENSHOCK_FW_VERSION "\n\ + Build: " OPENSHOCK_FW_MODE "\n\ + Commit: " OPENSHOCK_FW_GIT_COMMIT "\n\ + Board: " OPENSHOCK_FW_BOARD "\n\ + Chip: " OPENSHOCK_FW_CHIP "\n" + +#define CLEAR_LINE "\r\x1B[K" const int64_t PASTE_INTERVAL_THRESHOLD_MS = 20; const std::size_t SERIAL_BUFFER_CLEAR_THRESHOLD = 512; -static bool s_echoEnabled = true; -static std::vector s_commandGroups; -static std::unordered_map s_commandHandlers; +static bool serialEchoEnabled = true; +static std::vector serialCommandGroups; +static std::unordered_map serialCommandHandlers; -void _printCompleteHelp() +using namespace OpenShock; +using namespace std::string_view_literals; + +static void printCommandsHelp() { std::size_t commandCount = 0; std::size_t longestCommand = 0; std::size_t longestArgument = 0; std::size_t descriptionSize = 0; - for (const auto& group : s_commandGroups) { + for (const auto& group : serialCommandGroups) { longestCommand = std::max(longestCommand, group.name().size()); for (const auto& command : group.commands()) { commandCount++; @@ -94,7 +113,7 @@ void _printCompleteHelp() std::string buffer; buffer.reserve((paddedLength * commandCount) + descriptionSize); // Approximate size - for (const auto& group : s_commandGroups) { + for (const auto& group : serialCommandGroups) { for (const auto& command : group.commands()) { buffer.append(group.name()); buffer.append((longestCommand - group.name().size()) + 1, ' '); @@ -121,11 +140,11 @@ void _printCompleteHelp() } } - SerialInputHandler::PrintWelcomeHeader(); + SerialInputHandler::PrintBootInfo(); ::Serial.print(buffer.data()); } -void _printCommandHelp(Serial::CommandGroup& group) +static void printCommandHelp(Serial::CommandGroup& group) { std::size_t size = 0; for (const auto& command : group.commands()) { @@ -250,119 +269,35 @@ void _printCommandHelp(Serial::CommandGroup& group) ::Serial.print(buffer.data()); } -void _handleHelpCommand(std::string_view arg, bool isAutomated) +static void handleHelpCommand(std::string_view arg, bool isAutomated) { arg = OpenShock::StringTrim(arg); if (arg.empty()) { - _printCompleteHelp(); + printCommandsHelp(); return; } // Get help for a specific command - auto it = s_commandHandlers.find(arg); - if (it != s_commandHandlers.end()) { - _printCommandHelp(it->second); + auto it = serialCommandHandlers.find(arg); + if (it != serialCommandHandlers.end()) { + printCommandHelp(it->second); return; } SERPR_ERROR("Command \"%.*s\" not found", arg.length(), arg.data()); } -void RegisterCommandHandler(const OpenShock::Serial::CommandGroup& handler) +static void registerCommandHandler(const OpenShock::Serial::CommandGroup& handler) { - s_commandHandlers[handler.name()] = handler; + serialCommandHandlers[handler.name()] = handler; } -#define CLEAR_LINE "\r\x1B[K" - -class SerialBuffer { - DISABLE_COPY(SerialBuffer); - DISABLE_MOVE(SerialBuffer); - -public: - constexpr SerialBuffer() - : m_data(nullptr) - , m_size(0) - , m_capacity(0) - { - } - inline SerialBuffer(std::size_t capacity) - : m_data(new char[capacity]) - , m_size(0) - , m_capacity(capacity) - { - } - inline ~SerialBuffer() { delete[] m_data; } - - constexpr char* data() { return m_data; } - constexpr std::size_t size() const { return m_size; } - constexpr std::size_t capacity() const { return m_capacity; } - constexpr bool empty() const { return m_size == 0; } - - constexpr void clear() { m_size = 0; } - inline void destroy() - { - delete[] m_data; - m_data = nullptr; - m_size = 0; - m_capacity = 0; - } - - inline void reserve(std::size_t size) - { - size = (size + 31) & ~31; // Align to 32 bytes - - if (size <= m_capacity) { - return; - } - - char* newData = new char[size]; - if (m_data != nullptr) { - std::memcpy(newData, m_data, m_size); - delete[] m_data; - } - - m_data = newData; - m_capacity = size; - } - - inline void push_back(char c) - { - if (m_size >= m_capacity) { - reserve(m_capacity + 16); - } - - m_data[m_size++] = c; - } - - constexpr void pop_back() - { - if (m_size > 0) { - --m_size; - } - } - - constexpr operator std::string_view() const { return std::string_view(m_data, m_size); } - -private: - char* m_data; - std::size_t m_size; - std::size_t m_capacity; -}; - -enum class SerialReadResult { - NoData, - Data, - LineEnd, - AutoCompleteRequest, -}; - -SerialReadResult _tryReadSerialLine(SerialBuffer& buffer) +static OpenShock::Serial::SerialReadResult serialTryReadLine(OpenShock::Serial::SerialBuffer& buffer) { // Check if there's any data available int available = ::Serial.available(); if (available <= 0) { - return SerialReadResult::NoData; + return OpenShock::Serial::SerialReadResult::NoData; } // Reserve space for the new data @@ -381,7 +316,7 @@ SerialReadResult _tryReadSerialLine(SerialBuffer& buffer) // Handle newline if (c == '\r' || c == '\n') { if (!buffer.empty()) { - return SerialReadResult::LineEnd; + return OpenShock::Serial::SerialReadResult::LineEnd; } continue; } @@ -392,7 +327,7 @@ SerialReadResult _tryReadSerialLine(SerialBuffer& buffer) } if (c == '\t') { - return SerialReadResult::AutoCompleteRequest; + return OpenShock::Serial::SerialReadResult::AutoCompleteRequest; } // If character is printable, add it to the buffer @@ -401,10 +336,10 @@ SerialReadResult _tryReadSerialLine(SerialBuffer& buffer) } } - return SerialReadResult::Data; + return OpenShock::Serial::SerialReadResult::Data; } -void _skipSerialWhitespaces(SerialBuffer& buffer) +static void serialSkipWhitespaces(OpenShock::Serial::SerialBuffer& buffer) { int available = ::Serial.available(); @@ -418,18 +353,19 @@ void _skipSerialWhitespaces(SerialBuffer& buffer) } } -void _echoBuffer(std::string_view buffer) +static void serialEchoBuffer(std::string_view buffer) { ::Serial.printf(CLEAR_LINE "> %.*s", buffer.size(), buffer.data()); + ::Serial.flush(); } -void _echoHandleSerialInput(std::string_view buffer, bool hasData) +static void serialHandleActivity(std::string_view buffer, bool hasData) { static int64_t lastActivity = 0; static bool hasChanges = false; // If serial echo is disabled, don't do anything past this point - if (!s_echoEnabled) { + if (!serialEchoEnabled) { return; } @@ -446,13 +382,50 @@ void _echoHandleSerialInput(std::string_view buffer, bool hasData) // If theres has been received data, but no new data for a while, echo the buffer if (hasChanges && OpenShock::millis() - lastActivity > PASTE_INTERVAL_THRESHOLD_MS) { - _echoBuffer(buffer); + serialEchoBuffer(buffer); hasChanges = false; lastActivity = OpenShock::millis(); } } -void _processSerialLine(std::string_view line) +static const OpenShock::Serial::CommandEntry* getCommandEntry(const std::vector& arguments, const std::vector& commandsEntries) +{ + if (arguments.empty()) { + // If no arguments, check for the first command with no name and no arguments + for (const auto& command : commandsEntries) { + if (command.name().empty() && command.arguments().empty()) { + return &command; + } + } + return nullptr; + } + + const OpenShock::Serial::CommandEntry* bestMatch = nullptr; + size_t bestMatchArgsCount = 0; + + for (const auto& command : commandsEntries) { + auto commandName = command.name(); + auto commandArgs = command.arguments(); + + if (commandName.empty()) { + // Unnamed commands: select if argument size fits and has the closest to that many arguments seen yet + if (commandArgs.size() <= arguments.size() && commandArgs.size() >= bestMatchArgsCount) { + bestMatch = &command; + bestMatchArgsCount = commandArgs.size(); + } + } else { + // Named commands: exact name match, argument size fits and has the closest to that many arguments seen yet + if (commandName == arguments[0] && commandArgs.size() < arguments.size() && commandArgs.size() >= bestMatchArgsCount) { + bestMatch = &command; + bestMatchArgsCount = commandArgs.size(); + } + } + } + + return bestMatch; +} + +static void serialProcessLine(std::string_view line) { line = OpenShock::StringTrim(line); if (line.empty()) { @@ -465,87 +438,47 @@ void _processSerialLine(std::string_view line) // If it's not automated, we can echo the command if echo is enabled if (isAutomated) { line = line.substr(1); - } else if (s_echoEnabled) { - _echoBuffer(line); - ::Serial.println(); + } else if (serialEchoEnabled) { + serialEchoBuffer(line); } - auto parts = OpenShock::StringSplit(line, ' ', 1); - std::string_view command = OpenShock::StringTrim(parts[0]); - std::string_view arguments = parts.size() > 1 ? parts[1] : std::string_view(); + auto splitArguments = OpenShock::StringSplit(line, ' ', 1); + std::string_view command = OpenShock::StringTrim(splitArguments[0]); + std::string_view arguments = splitArguments.size() > 1 ? splitArguments[1] : std::string_view(); if (command == "help"sv) { - _handleHelpCommand(arguments, isAutomated); + handleHelpCommand(arguments, isAutomated); return; } - auto it = s_commandHandlers.find(command); - if (it == s_commandHandlers.end()) { + auto it = serialCommandHandlers.find(command); + if (it == serialCommandHandlers.end()) { SERPR_ERROR("Command \"%.*s\" not found", command.size(), command.data()); return; } - // Get potential subcommand - std::string_view firstArg; - parts = OpenShock::StringSplit(arguments, ' '); - if (parts.size() > 1) { - firstArg = OpenShock::StringTrim(parts[0]); - } else { - firstArg = arguments; - } - - // If the first argument is not empty, try to find a subcommand that matches - if (!firstArg.empty()) { - for (const auto& cmd : it->second.commands()) { - // Check subcommand name - if (cmd.name() != firstArg) { - continue; - } - - // Check if the subcommand requires arguments - if (cmd.arguments().size() > 1 && parts.size() < 2) { - _printCommandHelp(it->second); - return; - } - - // Command found, remove the subcommand from the arguments - arguments = OpenShock::StringTrim(arguments.substr(firstArg.size())); - - // Execute the subcommand - cmd.commandHandler()(arguments, isAutomated); - return; - } + auto commandEntry = getCommandEntry(OpenShock::StringSplit(arguments, ' '), it->second.commands()); + if (commandEntry == nullptr) { + printCommandHelp(it->second); + return; } - // If no subcommand was found, try to find a default command - for (const auto& cmd : it->second.commands()) { - // Skip subcommands - if (!cmd.name().empty()) { - continue; - } - - // Check if the command requires arguments - if (cmd.arguments().size() > 0 && arguments.empty()) { - _printCommandHelp(it->second); - return; - } - - // Execute the default command - cmd.commandHandler()(arguments, isAutomated); - return; + auto commandEntryName = commandEntry->name(); + if (!commandEntryName.empty()) { + arguments = OpenShock::StringTrim(arguments.substr(commandEntryName.size())); } - SERPR_ERROR("Command \"%.*s\" not found", command.size(), command.data()); + commandEntry->commandHandler()(arguments, isAutomated); } -void _serialRxTask(void*) +static void serialTaskRX(void*) { - SerialBuffer buffer(32); + OpenShock::Serial::SerialBuffer buffer(32); while (true) { - switch (_tryReadSerialLine(buffer)) { - case SerialReadResult::LineEnd: - _processSerialLine(buffer); + switch (serialTryReadLine(buffer)) { + case OpenShock::Serial::SerialReadResult::LineEnd: + serialProcessLine(buffer); // Deallocate memory if the buffer is too large if (buffer.capacity() > SERIAL_BUFFER_CLEAR_THRESHOLD) { @@ -555,16 +488,16 @@ void _serialRxTask(void*) } // Skip any remaining trailing whitespaces - _skipSerialWhitespaces(buffer); + serialSkipWhitespaces(buffer); break; - case SerialReadResult::AutoCompleteRequest: + case OpenShock::Serial::SerialReadResult::AutoCompleteRequest: ::Serial.printf(CLEAR_LINE "> %.*s [AutoComplete is not implemented]", buffer.size(), buffer.data()); break; - case SerialReadResult::Data: - _echoHandleSerialInput(buffer, true); + case OpenShock::Serial::SerialReadResult::Data: + serialHandleActivity(buffer, true); break; default: - _echoHandleSerialInput(buffer, false); + serialHandleActivity(buffer, false); break; } @@ -582,22 +515,20 @@ bool SerialInputHandler::Init() s_initialized = true; // Register command handlers - s_commandGroups = OpenShock::Serial::CommandHandlers::AllCommandHandlers(); - for (const auto& handler : s_commandGroups) { + serialCommandGroups = OpenShock::Serial::CommandHandlers::AllCommandHandlers(); + for (const auto& handler : serialCommandGroups) { OS_LOGV(TAG, "Registering command handler: %.*s", handler.name().size(), handler.name().data()); - RegisterCommandHandler(handler); + registerCommandHandler(handler); } - SerialInputHandler::PrintWelcomeHeader(); - SerialInputHandler::PrintVersionInfo(); - ::Serial.println(); + SerialInputHandler::PrintBootInfo(); - if (!Config::GetSerialInputConfigEchoEnabled(s_echoEnabled)) { + if (!Config::GetSerialInputConfigEchoEnabled(serialEchoEnabled)) { OS_LOGE(TAG, "Failed to get serial echo status from config"); return false; } - if (TaskUtils::TaskCreateExpensive(_serialRxTask, "SerialRX", 10'000, nullptr, 1, nullptr) != pdPASS) { // TODO: Profile stack size + if (TaskUtils::TaskCreateExpensive(serialTaskRX, "SerialRX", 10'000, nullptr, 1, nullptr) != pdPASS) { // TODO: Profile stack size OS_LOGE(TAG, "Failed to create serial RX task"); return false; } @@ -606,32 +537,20 @@ bool SerialInputHandler::Init() } bool SerialInputHandler::SerialEchoEnabled() { - return s_echoEnabled; + return serialEchoEnabled; } void SerialInputHandler::SetSerialEchoEnabled(bool enabled) { - s_echoEnabled = enabled; + serialEchoEnabled = enabled; } -void SerialInputHandler::PrintWelcomeHeader() +void SerialInputHandler::PrintBootInfo() { - ::Serial.print(R"( -============== OPENSHOCK ============== - Contribute @ github.com/OpenShock - Discuss @ discord.gg/OpenShock - Type 'help' for available commands -======================================= -)"); + ::Serial.print(OpenShock::StringToArduinoString(OPENSHOCK_WELCOME_HEADER_STR OPENSHOCK_VERSION_INFO_STR "\n"sv)); } void SerialInputHandler::PrintVersionInfo() { - ::Serial.print("\ - Version: " OPENSHOCK_FW_VERSION "\n\ - Build: " OPENSHOCK_FW_MODE "\n\ - Commit: " OPENSHOCK_FW_GIT_COMMIT "\n\ - Board: " OPENSHOCK_FW_BOARD "\n\ - Chip: " OPENSHOCK_FW_CHIP "\n\ -"); + ::Serial.print(OpenShock::StringToArduinoString("\n" OPENSHOCK_VERSION_INFO_STR ""sv)); } diff --git a/src/serial/command_handlers/CommandEntry.cpp b/src/serial/command_handlers/CommandEntry.cpp index 3282fe21..ef145356 100644 --- a/src/serial/command_handlers/CommandEntry.cpp +++ b/src/serial/command_handlers/CommandEntry.cpp @@ -4,30 +4,19 @@ using namespace OpenShock::Serial; CommandEntry::CommandEntry(std::string_view description, CommandHandler commandHandler) : m_description(description) - , m_commandHandler(commandHandler) { + , m_commandHandler(commandHandler) +{ } CommandEntry::CommandEntry(std::string_view name, std::string_view description, CommandHandler commandHandler) : m_name(name) , m_description(description) - , m_commandHandler(commandHandler) { + , m_commandHandler(commandHandler) +{ } -CommandArgument& CommandEntry::addArgument(std::string_view name, std::string_view constraint, std::string_view exampleValue, std::vector constraintExtensions) { +CommandArgument& CommandEntry::addArgument(std::string_view name, std::string_view constraint, std::string_view exampleValue, std::vector constraintExtensions) +{ m_arguments.push_back({name, constraint, exampleValue, constraintExtensions}); return m_arguments.back(); } - -CommandGroup::CommandGroup(std::string_view name) - : m_name(name) { -} - -CommandEntry& CommandGroup::addCommand(std::string_view description, CommandHandler commandHandler) { - m_commands.emplace_back(description, commandHandler); - return m_commands.back(); -} - -CommandEntry& CommandGroup::addCommand(std::string_view name, std::string_view description, CommandHandler commandHandler) { - m_commands.emplace_back(name, description, commandHandler); - return m_commands.back(); -} diff --git a/src/serial/command_handlers/CommandGroup.cpp b/src/serial/command_handlers/CommandGroup.cpp new file mode 100644 index 00000000..72f4b550 --- /dev/null +++ b/src/serial/command_handlers/CommandGroup.cpp @@ -0,0 +1,20 @@ +#include "serial/command_handlers/CommandGroup.h" + +using namespace OpenShock::Serial; + +CommandGroup::CommandGroup(std::string_view name) + : m_name(name) +{ +} + +CommandEntry& CommandGroup::addCommand(std::string_view description, CommandHandler commandHandler) +{ + m_commands.emplace_back(description, commandHandler); + return m_commands.back(); +} + +CommandEntry& CommandGroup::addCommand(std::string_view name, std::string_view description, CommandHandler commandHandler) +{ + m_commands.emplace_back(name, description, commandHandler); + return m_commands.back(); +} diff --git a/src/serial/command_handlers/authtoken.cpp b/src/serial/command_handlers/authtoken.cpp index fad27993..4513ac49 100644 --- a/src/serial/command_handlers/authtoken.cpp +++ b/src/serial/command_handlers/authtoken.cpp @@ -1,19 +1,31 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" #include -void _handleAuthtokenCommand(std::string_view arg, bool isAutomated) { +static void handleGet(std::string_view arg, bool isAutomated) +{ + if (!arg.empty()) { + SERPR_ERROR("Get command does not support parameters"); + return; + } + + std::string authToken; + if (!OpenShock::Config::GetBackendAuthToken(authToken)) { + SERPR_ERROR("Failed to get auth token from config"); + return; + } + + // Get auth token + SERPR_RESPONSE("AuthToken|%s", authToken.c_str()); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ if (arg.empty()) { - std::string authToken; - if (!OpenShock::Config::GetBackendAuthToken(authToken)) { - SERPR_ERROR("Failed to get auth token from config"); - return; - } - - // Get auth token - SERPR_RESPONSE("AuthToken|%s", authToken.c_str()); + SERPR_ERROR("Auth token cannot be empty"); return; } @@ -26,13 +38,32 @@ void _handleAuthtokenCommand(std::string_view arg, bool isAutomated) { } } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::AuthTokenHandler() { +static void handleClear(std::string_view arg, bool isAutomated) +{ + if (!arg.empty()) { + SERPR_ERROR("Clear command does not support parameters"); + return; + } + + bool result = OpenShock::Config::ClearBackendAuthToken(); + + if (result) { + SERPR_SUCCESS("Saved config"); + } else { + SERPR_ERROR("Failed to save config"); + } +} + +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::AuthTokenHandler() +{ auto group = OpenShock::Serial::CommandGroup("authtoken"sv); - auto& getCommand = group.addCommand("Get the backend auth token"sv, _handleAuthtokenCommand); + auto& getCommand = group.addCommand("get"sv, "Get the backend auth token"sv, handleGet); - auto& setCommand = group.addCommand("Set the auth token"sv, _handleAuthtokenCommand); + auto& setCommand = group.addCommand("set"sv, "Set the backend auth token"sv, handleSet); setCommand.addArgument("token"sv, "must be a string"sv, "mytoken"sv); + auto& clearCommand = group.addCommand("clear"sv, "Clear the backend auth token"sv, handleClear); + return group; } diff --git a/src/serial/command_handlers/domain.cpp b/src/serial/command_handlers/domain.cpp index 7b010de2..bd335621 100644 --- a/src/serial/command_handlers/domain.cpp +++ b/src/serial/command_handlers/domain.cpp @@ -1,3 +1,4 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" @@ -8,16 +9,27 @@ const char* const TAG = "Serial::CommandHandlers::Domain"; -void _handleDomainCommand(std::string_view arg, bool isAutomated) { +static void handleGet(std::string_view arg, bool isAutomated) +{ + if (!arg.empty()) { + SERPR_ERROR("Get command does not support parameters"); + return; + } + + std::string domain; + if (!OpenShock::Config::GetBackendDomain(domain)) { + SERPR_ERROR("Failed to get domain from config"); + return; + } + + // Get domain + SERPR_RESPONSE("Domain|%s", domain.c_str()); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ if (arg.empty()) { - std::string domain; - if (!OpenShock::Config::GetBackendDomain(domain)) { - SERPR_ERROR("Failed to get domain from config"); - return; - } - - // Get domain - SERPR_RESPONSE("Domain|%s", domain.c_str()); + SERPR_ERROR("Domain cannot be empty"); return; } @@ -60,13 +72,14 @@ void _handleDomainCommand(std::string_view arg, bool isAutomated) { ESP.restart(); } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::DomainHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::DomainHandler() +{ auto group = OpenShock::Serial::CommandGroup("domain"sv); - auto& getCommand = group.addCommand("Get the backend domain."sv, _handleDomainCommand); + auto& getCommand = group.addCommand("Get the backend domain."sv, handleGet); - auto& setCommand = group.addCommand("Set the backend domain."sv, _handleDomainCommand); - setCommand.addArgument("domain"sv, "must be a string"sv, "api.shocklink.net"sv); + auto& setCommand = group.addCommand("set"sv, "Set the backend domain."sv, handleSet); + setCommand.addArgument("domain"sv, "must be a string"sv, "api.openshock.app"sv); return group; } diff --git a/src/serial/command_handlers/echo.cpp b/src/serial/command_handlers/echo.cpp index 0b111dc6..2ac4aedb 100644 --- a/src/serial/command_handlers/echo.cpp +++ b/src/serial/command_handlers/echo.cpp @@ -1,18 +1,23 @@ #include "serial/command_handlers/common.h" - #include "serial/SerialInputHandler.h" #include "config/Config.h" #include "Convert.h" #include "util/StringUtils.h" -void _handleSerialEchoCommand(std::string_view arg, bool isAutomated) { - if (arg.empty()) { - // Get current serial echo status - SERPR_RESPONSE("SerialEcho|%s", OpenShock::SerialInputHandler::SerialEchoEnabled() ? "true" : "false"); +static void handleGet(std::string_view arg, bool isAutomated) +{ + if (!arg.empty()) { + SERPR_ERROR("Get command does not support parameters"); return; } + // Get current serial echo status + SERPR_RESPONSE("SerialEcho|%s", OpenShock::SerialInputHandler::SerialEchoEnabled() ? "true" : "false"); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ bool enabled; if (!OpenShock::Convert::ToBool(OpenShock::StringTrim(arg), enabled)) { SERPR_ERROR("Invalid argument (not a boolean)"); @@ -29,12 +34,13 @@ void _handleSerialEchoCommand(std::string_view arg, bool isAutomated) { } } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::EchoHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::EchoHandler() +{ auto group = OpenShock::Serial::CommandGroup("echo"sv); - auto& getCommand = group.addCommand("Get the serial echo status"sv, _handleSerialEchoCommand); + auto& getCommand = group.addCommand("Get the serial echo status"sv, handleGet); - auto& setCommand = group.addCommand("Enable/disable serial echo"sv, _handleSerialEchoCommand); + auto& setCommand = group.addCommand("set"sv, "Enable/disable serial echo"sv, handleSet); setCommand.addArgument("enabled"sv, "must be a boolean"sv, "true"sv); return group; diff --git a/src/serial/command_handlers/estop.cpp b/src/serial/command_handlers/estop.cpp index 6efc52a0..b5128b65 100644 --- a/src/serial/command_handlers/estop.cpp +++ b/src/serial/command_handlers/estop.cpp @@ -1,23 +1,30 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" #include "Convert.h" #include "estop/EStopManager.h" -void _handleEStopEnabledCommand(std::string_view arg, bool isAutomated) +static void handleGetEnabled(std::string_view arg, bool isAutomated) { + if (!arg.empty()) { + SERPR_ERROR("Get command does not support parameters"); + return; + } + bool enabled; - if (arg.empty()) { - if (!OpenShock::Config::GetEStopEnabled(enabled)) { - SERPR_ERROR("Failed to get EStop enabled from config"); - return; - } - - // Get EStop enabled - SERPR_RESPONSE("EStopEnabled|%s", enabled ? "true" : "false"); + if (!OpenShock::Config::GetEStopEnabled(enabled)) { + SERPR_ERROR("Failed to get EStop enabled from config"); return; } + // Get EStop enabled + SERPR_RESPONSE("EStopEnabled|%s", enabled ? "true" : "false"); +} + +static void handleSetEnabled(std::string_view arg, bool isAutomated) +{ + bool enabled; if (!OpenShock::Convert::ToBool(arg, enabled)) { SERPR_ERROR("Invalid argument (must be a boolean)"); return; @@ -36,20 +43,26 @@ void _handleEStopEnabledCommand(std::string_view arg, bool isAutomated) SERPR_SUCCESS("Saved config"); } -void _handleEStopPinCommand(std::string_view arg, bool isAutomated) +static void handleGetPin(std::string_view arg, bool isAutomated) { + if (!arg.empty()) { + SERPR_ERROR("Get command does not support parameters"); + return; + } + gpio_num_t estopPin; - if (arg.empty()) { - if (!OpenShock::Config::GetEStopGpioPin(estopPin)) { - SERPR_ERROR("Failed to get EStop pin from config"); - return; - } - - // Get EStop pin - SERPR_RESPONSE("EStopPin|%hhi", static_cast(estopPin)); + if (!OpenShock::Config::GetEStopGpioPin(estopPin)) { + SERPR_ERROR("Failed to get EStop pin from config"); return; } + // Get EStop pin + SERPR_RESPONSE("EStopPin|%hhi", static_cast(estopPin)); +} + +static void handleSetPin(std::string_view arg, bool isAutomated) +{ + gpio_num_t estopPin; if (!OpenShock::Convert::ToGpioNum(arg, estopPin)) { SERPR_ERROR("Invalid argument (number invalid or out of range)"); return; @@ -72,12 +85,12 @@ OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::EStopHandler { auto group = OpenShock::Serial::CommandGroup("estop"sv); - auto& getEnabledCommand = group.addCommand("enabled"sv, "Get the E-Stop enabled state."sv, _handleEStopEnabledCommand); - auto& setEnabledCommand = group.addCommand("enabled"sv, "Set the E-Stop enabled state."sv, _handleEStopEnabledCommand); + auto& getEnabledCommand = group.addCommand("enabled"sv, "Get the E-Stop enabled state."sv, handleGetEnabled); + auto& setEnabledCommand = group.addCommand("enabled"sv, "Set the E-Stop enabled state."sv, handleSetEnabled); setEnabledCommand.addArgument("enabled"sv, "must be a boolean"sv, "true"sv); - auto& getPinCommand = group.addCommand("pin"sv, "Get the GPIO pin used for the E-Stop."sv, _handleEStopPinCommand); - auto& setPinCommand = group.addCommand("pin"sv, "Set the GPIO pin used for the E-Stop."sv, _handleEStopPinCommand); + auto& getPinCommand = group.addCommand("pin"sv, "Get the GPIO pin used for the E-Stop."sv, handleGetPin); + auto& setPinCommand = group.addCommand("pin"sv, "Set the GPIO pin used for the E-Stop."sv, handleSetPin); setPinCommand.addArgument("pin"sv, "must be a number"sv, "4"sv); return group; diff --git a/src/serial/command_handlers/factoryreset.cpp b/src/serial/command_handlers/factoryreset.cpp index 5bebe53c..983d5582 100644 --- a/src/serial/command_handlers/factoryreset.cpp +++ b/src/serial/command_handlers/factoryreset.cpp @@ -1,8 +1,9 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" -void _handleFactoryResetCommand(std::string_view arg, bool isAutomated) +void handleReset(std::string_view arg, bool isAutomated) { (void)arg; @@ -16,7 +17,7 @@ OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::FactoryReset { auto group = OpenShock::Serial::CommandGroup("factoryreset"sv); - auto& cmd = group.addCommand("Reset the hub to factory defaults and restart"sv, _handleFactoryResetCommand); + auto& cmd = group.addCommand("Reset the hub to factory defaults and restart"sv, handleReset); return group; } diff --git a/src/serial/command_handlers/hostname.cpp b/src/serial/command_handlers/hostname.cpp index 6c1e9b1c..05fe1526 100644 --- a/src/serial/command_handlers/hostname.cpp +++ b/src/serial/command_handlers/hostname.cpp @@ -1,3 +1,4 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" @@ -6,18 +7,24 @@ const char* const TAG = "Serial::CommandHandlers::Domain"; -void _handleHostnameCommand(std::string_view arg, bool isAutomated) { - if (arg.empty()) { - std::string hostname; - if (!OpenShock::Config::GetWiFiHostname(hostname)) { - SERPR_ERROR("Failed to get hostname from config"); - return; - } - // Get hostname - SERPR_RESPONSE("Hostname|%s", hostname.c_str()); +static void handeGet(std::string_view arg, bool isAutomated) +{ + if (!arg.empty()) { + SERPR_ERROR("Get command does not support parameters"); return; } + std::string hostname; + if (!OpenShock::Config::GetWiFiHostname(hostname)) { + SERPR_ERROR("Failed to get hostname from config"); + return; + } + // Get hostname + SERPR_RESPONSE("Hostname|%s", hostname.c_str()); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ bool result = OpenShock::Config::SetWiFiHostname(arg); if (result) { SERPR_SUCCESS("Saved config, restarting..."); @@ -27,12 +34,13 @@ void _handleHostnameCommand(std::string_view arg, bool isAutomated) { } } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::HostnameHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::HostnameHandler() +{ auto group = OpenShock::Serial::CommandGroup("hostname"sv); - auto& getCommand = group.addCommand("Get the network hostname."sv, _handleHostnameCommand); + auto& getCommand = group.addCommand("Get the network hostname."sv, handeGet); - auto& setCommand = group.addCommand("Set the network hostname."sv, _handleHostnameCommand); + auto& setCommand = group.addCommand("set"sv, "Set the network hostname."sv, handleSet); setCommand.addArgument("hostname"sv, "must be a string"sv, "OpenShock"sv); return group; diff --git a/src/serial/command_handlers/jsonconfig.cpp b/src/serial/command_handlers/jsonconfig.cpp index 2c1091f6..c79de30e 100644 --- a/src/serial/command_handlers/jsonconfig.cpp +++ b/src/serial/command_handlers/jsonconfig.cpp @@ -1,16 +1,23 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" -void _handleJsonConfigCommand(std::string_view arg, bool isAutomated) { - if (arg.empty()) { - // Get raw config - std::string json = OpenShock::Config::GetAsJSON(true); - - SERPR_RESPONSE("JsonConfig|%s", json.c_str()); +static void handleGet(std::string_view arg, bool isAutomated) +{ + if (!arg.empty()) { + SERPR_ERROR("Get command does not support parameters"); return; } + // Get raw config + std::string json = OpenShock::Config::GetAsJSON(true); + + SERPR_RESPONSE("JsonConfig|%s", json.c_str()); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ if (!OpenShock::Config::SaveFromJSON(arg)) { SERPR_ERROR("Failed to save config"); return; @@ -21,12 +28,13 @@ void _handleJsonConfigCommand(std::string_view arg, bool isAutomated) { ESP.restart(); } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::JsonConfigHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::JsonConfigHandler() +{ auto group = OpenShock::Serial::CommandGroup("jsonconfig"sv); - auto& getCommand = group.addCommand("Get the configuration as JSON"sv, _handleJsonConfigCommand); + auto& getCommand = group.addCommand("Get the configuration as JSON"sv, handleGet); - auto& setCommand = group.addCommand("Set the configuration from JSON, and restart"sv, _handleJsonConfigCommand); + auto& setCommand = group.addCommand("set"sv, "Set the configuration from JSON, and restart"sv, handleSet); setCommand.addArgument("json"sv, "must be a valid JSON object"sv, "{ ... }"sv); return group; diff --git a/src/serial/command_handlers/keepalive.cpp b/src/serial/command_handlers/keepalive.cpp index eecfe868..854adc07 100644 --- a/src/serial/command_handlers/keepalive.cpp +++ b/src/serial/command_handlers/keepalive.cpp @@ -1,3 +1,4 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "CommandHandler.h" @@ -5,20 +6,25 @@ #include "Convert.h" #include "util/StringUtils.h" -void _handleKeepAliveCommand(std::string_view arg, bool isAutomated) { - bool keepAliveEnabled; - - if (arg.empty()) { - // Get keep alive status - if (!OpenShock::Config::GetRFConfigKeepAliveEnabled(keepAliveEnabled)) { - SERPR_ERROR("Failed to get keep-alive status from config"); - return; - } +static void handleGet(std::string_view arg, bool isAutomated) +{ + if (!arg.empty()) { + SERPR_ERROR("Get command does not support parameters"); + return; + } - SERPR_RESPONSE("KeepAlive|%s", keepAliveEnabled ? "true" : "false"); + bool keepAliveEnabled; + if (!OpenShock::Config::GetRFConfigKeepAliveEnabled(keepAliveEnabled)) { + SERPR_ERROR("Failed to get keep-alive status from config"); return; } + SERPR_RESPONSE("KeepAlive|%s", keepAliveEnabled ? "true" : "false"); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ + bool keepAliveEnabled; if (!OpenShock::Convert::ToBool(OpenShock::StringTrim(arg), keepAliveEnabled)) { SERPR_ERROR("Invalid argument (not a boolean)"); return; @@ -33,12 +39,13 @@ void _handleKeepAliveCommand(std::string_view arg, bool isAutomated) { } } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::KeepAliveHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::KeepAliveHandler() +{ auto group = OpenShock::Serial::CommandGroup("keepalive"sv); - auto& getCommand = group.addCommand("Get the shocker keep-alive status"sv, _handleKeepAliveCommand); + auto& getCommand = group.addCommand("Get the shocker keep-alive status"sv, handleGet); - auto& setCommand = group.addCommand("Enable/disable shocker keep-alive"sv, _handleKeepAliveCommand); + auto& setCommand = group.addCommand("set"sv, "Enable/disable shocker keep-alive"sv, handleSet); setCommand.addArgument("enabled"sv, "must be a boolean"sv, "true"sv); return group; diff --git a/src/serial/command_handlers/lcgoverride.cpp b/src/serial/command_handlers/lcgoverride.cpp index d2aa0ab1..8db8492b 100644 --- a/src/serial/command_handlers/lcgoverride.cpp +++ b/src/serial/command_handlers/lcgoverride.cpp @@ -1,3 +1,4 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" @@ -7,100 +8,95 @@ const char* TAG = "Serial::CommandHandlers::LcgOverride"; -void _handleLcgOverrideCommand(std::string_view arg, bool isAutomated) +static void handleGet(std::string_view arg, bool isAutomated) { - if (arg.empty()) { - std::string lcgOverride; - if (!OpenShock::Config::GetBackendLCGOverride(lcgOverride)) { - SERPR_ERROR("Failed to get LCG override from config"); - return; - } - - // Get LCG override - SERPR_RESPONSE("LcgOverride|%s", lcgOverride.c_str()); + std::string lcgOverride; + if (!OpenShock::Config::GetBackendLCGOverride(lcgOverride)) { + SERPR_ERROR("Failed to get LCG override from config"); return; } - if (OpenShock::StringHasPrefix(arg, "clear"sv)) { - if (arg.size() != 5) { - SERPR_ERROR("Invalid command (clear command should not have any arguments)"); - return; - } - - bool result = OpenShock::Config::SetBackendLCGOverride(std::string()); - if (result) { - SERPR_SUCCESS("Cleared LCG override"); - } else { - SERPR_ERROR("Failed to clear LCG override"); - } + // Get LCG override + SERPR_RESPONSE("LcgOverride|%s", lcgOverride.c_str()); +} + +static void handleClear(std::string_view arg, bool isAutomated) +{ + if (arg.size() != 5) { + SERPR_ERROR("Invalid command (clear command should not have any arguments)"); return; } - if (OpenShock::StringHasPrefix(arg, "set "sv)) { - if (arg.size() <= 4) { - SERPR_ERROR("Invalid command (set command should have an argument)"); - return; - } - - std::string_view domain = arg.substr(4); - - if (domain.size() + 40 >= OPENSHOCK_URI_BUFFER_SIZE) { - SERPR_ERROR("Domain name too long, please try increasing the \"OPENSHOCK_URI_BUFFER_SIZE\" constant in source code"); - return; - } - - char uri[OPENSHOCK_URI_BUFFER_SIZE]; - sprintf(uri, "https://%.*s/1", static_cast(domain.size()), domain.data()); - - auto resp = OpenShock::HTTP::GetJSON( - uri, - { - {"Accept", "application/json"} - }, - OpenShock::Serialization::JsonAPI::ParseLcgInstanceDetailsJsonResponse, - {200} - ); - - if (resp.result != OpenShock::HTTP::RequestResult::Success) { - SERPR_ERROR("Tried to connect to \"%.*s\", but failed with status [%d], refusing to save domain to config", domain.size(), domain.data(), resp.code); - return; - } - - OS_LOGI( - TAG, - "Successfully connected to \"%.*s\", name: %s, version: %s, current time: %s, country code: %s, FQDN: %s", - domain.size(), - domain.data(), - resp.data.name.c_str(), - resp.data.version.c_str(), - resp.data.currentTime.c_str(), - resp.data.countryCode.c_str(), - resp.data.fqdn.c_str() - ); - - bool result = OpenShock::Config::SetBackendLCGOverride(domain); - - if (result) { - SERPR_SUCCESS("Saved config"); - } else { - SERPR_ERROR("Failed to save config"); - } + bool result = OpenShock::Config::SetBackendLCGOverride(std::string()); + if (result) { + SERPR_SUCCESS("Cleared LCG override"); + } else { + SERPR_ERROR("Failed to clear LCG override"); + } +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ + if (arg.size() <= 4) { + SERPR_ERROR("Invalid command (set command should have an argument)"); return; } - SERPR_ERROR("Invalid subcommand"); + std::string_view domain = arg.substr(4); + + if (domain.size() + 40 >= OPENSHOCK_URI_BUFFER_SIZE) { + SERPR_ERROR("Domain name too long, please try increasing the \"OPENSHOCK_URI_BUFFER_SIZE\" constant in source code"); + return; + } + + char uri[OPENSHOCK_URI_BUFFER_SIZE]; + sprintf(uri, "https://%.*s/1", static_cast(domain.size()), domain.data()); + + auto resp = OpenShock::HTTP::GetJSON( + uri, + { + {"Accept", "application/json"} + }, + OpenShock::Serialization::JsonAPI::ParseLcgInstanceDetailsJsonResponse, + {200} + ); + + if (resp.result != OpenShock::HTTP::RequestResult::Success) { + SERPR_ERROR("Tried to connect to \"%.*s\", but failed with status [%d], refusing to save domain to config", domain.size(), domain.data(), resp.code); + return; + } + + OS_LOGI( + TAG, + "Successfully connected to \"%.*s\", name: %s, version: %s, current time: %s, country code: %s, FQDN: %s", + domain.size(), + domain.data(), + resp.data.name.c_str(), + resp.data.version.c_str(), + resp.data.currentTime.c_str(), + resp.data.countryCode.c_str(), + resp.data.fqdn.c_str() + ); + + bool result = OpenShock::Config::SetBackendLCGOverride(domain); + + if (result) { + SERPR_SUCCESS("Saved config"); + } else { + SERPR_ERROR("Failed to save config"); + } } OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::LcgOverrideHandler() { auto group = OpenShock::Serial::CommandGroup("lcgoverride"sv); - auto& getCommand = group.addCommand("Get the domain overridden for LCG endpoint (if any)."sv, _handleLcgOverrideCommand); + auto& getCommand = group.addCommand("get"sv, "Get the domain overridden for LCG endpoint (if any)."sv, handleGet); - auto& setCommand = group.addCommand("set"sv, "Set a domain to override the LCG endpoint."sv, _handleLcgOverrideCommand); + auto& setCommand = group.addCommand("set"sv, "Set a domain to override the LCG endpoint."sv, handleSet); setCommand.addArgument("domain"sv, "must be a string"sv, "eu1-gateway.shocklink.net"sv); - auto& clearCommand = group.addCommand("clear"sv, "Clear the overridden LCG endpoint."sv, _handleLcgOverrideCommand); + auto& clearCommand = group.addCommand("clear"sv, "Clear the overridden LCG endpoint."sv, handleClear); return group; } diff --git a/src/serial/command_handlers/networks.cpp b/src/serial/command_handlers/networks.cpp index 639deae7..db76778e 100644 --- a/src/serial/command_handlers/networks.cpp +++ b/src/serial/command_handlers/networks.cpp @@ -1,3 +1,4 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" @@ -9,35 +10,33 @@ const char* const TAG = "Serial::CommandHandlers::Networks"; -void _handleNetworksCommand(std::string_view arg, bool isAutomated) +static void handleGet(std::string_view arg, bool isAutomated) { - cJSON* root; - - if (arg.empty()) { - root = cJSON_CreateArray(); - if (root == nullptr) { - SERPR_ERROR("Failed to create JSON array"); - return; - } - - if (!OpenShock::Config::GetWiFiCredentials(root, true)) { - SERPR_ERROR("Failed to get WiFi credentials from config"); - return; - } - - char* out = cJSON_PrintUnformatted(root); - if (out == nullptr) { - SERPR_ERROR("Failed to print JSON"); - return; - } + cJSON* root = cJSON_CreateArray(); + if (root == nullptr) { + SERPR_ERROR("Failed to create JSON array"); + return; + } - SERPR_RESPONSE("Networks|%s", out); + if (!OpenShock::Config::GetWiFiCredentials(root, true)) { + SERPR_ERROR("Failed to get WiFi credentials from config"); + return; + } - cJSON_free(out); + char* out = cJSON_PrintUnformatted(root); + if (out == nullptr) { + SERPR_ERROR("Failed to print JSON"); return; } - root = cJSON_ParseWithLength(arg.data(), arg.length()); + SERPR_RESPONSE("Networks|%s", out); + + cJSON_free(out); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ + cJSON* root = cJSON_ParseWithLength(arg.data(), arg.length()); if (root == nullptr) { SERPR_ERROR("Failed to parse JSON: %s", cJSON_GetErrorPtr()); return; @@ -84,9 +83,9 @@ OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::NetworksHand { auto group = OpenShock::Serial::CommandGroup("networks"sv); - auto& getCommand = group.addCommand("Get all saved networks."sv, _handleNetworksCommand); + auto& getCommand = group.addCommand("Get all saved networks."sv, handleGet); - auto& setCommand = group.addCommand("Set all saved networks."sv, _handleNetworksCommand); + auto& setCommand = group.addCommand("set"sv, "Set all saved networks."sv, handleSet); setCommand.addArgument( "json"sv, "must be a array of objects with the following fields:"sv, diff --git a/src/serial/command_handlers/rawconfig.cpp b/src/serial/command_handlers/rawconfig.cpp index baea2fef..5b749c37 100644 --- a/src/serial/command_handlers/rawconfig.cpp +++ b/src/serial/command_handlers/rawconfig.cpp @@ -1,3 +1,4 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "config/Config.h" @@ -5,26 +6,25 @@ #include -void _handleRawConfigCommand(std::string_view arg, bool isAutomated) { - if (arg.empty()) { - std::vector buffer; - - // Get raw config - if (!OpenShock::Config::GetRaw(buffer)) { - SERPR_ERROR("Failed to get raw config"); - return; - } - - std::string base64; - if (!OpenShock::Base64Utils::Encode(buffer.data(), buffer.size(), base64)) { - SERPR_ERROR("Failed to encode raw config to base64"); - return; - } +static void handleGet(std::string_view arg, bool isAutomated) +{ + std::vector buffer; + if (!OpenShock::Config::GetRaw(buffer)) { + SERPR_ERROR("Failed to get raw config"); + return; + } - SERPR_RESPONSE("RawConfig|%s", base64.c_str()); + std::string base64; + if (!OpenShock::Base64Utils::Encode(buffer.data(), buffer.size(), base64)) { + SERPR_ERROR("Failed to encode raw config to base64"); return; } + SERPR_RESPONSE("RawConfig|%s", base64.c_str()); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ std::vector buffer; if (!OpenShock::Base64Utils::Decode(arg.data(), arg.length(), buffer)) { SERPR_ERROR("Failed to decode base64"); @@ -41,12 +41,13 @@ void _handleRawConfigCommand(std::string_view arg, bool isAutomated) { ESP.restart(); } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::RawConfigHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::RawConfigHandler() +{ auto group = OpenShock::Serial::CommandGroup("rawconfig"sv); - auto& getCommand = group.addCommand("Get the raw binary config"sv, _handleRawConfigCommand); + auto& getCommand = group.addCommand("Get the raw binary config"sv, handleGet); - auto& setCommand = group.addCommand("Set the raw binary config, and restart"sv, _handleRawConfigCommand); + auto& setCommand = group.addCommand("set"sv, "Set the raw binary config, and restart"sv, handleSet); setCommand.addArgument("base64"sv, "must be a base64 encoded string"sv, "(base64 encoded binary data)"sv); return group; diff --git a/src/serial/command_handlers/restart.cpp b/src/serial/command_handlers/restart.cpp index e9c898b2..ba6d57e4 100644 --- a/src/serial/command_handlers/restart.cpp +++ b/src/serial/command_handlers/restart.cpp @@ -1,18 +1,23 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include -void _handleRestartCommand(std::string_view arg, bool isAutomated) { +#include + +static void handleRestart(std::string_view arg, bool isAutomated) +{ (void)arg; ::Serial.println("Restarting ESP..."); - ESP.restart(); + esp_restart(); } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::RestartHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::RestartHandler() +{ auto group = OpenShock::Serial::CommandGroup("restart"sv); - auto& cmd = group.addCommand("Restart the board"sv, _handleRestartCommand); + auto& cmd = group.addCommand("Restart the board"sv, handleRestart); return group; } diff --git a/src/serial/command_handlers/rftransmit.cpp b/src/serial/command_handlers/rftransmit.cpp index 6b4905a1..aa3163a2 100644 --- a/src/serial/command_handlers/rftransmit.cpp +++ b/src/serial/command_handlers/rftransmit.cpp @@ -1,9 +1,10 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "CommandHandler.h" #include "serialization/JsonSerial.h" -void _handleRFTransmitCommand(std::string_view arg, bool isAutomated) +static void handleRfTransmit(std::string_view arg, bool isAutomated) { if (arg.empty()) { SERPR_ERROR("No command"); @@ -37,7 +38,7 @@ OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::RfTransmitHa { auto group = OpenShock::Serial::CommandGroup("rftransmit"sv); - auto& cmd = group.addCommand("Transmit a RF command"sv, _handleRFTransmitCommand); + auto& cmd = group.addCommand("Transmit a RF command"sv, handleRfTransmit); cmd.addArgument( "json"sv, "must be a JSON object with the following fields:"sv, diff --git a/src/serial/command_handlers/rftxpin.cpp b/src/serial/command_handlers/rftxpin.cpp index 0155f3ea..64f75242 100644 --- a/src/serial/command_handlers/rftxpin.cpp +++ b/src/serial/command_handlers/rftxpin.cpp @@ -1,3 +1,4 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "CommandHandler.h" @@ -5,21 +6,21 @@ #include "Convert.h" #include "SetGPIOResultCode.h" -void _handleRfTxPinCommand(std::string_view arg, bool isAutomated) +static void handleGet(std::string_view arg, bool isAutomated) { gpio_num_t txPin; - - if (arg.empty()) { - if (!OpenShock::Config::GetRFConfigTxPin(txPin)) { - SERPR_ERROR("Failed to get RF TX pin from config"); - return; - } - - // Get rmt pin - SERPR_RESPONSE("RmtPin|%hhi", static_cast(txPin)); + if (!OpenShock::Config::GetRFConfigTxPin(txPin)) { + SERPR_ERROR("Failed to get RF TX pin from config"); return; } + // Get rmt pin + SERPR_RESPONSE("RmtPin|%hhi", static_cast(txPin)); +} + +static void handleSet(std::string_view arg, bool isAutomated) +{ + gpio_num_t txPin; if (!OpenShock::Convert::ToGpioNum(arg, txPin)) { SERPR_ERROR("Invalid argument (number invalid or out of range)"); } @@ -49,9 +50,9 @@ OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::RfTxPinHandl { auto group = OpenShock::Serial::CommandGroup("rftxpin"sv); - auto& getCommand = group.addCommand("Get the GPIO pin used for the radio transmitter"sv, _handleRfTxPinCommand); + auto& getCommand = group.addCommand("Get the GPIO pin used for the radio transmitter"sv, handleGet); - auto& setCommand = group.addCommand("Set the GPIO pin used for the radio transmitter"sv, _handleRfTxPinCommand); + auto& setCommand = group.addCommand("set"sv, "Set the GPIO pin used for the radio transmitter"sv, handleSet); setCommand.addArgument("pin"sv, "must be a number"sv, "15"sv); return group; diff --git a/src/serial/command_handlers/sysinfo.cpp b/src/serial/command_handlers/sysinfo.cpp index 052f495d..c4ac82ed 100644 --- a/src/serial/command_handlers/sysinfo.cpp +++ b/src/serial/command_handlers/sysinfo.cpp @@ -1,3 +1,4 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "FormatHelpers.h" @@ -5,7 +6,8 @@ #include "wifi/WiFiManager.h" #include "wifi/WiFiNetwork.h" -void _handleDebugInfoCommand(std::string_view arg, bool isAutomated) { +static void handleDebugInfo(std::string_view arg, bool isAutomated) +{ (void)arg; SERPR_RESPONSE("RTOSInfo|Free Heap|%u", xPortGetFreeHeapSize()); @@ -35,10 +37,11 @@ void _handleDebugInfoCommand(std::string_view arg, bool isAutomated) { } } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::SysInfoHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::SysInfoHandler() +{ auto group = OpenShock::Serial::CommandGroup("sysinfo"sv); - auto& cmd = group.addCommand("Get system information from RTOS, WiFi, etc."sv, _handleDebugInfoCommand); + auto& cmd = group.addCommand("Get system information from RTOS, WiFi, etc."sv, handleDebugInfo); return group; } diff --git a/src/serial/command_handlers/validgpios.cpp b/src/serial/command_handlers/validgpios.cpp index 24e16913..fc52a560 100644 --- a/src/serial/command_handlers/validgpios.cpp +++ b/src/serial/command_handlers/validgpios.cpp @@ -1,10 +1,12 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "Chipset.h" #include -void _handleValidGpiosCommand(std::string_view arg, bool isAutomated) { +static void handleValidGpios(std::string_view arg, bool isAutomated) +{ if (!arg.empty()) { SERPR_ERROR("Invalid argument (too many arguments)"); return; @@ -18,7 +20,7 @@ void _handleValidGpiosCommand(std::string_view arg, bool isAutomated) { for (std::size_t i = 0; i < pins.size(); i++) { if (pins[i]) { buffer.append(std::to_string(i)); - buffer.append(","); + buffer.append(','); } } @@ -29,10 +31,11 @@ void _handleValidGpiosCommand(std::string_view arg, bool isAutomated) { SERPR_RESPONSE("ValidGPIOs|%s", buffer.c_str()); } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::ValidGpiosHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::ValidGpiosHandler() +{ auto group = OpenShock::Serial::CommandGroup("validgpios"sv); - auto& cmd = group.addCommand("List all valid GPIO pins"sv, _handleValidGpiosCommand); + auto& cmd = group.addCommand("List all valid GPIO pins"sv, handleValidGpios); return group; } diff --git a/src/serial/command_handlers/version.cpp b/src/serial/command_handlers/version.cpp index 3b3c5508..19eb429c 100644 --- a/src/serial/command_handlers/version.cpp +++ b/src/serial/command_handlers/version.cpp @@ -1,20 +1,25 @@ +#include "serial/command_handlers/CommandGroup.h" #include "serial/command_handlers/common.h" #include "serial/SerialInputHandler.h" #include -void _handleVersionCommand(std::string_view arg, bool isAutomated) { - (void)arg; +static void handleVersion(std::string_view arg, bool isAutomated) +{ + if (!arg.empty()) { + SERPR_ERROR("Command does not support parameters"); + return; + } - ::Serial.print("\n"); OpenShock::SerialInputHandler::PrintVersionInfo(); } -OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::VersionHandler() { +OpenShock::Serial::CommandGroup OpenShock::Serial::CommandHandlers::VersionHandler() +{ auto group = OpenShock::Serial::CommandGroup("version"sv); - auto cmd = group.addCommand("Print version information"sv, _handleVersionCommand); + auto cmd = group.addCommand("Print version information"sv, handleVersion); return group; }