From fae88f38b8fbb50fd47d6c7453ffde53d1b0904c Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Thu, 24 Jun 2021 18:39:34 +0200
Subject: [PATCH] Add save and load buttons, implement saving

---
 .../armem_objects/server/instance/Segment.cpp | 184 +++++++++++++++++-
 .../armem_objects/server/instance/Segment.h   |  24 ++-
 .../server/instance/SegmentAdapter.cpp        |   2 +-
 3 files changed, 206 insertions(+), 4 deletions(-)

diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
index 7b7dc7a42..9d2486baa 100644
--- a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
+++ b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp
@@ -1,12 +1,15 @@
 #include "Segment.h"
 
 #include <RobotAPI/libraries/armem_objects/aron_conversions.h>
+#include <RobotAPI/libraries/armem_objects/SceneSnapshot.h>
 
 #include <RobotAPI/libraries/armem/core/aron_conversions.h>
+#include <RobotAPI/libraries/armem/core/error.h>
 #include <RobotAPI/libraries/armem/core/workingmemory/visitor.h>
 #include <RobotAPI/libraries/armem/client/Writer.h>
 #include <RobotAPI/libraries/armem/client/query/Builder.h>
 #include <RobotAPI/libraries/armem/client/query/query_fns.h>
+#include <RobotAPI/libraries/armem/util/util.h>
 
 #include <RobotAPI/libraries/ArmarXObjects/ObjectFinder.h>
 #include <RobotAPI/libraries/ArmarXObjects/aron_conversions.h>
@@ -17,10 +20,16 @@
 #include <RobotAPI/libraries/core/remoterobot/RemoteRobot.h>
 
 #include <ArmarXCore/core/time/TimeUtil.h>
+#include <ArmarXCore/core/system/cmake/CMakePackageFinder.h>
+
+#include <SimoxUtility/math/pose/pose.h>
+
+#include <Eigen/Geometry>
 
 #include <sstream>
 
 
+
 namespace armarx::armem::server::obj::instance
 {
 
@@ -59,6 +68,7 @@ namespace armarx::armem::server::obj::instance
         });
     }
 
+
     void Segment::defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix)
     {
         defs->optional(p.coreSegmentName, prefix + "CoreSegmentName", "Name of the object instance core segment.");
@@ -70,6 +80,7 @@ namespace armarx::armem::server::obj::instance
         decay.defineProperties(defs, prefix + "decay.");
     }
 
+
     void Segment::init()
     {
         ARMARX_CHECK_NOT_NULL(iceMemory.workingMemory);
@@ -161,6 +172,7 @@ namespace armarx::armem::server::obj::instance
         return stats;
     }
 
+
     void Segment::commitObjectPoses(const std::string& providerName, const ObjectPoseSeq& objectPoses)
     {
         ARMARX_CHECK_NOT_NULL(coreSegment);
@@ -203,12 +215,14 @@ namespace armarx::armem::server::obj::instance
         return *coreSegment;
     }
 
+
     const wm::CoreSegment& Segment::getCoreSegment() const
     {
         ARMARX_CHECK_NOT_NULL(coreSegment);
         return *coreSegment;
     }
 
+
     objpose::ObjectPoseSeq Segment::getObjectPoses(IceUtil::Time now)
     {
         ObjectPoseSeq objectPoses = getLatestObjectPoses();
@@ -217,6 +231,7 @@ namespace armarx::armem::server::obj::instance
     }
 
 
+
     objpose::ObjectPoseSeq Segment::getObjectPosesByProvider(
         const std::string& providerName,
         IceUtil::Time now)
@@ -227,6 +242,7 @@ namespace armarx::armem::server::obj::instance
         return filterObjectPoses(objectPoses);
     }
 
+
     armem::wm::Entity* Segment::findObjectEntity(const ObjectID& objectID, const std::string& providerName)
     {
         ARMARX_CHECK_NOT_NULL(coreSegment);
@@ -329,12 +345,14 @@ namespace armarx::armem::server::obj::instance
         objectPose.updateAttached(agent);
     }
 
+
     objpose::ObjectPoseSeq Segment::getLatestObjectPoses() const
     {
         ARMARX_CHECK_NOT_NULL(coreSegment);
         return getLatestObjectPoses(*coreSegment);
     }
 
+
     objpose::ObjectPoseSeq Segment::getLatestObjectPoses(const armem::wm::CoreSegment& coreSeg)
     {
         ObjectPoseSeq result;
@@ -342,6 +360,7 @@ namespace armarx::armem::server::obj::instance
         return result;
     }
 
+
     objpose::ObjectPoseSeq Segment::getLatestObjectPoses(const armem::wm::ProviderSegment& provSeg)
     {
         ObjectPoseSeq result;
@@ -349,6 +368,7 @@ namespace armarx::armem::server::obj::instance
         return result;
     }
 
+
     objpose::ObjectPose Segment::getLatestObjectPose(const armem::wm::Entity& entity)
     {
         ObjectPose result;
@@ -356,6 +376,7 @@ namespace armarx::armem::server::obj::instance
         return result;
     }
 
+
     void Segment::getLatestObjectPoses(const armem::wm::CoreSegment& coreSeg, ObjectPoseSeq& out)
     {
         for (const auto& [_, provSegment] : coreSeg)
@@ -364,6 +385,7 @@ namespace armarx::armem::server::obj::instance
         }
     }
 
+
     void Segment::getLatestObjectPoses(const armem::wm::ProviderSegment& provSegment, ObjectPoseSeq& out)
     {
         for (const auto& [_, entity] : provSegment)
@@ -376,6 +398,7 @@ namespace armarx::armem::server::obj::instance
         }
     }
 
+
     void Segment::getLatestObjectPose(const armem::wm::Entity& entity, ObjectPose& out)
     {
         for (const armem::wm::EntityInstance& instance : entity.getLatestSnapshot())
@@ -402,11 +425,13 @@ namespace armarx::armem::server::obj::instance
         return data;
     }
 
+
     std::optional<simox::OrientedBoxf> Segment::getObjectOOBB(const ObjectID& id)
     {
         return oobbCache.get(id);
     }
 
+
     objpose::ProviderInfo Segment::getProviderInfo(const std::string& providerName)
     {
         try
@@ -427,7 +452,6 @@ namespace armarx::armem::server::obj::instance
     }
 
 
-
     objpose::AttachObjectToRobotNodeOutput
     Segment::attachObjectToRobotNode(const objpose::AttachObjectToRobotNodeInput& input)
     {
@@ -517,6 +541,7 @@ namespace armarx::armem::server::obj::instance
         return output;
     }
 
+
     objpose::DetachObjectFromRobotNodeOutput Segment::detachObjectFromRobotNode(
         const objpose::DetachObjectFromRobotNodeInput& input)
     {
@@ -581,6 +606,7 @@ namespace armarx::armem::server::obj::instance
         virtual bool visitEnter(armem::wm::Entity& entity) override;
     };
 
+
     bool DetachVisitor::visitEnter(armem::wm::Entity& entity)
     {
         const arondto::ObjectInstance data = owner.getLatestInstanceData(entity);
@@ -590,7 +616,6 @@ namespace armarx::armem::server::obj::instance
             // Store non-attached pose in new snapshot.
             owner.storeDetachedSnapshot(entity, data, now, commitAttachedPose);
         }
-
         return false; // Stop descending.
     }
 
@@ -613,6 +638,7 @@ namespace armarx::armem::server::obj::instance
         return output;
     }
 
+
     void Segment::storeDetachedSnapshot(
         armem::wm::Entity& entity,
         const arondto::ObjectInstance& data,
@@ -667,6 +693,124 @@ namespace armarx::armem::server::obj::instance
     }
 
 
+    void Segment::storeScene(const std::string& filename, const armem::obj::SceneSnapshot& scene)
+    {
+        if (const std::optional<std::filesystem::path> path = resolveSceneFilename(filename))
+        {
+            const nlohmann::json j = scene;
+            ARMARX_INFO << "Storing scene snapshot at: \n" << path.value() << "\n\n" << j.dump(2);
+            try
+            {
+                nlohmann::write_json(path.value(), j, 2);
+            }
+            catch (const std::ios_base::failure& e)
+            {
+                ARMARX_WARNING << "Storing scene snapshot failed: \n" << e.what();
+            }
+        }
+    }
+
+
+    std::optional<armem::obj::SceneSnapshot> Segment::loadScene(const std::string& filename)
+    {
+        if (const std::optional<std::filesystem::path> path = resolveSceneFilename(filename))
+        {
+            ARMARX_INFO << "Loading scene snapshot from: \n" << path.value();
+            nlohmann::json j;
+            try
+            {
+                j = nlohmann::read_json(path.value());
+            }
+            catch (const std::ios_base::failure& e)
+            {
+                ARMARX_WARNING << "Loading scene snapshot failed: \n" << e.what();
+                return std::nullopt;
+            }
+
+            armem::obj::SceneSnapshot snapshot = j;
+            return snapshot;
+        }
+        else
+        {
+            return std::nullopt;
+        }
+    }
+
+
+    static const std::string timestampPlaceholder = "%TIMESTAMP";
+    static const std::string targetPackage = "ArmarXObjects";
+
+
+    std::optional<std::filesystem::path> Segment::resolveSceneFilename(const std::string& _filename)
+    {
+        std::string filename = _filename;
+
+        filename = simox::alg::replace_all(filename, timestampPlaceholder,
+                                           Time::now().toString("%Y-%m-%d_%H-%M-%S"));
+        if (not simox::alg::ends_with(filename, ".json"))
+        {
+            filename += ".json";
+        }
+
+        if (!finder)
+        {
+            finder.reset(new CMakePackageFinder(targetPackage));
+            if (not finder->packageFound())
+            {
+                ARMARX_WARNING << "Could not find CMake package " << targetPackage << ".";
+            }
+        }
+        if (finder->packageFound())
+        {
+            std::filesystem::path dataDir = finder->getDataDir();
+            std::filesystem::path path = dataDir / targetPackage / "Scenes" / filename;
+            return path;
+        }
+        else
+        {
+            return std::nullopt;
+        }
+    }
+
+
+    armem::obj::SceneSnapshot Segment::getSceneSnapshot() const
+    {
+        armem::obj::SceneSnapshot scene;
+
+        wm::FunctionalVisitor visitor;
+        visitor.entityFn = [&scene](wm::Entity & entity)
+        {
+            ARMARX_IMPORTANT_S << "Processing entity " << entity.id();
+            std::optional<arondto::ObjectInstance> instance;
+            try
+            {
+                const wm::EntityInstance& entityInstance = entity.getLatestSnapshot().getInstance(0);
+                instance = tryCast<arondto::ObjectInstance>(entityInstance);
+            }
+            catch (const armem::error::ArMemError& e)
+            {
+                ARMARX_WARNING_S << e.what();
+            }
+            if (instance)
+            {
+                armem::obj::SceneSnapshot::Object& object = scene.objects.emplace_back();
+                object.classID = instance->classID.entityName;
+                object.postion = simox::math::position(instance->pose.objectPoseGlobal);
+                object.orientation = simox::math::orientation(instance->pose.objectPoseGlobal);
+            }
+            return false;
+        };
+
+        visitor.applyTo(*coreSegment);
+        return scene;
+    }
+
+
+    void Segment::commitSceneSnapshot(const armem::obj::SceneSnapshot& scene) const
+    {
+        ARMARX_WARNING << "ToDo: Implement";
+    }
+
 
     void Segment::RemoteGui::setup(const Segment& data)
     {
@@ -677,8 +821,20 @@ namespace armarx::armem::server::obj::instance
         infiniteHistory.setValue(data.p.maxHistorySize == -1);
         discardSnapshotsWhileAttached.setValue(data.p.discardSnapshotsWhileAttached);
 
+        storeLoadLine.setValue("Scene_" + timestampPlaceholder);
+        loadButton.setLabel("Load Scene");
+        storeButton.setLabel("Store Scene");
+        HBoxLayout storeLoadLineLayout;
+        storeLoadLineLayout.addChildren({Label(targetPackage + "/Scenes/"), storeLoadLine, Label(".json")});
+
         GridLayout grid;
         int row = 0;
+
+        grid.add(storeLoadLineLayout, {row, 0}, {1, 2});
+        row++;
+        grid.add(loadButton, {row, 0}).add(storeButton, {row, 1});
+        row++;
+
         grid.add(Label("Max History Size"), {row, 0}).add(maxHistorySize, {row, 1});
         row++;
         grid.add(Label("Infinite History Size"), {row, 0}).add(infiniteHistory, {row, 1});
@@ -690,8 +846,32 @@ namespace armarx::armem::server::obj::instance
         group.addChild(grid);
     }
 
+
     void Segment::RemoteGui::update(Segment& data)
     {
+        if (loadButton.wasClicked())
+        {
+            ARMARX_IMPORTANT_S << "Store button clicked";
+            if (const auto snapshot = data.loadScene(storeLoadLine.getValue()))
+            {
+                ARMARX_IMPORTANT_S << "Snapshot loaded";
+                std::scoped_lock lock(data.memoryMutex);
+                ARMARX_IMPORTANT_S << "Committing";
+                data.commitSceneSnapshot(snapshot.value());
+                ARMARX_IMPORTANT_S << "Committed";
+            }
+        }
+
+        if (storeButton.wasClicked())
+        {
+            armem::obj::SceneSnapshot scene;
+            {
+                std::scoped_lock lock(data.memoryMutex);
+                scene = data.getSceneSnapshot();
+            }
+            data.storeScene(storeLoadLine.getValue(), scene);
+        }
+
         if (infiniteHistory.hasValueChanged() || maxHistorySize.hasValueChanged()
             || discardSnapshotsWhileAttached.hasValueChanged())
         {
diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h
index 5b7c268bb..910b816e8 100644
--- a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h
+++ b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h
@@ -1,8 +1,9 @@
 #pragma once
 
+#include <filesystem>
 #include <map>
-#include <string>
 #include <optional>
+#include <string>
 
 #include <SimoxUtility/caching/CacheMap.h>
 #include <SimoxUtility/shapes/OrientedBox.h>
@@ -23,6 +24,11 @@
 #include "Decay.h"
 
 
+namespace armarx::armem::obj
+{
+    struct SceneSnapshot;
+}
+
 namespace armarx::armem::server::obj::instance
 {
 
@@ -135,6 +141,16 @@ namespace armarx::armem::server::obj::instance
         friend struct DetachVisitor;
 
 
+    private:
+
+        void storeScene(const std::string& filename, const armem::obj::SceneSnapshot& scene);
+        std::optional<armem::obj::SceneSnapshot> loadScene(const std::string& filename);
+        std::optional<std::filesystem::path> resolveSceneFilename(const std::string& filename);
+
+        armem::obj::SceneSnapshot getSceneSnapshot() const;
+        void commitSceneSnapshot(const armem::obj::SceneSnapshot& scene) const;
+
+
     public:
 
         RobotStateComponentInterfacePrx robotStateComponent;
@@ -172,6 +188,8 @@ namespace armarx::armem::server::obj::instance
         /// Class name -> dataset name.
         simox::caching::CacheMap<std::string, std::string> classNameToDatasetCache;
 
+        std::unique_ptr<CMakePackageFinder> finder;
+
 
     public:
 
@@ -179,6 +197,10 @@ namespace armarx::armem::server::obj::instance
         {
             armarx::RemoteGui::Client::GroupBox group;
 
+            armarx::RemoteGui::Client::LineEdit storeLoadLine;
+            armarx::RemoteGui::Client::Button storeButton;
+            armarx::RemoteGui::Client::Button loadButton;
+
             armarx::RemoteGui::Client::IntSpinBox maxHistorySize;
             armarx::RemoteGui::Client::CheckBox infiniteHistory;
             armarx::RemoteGui::Client::CheckBox discardSnapshotsWhileAttached;
diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp
index 3773c087b..46a7a4fba 100644
--- a/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp
+++ b/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp
@@ -484,9 +484,9 @@ namespace armarx::armem::server::obj::instance
             std::scoped_lock lock(adapter.visuMutex);
             this->visu.update(adapter.visu);
         }
+        this->segment.update(adapter.segment);
         {
             std::scoped_lock lock(adapter.memoryMutex);
-            this->segment.update(adapter.segment);
             this->decay.update(adapter.segment.decay);
         }
         {
-- 
GitLab