diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 2b40094..9156b9b 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,5 +1,5 @@ -find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets Svg WebSockets) -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Svg WebSockets) +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets Svg Protobuf Grpc) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Svg Protobuf Grpc) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) @@ -19,19 +19,16 @@ set(PROJECT_SOURCES helpers.hpp ui_base.cpp ui_base.hpp - web_socket_wrapper.cpp - web_socket_wrapper.hpp ) qt_add_executable(unisim_client ${PROJECT_SOURCES} - ) +) set_target_properties( unisim_client PROPERTIES AUTOMOC ON - AUTORCC ON AUTOUIC ON ) @@ -43,11 +40,11 @@ target_include_directories(unisim_client ${PROJECT_BINARY_DIR}/ ) -target_link_libraries(unisim_client PUBLIC Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::WebSockets spdlog magic_enum::magic_enum nlohmann_json::nlohmann_json git_tag qwt UI_enum) +target_link_libraries(unisim_client PUBLIC Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Protobuf Qt${QT_VERSION_MAJOR}::Grpc spdlog magic_enum::magic_enum qwt git_tag proto-ui-client) include(GNUInstallDirs) install(TARGETS unisim_client BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) +) \ No newline at end of file diff --git a/client/factory.hpp b/client/factory.hpp index 8817475..ded5171 100644 --- a/client/factory.hpp +++ b/client/factory.hpp @@ -9,7 +9,7 @@ class UIFactory { public: - using Creator = std::function; + using Creator = std::function; static UIFactory &instance() { @@ -25,12 +25,12 @@ class UIFactory return true; } - UI_base *create(const QString &key, Web_socket_wrapper *w, QWidget *parent = nullptr) const + UI_base *create(const QString &key, RemoteUiService::Client *s, QWidget *parent = nullptr) const { auto it = m_creators.constFind(key); if (it == m_creators.cend()) return nullptr; - return it.value()(w, parent); + return it.value()(s, parent); } QStringList keys() const @@ -50,7 +50,7 @@ class UIFactory Register##TYPE() \ { \ UIFactory::instance().registerType(TYPE::name(), \ - [](Web_socket_wrapper *w, QWidget *p) { return new TYPE(w, p); }); \ + [](RemoteUiService::Client *s, QWidget *p) { return new TYPE(s, p); }); \ } \ }; \ static Register##TYPE register_##TYPE; \ diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index e6a788a..5b6f687 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -3,33 +3,35 @@ #include "git_version.h" #include "ui_base.hpp" #include "ui_mainwindow.h" -#include "web_socket_wrapper.hpp" +#include "messages_client.grpc.qpb.h" #include #include #include #include +#include #include #include +#include +#include +#include +#include -MainWindow::MainWindow(QUrl ws_url, QString sim, QWidget *parent) +typedef UI_base *(*Get_UI)(RemoteUiService::Client *, QWidget *); + +MainWindow::MainWindow(QUrl server_url, QString sim, QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), m_error(new QMessageBox()), - m_web_socket(new Web_socket_wrapper(ws_url)) + m_remote_ui_client(new RemoteUiService::Client()), m_ping(new QTimer()) { ui->setupUi(this); ui->statusbar->showMessage(QString("Version: %1-%2%3") .arg(Git_version::branch) .arg(Git_version::shortSha1) .arg(Git_version::dirty ? "-dirty" : "")); - connect(m_web_socket.get(), &Web_socket_wrapper::log_signal, this, - [](const char *filename_in, int line_in, const char *funcname_in, int level, QString msg) { - if (spdlog::default_logger_raw()->should_log((spdlog::level::level_enum)level)) - spdlog::default_logger_raw()->log(spdlog::source_loc{filename_in, line_in, funcname_in}, - (spdlog::level::level_enum)level, msg.toStdString()); - }); + m_ping->setInterval(std::chrono::milliseconds(1000)); for (const QString &name : UIFactory::instance().keys()) { - if (auto *w = UIFactory::instance().create(name, m_web_socket.get(), this)) + if (auto *w = UIFactory::instance().create(name, m_remote_ui_client.get(), this)) { w->set_name(name); m_sims.insert(std::make_pair(name, w)); @@ -42,9 +44,6 @@ MainWindow::MainWindow(QUrl ws_url, QString sim, QWidget *parent) } } - // Insert debug sims here - // INSERT_SIMULATOR(SIM_NAME); - setWindowTitle("Unisim cpp client"); m_error->setIcon(QMessageBox::Critical); m_error->setWindowTitle("Critical"); @@ -52,12 +51,42 @@ MainWindow::MainWindow(QUrl ws_url, QString sim, QWidget *parent) QPushButton *exit_app = m_error->addButton("Exit Application", QMessageBox::AcceptRole); m_error->addButton("Close", QMessageBox::RejectRole); connect(exit_app, &QPushButton::clicked, this, &QCoreApplication::quit, Qt::QueuedConnection); - ui->connection->setText("Connected to " + ws_url.toString()); + ui->connection->setText("Connected to " + server_url.toString()); // Default sim if (!sim.isEmpty()) default_sim(sim); + QSslConfiguration ssl_config = QSslConfiguration::defaultConfiguration(); + + // Required for HTTP/2 + ssl_config.setAllowedNextProtocols({ "h2" }); + + // Disable certificate verification + ssl_config.setPeerVerifyMode(QSslSocket::VerifyNone); + + QGrpcChannelOptions options; + options.setSslConfiguration(ssl_config); + + server_url.setScheme("https"); + auto channel = std::make_shared(server_url, options); + m_remote_ui_client->attachChannel(channel); + connect(ui->pushButton, &QPushButton::clicked, this, [=, this] { open_sim(ui->comboBox->currentText()); }); + connect(m_ping, &QTimer::timeout, this, [this]{ +// TODO: find a way to check if the channel is open + }); + connect(this, &MainWindow::on_show_event, this, [this]{ + auto reply = m_remote_ui_client->Command_get_simulators(Empty()); + connect(reply.get(), &QGrpcCallReply::finished, this, [this, reply = reply.get()](const QGrpcStatus &status) { + auto response = reply->read(); + if (!response.has_value() || !status.isOk()) + { + SPDLOG_CRITICAL("Command get simulators has got no response"); + return; + } + pase_sim_names(response.value()); + }, Qt::SingleShotConnection); + }, Qt::SingleShotConnection); } MainWindow::~MainWindow() @@ -68,19 +97,14 @@ MainWindow::~MainWindow() void MainWindow::showEvent(QShowEvent *event) { QWidget::showEvent(event); - connect(m_web_socket.get(), &Web_socket_wrapper::on_command_cb, this, [=, this](json &j) { on_cmd_cb(j); }); - connect(m_web_socket.get(), &Web_socket_wrapper::on_connected, this, - [=, this] { m_web_socket->send_command(Web_socket_wrapper::Command::get_simulators); }); - connect(m_web_socket.get(), &Web_socket_wrapper::on_closed, this, [=, this] { - m_error->open(); - ui->connection->setText("Could not connect to the server"); - }); + emit on_show_event(); + m_ping->start(); } void MainWindow::hideEvent(QHideEvent *event) { QWidget::hideEvent(event); - disconnect(m_web_socket.get(), nullptr, nullptr, nullptr); + disconnect(m_remote_ui_client.get(), nullptr, nullptr, nullptr); } void MainWindow::default_sim(QString name) @@ -89,12 +113,11 @@ void MainWindow::default_sim(QString name) this->m_default_sim = true; } -void MainWindow::pase_sim_names(json &sims) +void MainWindow::pase_sim_names(Simulators_m &sims) { - for (json &item : sims) + for (auto &item : sims.simulator()) { - if (m_sims.contains(QString::fromStdString(item))) - ui->comboBox->addItem(QString::fromStdString(item)); + ui->comboBox->addItem(item); } QThread::msleep(30); @@ -119,7 +142,18 @@ void MainWindow::open_sim(QString sim_name) return; } - m_web_socket->send_command(Web_socket_wrapper::Command::get_active_simulator_name); + auto cmd = Simulator_m(); + cmd.setSimuator(sim_name); + auto reply = m_remote_ui_client->Command_switch_simulator(cmd); + connect(reply.get(), &QGrpcCallReply::finished, this, [this, reply = reply.get()] { + auto response = reply->read(); + if (!response.has_value()) + { + qDebug("Switch simulator deserialization failed"); + return; + } + open_sim_window(); + }, Qt::SingleShotConnection); } void MainWindow::open_sim_window(void) @@ -130,42 +164,23 @@ void MainWindow::open_sim_window(void) void MainWindow::check_active_sim(QString name) { - if (name == "") - { - SPDLOG_DEBUG("Name not set"); - m_web_socket->send_command(Web_socket_wrapper::Command::switch_simulator, m_selected_sim_name); - return; - } - - SPDLOG_DEBUG("Current sim:", name); - if (name == m_selected_sim_name) { SPDLOG_DEBUG("Name set and the same"); open_sim_window(); return; } - - SPDLOG_DEBUG("Name set and wrong"); - m_web_socket->send_command(Web_socket_wrapper::Command::switch_simulator, m_selected_sim_name); -} - -void MainWindow::on_cmd_cb(json &j) -{ - auto response = magic_enum::enum_cast(std::string(j.at("type"))) - .value_or(Web_socket_wrapper::Command::end); - switch (response) - { - case Web_socket_wrapper::Command::get_simulators: - pase_sim_names(j.at("simulators")); - break; - case Web_socket_wrapper::Command::get_active_simulator_name: - check_active_sim(QString::fromStdString(j.at("name"))); - break; - case Web_socket_wrapper::Command::switch_simulator: + + auto cmd = Simulator_m(); + cmd.setSimuator(m_selected_sim_name); + auto reply = m_remote_ui_client->Command_switch_simulator(cmd); + connect(reply.get(), &QGrpcCallReply::finished, this, [this, reply = reply.get()] { + auto response = reply->read(); + if (!response.has_value()) + { + qDebug("Switch simulator deserialization failed"); + return; + } open_sim_window(); - break; - default: - break; - } + }, Qt::SingleShotConnection); } diff --git a/client/mainwindow.hpp b/client/mainwindow.hpp index 5e410b0..f76d552 100644 --- a/client/mainwindow.hpp +++ b/client/mainwindow.hpp @@ -1,16 +1,23 @@ #pragma once -#include "nlohmann/json.hpp" #include #include - -using json = nlohmann::json; +#include "messages.qpb.h" #define INSERT_SIMULATOR(type) \ m_sims.insert(std::make_pair(type().sim_name(), std::make_unique(m_web_socket.get(), this))) Q_FORWARD_DECLARE_OBJC_CLASS(QMessageBox); -Q_FORWARD_DECLARE_OBJC_CLASS(Web_socket_wrapper); Q_FORWARD_DECLARE_OBJC_CLASS(UI_base); +Q_FORWARD_DECLARE_OBJC_CLASS(QTimer); +namespace RemoteUiService +{ + class Client; +} + +namespace Health +{ +class Client; +} namespace Ui { @@ -32,6 +39,8 @@ class MainWindow : public QMainWindow explicit MainWindow(QUrl ws_url, QString sim, QWidget *parent = nullptr); ~MainWindow(); + signals: + void on_show_event(); private: /** * @brief Handle the show event @@ -59,7 +68,7 @@ class MainWindow : public QMainWindow * * @param sims The JSON array of simulator names */ - void pase_sim_names(json &sims); + void pase_sim_names(Simulators_m &sims); /** * @brief Open the simulator window using the selected simulator */ @@ -78,18 +87,12 @@ class MainWindow : public QMainWindow */ void open_sim(QString sim_name); - /** - * @brief Handle command callbacks from the server - * - * @param j The JSON command - */ - void on_cmd_cb(json &j); - bool m_default_sim = false; QString m_selected_sim_name; QMainWindow *m_selected_sim; QMessageBox *m_error; Ui::MainWindow *ui; - std::unique_ptr m_web_socket; + std::unique_ptr m_remote_ui_client; std::map> m_sims; + QTimer *m_ping; }; diff --git a/client/simulators/can_debugger.cpp b/client/simulators/can_debugger.cpp index 1e58f0b..3a17d68 100644 --- a/client/simulators/can_debugger.cpp +++ b/client/simulators/can_debugger.cpp @@ -4,7 +4,7 @@ REGISTER_UI(Can_Debugger); -Can_Debugger::Can_Debugger(Web_socket_wrapper *web_socket, QWidget *parent) +Can_Debugger::Can_Debugger(RemoteUiService::Client *web_socket, QWidget *parent) : UI_base(web_socket, parent), ui(new Ui::Can_Debugger) { ui->setupUi(this); diff --git a/client/simulators/can_debugger.hpp b/client/simulators/can_debugger.hpp index 5a5c185..1d7167f 100644 --- a/client/simulators/can_debugger.hpp +++ b/client/simulators/can_debugger.hpp @@ -11,7 +11,7 @@ class Can_Debugger : public UI_base Q_OBJECT public: - explicit Can_Debugger(Web_socket_wrapper *web_socket = nullptr, QWidget *parent = nullptr); + explicit Can_Debugger(RemoteUiService::Client *web_socket = nullptr, QWidget *parent = nullptr); ~Can_Debugger(); static QString name() { diff --git a/client/simulators/test.cpp b/client/simulators/test.cpp index de8a779..ace361f 100644 --- a/client/simulators/test.cpp +++ b/client/simulators/test.cpp @@ -4,7 +4,7 @@ REGISTER_UI(Test_Sim); -Test_Sim::Test_Sim(Web_socket_wrapper *web_socket, QWidget *parent) : UI_base(web_socket, parent), ui(new Ui::Test_Sim) +Test_Sim::Test_Sim(RemoteUiService::Client *web_socket, QWidget *parent) : UI_base(web_socket, parent), ui(new Ui::Test_Sim) { ui->setupUi(this); diff --git a/client/simulators/test.hpp b/client/simulators/test.hpp index 72f9050..684d482 100644 --- a/client/simulators/test.hpp +++ b/client/simulators/test.hpp @@ -11,7 +11,7 @@ class Test_Sim : public UI_base Q_OBJECT public: - explicit Test_Sim(Web_socket_wrapper *web_socket = nullptr, QWidget *parent = nullptr); + explicit Test_Sim(RemoteUiService::Client *web_socket = nullptr, QWidget *parent = nullptr); ~Test_Sim(); static QString name() diff --git a/client/ui/can_transceive.cpp b/client/ui/can_transceive.cpp index 649466d..7403e3f 100644 --- a/client/ui/can_transceive.cpp +++ b/client/ui/can_transceive.cpp @@ -15,11 +15,11 @@ Can_Transceive::Can_Transceive(QWidget *parent) : QWidget(parent), ui(new Ui::Ca connect(ui->b_can_send_send, &QPushButton::clicked, this, [=, this] { emit send_can_message( ui->s_can_send_sid->value(), ui->c_can_send_dlc->currentIndex(), - std::array( - {(uint8_t)(can_send_hexspinboxes[0]->value()), (uint8_t)(can_send_hexspinboxes[1]->value()), - (uint8_t)(can_send_hexspinboxes[2]->value()), (uint8_t)(can_send_hexspinboxes[3]->value()), - (uint8_t)(can_send_hexspinboxes[4]->value()), (uint8_t)(can_send_hexspinboxes[5]->value()), - (uint8_t)(can_send_hexspinboxes[6]->value()), (uint8_t)(can_send_hexspinboxes[7]->value())})); + QByteArray().append((uint8_t)(can_send_hexspinboxes[0]->value())).append((uint8_t)(can_send_hexspinboxes[0]->value())) + .append((uint8_t)(can_send_hexspinboxes[1]->value())).append((uint8_t)(can_send_hexspinboxes[2]->value())) + .append((uint8_t)(can_send_hexspinboxes[3]->value())).append((uint8_t)(can_send_hexspinboxes[4]->value())) + .append((uint8_t)(can_send_hexspinboxes[5]->value())).append((uint8_t)(can_send_hexspinboxes[6]->value())) + .append((uint8_t)(can_send_hexspinboxes[7]->value()))); }); connect(ui->b_clear, &QPushButton::clicked, this, [=, this] { emit can_clear(); }); } @@ -48,7 +48,7 @@ void Can_Transceive::clear() ui->send_list->setRowCount(0); } -void Can_Transceive::add_send_item(uint32_t id, uint8_t dlc, std::array payload) +void Can_Transceive::add_send_item(uint32_t id, uint8_t dlc, QByteArray payload) { size_t rc = ui->send_list->rowCount(); ui->send_list->setRowCount(rc + 1); @@ -62,7 +62,7 @@ void Can_Transceive::add_send_item(uint32_t id, uint8_t dlc, std::arraysend_list->resizeColumnsToContents(); } -void Can_Transceive::add_receive_item(uint32_t id, uint8_t dlc, std::array payload) +void Can_Transceive::add_receive_item(uint32_t id, uint8_t dlc, QByteArray payload) { size_t rc = ui->recvd_list->rowCount(); ui->recvd_list->setRowCount(rc + 1); diff --git a/client/ui/can_transceive.hpp b/client/ui/can_transceive.hpp index e6e8fc7..c4f67a1 100644 --- a/client/ui/can_transceive.hpp +++ b/client/ui/can_transceive.hpp @@ -47,7 +47,7 @@ class Can_Transceive : public QWidget * @param dlc The Data Length Code * @param payload The CAN message payload */ - void add_send_item(uint32_t id, uint8_t dlc, std::array payload); + void add_send_item(uint32_t id, uint8_t dlc, QByteArray payload); /** * @brief Add a received CAN message to the receive list @@ -56,7 +56,7 @@ class Can_Transceive : public QWidget * @param dlc The Data Length Code * @param payload The CAN message payload */ - void add_receive_item(uint32_t id, uint8_t dlc, std::array payload); + void add_receive_item(uint32_t id, uint8_t dlc, QByteArray payload); signals: /** @@ -66,7 +66,7 @@ class Can_Transceive : public QWidget * @param dlc The Data Length Code * @param payload The CAN message payload */ - void send_can_message(uint32_t id, uint8_t dlc, std::array payload); + void send_can_message(uint32_t id, uint8_t dlc, QByteArray payload); /** * @brief Emitted when the CAN message lists are to be cleared diff --git a/client/ui_base.cpp b/client/ui_base.cpp index 67842c3..a031621 100644 --- a/client/ui_base.cpp +++ b/client/ui_base.cpp @@ -3,12 +3,12 @@ #include "helpers.hpp" #include "led.hpp" #include "plot_wrapper.hpp" -#include "web_socket_wrapper.hpp" #include #include +#include "messages_client.grpc.qpb.h" -UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) - : QMainWindow{parent}, m_timer_update(new QTimer()), m_error(new QMessageBox(this)), m_web_socket(web_socket) +UI_base::UI_base(RemoteUiService::Client *remote_ui_client, QWidget *parent) + : QMainWindow{parent}, m_timer_update(new QTimer()), m_error(new QMessageBox(this)), m_remote_ui_client(remote_ui_client) { this->setWindowState(Qt::WindowMaximized); m_ui_lookup.reserve(40); @@ -19,7 +19,8 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) QPushButton *exit_app = m_error->addButton("Exit Application", QMessageBox::AcceptRole); m_error->addButton("Close", QMessageBox::RejectRole); connect(exit_app, &QPushButton::clicked, this, &QCoreApplication::quit, Qt::QueuedConnection); - m_process_lookup.insert(UI_enum::ui_label, [=, this](json &ui_item, QWidget *widget) { + + m_process_lookup.insert(UI_typeGadget::UI_type::ui_label, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto label = qobject_cast(widget); if (label == nullptr) { @@ -27,9 +28,9 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - QString value = QString::fromStdString(ui_item.at("text")); - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); + QString value = ui_item.label().text(); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); if (label->isEnabled() != enabled) { @@ -47,8 +48,8 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) } }); - m_process_lookup.insert(UI_enum::ui_slider, [=, this](json &ui_item, QWidget *widget) { - QLabel *label = id_to_label(id_to_ui(ui_item.at("id"))); + m_process_lookup.insert(UI_typeGadget::UI_type::ui_slider, [=, this](const UI_item_m &ui_item, QWidget *widget) { + QLabel *label = id_to_label(id_to_ui(ui_item.id_proto())); auto slider = qobject_cast(widget); if (slider == nullptr) { @@ -56,10 +57,10 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - double value = ui_item.at("value"); - bool enable = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); - QString unit = QString::fromStdString(ui_item.at("unit")); + double value = ui_item.range().value(); + bool enable = ui_item.enabled(); + bool visible = ui_item.visible(); + QString unit = ui_item.range().unit(); if (label != nullptr) { @@ -82,8 +83,8 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) } // TODO: Add color for scale }); - m_process_lookup.insert(UI_enum::ui_dial, [=, this](json &ui_item, QWidget *widget) { - QLabel *label = id_to_label(id_to_ui(ui_item.at("id"))); + m_process_lookup.insert(UI_typeGadget::UI_type::ui_dial, [=, this](const UI_item_m &ui_item, QWidget *widget) { + QLabel *label = id_to_label(id_to_ui(ui_item.id_proto())); auto dial = qobject_cast(widget); if (dial == nullptr) { @@ -91,9 +92,9 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - double value = ui_item.at("value"); - bool visible = ui_item.at("visible"); - QString unit = QString::fromStdString(ui_item.at("unit")); + double value = ui_item.range().value(); + bool visible = ui_item.visible(); + QString unit = ui_item.range().unit(); if (label != nullptr) { @@ -110,8 +111,8 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) dial->setVisible(visible); } }); - m_process_lookup.insert(UI_enum::ui_thermo, [=, this](json &ui_item, QWidget *widget) { - QLabel *label = id_to_label(id_to_ui(ui_item.at("id"))); + m_process_lookup.insert(UI_typeGadget::UI_type::ui_thermo, [=, this](const UI_item_m &ui_item, QWidget *widget) { + QLabel *label = id_to_label(id_to_ui(ui_item.id_proto())); auto thermo = qobject_cast(widget); if (thermo == nullptr) { @@ -119,9 +120,9 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - double value = ui_item.at("value"); - bool visible = ui_item.at("visible"); - QString unit = QString::fromStdString(ui_item.at("unit")); + double value = ui_item.range().value(); + bool visible = ui_item.visible(); + QString unit = ui_item.range().unit(); if (label != nullptr) { @@ -138,7 +139,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) thermo->setVisible(visible); } }); - m_process_lookup.insert(UI_enum::ui_textbox, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_textbox, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto line_edit = qobject_cast(widget); if (line_edit == nullptr) { @@ -146,10 +147,10 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - bool visible = ui_item.at("visible"); - bool enabled = ui_item.at("enabled"); - QString hint = QString::fromStdString(ui_item.at("hint")); - QString text = QString::fromStdString(ui_item.at("text")); + bool visible = ui_item.visible(); + bool enabled = ui_item.enabled(); + QString hint = ui_item.textbox().hint(); + QString text = ui_item.textbox().text(); if (line_edit->isVisible() != visible) { @@ -169,7 +170,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) line_edit->setText(text); } }); - m_process_lookup.insert(UI_enum::ui_combobox, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_combobox, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto combobox = qobject_cast(widget); if (combobox == nullptr) { @@ -177,9 +178,9 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - size_t selected = ui_item.at("selected"); - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); + size_t selected = ui_item.combobox().selected(); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); if (combobox->isEnabled() != enabled) { @@ -191,10 +192,10 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) combobox->setVisible(visible); } - for (json &item : ui_item.at("options")) + for (const QString &item : ui_item.combobox().options()) { - if (combobox->findText(QString::fromStdString(item)) == -1) - combobox->addItem(QString::fromStdString(item)); + if (combobox->findText(item) == -1) + combobox->addItem(item); } if (combobox->currentIndex() != selected) @@ -202,9 +203,9 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) combobox->setCurrentIndex(selected); } }); - // m_process_lookup.insert(UI_enum::ui_radiobutton, - // [=, this](json &ui_item, QWidget *widget) { LOG_CRITICAL("Not implemented"); }); - m_process_lookup.insert(UI_enum::ui_checkbox, [=, this](json &ui_item, QWidget *widget) { + // m_process_lookup.insert(UI_typeGadget::UI_type::ui_radiobutton, + // [=, this](const UI_item_m &ui_item, QWidget *widget) { LOG_CRITICAL("Not implemented"); }); + m_process_lookup.insert(UI_typeGadget::UI_type::ui_checkbox, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto checkbox = qobject_cast(widget); if (checkbox == nullptr) { @@ -212,12 +213,12 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - QColor bg_color = QColor(QString::fromStdString(ui_item.at("bg_color"))); - QColor fg_color = QColor(QString::fromStdString(ui_item.at("fg_color"))); - QString text = QString::fromStdString(ui_item.at("text")); - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); - bool checked = ui_item.at("checked"); + QColor bg_color = QColor(ui_item.checkbox().color().bgColor()); + QColor fg_color = QColor(ui_item.checkbox().color().fgColor()); + QString text = ui_item.checkbox().text(); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); + bool checked = ui_item.checkbox().checked(); if (checkbox->isChecked() != checked) { @@ -268,7 +269,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) set_widget_color(checkbox, fg_color, bg_color); } }); - m_process_lookup.insert(UI_enum::ui_button, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_button, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto button = qobject_cast(widget); if (button == nullptr) { @@ -276,11 +277,11 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - QColor bg_color = QColor(QString::fromStdString(ui_item.at("bg_color"))); - QColor fg_color = QColor(QString::fromStdString(ui_item.at("fg_color"))); - QString text = QString::fromStdString(ui_item.at("text")); - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); + QColor bg_color = QColor(ui_item.label().color().bgColor()); + QColor fg_color = QColor(ui_item.label().color().fgColor()); + QString text = ui_item.label().text(); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); if (button->isEnabled() != enabled) { @@ -326,7 +327,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) set_widget_color(button, fg_color, bg_color); } }); - m_process_lookup.insert(UI_enum::ui_led, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_led, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto led = qobject_cast(widget); if (led == nullptr) { @@ -334,10 +335,10 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - QColor bg_color = QColor(QString::fromStdString(ui_item.at("bg_color"))); - QColor color = QColor(QString::fromStdString(ui_item.at("fg_color"))); - QString text = QString::fromStdString(ui_item.at("text")); - bool visible = ui_item.at("visible"); + QColor bg_color = QColor(ui_item.label().color().bgColor()); + QColor color = QColor(ui_item.label().color().fgColor()); + QString text = ui_item.label().text(); + bool visible = ui_item.visible(); if (led->isVisible() != visible) { @@ -378,7 +379,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) set_widget_color(led, color, bg_color); } }); - m_process_lookup.insert(UI_enum::ui_spinbox, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_spinbox, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto spinbox = qobject_cast(widget); if (spinbox == nullptr) { @@ -386,11 +387,11 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); - double min = ui_item.at("min"); - double max = ui_item.at("max"); - double value = ui_item.at("value"); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); + double min = ui_item.range().min(); + double max = ui_item.range().max(); + double value = ui_item.range().value(); if (enabled != spinbox->isEnabled()) { @@ -415,7 +416,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) spinbox->setValue((int)value); } }); - m_process_lookup.insert(UI_enum::ui_double_spinbox, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_double_spinbox, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto spinbox = qobject_cast(widget); if (spinbox == nullptr) { @@ -423,11 +424,11 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); - double min = ui_item.at("min"); - double max = ui_item.at("max"); - double value = ui_item.at("value"); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); + double min = ui_item.range().min(); + double max = ui_item.range().max(); + double value = ui_item.range().value(); if (enabled != spinbox->isEnabled()) { @@ -452,7 +453,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) spinbox->setValue(value); } }); - m_process_lookup.insert(UI_enum::ui_hex_spinbox, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_hex_spinbox, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto spinbox = qobject_cast(widget); if (spinbox == nullptr) { @@ -460,11 +461,11 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); - double min = ui_item.at("min"); - double max = ui_item.at("max"); - double value = ui_item.at("value"); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); + double min = ui_item.range().min(); + double max = ui_item.range().max(); + double value = ui_item.range().value(); if (enabled != spinbox->isEnabled()) { @@ -489,7 +490,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) spinbox->setValue((int)value); } }); - m_process_lookup.insert(UI_enum::ui_tab_widget, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_tab_widget, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto tab_widget = qobject_cast(widget); if (tab_widget == nullptr) { @@ -497,15 +498,15 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - if (tab_widget->count() != ui_item.at("tab_names").size()) + if (tab_widget->count() != ui_item.tabWidget().tabNames().size()) { LOG_ERROR("QTabWidget has not the same amount of tabs as the server"); return; } - auto tab_names = ui_item.at("tab_names"); - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); + auto tab_names = ui_item.tabWidget().tabNames(); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); if (tab_widget->isEnabled() != enabled) { @@ -519,22 +520,22 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) for (size_t i = 0; i < tab_names.size(); i++) { - if (tab_widget->tabText(i) != QString::fromStdString(tab_names[i])) + if (tab_widget->tabText(i) != tab_names[i]) { - tab_widget->setTabText(i, QString::fromStdString(tab_names[i])); + tab_widget->setTabText(i, tab_names[i]); } - if (tab_widget->isVisible() != ui_item.at("tab_visible")[i]) + if (tab_widget->isVisible() != ui_item.tabWidget().tabVisible()[i]) { - tab_widget->setTabVisible(i, ui_item.at("tab_visible")[i]); + tab_widget->setTabVisible(i, ui_item.tabWidget().tabVisible()[i]); } } - if (tab_widget->currentIndex() != ui_item.at("selected")) + if (tab_widget->currentIndex() != ui_item.tabWidget().selecteTab()) { - tab_widget->setCurrentIndex(ui_item.at("selected")); + tab_widget->setCurrentIndex(ui_item.tabWidget().selecteTab()); } }); - m_process_lookup.insert(UI_enum::ui_stacked_widget, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_stacked_widget, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto stacked_widget = qobject_cast(widget); if (stacked_widget == nullptr) { @@ -542,14 +543,14 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - if (stacked_widget->count() != ui_item.at("tab_count")) + if (stacked_widget->count() != ui_item.stackedWidget().tabCount()) { LOG_ERROR("QStackedWidget has not the same amount of tabs as the server"); return; } - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); if (stacked_widget->isEnabled() != enabled) { @@ -561,12 +562,12 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) stacked_widget->setVisible(visible); } - if (stacked_widget->currentIndex() != ui_item.at("current_tab")) + if (stacked_widget->currentIndex() != ui_item.stackedWidget().currentTab()) { - stacked_widget->setCurrentIndex(ui_item.at("current_tab")); + stacked_widget->setCurrentIndex(ui_item.stackedWidget().currentTab()); } }); - m_process_lookup.insert(UI_enum::ui_status_bar, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_status_bar, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto status_bar = qobject_cast(widget); if (status_bar == nullptr) { @@ -574,12 +575,12 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - if (status_bar->currentMessage() != QString::fromStdString(ui_item.at("text"))) + if (status_bar->currentMessage() != ui_item.statusBar().message()) { - status_bar->showMessage(QString::fromStdString(ui_item.at("text")), ui_item.at("timeout")); + status_bar->showMessage(ui_item.statusBar().message(), ui_item.statusBar().timeout()); } }); - m_process_lookup.insert(UI_enum::ui_plot, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_plot, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto plot = qobject_cast(widget); if (plot == nullptr) { @@ -587,17 +588,17 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - QString text = QString::fromStdString(ui_item.at("text")); - QColor bg_color = QColor(QString::fromStdString(ui_item.at("bg_color"))); - QColor color = QColor(QString::fromStdString(ui_item.at("fg_color"))); - QString x_label = QString::fromStdString(ui_item.at("x_label")); - QString y_label = QString::fromStdString(ui_item.at("y_label")); + QString text = ui_item.plot().text(); + QColor bg_color = QColor(ui_item.plot().color().bgColor()); + QColor color = QColor(ui_item.plot().color().fgColor()); + QString x_label = ui_item.plot().xLabel(); + QString y_label = ui_item.plot().yLabel(); - std::vector x_vals = (std::vector)ui_item.at("x_vals"); - std::vector y_vals = (std::vector)ui_item.at("y_vals"); + QList x_vals = ui_item.plot().xVals(); + QList y_vals = ui_item.plot().yVals(); - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); if (plot->isEnabled() != enabled) { @@ -639,7 +640,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) plot->setAxisScale(QwtAxis::XBottom, x_vals[0], x_vals[x_vals.size() - 1]); plot->replot(); }); - m_process_lookup.insert(UI_enum::ui_table, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_table, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto table = qobject_cast(widget); if (table == nullptr) { @@ -647,13 +648,13 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) return; } - size_t row_count = ui_item.at("row_count"); - std::vector row_labels = (std::vector)ui_item.at("row_labels"); - size_t column_count = ui_item.at("column_count"); - std::vector column_labels = (std::vector)ui_item.at("column_labels"); - std::vector table_data = (std::vector)ui_item.at("table"); - bool enabled = ui_item.at("enabled"); - bool visible = ui_item.at("visible"); + size_t row_count = ui_item.table().rowCount(); + QList row_labels = ui_item.table().rowLabels(); + size_t column_count = ui_item.table().columnCount(); + QList column_labels = ui_item.table().columnLabels(); + QList table_data = ui_item.table().table(); + bool enabled = ui_item.enabled(); + bool visible = ui_item.visible(); if (table->isEnabled() != enabled) { @@ -683,7 +684,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) if (table->verticalHeaderItem(i) == nullptr) table->setVerticalHeaderItem(i, new QTableWidgetItem()); if (row_labels[i] != table->verticalHeaderItem(i)->text()) - table->verticalHeaderItem(i)->setText(QString::fromStdString(row_labels[i])); + table->verticalHeaderItem(i)->setText(row_labels[i]); } } if (column_count != 0) @@ -693,7 +694,7 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) if (table->horizontalHeaderItem(i) == nullptr) table->setHorizontalHeaderItem(i, new QTableWidgetItem()); if (column_labels[i] != table->horizontalHeaderItem(i)->text()) - table->horizontalHeaderItem(i)->setText(QString::fromStdString(column_labels[i])); + table->horizontalHeaderItem(i)->setText(column_labels[i]); } } for (size_t i = 0; i < table_data.size(); i++) @@ -704,11 +705,11 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) { table->setItem(row, column, new QTableWidgetItem()); } - table->item(row, column)->setText(QString::fromStdString(table_data[i])); + table->item(row, column)->setText(table_data[i]); } table->resizeColumnsToContents(); }); - m_process_lookup.insert(UI_enum::ui_can, [=, this](json &ui_item, QWidget *widget) { + m_process_lookup.insert(UI_typeGadget::UI_type::ui_can, [=, this](const UI_item_m &ui_item, QWidget *widget) { auto can_ui = qobject_cast(widget); if (can_ui == nullptr) { @@ -717,14 +718,14 @@ UI_base::UI_base(Web_socket_wrapper *web_socket, QWidget *parent) } can_ui->clear(); - for (auto &send_item : ui_item.at("send_msgs")) + for (auto &send_item : ui_item.can().sendFrames()) { - can_ui->add_send_item(send_item.at("id"), send_item.at("dlc"), send_item.at("payload")); + can_ui->add_send_item(send_item.sid(), send_item.dlc(), send_item.payload()); } - for (auto &recvd_item : ui_item.at("rcvd_msgs")) + for (auto &recvd_item : ui_item.can().recvFrames()) { - can_ui->add_receive_item(recvd_item.at("id"), recvd_item.at("dlc"), recvd_item.at("payload")); + can_ui->add_receive_item(recvd_item.sid(), recvd_item.dlc(), recvd_item.payload()); } }); } @@ -746,12 +747,42 @@ void UI_base::showEvent(QShowEvent *event) LOG_DEBUG("Connecting callbacks"); m_first_load = true; - connect(m_web_socket, &Web_socket_wrapper::on_command_cb, this, [=, this](json &j) { on_cmd_cb(j); }); - connect(m_web_socket, &Web_socket_wrapper::on_event_cb, this, [=, this](json &j) { on_event_cb(j); }); - connect(m_web_socket, &Web_socket_wrapper::on_closed, this, [=, this] { m_error->open(); }); + auto reply = m_remote_ui_client->Command_get_ui_elements(Empty()); + connect(reply.get(), &QGrpcCallReply::finished, reply.get(), [this, reply = std::move(reply)](const QGrpcStatus &status) { + if (!status.isOk()) + { + LOG_ERROR(QString("Command get ui elements failed: %0").arg(status.message())); + return; + } + auto response = reply->read(); + if (!response.has_value()) + { + qDebug("Command get ui elements deserialization failed"); + return; + } + UI_item_parser(response.value()); + }, + Qt::SingleShotConnection); - m_web_socket->inhibit_events(true); - m_web_socket->send_command(Web_socket_wrapper::Command::get_UI_elements); + m_stream = m_remote_ui_client->SubscribeUIChanges(Empty()); + + connect( + m_stream.get(), &QGrpcServerStream::finished, this, + [this](const QGrpcStatus &status) { + if (!status.isOk()) + m_error->show(); + }, + Qt::SingleShotConnection); + + connect(m_stream.get(), &QGrpcServerStream::messageReceived, this, [this, stream = m_stream.get()] { + auto response = stream->read(); + if (!response.has_value()) + { + qDebug("Client (UnaryCall) deserialization failed"); + return; + } + UI_item_parser(response.value()); + }); this->m_timer_update->start(m_refresh_rate); m_open = true; } @@ -763,64 +794,30 @@ void UI_base::closeEvent(QCloseEvent *event) { LOG_DEBUG("Disconnecting callbacks"); m_timer_update->stop(); - disconnect(m_web_socket, nullptr, nullptr, nullptr); + disconnect(m_stream.get(), nullptr, nullptr, nullptr); m_open = false; this->parentWidget()->show(); } } -void UI_base::on_cmd_cb(json &j) -{ - auto response = magic_enum::enum_cast(std::string(j.at("type"))) - .value_or(Web_socket_wrapper::Command::end); - switch (response) - { - case Web_socket_wrapper::Command::get_UI_elements: - case Web_socket_wrapper::Command::get_UI_element: - UI_item_parser(j); - break; - default: - break; - } - if (m_first_load) - { - m_web_socket->inhibit_events(false); - m_first_load = false; - } -} - -void UI_base::on_event_cb(json &j) -{ - auto response = magic_enum::enum_cast(std::string(j.at("type"))) - .value_or(Web_socket_wrapper::Event::end); - switch (response) - { - case Web_socket_wrapper::Event::ui_changed: - UI_item_parser(j); - break; - default: - break; - } -} - void UI_base::push_ui_item(QWidget *item) { m_ui_lookup.emplace_back(item); setup_ui_item(item, m_ui_lookup.size() - 1); } -void UI_base::UI_item_parser(json &input) +void UI_base::UI_item_parser(UI_items_m &input) { - for (auto &ui_item : input.at("UI_items")) + for (auto &ui_item : input.item()) { - auto widget = id_to_ui(ui_item.at("id")); + auto widget = id_to_ui(ui_item.id_proto()); if (widget == nullptr) { - LOG_WARN(QString("id_to_ui returned null on %0").arg((size_t)ui_item.at("id"))); + LOG_WARN(QString("id_to_ui returned null on %0").arg((size_t)ui_item.id_proto())); return; } - m_process_lookup[magic_enum::enum_cast((std::string)ui_item.at("type")).value_or(UI_enum::end)]( + m_process_lookup[ui_item.type()]( ui_item, widget); } } @@ -845,8 +842,12 @@ void UI_base::setup_button(QWidget *item, size_t index) QPushButton *button = qobject_cast(item); if (button == nullptr) return; - connect(button, &QPushButton::clicked, this, - [=, this] { m_web_socket->send_event(Web_socket_wrapper::Event::clicked, index); }); + connect(button, &QPushButton::clicked, this, [=, this] { + auto event = Event_m(); + event.setId_proto(index); + event.setClicked(Clicked_m()); + (void)m_remote_ui_client->SendEvent(event); + }); } void UI_base::setup_combobox(QWidget *item, size_t index) @@ -855,8 +856,12 @@ void UI_base::setup_combobox(QWidget *item, size_t index) if (combobox == nullptr) return; - connect(combobox, &QComboBox::activated, this, [=, this] { - m_web_socket->send_event(Web_socket_wrapper::Event::selected, index, (size_t)combobox->currentIndex()); + connect(combobox, &QComboBox::activated, this, [=, this](int combo_index) { + auto event = Event_m(); + event.setSelected(Selected_m()); + event.setId_proto(index); + event.selected().setSelected(combo_index); + (void)m_remote_ui_client->SendEvent(event); }); } @@ -866,8 +871,12 @@ void UI_base::setup_checkbox(QWidget *item, size_t index) if (checkbox == nullptr) return; - connect(checkbox, &QCheckBox::clicked, this, - [=, this] { m_web_socket->send_event(Web_socket_wrapper::Event::clicked, index); }); + connect(checkbox, &QCheckBox::clicked, this, [=, this] { + auto event = Event_m(); + event.setId_proto(index); + event.setClicked(Clicked_m()); + (void)m_remote_ui_client->SendEvent(event); + }); } void UI_base::setup_dial(QWidget *item, size_t index) @@ -891,8 +900,13 @@ void UI_base::setup_slider(QWidget *item, size_t index) return; } - connect(slider, &QwtSlider::sliderMoved, this, - [=, this] { m_web_socket->send_event(Web_socket_wrapper::Event::value_changed, index, slider->value()); }); + connect(slider, &QwtSlider::sliderMoved, this, [=, this] (double value) { + auto event = Event_m(); + event.setId_proto(index); + event.setValueChanged(Value_changed_m()); + event.valueChanged().setValue(value); + (void)m_remote_ui_client->SendEvent(event); + }); } void UI_base::setup_textbox(QWidget *item, size_t index) @@ -901,8 +915,13 @@ void UI_base::setup_textbox(QWidget *item, size_t index) if (line_edit == nullptr) return; - connect(line_edit, &QLineEdit::textEdited, this, - [=, this] { m_web_socket->send_event(Web_socket_wrapper::Event::text_changed, index, line_edit->text()); }); + connect(line_edit, &QLineEdit::textEdited, this, [=, this] { + auto event = Event_m(); + event.setId_proto(index); + event.setTextChanged(Text_changed_m()); + event.textChanged().setText(line_edit->text()); + (void)m_remote_ui_client->SendEvent(event); + }); } void UI_base::setup_spinbox(QWidget *item, size_t index) @@ -912,7 +931,11 @@ void UI_base::setup_spinbox(QWidget *item, size_t index) return; connect(spinbox, &QSpinBox::valueChanged, this, [=, this](int value) { - m_web_socket->send_event(Web_socket_wrapper::Event::value_changed, index, (double)value); + auto event = Event_m(); + event.setId_proto(index); + event.setValueChanged(Value_changed_m()); + event.valueChanged().setValue(value); + (void)m_remote_ui_client->SendEvent(event); }); } @@ -923,7 +946,11 @@ void UI_base::setup_double_spinbox(QWidget *item, size_t index) return; connect(spinbox, &QDoubleSpinBox::valueChanged, this, [=, this](double value) { - m_web_socket->send_event(Web_socket_wrapper::Event::value_changed, index, value); + auto event = Event_m(); + event.setId_proto(index); + event.setValueChanged(Value_changed_m()); + event.valueChanged().setValue(value); + (void)m_remote_ui_client->SendEvent(event); }); } @@ -934,7 +961,11 @@ void UI_base::setup_hex_spinbox(QWidget *item, size_t index) return; connect(spinbox, &HexSpinBox::valueChanged, this, [=, this](int value) { - m_web_socket->send_event(Web_socket_wrapper::Event::value_changed, index, (double)value); + auto event = Event_m(); + event.setId_proto(index); + event.setValueChanged(Value_changed_m()); + event.valueChanged().setValue(value); + (void)m_remote_ui_client->SendEvent(event); }); } @@ -945,7 +976,11 @@ void UI_base::setup_tab_widget(QWidget *item, size_t index) return; connect(tab_widget, &QTabWidget::currentChanged, this, [=, this](int tab_index) { - m_web_socket->send_event(Web_socket_wrapper::Event::selected, index, (size_t)tab_index); + auto event = Event_m(); + event.setSelected(Selected_m()); + event.setId_proto(index); + event.selected().setSelected(tab_index); + (void)m_remote_ui_client->SendEvent(event); }); } @@ -956,9 +991,20 @@ void UI_base::setup_can_ui(QWidget *item, size_t index) return; connect(can, &Can_Transceive::send_can_message, this, - [=, this](uint32_t id, uint8_t dlc, std::array payload) { - m_web_socket->send_event(Web_socket_wrapper::Event::can_send, index, id, dlc, payload); + [=, this](uint32_t id, uint8_t dlc, QByteArray payload) { + auto event = Event_m(); + event.setId_proto(index); + event.setCanSend(CAN_send_m()); + event.canSend().mutFrame().setSid(id); + event.canSend().mutFrame().setDlc(dlc); + event.canSend().mutFrame().setPayload(payload); + (void)m_remote_ui_client->SendEvent(event); }); connect(can, &Can_Transceive::can_clear, this, - [=, this] { m_web_socket->send_event(Web_socket_wrapper::Event::clear, index); }); + [=, this] { + auto event = Event_m(); + event.setId_proto(index); + event.setClear(Clear_m()); + (void)m_remote_ui_client->SendEvent(event); + }); } diff --git a/client/ui_base.hpp b/client/ui_base.hpp index a19016a..65e355a 100644 --- a/client/ui_base.hpp +++ b/client/ui_base.hpp @@ -1,16 +1,19 @@ #pragma once -#include "ui_enum.hpp" #include #include #include -#include +#include "messages.qpb.h" +#include "m_ui_items.qpb.h" +#include "messages_client.grpc.qpb.h" -Q_FORWARD_DECLARE_OBJC_CLASS(Web_socket_wrapper); Q_FORWARD_DECLARE_OBJC_CLASS(QTimer); Q_FORWARD_DECLARE_OBJC_CLASS(QLabel); Q_FORWARD_DECLARE_OBJC_CLASS(QMessageBox); -using json = nlohmann::json; +namespace RemoteUiService +{ +class Client; +} #define PUSH_UI_ITEM(item) push_ui_item(ui->item) @@ -44,11 +47,10 @@ class UI_base : public QMainWindow * @brief Construct a new ui base object * * @param name The name of the simulator - * @param api The weboscket connection to the server API + * @param api The grpc connection to the server API * @param parent The parent widget */ - explicit UI_base(Web_socket_wrapper *api, QWidget *parent = nullptr); - void set_name(QString name); + explicit UI_base(RemoteUiService::Client *api, QWidget *parent = nullptr); /** * @brief Get the UI element by its ID * @@ -56,6 +58,7 @@ class UI_base : public QMainWindow * @return QWidget* The UI element */ QWidget *id_to_ui(size_t id); + void set_name(QString name); /** * @brief Get the label associated with a widget * This function has to be overridden in derived classes if labels are used @@ -70,18 +73,6 @@ class UI_base : public QMainWindow } protected: - /** - * @brief Handle an event callback from the server - * - * @param j The JSON event - */ - void on_event_cb(json &j); - /** - * @brief Handle a command callback from the server - * - * @param j The JSON command - */ - void on_cmd_cb(json &j); /** * @brief Push a UI item to the internal list and setup its connections * The item have to be in the same order as from the server @@ -94,7 +85,7 @@ class UI_base : public QMainWindow * * @param input The JSON input containing UI items */ - void UI_item_parser(json &input); + void UI_item_parser(UI_items_m &input); /** * @brief Setup a generic UI item * @@ -198,7 +189,8 @@ class UI_base : public QMainWindow void log_signal(const char *filename_in, int line_in, const char *funcname_in, int level, QString msg); private: - Web_socket_wrapper *m_web_socket; + RemoteUiService::Client *m_remote_ui_client; + std::unique_ptr m_stream; QString m_name; QTimer *m_timer_update; uint32_t m_refresh_rate; @@ -206,5 +198,5 @@ class UI_base : public QMainWindow bool m_open = false; bool m_first_load = true; std::vector m_ui_lookup; - QMap> m_process_lookup; + QMap> m_process_lookup; }; diff --git a/client/web_socket_wrapper.cpp b/client/web_socket_wrapper.cpp deleted file mode 100644 index cdf7047..0000000 --- a/client/web_socket_wrapper.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include "web_socket_wrapper.hpp" -#include -#include -#include - -#define LOG_SRC_LOC(level, msg) emit log_signal(__FILE__, __LINE__, __FUNCTION__, (int)(level), (msg)) -#define LOG_TRACE(msg) LOG_SRC_LOC(Log_level_enum::trace, msg) -#define LOG_DEBUG(msg) LOG_SRC_LOC(Log_level_enum::debug, msg) -#define LOG_INFO(msg) LOG_SRC_LOC(Log_level_enum::info, msg) -#define LOG_WARN(msg) LOG_SRC_LOC(Log_level_enum::warn, msg) -#define LOG_ERROR(msg) LOG_SRC_LOC(Log_level_enum::err, msg) -#define LOG_CRITICAL(msg) LOG_SRC_LOC(Log_level_enum::critical, msg) - -Web_socket_wrapper::Web_socket_wrapper(const QUrl &url, QObject *parent) - : QObject(parent), m_web_socket(new QWebSocket("", QWebSocketProtocol::VersionLatest, this)), - m_ping_timer(new QTimer(this)), m_pong_timer(new QTimer(this)) -{ - connect(m_web_socket, &QWebSocket::connected, this, [=, this] { - m_connected = true; - connect(m_web_socket, &QWebSocket::textMessageReceived, this, &Web_socket_wrapper::m_on_received); - m_ping_timer->start(5000); - emit on_connected(); - }); - connect(m_web_socket, &QWebSocket::disconnected, this, [=, this] { emit on_closed(); }); - connect(m_web_socket, &QWebSocket::errorOccurred, this, [this](QAbstractSocket::SocketError error) { - LOG_ERROR(QString::fromStdString(std::string(magic_enum::enum_name(error)))); - }); - - connect(m_ping_timer, &QTimer::timeout, this, [=, this] { -#ifdef EMSCRIPTEN - if (m_web_socket->state() != QAbstractSocket::ConnectedState) - { - m_web_socket->close(); - emit on_closed(); - } - m_ping_timer->start(1000); - }); -#else - m_web_socket->ping(); - m_pong_timer->start(1000); - }); - connect(m_web_socket, &QWebSocket::pong, this, [=, this](quint64 elapsedTime) { - if (elapsedTime > 1000) - { - m_web_socket->close(); - emit on_closed(); - } - - m_pong_timer->stop(); - m_ping_timer->start(1000); - }); - connect(m_pong_timer, &QTimer::timeout, this, [=, this] { - LOG_CRITICAL("Pong timeout"); - m_web_socket->close(); - emit on_closed(); - }); - connect(m_web_socket, &QWebSocket::sslErrors, - this, [&](const QList &errors) { - QList exeptions; - for (const QSslError &err : errors) { - switch (err.error()) { - case QSslError::UnableToGetLocalIssuerCertificate: - // case QSslError::UnableToVerifyFirstCertificate: - // case QSslError::SelfSignedCertificate: - // case QSslError::SelfSignedCertificateInChain: - // case QSslError::CertificateUntrusted: - exeptions.append(err); - LOG_WARN(QString("Allowing SSL error: %1 (%2)") - .arg(err.errorString(), err.certificate().isNull() - ? "no cert" - : err.certificate().subjectDisplayName())); - break; - default: - LOG_ERROR(QString("Unexpected SSL error (not ignored): %1") - .arg(err.errorString())); - break; - } - } - - if (!exeptions.isEmpty()) { - m_web_socket->ignoreSslErrors(exeptions); - } - }); -#endif - m_ping_timer->setSingleShot(true); - m_pong_timer->setSingleShot(true); - m_web_socket->setProxy(QNetworkProxy::NoProxy); - m_web_socket->open(url); -} - -void Web_socket_wrapper::send_command(Command c, size_t id) -{ - json j; - j["command"]["type"] = magic_enum::enum_name(c).data(); - switch (c) - { - case Command::get_UI_element: - j["command"]["id"] = id; - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - case Command::get_UI_elements: - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - case Command::get_active_simulator_name: - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - case Command::get_simulators: - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - default: - break; - } -} - -void Web_socket_wrapper::send_command(Command command, QString val) -{ - json j; - j["command"]["type"] = magic_enum::enum_name(command).data(); - switch (command) - { - case Command::switch_simulator: - j["command"]["name"] = val.toStdString(); - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - default: - break; - } -} - -void Web_socket_wrapper::send_event(Event e, size_t id) -{ - if (m_inhibit_events) - return; - json j; - j["event"]["type"] = magic_enum::enum_name(e).data(); - switch (e) - { - case Event::clicked: - case Event::clear: - j["event"]["id"] = id; - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - default: - break; - } -} - -void Web_socket_wrapper::send_event(Event e, size_t id, size_t val) -{ - if (m_inhibit_events) - return; - json j; - j["event"]["type"] = magic_enum::enum_name(e).data(); - switch (e) - { - case Event::selected: - j["event"]["id"] = id; - j["event"]["selected"] = val; - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - default: - break; - } -} - -void Web_socket_wrapper::send_event(Event e, size_t id, double val) -{ - if (m_inhibit_events) - return; - json j; - j["event"]["type"] = magic_enum::enum_name(e).data(); - switch (e) - { - case Event::value_changed: - j["event"]["id"] = id; - j["event"]["value"] = val; - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - default: - break; - } -} - -void Web_socket_wrapper::send_event(Event e, size_t id, QString val) -{ - if (m_inhibit_events) - return; - json j; - j["event"]["type"] = magic_enum::enum_name(e).data(); - switch (e) - { - case Event::text_changed: - j["event"]["id"] = id; - j["event"]["text"] = val.toStdString(); - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - default: - break; - } -} - -void Web_socket_wrapper::send_event(Event e, size_t id, uint32_t sid, uint8_t dlc, std::array payload) -{ - if (m_inhibit_events) - return; - json j; - j["event"]["type"] = magic_enum::enum_name(e).data(); - switch (e) - { - case Event::can_send: - j["event"]["id"] = id; - j["event"]["sid"] = sid; - j["event"]["dlc"] = dlc; - j["event"]["payload"] = payload; - m_web_socket->sendTextMessage(QString(j.dump().data())); - break; - default: - break; - } -} - -void Web_socket_wrapper::m_on_received(QString message) -{ - json j = json::parse(message.toStdString()); - if (j.contains("response")) - { - emit on_command_cb(j.at("response")); - return; - } - if (j.contains("event")) - { - emit on_event_cb(j.at("event")); - return; - } -} - -void Web_socket_wrapper::inhibit_events(bool inhibit) -{ - m_inhibit_events = inhibit; -} diff --git a/client/web_socket_wrapper.hpp b/client/web_socket_wrapper.hpp deleted file mode 100644 index 21280cb..0000000 --- a/client/web_socket_wrapper.hpp +++ /dev/null @@ -1,171 +0,0 @@ -#pragma once -#include -#include - -Q_FORWARD_DECLARE_OBJC_CLASS(QWebSocket); -Q_FORWARD_DECLARE_OBJC_CLASS(QTimer); -using json = nlohmann::json; - -/** - * @brief Class wrapping a WebSocket connection - * - */ -class Web_socket_wrapper : public QObject -{ - Q_OBJECT - public: - /** - * @brief Enumeration of commands - */ - enum class Command - { - get_UI_element, - get_UI_elements, - switch_simulator, - get_active_simulator_name, - get_simulators, - end, - begin = 0 - }; - - enum class Log_level_enum : int - { - trace, - debug, - info, - warn, - err, - critical, - off, - n_levels - }; - - /** - * @brief Enumeration of events - * - */ - enum class Event - { - clicked, - value_changed, - text_changed, - selected, - can_send, - clear, - ui_changed, - end, - begin = 0 - }; - - public: - /** - * @brief Construct a new Web_socket_wrapper object - * - * @param url The WebSocket URL to connect to - * @param parent The parent QObject - */ - explicit Web_socket_wrapper(const QUrl &url, QObject *parent = nullptr); - - /** - * @brief Send a command to the server - * - * @param c The command to send - * @param id The ID associated with the command (if applicable) - */ - void send_command(Command c, size_t id = 0); - /** - * @brief Send a command to the server with a string value - * - * @param c The command to send - * @param val The string value associated with the command - */ - void send_command(Command c, QString val); - - /** - * @brief Send an event to the server - * - * @param e The event to send - * @param id The ID associated with the event - */ - void send_event(Event e, size_t id); - /** - * @brief Send an event to the server with a size_t value - * - * @param e The event to send - * @param id The ID associated with the event - * @param val The size_t value associated with the event - */ - void send_event(Event e, size_t id, size_t val); - /** - * @brief Send an event to the server with a double value - * - * @param e The event to send - * @param id The ID associated with the event - * @param val The double value associated with the event - */ - void send_event(Event e, size_t id, double val); - /** - * @brief Send an event to the server with a string value - * - * @param e The event to send - * @param id The ID associated with the event - * @param val The string value associated with the event - */ - void send_event(Event e, size_t id, QString val); - /** - * @brief Send a CAN event to the server - * - * @param e The event to send - * @param id The ID associated with the event - * @param sid The CAN standard identifier - * @param dlc The CAN data length code - * @param payload The CAN data payload - */ - void send_event(Event e, size_t id, uint32_t sid, uint8_t dlc, std::array payload); - /** - * @brief Inhibit or allow sending events to the server - * - * @param inhibit True to inhibit events, false to allow - */ - void inhibit_events(bool inhibit); - - signals: - /** - * @brief Signal emitted when the WebSocket is connected - * - */ - void on_connected(); - /** - * @brief Signal emitted when the WebSocket is closed - * - */ - void on_closed(); - /** - * @brief Signal emitted when a command is received from the server - * - * @param data The JSON command data - */ - void on_command_cb(json &data); - /** - * @brief Signal emitted when an event is received from the server - * - * @param data The JSON event data - */ - void on_event_cb(json &data); - - void log_signal(const char *filename_in, int line_in, const char *funcname_in, int level, QString msg); - - private Q_SLOTS: - /** - * @brief Internal slot for handling WebSocket disconnection - * - */ - void m_on_received(QString message); - - private: - bool m_connected = false; - bool m_inhibit_events = false; - QWebSocket *m_web_socket; - QTimer *m_ping_timer; - QTimer *m_pong_timer; -};