From 44b5fe157a2d2a03f64880cb8dde46741d206ea4 Mon Sep 17 00:00:00 2001 From: Lorow Date: Thu, 9 Oct 2025 23:12:03 +0200 Subject: [PATCH] Rewrite commands to nlohmann-json --- components/CommandManager/CMakeLists.txt | 4 +- .../CommandManager/CommandManager.cpp | 92 ++--- .../CommandManager/CommandManager.hpp | 9 +- .../CommandManager/CommandResult.cpp | 24 ++ .../CommandManager/CommandResult.hpp | 65 ++-- .../CommandManager/CommandSchema.cpp | 81 ++++ .../CommandManager/CommandSchema.hpp | 37 +- .../commands/camera_commands.cpp | 83 +--- .../commands/camera_commands.hpp | 7 +- .../commands/config_commands.cpp | 38 +- .../commands/config_commands.hpp | 5 +- .../commands/device_commands.cpp | 78 +--- .../commands/device_commands.hpp | 8 +- .../CommandManager/commands/mdns_commands.cpp | 30 +- .../CommandManager/commands/mdns_commands.hpp | 5 +- .../CommandManager/commands/scan_commands.cpp | 27 +- .../CommandManager/commands/scan_commands.hpp | 2 +- .../commands/simple_commands.cpp | 22 +- .../commands/simple_commands.hpp | 5 +- .../CommandManager/commands/wifi_commands.cpp | 360 +++++------------- .../CommandManager/commands/wifi_commands.hpp | 14 +- components/RestAPI/RestAPI/RestAPI.cpp | 78 ++-- .../SerialManager/SerialManager.cpp | 22 +- .../SerialManager/SerialManager.hpp | 2 + 24 files changed, 441 insertions(+), 657 deletions(-) create mode 100644 components/CommandManager/CommandManager/CommandResult.cpp create mode 100644 components/CommandManager/CommandManager/CommandSchema.cpp diff --git a/components/CommandManager/CMakeLists.txt b/components/CommandManager/CMakeLists.txt index dc8ed4e..6de7ed4 100644 --- a/components/CommandManager/CMakeLists.txt +++ b/components/CommandManager/CMakeLists.txt @@ -1,6 +1,8 @@ idf_component_register( SRCS "CommandManager/CommandManager.cpp" + "CommandManager/CommandResult.cpp" + "CommandManager/CommandSchema.cpp" "CommandManager/commands/simple_commands.cpp" "CommandManager/commands/camera_commands.cpp" "CommandManager/commands/wifi_commands.cpp" @@ -11,5 +13,5 @@ idf_component_register( INCLUDE_DIRS "CommandManager" "CommandManager/commands" - REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks wifiManager Helpers LEDManager Monitoring + REQUIRES ProjectConfig nlohmann-json CameraManager OpenIrisTasks wifiManager Helpers LEDManager Monitoring ) \ No newline at end of file diff --git a/components/CommandManager/CommandManager/CommandManager.cpp b/components/CommandManager/CommandManager/CommandManager.cpp index ca1b68d..644ff0a 100644 --- a/components/CommandManager/CommandManager/CommandManager.cpp +++ b/components/CommandManager/CommandManager/CommandManager.cpp @@ -12,7 +12,6 @@ std::unordered_map commandTypeMap = { {"set_mdns", CommandType::SET_MDNS}, {"get_mdns_name", CommandType::GET_MDNS_NAME}, {"update_camera", CommandType::UPDATE_CAMERA}, - {"restart_camera", CommandType::RESTART_CAMERA}, {"save_config", CommandType::SAVE_CONFIG}, {"get_config", CommandType::GET_CONFIG}, {"reset_config", CommandType::RESET_CONFIG}, @@ -30,7 +29,7 @@ std::unordered_map commandTypeMap = { {"get_who_am_i", CommandType::GET_WHO_AM_I}, }; -std::function CommandManager::createCommand(const CommandType type, std::string_view json) const +std::function CommandManager::createCommand(const CommandType type, const nlohmann::json &json) const { switch (type) { @@ -39,8 +38,6 @@ std::function CommandManager::createCommand(const CommandType t case CommandType::PAUSE: return [json] { return PauseCommand(json); }; - return [json] - { return PauseCommand(json); }; case CommandType::UPDATE_OTA_CREDENTIALS: return [this, json] { return updateOTACredentialsCommand(this->registry, json); }; @@ -65,9 +62,6 @@ std::function CommandManager::createCommand(const CommandType t case CommandType::UPDATE_CAMERA: return [this, json] { return updateCameraCommand(this->registry, json); }; - case CommandType::RESTART_CAMERA: - return [this, json] - { return restartCameraCommand(this->registry, json); }; case CommandType::GET_CONFIG: return [this] { return getConfigCommand(this->registry); }; @@ -116,70 +110,56 @@ std::function CommandManager::createCommand(const CommandType t } } -CommandResult CommandManager::executeFromJson(const std::string_view json) const +CommandManagerResponse CommandManager::executeFromJson(const std::string_view json) const { - cJSON *parsedJson = cJSON_Parse(json.data()); - if (parsedJson == nullptr) - return CommandResult::getErrorResult("Initial JSON Parse: Invalid JSON"); - - const cJSON *commandData = nullptr; - const cJSON *commands = cJSON_GetObjectItem(parsedJson, "commands"); - cJSON *responseDocument = cJSON_CreateObject(); - cJSON *responses = cJSON_CreateArray(); - cJSON *response = nullptr; - - cJSON_AddItemToObject(responseDocument, "results", responses); - - if (cJSON_GetArraySize(commands) == 0) + if (!nlohmann::json::accept(json)) { - cJSON_Delete(parsedJson); - return CommandResult::getErrorResult("Commands missing"); + return CommandManagerResponse(nlohmann::json{{"error", "Initial JSON Parse - Invalid JSON"}}); } - cJSON_ArrayForEach(commandData, commands) + nlohmann::json parsedJson = nlohmann::json::parse(json); + if (!parsedJson.contains("commands") || !parsedJson["commands"].is_array() || parsedJson["commands"].empty()) { - const cJSON *commandTypeString = cJSON_GetObjectItem(commandData, "command"); - if (commandTypeString == nullptr) - { - return CommandResult::getErrorResult("Unknown command - missing command type"); - } - - const cJSON *commandPayload = cJSON_GetObjectItem(commandData, "data"); - const auto commandType = commandTypeMap.at(std::string(commandTypeString->valuestring)); - - std::string commandPayloadString; - if (commandPayload != nullptr) - commandPayloadString = std::string(cJSON_Print(commandPayload)); - - auto command = createCommand(commandType, commandPayloadString); - if (command == nullptr) - { - cJSON_Delete(parsedJson); - return CommandResult::getErrorResult("Unknown command"); - } - response = cJSON_CreateString(command().getResult().c_str()); - cJSON_AddItemToArray(responses, response); + return CommandManagerResponse(CommandResult::getErrorResult("Commands missing")); } - char *jsonString = cJSON_Print(responseDocument); - cJSON_Delete(responseDocument); - cJSON_Delete(parsedJson); + nlohmann::json results = nlohmann::json::array(); - // Return the JSON response directly without wrapping it - // The responseDocument already contains the proper format: {"results": [...]} - CommandResult result = CommandResult::getRawJsonResult(jsonString); - free(jsonString); - return result; + for (auto &commandObject : parsedJson["commands"].items()) + { + auto commandData = commandObject.value(); + if (!commandData.contains("command")) + { + return CommandManagerResponse({{"command", "Unknown command"}, {"error", "Missing command type"}}); + } + + const auto commandName = commandData["command"].get(); + if (!commandTypeMap.contains(commandName)) + { + return CommandManagerResponse({{"command", commandName}, {"error", "Unknown command"}}); + } + + const auto commandType = commandTypeMap.at(commandName); + const auto commandPayload = commandData.contains("data") ? commandData["data"] : nlohmann::json::object(); + + auto command = createCommand(commandType, commandPayload); + results.push_back({ + {"command", commandName}, + {"result", command()}, + }); + } + auto response = nlohmann::json{{"results", results}}; + return CommandManagerResponse(response); } -CommandResult CommandManager::executeFromType(const CommandType type, const std::string_view json) const +CommandManagerResponse CommandManager::executeFromType(const CommandType type, const std::string_view json) const { const auto command = createCommand(type, json); if (command == nullptr) { - return CommandResult::getErrorResult("Unknown command"); + return CommandManagerResponse({{"command", type}, {"error", "Unknown command"}}); } - return command(); + return CommandManagerResponse({"result", command()}); } \ No newline at end of file diff --git a/components/CommandManager/CommandManager/CommandManager.hpp b/components/CommandManager/CommandManager/CommandManager.hpp index 9e8886a..5563570 100644 --- a/components/CommandManager/CommandManager/CommandManager.hpp +++ b/components/CommandManager/CommandManager/CommandManager.hpp @@ -18,7 +18,7 @@ #include "commands/wifi_commands.hpp" #include "commands/device_commands.hpp" #include "commands/scan_commands.hpp" -#include +#include enum class CommandType { @@ -33,7 +33,6 @@ enum class CommandType SET_MDNS, GET_MDNS_NAME, UPDATE_CAMERA, - RESTART_CAMERA, SAVE_CONFIG, GET_CONFIG, RESET_CONFIG, @@ -57,10 +56,10 @@ class CommandManager public: explicit CommandManager(const std::shared_ptr &DependencyRegistry) : registry(DependencyRegistry) {}; - std::function createCommand(CommandType type, std::string_view json) const; + std::function createCommand(const CommandType type, const nlohmann::json &json) const; - CommandResult executeFromJson(std::string_view json) const; - CommandResult executeFromType(CommandType type, std::string_view json) const; + CommandManagerResponse executeFromJson(std::string_view json) const; + CommandManagerResponse executeFromType(CommandType type, std::string_view json) const; }; #endif \ No newline at end of file diff --git a/components/CommandManager/CommandManager/CommandResult.cpp b/components/CommandManager/CommandManager/CommandResult.cpp new file mode 100644 index 0000000..9396908 --- /dev/null +++ b/components/CommandManager/CommandManager/CommandResult.cpp @@ -0,0 +1,24 @@ +#include "CommandResult.hpp" + +void to_json(nlohmann::json &j, const CommandResult &result) +{ + j = nlohmann::json{{"status", result.isSuccess() ? "success" : "error"}, {"data", result.getData()}}; +} + +// defined only for interface compatibility, should not be used directly +void from_json(const nlohmann::json &j, CommandResult &result) +{ + auto message = j.at("message"); + j.at("status") == "success" ? result = CommandResult::getSuccessResult(message) : result = CommandResult::getErrorResult(message); +} + +void to_json(nlohmann::json &j, const CommandManagerResponse &result) +{ + j = result.getData(); +} + +// defined only for interface compatibility, should not be used directly +void from_json(const nlohmann::json &j, CommandManagerResponse &result) +{ + result = CommandManagerResponse(j.at("result")); +} \ No newline at end of file diff --git a/components/CommandManager/CommandManager/CommandResult.hpp b/components/CommandManager/CommandManager/CommandResult.hpp index 4a3bfbb..9dee2b0 100644 --- a/components/CommandManager/CommandManager/CommandResult.hpp +++ b/components/CommandManager/CommandManager/CommandResult.hpp @@ -1,9 +1,13 @@ +#pragma once #ifndef COMMAND_RESULT #define COMMAND_RESULT #include #include #include +#include + +using json = nlohmann::json; class CommandResult { @@ -15,60 +19,41 @@ public: }; private: + nlohmann::json data; Status status; - std::string message; public: - CommandResult(std::string message, const Status status) - { - this->status = status; - - // Escape quotes and backslashes in the message for JSON - std::string escapedMessage = message; - size_t pos = 0; - // First escape backslashes - while ((pos = escapedMessage.find('\\', pos)) != std::string::npos) { - escapedMessage.replace(pos, 1, "\\\\"); - pos += 2; - } - // Then escape quotes - pos = 0; - while ((pos = escapedMessage.find('"', pos)) != std::string::npos) { - escapedMessage.replace(pos, 1, "\\\""); - pos += 2; - } - - if (status == Status::SUCCESS) - { - this->message = std::format("{{\"result\":\"{}\"}}", escapedMessage); - } - else - { - this->message = std::format("{{\"error\":\"{}\"}}", escapedMessage); - } - } + CommandResult(nlohmann::json data, const Status status) : data(data), status(status) {} bool isSuccess() const { return status == Status::SUCCESS; } - static CommandResult getSuccessResult(const std::string &message) + static CommandResult getSuccessResult(nlohmann::json message) { return CommandResult(message, Status::SUCCESS); } - static CommandResult getErrorResult(const std::string &message) + static CommandResult getErrorResult(nlohmann::json message) { return CommandResult(message, Status::FAILURE); } - // Create a result that returns raw JSON without wrapper - static CommandResult getRawJsonResult(const std::string &jsonMessage) - { - CommandResult result("", Status::SUCCESS); - result.message = jsonMessage; - return result; - } - - std::string getResult() const { return this->message; } + nlohmann::json getData() const { return this->data; } }; +void to_json(nlohmann::json &j, const CommandResult &result); +void from_json(const nlohmann::json &j, CommandResult &result); + +class CommandManagerResponse +{ +private: + nlohmann::json data; + +public: + CommandManagerResponse(nlohmann::json data) : data(data) {} + nlohmann::json getData() const { return this->data; } +}; + +void to_json(nlohmann::json &j, const CommandManagerResponse &result); +void from_json(const nlohmann::json &j, CommandManagerResponse &result); + #endif \ No newline at end of file diff --git a/components/CommandManager/CommandManager/CommandSchema.cpp b/components/CommandManager/CommandManager/CommandSchema.cpp new file mode 100644 index 0000000..2ecead6 --- /dev/null +++ b/components/CommandManager/CommandManager/CommandSchema.cpp @@ -0,0 +1,81 @@ +#include "CommandSchema.hpp" + +void to_json(nlohmann::json &j, const UpdateWifiPayload &payload) +{ + j = nlohmann::json{{"name", payload.name}, {"ssid", payload.ssid}, {"password", payload.password}, {"channel", payload.channel}, {"power", payload.power}}; +} + +void from_json(const nlohmann::json &j, UpdateWifiPayload &payload) +{ + payload.name = j.at("name").get(); + if (j.contains("ssid")) + { + payload.ssid = j.at("ssid").get(); + } + + if (j.contains("password")) + { + payload.password = j.at("password").get(); + } + + if (j.contains("channel")) + { + payload.channel = j.at("channel").get(); + } + + if (j.contains("power")) + { + payload.power = j.at("power").get(); + } +} + +void to_json(nlohmann::json &j, const UpdateAPWiFiPayload &payload) +{ + j = nlohmann::json{{"ssid", payload.ssid}, {"password", payload.password}, {"channel", payload.channel}}; +} + +void from_json(const nlohmann::json &j, UpdateAPWiFiPayload &payload) +{ + if (j.contains("ssid")) + { + payload.ssid = j.at("ssid").get(); + } + + if (j.contains("password")) + { + payload.password = j.at("password").get(); + } + if (j.contains("channel")) + { + payload.channel = j.at("channel").get(); + } +} + +void to_json(nlohmann::json &j, const UpdateCameraConfigPayload &payload) +{ + j = nlohmann::json{{"vflip", payload.vflip}, {"href", payload.href}, {"framesize", payload.framesize}, {"quality", payload.quality}, {"brightness", payload.brightness}}; +} + +void from_json(const nlohmann::json &j, UpdateCameraConfigPayload &payload) +{ + if (j.contains("vflip")) + { + payload.vflip = j.at("vflip").get(); + } + if (j.contains("href")) + { + payload.href = j.at("href").get(); + } + if (j.contains("framesize")) + { + payload.framesize = j.at("framesize").get(); + } + if (j.contains("quality")) + { + payload.quality = j.at("quality").get(); + } + if (j.contains("brightness")) + { + payload.brightness = j.at("brightness").get(); + } +} \ No newline at end of file diff --git a/components/CommandManager/CommandManager/CommandSchema.hpp b/components/CommandManager/CommandManager/CommandSchema.hpp index 72e5440..ee761dc 100644 --- a/components/CommandManager/CommandManager/CommandSchema.hpp +++ b/components/CommandManager/CommandManager/CommandSchema.hpp @@ -1,31 +1,40 @@ #ifndef COMMAND_SCHEMA_HPP #define COMMAND_SCHEMA_HPP +#include -struct BasePayload {}; +struct BasePayload +{ +}; struct WifiPayload : BasePayload { - std::string networkName; + std::string name; std::string ssid; std::string password; uint8_t channel; uint8_t power; }; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(WifiPayload, name, ssid, password, channel, power) + struct UpdateWifiPayload : BasePayload { - std::string networkName; + std::string name; std::optional ssid; std::optional password; std::optional channel; std::optional power; }; +void to_json(nlohmann::json &j, const UpdateWifiPayload &payload); +void from_json(const nlohmann::json &j, UpdateWifiPayload &payload); struct deleteNetworkPayload : BasePayload { - std::string networkName; + std::string name; }; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(deleteNetworkPayload, name) + struct UpdateAPWiFiPayload : BasePayload { std::optional ssid; @@ -33,11 +42,15 @@ struct UpdateAPWiFiPayload : BasePayload std::optional channel; }; +void to_json(nlohmann::json &j, const UpdateAPWiFiPayload &payload); +void from_json(const nlohmann::json &j, UpdateAPWiFiPayload &payload); struct MDNSPayload : BasePayload { std::string hostname; }; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MDNSPayload, hostname) + struct UpdateCameraConfigPayload : BasePayload { std::optional vflip; @@ -48,18 +61,6 @@ struct UpdateCameraConfigPayload : BasePayload // TODO add more options here }; -struct ResetConfigPayload : BasePayload -{ - std::string section; -}; - -struct RestartCameraPayload : BasePayload -{ - bool mode; -}; - -struct PausePayload : BasePayload -{ - bool pause; -}; +void to_json(nlohmann::json &j, const UpdateCameraConfigPayload &payload); +void from_json(const nlohmann::json &j, UpdateCameraConfigPayload &payload); #endif \ No newline at end of file diff --git a/components/CommandManager/CommandManager/commands/camera_commands.cpp b/components/CommandManager/CommandManager/commands/camera_commands.cpp index b092541..b58dc62 100644 --- a/components/CommandManager/CommandManager/commands/camera_commands.cpp +++ b/components/CommandManager/CommandManager/commands/camera_commands.cpp @@ -1,86 +1,17 @@ #include "camera_commands.hpp" -std::optional parseUpdateCameraPayload(std::string_view jsonPayload) +CommandResult updateCameraCommand(std::shared_ptr registry, const nlohmann::json &json) { - UpdateCameraConfigPayload payload; - cJSON *parsedJson = cJSON_Parse(jsonPayload.data()); - - if (parsedJson == nullptr) - return std::nullopt; - - cJSON *vflipObject = cJSON_GetObjectItem(parsedJson, "vflip"); - cJSON *hrefObject = cJSON_GetObjectItem(parsedJson, "href"); - cJSON *framesize = cJSON_GetObjectItem(parsedJson, "framesize"); - cJSON *quality = cJSON_GetObjectItem(parsedJson, "quality"); - cJSON *brightness = cJSON_GetObjectItem(parsedJson, "brightness"); - - if (vflipObject != nullptr) - payload.vflip = vflipObject->valueint; - if (hrefObject != nullptr) - payload.href = hrefObject->valueint; - if (framesize != nullptr) - payload.framesize = framesize->valueint; - if (quality != nullptr) - payload.quality = quality->valueint; - if (brightness != nullptr) - payload.brightness = brightness->valueint; - - cJSON_Delete(parsedJson); - return payload; -} - -std::optional parseRestartCameraPayload(std::string_view jsonPayload) -{ - RestartCameraPayload payload; - cJSON *parsedJson = cJSON_Parse(jsonPayload.data()); - - if (parsedJson == nullptr) - return std::nullopt; - - cJSON *mode = cJSON_GetObjectItem(parsedJson, "mode"); - - if (mode == nullptr) - { - cJSON_Delete(parsedJson); - } - - payload.mode = (bool)mode->valueint; - - cJSON_Delete(parsedJson); - - return payload; -} - -CommandResult updateCameraCommand(std::shared_ptr registry, std::string_view jsonPayload) -{ - auto payload = parseUpdateCameraPayload(jsonPayload); - if (!payload.has_value()) - { - return CommandResult::getErrorResult("Invalid payload"); - } - auto updatedConfig = payload.value(); + auto payload = json.get(); std::shared_ptr projectConfig = registry->resolve(DependencyType::project_config); auto oldConfig = projectConfig->getCameraConfig(); projectConfig->setCameraConfig( - updatedConfig.vflip.has_value() ? updatedConfig.vflip.value() : oldConfig.vflip, - updatedConfig.framesize.has_value() ? updatedConfig.framesize.value() : oldConfig.framesize, - updatedConfig.href.has_value() ? updatedConfig.href.value() : oldConfig.href, - updatedConfig.quality.has_value() ? updatedConfig.quality.value() : oldConfig.quality, - updatedConfig.brightness.has_value() ? updatedConfig.brightness.value() : oldConfig.brightness); + payload.vflip.has_value() ? payload.vflip.value() : oldConfig.vflip, + payload.framesize.has_value() ? payload.framesize.value() : oldConfig.framesize, + payload.href.has_value() ? payload.href.value() : oldConfig.href, + payload.quality.has_value() ? payload.quality.value() : oldConfig.quality, + payload.brightness.has_value() ? payload.brightness.value() : oldConfig.brightness); return CommandResult::getSuccessResult("Config updated"); -} - -CommandResult restartCameraCommand(std::shared_ptr registry, std::string_view jsonPayload) -{ - auto payload = parseRestartCameraPayload(jsonPayload); - if (!payload.has_value()) - { - return CommandResult::getErrorResult("Invalid payload"); - } - - std::shared_ptr cameraManager = registry->resolve(DependencyType::camera_manager); - cameraManager->resetCamera(payload.value().mode); - return CommandResult::getSuccessResult("Camera restarted"); } \ No newline at end of file diff --git a/components/CommandManager/CommandManager/commands/camera_commands.hpp b/components/CommandManager/CommandManager/commands/camera_commands.hpp index 9de565a..81780b9 100644 --- a/components/CommandManager/CommandManager/commands/camera_commands.hpp +++ b/components/CommandManager/CommandManager/commands/camera_commands.hpp @@ -4,17 +4,14 @@ #include #include #include -#include #include "CommandResult.hpp" #include "CommandSchema.hpp" #include "DependencyRegistry.hpp" #include +#include -std::optional parseUpdateCameraPayload(std::string_view jsonPayload); -CommandResult updateCameraCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult updateCameraCommand(std::shared_ptr registry, const nlohmann::json &json); -std::optional parseRestartCameraPayload(std::string_view jsonPayload); -CommandResult restartCameraCommand(std::shared_ptr registry, std::string_view jsonPayload); #endif // add cropping command \ No newline at end of file diff --git a/components/CommandManager/CommandManager/commands/config_commands.cpp b/components/CommandManager/CommandManager/commands/config_commands.cpp index b0d52b9..b6fa289 100644 --- a/components/CommandManager/CommandManager/commands/config_commands.cpp +++ b/components/CommandManager/CommandManager/commands/config_commands.cpp @@ -1,27 +1,5 @@ #include "config_commands.hpp" -std::optional parseResetConfigCommandPayload(std::string_view jsonPayload) -{ - ResetConfigPayload payload; - cJSON *parsedJson = cJSON_Parse(jsonPayload.data()); - - if (parsedJson == nullptr) - return std::nullopt; - - cJSON *section = cJSON_GetObjectItem(parsedJson, "section"); - - if (section == nullptr) - { - cJSON_Delete(parsedJson); - return std::nullopt; - } - - payload.section = std::string(section->valuestring); - - cJSON_Delete(parsedJson); - return payload; -} - CommandResult saveConfigCommand(std::shared_ptr registry) { std::shared_ptr projectConfig = registry->resolve(DependencyType::project_config); @@ -38,29 +16,27 @@ CommandResult getConfigCommand(std::shared_ptr registry) return CommandResult::getSuccessResult(configRepresentation); } -CommandResult resetConfigCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult resetConfigCommand(std::shared_ptr registry, const nlohmann::json &json) { std::array supported_sections = { "all", }; - auto payload = parseResetConfigCommandPayload(jsonPayload); - - if (!payload.has_value()) + if (!json.contains("section")) { - return CommandResult::getErrorResult("Invalid payload or missing section"); + return CommandResult::getErrorResult("Invalid payload - missing section"); } - auto sectionPayload = payload.value(); + auto section = json["section"].get(); - if (std::find(supported_sections.begin(), supported_sections.end(), sectionPayload.section) == supported_sections.end()) + if (std::find(supported_sections.begin(), supported_sections.end(), section) == supported_sections.end()) { return CommandResult::getErrorResult("Selected section is unsupported"); } // we cannot match on string, and making a map would be overkill right now, sooo - // todo, add more granual control for other sections, like only reset camera, or only reset wifi - if (sectionPayload.section == "all") + // todo, add more granular control for other sections, like only reset camera, or only reset wifi + if (section == "all") { std::shared_ptr projectConfig = registry->resolve(DependencyType::project_config); projectConfig->reset(); diff --git a/components/CommandManager/CommandManager/commands/config_commands.hpp b/components/CommandManager/CommandManager/commands/config_commands.hpp index 1266425..337d267 100644 --- a/components/CommandManager/CommandManager/commands/config_commands.hpp +++ b/components/CommandManager/CommandManager/commands/config_commands.hpp @@ -2,13 +2,12 @@ #include #include #include -#include #include "CommandResult.hpp" #include "CommandSchema.hpp" #include "DependencyRegistry.hpp" +#include CommandResult saveConfigCommand(std::shared_ptr registry); CommandResult getConfigCommand(std::shared_ptr registry); -std::optional parseresetConfigCommandPayload(std::string_view jsonPayload); -CommandResult resetConfigCommand(std::shared_ptr registry, std::string_view jsonPayload); \ No newline at end of file +CommandResult resetConfigCommand(std::shared_ptr registry, const nlohmann::json &json); \ No newline at end of file diff --git a/components/CommandManager/CommandManager/commands/device_commands.cpp b/components/CommandManager/CommandManager/commands/device_commands.cpp index a0ef9c3..d4b80fa 100644 --- a/components/CommandManager/CommandManager/commands/device_commands.cpp +++ b/components/CommandManager/CommandManager/commands/device_commands.cpp @@ -4,23 +4,14 @@ #include "esp_mac.h" #include -// Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite -CommandResult setDeviceModeCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult setDeviceModeCommand(std::shared_ptr registry, const nlohmann::json &json) { - const auto parsedJson = cJSON_Parse(jsonPayload.data()); - if (parsedJson == nullptr) + if (!json.contains("mode") || !json["mode"].is_number_integer()) { - return CommandResult::getErrorResult("Invalid payload"); + return CommandResult::getErrorResult("Invalid payload - missing or unsupported mode"); } - const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode"); - if (modeObject == nullptr) - { - return CommandResult::getErrorResult("Invalid payload - missing mode"); - } - - const auto mode = modeObject->valueint; - + const auto mode = json["mode"].get(); if (mode < 0 || mode > 2) { return CommandResult::getErrorResult("Invalid payload - unsupported mode"); @@ -29,19 +20,11 @@ CommandResult setDeviceModeCommand(std::shared_ptr registry, const auto projectConfig = registry->resolve(DependencyType::project_config); projectConfig->setDeviceMode(static_cast(mode)); - cJSON_Delete(parsedJson); - return CommandResult::getSuccessResult("Device mode set"); } -CommandResult updateOTACredentialsCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult updateOTACredentialsCommand(std::shared_ptr registry, const nlohmann::json &json) { - const auto parsedJson = cJSON_Parse(jsonPayload.data()); - - if (parsedJson == nullptr) - { - return CommandResult::getErrorResult("Invalid payload"); - } const auto projectConfig = registry->resolve(DependencyType::project_config); const auto oldDeviceConfig = projectConfig->getDeviceConfig(); @@ -49,48 +32,39 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr re auto OTAPassword = oldDeviceConfig.OTAPassword; auto OTAPort = oldDeviceConfig.OTAPort; - if (const auto OTALoginObject = cJSON_GetObjectItem(parsedJson, "login"); OTALoginObject != nullptr) + if (json.contains("login") && json["login"].is_string()) { - if (const auto newLogin = OTALoginObject->valuestring; strcmp(newLogin, "") != 0) + if (const auto newLogin = json["login"].get(); strcmp(newLogin.c_str(), "") != 0) { OTALogin = newLogin; } } - if (const auto OTAPasswordObject = cJSON_GetObjectItem(parsedJson, "password"); OTAPasswordObject != nullptr) + if (json.contains("password") && json["password"].is_string()) { - OTAPassword = OTAPasswordObject->valuestring; + OTAPassword = json["password"].get(); } - if (const auto OTAPortObject = cJSON_GetObjectItem(parsedJson, "port"); OTAPortObject != nullptr) + if (json.contains("port") && json["port"].is_number_integer()) { - if (const auto newPort = OTAPortObject->valueint; newPort >= 82) + if (const auto newPort = json["port"].get(); newPort >= 82) { OTAPort = newPort; } } - cJSON_Delete(parsedJson); - projectConfig->setOTAConfig(OTALogin, OTAPassword, OTAPort); return CommandResult::getSuccessResult("OTA Config set"); } -CommandResult updateLEDDutyCycleCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult updateLEDDutyCycleCommand(std::shared_ptr registry, const nlohmann::json &json) { - const auto parsedJson = cJSON_Parse(jsonPayload.data()); - if (parsedJson == nullptr) - { - return CommandResult::getErrorResult("Invalid payload"); - } - - const auto dutyCycleObject = cJSON_GetObjectItem(parsedJson, "dutyCycle"); - if (dutyCycleObject == nullptr) + if (!json.contains("dutyCycle") || !json["dutyCycle"].is_number_integer()) { return CommandResult::getErrorResult("Invalid payload - missing dutyCycle"); } - const auto dutyCycle = dutyCycleObject->valueint; + const auto dutyCycle = json["dutyCycle"].get(); if (dutyCycle < 0 || dutyCycle > 100) { @@ -107,8 +81,6 @@ CommandResult updateLEDDutyCycleCommand(std::shared_ptr regi ledMgr->setExternalLEDDutyCycle(static_cast(dutyCycle)); } - cJSON_Delete(parsedJson); - return CommandResult::getSuccessResult("LED duty cycle set"); } @@ -145,34 +117,28 @@ CommandResult startStreamingCommand() return CommandResult::getSuccessResult("Streaming starting"); } -CommandResult switchModeCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult switchModeCommand(std::shared_ptr registry, const nlohmann::json &json) { - const auto parsedJson = cJSON_Parse(jsonPayload.data()); - if (parsedJson == nullptr) - { - return CommandResult::getErrorResult("Invalid payload"); - } - const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode"); - if (modeObject == nullptr) + if (!json.contains("mode") || !json["mode"].is_string()) { return CommandResult::getErrorResult("Invalid payload - missing mode"); } - const char *modeStr = modeObject->valuestring; + auto modeStr = json["mode"].get(); StreamingMode newMode; - ESP_LOGI("[DEVICE_COMMANDS]", "Switch mode command received with mode: %s", modeStr); + ESP_LOGI("[DEVICE_COMMANDS]", "Switch mode command received with mode: %s", modeStr.c_str()); - if (strcmp(modeStr, "uvc") == 0) + if (modeStr == "uvc") { newMode = StreamingMode::UVC; } - else if (strcmp(modeStr, "wifi") == 0) + else if (modeStr == "wifi") { newMode = StreamingMode::WIFI; } - else if (strcmp(modeStr, "setup") == 0 || strcmp(modeStr, "auto") == 0) + else if (modeStr == "setup" || modeStr == "auto")) { newMode = StreamingMode::SETUP; } @@ -185,8 +151,6 @@ CommandResult switchModeCommand(std::shared_ptr registry, st ESP_LOGI("[DEVICE_COMMANDS]", "Setting device mode to: %d", (int)newMode); projectConfig->setDeviceMode(newMode); - cJSON_Delete(parsedJson); - return CommandResult::getSuccessResult("Device mode switched, restart to apply"); } diff --git a/components/CommandManager/CommandManager/commands/device_commands.hpp b/components/CommandManager/CommandManager/commands/device_commands.hpp index e744af5..496aa8f 100644 --- a/components/CommandManager/CommandManager/commands/device_commands.hpp +++ b/components/CommandManager/CommandManager/commands/device_commands.hpp @@ -3,22 +3,22 @@ #include "OpenIrisTasks.hpp" #include "DependencyRegistry.hpp" #include "esp_timer.h" -#include "cJSON.h" #include "main_globals.hpp" #include #include +#include -CommandResult updateOTACredentialsCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult updateOTACredentialsCommand(std::shared_ptr registry, const nlohmann::json &json); -CommandResult updateLEDDutyCycleCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult updateLEDDutyCycleCommand(std::shared_ptr registry, const nlohmann::json &json); CommandResult getLEDDutyCycleCommand(std::shared_ptr registry); CommandResult restartDeviceCommand(); CommandResult startStreamingCommand(); -CommandResult switchModeCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult switchModeCommand(std::shared_ptr registry, const nlohmann::json &json); CommandResult getDeviceModeCommand(std::shared_ptr registry); diff --git a/components/CommandManager/CommandManager/commands/mdns_commands.cpp b/components/CommandManager/CommandManager/commands/mdns_commands.cpp index 923c5bd..1e14e2a 100644 --- a/components/CommandManager/CommandManager/commands/mdns_commands.cpp +++ b/components/CommandManager/CommandManager/commands/mdns_commands.cpp @@ -1,33 +1,13 @@ #include "mdns_commands.hpp" -std::optional parseMDNSCommandPayload(std::string_view jsonPayload) +CommandResult setMDNSCommand(std::shared_ptr registry, const nlohmann::json &json) { - MDNSPayload payload; - cJSON *parsedJson = cJSON_Parse(jsonPayload.data()); - if (parsedJson == nullptr) - return std::nullopt; - - cJSON *hostnameObject = cJSON_GetObjectItem(parsedJson, "hostname"); - - if (hostnameObject == nullptr) - { - cJSON_Delete(parsedJson); - return std::nullopt; - } - - payload.hostname = std::string(hostnameObject->valuestring); - cJSON_Delete(parsedJson); - return payload; -} - -CommandResult setMDNSCommand(std::shared_ptr registry, std::string_view jsonPayload) -{ - auto payload = parseMDNSCommandPayload(jsonPayload); - if (!payload.has_value()) - return CommandResult::getErrorResult("Invalid payload"); + const auto payload = json.get(); + if (payload.hostname.empty()) + return CommandResult::getErrorResult("Invalid payload - empty hostname"); std::shared_ptr projectConfig = registry->resolve(DependencyType::project_config); - projectConfig->setMDNSConfig(payload.value().hostname); + projectConfig->setMDNSConfig(payload.hostname); return CommandResult::getSuccessResult("Config updated"); } diff --git a/components/CommandManager/CommandManager/commands/mdns_commands.hpp b/components/CommandManager/CommandManager/commands/mdns_commands.hpp index 64b2cf9..1b07203 100644 --- a/components/CommandManager/CommandManager/commands/mdns_commands.hpp +++ b/components/CommandManager/CommandManager/commands/mdns_commands.hpp @@ -2,11 +2,10 @@ #include #include #include -#include #include "CommandResult.hpp" #include "CommandSchema.hpp" #include "DependencyRegistry.hpp" +#include -std::optional parseMDNSCommandPayload(std::string_view jsonPayload); -CommandResult setMDNSCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult setMDNSCommand(std::shared_ptr registry, const nlohmann::json &json); CommandResult getMDNSNameCommand(std::shared_ptr registry); \ No newline at end of file diff --git a/components/CommandManager/CommandManager/commands/scan_commands.cpp b/components/CommandManager/CommandManager/commands/scan_commands.cpp index bd5d6f7..cffd089 100644 --- a/components/CommandManager/CommandManager/commands/scan_commands.cpp +++ b/components/CommandManager/CommandManager/commands/scan_commands.cpp @@ -14,29 +14,24 @@ CommandResult scanNetworksCommand(std::shared_ptr registry) auto networks = wifiManager->ScanNetworks(); - cJSON *root = cJSON_CreateObject(); - cJSON *networksArray = cJSON_CreateArray(); - cJSON_AddItemToObject(root, "networks", networksArray); + nlohmann::json result; + std::vector networksJson; for (const auto &network : networks) { - cJSON *networkObject = cJSON_CreateObject(); - cJSON_AddStringToObject(networkObject, "ssid", network.ssid.c_str()); - cJSON_AddNumberToObject(networkObject, "channel", network.channel); - cJSON_AddNumberToObject(networkObject, "rssi", network.rssi); + nlohmann::json networkItem; + networkItem["ssid"] = network.ssid; + networkItem["channel"] = network.channel; + networkItem["rssi"] = network.rssi; char mac_str[18]; sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", network.mac[0], network.mac[1], network.mac[2], network.mac[3], network.mac[4], network.mac[5]); - cJSON_AddStringToObject(networkObject, "mac_address", mac_str); - cJSON_AddNumberToObject(networkObject, "auth_mode", network.auth_mode); - cJSON_AddItemToArray(networksArray, networkObject); + networkItem["mac_address"] = mac_str; + networkItem["auth_mode"] = network.auth_mode; + networksJson.push_back(networkItem); } - char *json_string = cJSON_PrintUnformatted(root); - printf("%s\n", json_string); - cJSON_Delete(root); - free(json_string); - - return CommandResult::getSuccessResult("Networks scanned"); + result["networks"] = networksJson; + return CommandResult::getSuccessResult(result); } \ No newline at end of file diff --git a/components/CommandManager/CommandManager/commands/scan_commands.hpp b/components/CommandManager/CommandManager/commands/scan_commands.hpp index df2e0da..a65a4c9 100644 --- a/components/CommandManager/CommandManager/commands/scan_commands.hpp +++ b/components/CommandManager/CommandManager/commands/scan_commands.hpp @@ -4,9 +4,9 @@ #include "CommandResult.hpp" #include "DependencyRegistry.hpp" #include "esp_log.h" -#include #include #include +#include CommandResult scanNetworksCommand(std::shared_ptr registry); diff --git a/components/CommandManager/CommandManager/commands/simple_commands.cpp b/components/CommandManager/CommandManager/commands/simple_commands.cpp index 33cd04d..018d1e5 100644 --- a/components/CommandManager/CommandManager/commands/simple_commands.cpp +++ b/components/CommandManager/CommandManager/commands/simple_commands.cpp @@ -7,27 +7,19 @@ CommandResult PingCommand() return CommandResult::getSuccessResult("pong"); }; -CommandResult PauseCommand(std::string_view jsonPayload) +CommandResult PauseCommand(const nlohmann::json &json) { - PausePayload payload; - // pause by default if this command gets executed, even if the payload was invalid - payload.pause = true; + auto pause = true; - cJSON *root = cJSON_Parse(std::string(jsonPayload).c_str()); - if (root) + if (json.contains("pause") && json["pause"].is_boolean()) { - cJSON *pauseItem = cJSON_GetObjectItem(root, "pause"); - if (pauseItem && cJSON_IsBool(pauseItem)) - { - payload.pause = cJSON_IsTrue(pauseItem); - } - cJSON_Delete(root); + pause = json["pause"].get(); } - ESP_LOGI(TAG, "Pause command received: %s", payload.pause ? "true" : "false"); + ESP_LOGI(TAG, "Pause command received: %s", pause ? "true" : "false"); - setStartupPaused(payload.pause); - if (payload.pause) + setStartupPaused(pause); + if (pause) { ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode"); return CommandResult::getSuccessResult("Startup paused"); diff --git a/components/CommandManager/CommandManager/commands/simple_commands.hpp b/components/CommandManager/CommandManager/commands/simple_commands.hpp index 23c2972..ed75517 100644 --- a/components/CommandManager/CommandManager/commands/simple_commands.hpp +++ b/components/CommandManager/CommandManager/commands/simple_commands.hpp @@ -3,12 +3,11 @@ #include #include "CommandResult.hpp" -#include "CommandSchema.hpp" #include "main_globals.hpp" #include "esp_log.h" -#include +#include CommandResult PingCommand(); -CommandResult PauseCommand(std::string_view jsonPayload); +CommandResult PauseCommand(const nlohmann::json &json); #endif \ No newline at end of file diff --git a/components/CommandManager/CommandManager/commands/wifi_commands.cpp b/components/CommandManager/CommandManager/commands/wifi_commands.cpp index 6499b58..db81371 100644 --- a/components/CommandManager/CommandManager/commands/wifi_commands.cpp +++ b/components/CommandManager/CommandManager/commands/wifi_commands.cpp @@ -2,212 +2,76 @@ #include "esp_netif.h" #include "sdkconfig.h" -std::optional parseSetWiFiCommandPayload(std::string_view jsonPayload) -{ - WifiPayload payload; - cJSON *parsedJson = cJSON_Parse(jsonPayload.data()); - - if (parsedJson == nullptr) - return std::nullopt; - - const cJSON *networkName = cJSON_GetObjectItem(parsedJson, "name"); - const cJSON *ssidPtr = cJSON_GetObjectItem(parsedJson, "ssid"); - const cJSON *passwordPtr = cJSON_GetObjectItem(parsedJson, "password"); - const cJSON *channelPtr = cJSON_GetObjectItem(parsedJson, "channel"); - const cJSON *powerPtr = cJSON_GetObjectItem(parsedJson, "power"); - - if (ssidPtr == nullptr) - { - // without an SSID we can't do anything - cJSON_Delete(parsedJson); - return std::nullopt; - } - - if (networkName == nullptr) - payload.networkName = "main"; - else - payload.networkName = std::string(networkName->valuestring); - - payload.ssid = std::string(ssidPtr->valuestring); - - if (passwordPtr == nullptr) - payload.password = ""; - else - payload.password = std::string(passwordPtr->valuestring); - - if (channelPtr == nullptr) - payload.channel = 0; - else - payload.channel = channelPtr->valueint; - - if (powerPtr == nullptr) - payload.power = 0; - else - payload.power = powerPtr->valueint; - - cJSON_Delete(parsedJson); - return payload; -} - -std::optional parseDeleteWifiCommandPayload(std::string_view jsonPayload) -{ - deleteNetworkPayload payload; - auto *parsedJson = cJSON_Parse(jsonPayload.data()); - - if (parsedJson == nullptr) - return std::nullopt; - - const auto *networkName = cJSON_GetObjectItem(parsedJson, "name"); - if (networkName == nullptr) - { - cJSON_Delete(parsedJson); - return std::nullopt; - } - - payload.networkName = std::string(networkName->valuestring); - - cJSON_Delete(parsedJson); - return payload; -} - -std::optional parseUpdateWifiCommandPayload(std::string_view jsonPayload) -{ - UpdateWifiPayload payload; - cJSON *parsedJson = cJSON_Parse(jsonPayload.data()); - - if (parsedJson == nullptr) - return std::nullopt; - - const cJSON *networkName = cJSON_GetObjectItem(parsedJson, "name"); - if (networkName == nullptr) - { - cJSON_Delete(parsedJson); - return std::nullopt; - } - - payload.networkName = std::string(networkName->valuestring); - - const auto *ssidObject = cJSON_GetObjectItem(parsedJson, "ssid"); - const auto *passwordObject = cJSON_GetObjectItem(parsedJson, "password"); - const auto *channelObject = cJSON_GetObjectItem(parsedJson, "channel"); - const auto *powerObject = cJSON_GetObjectItem(parsedJson, "power"); - - if (ssidObject != nullptr && (strcmp(ssidObject->valuestring, "") == 0)) - { - // we need ssid to actually connect - cJSON_Delete(parsedJson); - return std::nullopt; - } - - if (ssidObject != nullptr) - payload.ssid = std::string(ssidObject->valuestring); - - if (passwordObject != nullptr) - payload.password = std::string(passwordObject->valuestring); - - if (channelObject != nullptr) - payload.channel = channelObject->valueint; - - if (powerObject != nullptr) - payload.power = powerObject->valueint; - - cJSON_Delete(parsedJson); - return payload; -} - -std::optional parseUpdateAPWiFiCommandPayload(const std::string_view jsonPayload) -{ - UpdateAPWiFiPayload payload; - cJSON *parsedJson = cJSON_Parse(jsonPayload.data()); - - if (parsedJson == nullptr) - { - return std::nullopt; - } - - const cJSON *ssidObject = cJSON_GetObjectItem(parsedJson, "ssid"); - const cJSON *passwordObject = cJSON_GetObjectItem(parsedJson, "password"); - const cJSON *channelObject = cJSON_GetObjectItem(parsedJson, "channel"); - - if (ssidObject != nullptr) - payload.ssid = std::string(ssidObject->valuestring); - - if (passwordObject != nullptr) - payload.password = std::string(passwordObject->valuestring); - - if (channelObject != nullptr) - payload.channel = channelObject->valueint; - - cJSON_Delete(parsedJson); - return payload; -} - -CommandResult setWiFiCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult setWiFiCommand(std::shared_ptr registry, const nlohmann::json &json) { #if !CONFIG_GENERAL_ENABLE_WIRELESS - return CommandResult::getErrorResult("Not supported by current firmware"); + return CommandResult::getErrorResult("Not supported by current firmware"); #endif - const auto payload = parseSetWiFiCommandPayload(jsonPayload); - if (!payload.has_value()) + auto payload = json.get(); + if (payload.name.empty()) { - return CommandResult::getErrorResult("Invalid payload"); + payload.name = std::string("main"); + } + + if (payload.ssid.empty()) + { + return CommandResult::getErrorResult("Invalid payload: missing SSID"); } - const auto wifiConfig = payload.value(); std::shared_ptr projectConfig = registry->resolve(DependencyType::project_config); projectConfig->setWifiConfig( - wifiConfig.networkName, - wifiConfig.ssid, - wifiConfig.password, - wifiConfig.channel, - wifiConfig.power); + payload.name, + payload.ssid, + payload.password, + payload.channel, + payload.power); return CommandResult::getSuccessResult("Config updated"); } -CommandResult deleteWiFiCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult deleteWiFiCommand(std::shared_ptr registry, const nlohmann::json &json) { #if !CONFIG_GENERAL_ENABLE_WIRELESS - return CommandResult::getErrorResult("Not supported by current firmware"); + return CommandResult::getErrorResult("Not supported by current firmware"); #endif - const auto payload = parseDeleteWifiCommandPayload(jsonPayload); - if (!payload.has_value()) + + const auto payload = json.get(); + if (payload.name.empty()) return CommandResult::getErrorResult("Invalid payload"); auto projectConfig = registry->resolve(DependencyType::project_config); - projectConfig->deleteWifiConfig(payload.value().networkName); + projectConfig->deleteWifiConfig(payload.name); return CommandResult::getSuccessResult("Config updated"); } -CommandResult updateWiFiCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult updateWiFiCommand(std::shared_ptr registry, const nlohmann::json &json) { #if !CONFIG_GENERAL_ENABLE_WIRELESS - return CommandResult::getErrorResult("Not supported by current firmware"); + return CommandResult::getErrorResult("Not supported by current firmware"); #endif - const auto payload = parseUpdateWifiCommandPayload(jsonPayload); - if (!payload.has_value()) - { - return CommandResult::getErrorResult("Invalid payload"); - } - auto updatedConfig = payload.value(); + auto payload = json.get(); + if (payload.name.empty()) + { + return CommandResult::getErrorResult("Invalid payload - missing network name"); + } auto projectConfig = registry->resolve(DependencyType::project_config); auto storedNetworks = projectConfig->getWifiConfigs(); if (const auto networkToUpdate = std::ranges::find_if( - storedNetworks, - [&](auto &network){ return network.name == updatedConfig.networkName; } - ); + storedNetworks, + [&](auto &network) + { return network.name == payload.name; }); networkToUpdate != storedNetworks.end()) { projectConfig->setWifiConfig( - updatedConfig.networkName, - updatedConfig.ssid.has_value() ? updatedConfig.ssid.value() : networkToUpdate->ssid, - updatedConfig.password.has_value() ? updatedConfig.password.value() : networkToUpdate->password, - updatedConfig.channel.has_value() ? updatedConfig.channel.value() : networkToUpdate->channel, - updatedConfig.power.has_value() ? updatedConfig.power.value() : networkToUpdate->power); + payload.name, + payload.ssid.has_value() ? payload.ssid.value() : networkToUpdate->ssid, + payload.password.has_value() ? payload.password.value() : networkToUpdate->password, + payload.channel.has_value() ? payload.channel.value() : networkToUpdate->channel, + payload.power.has_value() ? payload.power.value() : networkToUpdate->power); return CommandResult::getSuccessResult("Config updated"); } @@ -215,113 +79,99 @@ CommandResult updateWiFiCommand(std::shared_ptr registry, st return CommandResult::getErrorResult("Requested network does not exist"); } -CommandResult updateAPWiFiCommand(std::shared_ptr registry, std::string_view jsonPayload) +CommandResult updateAPWiFiCommand(std::shared_ptr registry, const nlohmann::json &json) { #if !CONFIG_GENERAL_ENABLE_WIRELESS - return CommandResult::getErrorResult("Not supported by current firmware"); + return CommandResult::getErrorResult("Not supported by current firmware"); #endif - const auto payload = parseUpdateAPWiFiCommandPayload(jsonPayload); - if (!payload.has_value()) - return CommandResult::getErrorResult("Invalid payload"); - - auto updatedConfig = payload.value(); + const auto payload = json.get(); auto projectConfig = registry->resolve(DependencyType::project_config); const auto previousAPConfig = projectConfig->getAPWifiConfig(); projectConfig->setAPWifiConfig( - updatedConfig.ssid.has_value() ? updatedConfig.ssid.value() : previousAPConfig.ssid, - updatedConfig.password.has_value() ? updatedConfig.password.value() : previousAPConfig.password, - updatedConfig.channel.has_value() ? updatedConfig.channel.value() : previousAPConfig.channel); + payload.ssid.has_value() ? payload.ssid.value() : previousAPConfig.ssid, + payload.password.has_value() ? payload.password.value() : previousAPConfig.password, + payload.channel.has_value() ? payload.channel.value() : previousAPConfig.channel); return CommandResult::getSuccessResult("Config updated"); } -CommandResult getWiFiStatusCommand(std::shared_ptr registry) { +CommandResult getWiFiStatusCommand(std::shared_ptr registry) +{ #if !CONFIG_GENERAL_ENABLE_WIRELESS return CommandResult::getErrorResult("Not supported by current firmware"); #endif + auto wifiManager = registry->resolve(DependencyType::wifi_manager); - if (!wifiManager) { - return CommandResult::getErrorResult("Not supported by current firmware"); + auto projectConfig = registry->resolve(DependencyType::project_config); + + auto wifiState = wifiManager->GetCurrentWiFiState(); + auto networks = projectConfig->getWifiConfigs(); + nlohmann::json result; + + switch (wifiState) + { + case WiFiState_e::WiFiState_NotInitialized: + result["status"] = "not_initialized"; + break; + case WiFiState_e::WiFiState_Initialized: + result["status"] = "initialized"; + break; + case WiFiState_e::WiFiState_ReadyToConnect: + result["status"] = "ready"; + break; + case WiFiState_e::WiFiState_Connecting: + result["status"] = "connecting"; + break; + case WiFiState_e::WiFiState_WaitingForIp: + result["status"] = "waiting_for_ip"; + break; + case WiFiState_e::WiFiState_Connected: + result["status"] = "connected"; + break; + case WiFiState_e::WiFiState_Disconnected: + result["status"] = "disconnected"; + break; + case WiFiState_e::WiFiState_Error: + result["status"] = "error"; + break; } - auto projectConfig = registry->resolve(DependencyType::project_config); - - // Get current WiFi state - auto wifiState = wifiManager->GetCurrentWiFiState(); - auto networks = projectConfig->getWifiConfigs(); - - cJSON* statusJson = cJSON_CreateObject(); - - // Add WiFi state - const char* stateStr = "unknown"; - switch(wifiState) { - case WiFiState_e::WiFiState_NotInitialized: - stateStr = "not_initialized"; - break; - case WiFiState_e::WiFiState_Initialized: - stateStr = "initialized"; - break; - case WiFiState_e::WiFiState_ReadyToConnect: - stateStr = "ready"; - break; - case WiFiState_e::WiFiState_Connecting: - stateStr = "connecting"; - break; - case WiFiState_e::WiFiState_WaitingForIp: - stateStr = "waiting_for_ip"; - break; - case WiFiState_e::WiFiState_Connected: - stateStr = "connected"; - break; - case WiFiState_e::WiFiState_Disconnected: - stateStr = "disconnected"; - break; - case WiFiState_e::WiFiState_Error: - stateStr = "error"; - break; + + if (wifiState == WiFiState_e::WiFiState_Connected) + { + // Get IP address from ESP32 + esp_netif_ip_info_t ip_info; + esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) + { + char ip_str[16]; + sprintf(ip_str, IPSTR, IP2STR(&ip_info.ip)); + result["ip_address"] = ip_str; } - cJSON_AddStringToObject(statusJson, "status", stateStr); - cJSON_AddNumberToObject(statusJson, "networks_configured", networks.size()); - - // Add IP info if connected - if (wifiState == WiFiState_e::WiFiState_Connected) { - // Get IP address from ESP32 - esp_netif_ip_info_t ip_info; - esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); - if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) { - char ip_str[16]; - sprintf(ip_str, IPSTR, IP2STR(&ip_info.ip)); - cJSON_AddStringToObject(statusJson, "ip_address", ip_str); - } - } - - char* statusString = cJSON_PrintUnformatted(statusJson); - std::string result(statusString); - free(statusString); - cJSON_Delete(statusJson); - - return CommandResult::getSuccessResult(result); + } + + return CommandResult::getSuccessResult(result); } -CommandResult connectWiFiCommand(std::shared_ptr registry) { +CommandResult connectWiFiCommand(std::shared_ptr registry) +{ #if !CONFIG_GENERAL_ENABLE_WIRELESS return CommandResult::getErrorResult("Not supported by current firmware"); #endif + auto wifiManager = registry->resolve(DependencyType::wifi_manager); - if (!wifiManager) { - return CommandResult::getErrorResult("Not supported by current firmware"); + auto projectConfig = registry->resolve(DependencyType::project_config); + + auto networks = projectConfig->getWifiConfigs(); + if (networks.empty()) + { + return CommandResult::getErrorResult("No WiFi networks configured"); } - auto projectConfig = registry->resolve(DependencyType::project_config); - - auto networks = projectConfig->getWifiConfigs(); - if (networks.empty()) { - return CommandResult::getErrorResult("No WiFi networks configured"); - } - - // Trigger WiFi connection attempt - wifiManager->TryConnectToStoredNetworks(); - - return CommandResult::getSuccessResult("WiFi connection attempt started"); + + // Trigger WiFi connection attempt + wifiManager->TryConnectToStoredNetworks(); + + return CommandResult::getSuccessResult("WiFi connection attempt started"); } diff --git a/components/CommandManager/CommandManager/commands/wifi_commands.hpp b/components/CommandManager/CommandManager/commands/wifi_commands.hpp index f98245d..fd36e72 100644 --- a/components/CommandManager/CommandManager/commands/wifi_commands.hpp +++ b/components/CommandManager/CommandManager/commands/wifi_commands.hpp @@ -4,22 +4,18 @@ #include #include #include -#include #include "CommandResult.hpp" #include "CommandSchema.hpp" #include "DependencyRegistry.hpp" +#include -std::optional parseSetWiFiCommandPayload(std::string_view jsonPayload); -CommandResult setWiFiCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult setWiFiCommand(std::shared_ptr registry, const nlohmann::json &json); -std::optional parseDeleteWifiCommandPayload(std::string_view jsonPayload); -CommandResult deleteWiFiCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult deleteWiFiCommand(std::shared_ptr registry, const nlohmann::json &json); -std::optional parseUpdateWifiCommandPayload(std::string_view jsonPayload); -CommandResult updateWiFiCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult updateWiFiCommand(std::shared_ptr registry, const nlohmann::json &json); -std::optional parseUpdateAPWiFiCommandPayload(std::string_view jsonPayload); -CommandResult updateAPWiFiCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult updateAPWiFiCommand(std::shared_ptr registry, const nlohmann::json &json); CommandResult getWiFiStatusCommand(std::shared_ptr registry); CommandResult connectWiFiCommand(std::shared_ptr registry); diff --git a/components/RestAPI/RestAPI/RestAPI.cpp b/components/RestAPI/RestAPI/RestAPI.cpp index a0c9bb8..de002df 100644 --- a/components/RestAPI/RestAPI/RestAPI.cpp +++ b/components/RestAPI/RestAPI/RestAPI.cpp @@ -4,6 +4,19 @@ #define POST_METHOD "POST" +bool getIsSuccess(const nlohmann::json &response) +{ + // since the commandManager will be returning CommandManagerResponse to simplify parsing on the clients end + // we can slightly its json representation, and extract the status from there + // note: This will only work for commands executed with CommandManager::executeFromType(). + if (!response.contains("result")) + { + return false; + } + + return response.at("result").at("status").get() == "success"; +} + RestAPI::RestAPI(std::string url, std::shared_ptr commandManager) : command_manager(commandManager) { this->url = std::move(url); @@ -84,66 +97,73 @@ void HandleRestAPIPollTask(void *pvParameter) // COMMANDS // updates -void RestAPI::handle_update_wifi(RequestContext *context) { +void RestAPI::handle_update_wifi(RequestContext *context) +{ if (context->method != POST_METHOD) { mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed"); return; } - auto const result = command_manager->executeFromType(CommandType::UPDATE_WIFI, context->body); - auto const code = result.isSuccess() ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.getResult().c_str()); + const nlohmann::json result = command_manager->executeFromType(CommandType::UPDATE_WIFI, context->body); + const auto code = getIsSuccess(result) ? 200 : 400; + mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); } -void RestAPI::handle_update_device(RequestContext *context) { +void RestAPI::handle_update_device(RequestContext *context) +{ if (context->method != POST_METHOD) { mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed"); return; } - auto const result = command_manager->executeFromType(CommandType::UPDATE_OTA_CREDENTIALS, context->body); - auto const code = result.isSuccess() ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.getResult().c_str()); + const nlohmann::json result = command_manager->executeFromType(CommandType::UPDATE_OTA_CREDENTIALS, context->body); + const auto code = getIsSuccess(result) ? 200 : 500; + mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); } -void RestAPI::handle_update_camera(RequestContext *context) { +void RestAPI::handle_update_camera(RequestContext *context) +{ if (context->method != POST_METHOD) { mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed"); return; } - auto const result = command_manager->executeFromType(CommandType::UPDATE_CAMERA, context->body); - auto const code = result.isSuccess() ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.getResult().c_str()); + const nlohmann::json result = command_manager->executeFromType(CommandType::UPDATE_CAMERA, context->body); + const auto code = getIsSuccess(result) ? 200 : 500; + mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); } // gets -void RestAPI::handle_get_config(RequestContext *context) { +void RestAPI::handle_get_config(RequestContext *context) +{ auto const result = this->command_manager->executeFromType(CommandType::GET_CONFIG, ""); - mg_http_reply(context->connection, 200, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), result.getResult()); + const nlohmann::json jsonResult = result; + mg_http_reply(context->connection, 200, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), jsonResult.dump().c_str()); } // resets -void RestAPI::handle_reset_config(RequestContext *context) { +void RestAPI::handle_reset_config(RequestContext *context) +{ if (context->method != POST_METHOD) { mg_http_reply(context->connection, 401, JSON_RESPONSE, "{%m:%m}", MG_ESC("error"), "Method not allowed"); return; } - auto const result = this->command_manager->executeFromType(CommandType::RESET_CONFIG, "{\"section\": \"all\"}"); - auto const code = result.isSuccess() ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), result.getResult()); + const nlohmann::json result = this->command_manager->executeFromType(CommandType::RESET_CONFIG, "{\"section\": \"all\"}"); + const auto code = getIsSuccess(result) ? 200 : 500; + mg_http_reply(context->connection, code, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), result.dump().c_str()); } // reboots -void RestAPI::handle_reboot(RequestContext *context) { - auto const result = this->command_manager->executeFromType(CommandType::RESTART_DEVICE, ""); +void RestAPI::handle_reboot(RequestContext *context) +{ + const auto result = this->command_manager->executeFromType(CommandType::RESTART_DEVICE, ""); mg_http_reply(context->connection, 200, JSON_RESPONSE, "{%m:%m}", MG_ESC("result"), "Ok"); } @@ -154,16 +174,18 @@ void RestAPI::handle_camera_reboot(RequestContext *context) // heartbeat -void RestAPI::pong(RequestContext *context) { - auto const result = this->command_manager->executeFromType(CommandType::PING, ""); - auto const code = result.isSuccess() ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.getResult().c_str()); +void RestAPI::pong(RequestContext *context) +{ + const nlohmann::json result = this->command_manager->executeFromType(CommandType::PING, ""); + const auto code = getIsSuccess(result) ? 200 : 500; + mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); } // special -void RestAPI::handle_save(RequestContext *context) { - auto const result = this->command_manager->executeFromType(CommandType::SAVE_CONFIG, ""); - auto const code = result.isSuccess() ? 200 : 500; - mg_http_reply(context->connection, code, JSON_RESPONSE, result.getResult().c_str()); +void RestAPI::handle_save(RequestContext *context) +{ + const nlohmann::json result = this->command_manager->executeFromType(CommandType::SAVE_CONFIG, ""); + const auto code = getIsSuccess(result) ? 200 : 500; + mg_http_reply(context->connection, code, JSON_RESPONSE, result.dump().c_str()); } diff --git a/components/SerialManager/SerialManager/SerialManager.cpp b/components/SerialManager/SerialManager/SerialManager.cpp index b9efef0..b6e793d 100644 --- a/components/SerialManager/SerialManager/SerialManager.cpp +++ b/components/SerialManager/SerialManager/SerialManager.cpp @@ -50,14 +50,24 @@ void SerialManager::try_receive() data[current_position] = '\0'; current_position = 0; - const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast(this->data))); - const auto resultMessage = result.getResult(); - int written = usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20); - (void)written; // ignore errors if driver already uninstalled + const nlohmann::json result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast(this->data))); + const auto resultMessage = result.dump(); + usb_serial_jtag_write_bytes_chunked(resultMessage.c_str(), resultMessage.length(), 1000 / 20); } } } +void SerialManager::usb_serial_jtag_write_bytes_chunked(const char *data, size_t len, size_t timeout) +{ + while (len > 0) + { + auto to_write = len > BUF_SIZE ? BUF_SIZE : len; + auto written = usb_serial_jtag_write_bytes(data, to_write, timeout); + data += written; + len -= written; + } +} + // Function to notify that a command was received during startup void SerialManager::notify_startup_command_received() { @@ -171,8 +181,8 @@ void HandleCDCSerialManagerTask(void *pvParameters) if (idx >= BUF_SIZE || buffer[idx - 1] == '\n' || buffer[idx - 1] == '\r') { buffer[idx - 1] = '\0'; - const auto result = commandManager->executeFromJson(std::string_view(reinterpret_cast(buffer))); - const auto resultMessage = result.getResult(); + const nlohmann::json result = commandManager->executeFromJson(std::string_view(reinterpret_cast(buffer))); + const auto resultMessage = result.dump(); tud_cdc_write(resultMessage.c_str(), resultMessage.length()); tud_cdc_write_flush(); idx = 0; diff --git a/components/SerialManager/SerialManager/SerialManager.hpp b/components/SerialManager/SerialManager/SerialManager.hpp index 16b00ff..60c3fe8 100644 --- a/components/SerialManager/SerialManager/SerialManager.hpp +++ b/components/SerialManager/SerialManager/SerialManager.hpp @@ -41,6 +41,8 @@ public: void shutdown(); private: + void usb_serial_jtag_write_bytes_chunked(const char *data, size_t len, size_t timeout); + std::shared_ptr commandManager; esp_timer_handle_t *timerHandle; std::shared_ptr deviceConfig;