From 203cfd44727805764b54916b9df8795990c19cb6 Mon Sep 17 00:00:00 2001
From: Alberto Nidasio <alberto.nidasio@skywarder.eu>
Date: Fri, 23 Sep 2022 18:32:38 +0200
Subject: [PATCH] [CompactCommandPad] Implemented resend function

---
 .vscode/c_cpp_properties.json                 |  2 +-
 CMakeLists.txt                                |  1 +
 .../Components/ModulesPicker/modulespicker.ui |  4 +-
 .../SubscriptionsPanel/subscriptionspanel.cpp |  2 +-
 src/shared/Core/Message/topic.cpp             | 14 ++--
 src/shared/Core/Message/topic.h               | 13 ++--
 src/shared/Core/Message/topicfilter.h         | 11 +--
 src/shared/Core/QCustomPlot/QCustomPlot.cpp   |  4 +-
 .../CompactCommandPad/CommandSelector.cpp     | 61 ++++++++++++++-
 .../CompactCommandPad/CommandSelector.h       | 14 +++-
 .../CompactCommandPad/CompactCommandPad.cpp   | 77 +++++++++++++++----
 .../CompactCommandPad/CompactCommandPad.h     |  6 ++
 .../Modules/CompactCommandPad/SendThread.cpp  | 20 +++++
 .../Modules/CompactCommandPad/SendThread.h    | 22 ++++++
 src/shared/Modules/Mavlink/mavlinkmodule.cpp  |  4 +-
 15 files changed, 200 insertions(+), 55 deletions(-)
 create mode 100644 src/shared/Modules/CompactCommandPad/SendThread.cpp
 create mode 100644 src/shared/Modules/CompactCommandPad/SendThread.h

diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
index 52eb5831..7c8dbf1e 100644
--- a/.vscode/c_cpp_properties.json
+++ b/.vscode/c_cpp_properties.json
@@ -3,7 +3,7 @@
         {
             "name": "Linux",
             "includePath": [
-                "${workspaceFolder}/src/",
+                "${workspaceFolder}/src/shared",
                 "~/Qt/5.15.2/**"
             ],
             "defines": [],
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 47030a51..cb0d44cc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -61,6 +61,7 @@ add_executable(groundstation
     src/shared/Modules/CommandPad/MessageFormElement.cpp
     src/shared/Modules/CompactCommandPad/CompactCommandPad.cpp
     src/shared/Modules/CompactCommandPad/CommandSelector.cpp
+    src/shared/Modules/CompactCommandPad/SendThread.cpp
     src/shared/Modules/DefaultModule/defaultmodule.cpp
     src/shared/Modules/Empty/emptymodule.cpp
     src/shared/Modules/FileStream/filestreammodule.cpp
diff --git a/src/shared/Components/ModulesPicker/modulespicker.ui b/src/shared/Components/ModulesPicker/modulespicker.ui
index c1faf6a1..90067767 100644
--- a/src/shared/Components/ModulesPicker/modulespicker.ui
+++ b/src/shared/Components/ModulesPicker/modulespicker.ui
@@ -104,8 +104,8 @@
                             </property>
                             <property name="icon">
                               <iconset resource="../../application.qrc">
-                                <normaloff>:/Resources/Icons/splitter_vertical.png</normaloff>
-                                :/Resources/Icons/splitter_vertical.png
+                                <normaloff>:/assets/icons/splitter_horizontal.png</normaloff>
+                                :/assets/icons/splitter_vertical.png
                               </iconset>
                             </property>
                             <property name="iconSize">
diff --git a/src/shared/Components/SubscriptionsPanel/subscriptionspanel.cpp b/src/shared/Components/SubscriptionsPanel/subscriptionspanel.cpp
index 2b5225dc..4db9d323 100644
--- a/src/shared/Components/SubscriptionsPanel/subscriptionspanel.cpp
+++ b/src/shared/Components/SubscriptionsPanel/subscriptionspanel.cpp
@@ -3,7 +3,6 @@
 #include <QDebug>
 #include <functional>
 
-#include "Components/TopicAndFieldFilterSelector/topicandfieldfilterselector.h"
 #include "Components/TopicFilterSelector/topicfilterselector.h"
 #include "ui_subscriptionspanel.h"
 
@@ -60,6 +59,7 @@ void SubscriptionsPanel::onAddTopicClicked()
 
 void SubscriptionsPanel::onAddTopicAndFieldClicked()
 {
+    // TODO: Fix
     // TopicFilterSelector::selectFilter([&](const TopicAndFieldFilter& filter)
     //                                   { addTopicAndFieldFilter(filter); });
 }
diff --git a/src/shared/Core/Message/topic.cpp b/src/shared/Core/Message/topic.cpp
index 2c08e1a0..820ff406 100644
--- a/src/shared/Core/Message/topic.cpp
+++ b/src/shared/Core/Message/topic.cpp
@@ -2,13 +2,13 @@
 
 #include <QRegExp>
 
-Topic::Topic() : repr(""), valid(true) {}
+Topic::Topic() : topic(""), valid(true) {}
 
 Topic::Topic(QString str)
 {
     if (str.contains(QRegExp("^[A-Za-z0-9$_]+(\\/[A-Za-z0-9$_]+)*$")))
     {
-        repr  = str;
+        topic = str;
         valid = true;
     }
     else
@@ -19,16 +19,16 @@ Topic::Topic(QString str)
 
 Topic Topic::getParent() const
 {
-    int idx = repr.lastIndexOf('/');
-    return idx == -1 ? Topic(repr) : Topic(repr.left(idx));
+    int idx = topic.lastIndexOf('/');
+    return idx == -1 ? Topic(topic) : Topic(topic.left(idx));
 }
 
 Topic Topic::getRoot() const
 {
-    int idx = repr.indexOf('/');
-    return idx == -1 ? Topic(repr) : Topic(repr.left(idx));
+    int idx = topic.indexOf('/');
+    return idx == -1 ? Topic(topic) : Topic(topic.left(idx));
 }
 
 bool Topic::isValid() const { return valid; }
 
-QString Topic::toString() const { return repr; }
+QString Topic::toString() const { return topic; }
diff --git a/src/shared/Core/Message/topic.h b/src/shared/Core/Message/topic.h
index 5e763c8d..5c1e72d3 100644
--- a/src/shared/Core/Message/topic.h
+++ b/src/shared/Core/Message/topic.h
@@ -1,5 +1,4 @@
-#ifndef TOPIC_H
-#define TOPIC_H
+#pragma once
 
 #include <QString>
 
@@ -17,10 +16,10 @@ public:
     Topic();
     explicit Topic(QString str);
 
-    Topic(const Topic&) = default;
-    Topic(Topic&&)      = default;
+    Topic(const Topic&)            = default;
+    Topic(Topic&&)                 = default;
     Topic& operator=(const Topic&) = default;
-    Topic& operator=(Topic&&) = default;
+    Topic& operator=(Topic&&)      = default;
 
     // Exact match
     bool operator==(const Topic& topic) const;
@@ -32,8 +31,6 @@ public:
     QString toString() const;
 
 private:
-    QString repr;
+    QString topic;
     bool valid;
 };
-
-#endif  // TOPIC_H
diff --git a/src/shared/Core/Message/topicfilter.h b/src/shared/Core/Message/topicfilter.h
index af1d353f..32c67c9f 100644
--- a/src/shared/Core/Message/topicfilter.h
+++ b/src/shared/Core/Message/topicfilter.h
@@ -1,5 +1,4 @@
-#ifndef TOPICFILTER_H
-#define TOPICFILTER_H
+#pragma once
 
 #include <QRegularExpression>
 #include <QString>
@@ -22,10 +21,10 @@ public:
     TopicFilter(const QString& expression = "*");
     bool operator==(const TopicFilter&) const;
 
-    TopicFilter(const TopicFilter&) = default;
-    TopicFilter(TopicFilter&&)      = default;
+    TopicFilter(const TopicFilter&)            = default;
+    TopicFilter(TopicFilter&&)                 = default;
     TopicFilter& operator=(const TopicFilter&) = default;
-    TopicFilter& operator=(TopicFilter&&) = default;
+    TopicFilter& operator=(TopicFilter&&)      = default;
 
     /**
      * @brief Returns true if the filter matches the given topic.
@@ -47,5 +46,3 @@ private:
     QString expression;
     bool valid;
 };
-
-#endif  // TOPICFILTER_H
diff --git a/src/shared/Core/QCustomPlot/QCustomPlot.cpp b/src/shared/Core/QCustomPlot/QCustomPlot.cpp
index fc161301..90676021 100644
--- a/src/shared/Core/QCustomPlot/QCustomPlot.cpp
+++ b/src/shared/Core/QCustomPlot/QCustomPlot.cpp
@@ -5795,8 +5795,8 @@ void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos,
         {
             QCPVector2D widthVecPerp = widthVec.perpendicular();
             QPointF points[4]        = {
-                (pos - widthVecPerp).toPointF(), (pos - widthVec).toPointF(),
-                (pos + widthVecPerp).toPointF(), (pos + widthVec).toPointF()};
+                       (pos - widthVecPerp).toPointF(), (pos - widthVec).toPointF(),
+                       (pos + widthVecPerp).toPointF(), (pos + widthVec).toPointF()};
             painter->setPen(miterPen);
             painter->setBrush(brush);
             painter->drawConvexPolygon(points, 4);
diff --git a/src/shared/Modules/CompactCommandPad/CommandSelector.cpp b/src/shared/Modules/CompactCommandPad/CommandSelector.cpp
index 06efd3c8..644574f3 100644
--- a/src/shared/Modules/CompactCommandPad/CommandSelector.cpp
+++ b/src/shared/Modules/CompactCommandPad/CommandSelector.cpp
@@ -4,7 +4,11 @@
 
 #include <QDebug>
 
-CommandSelector::CommandSelector() : QDialog() { setupUi(); }
+CommandSelector::CommandSelector(DefaultModule* _parent)
+    : QDialog(), parent(_parent)
+{
+    setupUi();
+}
 
 CommandSelector::~CommandSelector()
 {
@@ -14,6 +18,11 @@ CommandSelector::~CommandSelector()
 
 XmlObject CommandSelector::toXmlObject(XmlObject& obj)
 {
+    bool ok;
+    long timeout = continuosTimeoutEdit->text().toUInt(&ok);
+    if (ok)
+        obj.addAttribute("timeout", QString() + timeout);
+
     auto key = messagesListComboBox->currentText();
     if (formElements.contains(key))
     {
@@ -27,6 +36,14 @@ XmlObject CommandSelector::toXmlObject(XmlObject& obj)
 
 void CommandSelector::fromXmlObject(const XmlObject& obj)
 {
+    QString str = obj.getAttribute("timeout", "0");
+    long timeout;
+    bool ok;
+    timeout = str.toUInt(&ok);
+
+    if (ok)
+        continuosTimeoutEdit->setText(QString() + timeout);
+
     if (obj.hasAttribute("message_id"))
     {
         auto key = obj.getAttribute("message_id");
@@ -44,10 +61,14 @@ void CommandSelector::fromXmlObject(const XmlObject& obj)
     }
 }
 
-bool CommandSelector::getSelection(QString& label, ModuleMessage& message)
+bool CommandSelector::getSelection(QString& label, ModuleMessage& message,
+                                   long& continuosSendTimeout)
 {
-    message = selectedMessage;
-    label   = selectedLabel;
+    message              = selectedMessage;
+    label                = selectedLabel;
+    continuosSendTimeout = continuosCheck->isChecked()
+                               ? continuosTimeoutEdit->text().toUInt(nullptr)
+                               : 0;
     return selected;
 }
 
@@ -60,6 +81,19 @@ void CommandSelector::setupUi()
     lineEdit->setPlaceholderText("Write the button label");
     outerLayout->addWidget(lineEdit);
 
+    continuosCheck = new QCheckBox;
+    continuosCheck->setText("Continuos Send");
+    outerLayout->addWidget(continuosCheck);
+    QObject::connect(
+        continuosCheck, &QCheckBox::stateChanged,
+        [this]()
+        { continuosTimeoutEdit->setHidden(!continuosCheck->isChecked()); });
+
+    continuosTimeoutEdit = new QLineEdit();
+    continuosTimeoutEdit->setPlaceholderText("Choose resend timeout in ms");
+    continuosTimeoutEdit->setHidden(true);
+    outerLayout->addWidget(continuosTimeoutEdit);
+
     messagesListComboBox = new QComboBox;
     messagesListComboBox->setSizePolicy(
         QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
@@ -97,6 +131,25 @@ void CommandSelector::setupUi()
     connect(sendButton, &QPushButton::clicked,
             [=]()
             {
+                if (continuosCheck->isChecked())
+                {
+                    bool ok;
+                    continuosTimeoutEdit->text().toUInt(&ok);
+                    if (!ok)
+                    {
+                        parent->error("Invalid Input",
+                                      "Please enter a valid unsigned integer.");
+                        return;
+                    }
+                }
+
+                if (lineEdit->text().isEmpty())
+                {
+                    parent->error("Invalid Input",
+                                  "Please enter a name for the button.");
+                    return;
+                }
+
                 auto key = messagesListComboBox->currentText();
                 if (formElements.contains(key))
                 {
diff --git a/src/shared/Modules/CompactCommandPad/CommandSelector.h b/src/shared/Modules/CompactCommandPad/CommandSelector.h
index 3fa838b5..1215f0f7 100644
--- a/src/shared/Modules/CompactCommandPad/CommandSelector.h
+++ b/src/shared/Modules/CompactCommandPad/CommandSelector.h
@@ -2,8 +2,10 @@
 
 #include <Core/xmlobject.h>
 #include <Modules/CommandPad/MessageFormElement.h>
+#include <Modules/DefaultModule/defaultmodule.h>
 
 #include <QBoxLayout>
+#include <QCheckBox>
 #include <QComboBox>
 #include <QDialog>
 #include <QGridLayout>
@@ -17,26 +19,30 @@ class CommandSelector : public QDialog
     Q_OBJECT
 
 public:
-    explicit CommandSelector();
+    explicit CommandSelector(DefaultModule* _parent);
     ~CommandSelector();
 
     XmlObject toXmlObject(XmlObject& obj);
     void fromXmlObject(const XmlObject& xmlObject);
 
-    bool getSelection(QString& label, ModuleMessage& message);
+    bool getSelection(QString& label, ModuleMessage& message,
+                      long& continuosSendTimeout);
 
 private:
     void setupUi();
 
     QString currentMessage;
     QVBoxLayout* outerLayout;
-    QLineEdit* lineEdit;
+    QLineEdit *lineEdit, *continuosTimeoutEdit;
     QComboBox* messagesListComboBox;
     QMap<QString, MessageFormElement*> formElements;
     QGridLayout* formGridLayout;
     QPushButton* sendButton;
+    QCheckBox* continuosCheck;
 
     bool selected = false;
     QString selectedLabel;
     ModuleMessage selectedMessage;
-};
\ No newline at end of file
+
+    DefaultModule* parent;
+};
diff --git a/src/shared/Modules/CompactCommandPad/CompactCommandPad.cpp b/src/shared/Modules/CompactCommandPad/CompactCommandPad.cpp
index 1b50605b..5094fbad 100644
--- a/src/shared/Modules/CompactCommandPad/CompactCommandPad.cpp
+++ b/src/shared/Modules/CompactCommandPad/CompactCommandPad.cpp
@@ -1,6 +1,7 @@
 #include "CompactCommandPad.h"
 
-CompactCommandPad::CompactCommandPad(QWidget* parent) : DefaultModule(parent)
+CompactCommandPad::CompactCommandPad(QWidget* parent)
+    : DefaultModule(parent), timer(nullptr), continuosSendTimeout(false)
 {
     setupUi();
     defaultContextMenuSetup();
@@ -13,9 +14,7 @@ QWidget* CompactCommandPad::toWidget() { return this; }
 XmlObject CompactCommandPad::toXmlObject()
 {
     XmlObject obj = XmlObject("compact_command_pad");
-
     selector->toXmlObject(obj);
-
     return obj;
 }
 
@@ -25,21 +24,38 @@ void CompactCommandPad::fromXmlObject(const XmlObject& obj)
         selector->fromXmlObject(obj);
 
     QString label;
-    selected = selector->getSelection(label, selectedMessage);
+    selected =
+        selector->getSelection(label, selectedMessage, continuosSendTimeout);
 
     if (selected)
+    {
+        if (continuosSendTimeout != 0)
+            button->setText(label + " [Start]");
+
         button->setText(label);
+    }
 }
 
 void CompactCommandPad::buttonEditActionTriggered()
 {
     selector->exec();
+    selected =
+        selector->getSelection(label, selectedMessage, continuosSendTimeout);
 
-    QString label;
-    selected = selector->getSelection(label, selectedMessage);
+    if (timer)
+    {
+        timer->stop();
+        timer->deleteLater();
+        timer = nullptr;
+    }
 
     if (selected)
-        button->setText(label);
+    {
+        if (continuosSendTimeout != 0)
+            button->setText(label + " [Start]");
+        else
+            button->setText(label);
+    }
 }
 
 void CompactCommandPad::setupUi()
@@ -49,17 +65,46 @@ void CompactCommandPad::setupUi()
 
     button = new QPushButton("Edit me");
     outerLayout->addWidget(button);
-    connect(
-        button, &QPushButton::clicked,
-        [=]()
-        {
-            if (selected)
-                getCore()->getModuleMessagesBroker()->publish(selectedMessage);
-        });
+    connect(button, &QPushButton::clicked,
+            [=]()
+            {
+                if (timer)
+                {
+                    timer->stop();
+                    timer->deleteLater();
+                    timer = nullptr;
+                    button->setText(label + " [Start]");
+                }
+                else
+                {
+                    if (selected)
+                    {
+                        if (continuosSendTimeout == 0)
+                        {
+                            send();
+                        }
+                        else
+                        {
+                            button->setText(label + " [Stop]");
+
+                            send();
+                            timer = new QTimer(this);
+                            connect(timer, &QTimer::timeout, this,
+                                    &CompactCommandPad::send);
+                            timer->start(continuosSendTimeout);
+                        }
+                    }
+                }
+            });
 
     setLayout(outerLayout);
 
-    selector = new CommandSelector;
+    selector = new CommandSelector(this);
+}
+
+void CompactCommandPad::send()
+{
+    getCore()->getModuleMessagesBroker()->publish(selectedMessage);
 }
 
 void CompactCommandPad::addCustomActionsToMenu()
@@ -68,4 +113,4 @@ void CompactCommandPad::addCustomActionsToMenu()
     addActionToMenu(edit);
     connect(edit, &QAction::triggered, this,
             &CompactCommandPad::buttonEditActionTriggered);
-}
\ No newline at end of file
+}
diff --git a/src/shared/Modules/CompactCommandPad/CompactCommandPad.h b/src/shared/Modules/CompactCommandPad/CompactCommandPad.h
index 69993d2f..95da8691 100644
--- a/src/shared/Modules/CompactCommandPad/CompactCommandPad.h
+++ b/src/shared/Modules/CompactCommandPad/CompactCommandPad.h
@@ -3,6 +3,8 @@
 #include <Modules/CompactCommandPad/CommandSelector.h>
 #include <Modules/DefaultModule/defaultmodule.h>
 
+#include <QTimer>
+
 class CompactCommandPad : public DefaultModule
 {
     Q_OBJECT
@@ -18,6 +20,7 @@ public:
 
 private slots:
     void buttonEditActionTriggered();
+    void send();
 
 private:
     void setupUi();
@@ -25,7 +28,10 @@ private:
 
     CommandSelector* selector;
     QPushButton* button;
+    QTimer* timer;
 
+    QString label;
     bool selected;
+    long continuosSendTimeout;
     ModuleMessage selectedMessage;
 };
diff --git a/src/shared/Modules/CompactCommandPad/SendThread.cpp b/src/shared/Modules/CompactCommandPad/SendThread.cpp
new file mode 100644
index 00000000..89bab6c5
--- /dev/null
+++ b/src/shared/Modules/CompactCommandPad/SendThread.cpp
@@ -0,0 +1,20 @@
+#include "SendThread.h"
+
+SendThread::SendThread(ModuleMessagesBroker* broker, const ModuleMessage& msg,
+                       long timeout)
+    : broker(broker), msg(msg), timeout(timeout), toStop(false)
+{
+    connect(this, &SendThread::finished, this, &SendThread::deleteLater);
+    start();
+}
+
+void SendThread::requestStop() { toStop = true; }
+
+void SendThread::run()
+{
+    while (!toStop)
+    {
+        broker->publish(msg);
+        QThread::sleep(timeout);
+    }
+}
diff --git a/src/shared/Modules/CompactCommandPad/SendThread.h b/src/shared/Modules/CompactCommandPad/SendThread.h
new file mode 100644
index 00000000..4252e2cb
--- /dev/null
+++ b/src/shared/Modules/CompactCommandPad/SendThread.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <QThread>
+
+#include "Core/modulemessagesbroker.h"
+
+class SendThread : public QThread
+{
+public:
+    SendThread(ModuleMessagesBroker* broker, const ModuleMessage& msg,
+               long timeout);
+    void requestStop();
+
+protected:
+    void run() override;
+
+private:
+    ModuleMessagesBroker* broker;
+    ModuleMessage msg;
+    long timeout;
+    bool toStop;
+};
diff --git a/src/shared/Modules/Mavlink/mavlinkmodule.cpp b/src/shared/Modules/Mavlink/mavlinkmodule.cpp
index 49a624ad..7d7bd59c 100644
--- a/src/shared/Modules/Mavlink/mavlinkmodule.cpp
+++ b/src/shared/Modules/Mavlink/mavlinkmodule.cpp
@@ -143,7 +143,7 @@ void MavlinkModule::onCommandReceived(const ModuleMessage &msg)
         mavlinkCommandAdapter.send(encoded_mvl_msg);
 
         ModuleMessage confirmationMsg(
-            Topic(SkywardHubStrings::logCommandsTopic));
+            Topic((QString)SkywardHubStrings::logCommandsTopic));
 
         auto nameField = MessageField(msg.getTopic().toString().replace(
             SkywardHubStrings::commandsTopic + "/", ""));
@@ -219,8 +219,6 @@ void MavlinkModule::initializeSerialPortView()
     for (auto serialPortInfo : serialPortInfos)
         portsComboBox->addItem(serialPortInfo.portName());
 
-    // portsComboBox->setCurrentIndex(0);
-
     baudrateComboBox->addItem("115200", QSerialPort::Baud115200);
     baudrateComboBox->addItem("19200", QSerialPort::Baud19200);
     baudrateComboBox->addItem("9600", QSerialPort::Baud9600);
-- 
GitLab