From e45b87e5d7ca4bc2cc5b371ca2d15c2c3b4a54dc Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Wed, 29 Nov 2023 15:07:54 +0100
Subject: [PATCH] Implement MemoryPlotter core functionality

---
 .../client/MemoryPlotter/MemoryPlotter.cpp    | 190 +++++++++++++++++-
 .../client/MemoryPlotter/MemoryPlotter.h      |  18 +-
 2 files changed, 204 insertions(+), 4 deletions(-)

diff --git a/source/RobotAPI/components/armem/client/MemoryPlotter/MemoryPlotter.cpp b/source/RobotAPI/components/armem/client/MemoryPlotter/MemoryPlotter.cpp
index e442247df..d292688d7 100644
--- a/source/RobotAPI/components/armem/client/MemoryPlotter/MemoryPlotter.cpp
+++ b/source/RobotAPI/components/armem/client/MemoryPlotter/MemoryPlotter.cpp
@@ -25,9 +25,15 @@
 #include <ArmarXCore/core/time/Frequency.h>
 #include <ArmarXCore/core/time/Metronome.h>
 
+#include <RobotAPI/libraries/armem/core/error/mns.h>
+#include <RobotAPI/libraries/aron/core/data/variant/Variant.h>
+#include <RobotAPI/libraries/aron/core/data/variant/primitive/All.h>
+#include <RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.h>
+
 namespace armarx
 {
 
+
     armarx::PropertyDefinitionsPtr
     MemoryPlotter::createPropertyDefinitions()
     {
@@ -38,6 +44,10 @@ namespace armarx
         return defs;
     }
 
+    MemoryPlotter::MemoryPlotter()
+    {
+    }
+
     std::string
     MemoryPlotter::getDefaultName() const
     {
@@ -47,6 +57,30 @@ namespace armarx
     void
     MemoryPlotter::onInitComponent()
     {
+        DebugObserverComponentPluginUser::setDebugObserverBatchModeEnabled(true);
+
+        armem::MemoryID robotEntityId{"RobotState", "Proprioception", "Armar7", "Armar7"};
+
+        std::vector<std::string> jointNames{
+            "Neck_1_Yaw",
+            "Neck_2_Hemisphere_A",
+            "Neck_3_Hemisphere_B",
+        };
+
+        for (const std::string& jointName : jointNames)
+        {
+            std::vector<std::string> attributes{
+                "position",
+                "velocity",
+            };
+            for (const std::string& attribute : attributes)
+            {
+                properties.plottedValues.push_back(MemoryValueID{
+                    .entityID = robotEntityId,
+                    .aronPath = {{"joints", attribute, jointName}},
+                });
+            }
+        }
     }
 
     void
@@ -55,7 +89,7 @@ namespace armarx
         createRemoteGuiTab();
         RemoteGui_startRunningTask();
 
-        task = new RunningTask<MemoryPlotter>(this, &MemoryPlotter::run);
+        task = new RunningTask<MemoryPlotter>(this, &MemoryPlotter::runLoop);
         task->start();
     }
 
@@ -71,16 +105,168 @@ namespace armarx
     }
 
     void
-    MemoryPlotter::run()
+    MemoryPlotter::runLoop()
     {
         Frequency frequency = Frequency::Hertz(30);
         Metronome metronome(frequency);
         while (not task->isStopped())
         {
+            loopOnce();
             metronome.waitForNextTick();
         }
     }
 
+    class Visitor : public aron::data::ConstVariantVisitor
+    {
+    public:
+        Visitor(armarx::plugins::DebugObserverComponentPlugin& debugObserver) :
+            debugObserver{debugObserver}
+        {
+        }
+
+        // void visitAronVariant(const data::DictPtr&) override;
+        // void visitAronVariant(const data::ListPtr&) override;
+        // void visitAronVariant(const data::NDArrayPtr&) override;
+        void
+        visitAronVariant(const aron::data::IntPtr& v) override
+        {
+            setDatafield(v->getValue());
+        }
+
+        void
+        visitAronVariant(const aron::data::LongPtr& v) override
+        {
+            setDatafield(v->getValue());
+        }
+
+        void
+        visitAronVariant(const aron::data::FloatPtr& v) override
+        {
+            setDatafield(v->getValue());
+        }
+
+        void
+        visitAronVariant(const aron::data::DoublePtr& v) override
+        {
+            setDatafield(v->getValue());
+        }
+
+        void
+        visitAronVariant(const aron::data::BoolPtr& v) override
+        {
+            setDatafield(v->getValue());
+        }
+
+        void
+        visitAronVariant(const aron::data::StringPtr& v) override
+        {
+            setDatafield(v->getValue());
+        }
+
+        template <class ValueT>
+        void
+        setDatafield(const ValueT& value)
+        {
+            debugObserver.setDebugObserverDatafield(channelName, datafieldName, value);
+            ++count;
+        }
+
+        armarx::plugins::DebugObserverComponentPlugin& debugObserver;
+        std::string channelName;
+        std::string datafieldName;
+
+        int count = 0;
+    };
+
+    void
+    MemoryPlotter::loopOnce()
+    {
+        Visitor visitor(getDebugObserverComponentPlugin());
+
+        std::stringstream log;
+        for (const MemoryValueID& valueId : properties.plottedValues)
+        {
+            armem::client::Reader* reader = getReader(valueId.entityID, log);
+            if (reader == nullptr)
+            {
+                // Log is written in getReader.
+                continue;
+            }
+
+            std::optional<armem::wm::EntitySnapshot> snapshot =
+                reader->getLatestSnapshotIn(valueId.entityID);
+
+            if (not snapshot.has_value())
+            {
+                log << "\nDid not find snapshot in entity " << valueId.entityID << ".";
+                continue;
+            }
+
+            aron::data::DictPtr data = snapshot->getInstance(0).data();
+            aron::data::VariantPtr valueVariant = data->navigateAbsolute(valueId.aronPath);
+            if (not valueVariant)
+            {
+                log << "\nDid not find " << valueId.aronPath.toString() << " in entity snapshot "
+                    << snapshot->id() << ".";
+                continue;
+            }
+
+
+            auto makeDatafieldName = [](const aron::Path& path) -> std::string
+            {
+                // The first element is always "ARON->", which we can discard for readability.
+                std::string str = path.toString();
+                str =
+                    simox::alg::remove_prefix(str, path.getRootIdentifier() + path.getDelimeter());
+                str = simox::alg::replace_all(str, path.getDelimeter(), ">");
+                return str;
+            };
+
+            visitor.channelName = simox::alg::replace_all(valueId.entityID.str(), "/", ">");
+            visitor.datafieldName = makeDatafieldName(valueId.aronPath);
+
+            aron::data::visit(visitor, valueVariant);
+        }
+
+        ARMARX_INFO << "Sending debug observer batch with " << visitor.count << " values ...";
+        sendDebugObserverBatch();
+
+        if (not log.str().empty())
+        {
+            ARMARX_INFO << "Encountered issues while sending memory values to the debug observer "
+                           "for plotting: "
+                        << log.str();
+        }
+    }
+
+    armem::client::Reader*
+    MemoryPlotter::getReader(const armem::MemoryID& memoryID, std::stringstream& log)
+    {
+        armem::MemoryID key = memoryID.getProviderSegmentID();
+
+        auto it = memoryReaders.find(key);
+        if (it != memoryReaders.end())
+        {
+            return &it->second;
+        }
+        else
+        {
+            armem::client::Reader reader;
+            try
+            {
+                reader = memoryNameSystem().getReader(key);
+            }
+            catch (armem::error::CouldNotResolveMemoryServer& e)
+            {
+                log << "\n" << e.what();
+                return nullptr;
+            }
+
+            auto [it, _] = memoryReaders.emplace(key, reader);
+            return &it->second;
+        }
+    }
+
     void
     MemoryPlotter::createRemoteGuiTab()
     {
diff --git a/source/RobotAPI/components/armem/client/MemoryPlotter/MemoryPlotter.h b/source/RobotAPI/components/armem/client/MemoryPlotter/MemoryPlotter.h
index 5e3067985..e60c3dae4 100644
--- a/source/RobotAPI/components/armem/client/MemoryPlotter/MemoryPlotter.h
+++ b/source/RobotAPI/components/armem/client/MemoryPlotter/MemoryPlotter.h
@@ -22,6 +22,7 @@
 
 #pragma once
 
+
 #include <ArmarXCore/core/Component.h>
 #include <ArmarXCore/interface/observers/ObserverInterface.h>
 #include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h>
@@ -37,6 +38,13 @@
 namespace armarx
 {
 
+
+    struct MemoryValueID
+    {
+        armem::MemoryID entityID;
+        aron::Path aronPath;
+    };
+
     /**
      * @defgroup Component-MemoryPlotter MemoryPlotter
      * @ingroup RobotAPI-Components
@@ -55,6 +63,8 @@ namespace armarx
         virtual public armarx::LightweightRemoteGuiComponentPluginUser
     {
     public:
+        MemoryPlotter();
+
         /// @see armarx::ManagedIceObject::getDefaultName()
         std::string getDefaultName() const override;
 
@@ -72,15 +82,19 @@ namespace armarx
         void onDisconnectComponent() override;
         void onExitComponent() override;
 
-        void run();
+        void runLoop();
+        void loopOnce();
+
+        armem::client::Reader* getReader(const armem::MemoryID& memoryID, std::stringstream& log);
 
 
     private:
         struct Properties
         {
+            std::vector<MemoryValueID> plottedValues;
         };
 
-        Properties p;
+        Properties properties;
 
         std::map<armem::MemoryID, armem::client::Reader> memoryReaders;
 
-- 
GitLab