From b4027acfce38fcfb574801d7fa09b5c67ba20c12 Mon Sep 17 00:00:00 2001
From: Fabian Paus <fabian.paus@kit.edu>
Date: Thu, 29 Jul 2021 14:49:31 +0200
Subject: [PATCH] ArViz: Cleanup timer changes from Coin to Qt

---
 .../components/ArViz/Coin/Visualizer.cpp      | 14 +---
 .../components/ArViz/Coin/Visualizer.h        |  1 -
 .../ArViz/ArVizWidgetController.cpp           | 73 ++++++++++++++-----
 .../gui-plugins/ArViz/ArVizWidgetController.h |  3 +
 4 files changed, 59 insertions(+), 32 deletions(-)

diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp
index 5f99047a6..778c2db5f 100644
--- a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp
+++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp
@@ -5,6 +5,7 @@
 
 #include <Inventor/sensors/SoTimerSensor.h>
 #include <Inventor/nodes/SoUnits.h>
+#include <QCoreApplication>
 #include <thread>
 
 
@@ -88,11 +89,6 @@ namespace armarx::viz
                    &CoinVisualizerWrapper::onUpdateSuccess,
                    &CoinVisualizerWrapper::onUpdateFailure);
         root = new SoSeparator;
-
-        //timerSensor = new SoTimerSensor(updateVisualizationCB, this);
-
-        //float cycleTimeMS = 33.0f;
-        //timerSensor->setInterval(SbTime(cycleTimeMS / 1000.0f));
     }
 
     CoinVisualizer::~CoinVisualizer()
@@ -114,9 +110,6 @@ namespace armarx::viz
         }
         state = CoinVisualizerState::STARTING;
         stateStorage = storage;
-
-        //SoSensorManager* sensor_mgr = SoDB::getSensorManager();
-        //sensor_mgr->insertTimerSensor(timerSensor);
     }
 
     void CoinVisualizer::stop()
@@ -126,15 +119,12 @@ namespace armarx::viz
             return;
         }
 
-        SoSensorManager* sensor_mgr = SoDB::getSensorManager();
         state = CoinVisualizerState::STOPPING;
         while (state != CoinVisualizerState::STOPPED)
         {
-            sensor_mgr->processTimerQueue();
+            QCoreApplication::processEvents();
             usleep(1000);
         }
-        sensor_mgr->removeTimerSensor(timerSensor);
-
     }
 
     CoinVisualizer_ApplyTiming CoinVisualizer::apply(data::LayerUpdate const& update)
diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.h b/source/RobotAPI/components/ArViz/Coin/Visualizer.h
index 49ad2a51b..1494be07d 100644
--- a/source/RobotAPI/components/ArViz/Coin/Visualizer.h
+++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.h
@@ -229,7 +229,6 @@ namespace armarx::viz
         std::mutex storageMutex;
         viz::StorageInterfacePrx storage;
 
-        SoTimerSensor* timerSensor = nullptr;
         CoinLayerMap layers;
 
         std::vector<std::type_index> elementVisualizersTypes;
diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
index 4f7372a5e..e210b778f 100644
--- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
+++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
@@ -810,30 +810,34 @@ namespace armarx
 
     void ArVizWidgetController::onReplayTimerTick()
     {
-        if (mode != ArVizWidgetMode::ReplayingTimed)
+        if (mode == ArVizWidgetMode::ReplayingTimed)
         {
-            return;
-        }
-        double replaySpeed = widget.replaySpeedSpinBox->value();
+            double replaySpeed = widget.replaySpeedSpinBox->value();
 
-        replayCurrentTimestamp += 33000 * replaySpeed;
+            long currentRealTime = IceUtil::Time::now().toMicroSeconds();
+            long elapsedRealTime = currentRealTime - lastReplayRealTime;
 
-        long revision = getRevisionForTimestamp(replayCurrentTimestamp);
-        if (revision == -2)
-        {
-            if (widget.replayLoopbackCheckBox->checkState() == Qt::Checked)
+            replayCurrentTimestamp += elapsedRealTime * replaySpeed;
+
+            long revision = getRevisionForTimestamp(replayCurrentTimestamp);
+            if (revision == -2)
             {
-                replayCurrentTimestamp = currentRecording.firstTimestampInMicroSeconds;
+                if (widget.replayLoopbackCheckBox->checkState() == Qt::Checked)
+                {
+                    replayCurrentTimestamp = currentRecording.firstTimestampInMicroSeconds;
+                }
+                else
+                {
+                    revision = currentRecording.lastRevision;
+                }
             }
-            else
+            if (revision >= 0)
             {
-                revision = currentRecording.lastRevision;
+                widget.replayRevisionSlider->setValue(revision);
             }
         }
-        if (revision >= 0)
-        {
-            widget.replayRevisionSlider->setValue(revision);
-        }
+
+        lastReplayRealTime = IceUtil::Time::now().toMicroSeconds();
     }
 
     void ArVizWidgetController::changeMode(ArVizWidgetMode newMode)
@@ -917,10 +921,16 @@ namespace armarx
         auto& entry = recordingBatchCache[batch.header.index];
         entry.data = batch;
         entry.lastUsed = IceUtil::Time::now();
+
+        limitRecordingBatchCacheSize();
+
+        recordingWaitingForBatchIndex = -1;
     }
 
     viz::data::RecordingBatch const& ArVizWidgetController::getRecordingBatch(long index)
     {
+        ARMARX_TRACE;
+
         IceUtil::Time now = IceUtil::Time::now();
 
         std::unique_lock<std::mutex> lock(recordingBatchCacheMutex);
@@ -930,16 +940,23 @@ namespace armarx
         {
             // Start prefetching neighbouring batches
             bool asyncPrefetchIsRunning = callbackResult && !callbackResult->isCompleted();
-            if (!asyncPrefetchIsRunning)
+            if (!asyncPrefetchIsRunning && recordingWaitingForBatchIndex == -1)
             {
                 if (index + 1 < long(currentRecording.batchHeaders.size())
                     && recordingBatchCache.count(index + 1) == 0)
                 {
+                    //                    ARMARX_WARNING << "after begin_getRecordingBatch: " << (index + 1)
+                    //                                   << " waiting for " << recordingWaitingForBatchIndex;
                     callbackResult = storage->begin_getRecordingBatch(currentRecording.id, index + 1, callback);
+                    recordingWaitingForBatchIndex = index + 1;
+                    ARMARX_INFO << "Now waiting for " << recordingWaitingForBatchIndex;
                 }
                 else if (index > 0 && recordingBatchCache.count(index - 1) == 0)
                 {
+                    //                    ARMARX_WARNING << "before begin_getRecordingBatch: " << (index - 1)
+                    //                                   << " waiting for " << recordingWaitingForBatchIndex;
                     callbackResult = storage->begin_getRecordingBatch(currentRecording.id, index - 1, callback);
+                    recordingWaitingForBatchIndex = index - 1;
                 }
 
             }
@@ -949,6 +966,19 @@ namespace armarx
             return entry.data;
         }
 
+        // Maybe there has already been sent a asynchronous request to get
+        if (index == recordingWaitingForBatchIndex)
+        {
+            lock.unlock();
+            ARMARX_INFO << "Waiting to receive async batch: " << index;
+            // Wait until request completes
+            while (recordingWaitingForBatchIndex != -1)
+            {
+                QCoreApplication::processEvents();
+            }
+            return getRecordingBatch(index);
+        }
+
         ARMARX_INFO << "Batch #" << index << " is not in the cache. Getting synchronously, blocking GUI...";
 
         // Entry is not in the cache, we have to get it from ArVizStorage
@@ -956,6 +986,13 @@ namespace armarx
         newEntry.lastUsed = now;
         newEntry.data = storage->getRecordingBatch(currentRecording.id, index);
 
+        limitRecordingBatchCacheSize();
+
+        return newEntry.data;
+    }
+
+    void ArVizWidgetController::limitRecordingBatchCacheSize()
+    {
         if (recordingBatchCache.size() > recordingBatchCacheMaxSize)
         {
             // Remove the entry with the oldest last used timestamp
@@ -972,8 +1009,6 @@ namespace armarx
 
             recordingBatchCache.erase(oldestIter);
         }
-
-        return newEntry.data;
     }
 
     SoNode* ArVizWidgetController::getScene()
diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h
index 31383a63e..4a493dbd1 100644
--- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h
+++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h
@@ -182,6 +182,7 @@ namespace armarx
 
         QTimer* replayTimer;
         long replayCurrentTimestamp = 0;
+        long lastReplayRealTime = 0;
 
         std::string storageName;
         armarx::viz::StorageInterfacePrx storage;
@@ -207,9 +208,11 @@ namespace armarx
         };
 
         viz::data::RecordingBatch const& getRecordingBatch(long index);
+        void limitRecordingBatchCacheSize();
 
         std::size_t recordingBatchCacheMaxSize = 5;
         std::mutex recordingBatchCacheMutex;
+        std::atomic<long> recordingWaitingForBatchIndex = -1;
         std::map<long, TimestampedRecordingBatch> recordingBatchCache;
 
         ArVizWidgetMode mode = ArVizWidgetMode::NotConnected;
-- 
GitLab