From 9765cd3e51196b89f8ad7f1e4b7ed7bec0f5155d Mon Sep 17 00:00:00 2001 From: Sander Speetjens Date: Sat, 14 Feb 2026 12:37:35 +0100 Subject: [PATCH] client now works on windows --- client/CMakeLists.txt | 76 +++++++++++++++++--------------- client/mainwindow.cpp | 8 +++- client/ui/better_radiobutton.hpp | 8 +++- client/ui/can_transceive.hpp | 8 +++- client/ui/hex_spinbox.hpp | 8 +++- client/ui/led.hpp | 8 +++- client/ui/plot_wrapper.hpp | 8 +++- client/ui_base.hpp | 8 +++- client/web_socket_wrapper.cpp | 16 +++++-- client/web_socket_wrapper.hpp | 25 ++++++++++- external/CMakeLists.txt | 1 + external/qwt | 2 +- 12 files changed, 127 insertions(+), 49 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6d6c4f0..1d4e41c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -6,43 +6,17 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) file(GLOB_RECURSE ui "ui/*") set(PROJECT_SOURCES - ${ui} - helpers.cpp - helpers.hpp IterableEnum.hpp main.cpp mainwindow.cpp mainwindow.hpp mainwindow.ui - ui_base.cpp - ui_base.hpp - web_socket_wrapper.cpp - web_socket_wrapper.hpp ) -if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) - qt_add_executable(unisim_client - MANUAL_FINALIZATION - ${PROJECT_SOURCES} - $<$:${sims}> - ) -# Define target properties for Android with Qt 6 as: -# set_property(TARGET Simulator - CVS_I10 APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR -# ${CMAKE_CURRENT_SOURCE_DIR } / android) -# For more information, see https: // doc.qt.io/qt-6/qt-add-executable.html#target-creation -else() - if(ANDROID) - add_library(unisim_client SHARED - ${PROJECT_SOURCES} - ) -# Define properties for Android with Qt 5 after find_package() calls as: -# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") - else() - add_executable(unisim_client - ${PROJECT_SOURCES} - ) - endif() -endif() +qt_add_executable(unisim_client + MANUAL_FINALIZATION + ${PROJECT_SOURCES} + ) set_target_properties( unisim_client @@ -50,18 +24,16 @@ set_target_properties( AUTOMOC ON AUTORCC ON AUTOUIC ON - LINK_FLAGS "-Wl,--export-dynamic" ) target_include_directories(unisim_client PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/simulators - ${CMAKE_CURRENT_LIST_DIR}/ui ${PROJECT_BINARY_DIR}/ ) -target_link_libraries(unisim_client PRIVATE qwt spdlog Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::WebSockets magic_enum::magic_enum nlohmann_json::nlohmann_json git_tag) +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 client_common) include(GNUInstallDirs) install(TARGETS unisim_client @@ -74,6 +46,40 @@ if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(unisim_client) endif() +add_library(client_common SHARED) +target_sources(client_common + PRIVATE + ${ui} + helpers.cpp + helpers.hpp + ui_base.cpp + ui_base.hpp + web_socket_wrapper.cpp + web_socket_wrapper.hpp +) +target_compile_definitions(client_common PRIVATE + INTERNAL +) +target_link_libraries(client_common + PUBLIC + qwt Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::WebSockets magic_enum::magic_enum nlohmann_json::nlohmann_json +) + +set_target_properties( + client_common + PROPERTIES + AUTOMOC ON + AUTORCC ON + AUTOUIC ON +) + +target_include_directories(client_common + PUBLIC + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/ui + ${PROJECT_BINARY_DIR} +) + if (NOT EMSCRIPTEN) file(GLOB_RECURSE sims "simulators/*.cpp") # Loop through each .cpp file and create a library for it @@ -83,8 +89,8 @@ if (NOT EMSCRIPTEN) # Create a library for each .cpp file add_library(ui_${LIB_NAME} SHARED ${CMAKE_CURRENT_SOURCE_DIR}/simulators/${LIB_NAME}.hpp ${CMAKE_CURRENT_SOURCE_DIR}/simulators/${LIB_NAME}.cpp ${CMAKE_CURRENT_SOURCE_DIR}/simulators/${LIB_NAME}.ui) - target_link_libraries(ui_${LIB_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets magic_enum::magic_enum nlohmann_json::nlohmann_json) - target_include_directories(ui_${LIB_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/ui ${CMAKE_SOURCE_DIR}/external/qwt/src ${PROJECT_BINARY_DIR}/) + target_link_libraries(ui_${LIB_NAME} PUBLIC Qt${QT_VERSION_MAJOR}::Widgets client_common) + target_include_directories(ui_${LIB_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/ui ${PROJECT_BINARY_DIR}/) set_target_properties( ui_${LIB_NAME} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 21a6dfc..91d5349 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -16,8 +16,14 @@ MainWindow::MainWindow(QUrl ws_url, QString sim, QWidget *parent) m_web_socket(new Web_socket_wrapper(ws_url)) { ui->setupUi(this); + 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) { + spdlog::default_logger_raw()->log(spdlog::source_loc{filename_in, line_in, funcname_in}, + (spdlog::level::level_enum)level, msg.toStdString()); + }); + QLibrary lib; - QDirIterator it(QCoreApplication::applicationDirPath(), QStringList() << "*.so", QDir::Files, + QDirIterator it(QCoreApplication::applicationDirPath(), QStringList() << "*.so" << "*.dll", QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { diff --git a/client/ui/better_radiobutton.hpp b/client/ui/better_radiobutton.hpp index ed06d35..a6997bc 100644 --- a/client/ui/better_radiobutton.hpp +++ b/client/ui/better_radiobutton.hpp @@ -2,11 +2,17 @@ #include #include +#ifdef INTERNAL +#define EXPORT Q_DECL_EXPORT +#else +#define EXPORT Q_DECL_IMPORT +#endif + /** * @brief Class representing a better radiobutton group * */ -class Better_Radiobutton : public QWidget +class EXPORT Better_Radiobutton : public QWidget { Q_OBJECT public: diff --git a/client/ui/can_transceive.hpp b/client/ui/can_transceive.hpp index e6e8fc7..5eec430 100644 --- a/client/ui/can_transceive.hpp +++ b/client/ui/can_transceive.hpp @@ -2,6 +2,12 @@ #include "hex_spinbox.hpp" #include +#ifdef INTERNAL +#define EXPORT Q_DECL_EXPORT +#else +#define EXPORT Q_DECL_IMPORT +#endif + namespace Ui { class Can_Transceive; @@ -11,7 +17,7 @@ class Can_Transceive; * @brief Class representing a CAN transceive UI component * */ -class Can_Transceive : public QWidget +class EXPORT Can_Transceive : public QWidget { Q_OBJECT diff --git a/client/ui/hex_spinbox.hpp b/client/ui/hex_spinbox.hpp index 2ec7bd3..8ee11ee 100644 --- a/client/ui/hex_spinbox.hpp +++ b/client/ui/hex_spinbox.hpp @@ -3,13 +3,19 @@ #include #include +#ifdef INTERNAL +#define EXPORT Q_DECL_EXPORT +#else +#define EXPORT Q_DECL_IMPORT +#endif + /** * @brief Class representing a hexadecimal spin box * @note Since QSpinBox uses int as the storage type, the effective editing range * is +/- 0x7FFF FFFF, so it can't handle a full unsigned int. * QDoubleSpinBox would be a more suitable base class if a wider range is needed. */ -class HexSpinBox : public QSpinBox +class EXPORT HexSpinBox : public QSpinBox { Q_OBJECT diff --git a/client/ui/led.hpp b/client/ui/led.hpp index 5d0977c..3c74843 100644 --- a/client/ui/led.hpp +++ b/client/ui/led.hpp @@ -1,11 +1,17 @@ #pragma once #include "QPushButton" +#ifdef INTERNAL +#define EXPORT Q_DECL_EXPORT +#else +#define EXPORT Q_DECL_IMPORT +#endif + /** * @brief Class representing an LED widget * */ -class Led : public QPushButton +class EXPORT Led : public QPushButton { Q_OBJECT public: diff --git a/client/ui/plot_wrapper.hpp b/client/ui/plot_wrapper.hpp index 49ee226..87ebb86 100644 --- a/client/ui/plot_wrapper.hpp +++ b/client/ui/plot_wrapper.hpp @@ -4,11 +4,17 @@ #include #include +#ifdef INTERNAL +#define EXPORT Q_DECL_EXPORT +#else +#define EXPORT Q_DECL_IMPORT +#endif + /** * @brief Class wrapping a QwtPlot for plotting data * */ -class Plot_wrapper : public QwtPlot +class EXPORT Plot_wrapper : public QwtPlot { Q_OBJECT public: diff --git a/client/ui_base.hpp b/client/ui_base.hpp index b51dc32..95779d5 100644 --- a/client/ui_base.hpp +++ b/client/ui_base.hpp @@ -3,6 +3,12 @@ #include #include +#ifdef INTERNAL +#define EXPORT Q_DECL_EXPORT +#else +#define EXPORT Q_DECL_IMPORT +#endif + Q_FORWARD_DECLARE_OBJC_CLASS(Web_socket_wrapper); Q_FORWARD_DECLARE_OBJC_CLASS(QTimer); Q_FORWARD_DECLARE_OBJC_CLASS(QLabel); @@ -28,7 +34,7 @@ using json = nlohmann::json; /** * @brief Base class for client UIs */ -class UI_base : public QMainWindow +class EXPORT UI_base : public QMainWindow { Q_OBJECT public: diff --git a/client/web_socket_wrapper.cpp b/client/web_socket_wrapper.cpp index 100ce73..a96a37b 100644 --- a/client/web_socket_wrapper.cpp +++ b/client/web_socket_wrapper.cpp @@ -1,9 +1,16 @@ #include "web_socket_wrapper.hpp" -#include "spdlog/spdlog.h" #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)) @@ -11,8 +18,9 @@ Web_socket_wrapper::Web_socket_wrapper(const QUrl &url, QObject *parent) connect(m_web_socket, &QWebSocket::connected, this, &Web_socket_wrapper::m_on_connected); connect(m_web_socket, &QWebSocket::disconnected, this, &Web_socket_wrapper::on_closed); connect(m_web_socket, &QWebSocket::pong, this, &Web_socket_wrapper::m_on_pong); - connect(m_web_socket, &QWebSocket::errorOccurred, this, - [=](QAbstractSocket::SocketError error) { SPDLOG_ERROR(magic_enum::enum_name(error)); }); + 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] { m_web_socket->ping(); m_pong_timer->start(1000); @@ -200,7 +208,7 @@ void Web_socket_wrapper::m_on_pong(quint64 elapsedTime) void Web_socket_wrapper::m_on_pong_timeout() { - SPDLOG_CRITICAL("Pong timeout"); + LOG_CRITICAL("Pong timeout"); m_web_socket->close(); emit on_closed(); } diff --git a/client/web_socket_wrapper.hpp b/client/web_socket_wrapper.hpp index 42a2c3e..eb057df 100644 --- a/client/web_socket_wrapper.hpp +++ b/client/web_socket_wrapper.hpp @@ -1,6 +1,13 @@ #pragma once #include #include + +#ifdef INTERNAL +#define EXPORT Q_DECL_EXPORT +#else +#define EXPORT Q_DECL_IMPORT +#endif + Q_FORWARD_DECLARE_OBJC_CLASS(QWebSocket); Q_FORWARD_DECLARE_OBJC_CLASS(QTimer); using json = nlohmann::json; @@ -9,7 +16,7 @@ using json = nlohmann::json; * @brief Class wrapping a WebSocket connection * */ -class Web_socket_wrapper : public QObject +class EXPORT Web_socket_wrapper : public QObject { Q_OBJECT public: @@ -27,6 +34,18 @@ class Web_socket_wrapper : public QObject begin = 0 }; + enum class Log_level_enum : int + { + trace, + debug, + info, + warn, + err, + critical, + off, + n_levels + }; + /** * @brief Enumeration of events * @@ -121,7 +140,7 @@ class Web_socket_wrapper : public QObject */ void inhibit_events(bool inhibit); - Q_SIGNALS: + signals: /** * @brief Signal emitted when the WebSocket is connected * @@ -145,6 +164,8 @@ class Web_socket_wrapper : public QObject */ 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 connection established diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 4b781fe..24e092f 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,3 +1,4 @@ +set(QWT_BUILD_SHARED ON) add_subdirectory(color) add_subdirectory(cpp-templates) add_subdirectory(git_tag_cpp) diff --git a/external/qwt b/external/qwt index a5dff94..2a2b02f 160000 --- a/external/qwt +++ b/external/qwt @@ -1 +1 @@ -Subproject commit a5dff947d9d82a3242651028d2d89e540c73486e +Subproject commit 2a2b02f3101ff5f105559531c82a685e33926dfb