Browse Source

GUI CLI Integration works

Cyamond 4 years ago
parent
commit
5564b39c67

+ 4 - 1
gui/CMakeLists.txt

@@ -11,9 +11,12 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
 find_package(Threads)
 find_package(Boost 1.67 REQUIRED COMPONENTS system)
 find_package(Qt5 COMPONENTS Core Quick REQUIRED)
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(JSONCPP REQUIRED jsoncpp)
 
 add_executable(${PROJECT_NAME} src/main.cpp src/qmlhandler.cpp src/qml.qrc)
 
-target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} Qt5::Core Qt5::Quick)
+include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDEDIR} include)
+target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${JSONCPP_LIBRARIES} ${Boost_LIBRARIES} Qt5::Core Qt5::Quick)
 
 

+ 0 - 0
gui/src/.gitkeep


+ 30 - 7
gui/src/FooterForm.ui.qml

@@ -6,12 +6,18 @@ Page {
     width: 1280
     height: 200
 
+    font.capitalization: Font.MixedCase
+
     Connections {
         target: _qmlHandler
         onLog: {
             footerLog.append(logText)
             footerFlickable.contentY = footerLog.height - footerFlickable.height
         }
+
+        onFooterSetStatus: {
+            footerStatusText.text = "Status: " + status
+        }
     }
 
     Rectangle {
@@ -21,14 +27,31 @@ Page {
         ColumnLayout {
             anchors.fill: parent
 
-            Text {
-                id: footerStatusText
-                height: 30
-                color: "#ffffff"
-                text: qsTr("Status: Connected to foobar")
-                font.pixelSize: 23
+            RowLayout {
                 Layout.preferredHeight: 30
                 Layout.preferredWidth: parent.width
+
+                Text {
+                    id: footerStatusText
+                    Layout.preferredHeight: parent.height
+                    Layout.preferredWidth: 1100
+                    color: "#ffffff"
+                    text: qsTr("")
+                    font.pixelSize: 23
+                }
+
+                Button {
+                    id: footerGetStatusButton
+                    Layout.preferredHeight: 30
+                    Layout.preferredWidth: 180
+                    text: qsTr("Get Status")
+                    // @disable-check M223
+                    onClicked: {
+                        footerStatusText.text = ""
+                        // @disable-check M222
+                        _qmlHandler.onFooterGetStatusButton()
+                    }
+                }
             }
 
             Flickable {
@@ -41,7 +64,7 @@ Page {
                     selectByMouse: true
                     id: footerLog
                     wrapMode: TextArea.Wrap
-                    text: qsTr("Log goes here\nNew Line works as well")
+                    text: qsTr("")
                     font.pointSize: 15
                 }
 

+ 23 - 4
gui/src/IpPopupForm.ui.qml

@@ -29,18 +29,23 @@ Popup {
             Layout.alignment: Qt.AlignCenter
             id: popupIpInput
             selectByMouse: true
+            focus: true
             text: qsTr("")
             placeholderText: "Enter IP"
             horizontalAlignment: Text.AlignHCenter
             validator: RegExpValidator {
                 regExp: /^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$/
             }
+            // @disable-check M222
+            Keys.onReturnPressed: popupConnectButton.activate()
+            // @disable-check M222
+            Keys.onEnterPressed: popupConnectButton.activate()
         }
 
         Text {
             id: popupStatusText
             color: "#df3f3f"
-            text: qsTr("Text field for status")
+            text: qsTr("")
             horizontalAlignment: Text.AlignHCenter
             verticalAlignment: Text.AlignVCenter
             Layout.alignment: Qt.AlignCenter
@@ -56,10 +61,24 @@ Popup {
             // @disable-check M223
             onClicked: {
                 // @disable-check M222
-                _qmlHandler.onIpPopupEnterIp(popupIpInput.text)
-                // @disable-check M222
-                popup.close()
+                popupConnectButton.activate()
+            }
+
+            // @disable-check M222
+            function activate() {
+                // @disable-check M223
+                if (popupIpInput.acceptableInput) {
+                    // @disable-check M222
+                    _qmlHandler.onIpPopupEnterIp(popupIpInput.text)
+                    // @disable-check M222
+                    popup.close()
+                }
             }
         }
     }
 }
+
+/*##^## Designer {
+    D{i:0;autoSize:true;height:480;width:640}
+}
+ ##^##*/

+ 12 - 2
gui/src/MessagesForm.ui.qml

@@ -29,7 +29,6 @@ Page {
                 id: messagesLog
                 selectByMouse: true
                 wrapMode: TextArea.Wrap
-                text: qsTr("Messages go here\nNew Line works as well")
                 font.pointSize: 15
             }
 
@@ -49,7 +48,9 @@ Page {
                 placeholderText: "Enter message..."
                 text: qsTr("")
                 font.pixelSize: 20
+                // @disable-check M222
                 Keys.onReturnPressed: messagesSendButton.activate()
+                // @disable-check M222
                 Keys.onEnterPressed: messagesSendButton.activate()
             }
 
@@ -66,7 +67,16 @@ Page {
                 }
 
                 enabled: messagesInputField.text != ""
-                function activate() {if(messagesInputField.text != "") {_qmlHandler.onMessagesSendButton(messagesInputField.text); messagesInputField.text = ""}}
+                // @disable-check M222
+                function activate() {
+                    // @disable-check M223
+                    if (messagesInputField.text != "") {
+                        // @disable-check M222
+                        _qmlHandler.onMessagesSendButton(
+                                    messagesInputField.text)
+                        messagesInputField.text = ""
+                    }
+                }
             }
         }
     }

+ 1 - 1
gui/src/SendingForm.ui.qml

@@ -61,7 +61,7 @@ Page {
         // @disable-check M223
         onAccepted: {
             // @disable-check M222
-            _qmlHandler.onSendingSelectFileButton(fileDialog.fileUrl)
+            _qmlHandler.onSendingSelectFileButton(sendingFileDialog.fileUrl)
         }
     }
 

+ 18 - 4
gui/src/SwitchIpPopupForm.ui.qml

@@ -35,12 +35,17 @@ Popup {
             validator: RegExpValidator {
                 regExp: /^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$/
             }
+
+            // @disable-check M222
+            Keys.onReturnPressed: switchConnectButton.activate()
+            // @disable-check M222
+            Keys.onEnterPressed: switchConnectButton.activate()
         }
 
         Text {
             id: switchStatusText
             color: "#df3f3f"
-            text: qsTr("Text field for status")
+            text: qsTr("")
             horizontalAlignment: Text.AlignHCenter
             verticalAlignment: Text.AlignVCenter
             Layout.alignment: Qt.AlignCenter
@@ -61,9 +66,18 @@ Popup {
                 // @disable-check M223
                 onClicked: {
                     // @disable-check M222
-                    _qmlHandler.onSwitchPopupEnterIp(popupIpInput.text)
-                    // @disable-check M222
-                    popup.close()
+                    switchConnectButton.activate()
+                }
+
+                // @disable-check M222
+                function activate() {
+                    // @disable-check M223
+                    if (switchIpInput.acceptableInput) {
+                        // @disable-check M222
+                        _qmlHandler.onSwitchPopupEnterIp(popupIpInput.text)
+                        // @disable-check M222
+                        popup.close()
+                    }
                 }
             }
 

+ 3 - 30
gui/src/main.cpp

@@ -10,45 +10,16 @@
 #include <iostream>
 #include <sys/prctl.h>
 #include <sys/wait.h>
+#include <thread>
 #include <unistd.h>
 
 #include "qmlhandler.h"
 
 using namespace std;
-int inpipefd[2];
-int outpipefd[2];
-char buf[1024];
 
 int main(int argc, char *argv[]) {
   qInfo() << "Program Start";
 
-  pid_t pid = 0;
-  char msg[256];
-  int status;
-
-  pipe(inpipefd);
-  pipe(outpipefd);
-  pid = fork();
-  if (pid == 0) {
-    // Child
-    dup2(outpipefd[0], STDIN_FILENO);
-    dup2(inpipefd[1], STDOUT_FILENO);
-    dup2(inpipefd[1], STDERR_FILENO);
-
-    // ask kernel to deliver SIGTERM in case the parent dies
-    prctl(PR_SET_PDEATHSIG, SIGTERM);
-
-    // Set the path to the CLI - pass argument h (help) for now
-    // TODO: Change hardcoded path
-    execl("../../cli/build/ccats-cli", "ccats-cli", "127.0.0.1", "--machine",
-          (char *)NULL);
-
-    exit(1);
-  }
-
-  close(outpipefd[0]);
-  close(inpipefd[1]);
-
   // ########## Load GUI ##########
 
   QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
@@ -68,5 +39,7 @@ int main(int argc, char *argv[]) {
 
   QObject *object = component.create();
 
+  app.setQuitOnLastWindowClosed(false);
+
   return app.exec();
 }

+ 4 - 0
gui/src/main.qml

@@ -87,4 +87,8 @@ ApplicationWindow {
       swipeView.interactive = false
       ipDialog.open()
     }
+
+    onClosing: {
+      _qmlHandler.onClosing();
+    }
 }

+ 119 - 6
gui/src/qmlhandler.cpp

@@ -1,17 +1,97 @@
+#include <csignal>
+#include <cstdio>
+#include <cstdlib>
+#include <iostream>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <thread>
+#include <unistd.h>
+
 #include "qmlhandler.h"
+#include <boost/asio.hpp>
+#include <iostream>
+#include <json/json.h>
+
+using boost::asio::buffer;
+
+using namespace std;
+
+int inpipefd[2];
+int outpipefd[2];
+char buf[1024];
+
+void QMLHandler::handleJSON(string buffer) {
+  Json::Value root;
+  Json::Reader reader;
+
+  bool parsingSuccessful = reader.parse(buffer, root);
+  qInfo() << parsingSuccessful;
+  if (!parsingSuccessful) {
+    return;
+  }
+  const Json::Value command = root["command"];
+  string cmd = command.asString();
+
+  if (cmd == "status") {
+    emit footerSetStatus(root["response"].asString().c_str());
+  }
+
+  else if (cmd == "close") {
+    exit(0);
+  }
+}
+
+void QMLHandler::readPipeLoop() {
+  unsigned int readOffset = 0;
+  unsigned int pollCount = 0;
+  struct pollfd inPipeStatus;
+  inPipeStatus.fd = inpipefd[0];
+  inPipeStatus.events = POLLIN;
+
+  while (true) {
+    poll(&inPipeStatus, 1, 100);
+
+    if (inPipeStatus.revents & POLLIN) {
+      readOffset += read(inpipefd[0], buf + readOffset, 1024);
+
+      pollCount = 0;
+
+      buf[1023] = 0;
+      buf[strlen(buf)] = 0;
+    } else {
+      pollCount++;
+    }
+
+    if (pollCount > 9 && buf[0]) {
+      string cleanBuffer = buf + strcspn(buf, "\n") + 1;
+      string receivedData = cleanBuffer.substr(0, cleanBuffer.size() - 1);
+
+      emit log(QString::fromStdString(receivedData));
+      qInfo() << QString::fromStdString(receivedData);
+      handleJSON(receivedData);
+      memset(buf, 0, 1024);
+      pollCount = 0;
+      readOffset = 0;
+    }
+  }
+}
 
 QMLHandler::QMLHandler(QObject *parent) : QObject(parent) {}
 
+// Main
+void QMLHandler::onClosing() {
+  qInfo() << "Closing";
+  write(outpipefd[1], "disconnect\n", strlen("disconnect\n"));
+}
+
 // Sending
 void QMLHandler::onSendingSelectFileButton(QUrl url) {
-  qInfo() << "File Selected: " << url.toString();
+  emit log("File Selected: " + url.toString());
   emit sendingSetFileUrlText("Selected File: " + url.toString());
 }
 
-void QMLHandler::onSendingSendFileButton() {
-  qInfo() << "Sending File";
-  emit log("Sending File");
-}
+void QMLHandler::onSendingSendFileButton() { emit log("Sending File !"); }
 
 // Receiving
 
@@ -25,10 +105,43 @@ void QMLHandler::onSettingsSwitchServerButton() {
 
 // Ip Popup
 void QMLHandler::onIpPopupEnterIp(QString ip) {
-  qInfo() << "Connecting to " << ip;
+  pid_t pid = 0;
+
+  pipe(inpipefd);
+  pipe(outpipefd);
+  pid = fork();
+  if (pid == 0) {
+    // Child
+    dup2(outpipefd[0], STDIN_FILENO);
+    dup2(inpipefd[1], STDOUT_FILENO);
+    dup2(inpipefd[1], STDERR_FILENO);
+
+    // ask kernel to deliver SIGTERM in case the parent dies
+    prctl(PR_SET_PDEATHSIG, SIGTERM);
+
+    // Set the path to the CLI - pass argument h (help) for now
+    // TODO: Change hardcoded path
+    execl("../../cli/build/ccats-cli", "ccats-cli", ip.toUtf8().constData(),
+          "--machine", (char *)NULL);
+
+    exit(1);
+  }
+
+  // TODO: Not hardcoded
+  emit footerSetStatus("Connected to " + ip);
+
+  close(outpipefd[0]);
+  close(inpipefd[1]);
+
+  std::thread(&QMLHandler::readPipeLoop, this).detach();
 }
 
 // Switch Popup
 void QMLHandler::onSwitchPopupEnterIp(QString ip) {
   qInfo() << "Switching to " << ip;
 }
+
+// Footer
+void QMLHandler::onFooterGetStatusButton() {
+  write(outpipefd[1], "status\n", strlen("status\n"));
+}

+ 13 - 0
gui/src/qmlhandler.h

@@ -5,12 +5,18 @@
 #include <QDebug>
 #include <QObject>
 #include <QUrl>
+#include <string>
 
 class QMLHandler : public QObject {
   Q_OBJECT
 
+private:
+  void handleJSON(std::string buffer);
+
 public:
   explicit QMLHandler(QObject *parent = 0);
+  void readPipeLoop();
+  void serverStatusLoop();
 
 signals:
   // Sending
@@ -30,8 +36,12 @@ signals:
 
   // Footer
   void log(QString logText);
+  void footerSetStatus(QString status);
 
 public slots:
+  // Main
+  void onClosing();
+
   // Sending
   void onSendingSelectFileButton(QUrl url);
   void onSendingSendFileButton();
@@ -49,6 +59,9 @@ public slots:
 
   // Switch Popup
   void onSwitchPopupEnterIp(QString ip);
+
+  // Footer
+  void onFooterGetStatusButton();
 };
 
 #endif // QMLHANDLER_H