From 7e640165dad58d273496588866542ff59f0e17f5 Mon Sep 17 00:00:00 2001
From: Hoop77 <p.badenhoop@gmx.de>
Date: Mon, 14 May 2018 14:46:54 +0200
Subject: [PATCH] Message gui

---
 .../MessageLib/include/MessageLib/Client.h    |  57 ++++-
 .../catkin_ws/src/MessageLib/src/Client.cpp   |  72 +++++--
 .../src/MessageLib/test/ServerClientTest.cpp  |  30 +--
 modules/catkin_ws/src/PCGui/.gitignore        |   3 +-
 .../catkin_ws/src/PCGui/PCGui/CMakeLists.txt  |  49 +++--
 .../PCGui/PCGui/{main.qml => CommandGui.qml}  |   2 +-
 .../catkin_ws/src/PCGui/PCGui/MessageGui.qml  | 142 +++++++++++++
 .../src/PCGui/PCGui/include/Logger.h          |  69 ------
 .../src/PCGui/PCGui/include/MessageList.h     |  32 +++
 .../src/PCGui/PCGui/include/MessageModel.h    |  38 ++++
 .../src/PCGui/PCGui/include/MessageProducer.h |  23 ++
 .../include/QtQuickControlsApplication.h      |  24 +++
 .../PCGui/include/SortFilterProxyModel.h      | 121 +++++++++++
 modules/catkin_ws/src/PCGui/PCGui/qml.qrc     |   3 +-
 .../PCGui/src/{main.cpp => CommandGui.cpp}    |   2 +-
 .../catkin_ws/src/PCGui/PCGui/src/Logger.cpp  |  48 -----
 .../src/PCGui/PCGui/src/MessageGui.cpp        |  54 +++++
 .../src/PCGui/PCGui/src/MessageList.cpp       |  21 ++
 .../src/PCGui/PCGui/src/MessageModel.cpp      |  78 +++++++
 .../PCGui/PCGui/src/SortFilterProxyModel.cpp  | 199 ++++++++++++++++++
 .../{noUss.launch => noHardware.launch}       |   1 -
 .../catkin_ws/src/car/src/mavLink/MavLink.cpp |   6 +-
 22 files changed, 886 insertions(+), 188 deletions(-)
 rename modules/catkin_ws/src/PCGui/PCGui/{main.qml => CommandGui.qml} (77%)
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/MessageGui.qml
 delete mode 100644 modules/catkin_ws/src/PCGui/PCGui/include/Logger.h
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/include/MessageList.h
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/include/MessageModel.h
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/include/MessageProducer.h
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/include/QtQuickControlsApplication.h
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/include/SortFilterProxyModel.h
 rename modules/catkin_ws/src/PCGui/PCGui/src/{main.cpp => CommandGui.cpp} (80%)
 delete mode 100644 modules/catkin_ws/src/PCGui/PCGui/src/Logger.cpp
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/src/MessageGui.cpp
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/src/MessageList.cpp
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/src/MessageModel.cpp
 create mode 100644 modules/catkin_ws/src/PCGui/PCGui/src/SortFilterProxyModel.cpp
 rename modules/catkin_ws/src/car/launch/{noUss.launch => noHardware.launch} (88%)

diff --git a/modules/catkin_ws/src/MessageLib/include/MessageLib/Client.h b/modules/catkin_ws/src/MessageLib/include/MessageLib/Client.h
index 18365c3a..201d8055 100644
--- a/modules/catkin_ws/src/MessageLib/include/MessageLib/Client.h
+++ b/modules/catkin_ws/src/MessageLib/include/MessageLib/Client.h
@@ -30,18 +30,22 @@ public:
     using OnMessageProcessedCallback = std::function<void(const Message & message)>;
     using OnRequestFinishedCallback = std::function<void(const networking::error::ErrorCode & error)>;
 
-    static constexpr std::size_t MAX_MESSAGE_SIZE{1024};
+    static Ptr create(networking::Networking & net, std::uint16_t port, std::size_t maxBufferSize=1024)
+    { return std::make_shared<Client>(PrivateTag{}, net, port, maxBufferSize); }
 
-    static Ptr create(networking::Networking & net, std::uint16_t port)
-    { return std::make_shared<Client>(PrivateTag{}, net, port); }
-
-    Client(PrivateTag, networking::Networking & net, std::uint16_t port);
+    Client(PrivateTag, networking::Networking & net, std::uint16_t port, std::size_t maxBufferSize);
 
     void requestMessages(const std::string & host,
                          const networking::time::Duration & timeout,
-                         const Client::OnMessageProcessedCallback & onMessageProcessedCallback,
-                         const Client::OnRequestFinishedCallback & onRequestFinishedCallback =
-                         [](const auto & error) {});
+                         const OnMessageProcessedCallback & onMessageProcessedCallback,
+                         const OnRequestFinishedCallback & onRequestFinishedCallback = [](const auto & error) {});
+
+    void requestMessagesPeriodically(const std::string & host,
+                                     const networking::time::Duration & requestInterval,
+                                     const networking::time::Duration & retryInterval,
+                                     const OnMessageProcessedCallback & onMessageProcessedCallback,
+                                     const OnRequestFinishedCallback & onRequestFinishedCallback =
+                                     [](const auto & error) {});
 
     void stop();
 
@@ -65,10 +69,47 @@ private:
         OnRequestFinishedCallback onRequestFinishedCallback;
     };
 
+    struct PeriodicAsyncState
+    {
+        using Ptr = std::shared_ptr<PeriodicAsyncState>;
+
+        PeriodicAsyncState(Client::Ptr self,
+                           const std::string & host,
+                           const networking::time::Duration & requestInterval,
+                           const networking::time::Duration & retryInterval,
+                           const OnMessageProcessedCallback & onMessageProcessedCallback,
+                           const OnRequestFinishedCallback & onRequestFinishedCallback)
+            : lock(*self)
+              , self(self)
+              , host(host)
+              , requestInterval(requestInterval)
+              , retryInterval(retryInterval)
+              , onMessageProcessedCallback(onMessageProcessedCallback)
+              , onRequestFinishedCallback(onRequestFinishedCallback)
+        {}
+
+        networking::BusyLock lock;
+        Client::Ptr self;
+        std::string host;
+        networking::time::Duration requestInterval;
+        networking::time::Duration retryInterval;
+        OnMessageProcessedCallback onMessageProcessedCallback;
+        OnRequestFinishedCallback onRequestFinishedCallback;
+    };
+
     networking::Networking & net;
     std::uint16_t port;
     ServiceClient::Ptr client;
     Parser parser;
+    networking::time::Timer::Ptr timer;
+    std::atomic<bool> requesting{false};
+
+    void processResponse(const std::string & response,
+                         const OnMessageProcessedCallback & onMessageProcessedCallback);
+
+    void scheduleNextRequest(PeriodicAsyncState::Ptr state, const networking::time::Duration & waitTime);
+
+    void nextRequest(PeriodicAsyncState::Ptr state);
 };
 
 }
diff --git a/modules/catkin_ws/src/MessageLib/src/Client.cpp b/modules/catkin_ws/src/MessageLib/src/Client.cpp
index 2f6e4711..a00ef36c 100644
--- a/modules/catkin_ws/src/MessageLib/src/Client.cpp
+++ b/modules/catkin_ws/src/MessageLib/src/Client.cpp
@@ -8,10 +8,11 @@
 namespace message
 {
 
-Client::Client(Client::PrivateTag, networking::Networking & net, std::uint16_t port)
+Client::Client(Client::PrivateTag, networking::Networking & net, std::uint16_t port, std::size_t maxBufferSize)
     : net(net), port(port)
 {
-    client = ServiceClient::create(net, MAX_MESSAGE_SIZE);
+    client = ServiceClient::create(net, maxBufferSize);
+    timer = networking::time::Timer::create(net);
 }
 
 void Client::requestMessages(const std::string & host,
@@ -25,30 +26,75 @@ void Client::requestMessages(const std::string & host,
     std::string emptyRequest;
     client->asyncCall(
         emptyRequest, host, port, timeout,
-        [state](const auto & error, auto & responseMessage)
+        [state](const auto & error, const auto & responseMessage)
         {
             if (error)
             {
                 state->onRequestFinishedCallback(error);
                 return;
             }
-
-            if (!responseMessage.empty())
-            {
-                auto & parser = state->self->parser;
-                parser << responseMessage;
-                Message message;
-                while (parser >> message)
-                    state->onMessageProcessedCallback(message);
-            }
-
+            state->self->processResponse(responseMessage, state->onMessageProcessedCallback);
             state->onRequestFinishedCallback(error);
         });
 }
 
+void Client::requestMessagesPeriodically(const std::string & host,
+                                         const networking::time::Duration & requestInterval,
+                                         const networking::time::Duration & retryInterval,
+                                         const OnMessageProcessedCallback & onMessageProcessedCallback,
+                                         const OnRequestFinishedCallback & onRequestFinishedCallback)
+{
+    auto self = shared_from_this();
+    auto state = std::make_shared<PeriodicAsyncState>(
+        self, host, requestInterval, retryInterval, onMessageProcessedCallback, onRequestFinishedCallback);
+    requesting = true;
+    nextRequest(std::move(state));
+}
+
 void Client::stop()
 {
     client->stop();
+    timer->stop();
+    requesting = false;
+}
+
+void Client::processResponse(const std::string & response,
+                             const OnMessageProcessedCallback & onMessageProcessedCallback)
+{
+    if (!response.empty())
+    {
+        parser << response;
+        Message message;
+        while (parser >> message)
+            onMessageProcessedCallback(message);
+    }
+}
+
+void Client::nextRequest(PeriodicAsyncState::Ptr state)
+{
+    using namespace std::chrono_literals;
+
+    if (!requesting)
+        return;
+
+    std::string emptyRequest;
+    net.waitWhileBusy(*(state->self->client));
+    client->asyncCall(
+        emptyRequest, state->host, state->self->port, state->retryInterval,
+        [state](const auto & error, const auto & responseMessage)
+        {
+            auto & self = state->self;
+            if (error)
+            {
+                state->onRequestFinishedCallback(error);
+                self->net.callLater([state] { state->self->nextRequest(state); });
+                return;
+            }
+            self->processResponse(responseMessage, state->onMessageProcessedCallback);
+            state->onRequestFinishedCallback(error);
+            self->timer->startTimeout(
+                state->requestInterval, [state] { state->self->nextRequest(state); });
+        });
 }
 
 }
diff --git a/modules/catkin_ws/src/MessageLib/test/ServerClientTest.cpp b/modules/catkin_ws/src/MessageLib/test/ServerClientTest.cpp
index 7b8f7a9a..969b82ac 100644
--- a/modules/catkin_ws/src/MessageLib/test/ServerClientTest.cpp
+++ b/modules/catkin_ws/src/MessageLib/test/ServerClientTest.cpp
@@ -41,38 +41,16 @@ void fromLocal()
     while (running);
 }
 
-void requestNextMessages(networking::Networking & net,
-                        networking::time::Timer::Ptr timer,
-                        message::Client::Ptr client,
-                        const networking::time::Duration & delay)
-{
-    using namespace std::chrono_literals;
-    timer->startTimeout(delay, [&net, timer, client]
-    {
-        net.waitWhileBusy(*client);
-        client->requestMessages(
-            "127.0.0.1", 10s,
-            [](const auto & message) { printMessage(message); },
-            [&net, timer, client](const auto & error)
-            {
-                if (error)
-                {
-                    std::cerr << "Could not connect to car!\n";
-                    requestNextMessages(net, timer, client, 3s);
-                }
-                else requestNextMessages(net, timer, client, 10ms);
-            });
-    });
-}
-
 void fromCar()
 {
     using namespace message;
     using namespace std::chrono_literals;
     networking::Networking net;
     auto client = Client::create(net, 10207);
-    auto timer = networking::time::Timer::create(net);
-    requestNextMessages(net, timer, client, 10ms);
+    client->requestMessagesPeriodically(
+        "127.0.0.1", 10ms, 3s,
+        [](const auto & message) { printMessage(message); },
+        [](const auto & error) { if (error) std::cerr << "Could not connect to car!\n"; });
     while (1);
 }
 
diff --git a/modules/catkin_ws/src/PCGui/.gitignore b/modules/catkin_ws/src/PCGui/.gitignore
index 46e00774..9b34f868 100644
--- a/modules/catkin_ws/src/PCGui/.gitignore
+++ b/modules/catkin_ws/src/PCGui/.gitignore
@@ -1,3 +1,4 @@
 .vscode/
 build-PCGui-Desktop_Qt_5_7_1_GCC_64bit-Default/
-CMakeLists.txt.*
\ No newline at end of file
+CMakeLists.txt.*
+cmake-build-debug/
\ No newline at end of file
diff --git a/modules/catkin_ws/src/PCGui/PCGui/CMakeLists.txt b/modules/catkin_ws/src/PCGui/PCGui/CMakeLists.txt
index b4caae0f..ac4fc842 100644
--- a/modules/catkin_ws/src/PCGui/PCGui/CMakeLists.txt
+++ b/modules/catkin_ws/src/PCGui/PCGui/CMakeLists.txt
@@ -2,12 +2,10 @@ cmake_minimum_required(VERSION 3.5.1)
 
 project(PCGui LANGUAGES CXX)
 
-set(CMAKE_CXX_STANDARD 14)
-
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTORCC ON)
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 14)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 find_package(Qt5 COMPONENTS Core Quick REQUIRED)
@@ -20,17 +18,38 @@ find_package(Boost REQUIRED COMPONENTS regex system)
 find_package(NetworkingLib REQUIRED)
 find_package(PlatoonProtocolLib REQUIRED)
 find_package(PC2CarLib REQUIRED)
+find_package(MessageLib REQUIRED)
+
+set(LIBS
+        Qt5::Core Qt5::Quick
+        NetworkingLib
+        PlatoonProtocolLib
+        PC2CarLib
+        MessageLib)
+
+set(INCLUDE_DIRS
+        ${PC2CarLib_INCLUDE_DIRS}
+        ${MessageLib_INCLUDE_DIRS})
 
 set(SOURCE_FILES
-    include/Logger.h
-    src/Logger.cpp
-    src/main.cpp
-    qml.qrc)
-
-add_executable(${PROJECT_NAME} ${SOURCE_FILES})
-target_link_libraries(${PROJECT_NAME}
-    Qt5::Core Qt5::Quick
-    NetworkingLib
-    PlatoonProtocolLib
-    PC2CarLib)
-target_include_directories(${PROJECT_NAME} PUBLIC ${PC2CarLib_INCLUDE_DIRS})
+        include/QtQuickControlsApplication.h
+        qml.qrc)
+
+add_executable(CommandGui ${SOURCE_FILES}
+        src/CommandGui.cpp)
+
+add_executable(MessageGui ${SOURCE_FILES}
+        include/SortFilterProxyModel.h
+        include/MessageModel.h
+        include/MessageList.h
+        include/MessageProducer.h
+        src/MessageGui.cpp
+        src/MessageModel.cpp
+        src/MessageList.cpp
+        src/SortFilterProxyModel.cpp)
+
+target_link_libraries(CommandGui ${LIBS})
+target_link_libraries(MessageGui ${LIBS})
+
+target_include_directories(CommandGui PUBLIC ${INCLUDE_DIRS})
+target_include_directories(MessageGui PUBLIC ${INCLUDE_DIRS})
diff --git a/modules/catkin_ws/src/PCGui/PCGui/main.qml b/modules/catkin_ws/src/PCGui/PCGui/CommandGui.qml
similarity index 77%
rename from modules/catkin_ws/src/PCGui/PCGui/main.qml
rename to modules/catkin_ws/src/PCGui/PCGui/CommandGui.qml
index e097a0e4..9a40ba2d 100644
--- a/modules/catkin_ws/src/PCGui/PCGui/main.qml
+++ b/modules/catkin_ws/src/PCGui/PCGui/CommandGui.qml
@@ -5,5 +5,5 @@ Window {
     visible: true
     width: 640
     height: 480
-    title: qsTr("Hello World")
+    title: qsTr("Command Gui")
 }
diff --git a/modules/catkin_ws/src/PCGui/PCGui/MessageGui.qml b/modules/catkin_ws/src/PCGui/PCGui/MessageGui.qml
new file mode 100644
index 00000000..45d0b62e
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/MessageGui.qml
@@ -0,0 +1,142 @@
+import QtQuick 2.2
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 1.2
+import car.pcgui 1.0
+
+ApplicationWindow {
+    id: window
+    visible: true
+    title: "Message Gui"
+
+    toolBar: ToolBar {
+        height: 180
+
+        Label {
+            id: moduleLabel
+            text: "Module: "
+            anchors.left: parent.left
+            anchors.leftMargin: 10
+            anchors.top: parent.top
+            anchors.topMargin: 20
+        }
+
+        TextField {
+            id: moduleSearchBox
+
+            placeholderText: "Search..."
+            inputMethodHints: Qt.ImhNoPredictiveText
+
+            anchors.left: moduleLabel.right
+            anchors.leftMargin: 10
+            anchors.right: parent.right
+            anchors.rightMargin: 10
+            anchors.verticalCenter: moduleLabel.verticalCenter
+        }
+
+        Label {
+            id: keyLabel
+            text: "Key: "
+            anchors.left: moduleLabel.left
+            anchors.top: moduleLabel.bottom
+            anchors.topMargin: 20
+        }
+
+        TextField {
+            id: keySearchBox
+
+            placeholderText: "Search..."
+            inputMethodHints: Qt.ImhNoPredictiveText
+
+            anchors.left: moduleSearchBox.left
+            anchors.right: moduleSearchBox.right
+            anchors.verticalCenter: keyLabel.verticalCenter
+        }
+
+        CheckBox {
+            id: mostRecentCheckBox
+            text: "show only most recent values"
+            checked: false
+
+            anchors.left: moduleLabel.left
+            anchors.top: keyLabel.bottom
+            anchors.topMargin: 20
+        }
+
+        Label {
+            id: statusLabel
+            text: "Status:"
+
+            anchors.left: moduleLabel.left
+            anchors.top: mostRecentCheckBox.bottom
+            anchors.topMargin: 20
+        }
+
+        Label {
+            id: status
+            text: "Disconnected"
+            color: "red"
+
+            anchors.left: moduleSearchBox.left
+            anchors.top: statusLabel.top
+        }
+    }
+
+    TableView {
+        id: tableView
+
+        frameVisible: false
+        sortIndicatorVisible: true
+
+        anchors.fill: parent
+
+        Layout.minimumWidth: 400
+        Layout.minimumHeight: 240
+        Layout.preferredWidth: 600
+        Layout.preferredHeight: 400
+
+        TableViewColumn {
+            id: moduleColumn
+            title: "Module"
+            role: "module"
+            movable: false
+            resizable: true
+            width: tableView.viewport.width / 3
+        }
+
+        TableViewColumn {
+            id: keyColumn
+            title: "Key"
+            role: "key"
+            movable: false
+            resizable: true
+            width: tableView.viewport.width / 3
+        }
+
+        TableViewColumn {
+            id: valueColumn
+            title: "Value"
+            role: "value"
+            movable: false
+            resizable: true
+            width: tableView.viewport.width / 3
+        }
+
+        // model: SortFilterProxyModel {
+        //     id: proxyModel
+        //     source: sourceModel.count > 0 ? sourceModel : null
+
+        //     sortOrder: tableView.sortIndicatorOrder
+        //     sortCaseSensitivity: Qt.CaseInsensitive
+        //     sortRole: sourceModel.count > 0 ? tableView.getColumn(tableView.sortIndicatorColumn).role : ""
+
+        //     filterString: moduleSearchBox.text
+        //     filterSyntax: SortFilterProxyModel.FixedString
+        //     filterCaseSensitivity: Qt.CaseInsensitive
+        // }
+
+        model: MessageModel {
+            id: messageModel
+            messageList: messages
+        }
+    }
+}
diff --git a/modules/catkin_ws/src/PCGui/PCGui/include/Logger.h b/modules/catkin_ws/src/PCGui/PCGui/include/Logger.h
deleted file mode 100644
index a78e9897..00000000
--- a/modules/catkin_ws/src/PCGui/PCGui/include/Logger.h
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// Created by philipp on 08.05.18.
-//
-
-#ifndef PC_LOGGER_H
-#define PC_LOGGER_H
-
-#include <memory>
-#include <NetworkingLib/Networking.h>
-#include <NetworkingLib/ServiceClient.h>
-
-namespace pc
-{
-
-class Logger
-    : public std::enable_shared_from_this<Logger>
-      , private networking::Busyable
-{
-private:
-    struct PrivateTag
-    {
-    };
-
-    struct LoggingService
-    {
-        using RequestMessage = std::string;
-        using ResponseMessage = std::string;
-    };
-
-public:
-    using Ptr = std::shared_ptr<Logger>;
-    using LoggingClient = networking::service::Client<LoggingService>;
-
-    static constexpr std::size_t PORT{10207};
-    static constexpr std::size_t MAX_MESSAGE_SIZE{1024};
-
-    static Ptr create(networking::Networking & net, const std::string & host)
-    { return std::make_shared<Logger>(PrivateTag{}, net, host); }
-
-    Logger(PrivateTag, networking::Networking & net, const std::string & host);
-
-    void start();
-
-    void stop();
-
-private:
-    struct AsyncState
-    {
-        using Ptr = std::shared_ptr<AsyncState>;
-
-        AsyncState(Logger::Ptr self)
-            : lock(*self), self(self)
-        {}
-
-        networking::BusyLock lock;
-        Logger::Ptr self;
-    };
-
-    networking::Networking & net;
-    std::string host;
-    LoggingClient::Ptr client;
-    networking::time::Timer::Ptr timer;
-
-    void log(AsyncState::Ptr state);
-};
-
-}
-
-#endif //PC_LOGGER_H
diff --git a/modules/catkin_ws/src/PCGui/PCGui/include/MessageList.h b/modules/catkin_ws/src/PCGui/PCGui/include/MessageList.h
new file mode 100644
index 00000000..06ada7ef
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/include/MessageList.h
@@ -0,0 +1,32 @@
+//
+// Created by philipp on 13.05.18.
+//
+
+#ifndef PCGUI_MESSAGELIST_H
+#define PCGUI_MESSAGELIST_H
+
+#include <QObject>
+#include <MessageLib/Message.h>
+#include <QtCore/QVector>
+
+class MessageList : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit MessageList(QObject * parent = nullptr);
+
+    const QVector<message::Message> & getItems() const noexcept;
+
+signals:
+    void preMessageAppended();
+    void postMessageAppended();
+
+public slots:
+    void appendMessage(const message::Message & message);
+
+private:
+    QVector<message::Message> items;
+};
+
+#endif //PCGUI_MESSAGELIST_H
diff --git a/modules/catkin_ws/src/PCGui/PCGui/include/MessageModel.h b/modules/catkin_ws/src/PCGui/PCGui/include/MessageModel.h
new file mode 100644
index 00000000..3434974b
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/include/MessageModel.h
@@ -0,0 +1,38 @@
+#ifndef MESSAGEMODEL_H
+#define MESSAGEMODEL_H
+
+#include <QAbstractListModel>
+#include "MessageLib/Message.h"
+#include "MessageList.h"
+
+class MessageModel : public QAbstractListModel
+{
+Q_OBJECT
+
+Q_PROPERTY(MessageList * messageList READ getMessageList WRITE setMessageList)
+
+public:
+    explicit MessageModel(QObject * parent = nullptr);
+
+    enum
+    {
+        ModuleRole = Qt::UserRole,
+        KeyRole,
+        ValueRole
+    };
+
+    int rowCount(const QModelIndex & parent = QModelIndex()) const override;
+
+    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
+
+    QHash<int, QByteArray> roleNames() const override;
+
+    MessageList * getMessageList() const noexcept;
+
+    void setMessageList(MessageList * newMessageList);
+
+private:
+    MessageList * messageList;
+};
+
+#endif // MESSAGEMODEL_H
\ No newline at end of file
diff --git a/modules/catkin_ws/src/PCGui/PCGui/include/MessageProducer.h b/modules/catkin_ws/src/PCGui/PCGui/include/MessageProducer.h
new file mode 100644
index 00000000..04d36010
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/include/MessageProducer.h
@@ -0,0 +1,23 @@
+//
+// Created by philipp on 14.05.18.
+//
+
+#ifndef PCGUI_MESSAGEPRODUCER_H
+#define PCGUI_MESSAGEPRODUCER_H
+
+#include <QObject>
+#include "MessageLib/Message.h"
+
+class MessageProducer : public QObject
+{
+Q_OBJECT
+
+public:
+    void produceMessage(const message::Message & message)
+    { emit messageProduced(message); }
+
+signals:
+    void messageProduced(message::Message);
+};
+
+#endif //PCGUI_MESSAGEPRODUCER_H
diff --git a/modules/catkin_ws/src/PCGui/PCGui/include/QtQuickControlsApplication.h b/modules/catkin_ws/src/PCGui/PCGui/include/QtQuickControlsApplication.h
new file mode 100644
index 00000000..a35ca7e5
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/include/QtQuickControlsApplication.h
@@ -0,0 +1,24 @@
+//
+// Created by philipp on 13.05.18.
+//
+
+#ifndef PCGUI_QTQUICKCONTROLSAPPLICATION_H
+#define PCGUI_QTQUICKCONTROLSAPPLICATION_H
+
+#ifdef QT_WIDGETS_LIB
+#include <QtWidgets/QApplication>
+#else
+#include <QtGui/QGuiApplication>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_WIDGETS_LIB
+#define QtQuickControlsApplication QApplication
+#else
+#define QtQuickControlsApplication QGuiApplication
+#endif
+
+QT_END_NAMESPACE
+
+#endif //PCGUI_QTQUICKCONTROLSAPPLICATION_H
diff --git a/modules/catkin_ws/src/PCGui/PCGui/include/SortFilterProxyModel.h b/modules/catkin_ws/src/PCGui/PCGui/include/SortFilterProxyModel.h
new file mode 100644
index 00000000..08b81a85
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/include/SortFilterProxyModel.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of The Qt Company Ltd nor the names of its
+**     contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SORTFILTERPROXYMODEL_H
+#define SORTFILTERPROXYMODEL_H
+
+#include <QtCore/qsortfilterproxymodel.h>
+#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qjsvalue.h>
+
+class SortFilterProxyModel : public QSortFilterProxyModel, public QQmlParserStatus
+{
+    Q_OBJECT
+    Q_INTERFACES(QQmlParserStatus)
+
+    Q_PROPERTY(int count READ count NOTIFY countChanged)
+    Q_PROPERTY(QObject *source READ source WRITE setSource)
+
+    Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole)
+    Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder)
+
+    Q_PROPERTY(QByteArray filterRole READ filterRole WRITE setFilterRole)
+    Q_PROPERTY(QString filterString READ filterString WRITE setFilterString)
+    Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax)
+
+    Q_ENUMS(FilterSyntax)
+
+public:
+    explicit SortFilterProxyModel(QObject *parent = 0);
+
+    QObject *source() const;
+    void setSource(QObject *source);
+
+    QByteArray sortRole() const;
+    void setSortRole(const QByteArray &role);
+
+    void setSortOrder(Qt::SortOrder order);
+
+    QByteArray filterRole() const;
+    void setFilterRole(const QByteArray &role);
+
+    QString filterString() const;
+    void setFilterString(const QString &filter);
+
+    enum FilterSyntax {
+        RegExp,
+        Wildcard,
+        FixedString
+    };
+
+    FilterSyntax filterSyntax() const;
+    void setFilterSyntax(FilterSyntax syntax);
+
+    int count() const;
+    Q_INVOKABLE QJSValue get(int index) const;
+
+    void classBegin();
+    void componentComplete();
+
+signals:
+    void countChanged();
+
+protected:
+    int roleKey(const QByteArray &role) const;
+    QHash<int, QByteArray> roleNames() const;
+    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
+
+private:
+    bool m_complete;
+    QByteArray m_sortRole;
+    QByteArray m_filterRole;
+};
+
+#endif // SORTFILTERPROXYMODEL_H
diff --git a/modules/catkin_ws/src/PCGui/PCGui/qml.qrc b/modules/catkin_ws/src/PCGui/PCGui/qml.qrc
index 5f6483ac..23750ca0 100644
--- a/modules/catkin_ws/src/PCGui/PCGui/qml.qrc
+++ b/modules/catkin_ws/src/PCGui/PCGui/qml.qrc
@@ -1,5 +1,6 @@
 <RCC>
     <qresource prefix="/">
-        <file>main.qml</file>
+        <file>CommandGui.qml</file>
+        <file>MessageGui.qml</file>
     </qresource>
 </RCC>
diff --git a/modules/catkin_ws/src/PCGui/PCGui/src/main.cpp b/modules/catkin_ws/src/PCGui/PCGui/src/CommandGui.cpp
similarity index 80%
rename from modules/catkin_ws/src/PCGui/PCGui/src/main.cpp
rename to modules/catkin_ws/src/PCGui/PCGui/src/CommandGui.cpp
index f3de8b09..04741d3d 100644
--- a/modules/catkin_ws/src/PCGui/PCGui/src/main.cpp
+++ b/modules/catkin_ws/src/PCGui/PCGui/src/CommandGui.cpp
@@ -6,7 +6,7 @@ int main(int argc, char *argv[])
     QGuiApplication app(argc, argv);
 
     QQmlApplicationEngine engine;
-    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+    engine.load(QUrl(QStringLiteral("qrc:/CommandGui.qml")));
     if (engine.rootObjects().isEmpty())
         return -1;
 
diff --git a/modules/catkin_ws/src/PCGui/PCGui/src/Logger.cpp b/modules/catkin_ws/src/PCGui/PCGui/src/Logger.cpp
deleted file mode 100644
index 7d5588cb..00000000
--- a/modules/catkin_ws/src/PCGui/PCGui/src/Logger.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// Created by philipp on 08.05.18.
-//
-
-#include "../include/Logger.h"
-#include <iostream>
-
-namespace pc
-{
-
-Logger::Logger(Logger::PrivateTag, networking::Networking & net, const std::string & host)
-    : net(net), host(host)
-{
-    client = LoggingClient::create(net, MAX_MESSAGE_SIZE);
-    timer = networking::time::Timer::create(net);
-}
-
-void Logger::start()
-{
-    auto self = shared_from_this();
-    auto state = std::make_shared<AsyncState>(self);
-    log(state);
-}
-
-void Logger::stop()
-{
-    timer->stop();
-    client->stop();
-}
-
-void Logger::log(AsyncState::Ptr state)
-{
-    using namespace std::chrono_literals;
-    std::string emptyRequest;
-    client->asyncCall(
-        emptyRequest, host, PORT, 10s,
-        [state](const auto & error, auto & responseMessage)
-        {
-            if (!responseMessage.empty())
-                std::cout << responseMessage;
-
-            state->self->timer->startTimeout(1ms, [state]
-            { state->self->log(state); });
-        });
-}
-
-}
-
diff --git a/modules/catkin_ws/src/PCGui/PCGui/src/MessageGui.cpp b/modules/catkin_ws/src/PCGui/PCGui/src/MessageGui.cpp
new file mode 100644
index 00000000..7dcf04af
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/src/MessageGui.cpp
@@ -0,0 +1,54 @@
+#include "../include/QtQuickControlsApplication.h"
+#include "../include/SortFilterProxyModel.h"
+#include <QtQml/qqmlapplicationengine.h>
+#include <QtGui/qsurfaceformat.h>
+#include <QtQml/qqml.h>
+#include <QtQml/QQmlContext>
+#include <include/MessageModel.h>
+#include "NetworkingLib/Networking.h"
+#include "MessageLib/Client.h"
+#include <chrono>
+#include <iostream>
+#include <include/MessageProducer.h>
+
+Q_DECLARE_METATYPE(message::Message);
+
+int main(int argc, char * argv[])
+{
+    using namespace std::chrono_literals;
+
+    qmlRegisterType<SortFilterProxyModel>("car.pcgui", 1, 0, "SortFilterProxyModel");
+    qmlRegisterType<MessageModel>("car.pcgui", 1, 0, "MessageModel");
+    qRegisterMetaType<message::Message>();
+    qmlRegisterUncreatableType<MessageList>(
+        "car.pcgui", 1, 0, "MessageList", QStringLiteral("MessageList should not be created in QML"));
+
+    networking::Networking net;
+    auto messageClient = message::Client::create(net, 10207);
+    MessageList messages;
+    MessageProducer producer;
+    QObject::connect(&producer, SIGNAL(messageProduced(message::Message)),
+                     &messages, SLOT(appendMessage(const message::Message &)));
+
+    QtQuickControlsApplication app(argc, argv);
+    if (QCoreApplication::arguments().contains(QLatin1String("--coreprofile")))
+    {
+        QSurfaceFormat fmt;
+        fmt.setVersion(4, 4);
+        fmt.setProfile(QSurfaceFormat::CoreProfile);
+        QSurfaceFormat::setDefaultFormat(fmt);
+    }
+
+    QQmlApplicationEngine engine{};
+    engine.rootContext()->setContextProperty(QStringLiteral("messages"), &messages);
+    engine.load(QUrl("qrc:/MessageGui.qml"));
+    if (engine.rootObjects().isEmpty())
+        return -1;
+
+    messageClient->requestMessagesPeriodically(
+        "127.0.0.1", 10ms, 3s,
+        [&producer](const auto & message)
+        { producer.produceMessage(message); });
+
+    return app.exec();
+}
diff --git a/modules/catkin_ws/src/PCGui/PCGui/src/MessageList.cpp b/modules/catkin_ws/src/PCGui/PCGui/src/MessageList.cpp
new file mode 100644
index 00000000..9fc693b9
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/src/MessageList.cpp
@@ -0,0 +1,21 @@
+//
+// Created by philipp on 13.05.18.
+//
+
+#include "../include/MessageList.h"
+
+MessageList::MessageList(QObject * parent) : QObject(parent)
+{
+}
+
+const QVector<message::Message> & MessageList::getItems() const noexcept
+{
+    return items;
+}
+
+void MessageList::appendMessage(const message::Message & message)
+{
+    emit preMessageAppended();
+    items.append(message);
+    emit postMessageAppended();
+}
diff --git a/modules/catkin_ws/src/PCGui/PCGui/src/MessageModel.cpp b/modules/catkin_ws/src/PCGui/PCGui/src/MessageModel.cpp
new file mode 100644
index 00000000..b305119a
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/src/MessageModel.cpp
@@ -0,0 +1,78 @@
+#include "../include/MessageModel.h"
+
+MessageModel::MessageModel(QObject * parent)
+    : QAbstractListModel(parent)
+      , messageList(nullptr)
+{
+}
+
+int MessageModel::rowCount(const QModelIndex & parent) const
+{
+    // For list models only the root node (an invalid parent) should return the list's size. For all
+    // other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
+    if (parent.isValid() || !messageList)
+        return 0;
+
+    return messageList->getItems().size();
+}
+
+QVariant MessageModel::data(const QModelIndex & index, int role) const
+{
+    if (!index.isValid() || !messageList)
+        return QVariant{};
+
+    auto message = messageList->getItems().at(index.row());
+    switch (role)
+    {
+        case ModuleRole:
+            return QVariant{QString::fromStdString(message.module)};
+
+        case KeyRole:
+            return QVariant{QString::fromStdString(message.key)};
+
+        case ValueRole:
+            return QVariant{QString::fromStdString(message.value)};
+    }
+
+    return QVariant{};
+}
+
+QHash<int, QByteArray> MessageModel::roleNames() const
+{
+    QHash<int, QByteArray> names;
+    names[ModuleRole] = "module";
+    names[KeyRole] = "key";
+    names[ValueRole] = "value";
+    return names;
+}
+
+MessageList * MessageModel::getMessageList() const noexcept
+{
+    return messageList;
+}
+
+void MessageModel::setMessageList(MessageList * newMessageList)
+{
+    beginResetModel();
+
+    if (messageList)
+        messageList->disconnect(this);
+
+    messageList = newMessageList;
+
+    if (messageList)
+    {
+        connect(messageList, &MessageList::preMessageAppended, this, [this]
+        {
+            int index = messageList->getItems().size();
+            beginInsertRows(QModelIndex{}, index, index);
+        });
+
+        connect(messageList, &MessageList::postMessageAppended, this, [this]
+        {
+            endInsertRows();
+        });
+    }
+
+    endResetModel();
+}
diff --git a/modules/catkin_ws/src/PCGui/PCGui/src/SortFilterProxyModel.cpp b/modules/catkin_ws/src/PCGui/PCGui/src/SortFilterProxyModel.cpp
new file mode 100644
index 00000000..4baa931f
--- /dev/null
+++ b/modules/catkin_ws/src/PCGui/PCGui/src/SortFilterProxyModel.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of The Qt Company Ltd nor the names of its
+**     contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../include/SortFilterProxyModel.h"
+#include <QtDebug>
+#include <QtQml>
+
+SortFilterProxyModel::SortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), m_complete(false)
+{
+    connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
+    connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
+}
+
+int SortFilterProxyModel::count() const
+{
+    return rowCount();
+}
+
+QObject *SortFilterProxyModel::source() const
+{
+    return sourceModel();
+}
+
+void SortFilterProxyModel::setSource(QObject *source)
+{
+    setSourceModel(qobject_cast<QAbstractItemModel *>(source));
+}
+
+QByteArray SortFilterProxyModel::sortRole() const
+{
+    return m_sortRole;
+}
+
+void SortFilterProxyModel::setSortRole(const QByteArray &role)
+{
+    if (m_sortRole != role) {
+        m_sortRole = role;
+        if (m_complete)
+            QSortFilterProxyModel::setSortRole(roleKey(role));
+    }
+}
+
+void SortFilterProxyModel::setSortOrder(Qt::SortOrder order)
+{
+    QSortFilterProxyModel::sort(0, order);
+}
+
+QByteArray SortFilterProxyModel::filterRole() const
+{
+    return m_filterRole;
+}
+
+void SortFilterProxyModel::setFilterRole(const QByteArray &role)
+{
+    if (m_filterRole != role) {
+        m_filterRole = role;
+        if (m_complete)
+            QSortFilterProxyModel::setFilterRole(roleKey(role));
+    }
+}
+
+QString SortFilterProxyModel::filterString() const
+{
+    return filterRegExp().pattern();
+}
+
+void SortFilterProxyModel::setFilterString(const QString &filter)
+{
+    setFilterRegExp(QRegExp(filter, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())));
+}
+
+SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const
+{
+    return static_cast<FilterSyntax>(filterRegExp().patternSyntax());
+}
+
+void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax syntax)
+{
+    setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(syntax)));
+}
+
+QJSValue SortFilterProxyModel::get(int idx) const
+{
+    QJSEngine *engine = qmlEngine(this);
+    QJSValue value = engine->newObject();
+    if (idx >= 0 && idx < count()) {
+        QHash<int, QByteArray> roles = roleNames();
+        QHashIterator<int, QByteArray> it(roles);
+        while (it.hasNext()) {
+            it.next();
+            value.setProperty(QString::fromUtf8(it.value()), data(index(idx, 0), it.key()).toString());
+        }
+    }
+    return value;
+}
+
+void SortFilterProxyModel::classBegin()
+{
+}
+
+void SortFilterProxyModel::componentComplete()
+{
+    m_complete = true;
+    if (!m_sortRole.isEmpty())
+        QSortFilterProxyModel::setSortRole(roleKey(m_sortRole));
+    if (!m_filterRole.isEmpty())
+        QSortFilterProxyModel::setFilterRole(roleKey(m_filterRole));
+}
+
+int SortFilterProxyModel::roleKey(const QByteArray &role) const
+{
+    QHash<int, QByteArray> roles = roleNames();
+    QHashIterator<int, QByteArray> it(roles);
+    while (it.hasNext()) {
+        it.next();
+        if (it.value() == role)
+            return it.key();
+    }
+    return -1;
+}
+
+QHash<int, QByteArray> SortFilterProxyModel::roleNames() const
+{
+    if (QAbstractItemModel *source = sourceModel())
+        return source->roleNames();
+    return QHash<int, QByteArray>();
+}
+
+bool SortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+    QRegExp rx = filterRegExp();
+    if (rx.isEmpty())
+        return true;
+    QAbstractItemModel *model = sourceModel();
+    if (filterRole().isEmpty()) {
+        QHash<int, QByteArray> roles = roleNames();
+        QHashIterator<int, QByteArray> it(roles);
+        while (it.hasNext()) {
+            it.next();
+            QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent);
+            QString key = model->data(sourceIndex, it.key()).toString();
+            if (key.contains(rx))
+                return true;
+        }
+        return false;
+    }
+    QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent);
+    if (!sourceIndex.isValid())
+        return true;
+    QString key = model->data(sourceIndex, roleKey(filterRole())).toString();
+    return key.contains(rx);
+}
diff --git a/modules/catkin_ws/src/car/launch/noUss.launch b/modules/catkin_ws/src/car/launch/noHardware.launch
similarity index 88%
rename from modules/catkin_ws/src/car/launch/noUss.launch
rename to modules/catkin_ws/src/car/launch/noHardware.launch
index 96aa1a2c..2c09576a 100644
--- a/modules/catkin_ws/src/car/launch/noUss.launch
+++ b/modules/catkin_ws/src/car/launch/noHardware.launch
@@ -8,7 +8,6 @@ dsdf  <node pkg="nodelet" type="nodelet" name="Master" args="manager" output="sc
   <node pkg="nodelet" type="nodelet" name="nodelet_environment" args="load car/environment Master"/>
   <node pkg="nodelet" type="nodelet" name="nodelet_camera" args="load car/camera Master"/>
   <node pkg="nodelet" type="nodelet" name="nodelet_lanekeeping" args="load car/lanekeeping Master"/>
-  <node pkg="nodelet" type="nodelet" name="nodelet_mav_link" args="load car/mav_link Master"/>
 </launch>
 
 <!--
diff --git a/modules/catkin_ws/src/car/src/mavLink/MavLink.cpp b/modules/catkin_ws/src/car/src/mavLink/MavLink.cpp
index 06f846b0..f5ed75cd 100644
--- a/modules/catkin_ws/src/car/src/mavLink/MavLink.cpp
+++ b/modules/catkin_ws/src/car/src/mavLink/MavLink.cpp
@@ -58,14 +58,12 @@ void MavLink::onStmDataReceived()
 
 float MavLink::convertSpeedToStm(float speed) const
 {
-    // TODO: convert speed!
-    return speed;
+    return speed / 100;
 }
 
 float MavLink::convertSpeedFromStm(float speed) const
 {
-    // TODO: convert speed!
-    return speed;
+    return speed * 100;
 }
 
 
-- 
GitLab