From 4fb8b6c906f7e073ea11317d506f69bef251ec80 Mon Sep 17 00:00:00 2001
From: Peter Albrecht <albrecpe@gmail.com>
Date: Thu, 21 Dec 2023 21:43:32 +0100
Subject: [PATCH] feature: connection status label

---
 .../SkillManagerMonitorWidget.ui              |  9 ++++-
 .../SkillManagerMonitorWidgetController.cpp   |  2 ++
 .../libraries/skills_gui/CMakeLists.txt       |  2 ++
 .../skills_gui/ConnectionStatusLabel.cpp      | 31 +++++++++++++++++
 .../skills_gui/ConnectionStatusLabel.h        | 33 +++++++++++++++++++
 .../libraries/skills_gui/SkillMemoryGui.cpp   | 16 ++++++++-
 .../libraries/skills_gui/SkillMemoryGui.h     |  7 ++++
 .../skills_gui/memory/SkillManagerWrapper.cpp | 16 +++++++--
 .../skills_gui/memory/SkillManagerWrapper.h   |  7 ++--
 9 files changed, 117 insertions(+), 6 deletions(-)
 create mode 100644 source/RobotAPI/libraries/skills_gui/ConnectionStatusLabel.cpp
 create mode 100644 source/RobotAPI/libraries/skills_gui/ConnectionStatusLabel.h

diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui
index 73bb1a8f7..65c28683d 100644
--- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui
+++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui
@@ -19,7 +19,7 @@
   <property name="windowTitle">
    <string>SkillManagerMonitorWidget</string>
   </property>
-  <layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0">
+  <layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0,0">
    <item row="3" column="0" colspan="2">
     <widget class="QSplitter" name="splitter_2">
      <property name="enabled">
@@ -161,6 +161,13 @@
      </item>
     </layout>
    </item>
+   <item row="4" column="0" colspan="2">
+    <widget class="QLabel" name="connectionStatusLabel">
+     <property name="text">
+      <string>(hidden in GUI)</string>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <resources/>
diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp
index 909f514ba..245ea0803 100644
--- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp
+++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp
@@ -115,6 +115,8 @@ namespace armarx
 
             widget.stopAllLayout,
 
+            widget.connectionStatusLabel,
+
             this->mem_wrapper);
 
         connectSignals();
diff --git a/source/RobotAPI/libraries/skills_gui/CMakeLists.txt b/source/RobotAPI/libraries/skills_gui/CMakeLists.txt
index 646d99495..da9c18993 100644
--- a/source/RobotAPI/libraries/skills_gui/CMakeLists.txt
+++ b/source/RobotAPI/libraries/skills_gui/CMakeLists.txt
@@ -64,6 +64,7 @@ set(SOURCES
     PeriodicUpdateWidget.cpp
     SkillMemoryGui.cpp
     gui_utils.cpp
+    ConnectionStatusLabel.cpp
 )
 set(HEADERS
     aron_tree_widget/visitors/AronTreeWidgetCreator.h
@@ -106,6 +107,7 @@ set(HEADERS
     PeriodicUpdateWidget.h
     SkillMemoryGui.h
     gui_utils.h
+    ConnectionStatusLabel.h
 )
 
 armarx_gui_library("${LIB_NAME}" "${SOURCES}" "${GUI_MOC_HDRS}" "${GUI_UIS}" "" "${LIBRARIES}")
diff --git a/source/RobotAPI/libraries/skills_gui/ConnectionStatusLabel.cpp b/source/RobotAPI/libraries/skills_gui/ConnectionStatusLabel.cpp
new file mode 100644
index 000000000..7cdf488c5
--- /dev/null
+++ b/source/RobotAPI/libraries/skills_gui/ConnectionStatusLabel.cpp
@@ -0,0 +1,31 @@
+#include "ConnectionStatusLabel.h"
+
+namespace armarx::skills::gui
+{
+    ConnectionStatusLabel::ConnectionStatusLabel(int ms)
+    {
+        timer = new QTimer();
+        setStyleSheet("color: red");
+        ms_delay = ms;
+    }
+
+    void
+    ConnectionStatusLabel::handleMessage(std::string const& message)
+    {
+        // reset the timer if it is running
+        if (timer->isActive())
+        {
+            timer->stop();
+        }
+
+        setText(QString::fromStdString(message));
+
+        timer->singleShot(2000, this, &ConnectionStatusLabel::resetLabel);
+    }
+
+    void
+    ConnectionStatusLabel::resetLabel()
+    {
+        setText(QString::fromStdString(""));
+    }
+} // namespace armarx::skills::gui
diff --git a/source/RobotAPI/libraries/skills_gui/ConnectionStatusLabel.h b/source/RobotAPI/libraries/skills_gui/ConnectionStatusLabel.h
new file mode 100644
index 000000000..36881fad9
--- /dev/null
+++ b/source/RobotAPI/libraries/skills_gui/ConnectionStatusLabel.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <QLabel>
+#include <QTimer>
+
+namespace armarx::skills::gui
+{
+    class ConnectionStatusLabel : public QLabel
+    {
+    public:
+        /**
+         * @brief Constructor for ConnectionStatusLabel
+         * @param ms The time in [ms], for which the message appears.
+         */
+        ConnectionStatusLabel(int ms = 3000);
+
+    public slots:
+        /**
+         * @brief Display a message to indicate a connection drop.
+         */
+        void handleMessage(std::string const& message);
+
+    private slots:
+        /**
+         * @brief Reset the label to default state.
+         */
+        void resetLabel();
+
+    private:
+        QTimer* timer = nullptr;
+        int ms_delay;
+    };
+} // namespace armarx::skills::gui
diff --git a/source/RobotAPI/libraries/skills_gui/SkillMemoryGui.cpp b/source/RobotAPI/libraries/skills_gui/SkillMemoryGui.cpp
index 4a9138a67..257699aaa 100644
--- a/source/RobotAPI/libraries/skills_gui/SkillMemoryGui.cpp
+++ b/source/RobotAPI/libraries/skills_gui/SkillMemoryGui.cpp
@@ -14,6 +14,8 @@ namespace armarx::skills::gui
 
                                    QLayout* stopAllLayout,
 
+                                   QLabel* _connectionStatusLabel,
+
                                    std::shared_ptr<SkillManagerWrapper> _memory)
     {
         Logging::setTag("SkillMemoryGui");
@@ -28,6 +30,7 @@ namespace armarx::skills::gui
         ARMARX_CHECK(_skillGroupBox);
         ARMARX_CHECK(_skillDetailGroupBox);
         ARMARX_CHECK(_updateWidgetLayout);
+        ARMARX_CHECK(_connectionStatusLabel);
         ARMARX_CHECK(_memory);
 
         // setup memory
@@ -57,15 +60,20 @@ namespace armarx::skills::gui
 
         // setup stop all button
         stopAllButton = new QPushButton(QString::fromStdString(STOP_ALL_BUTTON_TEXT));
-        stopAllButton->setStyleSheet("background-color: red");
         stopAllLayout->addWidget(stopAllButton);
 
+        this->connectionStatusLabel = new ConnectionStatusLabel();
+        armarx::gui::replaceWidget(_connectionStatusLabel,
+                                   this->connectionStatusLabel,
+                                   _connectionStatusLabel->parentWidget()->layout());
+
         setupUi();
     }
 
     void
     SkillMemoryGUI::setupUi()
     {
+        stopAllButton->setStyleSheet("background-color: red");
         connectSignals();
     }
 
@@ -115,5 +123,11 @@ namespace armarx::skills::gui
                 &QPushButton::clicked,
                 memory.get(),
                 &SkillManagerWrapper::stopAllExecutions);
+
+        // status label
+        connect(memory.get(),
+                &SkillManagerWrapper::connectionUpdate,
+                connectionStatusLabel,
+                &ConnectionStatusLabel::handleMessage);
     }
 } // namespace armarx::skills::gui
diff --git a/source/RobotAPI/libraries/skills_gui/SkillMemoryGui.h b/source/RobotAPI/libraries/skills_gui/SkillMemoryGui.h
index 019cc3b31..30ac10f78 100644
--- a/source/RobotAPI/libraries/skills_gui/SkillMemoryGui.h
+++ b/source/RobotAPI/libraries/skills_gui/SkillMemoryGui.h
@@ -2,12 +2,15 @@
 #define SKILLMEMORYGUI_H
 
 #include <QHBoxLayout>
+#include <QLabel>
 #include <QSplitter>
 #include <QTreeWidget>
 #include <QWidget>
 
 #include <ArmarXCore/core/logging/Logging.h>
 
+#include "RobotAPI/libraries/skills_gui/ConnectionStatusLabel.h"
+
 #include "./PeriodicUpdateWidget.h"
 #include "./aron_tree_widget/widgets/SkillDescriptionWidget.h"
 #include "./executions/SkillExecutionTreeWidget.h"
@@ -33,6 +36,8 @@ namespace armarx::skills::gui
 
                        QLayout* stopAllLayout,
 
+                       QLabel* connectionStatusLabel,
+
                        std::shared_ptr<SkillManagerWrapper> _memory);
 
     signals:
@@ -66,6 +71,8 @@ namespace armarx::skills::gui
         PeriodicUpdateWidget* updateWidget = nullptr;
 
         QPushButton* stopAllButton = nullptr;
+
+        ConnectionStatusLabel* connectionStatusLabel = nullptr;
     };
 } // namespace armarx::skills::gui
 
diff --git a/source/RobotAPI/libraries/skills_gui/memory/SkillManagerWrapper.cpp b/source/RobotAPI/libraries/skills_gui/memory/SkillManagerWrapper.cpp
index 328595374..9f6828966 100644
--- a/source/RobotAPI/libraries/skills_gui/memory/SkillManagerWrapper.cpp
+++ b/source/RobotAPI/libraries/skills_gui/memory/SkillManagerWrapper.cpp
@@ -11,7 +11,7 @@ namespace armarx::skills::gui
         std::map<skills::ProviderID, std::map<skills::SkillID, skills::SkillDescription>>;
 
     const StatusMap
-    SkillManagerWrapper::fetchExecutions() const
+    SkillManagerWrapper::fetchExecutions()
     {
         if (!memory)
         {
@@ -44,6 +44,9 @@ namespace armarx::skills::gui
             ARMARX_WARNING
                 << "Unhandled Ice exception encountered while updating executions. Exception was: "
                 << e;
+            std::stringstream sstream;
+            sstream << "Could not fetch executions: " << e.what();
+            emit connectionUpdate(sstream.str());
             return {};
         }
         catch (...)
@@ -119,6 +122,9 @@ namespace armarx::skills::gui
             ARMARX_WARNING
                 << "Unhandled Ice exception encountered while updating skills. Exception was: "
                 << e;
+            std::stringstream sstream;
+            sstream << "Could not fetch skills: " << e.what();
+            emit connectionUpdate(sstream.str());
             return {};
         }
         catch (...)
@@ -193,7 +199,7 @@ namespace armarx::skills::gui
 
     void
     SkillManagerWrapper::stopExecution(skills::SkillExecutionID const& executionId,
-                                       const unsigned int max_retries) const
+                                       const unsigned int max_retries)
     {
         // memory???
         if (!memory)
@@ -217,6 +223,10 @@ namespace armarx::skills::gui
                 retry = true;
                 ARMARX_ERROR << "Unhandeled Ice exception while aborting skill '"
                              << executionId.skillId.skillName << "'.";
+                std::stringstream sstream;
+                sstream << "Could not abort skill " << executionId.skillId.skillName << " : "
+                        << e.what();
+                emit connectionUpdate(sstream.str());
             }
             catch (...)
             {
@@ -295,6 +305,8 @@ namespace armarx::skills::gui
         {
             ARMARX_ERROR << "Unhandeled Ice exception while executing skill '" << skillId.skillName
                          << "'. Aborting...";
+            emit connectionUpdate("Could not begin execution of skill " + skillId.skillName + ": " +
+                                  e.what());
         }
         catch (...)
         {
diff --git a/source/RobotAPI/libraries/skills_gui/memory/SkillManagerWrapper.h b/source/RobotAPI/libraries/skills_gui/memory/SkillManagerWrapper.h
index dfa915459..a4f1407e0 100644
--- a/source/RobotAPI/libraries/skills_gui/memory/SkillManagerWrapper.h
+++ b/source/RobotAPI/libraries/skills_gui/memory/SkillManagerWrapper.h
@@ -50,7 +50,7 @@ namespace armarx::skills::gui
          * @param max_retries The amount of times to retry, if aborting fails.
          */
         void stopExecution(skills::SkillExecutionID const& executionId,
-                           const unsigned int max_retries = 0) const;
+                           const unsigned int max_retries = 0);
 
         /**
          * @brief Fetches and returns the latest skills update from memory.
@@ -62,11 +62,14 @@ namespace armarx::skills::gui
          * @brief Fetches and returns the latest status update from memory.
          * @return The map containing status updates for all execution ids. Empty, if error occurred.
          */
-        const StatusMap fetchExecutions() const;
+        const StatusMap fetchExecutions();
 
         static const std::optional<skills::ProviderID> findFirstProvider(SkillMap const& map,
                                                                          SkillID const& skillId);
 
+    signals:
+        void connectionUpdate(std::string const& message);
+
     public slots:
         /**
          * @brief Disconnects the interface from memory.
-- 
GitLab