From c0b9a10e123298500f2231df252189fe46082208 Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Thu, 1 Dec 2022 15:50:25 +0100
Subject: [PATCH] Add filtering of object poses

---
 .../ObjectInstanceToIndex.cpp                 |  7 ++
 .../ObjectInstanceToIndex.h                   |  8 ++
 .../impl/ObjectInstanceToIndex.cpp            | 78 ++++++++++++++++---
 .../impl/ObjectInstanceToIndex.h              | 25 +++++-
 source/RobotAPI/interface/aron/Aron.ice       |  1 +
 5 files changed, 106 insertions(+), 13 deletions(-)

diff --git a/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/ObjectInstanceToIndex.cpp b/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/ObjectInstanceToIndex.cpp
index 2c1d33955..fedf604b0 100644
--- a/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/ObjectInstanceToIndex.cpp
+++ b/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/ObjectInstanceToIndex.cpp
@@ -53,6 +53,8 @@ namespace armarx
         armarx::PropertyDefinitionsPtr defs =
             new ComponentPropertyDefinitions(getConfigIdentifier());
 
+        defs->optional(properties.object.maxFrequencyHz, "p.object.maxFrequency");
+
         return defs;
     }
 
@@ -212,6 +214,7 @@ namespace armarx
     ObjectInstanceToIndex::processObjectInstance(const armem::MemoryID& id,
                                                  const std::vector<armem::MemoryID>& snapshotIDs)
     {
+        std::scoped_lock lock(objectMutex);
         if (not object.has_value())
         {
             object = armem::objects::ObjectInstanceToIndex{
@@ -221,6 +224,10 @@ namespace armarx
                     armem::index::spatialSegmentID.withProviderSegmentName(getName()),
                 .indexNamedProviderSegmentID =
                     armem::index::namedSegmentID.withProviderSegmentName(getName()),
+                .params = armem::objects::ObjectInstanceToIndex::Parameters{
+                    .maxFrequency = armarx::Frequency::Hertz(properties.object.maxFrequencyHz)
+                },
+                .state = {}
             };
         }
         ARMARX_CHECK(object.has_value());
diff --git a/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/ObjectInstanceToIndex.h b/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/ObjectInstanceToIndex.h
index c3a29df6f..c5e6486e7 100644
--- a/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/ObjectInstanceToIndex.h
+++ b/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/ObjectInstanceToIndex.h
@@ -23,6 +23,7 @@
 #pragma once
 
 #include <ArmarXCore/core/Component.h>
+#include <ArmarXCore/core/time/Frequency.h>
 
 #include <ArmarXGui/libraries/ArmarXGuiComponentPlugins/LightweightRemoteGuiComponentPlugin.h>
 
@@ -86,6 +87,11 @@ namespace armarx
     private:
         struct Properties
         {
+            struct Object
+            {
+                float maxFrequencyHz = 10;
+            };
+            Object object;
         };
         Properties properties;
 
@@ -93,6 +99,8 @@ namespace armarx
 
 
         armarx::plugins::ObjectPoseClientPlugin* objectClientPlugin = nullptr;
+
+        std::mutex objectMutex;
         std::optional<armem::objects::ObjectInstanceToIndex> object;
 
         armem::client::Reader robotMemoryReader;
diff --git a/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/impl/ObjectInstanceToIndex.cpp b/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/impl/ObjectInstanceToIndex.cpp
index c3a0779a0..4498d01ce 100644
--- a/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/impl/ObjectInstanceToIndex.cpp
+++ b/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/impl/ObjectInstanceToIndex.cpp
@@ -38,22 +38,26 @@ namespace armarx::armem::objects
 
     void
     ObjectInstanceToIndex::fetchAndCommitObjectInstances(
-        const std::vector<armem::MemoryID>& objectPoseSnapshotIDs)
+        const std::vector<armem::MemoryID>& updatedObjectInstanceSnapshotIDs)
     {
         ARMARX_CHECK(objectPoseClient.isConnected());
 
         // Fetch the latest poses.
         const objpose::ObjectPoseSeq objectPoses = objectPoseClient.fetchObjectPoses();
 
-        // ToDo: Filter by snapshotIDs
+        const auto filtered = filterObjectPoses(objectPoses, updatedObjectInstanceSnapshotIDs);
+
 
         // Prepare the commit.
 
         armem::Commit commit;
 
-        for (const objpose::ObjectPose& objectPose : objectPoses)
+        for (const objpose::ObjectPose* objectPosePtr : filtered)
         {
-            armem::MemoryID objectInstanceID = armem::objects::reconstructObjectInstanceID(objectPose);
+            const objpose::ObjectPose& objectPose = *objectPosePtr;
+
+            armem::MemoryID objectInstanceID =
+                armem::objects::reconstructObjectInstanceID(objectPose);
 
             // Spatial
 
@@ -69,7 +73,8 @@ namespace armarx::armem::objects
                 toAron(spatial.aabbGlobal, aabb);
 
                 armem::EntityUpdate& update = commit.add();
-                update.entityID = indexSpatialProviderSegmentID.withEntityName(objectInstanceID.getEntityID().str());
+                update.entityID = indexSpatialProviderSegmentID.withEntityName(
+                    objectInstanceID.getEntityID().str());
                 update.timeCreated = objectPose.timestamp;
                 update.instancesData = {spatial.toAron()};
             }
@@ -77,7 +82,8 @@ namespace armarx::armem::objects
             // Named
 
             // Load object class information.
-            std::optional<ObjectInfo> info = objectPoseClient.getObjectFinder().findObject(objectPose.objectID);
+            std::optional<ObjectInfo> info =
+                objectPoseClient.getObjectFinder().findObject(objectPose.objectID);
             if (info.has_value())
             {
                 std::optional<std::vector<std::string>> recognized, spoken;
@@ -93,7 +99,7 @@ namespace armarx::armem::objects
                 }
                 else
                 {
-                    named.names.recognized = { info->className() };
+                    named.names.recognized = {info->className()};
                 }
 
                 if (spoken.has_value())
@@ -102,11 +108,12 @@ namespace armarx::armem::objects
                 }
                 else
                 {
-                    named.names.spoken = { info->className() };
+                    named.names.spoken = {info->className()};
                 }
 
                 armem::EntityUpdate& update = commit.add();
-                update.entityID = indexNamedProviderSegmentID.withEntityName(objectInstanceID.getEntityID().str());
+                update.entityID = indexNamedProviderSegmentID.withEntityName(
+                    objectInstanceID.getEntityID().str());
                 update.timeCreated = objectPose.timestamp;
                 update.instancesData = {named.toAron()};
             }
@@ -116,4 +123,55 @@ namespace armarx::armem::objects
         indexSpatialMemoryWriter.commit(commit);
     }
 
-} // namespace armarx
+
+    std::vector<const objpose::ObjectPose*>
+    ObjectInstanceToIndex::filterObjectPoses(const objpose::ObjectPoseSeq& objectPoses,
+                                             const std::vector<MemoryID>& updatedSnapshotIDs)
+    {
+        // Returns true to keep the item, false to skip it.
+        auto filter = [this, &updatedSnapshotIDs](const objpose::ObjectPose& objectPose)
+        {
+            auto it = state.latestUpdateDateTimes.find(objectPose.objectID);
+            if (it == state.latestUpdateDateTimes.end())
+            {
+                // Never encountered that before, commit it.
+                return true;
+            }
+
+            const armarx::DateTime& latestTime = it->second;
+            armarx::DateTime nextDueTime = latestTime + params.maxFrequency.toCycleDuration();
+            if (objectPose.timestamp < nextDueTime)
+            {
+                // Skip.
+                return false;
+            }
+
+            armem::MemoryID objectInstanceID =
+                armem::objects::reconstructObjectInstanceID(objectPose);
+            bool found = false;
+            for (const MemoryID& updatedSnapshotID : updatedSnapshotIDs)
+            {
+                if (armem::contains(updatedSnapshotID, objectInstanceID))
+                {
+                    found = true;
+                    break;
+                }
+            }
+            return found;
+        };
+
+
+        std::vector<const objpose::ObjectPose*> filtered;
+        for (const objpose::ObjectPose& objectPose : objectPoses)
+        {
+            if (filter(objectPose))
+            {
+                filtered.push_back(&objectPose);
+                state.latestUpdateDateTimes[objectPose.objectID] = objectPose.timestamp;
+            }
+        }
+
+        return filtered;
+    }
+
+} // namespace armarx::armem::objects
diff --git a/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/impl/ObjectInstanceToIndex.h b/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/impl/ObjectInstanceToIndex.h
index 41ba28b79..dd8f29399 100644
--- a/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/impl/ObjectInstanceToIndex.h
+++ b/source/RobotAPI/components/armem/client/ObjectInstanceToIndex/impl/ObjectInstanceToIndex.h
@@ -24,6 +24,8 @@
 
 #include <vector>
 
+#include <ArmarXCore/core/time/Frequency.h>
+
 #include <RobotAPI/libraries/ArmarXObjects/ObjectPoseClient.h>
 #include <RobotAPI/libraries/armem/client/Writer.h>
 #include <RobotAPI/libraries/armem/core/MemoryID.h>
@@ -35,9 +37,13 @@ namespace armarx::armem::objects
     class ObjectInstanceToIndex
     {
     public:
-        void
-        fetchAndCommitObjectInstances(const std::vector<armem::MemoryID>& objectPoseSnapshotIDs);
+        void fetchAndCommitObjectInstances(
+            const std::vector<armem::MemoryID>& updatedObjectInstanceSnapshotIDs);
 
+    private:
+        std::vector<const objpose::ObjectPose*>
+        filterObjectPoses(const objpose::ObjectPoseSeq& objectPoses,
+                          const std::vector<armem::MemoryID>& updatedObjectInstanceSnapshotIDs);
 
     public:
         objpose::ObjectPoseClient objectPoseClient;
@@ -45,6 +51,19 @@ namespace armarx::armem::objects
 
         armem::MemoryID indexSpatialProviderSegmentID;
         armem::MemoryID indexNamedProviderSegmentID;
+
+
+        struct Parameters
+        {
+            armarx::Frequency maxFrequency = armarx::Frequency::HertzDouble(60);
+        };
+        Parameters params;
+
+        struct State
+        {
+            std::map<armarx::ObjectID, armarx::DateTime> latestUpdateDateTimes;
+        };
+        State state;
     };
 
-} // namespace armarx
+} // namespace armarx::armem::objects
diff --git a/source/RobotAPI/interface/aron/Aron.ice b/source/RobotAPI/interface/aron/Aron.ice
index c766750c2..fb412c46c 100644
--- a/source/RobotAPI/interface/aron/Aron.ice
+++ b/source/RobotAPI/interface/aron/Aron.ice
@@ -170,6 +170,7 @@ module armarx
 
                 // useful for memory ice_conversions
                 sequence<Dict> AronDictSeq;
+                sequence<Dict> DictSeq;
             };
         };
     };
-- 
GitLab