mirror of
https://github.com/EyeTrackVR/OpenIris.git
synced 2025-11-04 15:39:42 +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"
|
#include "CommandManager.hpp"
|
||||||
|
|
||||||
CommandManager::CommandManager(ProjectConfig* deviceConfig)
|
std::unique_ptr<ICommand> CommandManager::createCommand(CommandType commandType,
|
||||||
: deviceConfig(deviceConfig) {}
|
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"))
|
if (!command.containsKey("command"))
|
||||||
return CommandType::None;
|
return CommandType::None;
|
||||||
|
|
||||||
@ -18,64 +29,87 @@ bool CommandManager::hasDataField(JsonVariant& command) {
|
|||||||
return command.containsKey("data");
|
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")) {
|
if (!commandsPayload.data.containsKey("commands")) {
|
||||||
log_e("Json data sent not supported, lacks commands field");
|
std::string error = "Json data sent not supported, lacks commands field";
|
||||||
return;
|
log_e("%s", error.c_str());
|
||||||
|
return CommandResult::getErrorResult(
|
||||||
|
Helpers::format_string("\"error\":\"%s\"", error));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (JsonVariant commandData :
|
for (JsonVariant commandData :
|
||||||
commandsPayload.data["commands"].as<JsonArray>()) {
|
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) {
|
CommandResult CommandManager::handleSingleCommand(
|
||||||
auto command_type = this->getCommandType(command);
|
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) {
|
CommandResult::getErrorResult(
|
||||||
case CommandType::SET_WIFI: {
|
Helpers::format_string("\"error\":\"%s\"", error));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
#ifndef TASK_MANAGER_HPP
|
||||||
#define TASK_MANAGER_HPP
|
#define TASK_MANAGER_HPP
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
#include <unordered_map>
|
#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"
|
#include "data/config/project_config.hpp"
|
||||||
|
|
||||||
|
struct CommandsPayload {
|
||||||
|
JsonVariant data;
|
||||||
|
};
|
||||||
|
|
||||||
enum CommandType {
|
enum CommandType {
|
||||||
None,
|
None,
|
||||||
PING,
|
PING,
|
||||||
SET_WIFI,
|
SET_WIFI,
|
||||||
SET_MDNS,
|
SET_MDNS,
|
||||||
|
SAVE_CONFIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CommandsPayload {
|
const std::unordered_map<std::string, CommandType> commandMap = {
|
||||||
JsonDocument data;
|
{"ping", CommandType::PING},
|
||||||
|
{"set_wifi", CommandType::SET_WIFI},
|
||||||
|
{"set_mdns", CommandType::SET_MDNS},
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandManager {
|
class CommandManager {
|
||||||
private:
|
private:
|
||||||
const std::unordered_map<std::string, CommandType> commandMap = {
|
ProjectConfig& projectConfig;
|
||||||
{"ping", CommandType::PING},
|
|
||||||
{"set_wifi", CommandType::SET_WIFI},
|
|
||||||
{"set_mdns", CommandType::SET_MDNS},
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
bool hasDataField(JsonVariant& command);
|
||||||
void handleCommand(JsonVariant command);
|
std::unique_ptr<ICommand> createCommand(CommandType commandType,
|
||||||
const CommandType getCommandType(JsonVariant& command);
|
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:
|
public:
|
||||||
CommandManager(ProjectConfig* deviceConfig);
|
CommandManager(ProjectConfig& projectConfig)
|
||||||
void handleCommands(CommandsPayload commandsPayload);
|
: projectConfig(projectConfig) {};
|
||||||
};
|
|
||||||
|
|
||||||
|
CommandResult handleSingleCommand(CommandsPayload commandsPayload);
|
||||||
|
std::variant<std::vector<CommandResult>, CommandResult> handleBatchCommands(
|
||||||
|
CommandsPayload commandsPayload);
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -72,7 +72,7 @@ void SerialManager::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CommandsPayload commands = {doc};
|
CommandsPayload commands = {doc};
|
||||||
this->commandManager->handleCommands(commands);
|
this->commandManager->handleBatchCommands(commands);
|
||||||
}
|
}
|
||||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
#ifdef ETVR_EYE_TRACKER_USB_API
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -8,9 +8,7 @@
|
|||||||
#include <esp_camera.h>
|
#include <esp_camera.h>
|
||||||
#include "data/CommandManager/CommandManager.hpp"
|
#include "data/CommandManager/CommandManager.hpp"
|
||||||
#include "data/config/project_config.hpp"
|
#include "data/config/project_config.hpp"
|
||||||
|
#include "data/utilities/helpers.hpp"
|
||||||
const char* const ETVR_HEADER = "\xff\xa0";
|
|
||||||
const char* const ETVR_HEADER_FRAME = "\xff\xa1";
|
|
||||||
|
|
||||||
enum QueryAction {
|
enum QueryAction {
|
||||||
READY_TO_RECEIVE,
|
READY_TO_RECEIVE,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user