From 9eef1fc3ffefcf7dd36aaf2cf6fac0e258c2631e Mon Sep 17 00:00:00 2001 From: Sander Speetjens Date: Thu, 12 Sep 2024 16:59:54 +0200 Subject: [PATCH] Move to Qt Everything seems to be working Only timer is not moved over --- src/UI/CMakeLists.txt | 23 +++- src/UI/UI_button.cpp | 16 +-- src/UI/UI_button.hpp | 14 +-- src/UI/UI_checkbox.cpp | 15 +-- src/UI/UI_checkbox.hpp | 15 +-- src/UI/UI_combobox.cpp | 17 ++- src/UI/UI_combobox.hpp | 14 +-- src/UI/UI_dial.cpp | 9 +- src/UI/UI_dial.hpp | 7 +- src/UI/UI_item.cpp | 37 ++++-- src/UI/UI_item.hpp | 19 ++- src/UI/UI_label.cpp | 8 +- src/UI/UI_label.hpp | 7 +- src/UI/UI_led.cpp | 14 ++- src/UI/UI_led.hpp | 7 +- src/UI/UI_slider.cpp | 16 ++- src/UI/UI_slider.hpp | 12 +- src/UI/UI_textbox.cpp | 16 ++- src/UI/UI_textbox.hpp | 11 +- src/UI/UI_thermo.cpp | 15 ++- src/UI/UI_thermo.hpp | 7 +- src/UI/UI_x_boxes.cpp | 8 +- src/UI/UI_x_boxes.hpp | 7 +- tests/CMakeLists.txt | 10 +- tests/UI_button.cpp | 14 +-- tests/UI_slider.cpp | 17 +-- tools/CMakeLists.txt | 19 ++- tools/main.cpp | 88 +++----------- tools/message_parser.cpp | 36 +++--- tools/message_parser.hpp | 29 ----- tools/simulator_base.cpp | 7 +- tools/simulator_base.hpp | 10 +- tools/simulators.cpp | 25 +++- tools/simulators.hpp | 37 +++++- tools/simulators/can_debugger.hpp | 1 + tools/simulators/test_sim.cpp | 17 +-- tools/simulators/test_sim.hpp | 3 +- tools/websocket.cpp | 189 +++++++++--------------------- tools/websocket.hpp | 84 ++++--------- 39 files changed, 399 insertions(+), 501 deletions(-) delete mode 100644 tools/message_parser.hpp diff --git a/src/UI/CMakeLists.txt b/src/UI/CMakeLists.txt index f3f1318..32e4a0e 100644 --- a/src/UI/CMakeLists.txt +++ b/src/UI/CMakeLists.txt @@ -5,6 +5,15 @@ include(FetchContent) FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz DOWNLOAD_EXTRACT_TIMESTAMP TRUE) FetchContent_MakeAvailable(json) +find_package(Qt6 REQUIRED COMPONENTS Core) + +set_target_properties( + UI + PROPERTIES + AUTOMOC ON + AUTORCC ON +) + target_compile_options(UI PRIVATE $<$: -Wall -Wextra -pedantic-errors -Wconversion -Wsign-conversion >) @@ -12,16 +21,27 @@ target_compile_options(UI PRIVATE $<$: target_sources(UI PRIVATE ${CMAKE_CURRENT_LIST_DIR}/UI_item.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_item.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_button.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_button.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_checkbox.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_checkbox.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_combobox.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_combobox.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_dial.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_dial.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_label.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_label.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_led.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_led.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_slider.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_slider.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_textbox.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_textbox.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_thermo.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_thermo.hpp ${CMAKE_CURRENT_LIST_DIR}/UI_x_boxes.cpp + ${CMAKE_CURRENT_LIST_DIR}/UI_x_boxes.hpp ) target_include_directories(UI @@ -36,4 +56,5 @@ target_link_libraries(UI PUBLIC Color nlohmann_json::nlohmann_json -) \ No newline at end of file + Qt::Core +) diff --git a/src/UI/UI_button.cpp b/src/UI/UI_button.cpp index db222df..bbbf106 100644 --- a/src/UI/UI_button.cpp +++ b/src/UI/UI_button.cpp @@ -1,13 +1,12 @@ #include "UI_button.hpp" -UI_button::UI_button(std::string id, std::string text, Color fg_color, Color bg_color, - std::function on_click) - : UI_item(id, UI_BUTTON_TYPE, text, 12, fg_color, bg_color) +UI_button::UI_button(std::string id, std::string text, Color fg_color, Color bg_color, QObject* parrent) + : UI_item(id, UI_BUTTON_TYPE, text, 12, fg_color, bg_color, parrent) { - this->m_on_click = on_click; + } -UI_button::UI_button(const json &j) : UI_item(UI_BUTTON_TYPE), m_on_click(nullptr) +UI_button::UI_button(const json &j, QObject* parrent) : UI_item(UI_BUTTON_TYPE, parrent) { from_json(j); } @@ -23,10 +22,7 @@ json UI_button::to_json() const return j; } -void UI_button::click() const +void UI_button::click() { - if (m_on_click != nullptr) - { - std::thread([this] { m_on_click(m_id); }).detach(); - } + emit on_click(m_id); } diff --git a/src/UI/UI_button.hpp b/src/UI/UI_button.hpp index a55c2ef..465c02e 100644 --- a/src/UI/UI_button.hpp +++ b/src/UI/UI_button.hpp @@ -6,16 +6,16 @@ class UI_button : public UI_item { + Q_OBJECT public: - UI_button(std::string id, std::string text, Color fg_color, Color bg_color, - std::function on_click = nullptr); - UI_button(const json &j); + UI_button(std::string id, std::string text, Color fg_color, Color bg_color, QObject* parrent = nullptr); + UI_button(const json &j, QObject* parrent = nullptr); void from_json(const json &j) override; json to_json() const override; - void click() const override; + void click() override; - private: - std::function m_on_click = nullptr; -}; \ No newline at end of file + signals: + void on_click(std::string id); +}; diff --git a/src/UI/UI_checkbox.cpp b/src/UI/UI_checkbox.cpp index fdfcfe8..e4b19cc 100644 --- a/src/UI/UI_checkbox.cpp +++ b/src/UI/UI_checkbox.cpp @@ -1,13 +1,12 @@ #include "UI_checkbox.hpp" -UI_checkbox::UI_checkbox() : UI_x_boxes(UI_CHECKBOX_TYPE), m_selected(), m_on_change(nullptr) +UI_checkbox::UI_checkbox(QObject* parrent) : UI_x_boxes(UI_CHECKBOX_TYPE, parrent), m_selected() { } UI_checkbox::UI_checkbox(std::string id, std::string text, Color fg_color, Color bg_color, - std::vector options, - std::function)> on_change) - : UI_x_boxes(id, UI_CHECKBOX_TYPE, text, fg_color, bg_color, options), m_selected(), m_on_change(on_change) + std::vector options, QObject* parrent) + : UI_x_boxes(id, UI_CHECKBOX_TYPE, text, fg_color, bg_color, options, parrent), m_selected() { for (size_t i = 0; i < m_options.size(); i++) { @@ -15,7 +14,7 @@ UI_checkbox::UI_checkbox(std::string id, std::string text, Color fg_color, Color } } -UI_checkbox::UI_checkbox(const json &j) : UI_x_boxes(UI_CHECKBOX_TYPE), m_selected(), m_on_change(nullptr) +UI_checkbox::UI_checkbox(const json &j, QObject* parrent) : UI_x_boxes(UI_CHECKBOX_TYPE, parrent), m_selected() { from_json(j); } @@ -25,10 +24,8 @@ void UI_checkbox::toggle_selected(size_t selected) if (selected >= m_options.size()) return; m_selected[selected] = !m_selected[selected]; - if (m_on_change != nullptr) - { - std::thread([this] { m_on_change(m_id, selected_text()); }).detach(); - } + emit value_changed(); + emit on_change(m_id, selected_text()[selected]); } void UI_checkbox::toggle_selected(std::string selected) diff --git a/src/UI/UI_checkbox.hpp b/src/UI/UI_checkbox.hpp index 07461f3..00d5fef 100644 --- a/src/UI/UI_checkbox.hpp +++ b/src/UI/UI_checkbox.hpp @@ -7,11 +7,11 @@ class UI_checkbox : public UI_x_boxes { + Q_OBJECT public: - UI_checkbox(); - UI_checkbox(std::string id, std::string text, Color fg_color, Color bg_color, std::vector options, - std::function)> on_change = nullptr); - UI_checkbox(const json &j); + UI_checkbox(QObject* parrent = nullptr); + UI_checkbox(std::string id, std::string text, Color fg_color, Color bg_color, std::vector options, QObject* parrent = nullptr); + UI_checkbox(const json &j, QObject* parrent = nullptr); void toggle_selected(size_t selected); void toggle_selected(std::string selected); @@ -21,8 +21,9 @@ class UI_checkbox : public UI_x_boxes void from_json(const json &j) override; json to_json() const override; + signals: + void on_change(std::string, std::string); + private: std::vector m_selected; - - std::function)> m_on_change; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_combobox.cpp b/src/UI/UI_combobox.cpp index b8ac84e..e7895cd 100644 --- a/src/UI/UI_combobox.cpp +++ b/src/UI/UI_combobox.cpp @@ -1,17 +1,16 @@ #include "UI_combobox.hpp" -UI_combobox::UI_combobox() : UI_x_boxes(UI_COMBOBOX_TYPE) +UI_combobox::UI_combobox(QObject* parrent) : UI_x_boxes(UI_COMBOBOX_TYPE, parrent) { } UI_combobox::UI_combobox(std::string id, std::string text, Color fg_color, Color bg_color, - std::vector options, size_t selected, - std::function on_change) - : UI_x_boxes(id, UI_COMBOBOX_TYPE, text, fg_color, bg_color, options), m_selected(selected), m_on_change(on_change) + std::vector options, size_t selected, QObject* parrent) + : UI_x_boxes(id, UI_COMBOBOX_TYPE, text, fg_color, bg_color, options, parrent), m_selected(selected) { } -UI_combobox::UI_combobox(const json &j) : UI_x_boxes(UI_COMBOBOX_TYPE), m_on_change(nullptr) +UI_combobox::UI_combobox(const json &j, QObject* parrent) : UI_x_boxes(UI_COMBOBOX_TYPE, parrent) { from_json(j); } @@ -21,10 +20,8 @@ void UI_combobox::set_selected(size_t selected) if (selected == m_selected) return; m_selected = selected; - if (m_on_change != nullptr) - { - std::thread([this] { m_on_change(m_id, selected_text()); }).detach(); - } + emit value_changed(); + emit on_change(m_id, selected_text()); } void UI_combobox::set_selected(std::string selected) @@ -68,4 +65,4 @@ json UI_combobox::to_json() const json j = UI_x_boxes::to_json(); j["selected"] = m_options[m_selected]; return j; -} \ No newline at end of file +} diff --git a/src/UI/UI_combobox.hpp b/src/UI/UI_combobox.hpp index bab1c55..263124c 100644 --- a/src/UI/UI_combobox.hpp +++ b/src/UI/UI_combobox.hpp @@ -8,11 +8,12 @@ class UI_combobox : public UI_x_boxes { + Q_OBJECT public: - UI_combobox(); + UI_combobox(QObject* parrent = nullptr); UI_combobox(std::string id, std::string text, Color fg_color, Color bg_color, std::vector options, - size_t selected = 0, std::function on_change = nullptr); - UI_combobox(const json &j); + size_t selected = 0, QObject* parrent = nullptr); + UI_combobox(const json &j, QObject* parrent = nullptr); void set_selected(size_t selected); void set_selected(std::string selected) override; @@ -21,9 +22,8 @@ class UI_combobox : public UI_x_boxes void from_json(const json &j) override; json to_json() const override; - + signals: + void on_change(std::string id, std::string selected); private: size_t m_selected; - - std::function m_on_change; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_dial.cpp b/src/UI/UI_dial.cpp index b0f2726..43afd91 100644 --- a/src/UI/UI_dial.cpp +++ b/src/UI/UI_dial.cpp @@ -1,11 +1,11 @@ #include "UI_dial.hpp" -UI_dial::UI_dial(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, double value) - : UI_item(id, UI_DIAL_TYPE, text, 12, fg_color, bg_color), m_min(min), m_max(max), m_value(value) +UI_dial::UI_dial(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, double value, QObject* parrent) + : UI_item(id, UI_DIAL_TYPE, text, 12, fg_color, bg_color, parrent), m_min(min), m_max(max), m_value(value) { } -UI_dial::UI_dial(const json &j) : UI_item(UI_DIAL_TYPE) +UI_dial::UI_dial(const json &j, QObject* parrent) : UI_item(UI_DIAL_TYPE, parrent) { from_json(j); } @@ -18,6 +18,7 @@ void UI_dial::set_value(double value) return; this->m_value = value; + emit value_changed(); } double UI_dial::value() const @@ -51,4 +52,4 @@ json UI_dial::to_json() const j["max"] = this->m_max; j["value"] = this->m_value; return j; -} \ No newline at end of file +} diff --git a/src/UI/UI_dial.hpp b/src/UI/UI_dial.hpp index 33e468d..4efa557 100644 --- a/src/UI/UI_dial.hpp +++ b/src/UI/UI_dial.hpp @@ -5,9 +5,10 @@ class UI_dial : public UI_item { + Q_OBJECT public: - UI_dial(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, double value); - UI_dial(const json &j); + UI_dial(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, double value, QObject* parrent = nullptr); + UI_dial(const json &j, QObject* parrent = nullptr); void set_value(double value) override; double value() const; @@ -22,4 +23,4 @@ class UI_dial : public UI_item double m_min; double m_max; double m_value; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_item.cpp b/src/UI/UI_item.cpp index ce0d5f1..24a37e2 100644 --- a/src/UI/UI_item.cpp +++ b/src/UI/UI_item.cpp @@ -1,22 +1,29 @@ #include "UI_item.hpp" -UI_item::UI_item(std::string id, std::string type, std::string text, uint8_t text_size, Color fg_color, Color bg_color) - : m_id(id), m_type(type), m_text(text), m_text_size(text_size), m_fg_color(fg_color), m_bg_color(bg_color), +UI_item::UI_item(std::string id, std::string type, std::string text, uint8_t text_size, Color fg_color, Color bg_color, QObject* parrent) + : QObject(parrent), + m_id(id), m_type(type), m_text(text), m_text_size(text_size), m_fg_color(fg_color), m_bg_color(bg_color), enabled(true), visible(true) { } -UI_item::UI_item(std::string type) - : m_id(""), m_type(type), m_text(""), m_text_size(12), m_fg_color(Color::Black), m_bg_color(Color::White), +UI_item::UI_item(std::string type, QObject* parrent) + : QObject(parrent), + m_id(""), m_type(type), m_text(""), m_text_size(12), m_fg_color(Color::Black), m_bg_color(Color::White), enabled(true), visible(true) { } -UI_item::UI_item(const json &j) +UI_item::UI_item(const json &j, QObject* parrent) : + QObject(parrent) { from_json(j); } +UI_item::~UI_item() +{ +} + std::string UI_item::id() const { return m_id; @@ -41,6 +48,7 @@ void UI_item::set_text(std::string text) if (this->m_text != text) { this->m_text = text; + emit value_changed(); } } @@ -54,6 +62,7 @@ void UI_item::set_text_size(uint8_t text_size) if (this->m_text_size != text_size) { this->m_text_size = text_size; + emit value_changed(); } } @@ -67,6 +76,7 @@ void UI_item::set_fg_color(Color fg_color) if (this->m_fg_color != fg_color) { this->m_fg_color = fg_color; + emit value_changed(); } } @@ -80,6 +90,7 @@ void UI_item::set_bg_color(Color bg_color) if (this->m_bg_color != bg_color) { this->m_bg_color = bg_color; + emit value_changed(); } } @@ -90,7 +101,11 @@ Color UI_item::bg_color() const void UI_item::set_enabled(bool enabled) { - this->enabled = enabled; + if (this->enabled != enabled) + { + this->enabled = enabled; + emit value_changed(); + } } bool UI_item::is_enabled() const @@ -100,7 +115,11 @@ bool UI_item::is_enabled() const void UI_item::set_visible(bool visible) { - this->visible = visible; + if (this->visible != visible) + { + this->visible = visible; + emit value_changed(); + } } bool UI_item::is_visible() const @@ -139,7 +158,7 @@ json UI_item::to_json() const return j; } -void UI_item::click() const {}; +void UI_item::click() {}; void UI_item::set_selected(std::string selected) { @@ -148,4 +167,4 @@ void UI_item::set_selected(std::string selected) void UI_item::set_value(double value) { UNUSED(value); -}; \ No newline at end of file +}; diff --git a/src/UI/UI_item.hpp b/src/UI/UI_item.hpp index 6505063..e40b845 100644 --- a/src/UI/UI_item.hpp +++ b/src/UI/UI_item.hpp @@ -5,16 +5,20 @@ #include #include #include + +#include using json = nlohmann::json; #define UNUSED(x) (void)(x) -class UI_item +class UI_item : public QObject { + Q_OBJECT public: - UI_item(std::string type); - UI_item(std::string id, std::string type, std::string text, uint8_t text_size, Color fg_color, Color bg_color); - UI_item(const json &j); + explicit UI_item(std::string type, QObject* parrent = nullptr); + explicit UI_item(std::string id, std::string type, std::string text, uint8_t text_size, Color fg_color, Color bg_color, QObject* parrent = nullptr); + explicit UI_item(const json &j, QObject* parrent = nullptr); + ~UI_item(); std::string id() const; std::string type() const; @@ -42,10 +46,13 @@ class UI_item virtual json to_json() const; - virtual void click() const; + virtual void click(); virtual void set_selected(std::string selected); virtual void set_value(double value); + signals: + void value_changed(); + protected: std::string m_id; std::string m_type; @@ -55,4 +62,4 @@ class UI_item Color m_bg_color; bool enabled; bool visible; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_label.cpp b/src/UI/UI_label.cpp index 514b85c..b4368e7 100644 --- a/src/UI/UI_label.cpp +++ b/src/UI/UI_label.cpp @@ -1,11 +1,11 @@ #include "UI_label.hpp" -UI_label::UI_label(std::string id, std::string text, Color fg_color, Color bg_color) - : UI_item(id, UI_LABEL_TYPE, text, 12, fg_color, bg_color) +UI_label::UI_label(std::string id, std::string text, Color fg_color, Color bg_color, QObject* parrent) + : UI_item(id, UI_LABEL_TYPE, text, 12, fg_color, bg_color, parrent) { } -UI_label::UI_label(const json &j) : UI_item(UI_LABEL_TYPE) +UI_label::UI_label(const json &j, QObject* parrent) : UI_item(UI_LABEL_TYPE, parrent) { from_json(j); } @@ -18,4 +18,4 @@ void UI_label::from_json(const json &j) json UI_label::to_json() const { return UI_item::to_json(); -} \ No newline at end of file +} diff --git a/src/UI/UI_label.hpp b/src/UI/UI_label.hpp index 3e3b400..f6b8a6d 100644 --- a/src/UI/UI_label.hpp +++ b/src/UI/UI_label.hpp @@ -6,10 +6,11 @@ class UI_label : public UI_item { + Q_OBJECT public: - UI_label(std::string id, std::string text, Color fg_color, Color bg_color); - UI_label(const json &j); + UI_label(std::string id, std::string text, Color fg_color, Color bg_color, QObject* parrent = nullptr); + UI_label(const json &j, QObject* parrent = nullptr); void from_json(const json &j) override; json to_json() const override; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_led.cpp b/src/UI/UI_led.cpp index 7917e0a..6f4f0c4 100644 --- a/src/UI/UI_led.cpp +++ b/src/UI/UI_led.cpp @@ -1,11 +1,11 @@ #include "UI_led.hpp" -UI_led::UI_led(std::string id, std::string text, Color fg_color, Color bg_color, Color led_color) - : UI_item(id, UI_LED_TYPE, text, 12, fg_color, bg_color), m_led_color(led_color) +UI_led::UI_led(std::string id, std::string text, Color fg_color, Color bg_color, Color led_color, QObject* parrent) + : UI_item(id, UI_LED_TYPE, text, 12, fg_color, bg_color, parrent), m_led_color(led_color) { } -UI_led::UI_led(const json &j) : UI_item(UI_LED_TYPE) +UI_led::UI_led(const json &j, QObject* parrent) : UI_item(UI_LED_TYPE, parrent) { from_json(j); } @@ -17,7 +17,11 @@ Color UI_led::led_color() const void UI_led::set_led_color(Color led_color) { - m_led_color = led_color; + if (m_led_color != led_color) + { + m_led_color = led_color; + emit value_changed(); + } } void UI_led::from_json(const json &j) @@ -33,4 +37,4 @@ json UI_led::to_json() const json j = UI_item::to_json(); j["led_color"] = m_led_color.to_hex(); return j; -} \ No newline at end of file +} diff --git a/src/UI/UI_led.hpp b/src/UI/UI_led.hpp index 3f52884..79cdc0f 100644 --- a/src/UI/UI_led.hpp +++ b/src/UI/UI_led.hpp @@ -6,9 +6,10 @@ class UI_led : public UI_item { + Q_OBJECT public: - UI_led(std::string id, std::string text, Color fg_color, Color bg_color, Color led_color); - UI_led(const json &j); + UI_led(std::string id, std::string text, Color fg_color, Color bg_color, Color led_color, QObject* parrent = nullptr); + UI_led(const json &j, QObject* parrent = nullptr); Color led_color() const; void set_led_color(Color led_color); @@ -18,4 +19,4 @@ class UI_led : public UI_item private: Color m_led_color; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_slider.cpp b/src/UI/UI_slider.cpp index 78b7280..886cf32 100644 --- a/src/UI/UI_slider.cpp +++ b/src/UI/UI_slider.cpp @@ -1,13 +1,13 @@ #include "UI_slider.hpp" UI_slider::UI_slider(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, - double value, std::function on_change) - : UI_item(id, UI_SLIDER_TYPE, text, 12, fg_color, bg_color), m_min(min), m_max(max), m_value(value) + double value, QObject* parrent) + : UI_item(id, UI_SLIDER_TYPE, text, 12, fg_color, bg_color, parrent), m_min(min), m_max(max), m_value(value) { - this->m_on_change = on_change; + } -UI_slider::UI_slider(const json &j) : UI_item(UI_SLIDER_TYPE), m_on_change(nullptr) +UI_slider::UI_slider(const json &j, QObject* parrent) : UI_item(UI_SLIDER_TYPE, parrent) { from_json(j); } @@ -20,10 +20,8 @@ void UI_slider::set_value(double value) return; this->m_value = value; - if (m_on_change != nullptr) - { - std::thread([this] { m_on_change(m_id, m_value); }).detach(); - } + emit value_changed(); + emit on_change(m_id, m_value); } double UI_slider::value() const @@ -57,4 +55,4 @@ json UI_slider::to_json() const j["max"] = this->m_max; j["value"] = this->m_value; return j; -} \ No newline at end of file +} diff --git a/src/UI/UI_slider.hpp b/src/UI/UI_slider.hpp index 2266bb8..2bc0bde 100644 --- a/src/UI/UI_slider.hpp +++ b/src/UI/UI_slider.hpp @@ -5,10 +5,10 @@ class UI_slider : public UI_item { + Q_OBJECT public: - UI_slider(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, double value, - std::function on_change = nullptr); - UI_slider(const json &j); + UI_slider(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, double value, QObject* parrent = nullptr); + UI_slider(const json &j, QObject* parrent = nullptr); void set_value(double value) override; double value() const; @@ -18,11 +18,11 @@ class UI_slider : public UI_item void from_json(const json &j) override; json to_json() const override; + signals: + void on_change(std::string id, double value); private: double m_min; double m_max; double m_value; - - std::function m_on_change; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_textbox.cpp b/src/UI/UI_textbox.cpp index e64f1b3..2e092ba 100644 --- a/src/UI/UI_textbox.cpp +++ b/src/UI/UI_textbox.cpp @@ -1,12 +1,11 @@ #include "UI_textbox.hpp" -UI_textbox::UI_textbox(std::string id, std::string hint, Color fg_color, Color bg_color, - std::function on_change) - : UI_item(id, UI_TEXTBOX_TYPE, "", 0, fg_color, bg_color), m_hint(hint), m_on_change(on_change) +UI_textbox::UI_textbox(std::string id, std::string hint, Color fg_color, Color bg_color, QObject* parrent) + : UI_item(id, UI_TEXTBOX_TYPE, "", 0, fg_color, bg_color, parrent), m_hint(hint) { } -UI_textbox::UI_textbox(const json &j) : UI_item(UI_TEXTBOX_TYPE), m_on_change(nullptr) +UI_textbox::UI_textbox(const json &j, QObject* parrent) : UI_item(UI_TEXTBOX_TYPE, parrent) { from_json(j); } @@ -16,15 +15,14 @@ void UI_textbox::set_text(std::string text) if (text == m_text) return; m_text = text; - if (m_on_change != nullptr) - { - std::thread([this] { m_on_change(m_id, m_text); }).detach(); - } + emit value_changed(); + emit on_change(m_id, m_text); } void UI_textbox::set_hint(std::string hint) { m_hint = hint; + emit value_changed(); } std::string UI_textbox::hint() const @@ -43,4 +41,4 @@ json UI_textbox::to_json() const json j = UI_item::to_json(); j["hint"] = m_hint; return j; -} \ No newline at end of file +} diff --git a/src/UI/UI_textbox.hpp b/src/UI/UI_textbox.hpp index 39eefb3..2a51c40 100644 --- a/src/UI/UI_textbox.hpp +++ b/src/UI/UI_textbox.hpp @@ -6,10 +6,10 @@ class UI_textbox : public UI_item { + Q_OBJECT public: - UI_textbox(std::string id, std::string hint, Color fg_color, Color bg_color, - std::function on_change = nullptr); - UI_textbox(const json &j); + UI_textbox(std::string id, std::string hint, Color fg_color, Color bg_color, QObject* parrent = nullptr); + UI_textbox(const json &j, QObject* parrent = nullptr); void set_text(std::string text) override; @@ -18,8 +18,9 @@ class UI_textbox : public UI_item void from_json(const json &j) override; json to_json() const override; + signals: + void on_change(std::string id, std::string text); private: std::string m_hint; - std::function m_on_change; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_thermo.cpp b/src/UI/UI_thermo.cpp index 99e92cc..0f461ab 100644 --- a/src/UI/UI_thermo.cpp +++ b/src/UI/UI_thermo.cpp @@ -1,12 +1,12 @@ #include "UI_thermo.hpp" UI_thermo::UI_thermo(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, - double value) - : UI_item(id, UI_THERMO_TYPE, text, 12, fg_color, bg_color), m_min(min), m_max(max), m_value(value) + double value, QObject* parrent) + : UI_item(id, UI_THERMO_TYPE, text, 12, fg_color, bg_color, parrent), m_min(min), m_max(max), m_value(value) { } -UI_thermo::UI_thermo(const json &j) : UI_item(UI_THERMO_TYPE) +UI_thermo::UI_thermo(const json &j, QObject* parrent) : UI_item(UI_THERMO_TYPE, parrent) { from_json(j); } @@ -19,6 +19,7 @@ void UI_thermo::set_value(double value) return; this->m_value = value; + emit value_changed(); } double UI_thermo::value() const @@ -39,11 +40,13 @@ double UI_thermo::max() const void UI_thermo::set_start_color(Color color) { this->m_color_map[0] = color; + emit value_changed(); } void UI_thermo::set_end_color(Color color) { this->m_color_map[1] = color; + emit value_changed(); } void UI_thermo::add_color_stop(double value, Color color) @@ -52,6 +55,7 @@ void UI_thermo::add_color_stop(double value, Color color) return; this->m_color_map[value] = color; + emit value_changed(); } void UI_thermo::add_color_stop_normalized(double value, Color color) @@ -61,6 +65,7 @@ void UI_thermo::add_color_stop_normalized(double value, Color color) double normalized = (value - m_min) / (m_max - m_min); this->m_color_map[normalized] = color; + emit value_changed(); } void UI_thermo::remove_color_stop(double value) @@ -69,6 +74,7 @@ void UI_thermo::remove_color_stop(double value) return; this->m_color_map.erase(value); + emit value_changed(); } void UI_thermo::remove_color_stop_normalized(double value) @@ -78,6 +84,7 @@ void UI_thermo::remove_color_stop_normalized(double value) double normalized = (value - m_min) / (m_max - m_min); this->m_color_map.erase(normalized); + emit value_changed(); } void UI_thermo::from_json(const json &j) @@ -111,4 +118,4 @@ json UI_thermo::to_json() const j["color_map"].push_back(item); } return j; -} \ No newline at end of file +} diff --git a/src/UI/UI_thermo.hpp b/src/UI/UI_thermo.hpp index 64c9a6a..34c57a5 100644 --- a/src/UI/UI_thermo.hpp +++ b/src/UI/UI_thermo.hpp @@ -6,9 +6,10 @@ // TODO: Add color scale class UI_thermo : public UI_item { + Q_OBJECT public: - UI_thermo(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, double value); - UI_thermo(const json &j); + UI_thermo(std::string id, std::string text, Color fg_color, Color bg_color, double min, double max, double value, QObject* parrent = nullptr); + UI_thermo(const json &j, QObject* parrent = nullptr); void set_value(double value) override; double value() const; @@ -31,4 +32,4 @@ class UI_thermo : public UI_item double m_max; double m_value; std::unordered_map m_color_map; -}; \ No newline at end of file +}; diff --git a/src/UI/UI_x_boxes.cpp b/src/UI/UI_x_boxes.cpp index 55ee526..150a3c0 100644 --- a/src/UI/UI_x_boxes.cpp +++ b/src/UI/UI_x_boxes.cpp @@ -1,12 +1,12 @@ #include "UI_x_boxes.hpp" -UI_x_boxes::UI_x_boxes(std::string type) : UI_item(type), m_options() +UI_x_boxes::UI_x_boxes(std::string type, QObject* parrent) : UI_item(type, parrent), m_options() { } UI_x_boxes::UI_x_boxes(std::string id, std::string type, std::string text, Color fg_color, Color bg_color, - std::vector options) - : UI_item(id, type, text, 12, fg_color, bg_color), m_options(options) + std::vector options, QObject* parrent) + : UI_item(id, type, text, 12, fg_color, bg_color, parrent), m_options(options) { } @@ -28,4 +28,4 @@ json UI_x_boxes::to_json() const json j = UI_item::to_json(); j["options"] = this->m_options; return j; -} \ No newline at end of file +} diff --git a/src/UI/UI_x_boxes.hpp b/src/UI/UI_x_boxes.hpp index 6c72819..90562c6 100644 --- a/src/UI/UI_x_boxes.hpp +++ b/src/UI/UI_x_boxes.hpp @@ -4,14 +4,15 @@ class UI_x_boxes : public UI_item { + Q_OBJECT public: - UI_x_boxes(std::string type); + UI_x_boxes(std::string type, QObject* parrent = nullptr); UI_x_boxes(std::string id, std::string type, std::string text, Color fg_color, Color bg_color, - std::vector options); + std::vector options, QObject* parrent = nullptr); void from_json(const json &j) override; json to_json() const override; protected: std::vector m_options; -}; \ No newline at end of file +}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ba4ba57..0f3f10e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,11 +4,14 @@ include_directories(${GTEST_INCLUDE_DIR}) link_directories(${GTEST_LIB_DIR}) find_package(GTest REQUIRED) +find_package(Qt6 REQUIRED COMPONENTS Core) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) # tests -file(GLOB_RECURSE TEST_SOURCES "*.cpp") +file(GLOB_RECURSE TEST_SOURCES "*.[hc]pp") add_executable(tests ${TEST_SOURCES}) -target_link_libraries(tests ${LIBS}) target_compile_options(tests PRIVATE $<$: -Wall -Wextra -pedantic-errors -Wconversion -Wsign-conversion @@ -19,6 +22,7 @@ target_link_libraries(tests UI gtest GTest::gtest_main + Qt::Core ) target_include_directories(tests @@ -30,4 +34,4 @@ target_include_directories(tests ) include(GoogleTest) -gtest_discover_tests(tests) \ No newline at end of file +gtest_discover_tests(tests) diff --git a/tests/UI_button.cpp b/tests/UI_button.cpp index c512841..45dd74a 100644 --- a/tests/UI_button.cpp +++ b/tests/UI_button.cpp @@ -1,4 +1,5 @@ #include "Test.hpp" +#include TEST(UI_button, to_from_json) { @@ -10,16 +11,3 @@ TEST(UI_button, to_from_json) EXPECT_EQ(button.fg_color(), button2.fg_color()); EXPECT_EQ(button.bg_color(), button2.bg_color()); } - -void on_click(std::string id) -{ - UNUSED(id); - EXPECT_EQ(id, "button_id"); -} - -TEST(UI_button, on_click) -{ - UI_button button("button_id", "button_text", Color(255, 255, 255), Color(0, 0, 0), on_click); - button.click(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); -} \ No newline at end of file diff --git a/tests/UI_slider.cpp b/tests/UI_slider.cpp index a93c825..a7ebda0 100644 --- a/tests/UI_slider.cpp +++ b/tests/UI_slider.cpp @@ -12,23 +12,8 @@ TEST(UI_slider, to_from_json) EXPECT_EQ(slider.value(), slider2.value()); } -static double val = 0; -void on_change(std::string id, double value) -{ - EXPECT_EQ(id, "slider_id"); - val = value; -} - -TEST(UI_slider, set_value) -{ - UI_slider slider("slider_id", "slider_text", Color(255, 255, 255), Color(0, 0, 0), 0, 100, 50, on_change); - slider.set_value(75); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - EXPECT_EQ(val, 75); -} - TEST(UI_slider, value) { - UI_slider slider("slider_id", "slider_text", Color(255, 255, 255), Color(0, 0, 0), 0, 100, 50, on_change); + UI_slider slider("slider_id", "slider_text", Color(255, 255, 255), Color(0, 0, 0), 0, 100, 50); EXPECT_EQ(slider.value(), 50); } diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index cee8267..dc8d0d6 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,18 +1,29 @@ -add_executable(server) +if (ANDROID) + message(FATAL_ERROR "This project cannot be built on Android.") +endif() + +set(CMAKE_AUTOMOC ON) + +find_package(Qt6 REQUIRED COMPONENTS WebSockets) + +qt_add_executable(server) include(FetchContent) FetchContent_Declare(magic_enum URL https://github.com/Neargye/magic_enum/releases/download/v0.9.6/magic_enum-v0.9.6.tar.gz DOWNLOAD_EXTRACT_TIMESTAMP TRUE) FetchContent_MakeAvailable(magic_enum) -file(GLOB_RECURSE SIM_SOURCES "simulators/*.cpp") +file(GLOB_RECURSE SIM_SOURCES "simulators/*.[hc]pp") target_sources(server PRIVATE ${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/message_parser.cpp ${CMAKE_CURRENT_LIST_DIR}/simulator_base.cpp + ${CMAKE_CURRENT_LIST_DIR}/simulator_base.hpp ${CMAKE_CURRENT_LIST_DIR}/simulators.cpp + ${CMAKE_CURRENT_LIST_DIR}/simulators.hpp ${CMAKE_CURRENT_LIST_DIR}/websocket.cpp + ${CMAKE_CURRENT_LIST_DIR}/websocket.hpp ${SIM_SOURCES} ) @@ -25,12 +36,12 @@ target_include_directories(server ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/simulators PRIVATE - ${PROJECT_DIR}/external/websocketpp ) target_link_libraries(server PRIVATE + Qt::WebSockets UI Timer magic_enum::magic_enum spdlog -) \ No newline at end of file +) diff --git a/tools/main.cpp b/tools/main.cpp index 9c727cb..5887b20 100644 --- a/tools/main.cpp +++ b/tools/main.cpp @@ -1,58 +1,15 @@ -#include -#include -#include -#include +#include +#include +#include #include "spdlog/async.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/spdlog.h" -#include "message_parser.hpp" #include "simulators.hpp" #include "websocket.hpp" -class Spdlog_buffer : public std::stringbuf -{ - public: - Spdlog_buffer(std::function log_function) : log_function(log_function) - { - } - int sync() - { - std::string str = this->str(); - str.pop_back(); - log_function(str); - this->str(""); - return 0; - } - - private: - std::function log_function; -}; - -Simulators *g_simulators; -Websocket *g_web_socket; -Spdlog_buffer g_info_buffer([](std::string message) { spdlog::info(message); }); -Spdlog_buffer g_error_buffer([](std::string message) { spdlog::error(message); }); -std::ostream g_out(&g_info_buffer); -std::ostream g_err(&g_error_buffer); - -void int_handler(int s) -{ - UNUSED(s); - std::cout << std::endl; - g_out << "Caught Ctrl + C" << std::endl; - if (g_web_socket != nullptr) - g_web_socket->stop(); - if (g_simulators != nullptr) - g_simulators->stop(); - g_out << "Shutting down." << std::endl; - // delay to allow the server to stop gracefully - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - exit(1); -} - void init_logger() { spdlog::init_thread_pool(8192, 1); @@ -67,38 +24,27 @@ void init_logger() spdlog::set_level(spdlog::level::trace); } -int main(void) +int main(int argc, char *argv[]) { - // Setup the signal handler - struct sigaction sigIntHandler; + QCoreApplication a(argc, argv); - sigIntHandler.sa_handler = int_handler; - sigemptyset(&sigIntHandler.sa_mask); - sigIntHandler.sa_flags = 0; + QCommandLineParser parser; + parser.setApplicationDescription("QtWebSockets example: echoserver"); + parser.addHelpOption(); - sigaction(SIGINT, &sigIntHandler, NULL); + QCommandLineOption portOption(QStringList() << "p" << "port", + QCoreApplication::translate("main", "Port for echoserver [default: 9002]."), + QCoreApplication::translate("main", "port"), QLatin1String("9002")); + parser.addOption(portOption); + parser.process(a); + uint16_t port = parser.value(portOption).toUShort(); // Initialize the logger init_logger(); // The server starts in this thread spdlog::info("Starting server"); - Simulators simulators; - g_simulators = &simulators; + Simulators simulators(port); - Websocket web_socket( - 9002, &g_out, &g_err, [](std::string message) { return message_parser(message); }, - []() { return g_simulators->changed_UI_items().dump(); }); - g_web_socket = &web_socket; - - // Start the message processor and broadcast processor in separate threads - std::thread message_processor_thread(std::bind(&Websocket::process_messages, &web_socket)); - std::thread broadcast_processor_thred(std::bind(&Websocket::process_broadcast, &web_socket)); - - // Run the asio loop with the main thread - web_socket.run(); - - // Wait for the threads to finish (should never happen) - message_processor_thread.join(); - broadcast_processor_thred.join(); -} \ No newline at end of file + return a.exec(); +} diff --git a/tools/message_parser.cpp b/tools/message_parser.cpp index 8f2f72f..376f2b6 100644 --- a/tools/message_parser.cpp +++ b/tools/message_parser.cpp @@ -1,15 +1,13 @@ -#include "message_parser.hpp" +#include "simulators.hpp" #include #include -extern Simulators *g_simulators; - -std::string message_parser(std::string message) +QString Simulators::message_parser(QString message) { - json j = json::parse(message); + json j = json::parse(message.toStdString()); if (j.contains("command")) { - return command_parser(j["command"]).dump(); + return QString::fromStdString(command_parser(j["command"]).dump()); } else if (j.contains("event")) { @@ -22,7 +20,7 @@ std::string message_parser(std::string message) } } -json command_parser(json command) +json Simulators::command_parser(json command) { json j = "{}"_json; try @@ -35,27 +33,27 @@ json command_parser(json command) spdlog::info("Command {}: {}", type, std::string(command.at("id"))); j["response"]["type"] = type; j["response"]["UI_item"] = - g_simulators->invoke_active_simulator()->get_UI_item(command.at("id"))->to_json(); + invoke_active_simulator()->get_UI_item(command.at("id"))->to_json(); break; case Command::get_UI_elements: spdlog::info("Command {}", type); j["response"]["type"] = type; - j["response"]["UI_items"] = g_simulators->invoke_active_simulator()->get_UI_items()["UI_items"]; + j["response"]["UI_items"] = invoke_active_simulator()->get_UI_items()["UI_items"]; break; case Command::switch_simulator: spdlog::info("Command {} to {}", type, std::string(command.at("name"))); - g_simulators->switch_simulator(command.at("name")); + switch_simulator(command.at("name")); j["response"]["type"] = type; break; case Command::get_active_simulator_name: spdlog::info("Command {}", type); j["response"]["type"] = type; - j["response"]["name"] = g_simulators->active_simulator_name(); + j["response"]["name"] = active_simulator_name(); break; case Command::get_simulators: spdlog::info("Command {}", type); j["response"]["type"] = type; - j["response"]["simulators"] = g_simulators->list_simulators(); + j["response"]["simulators"] = list_simulators(); break; default: break; @@ -69,13 +67,13 @@ json command_parser(json command) return j; } -void event_handler(json event) +void Simulators::event_handler(json event) { try { std::string type = event.at("type"); Event e = magic_enum::enum_cast(type).value_or(Event::end); - if (g_simulators->invoke_active_simulator() == nullptr) + if (invoke_active_simulator() == nullptr) { spdlog::error("Event got called but there is no active simulator"); return; @@ -85,26 +83,26 @@ void event_handler(json event) { case Event::clicked: { spdlog::debug("Event {}: {}", type, std::string(event.at("id"))); - g_simulators->invoke_active_simulator()->get_UI_item(std::string(event.at("id")))->click(); + invoke_active_simulator()->get_UI_item(std::string(event.at("id")))->click(); break; } case Event::value_changed: { spdlog::debug("Event {}: {} {}", type, std::string(event.at("id")), (double)(event["value"])); - g_simulators->invoke_active_simulator() + invoke_active_simulator() ->get_UI_item(std::string(event.at("id"))) ->set_value((double)event.at("value")); break; } case Event::text_changed: { spdlog::debug("Event {}: {} {}", type, std::string(event.at("id")), std::string(event.at("text"))); - g_simulators->invoke_active_simulator() + invoke_active_simulator() ->get_UI_item(std::string(event.at("id"))) ->set_text(std::string(event.at("text"))); break; } case Event::selected: { spdlog::debug("Event {}: {} {}", type, std::string(event.at("id")), std::string(event.at("selected"))); - g_simulators->invoke_active_simulator() + invoke_active_simulator() ->get_UI_item(std::string(event.at("id"))) ->set_selected(std::string(event.at("selected"))); break; @@ -118,4 +116,4 @@ void event_handler(json event) { spdlog::error(e.what()); } -} \ No newline at end of file +} diff --git a/tools/message_parser.hpp b/tools/message_parser.hpp deleted file mode 100644 index de42f88..0000000 --- a/tools/message_parser.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "simulators.hpp" - -enum class Command -{ - get_UI_element, - get_UI_elements, - switch_simulator, - get_active_simulator_name, - get_simulators, - end, - begin = 0 -}; - -enum class Event -{ - clicked, - value_changed, - text_changed, - selected, - ui_changed, - end, - begin = 0 -}; - -std::string message_parser(std::string message); -json command_parser(json command); -void event_handler(json event); \ No newline at end of file diff --git a/tools/simulator_base.cpp b/tools/simulator_base.cpp index 173a247..1ab7999 100644 --- a/tools/simulator_base.cpp +++ b/tools/simulator_base.cpp @@ -1,7 +1,7 @@ #include "simulator_base.hpp" -Simulator_base::Simulator_base(std::string name, std::chrono::milliseconds interval) - : m_name(name), m_timer([this] { timer(); }), m_interval(interval) +Simulator_base::Simulator_base(std::string name, std::chrono::milliseconds interval, QObject* parrent) + : QObject(parrent), m_name(name), m_timer([this] { timer(); }), m_interval(interval) { } @@ -32,6 +32,7 @@ void Simulator_base::add_UI_item(UI_item *item) { throw std::runtime_error("UI item id " + item->id() + " is not unique"); } + connect(item, &UI_item::value_changed, this, [this]{emit sim_changed();}); m_UI_items_map[item->id()] = item; m_UI_items.push_back(item); } @@ -78,4 +79,4 @@ void Simulator_base::reboot() { // Reboot the simulator system("reboot"); -} \ No newline at end of file +} diff --git a/tools/simulator_base.hpp b/tools/simulator_base.hpp index ddf8e5a..3d37fd2 100644 --- a/tools/simulator_base.hpp +++ b/tools/simulator_base.hpp @@ -4,9 +4,9 @@ #include "spdlog/spdlog.h" #include "timer.hpp" -class Simulator_base +class Simulator_base : public QObject { - + Q_OBJECT public: /** * @brief Construct a new Simulator_base object @@ -14,7 +14,7 @@ class Simulator_base * @param name The name of the simulator * @param interval The interval of the timer (default is 100ms) */ - Simulator_base(std::string name, std::chrono::milliseconds interval = std::chrono::milliseconds(100)); + Simulator_base(std::string name, std::chrono::milliseconds interval = std::chrono::milliseconds(100), QObject* parrent = nullptr); /** * @brief Returns the name of the simulator @@ -82,6 +82,8 @@ class Simulator_base void shutdown(); void reboot(); + signals: + void sim_changed(); protected: std::string m_name; @@ -89,4 +91,4 @@ class Simulator_base std::map m_UI_items_map; Timer m_timer; std::chrono::milliseconds m_interval; -}; \ No newline at end of file +}; diff --git a/tools/simulators.cpp b/tools/simulators.cpp index 1233650..7aff4e8 100644 --- a/tools/simulators.cpp +++ b/tools/simulators.cpp @@ -2,11 +2,26 @@ #include "can_debugger.hpp" #include "test_sim.hpp" #include +#include -Simulators::Simulators() +Simulators::Simulators(uint16_t port, QObject* parrent) : + QObject(parrent), + m_server(new Websocket(port, this)) { + qDebug() << port; INSERT_SIMULATOR(Test_Sim); INSERT_SIMULATOR(Can_Debugger); + setup_connections(); +} + +void Simulators::setup_connections() +{ + connect(m_server, &Websocket::on_message, this, [this](QWebSocket* conn, QString message){ + QString response = message_parser(message); + if (response.isEmpty() || response == "{}") + return; + m_server->send(conn, response); + }); } std::string Simulators::active_simulator_name() const @@ -59,7 +74,13 @@ void Simulators::switch_simulator(std::string name) return; } + this->stop(); + if (!this->m_current_simulator.empty()) + { + disconnect(m_simulators.at(m_current_simulator).get()); + } this->m_current_simulator = name; + connect(m_simulators.at(m_current_simulator).get(), &Simulator_base::sim_changed, this, [this]{m_server->broadcast(QString::fromStdString(changed_UI_items().dump()));}); this->run(); } @@ -139,4 +160,4 @@ json Simulators::changed_UI_items() m_before = after; return changed; -} \ No newline at end of file +} diff --git a/tools/simulators.hpp b/tools/simulators.hpp index 5e271c7..a367e04 100644 --- a/tools/simulators.hpp +++ b/tools/simulators.hpp @@ -3,14 +3,39 @@ #include #include +#include "websocket.hpp" #include "simulator_base.hpp" #define INSERT_SIMULATOR(type) m_simulators.insert(std::make_pair(type().name(), std::make_unique())); -class Simulators +class Simulators : public QObject { + Q_OBJECT public: - Simulators(); + enum class Command + { + get_UI_element, + get_UI_elements, + switch_simulator, + get_active_simulator_name, + get_simulators, + end, + begin = 0 + }; + + enum class Event + { + clicked, + value_changed, + text_changed, + selected, + ui_changed, + end, + begin = 0 + }; + + Simulators(uint16_t port, QObject *parent = nullptr); + void setup_connections(); std::string active_simulator_name() const; void run(); void stop(); @@ -19,9 +44,15 @@ class Simulators Simulator_base *invoke_active_simulator(); json changed_UI_items(); + //parsing of websocket messages + QString message_parser(QString message); + json command_parser(json command); + void event_handler(json event); + private: + Websocket *m_server; std::map> m_simulators; std::string m_current_simulator = ""; std::mutex m_mutex; json m_before; -}; \ No newline at end of file +}; diff --git a/tools/simulators/can_debugger.hpp b/tools/simulators/can_debugger.hpp index be74cdb..9b074ae 100644 --- a/tools/simulators/can_debugger.hpp +++ b/tools/simulators/can_debugger.hpp @@ -2,6 +2,7 @@ #include "simulator_base.hpp" class Can_Debugger : public Simulator_base { + Q_OBJECT public: Can_Debugger(); void timer() override; diff --git a/tools/simulators/test_sim.cpp b/tools/simulators/test_sim.cpp index 3635f2c..fd46623 100644 --- a/tools/simulators/test_sim.cpp +++ b/tools/simulators/test_sim.cpp @@ -1,13 +1,13 @@ #include "test_sim.hpp" #include -Test_Sim::Test_Sim() : -Simulator_base("Test_Sim", std::chrono::milliseconds(1000)), -m_button("button", "Off", Color(Color::White), Color(Color::Red), [this](std::string id) { button_clicked(id); }), -m_combobox("combobox", "Combobox", Color(Color::White), Color(Color::Black), {"Option 1", "Option 2", "Option 3"}, 0, [](std::string id, std::string selected) { spdlog::info("Combobox {} selected {}", id, selected); }), +Test_Sim::Test_Sim(QObject* parrent) : +Simulator_base("Test_Sim", std::chrono::milliseconds(1000), parrent), +m_button("button", "Off", Color(Color::White), Color(Color::Red)), +m_combobox("combobox", "Combobox", Color(Color::White), Color(Color::Black), {"Option 1", "Option 2", "Option 3"}, 0), m_label("label", "Label", Color(Color::White), Color(Color::Black)), -m_checkbox("checkbox", "Checkbox", Color(Color::White), Color(Color::Black), {"Option 1", "Option 2", "Option 3"}, [](std::string id, std::vector selected) { spdlog::info("Checkbox {} selected {}", id, (selected.size() > 0 ? selected[0] : "")); }), -m_slider("slider", "Slider", Color(Color::White), Color(Color::Black), 0, 100, 0, [this](std::string id, double value) { slider_changed(id, value); }), +m_checkbox("checkbox", "Checkbox", Color(Color::White), Color(Color::Black), {"Option 1", "Option 2", "Option 3"}), +m_slider("slider", "Slider", Color(Color::White), Color(Color::Black), 0, 100, 0), m_dial("dial", "Dial", Color(Color::White), Color(Color::Black), 0, 100, 0), m_thermo("thermo", "Thermo", Color(Color::White), Color(Color::Black), 0, 100, 0), m_led("led", "Led", Color(Color::White), Color(Color::Black), Color(Color::Red)) @@ -27,6 +27,9 @@ m_led("led", "Led", Color(Color::White), Color(Color::Black), Color(Color::Red)) this->add_UI_item(&this->m_dial); this->add_UI_item(&this->m_thermo); this->add_UI_item(&this->m_led); + + QObject::connect(&m_button, &UI_button::on_click, [this](std::string id) { button_clicked(id); }); + QObject::connect(&m_slider, &UI_slider::on_change, [this](std::string id, double value) { slider_changed(id, value); }); } void Test_Sim::timer() @@ -69,4 +72,4 @@ void Test_Sim::slider_changed(std::string id, double value) this->m_thermo.set_value(value); return; } -} \ No newline at end of file +} diff --git a/tools/simulators/test_sim.hpp b/tools/simulators/test_sim.hpp index 4b19f9c..11f70a2 100644 --- a/tools/simulators/test_sim.hpp +++ b/tools/simulators/test_sim.hpp @@ -2,8 +2,9 @@ #include "simulator_base.hpp" class Test_Sim : public Simulator_base { + Q_OBJECT public: - Test_Sim(); + Test_Sim(QObject* parrent = nullptr); void timer() override; void run_at_startup() override; private: diff --git a/tools/websocket.cpp b/tools/websocket.cpp index 671256c..87fce86 100644 --- a/tools/websocket.cpp +++ b/tools/websocket.cpp @@ -1,150 +1,69 @@ #include "websocket.hpp" -#include "spdlog/spdlog.h" +#include "QtWebSockets/qwebsocketserver.h" +#include "QtWebSockets/qwebsocket.h" +#include -Websocket::Websocket(uint16_t port, std::ostream *out, std::ostream *err, - std::function on_message, std::function on_update) - : m_on_message(on_message), m_on_update(on_update), m_port(port) +Websocket::Websocket(uint16_t port, QObject *parent) : + QObject(parent), + m_pWebSocketServer(new QWebSocketServer(QStringLiteral("sim_us Server"), + QWebSocketServer::NonSecureMode, this)) { - // Set logging settings - m_server.get_alog().set_custom(out != &std::cout); - m_server.get_elog().set_custom(err != &std::cerr); - m_server.get_alog().set_ostream(out); - m_server.get_elog().set_ostream(err); - m_server.set_access_channels(websocketpp::log::alevel::connect + websocketpp::log::alevel::disconnect); - m_server.clear_access_channels(websocketpp::log::alevel::frame_payload + websocketpp::log::alevel::frame_header + - websocketpp::log::alevel::control); - - // Initialize Asio - m_server.init_asio(); - - // Register our message handler - m_server.set_open_handler(std::bind(&Websocket::on_open, this, ::_1)); - m_server.set_close_handler(std::bind(&Websocket::on_close, this, ::_1)); - m_server.set_message_handler(std::bind(&Websocket::on_message, this, ::_1, ::_2)); -} - -void Websocket::run() -{ - try - { - // listen on specified port - m_server.listen(m_port); - // Start the server accept loop - m_server.start_accept(); - // Start the ASIO io_service run loop - m_server.run(); - } - catch (websocketpp::exception const &e) - { - spdlog::critical("{}", e.what()); - exit(1); - } - catch (...) - { - spdlog::critical("other exception"); - exit(1); + if (m_pWebSocketServer->listen(QHostAddress::Any, port)) { + qDebug() << "Echoserver listening on port" << port; + connect(m_pWebSocketServer, &QWebSocketServer::newConnection, + this, &Websocket::onNewConnection); + connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &Websocket::closed); } } -void Websocket::stop() +Websocket::~Websocket() { - m_server.stop_listening(); - m_server.stop(); + m_pWebSocketServer->close(); + qDeleteAll(m_clients.begin(), m_clients.end()); } -void Websocket::on_open(websocketpp::connection_hdl hdl) +void Websocket::broadcast(QString message) { - { - std::lock_guard guard(m_action_lock); - m_actions.push(action(SUBSCRIBE, hdl)); - } - m_action_cond.notify_one(); -} - -void Websocket::on_close(websocketpp::connection_hdl hdl) -{ - { - std::lock_guard guard(m_action_lock); - m_actions.push(action(UNSUBSCRIBE, hdl)); - } - m_action_cond.notify_one(); -} - -void Websocket::on_message(websocketpp::connection_hdl hdl, Server::message_ptr msg) -{ - // return pong - if (msg->get_opcode() == websocketpp::frame::opcode::PING) - { - m_server.send(hdl, msg->get_payload(), websocketpp::frame::opcode::PONG); - return; - } - // queue message up for sending by processing thread - { - std::lock_guard guard(m_action_lock); - m_actions.push(action(MESSAGE, hdl, msg)); - } - m_action_cond.notify_one(); -} - -void Websocket::process_messages() -{ - while (1) - { - std::unique_lock lock(m_action_lock); - - while (m_actions.empty()) - { - m_action_cond.wait(lock); - } - - action a = m_actions.front(); - m_actions.pop(); - - lock.unlock(); - - switch (a.type) - { - case SUBSCRIBE: { - std::lock_guard guard(m_connection_lock); - m_connections.insert(a.hdl); - } - break; - case UNSUBSCRIBE: { - std::lock_guard guard(m_connection_lock); - m_connections.erase(a.hdl); - } - break; - case MESSAGE: { - std::string response = m_on_message(a.msg->get_payload()); - if (response.empty() || response == "{}") - { - continue; - } - m_server.send(a.hdl, response, websocketpp::frame::opcode::TEXT); - } - break; - default: - break; - } + foreach (QWebSocket* conn, m_clients) { + conn->sendTextMessage(message); } } -void Websocket::process_broadcast() +void Websocket::send(QWebSocket* conn, QString message) { - while (1) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - std::string update = m_on_update(); - if (update.empty() || update == "{}") - { - continue; - } - { - std::lock_guard guard(m_connection_lock); - for (auto it : m_connections) - { - m_server.send(it, update, websocketpp::frame::opcode::TEXT); - } - } + conn->sendTextMessage(message); +} + +void Websocket::onNewConnection() +{ + QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection(); + + connect(pSocket, &QWebSocket::textMessageReceived, this, &Websocket::processTextMessage); + connect(pSocket, &QWebSocket::binaryMessageReceived, this, &Websocket::processBinaryMessage); + connect(pSocket, &QWebSocket::disconnected, this, &Websocket::socketDisconnected); + + m_clients << pSocket; +} + +void Websocket::processTextMessage(QString message) +{ + QWebSocket *pClient = qobject_cast(sender()); + qDebug() << "Message received:" << message; + emit on_message(pClient, message); +} + +void Websocket::processBinaryMessage(QByteArray message) +{ + QWebSocket *pClient = qobject_cast(sender()); + qDebug() << "Binary Message received:" << message; +} + +void Websocket::socketDisconnected() +{ + QWebSocket *pClient = qobject_cast(sender()); + qDebug() << "socketDisconnected:" << pClient; + if (pClient) { + m_clients.removeAll(pClient); + pClient->deleteLater(); } -} \ No newline at end of file +} diff --git a/tools/websocket.hpp b/tools/websocket.hpp index 2145ce2..0d0118f 100644 --- a/tools/websocket.hpp +++ b/tools/websocket.hpp @@ -1,69 +1,33 @@ #pragma once -#include -#include +#include +#include +#include -#include -#include +QT_FORWARD_DECLARE_CLASS(QWebSocketServer) +QT_FORWARD_DECLARE_CLASS(QWebSocket) -typedef websocketpp::server Server; - -using websocketpp::lib::placeholders::_1; -using websocketpp::lib::placeholders::_2; - -/* on_open insert connection_hdl into channel - * on_close remove connection_hdl from channel - * on_message queue send to all channels - */ - -enum action_type +class Websocket : public QObject { - SUBSCRIBE, - UNSUBSCRIBE, - MESSAGE, - BROADCAST -}; + Q_OBJECT -struct action -{ - action(action_type t, websocketpp::connection_hdl h) : type(t), hdl(h) - { - } - action(action_type t, websocketpp::connection_hdl h, Server::message_ptr m) : type(t), hdl(h), msg(m) - { - } - - action_type type; - websocketpp::connection_hdl hdl; - Server::message_ptr msg; -}; - -class Websocket -{ public: - Websocket(uint16_t port, std::ostream *out, std::ostream *err, std::function on_message, - std::function on_update); - void run(); - void stop(); - void process_messages(); - void process_broadcast(); + Websocket(uint16_t port, QObject *parent = nullptr); + ~Websocket(); + + void broadcast(QString message); + void send(QWebSocket* conn, QString message); + signals: + void on_message(QWebSocket* conn, QString message); + void closed(); + + private slots: + void onNewConnection(); + void processTextMessage(QString message); + void processBinaryMessage(QByteArray message); + void socketDisconnected(); private: - void on_open(websocketpp::connection_hdl hdl); - void on_close(websocketpp::connection_hdl hdl); - void on_message(websocketpp::connection_hdl hdl, Server::message_ptr msg); - - private: - std::function m_on_message; - std::function m_on_update; - Server m_server; - uint16_t m_port; - typedef std::set> con_list; - con_list m_connections; - - std::queue m_actions; - - std::mutex m_action_lock; - std::mutex m_connection_lock; - std::condition_variable m_action_cond; -}; \ No newline at end of file + QWebSocketServer *m_pWebSocketServer; + QList m_clients; +};