From 286344c84e13f8d80cb81792ce63407b94fee236 Mon Sep 17 00:00:00 2001 From: Rainer Kartmann <rainer.kartmann@kit.edu> Date: Wed, 9 Jun 2021 15:25:47 +0200 Subject: [PATCH] Add and implement part of mns::Client --- .../RobotAPI/libraries/armem/CMakeLists.txt | 4 + source/RobotAPI/libraries/armem/core/error.h | 1 + .../libraries/armem/core/error/ArMemError.cpp | 2 + .../libraries/armem/core/error/ArMemError.h | 5 +- .../libraries/armem/core/error/mns.cpp | 64 ++++++ .../RobotAPI/libraries/armem/core/error/mns.h | 51 +++++ .../RobotAPI/libraries/armem/mns/Client.cpp | 183 ++++++++++++++++++ source/RobotAPI/libraries/armem/mns/Client.h | 178 +++++++++++++++++ .../libraries/armem/mns/ClientPlugin.cpp | 8 +- .../libraries/armem/mns/ClientPlugin.h | 5 +- 10 files changed, 494 insertions(+), 7 deletions(-) create mode 100644 source/RobotAPI/libraries/armem/core/error/mns.cpp create mode 100644 source/RobotAPI/libraries/armem/core/error/mns.h create mode 100644 source/RobotAPI/libraries/armem/mns/Client.cpp create mode 100644 source/RobotAPI/libraries/armem/mns/Client.h diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt index eb83670b3..621f11585 100644 --- a/source/RobotAPI/libraries/armem/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/CMakeLists.txt @@ -59,6 +59,7 @@ set(LIB_FILES core/diskmemory/ProviderSegment.cpp core/error/ArMemError.cpp + core/error/mns.cpp client/ComponentPlugin.cpp client/Reader.cpp @@ -94,6 +95,7 @@ set(LIB_FILES server/query_proc/longtermmemory/MemoryQueryProcessor.cpp mns/MemoryNameSystem.cpp + mns/Client.cpp mns/ClientPlugin.cpp mns/ComponentPlugin.cpp @@ -113,6 +115,7 @@ set(LIB_HEADERS core/error.h core/error/ArMemError.h + core/error/mns.h core/base/detail/MemoryItem.h core/base/detail/MaxHistorySize.h @@ -197,6 +200,7 @@ set(LIB_HEADERS mns.h mns/MemoryNameSystem.h + mns/Client.h mns/ClientPlugin.h mns/ComponentPlugin.h diff --git a/source/RobotAPI/libraries/armem/core/error.h b/source/RobotAPI/libraries/armem/core/error.h index 36b745974..31bef6c27 100644 --- a/source/RobotAPI/libraries/armem/core/error.h +++ b/source/RobotAPI/libraries/armem/core/error.h @@ -1,6 +1,7 @@ #pragma once #include "error/ArMemError.h" +#include "error/mns.h" namespace armarx::armem::error diff --git a/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp b/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp index c44a40758..54f7770cc 100644 --- a/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp +++ b/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp @@ -4,6 +4,8 @@ #include <SimoxUtility/algorithm/string/string_tools.h> +#include "../MemoryID.h" + namespace armarx::armem::error { diff --git a/source/RobotAPI/libraries/armem/core/error/ArMemError.h b/source/RobotAPI/libraries/armem/core/error/ArMemError.h index 3456c7a35..5de5f7249 100644 --- a/source/RobotAPI/libraries/armem/core/error/ArMemError.h +++ b/source/RobotAPI/libraries/armem/core/error/ArMemError.h @@ -3,8 +3,11 @@ #include <stdexcept> #include <SimoxUtility/meta/type_name.h> -#include "../MemoryID.h" +namespace armarx::armem +{ + class MemoryID; +} namespace armarx::armem::error { diff --git a/source/RobotAPI/libraries/armem/core/error/mns.cpp b/source/RobotAPI/libraries/armem/core/error/mns.cpp new file mode 100644 index 000000000..5a29321f7 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/error/mns.cpp @@ -0,0 +1,64 @@ +#include "mns.h" + +#include <sstream> + +#include "../MemoryID.h" + + +namespace armarx::armem::error +{ + + MemoryNameSystemQueryFailed::MemoryNameSystemQueryFailed(const std::string& function, const std::string& errorMessage) : + ArMemError(makeMsg(function, errorMessage)) + { + } + + std::string MemoryNameSystemQueryFailed::makeMsg(const std::string& function, const std::string& errorMessage) + { + std::stringstream ss; + ss << "Failed to call '" << function << "' on the memory name system.\n"; + if (not errorMessage.empty()) + { + ss << "\n" << errorMessage; + } + return ss.str(); + } + + + + CouldNotResolveMemoryServer::CouldNotResolveMemoryServer(const MemoryID& memoryID, const std::string& errorMessage) : + ArMemError(makeMsg(memoryID, errorMessage)) + { + } + + std::string CouldNotResolveMemoryServer::makeMsg(const MemoryID& memoryID, const std::string& errorMessage) + { + std::stringstream ss; + ss << "Could not resolve the memory name '" << memoryID << "'." + << "\nMemory server for '" << memoryID << "' is not registered."; + if (not errorMessage.empty()) + { + ss << "\n" << errorMessage; + } + return ss.str(); + } + + + + MemoryServerRegistrationFailed::MemoryServerRegistrationFailed(const MemoryID& memoryID, const std::string& errorMessage) : + ArMemError(makeMsg(memoryID, errorMessage)) + { + } + + std::string MemoryServerRegistrationFailed::makeMsg(const MemoryID& memoryID, const std::string& errorMessage) + { + std::stringstream ss; + ss << "Failed to register memory server for '" << memoryID << "' in the Memory Name System (MNS)."; + if (not errorMessage.empty()) + { + ss << "\n" << errorMessage; + } + return ss.str(); + } + +} diff --git a/source/RobotAPI/libraries/armem/core/error/mns.h b/source/RobotAPI/libraries/armem/core/error/mns.h new file mode 100644 index 000000000..4c4295729 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/error/mns.h @@ -0,0 +1,51 @@ +#pragma once + +#include "ArMemError.h" + + +namespace armarx::armem::error +{ + + /** + * @brief Indicates that a query to the Memory Name System failed. + */ + class MemoryNameSystemQueryFailed : public ArMemError + { + public: + + MemoryNameSystemQueryFailed(const std::string& function, const std::string& errorMessage = ""); + + static std::string makeMsg(const std::string& function, const std::string& errorMessage = ""); + + }; + + + /** + * @brief Indicates that a query to the Memory Name System failed. + */ + class CouldNotResolveMemoryServer : public ArMemError + { + public: + + CouldNotResolveMemoryServer(const MemoryID& memoryID, const std::string& errorMessage = ""); + + static std::string makeMsg(const MemoryID& memoryID, const std::string& errorMessage = ""); + + }; + + + /** + * @brief Indicates that a query to the Memory Name System failed. + */ + class MemoryServerRegistrationFailed : public ArMemError + { + public: + + MemoryServerRegistrationFailed(const MemoryID& memoryID, const std::string& errorMessage = ""); + + static std::string makeMsg(const MemoryID& memoryID, const std::string& errorMessage = ""); + + }; + +} + diff --git a/source/RobotAPI/libraries/armem/mns/Client.cpp b/source/RobotAPI/libraries/armem/mns/Client.cpp new file mode 100644 index 000000000..aa751e6d0 --- /dev/null +++ b/source/RobotAPI/libraries/armem/mns/Client.cpp @@ -0,0 +1,183 @@ +#include "Client.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/ManagedIceObject.h> + +#include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/client/Reader.h> +#include <RobotAPI/libraries/armem/client/Writer.h> + + +namespace armarx::armem::mns +{ + + Client::Client() + { + } + + + Client::Client(MemoryNameSystemInterfacePrx mns) : mns(mns) + { + } + + + void Client::update() + { + ARMARX_CHECK_NOT_NULL(mns); + + data::GetAllRegisteredMemoriesResult result = mns->getAllRegisteredMemories(); + + if (result.success) + { + this->memoryMap = result.proxies; + } + else + { + throw error::MemoryNameSystemQueryFailed(result.errorMessage); + } + } + + + server::MemoryInterfacePrx Client::resolveServer(const MemoryID& memoryID) + { + if (auto it = memoryMap.find(memoryID.memoryName); it != memoryMap.end()) + { + return it->second; + } + else + { + update(); + if (auto it = memoryMap.find(memoryID.memoryName); it != memoryMap.end()) + { + return it->second; + } + else + { + throw error::CouldNotResolveMemoryServer(memoryID); + } + } + } + + + server::MemoryInterfacePrx Client::waitForServer(const MemoryID& memoryID, Time timeout) + { + if (auto it = memoryMap.find(memoryID.memoryName); it != memoryMap.end()) + { + return it->second; + } + else + { + armem::data::WaitForMemoryInput input; + input.name = memoryID.memoryName; + input.timeoutMilliSeconds = timeout.toMilliSeconds(); + + armem::data::WaitForMemoryResult result = mns->waitForMemory(input); + if (result.success) + { + return result.proxy; + } + else + { + throw error::CouldNotResolveMemoryServer(memoryID, result.errorMessage); + } + } + } + + + server::MemoryInterfacePrx Client::useServer(const MemoryID& memoryID, ManagedIceObject& component) + { + server::MemoryInterfacePrx server = waitForServer(memoryID); + // Add dependency. + component.usingProxy(server->ice_getIdentity().name); + return server; + } + + client::Reader Client::getReader(const MemoryID& memoryID) + { + return client::Reader(resolveServer(memoryID)); + } + + + template <class ClientT> + std::map<std::string, ClientT> Client::_getAllClients() const + { + std::map<std::string, ClientT> result; + for (const auto& [name, server] : memoryMap) + { + result[name] = ClientT(server); + } + return result; + } + + + template <class ClientT> + std::map<std::string, ClientT> Client::_getAllClients(bool update) + { + if (update) + { + this->update(); + } + return const_cast<const Client&>(*this)._getAllClients<ClientT>(); + } + + + std::map<std::string, client::Reader> Client::getAllReaders(bool update) + { + return _getAllClients<client::Reader>(update); + } + + + std::map<std::string, client::Reader> Client::getAllReaders() const + { + return _getAllClients<client::Reader>(); + } + + + client::Writer Client::getWriter(const MemoryID& memoryID) + { + client::Writer(resolveServer(memoryID)); + } + + + std::map<std::string, client::Writer> Client::getAllWriters(bool update) + { + return _getAllClients<client::Writer>(update); + } + + + std::map<std::string, client::Writer> Client::getAllWriters() const + { + return _getAllClients<client::Writer>(); + } + + + void Client::registerServer(const MemoryID& memoryID, server::MemoryInterfacePrx proxy) + { + data::RegisterMemoryInput input; + input.name = memoryID.memoryName; + input.proxy = proxy; + ARMARX_CHECK_NOT_NULL(input.proxy); + + data::RegisterMemoryResult result = mns->registerMemory(input); + if (!result.success) + { + throw error::MemoryServerRegistrationFailed(memoryID, result.errorMessage); + } + } + + + mns::MemoryNameSystemInterfacePrx Client::getMemoryNameSystem() const + { + return mns; + } + + + void Client::getMemoryNameSystem(mns::MemoryNameSystemInterfacePrx mns) + { + this->mns = mns; + } + +} + + + diff --git a/source/RobotAPI/libraries/armem/mns/Client.h b/source/RobotAPI/libraries/armem/mns/Client.h new file mode 100644 index 000000000..bf77eec91 --- /dev/null +++ b/source/RobotAPI/libraries/armem/mns/Client.h @@ -0,0 +1,178 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::libraries::armem + * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu ) + * @date 2020 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> +#include <RobotAPI/interface/armem/server/MemoryInterface.h> + +#include <RobotAPI/libraries/armem/core/MemoryID.h> + + +namespace armarx +{ + class ManagedIceObject; +} +namespace armarx::armem::client +{ + class Reader; + class Writer; +} + +namespace armarx::armem::mns +{ + + /** + * @brief The memory name system (MNS) client. + * + * This client class has a proxy to the MNS and can be used to construct + * (single) Memory Clients as well as fetch the data for any Memory IDs. + * Like in DNS, it stores a map from memory names to proxies which is used + * if the respective entry exists. Otherwise, it queries the MNS. + * Provides the other interface functions of the MNS. + * Can be used to query arbitrary Memory IDs (resolving the memory name, + * then sending a query to that memory, returning the result). + */ + class Client + { + public: + + Client(); + Client(MemoryNameSystemInterfacePrx mns); + + + mns::MemoryNameSystemInterfacePrx getMemoryNameSystem() const; + void getMemoryNameSystem(mns::MemoryNameSystemInterfacePrx mns); + + + // Name Resolution + + /** + * @brief Update the internal registry to the data in the MNS. + * + * @throw `error::MemoryNameSystemQueryFailed` If the call to the MNS failed. + */ + void update(); + + /** + * @brief Resolve the given memory server for the given memory ID. + * + * @param memoryID The memory ID. + * @return The memory server proxy. + * + * @throw `error::CouldNotResolveMemoryServer` If the memory name could not be resolved. + */ + server::MemoryInterfacePrx resolveServer(const MemoryID& memoryID); + + /** + * @brief Wait for the given memory server. + * + * @param memoryID The memory ID. + * @param timeout How long to wait at maximum. Negative values indicate infinite wait. + * @return The memory server proxy. + * + * @throw `error::CouldNotResolveMemoryServer` If the memory name could not be resolved. + */ + server::MemoryInterfacePrx waitForServer(const MemoryID& memoryID, Time timeout = Time::milliSeconds(-1)); + + /** + * @brief Wait for the given memory server and add a dependency to the memory server. + * + * @param memoryID The memory ID. + * @param component The component that should depend on the memory server. + * @return The memory server proxy. + * + * @throw `error::CouldNotResolveMemoryServer` If the memory name could not be resolved. + */ + server::MemoryInterfacePrx useServer(const MemoryID& memoryID, ManagedIceObject& component); + + + // Client reader/writer construction + + /** + * @brief Get a reader to the given memory name. + * + * @param memoryID The memory ID. + * @return The reader. + * + * @throw `error::CouldNotResolveMemoryServer` If the memory name could not be resolved. + */ + client::Reader getReader(const MemoryID& memoryID); + + std::map<std::string, client::Reader> getAllReaders(bool update = true); + std::map<std::string, client::Reader> getAllReaders() const; + + + /** + * @brief Get a writer to the given memory name. + * + * @param memoryID The memory ID. + * @return The writer. + * + * @throw `error::CouldNotResolveMemoryServer` If the memory name could not be resolved. + */ + client::Writer getWriter(const MemoryID& memoryID); + + std::map<std::string, client::Writer> getAllWriters(bool update = true); + std::map<std::string, client::Writer> getAllWriters() const; + + + // Registration - only for memory servers + + /** + * @brief Register a memory server in the MNS. + * + * @param memoryID The memoryID. + * @param server The memory server proxy. + * + * @throw `error::MemoryServerRegistrationFailed` If the registration failed. + */ + void registerServer(const MemoryID& memoryID, server::MemoryInterfacePrx server); + + + + // Operators + + /// Indicate whether the proxy is set. + inline operator bool() const + { + return bool(mns); + } + + + private: + + template <class ClientT> + std::map<std::string, ClientT> _getAllClients(bool update); + template <class ClientT> + std::map<std::string, ClientT> _getAllClients() const; + + + MemoryNameSystemInterfacePrx mns = nullptr; + + std::map<std::string, server::MemoryInterfacePrx> memoryMap; + + + }; + + +} diff --git a/source/RobotAPI/libraries/armem/mns/ClientPlugin.cpp b/source/RobotAPI/libraries/armem/mns/ClientPlugin.cpp index f7bba9445..6b318abf8 100644 --- a/source/RobotAPI/libraries/armem/mns/ClientPlugin.cpp +++ b/source/RobotAPI/libraries/armem/mns/ClientPlugin.cpp @@ -44,7 +44,7 @@ namespace armarx::armem::mns::plugins { if (isMemoryNameSystemEnabled()) { - parent<ClientPluginUserBase>().memoryNameSystem = getMemoryNameSystem(); + parent<ClientPluginUserBase>().memoryNameSystem = mns::Client(getMemoryNameSystem()); } } @@ -83,7 +83,7 @@ namespace armarx::armem::mns::plugins armem::data::WaitForMemoryInput input; input.name = memoryName; - armem::data::WaitForMemoryResult result = memoryNameSystem->waitForMemory(input); + armem::data::WaitForMemoryResult result = memoryNameSystem.getMemoryNameSystem()->waitForMemory(input); if (result.success) { if (Component* comp = dynamic_cast<Component*>(this)) @@ -103,7 +103,7 @@ namespace armarx::armem::mns::plugins } armem::data::WaitForMemoryInput input; input.name = memoryName; - return memoryNameSystem->waitForMemory(input); + return memoryNameSystem.getMemoryNameSystem()->waitForMemory(input); } armem::data::ResolveMemoryNameResult ClientPluginUserBase::resolveMemoryName(const std::string& memoryName) @@ -114,7 +114,7 @@ namespace armarx::armem::mns::plugins } armem::data::ResolveMemoryNameInput input; input.name = memoryName; - return memoryNameSystem->resolveMemoryName(input); + return memoryNameSystem.getMemoryNameSystem()->resolveMemoryName(input); } bool ClientPluginUserBase::isMemoryAvailable(const std::string& memoryName) diff --git a/source/RobotAPI/libraries/armem/mns/ClientPlugin.h b/source/RobotAPI/libraries/armem/mns/ClientPlugin.h index 4ebc9aad3..51249a2a3 100644 --- a/source/RobotAPI/libraries/armem/mns/ClientPlugin.h +++ b/source/RobotAPI/libraries/armem/mns/ClientPlugin.h @@ -4,6 +4,7 @@ #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> #include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/mns/Client.h> namespace armarx::armem::mns @@ -72,8 +73,8 @@ namespace armarx::armem::mns::plugins public: - /// Only set when enabled. - mns::MemoryNameSystemInterfacePrx memoryNameSystem = nullptr; + /// Only valid when enabled. + mns::Client memoryNameSystem; bool memoryNameSystemEnabled = true; std::string memoryNameSystemName = "MemoryNameSystem"; -- GitLab