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