mirror of
https://github.com/EyeTrackVR/OpenIris.git
synced 2025-09-26 23:29:14 +08:00
Rewrite commands into a more modern form in anticipation for API rewrite
This commit is contained in:
parent
4705e41476
commit
27177e411c
41
ESP/lib/src/data/CommandManager/Command.cpp
Normal file
41
ESP/lib/src/data/CommandManager/Command.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "Command.hpp"
|
||||
|
||||
CommandResult PingCommand::execute() {
|
||||
return CommandResult::getSuccessResult("pong");
|
||||
}
|
||||
|
||||
CommandResult SetWiFiCommand::validate() {
|
||||
if (!data.containsKey("ssid"))
|
||||
return CommandResult::getErrorResult("{\"error\": \"Missing ssid\"}");
|
||||
if (!data.containsKey("password"))
|
||||
return CommandResult::getErrorResult("{\"error\": \"Missing password\"}");
|
||||
return CommandResult::getSuccessResult("");
|
||||
}
|
||||
|
||||
CommandResult SetWiFiCommand::execute() {
|
||||
std::string network_name = "main";
|
||||
if (data.containsKey("network_name"))
|
||||
network_name = data["network_name"].as<std::string>();
|
||||
|
||||
projectConfig.setWifiConfig(network_name, data["ssid"], data["password"], 0,
|
||||
0, false, false);
|
||||
|
||||
return CommandResult::getSuccessResult("WIFI SET");
|
||||
}
|
||||
|
||||
CommandResult SetMDNSCommand::validate() {
|
||||
if (!data.containsKey("hostname") || !strlen(data["hostname"]))
|
||||
return CommandResult::getErrorResult("{\"error\": \"Missing hostname\"}");
|
||||
|
||||
return CommandResult::getSuccessResult("");
|
||||
}
|
||||
|
||||
CommandResult SetMDNSCommand::execute() {
|
||||
projectConfig.setMDNSConfig(data["hostname"], "openiristracker", false);
|
||||
return CommandResult::getSuccessResult("MDNS SET");
|
||||
}
|
||||
|
||||
CommandResult SaveConfigCommand::execute() {
|
||||
projectConfig.save();
|
||||
return CommandResult::getSuccessResult("CONFIG SAVED");
|
||||
}
|
85
ESP/lib/src/data/CommandManager/Command.hpp
Normal file
85
ESP/lib/src/data/CommandManager/Command.hpp
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef COMMAND_HPP
|
||||
#define COMMAND_HPP
|
||||
#include <ArduinoJson.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include "data/config/project_config.hpp"
|
||||
|
||||
class CommandResult {
|
||||
private:
|
||||
// or maybe std::optional?
|
||||
std::optional<std::string> successMessage;
|
||||
std::optional<std::string> errorMessage;
|
||||
|
||||
public:
|
||||
CommandResult(std::optional<std::string> successMessage,
|
||||
std::optional<std::string> errorMessage)
|
||||
: successMessage(successMessage), errorMessage(errorMessage) {}
|
||||
|
||||
bool isSuccess() const { return successMessage.has_value(); }
|
||||
|
||||
static CommandResult getSuccessResult(std::string message) {
|
||||
return CommandResult(message, std::nullopt);
|
||||
}
|
||||
|
||||
static CommandResult getErrorResult(std::string message) {
|
||||
return CommandResult(std::nullopt, message);
|
||||
}
|
||||
|
||||
std::string getSuccessMessage() const { return successMessage.value(); };
|
||||
std::string getErrorMessage() const { return errorMessage.value(); }
|
||||
};
|
||||
|
||||
class ICommand {
|
||||
public:
|
||||
virtual CommandResult validate() = 0;
|
||||
virtual CommandResult execute() = 0;
|
||||
virtual ~ICommand() = default;
|
||||
};
|
||||
|
||||
class PingCommand : public ICommand {
|
||||
public:
|
||||
CommandResult validate() override {
|
||||
return CommandResult::getSuccessResult("");
|
||||
};
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
class SetWiFiCommand : public ICommand {
|
||||
ProjectConfig& projectConfig;
|
||||
JsonVariant data;
|
||||
|
||||
public:
|
||||
SetWiFiCommand(ProjectConfig& projectConfig, JsonVariant data)
|
||||
: projectConfig(projectConfig), data(data) {}
|
||||
CommandResult validate() override;
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
class SetMDNSCommand : public ICommand {
|
||||
ProjectConfig& projectConfig;
|
||||
JsonVariant data;
|
||||
|
||||
public:
|
||||
SetMDNSCommand(ProjectConfig& projectConfig, JsonVariant data)
|
||||
: projectConfig(projectConfig), data(data) {}
|
||||
CommandResult validate() override;
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
class SaveConfigCommand : public ICommand {
|
||||
ProjectConfig& projectConfig;
|
||||
|
||||
public:
|
||||
SaveConfigCommand(ProjectConfig& projectConfig)
|
||||
: projectConfig(projectConfig) {}
|
||||
|
||||
CommandResult validate() override {
|
||||
return CommandResult::getSuccessResult("");
|
||||
};
|
||||
|
||||
CommandResult execute() override;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,9 +1,20 @@
|
||||
#include "CommandManager.hpp"
|
||||
|
||||
CommandManager::CommandManager(ProjectConfig* deviceConfig)
|
||||
: deviceConfig(deviceConfig) {}
|
||||
std::unique_ptr<ICommand> CommandManager::createCommand(CommandType commandType,
|
||||
JsonVariant& data) {
|
||||
switch (commandType) {
|
||||
case CommandType::PING:
|
||||
return std::make_unique<PingCommand>();
|
||||
case CommandType::SET_WIFI:
|
||||
return std::make_unique<SetWiFiCommand>(this->projectConfig, data);
|
||||
case CommandType::SET_MDNS:
|
||||
return std::make_unique<SetMDNSCommand>(this->projectConfig, data);
|
||||
case CommandType::SAVE_CONFIG:
|
||||
return std::make_unique<SaveConfigCommand>(this->projectConfig);
|
||||
}
|
||||
}
|
||||
|
||||
const CommandType CommandManager::getCommandType(JsonVariant& command) {
|
||||
CommandType CommandManager::getCommandType(JsonVariant& command) {
|
||||
if (!command.containsKey("command"))
|
||||
return CommandType::None;
|
||||
|
||||
@ -18,64 +29,87 @@ bool CommandManager::hasDataField(JsonVariant& command) {
|
||||
return command.containsKey("data");
|
||||
}
|
||||
|
||||
void CommandManager::handleCommands(CommandsPayload commandsPayload) {
|
||||
std::variant<std::vector<CommandResult>, CommandResult>
|
||||
CommandManager::handleBatchCommands(CommandsPayload commandsPayload) {
|
||||
std::vector<CommandResult> results = {};
|
||||
std::vector<std::string> errors = {};
|
||||
std::vector<std::unique_ptr<ICommand>> commands;
|
||||
|
||||
if (!commandsPayload.data.containsKey("commands")) {
|
||||
log_e("Json data sent not supported, lacks commands field");
|
||||
return;
|
||||
std::string error = "Json data sent not supported, lacks commands field";
|
||||
log_e("%s", error.c_str());
|
||||
return CommandResult::getErrorResult(
|
||||
Helpers::format_string("\"error\":\"%s\"", error));
|
||||
}
|
||||
|
||||
for (JsonVariant commandData :
|
||||
commandsPayload.data["commands"].as<JsonArray>()) {
|
||||
this->handleCommand(commandData);
|
||||
auto command_or_result = this->createCommandFromJsonVariant(commandData);
|
||||
|
||||
if (auto command_ptr =
|
||||
std::get_if<std::unique_ptr<ICommand>>(&command_or_result)) {
|
||||
auto validation_result = (*command_ptr)->validate();
|
||||
if (validation_result.isSuccess())
|
||||
commands.emplace_back(std::move((*command_ptr)));
|
||||
else
|
||||
errors.push_back(validation_result.getErrorMessage());
|
||||
} else {
|
||||
errors.push_back(
|
||||
std::get<CommandResult>(command_or_result).getErrorMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this->deviceConfig->save();
|
||||
// if we have any errors, consolidate them into a single message and return
|
||||
if (errors.size() > 0) {
|
||||
return CommandResult::getErrorResult(Helpers::format_string(
|
||||
"\"error\":\"[%s]\"", this->join_strings(errors, ",")));
|
||||
}
|
||||
|
||||
for (auto& valid_command : commands) {
|
||||
results.push_back(valid_command->execute());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
void CommandManager::handleCommand(JsonVariant command) {
|
||||
auto command_type = this->getCommandType(command);
|
||||
CommandResult CommandManager::handleSingleCommand(
|
||||
CommandsPayload commandsPayload) {
|
||||
if (!commandsPayload.data.containsKey("command")) {
|
||||
std::string error = "Json data sent not supported, lacks commands field";
|
||||
log_e("%s", error.c_str());
|
||||
|
||||
switch (command_type) {
|
||||
case CommandType::SET_WIFI: {
|
||||
if (!this->hasDataField(command))
|
||||
// malformed command, lacked data field
|
||||
break;
|
||||
|
||||
if (!command["data"].containsKey("ssid") ||
|
||||
!command["data"].containsKey("password"))
|
||||
break;
|
||||
|
||||
std::string customNetworkName = "main";
|
||||
if (command["data"].containsKey("network_name"))
|
||||
customNetworkName = command["data"]["network_name"].as<std::string>();
|
||||
|
||||
this->deviceConfig->setWifiConfig(customNetworkName,
|
||||
command["data"]["ssid"],
|
||||
command["data"]["password"],
|
||||
0, // channel, should this be zero?
|
||||
0, // power, should this be zero?
|
||||
false, false);
|
||||
|
||||
break;
|
||||
}
|
||||
case CommandType::SET_MDNS: {
|
||||
if (!this->hasDataField(command))
|
||||
break;
|
||||
|
||||
if (!command["data"].containsKey("hostname") ||
|
||||
!strlen(command["data"]["hostname"]))
|
||||
break;
|
||||
|
||||
this->deviceConfig->setMDNSConfig(command["data"]["hostname"],
|
||||
"openiristracker", false);
|
||||
|
||||
break;
|
||||
}
|
||||
case CommandType::PING: {
|
||||
Serial.println("PONG \n\r");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
CommandResult::getErrorResult(
|
||||
Helpers::format_string("\"error\":\"%s\"", error));
|
||||
}
|
||||
|
||||
JsonVariant commandData = commandsPayload.data["command"];
|
||||
auto command_or_result = this->createCommandFromJsonVariant(commandData);
|
||||
|
||||
if (std::holds_alternative<CommandResult>(command_or_result)) {
|
||||
return std::get<CommandResult>(command_or_result);
|
||||
}
|
||||
|
||||
auto command =
|
||||
std::move(std::get<std::unique_ptr<ICommand>>(command_or_result));
|
||||
|
||||
auto validation_result = command->validate();
|
||||
if (!validation_result.isSuccess()) {
|
||||
return validation_result;
|
||||
};
|
||||
|
||||
return command->execute();
|
||||
}
|
||||
|
||||
std::variant<std::unique_ptr<ICommand>, CommandResult>
|
||||
CommandManager::createCommandFromJsonVariant(JsonVariant& command) {
|
||||
auto command_type = this->getCommandType(command);
|
||||
if (command_type == CommandType::None) {
|
||||
std::string error =
|
||||
Helpers::format_string("Command not supported: %s", command["command"]);
|
||||
log_e("%s", error.c_str());
|
||||
throw CommandResult::getErrorResult(
|
||||
Helpers::format_string("\"error\":\"%s\"", error));
|
||||
}
|
||||
return this->createCommand(command_type, command);
|
||||
}
|
@ -2,37 +2,69 @@
|
||||
#ifndef TASK_MANAGER_HPP
|
||||
#define TASK_MANAGER_HPP
|
||||
#include <ArduinoJson.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "data/CommandManager/Command.hpp"
|
||||
#include "data/config/project_config.hpp"
|
||||
|
||||
struct CommandsPayload {
|
||||
JsonVariant data;
|
||||
};
|
||||
|
||||
enum CommandType {
|
||||
None,
|
||||
PING,
|
||||
SET_WIFI,
|
||||
SET_MDNS,
|
||||
SAVE_CONFIG,
|
||||
};
|
||||
|
||||
struct CommandsPayload {
|
||||
JsonDocument data;
|
||||
const std::unordered_map<std::string, CommandType> commandMap = {
|
||||
{"ping", CommandType::PING},
|
||||
{"set_wifi", CommandType::SET_WIFI},
|
||||
{"set_mdns", CommandType::SET_MDNS},
|
||||
};
|
||||
|
||||
class CommandManager {
|
||||
private:
|
||||
const std::unordered_map<std::string, CommandType> commandMap = {
|
||||
{"ping", CommandType::PING},
|
||||
{"set_wifi", CommandType::SET_WIFI},
|
||||
{"set_mdns", CommandType::SET_MDNS},
|
||||
};
|
||||
ProjectConfig& projectConfig;
|
||||
|
||||
ProjectConfig* deviceConfig;
|
||||
std::string join_strings(std::vector<std::string> const& strings,
|
||||
std::string delim) {
|
||||
std::stringstream ss;
|
||||
std::copy(strings.begin(), strings.end(),
|
||||
std::ostream_iterator<std::string>(ss, delim.c_str()));
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool hasDataField(JsonVariant& command);
|
||||
void handleCommand(JsonVariant command);
|
||||
const CommandType getCommandType(JsonVariant& command);
|
||||
std::unique_ptr<ICommand> createCommand(CommandType commandType,
|
||||
JsonVariant& data);
|
||||
|
||||
std::variant<std::unique_ptr<ICommand>, CommandResult>
|
||||
createCommandFromJsonVariant(JsonVariant& command);
|
||||
|
||||
CommandType getCommandType(JsonVariant& command);
|
||||
|
||||
// // TODO rewrite the API
|
||||
// // TODO add FPS/ Freq / cropping to the API
|
||||
// // TODO rewrite camera handler to be simpler and easier to change
|
||||
|
||||
public:
|
||||
CommandManager(ProjectConfig* deviceConfig);
|
||||
void handleCommands(CommandsPayload commandsPayload);
|
||||
};
|
||||
CommandManager(ProjectConfig& projectConfig)
|
||||
: projectConfig(projectConfig) {};
|
||||
|
||||
CommandResult handleSingleCommand(CommandsPayload commandsPayload);
|
||||
std::variant<std::vector<CommandResult>, CommandResult> handleBatchCommands(
|
||||
CommandsPayload commandsPayload);
|
||||
};
|
||||
#endif
|
@ -72,7 +72,7 @@ void SerialManager::run() {
|
||||
}
|
||||
|
||||
CommandsPayload commands = {doc};
|
||||
this->commandManager->handleCommands(commands);
|
||||
this->commandManager->handleBatchCommands(commands);
|
||||
}
|
||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
||||
else {
|
||||
|
@ -8,9 +8,7 @@
|
||||
#include <esp_camera.h>
|
||||
#include "data/CommandManager/CommandManager.hpp"
|
||||
#include "data/config/project_config.hpp"
|
||||
|
||||
const char* const ETVR_HEADER = "\xff\xa0";
|
||||
const char* const ETVR_HEADER_FRAME = "\xff\xa1";
|
||||
#include "data/utilities/helpers.hpp"
|
||||
|
||||
enum QueryAction {
|
||||
READY_TO_RECEIVE,
|
||||
|
Loading…
Reference in New Issue
Block a user