This commit is contained in:
2026-09-30 12:33:21 +02:00
parent 08fd5407c3
commit 67d7e959b1
14 changed files with 353 additions and 711 deletions
+3 -6
View File
@@ -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,8 +19,6 @@ set(PROJECT_SOURCES
helpers.hpp
ui_base.cpp
ui_base.hpp
web_socket_wrapper.cpp
web_socket_wrapper.hpp
)
qt_add_executable(unisim_client
@@ -31,7 +29,6 @@ set_target_properties(
unisim_client
PROPERTIES
AUTOMOC ON
AUTORCC ON
AUTOUIC ON
)
@@ -43,7 +40,7 @@ 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
+4 -4
View File
@@ -9,7 +9,7 @@
class UIFactory
{
public:
using Creator = std::function<UI_base *(Web_socket_wrapper *web_socket, QWidget *)>;
using Creator = std::function<UI_base *(RemoteUiService::Client *s, QWidget *)>;
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; \
+71 -56
View File
@@ -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 <QDirIterator>
#include <QLibrary>
#include <QMessageBox>
#include <QThread>
#include <QTimer>
#include <magic_enum/magic_enum.hpp>
#include <spdlog/spdlog.h>
#include <QGrpcHttp2Channel>
#include <QGrpcChannelOptions>
#include <QSslConfiguration>
#include <QSslSocket>
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<QGrpcHttp2Channel>(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<Simulators_m>();
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<Empty>();
if (!response.has_value())
{
qDebug("Switch simulator deserialization failed");
return;
}
open_sim_window();
}, Qt::SingleShotConnection);
}
void MainWindow::open_sim_window(void)
@@ -130,15 +164,6 @@ 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");
@@ -146,26 +171,16 @@ void MainWindow::check_active_sim(QString name)
return;
}
SPDLOG_DEBUG("Name set and wrong");
m_web_socket->send_command(Web_socket_wrapper::Command::switch_simulator, m_selected_sim_name);
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<Empty>();
if (!response.has_value())
{
qDebug("Switch simulator deserialization failed");
return;
}
void MainWindow::on_cmd_cb(json &j)
{
auto response = magic_enum::enum_cast<Web_socket_wrapper::Command>(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:
open_sim_window();
break;
default:
break;
}
}, Qt::SingleShotConnection);
}
+16 -13
View File
@@ -1,16 +1,23 @@
#pragma once
#include "nlohmann/json.hpp"
#include <QMainWindow>
#include <map>
using json = nlohmann::json;
#include "messages.qpb.h"
#define INSERT_SIMULATOR(type) \
m_sims.insert(std::make_pair(type().sim_name(), std::make_unique<type>(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<Web_socket_wrapper> m_web_socket;
std::unique_ptr<RemoteUiService::Client> m_remote_ui_client;
std::map<QString, std::unique_ptr<UI_base>> m_sims;
QTimer *m_ping;
};
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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()
{
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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()
+7 -7
View File
@@ -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, 8>(
{(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<uint8_t, 8> 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::array<uint8_t,
ui->send_list->resizeColumnsToContents();
}
void Can_Transceive::add_receive_item(uint32_t id, uint8_t dlc, std::array<uint8_t, 8> 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);
+3 -3
View File
@@ -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<uint8_t, 8> 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<uint8_t, 8> 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<uint8_t, 8> payload);
void send_can_message(uint32_t id, uint8_t dlc, QByteArray payload);
/**
* @brief Emitted when the CAN message lists are to be cleared
+227 -181
View File
@@ -3,12 +3,12 @@
#include "helpers.hpp"
#include "led.hpp"
#include "plot_wrapper.hpp"
#include "web_socket_wrapper.hpp"
#include <QMessageBox>
#include <magic_enum/magic_enum.hpp>
#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<QLabel *>(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<QwtSlider *>(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<QwtDial *>(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<QwtThermo *>(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<QLineEdit *>(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<QComboBox *>(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<QCheckBox *>(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<QPushButton *>(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<Led *>(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<QSpinBox *>(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<QDoubleSpinBox *>(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<HexSpinBox *>(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<QTabWidget *>(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<QStackedWidget *>(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<QStatusBar *>(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<Plot_wrapper *>(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<double> x_vals = (std::vector<double>)ui_item.at("x_vals");
std::vector<double> y_vals = (std::vector<double>)ui_item.at("y_vals");
QList<double> x_vals = ui_item.plot().xVals();
QList<double> 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<QTableWidget *>(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<std::string> row_labels = (std::vector<std::string>)ui_item.at("row_labels");
size_t column_count = ui_item.at("column_count");
std::vector<std::string> column_labels = (std::vector<std::string>)ui_item.at("column_labels");
std::vector<std::string> table_data = (std::vector<std::string>)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<QString> row_labels = ui_item.table().rowLabels();
size_t column_count = ui_item.table().columnCount();
QList<QString> column_labels = ui_item.table().columnLabels();
QList<QString> 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<Can_Transceive *>(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<UI_items_m>();
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<UI_items_m>();
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<Web_socket_wrapper::Command>(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<Web_socket_wrapper::Event>(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<UI_enum>((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<QPushButton *>(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<uint8_t, 8> 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);
});
}
+14 -22
View File
@@ -1,16 +1,19 @@
#pragma once
#include "ui_enum.hpp"
#include <QMainWindow>
#include <QWidget>
#include <functional>
#include <nlohmann/json.hpp>
#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<QGrpcServerStream> 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<QWidget *> m_ui_lookup;
QMap<UI_enum, std::function<void(json &, QWidget *)>> m_process_lookup;
QMap<UI_typeGadget::UI_type, std::function<void(const UI_item_m &, QWidget *)>> m_process_lookup;
};
-240
View File
@@ -1,240 +0,0 @@
#include "web_socket_wrapper.hpp"
#include <QTimer>
#include <QtWebSockets/QWebSocket>
#include <magic_enum/magic_enum.hpp>
#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<QSslError> &errors) {
QList<QSslError> 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<uint8_t, 8> 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;
}
-171
View File
@@ -1,171 +0,0 @@
#pragma once
#include <QObject>
#include <nlohmann/json.hpp>
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<uint8_t, 8> 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;
};