From 2e93f83cc0712a172450cf22f5837c1603f4dece Mon Sep 17 00:00:00 2001
From: Fabian Reister <fabian.reister@kit.edu>
Date: Mon, 12 Apr 2021 17:22:48 +0200
Subject: [PATCH] minor fixes

---
 .../RobotAPI/libraries/armem/CMakeLists.txt   |   4 +
 source/RobotAPI/libraries/armem/util/util.cpp |   3 +
 source/RobotAPI/libraries/armem/util/util.h   | 142 ++++++++++++++
 .../MemoryConnector.cpp                       |   2 +
 .../MemoryConnector.h                         |  13 +-
 .../armem_robot_mapping/MappingDataReader.cpp | 173 +++++++-----------
 .../armem_robot_mapping/MappingDataReader.h   |   2 +-
 .../armem_robot_mapping/MappingDataWriter.h   |   2 +-
 8 files changed, 231 insertions(+), 110 deletions(-)
 create mode 100644 source/RobotAPI/libraries/armem/util/util.cpp
 create mode 100644 source/RobotAPI/libraries/armem/util/util.h

diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt
index 0dca18cdb..84bb291a7 100644
--- a/source/RobotAPI/libraries/armem/CMakeLists.txt
+++ b/source/RobotAPI/libraries/armem/CMakeLists.txt
@@ -66,6 +66,8 @@ set(LIB_FILES
     mns/MemoryNameSystem.cpp
     mns/ClientPlugin.cpp
     mns/ComponentPlugin.cpp
+
+    util/util.cpp
 )
 
 set(LIB_HEADERS
@@ -135,6 +137,8 @@ set(LIB_HEADERS
     mns/ClientPlugin.h
     mns/ComponentPlugin.h
 
+    util/util.h
+
 )
 
 armarx_add_library("${LIB_NAME}" "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}")
diff --git a/source/RobotAPI/libraries/armem/util/util.cpp b/source/RobotAPI/libraries/armem/util/util.cpp
new file mode 100644
index 000000000..34f0e2f56
--- /dev/null
+++ b/source/RobotAPI/libraries/armem/util/util.cpp
@@ -0,0 +1,3 @@
+#include "util.h"
+
+// intentionally left blank
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem/util/util.h b/source/RobotAPI/libraries/armem/util/util.h
new file mode 100644
index 000000000..8dcb4b997
--- /dev/null
+++ b/source/RobotAPI/libraries/armem/util/util.h
@@ -0,0 +1,142 @@
+/*
+ * 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    RobotComponents::ArmarXObjects::
+ * @author     Fabian Reister ( fabian dot reister at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#pragma once
+
+#include <vector>
+#include <optional>
+
+#include <RobotAPI/libraries/armem/core/Entity.h>
+#include <RobotAPI/libraries/armem/core/EntityInstance.h>
+#include <RobotAPI/libraries/aron/core/codegenerator/codeWriter/cpp/AronCppClass.h>
+
+namespace armarx::armem {
+
+    /**
+     * @brief Tries to cast a armem::EntityInstance to AronClass
+     * 
+     * @tparam AronClass class name. Needs to be derived from armarx::aron::cppcodegenerator::AronCppClass
+     * @param item 
+     * @return std::optional<AronClass> 
+     */
+    template <typename AronClass>
+    std::optional<AronClass> tryCast(const EntityInstance& item)
+    {
+        static_assert(std::is_base_of<armarx::aron::cppcodegenerator::AronCppClass,
+                      AronClass>::value);
+
+        try
+        {
+            AronClass t;
+            t.fromAron(item.data());
+            return t;
+        }
+        catch (const armarx::aron::error::AronException&)
+        {
+            return std::nullopt;
+        }
+    }
+
+    /**
+     * @brief Returns all entities that can be cast to AronClass
+     * 
+     * @tparam AronClass class name. Needs to be derived from armarx::aron::cppcodegenerator::AronCppClass
+     * @param entities collection of entities
+     * @return std::vector<AronClass> 
+     */
+    template <typename AronClass>
+    std::vector<AronClass>
+    allOfType(const std::map<std::string, Entity>& entities)
+    {
+        static_assert(std::is_base_of<armarx::aron::cppcodegenerator::AronCppClass,
+                      AronClass>::value);
+
+        std::vector<AronClass> outV;
+
+        // loop over all entities and their snapshots
+        for (const auto &[s, entity] : entities)
+        {
+            for (const auto &[ss, entitySnapshot] : entity.history)
+            {
+                for (const auto& entityInstance : entitySnapshot.instances)
+                {
+                    const auto o = tryCast<AronClass>(entityInstance);
+
+                    if (o)
+                    {
+                        outV.push_back(*o);
+                    }
+                }
+            }
+        }
+
+        return outV;
+    }
+
+    /**
+     * @brief filter + transform for entities. 
+     * 
+     * Can be used instead of
+     *
+     *      std::vector<Bar> ret;
+     *
+     *      const auto allOf = allOfType<Foo>(entities);
+     *      std::transform(allOf.begin(), allOf.end(), std::back_inserter(ret), pred)
+     *
+     * This function has the benefit that the transform function will be applied directly. 
+     * No intermediate vector has to be created (e.g. "allOf" in the example above). 
+     * 
+     * @tparam AronClass class name. Needs to be derived from armarx::aron::cppcodegenerator::AronCppClass
+     * @param entities collection of entities
+     * @param pred binary predicate function, applied to all entity instances
+     * @return vector of "pred"-transformed elements that can be cast to AronClass 
+     */
+    template <typename AronClass>
+    auto transformAllOfType(const std::map<std::string, Entity>& entities,
+                            auto pred) -> std::vector<decltype(pred(AronClass()))> 
+    {
+        static_assert(std::is_base_of<armarx::aron::cppcodegenerator::AronCppClass,
+                      AronClass>::value);
+
+        std::vector<decltype(pred(AronClass()))> outV;
+
+        // loop over all entities and their snapshots
+        for (const auto &[s, entity] : entities)
+        {
+            for (const auto &[ss, entitySnapshot] : entity.history)
+            {
+                for (const auto& entityInstance : entitySnapshot.instances)
+                {
+                    const auto o = tryCast<AronClass>(entityInstance);
+
+                    if (o)
+                    {
+                        outV.push_back(pred(*o));
+                    }
+                }
+            }
+        }
+
+        return outV;
+    }
+
+} // namespace armarx::armem
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_robot_localization/MemoryConnector.cpp b/source/RobotAPI/libraries/armem_robot_localization/MemoryConnector.cpp
index c73dbdd19..4fadf6f65 100644
--- a/source/RobotAPI/libraries/armem_robot_localization/MemoryConnector.cpp
+++ b/source/RobotAPI/libraries/armem_robot_localization/MemoryConnector.cpp
@@ -10,6 +10,8 @@ namespace armarx::armem
 
     MemoryConnector::MemoryConnector(ManagedIceObject& component) : component(component) {}
 
+    MemoryConnector::~MemoryConnector() = default;
+
     void MemoryConnector::registerPropertyDefinitions(PropertyDefinitionsPtr& def)
     {
         const std::string prefix = getPropertyPrefix();
diff --git a/source/RobotAPI/libraries/armem_robot_localization/MemoryConnector.h b/source/RobotAPI/libraries/armem_robot_localization/MemoryConnector.h
index 68da96eb0..d3a9f288d 100644
--- a/source/RobotAPI/libraries/armem_robot_localization/MemoryConnector.h
+++ b/source/RobotAPI/libraries/armem_robot_localization/MemoryConnector.h
@@ -36,16 +36,19 @@ namespace armarx
 namespace armarx::armem
 {
 
-
-    // TODO(fabian.reister): add
-    // class PropertyDefinitionsPtr;
-
+    /**
+     * @brief The MemoryConnector class simplifies connecting to the ArMem memory.
+     *
+     * Use this as the base class of any class that needs to connect to the memory.
+     *
+     *
+     */
     class MemoryConnector
     {
 
     public:
         MemoryConnector(ManagedIceObject& component);
-        virtual ~MemoryConnector() = default;
+        virtual ~MemoryConnector();
 
     protected:
         armem::data::WaitForMemoryResult useMemory(const std::string& memoryName);
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp
index 7797a72db..126c6b003 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp
+++ b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.cpp
@@ -1,19 +1,62 @@
 #include "MappingDataReader.h"
-#include "ArmarXCore/core/logging/Logging.h"
-#include "RobotAPI/libraries/armem/core/EntityInstance.h"
-#include "RobotAPI/libraries/armem/core/EntitySnapshot.h"
-#include "RobotAPI/libraries/armem_robot_mapping/aron_conversions.h"
-#include "RobotAPI/libraries/armem_robot_mapping/types.h"
-#include "RobotAPI/libraries/aron/core/codegenerator/codeWriter/cpp/AronCppClass.h"
+#include "RobotAPI/libraries/armem_robot_localization/MemoryConnector.h"
+
+#include <vector>
 
 #include <IceUtil/Time.h>
+
+#include <ArmarXCore/core/logging/Logging.h>
+
+#include <RobotAPI/libraries/armem/core/EntityInstance.h>
+#include <RobotAPI/libraries/armem/core/EntitySnapshot.h>
+#include <RobotAPI/libraries/armem/util/util.h>
+#include <RobotAPI/libraries/armem_robot_mapping/aron_conversions.h>
+#include <RobotAPI/libraries/armem_robot_mapping/types.h>
+#include <RobotAPI/libraries/aron/core/codegenerator/codeWriter/cpp/AronCppClass.h>
 #include <RobotAPI/libraries/armem_robot_mapping/aron/LaserScan.aron.generated.h>
-#include <vector>
 
 namespace armarx::armem
 {
 
-    armem::client::query::Builder MappingDataReader::buildQuery(const Query& query) const
+    MappingDataReader::MappingDataReader(ManagedIceObject& component) : armarx::armem::MemoryConnector(component) {}
+
+    MappingDataReader::~MappingDataReader() = default;
+
+
+    void MappingDataReader::registerPropertyDefinitions(
+        armarx::PropertyDefinitionsPtr& def)
+    {
+        ARMARX_DEBUG << "TransformReader: registerPropertyDefinitions";
+        MemoryConnector::registerPropertyDefinitions(def);
+
+        const std::string prefix = getPropertyPrefix();
+
+        def->optional(properties.mappingMemoryName, prefix + "MappingMemoryName",
+                      "Name of the mapping memory core segment to use.");
+
+        def->optional(properties.memoryName, prefix + "MemoryName");
+    }
+
+    void MappingDataReader::connect()
+    {
+        // Wait for the memory to become available and add it as dependency.
+        ARMARX_IMPORTANT << "TransformReader: Waiting for memory '"
+                         << properties.memoryName << "' ...";
+        auto result = useMemory(properties.memoryName);
+        if (not result.success)
+        {
+            ARMARX_ERROR << result.errorMessage;
+            return;
+        }
+
+        ARMARX_IMPORTANT << "TransformReader: Connected to memory '"
+                         << properties.memoryName;
+
+        memoryReader.setReadingMemory(result.proxy);
+    }
+
+    armem::client::query::Builder
+    MappingDataReader::buildQuery(const Query& query) const
     {
         armem::client::query::Builder qb;
 
@@ -35,125 +78,49 @@ namespace armarx::armem
 
         qbEntities.snapshots().timeRange(
             IceUtil::Time::microSeconds(query.timeRange.min),
-            IceUtil::Time::microSeconds(query.timeRange.max)
-        );
+            IceUtil::Time::microSeconds(query.timeRange.max));
 
         return qb;
     }
 
-    template <typename AronClass>
-    std::optional<AronClass> tryCast(const EntityInstance& item)
-    {
-        static_assert(std::is_base_of<armarx::aron::cppcodegenerator::AronCppClass, AronClass>::value);
-
-        try
-        {
-            AronClass t;
-            t.fromAron(item.data());
-            return t;
-        }
-        catch (const armarx::aron::error::AronException&)
-        {
-            return std::nullopt;
-        }
-    }
-
-    template <typename AronClass>
-    std::vector<AronClass> allOfType(const std::map<std::string, Entity>& entities)
-    {
-        static_assert(std::is_base_of<armarx::aron::cppcodegenerator::AronCppClass, AronClass>::value);
-
-        std::vector<AronClass> outV;
-
-        // loop over all entities and their snapshots
-        for (const auto& [s, entity] : entities)
-        {
-            for (const auto& [ss, entitySnapshot] : entity.history)
-            {
-                for (const auto& entityInstance : entitySnapshot.instances)
-                {
-                    const auto o = tryCast<AronClass>(entityInstance);
-
-                    if (o)
-                    {
-                        outV.push_back(*o);
-                    }
-                }
-            }
-        }
-
-        return outV;
-    }
 
-    template <typename AronClass>
-    auto transformAllOfType(const std::map<std::string, Entity>& entities, auto pred)
-    {
-        static_assert(std::is_base_of<armarx::aron::cppcodegenerator::AronCppClass, AronClass>::value);
-
-        std::vector<decltype(pred(AronClass()))> outV;
-
-        // loop over all entities and their snapshots
-        for (const auto& [s, entity] : entities)
-        {
-            for (const auto& [ss, entitySnapshot] : entity.history)
-            {
-                for (const auto& entityInstance : entitySnapshot.instances)
-                {
-                    const auto o = tryCast<AronClass>(entityInstance);
-
-                    if (o)
-                    {
-                        outV.push_back(pred(*o));
-                    }
-                }
-            }
-        }
-
-        return outV;
-    }
-
-
-    MappingDataReader::Result MappingDataReader::queryData(const Query& query) const
+    MappingDataReader::Result
+    MappingDataReader::queryData(const Query& query) const
     {
         auto qb = buildQuery(query);
 
         ARMARX_IMPORTANT << "[MappingDataReader] query ... ";
 
-        const armem::client::QueryResult qResult = memoryReader.query(qb.buildQueryInput());
+        const armem::client::QueryResult qResult =
+            memoryReader.query(qb.buildQueryInput());
 
         ARMARX_DEBUG << "[MappingDataReader] result: " << qResult;
 
         if (not qResult.success)
         {
-            return
-            {
-                .laserScans = {},
-                .status       = Result::Status::Error,
-                .errorMessage = qResult.errorMessage
-            };
+            return {.laserScans = {},
+                    .status = Result::Status::Error,
+                    .errorMessage = qResult.errorMessage};
         }
 
         // now create result from memory
-        const auto& entities = qResult.memory
-                               .getCoreSegment(properties.mappingMemoryName)
-                               .getProviderSegment(query.agent)
-                               .entities;
-
-
-        auto laserScans = transformAllOfType<aron::LaserScanStamped>(entities, [](const aron::LaserScanStamped & aronLaserScan) -> LaserScanStamped
+        const auto& entities =
+            qResult.memory.getCoreSegment(properties.mappingMemoryName)
+            .getProviderSegment(query.agent)
+            .entities;
+
+        auto laserScans = transformAllOfType<aron::LaserScanStamped>(
+                              entities,
+                              [](const aron::LaserScanStamped & aronLaserScan) -> LaserScanStamped
         {
             LaserScanStamped laserScan;
             fromAron(aronLaserScan, laserScan);
             return laserScan;
         });
 
-        return
-        {
-            .laserScans = std::move(laserScans),
-            .status = Result::Status::Success,
-            .errorMessage = ""
-        };
-
+        return {.laserScans = std::move(laserScans),
+                .status = Result::Status::Success,
+                .errorMessage = ""};
     }
 
 } // namespace armarx::armem
\ No newline at end of file
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h
index b5307d0de..3ccfc02ff 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h
+++ b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataReader.h
@@ -61,7 +61,7 @@ namespace armarx::armem
     public:
         MappingDataReader(ManagedIceObject& component);
 
-        ~MappingDataReader() override;
+        virtual ~MappingDataReader();
 
         void connect();
 
diff --git a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h
index 09ff4a1f3..5d5f21185 100644
--- a/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h
+++ b/source/RobotAPI/libraries/armem_robot_mapping/MappingDataWriter.h
@@ -50,7 +50,7 @@ namespace armarx::armem
     {
     public:
         MappingDataWriter(ManagedIceObject& component);
-        ~MappingDataWriter();
+        virtual ~MappingDataWriter();
 
         void connect();
 
-- 
GitLab