#pragma once #include #include #include #include "Commands/Command.hpp" #include "Responses/Response.hpp" #include "Responses/Error.hpp" #include "AtomicSession.hpp" #include "TargetControllerComponent.hpp" #include "src/Exceptions/Exception.hpp" #include "src/Logger/Logger.hpp" namespace TargetController { class CommandManager { public: template requires std::is_base_of_v && std::is_base_of_v auto sendCommandAndWaitForResponse( std::unique_ptr command, std::chrono::milliseconds timeout, std::optional atomicSessionId = std::nullopt ) const { using SuccessResponseType = typename CommandType::SuccessResponseType; const auto commandId = command->id; Logger::debug( "Issuing " + CommandType::name + " command (ID: " + std::to_string(commandId) + ") to TargetController" ); TargetControllerComponent::registerCommand(std::move(command), atomicSessionId); auto optionalResponse = TargetControllerComponent::waitForResponse(commandId, timeout); if (!optionalResponse.has_value()) { Logger::debug( "Timed out whilst waiting for TargetController to respond to " + CommandType::name + " command" ); throw Exceptions::Exception("Command timed out"); } auto& response = optionalResponse.value(); if (response->getType() == Responses::ResponseType::ERROR) { const auto errorResponse = dynamic_cast(response.get()); Logger::debug( "TargetController returned error in response to " + CommandType::name + " command (ID: " + std::to_string(commandId) + "). Error: " + errorResponse->errorMessage ); throw Exceptions::Exception(errorResponse->errorMessage); } Logger::debug( "Delivering response for " + CommandType::name + " command (ID: " + std::to_string(commandId) + ")" ); // Only downcast if the command's SuccessResponseType is not the generic Response type. if constexpr (!std::is_same_v) { assert(response->getType() == SuccessResponseType::type); return std::unique_ptr( dynamic_cast(response.release()) ); } else { return std::move(response); } } }; }