upload mutimodal

This commit is contained in:
Summer
2025-06-11 04:55:38 -07:00
committed by Lorow
parent b30a00900f
commit d9ace4bc05
41 changed files with 2484 additions and 219 deletions

View File

@@ -7,8 +7,9 @@ idf_component_register(
"CommandManager/commands/config_commands.cpp"
"CommandManager/commands/mdns_commands.cpp"
"CommandManager/commands/device_commands.cpp"
"CommandManager/commands/scan_commands.cpp"
INCLUDE_DIRS
"CommandManager"
"CommandManager/commands"
REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks
REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks wifiManager Helpers
)

View File

@@ -1,7 +1,9 @@
#include "CommandManager.hpp"
#include <cstdlib>
std::unordered_map<std::string, CommandType> commandTypeMap = {
{"ping", CommandType::PING},
{"pause", CommandType::PAUSE},
{"set_wifi", CommandType::SET_WIFI},
{"update_wifi", CommandType::UPDATE_WIFI},
{"set_streaming_mode", CommandType::SET_STREAMING_MODE},
@@ -15,6 +17,12 @@ std::unordered_map<std::string, CommandType> commandTypeMap = {
{"get_config", CommandType::GET_CONFIG},
{"reset_config", CommandType::RESET_CONFIG},
{"restart_device", CommandType::RESTART_DEVICE},
{"scan_networks", CommandType::SCAN_NETWORKS},
{"start_streaming", CommandType::START_STREAMING},
{"get_wifi_status", CommandType::GET_WIFI_STATUS},
{"connect_wifi", CommandType::CONNECT_WIFI},
{"switch_mode", CommandType::SWITCH_MODE},
{"get_device_mode", CommandType::GET_DEVICE_MODE},
};
std::function<CommandResult()> CommandManager::createCommand(const CommandType type, std::string_view json) const {
@@ -22,6 +30,23 @@ std::function<CommandResult()> CommandManager::createCommand(const CommandType t
{
case CommandType::PING:
return { PingCommand };
case CommandType::PAUSE:
return [json] {
PausePayload payload;
cJSON* root = cJSON_Parse(std::string(json).c_str());
if (root) {
cJSON* pauseItem = cJSON_GetObjectItem(root, "pause");
if (pauseItem && cJSON_IsBool(pauseItem)) {
payload.pause = cJSON_IsTrue(pauseItem);
} else {
payload.pause = true; // Default to pause if not specified
}
cJSON_Delete(root);
} else {
payload.pause = true; // Default to pause if parsing fails
}
return PauseCommand(payload);
};
case CommandType::SET_STREAMING_MODE:
return [this, json] {return setDeviceModeCommand(this->registry, json); };
case CommandType::UPDATE_OTA_CREDENTIALS:
@@ -48,6 +73,18 @@ std::function<CommandResult()> CommandManager::createCommand(const CommandType t
return [this, json] { return resetConfigCommand(this->registry, json); };
case CommandType::RESTART_DEVICE:
return restartDeviceCommand;
case CommandType::SCAN_NETWORKS:
return [this] { return scanNetworksCommand(this->registry); };
case CommandType::START_STREAMING:
return startStreamingCommand;
case CommandType::GET_WIFI_STATUS:
return [this] { return getWiFiStatusCommand(this->registry); };
case CommandType::CONNECT_WIFI:
return [this] { return connectWiFiCommand(this->registry); };
case CommandType::SWITCH_MODE:
return [this, json] { return switchModeCommand(this->registry, json); };
case CommandType::GET_DEVICE_MODE:
return [this] { return getDeviceModeCommand(this->registry); };
default:
return nullptr;
}
@@ -98,11 +135,15 @@ CommandResult CommandManager::executeFromJson(const std::string_view json) const
cJSON_AddItemToArray(responses, response);
}
const auto jsonString = cJSON_Print(responseDocument);
char* jsonString = cJSON_Print(responseDocument);
cJSON_Delete(responseDocument);
cJSON_Delete(parsedJson);
return CommandResult::getSuccessResult(jsonString);
// 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;
}
CommandResult CommandManager::executeFromType(const CommandType type, const std::string_view json) const

View File

@@ -17,12 +17,14 @@
#include "commands/mdns_commands.hpp"
#include "commands/wifi_commands.hpp"
#include "commands/device_commands.hpp"
#include "commands/scan_commands.hpp"
#include <cJSON.h>
enum class CommandType
{
None,
PING,
PAUSE,
SET_WIFI,
UPDATE_OTA_CREDENTIALS,
SET_STREAMING_MODE,
@@ -36,6 +38,12 @@ enum class CommandType
GET_CONFIG,
RESET_CONFIG,
RESTART_DEVICE,
SCAN_NETWORKS,
START_STREAMING,
GET_WIFI_STATUS,
CONNECT_WIFI,
SWITCH_MODE,
GET_DEVICE_MODE,
};
class CommandManager

View File

@@ -3,34 +3,52 @@
#include <format>
#include <string>
#include <algorithm>
class CommandResult
{
private:
public:
enum class Status
{
SUCCESS,
FAILURE,
};
private:
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)
{
// we gotta do it this way, because if we define it as { "result": " {} " } it crashes the compiler, lol
this->message = std::format("{}\"result\":\" {} \"{}", "{", message, "}");
this->message = std::format("{}\"result\":\"{}\"{}", "{", escapedMessage, "}");
}
else
{
this->message = std::format("{}\"error\":\" {} \"{}", "{", message, "}");
this->message = std::format("{}\"error\":\"{}\"{}", "{", escapedMessage, "}");
}
}
public:
bool isSuccess() const { return status == Status::SUCCESS; }
static CommandResult getSuccessResult(const std::string &message)
@@ -42,8 +60,17 @@ public:
{
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; }
};
#endif

View File

@@ -57,4 +57,9 @@ struct RestartCameraPayload : BasePayload
{
bool mode;
};
struct PausePayload : BasePayload
{
bool pause;
};
#endif

View File

@@ -7,7 +7,8 @@
enum class DependencyType
{
project_config,
camera_manager
camera_manager,
wifi_manager
};
class DependencyRegistry

View File

@@ -2,6 +2,8 @@
#include <cJSON.h>
#include <ProjectConfig.hpp>
#include "esp_timer.h"
#include <main_globals.hpp>
// Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) {
@@ -64,3 +66,66 @@ CommandResult restartDeviceCommand() {
OpenIrisTasks::ScheduleRestart(2000);
return CommandResult::getSuccessResult("Device restarted");
}
CommandResult startStreamingCommand() {
activateStreaming(false); // Don't disable setup interfaces by default
return CommandResult::getSuccessResult("Streaming started");
}
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) {
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) {
return CommandResult::getErrorResult("Invalid payload - missing mode");
}
const char* modeStr = modeObject->valuestring;
StreamingMode newMode;
ESP_LOGI("[DEVICE_COMMANDS]", "Switch mode command received with mode: %s", modeStr);
if (strcmp(modeStr, "uvc") == 0 || strcmp(modeStr, "UVC") == 0) {
newMode = StreamingMode::UVC;
} else if (strcmp(modeStr, "wifi") == 0 || strcmp(modeStr, "WiFi") == 0 || strcmp(modeStr, "WIFI") == 0) {
newMode = StreamingMode::WIFI;
} else if (strcmp(modeStr, "auto") == 0 || strcmp(modeStr, "AUTO") == 0) {
newMode = StreamingMode::AUTO;
} else {
return CommandResult::getErrorResult("Invalid mode - use 'uvc', 'wifi', or 'auto'");
}
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
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");
}
CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry) {
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
StreamingMode currentMode = projectConfig->getDeviceMode();
const char* modeStr = "unknown";
switch (currentMode) {
case StreamingMode::UVC:
modeStr = "UVC";
break;
case StreamingMode::WIFI:
modeStr = "WiFi";
break;
case StreamingMode::AUTO:
modeStr = "Auto";
break;
}
char result[100];
sprintf(result, "{\"mode\":\"%s\",\"value\":%d}", modeStr, (int)currentMode);
return CommandResult::getSuccessResult(result);
}

View File

@@ -6,4 +6,10 @@ CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry,
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);
CommandResult restartDeviceCommand();
CommandResult restartDeviceCommand();
CommandResult startStreamingCommand();
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);
CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry);

View File

@@ -0,0 +1,38 @@
#include "scan_commands.hpp"
#include "cJSON.h"
#include "esp_log.h"
#include <string>
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry) {
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
if (!wifiManager) {
return CommandResult::getErrorResult("WiFiManager not available");
}
auto networks = wifiManager->ScanNetworks();
cJSON *root = cJSON_CreateObject();
cJSON *networksArray = cJSON_CreateArray();
cJSON_AddItemToObject(root, "networks", networksArray);
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);
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);
}
char *json_string = cJSON_PrintUnformatted(root);
printf("%s\n", json_string);
cJSON_Delete(root);
free(json_string);
return CommandResult::getSuccessResult("Networks scanned");
}

View File

@@ -0,0 +1,10 @@
#ifndef SCAN_COMMANDS_HPP
#define SCAN_COMMANDS_HPP
#include "../CommandResult.hpp"
#include "../DependencyRegistry.hpp"
#include <wifiManager.hpp>
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry);
#endif

View File

@@ -1,6 +1,25 @@
#include "simple_commands.hpp"
#include "main_globals.hpp"
#include "esp_log.h"
static const char* TAG = "SimpleCommands";
CommandResult PingCommand()
{
return CommandResult::getSuccessResult("pong");
};
CommandResult PauseCommand(const PausePayload& payload)
{
ESP_LOGI(TAG, "Pause command received: %s", payload.pause ? "true" : "false");
startupPaused = payload.pause;
if (payload.pause) {
ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode");
return CommandResult::getSuccessResult("Startup paused");
} else {
ESP_LOGI(TAG, "Startup resumed");
return CommandResult::getSuccessResult("Startup resumed");
}
};

View File

@@ -3,7 +3,9 @@
#include <string>
#include "CommandResult.hpp"
#include "CommandSchema.hpp"
CommandResult PingCommand();
CommandResult PauseCommand(const PausePayload& payload);
#endif

View File

@@ -1,4 +1,5 @@
#include "wifi_commands.hpp"
#include "esp_netif.h"
std::optional<WifiPayload> parseSetWiFiCommandPayload(std::string_view jsonPayload)
{
@@ -223,3 +224,79 @@ CommandResult updateAPWiFiCommand(std::shared_ptr<DependencyRegistry> registry,
return CommandResult::getSuccessResult("Config updated");
}
CommandResult getWiFiStatusCommand(std::shared_ptr<DependencyRegistry> registry) {
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
auto projectConfig = registry->resolve<ProjectConfig>(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;
}
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);
}
CommandResult connectWiFiCommand(std::shared_ptr<DependencyRegistry> registry) {
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
auto projectConfig = registry->resolve<ProjectConfig>(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");
}

View File

@@ -1,4 +1,6 @@
#include <ProjectConfig.hpp>
#include <wifiManager.hpp>
#include <StateManager.hpp>
#include <memory>
#include <string>
#include <optional>
@@ -18,3 +20,6 @@ CommandResult updateWiFiCommand(std::shared_ptr<DependencyRegistry> registry, st
std::optional<UpdateAPWiFiPayload> parseUpdateAPWiFiCommandPayload(std::string_view jsonPayload);
CommandResult updateAPWiFiCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);
CommandResult getWiFiStatusCommand(std::shared_ptr<DependencyRegistry> registry);
CommandResult connectWiFiCommand(std::shared_ptr<DependencyRegistry> registry);