Fix rebase errors

This commit is contained in:
2026-02-02 14:29:44 +01:00
parent e525b120d5
commit 592ceb5b29
15 changed files with 0 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
#include "CAN_interface.hpp"
#include "spdlog/spdlog.h"
#include "magic_enum.hpp"
CAN_Interface::CAN_Interface(QString dev, QObject *parent)
: QObject(parent)
{
if (!QCanBus::instance()->plugins().contains(QStringLiteral("socketcan"))) {
spdlog::critical("Runtime error: CAN Interface: socketcan plugin not found");
throw std::runtime_error("CAN Interface: socketcan plugin not found");
}
QString errorString;
m_canDevice = QCanBus::instance()->createDevice(QStringLiteral("socketcan"), dev, &errorString);
if (!m_canDevice)
{
// Error handling goes here
spdlog::critical("Runtime error: CAN Interface: {}", errorString.toStdString());
throw std::runtime_error("CAN Interface: " + errorString.toStdString());
}
connect(m_canDevice, &QCanBusDevice::errorOccurred,
this, [=, this](QCanBusDevice::CanBusError error){processErrors(error);});
m_canDevice->connectDevice();
connect(m_canDevice, &QCanBusDevice::framesReceived, this, [this] {
for (const QCanBusFrame &frame : m_canDevice->readAllFrames())
{
emit frame_received(frame);
}
});
}
CAN_Interface::~CAN_Interface()
{
m_canDevice->disconnectDevice();
delete m_canDevice;
}
void CAN_Interface::send_frame(const QCanBusFrame frame)
{
m_canDevice->writeFrame(frame);
emit frame_send(frame);
}
void CAN_Interface::processErrors(QCanBusDevice::CanBusError error) const
{
switch (error) {
case QCanBusDevice::ReadError:
case QCanBusDevice::WriteError:
case QCanBusDevice::ConnectionError:
case QCanBusDevice::ConfigurationError:
case QCanBusDevice::UnknownError:
spdlog::critical("CAN Interface: {} {}", magic_enum::enum_name<QCanBusDevice::CanBusError>(error), m_canDevice->errorString().toStdString());
break;
default:
break;
}
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <QObject>
#include <QCanBus>
#include <QCanBusDevice>
#include <QCanBusFrame>
class CAN_Interface : public QObject
{
Q_OBJECT
public:
CAN_Interface(QString dev, QObject *parent = nullptr);
~CAN_Interface();
void send_frame(const QCanBusFrame frame);
signals:
void frame_received(QCanBusFrame frame);
void frame_send(QCanBusFrame frame);
private:
void processErrors(QCanBusDevice::CanBusError error) const;
QCanBusDevice *m_canDevice;
};
+51
View File
@@ -0,0 +1,51 @@
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Core WebSockets SerialBus)
qt_standard_project_setup()
qt_add_executable(server)
include(FetchContent)
FetchContent_Declare(magic_enum URL https://github.com/Neargye/magic_enum/releases/download/v0.9.6/magic_enum-v0.9.6.tar.gz DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
FetchContent_MakeAvailable(magic_enum)
file(GLOB_RECURSE SIM_SOURCES "simulators/*.[hc]pp")
file(GLOB_RECURSE CAN_SOURCES "CAN/*.[hc]pp")
target_sources(server PRIVATE
${CMAKE_CURRENT_LIST_DIR}/main.cpp
${CMAKE_CURRENT_LIST_DIR}/message_parser.cpp
${CMAKE_CURRENT_LIST_DIR}/simulator_base.cpp
${CMAKE_CURRENT_LIST_DIR}/simulator_base.hpp
${CMAKE_CURRENT_LIST_DIR}/simulators.cpp
${CMAKE_CURRENT_LIST_DIR}/simulators.hpp
${CMAKE_CURRENT_LIST_DIR}/websocket.cpp
${CMAKE_CURRENT_LIST_DIR}/websocket.hpp
${SIM_SOURCES}
${CAN_SOURCES}
)
target_compile_options(server PRIVATE $<$<CONFIG:Debug>:
-Wall -Wextra -pedantic-errors -Wconversion -Wsign-conversion
>)
target_include_directories(server
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/simulators
PRIVATE
)
target_link_libraries(server PRIVATE
Qt::WebSockets
Qt::SerialBus
magic_enum::magic_enum
spdlog
UI
cpp_templates
)
+58
View File
@@ -0,0 +1,58 @@
#include <QtCore/QCommandLineOption>
#include <QtCore/QCommandLineParser>
#include <QtCore/QCoreApplication>
#include "spdlog/async.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
#include "simulators.hpp"
#include "websocket.hpp"
void init_logger()
{
spdlog::init_thread_pool(8192, 1);
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
//auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("log-sim_us.txt", 1024 * 1024 * 10, 3);
std::vector<spdlog::sink_ptr> sinks{stdout_sink/*, rotating_sink*/};
auto logger = std::make_shared<spdlog::async_logger>("sim_us", sinks.begin(), sinks.end(), spdlog::thread_pool(),
spdlog::async_overflow_policy::block);
spdlog::set_default_logger(logger);
spdlog::set_pattern("[%H:%M:%S %z] [%^---%L---%$] %v");
spdlog::flush_every(std::chrono::seconds(3));
spdlog::set_level(spdlog::level::trace);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QCommandLineParser parser;
parser.setApplicationDescription("QtWebSockets example: echoserver");
parser.addHelpOption();
QCommandLineOption port_option(QStringList() << "p"
<< "port",
QCoreApplication::translate("main", "Port for echoserver [default: 9002]."),
QCoreApplication::translate("main", "port"), QLatin1String("9002"));
QCommandLineOption can_port_option(QStringList() << "c"
<< "can",
QCoreApplication::translate("main", "CAN device [default: can0]."),
QCoreApplication::translate("main", "can device"), QLatin1String("can0"));
parser.addOption(port_option);
parser.addOption(can_port_option);
parser.process(a);
uint16_t port = parser.value(port_option).toUShort();
QString can_port = parser.value(can_port_option);
spdlog::info(can_port.toStdString());
// Initialize the logger
init_logger();
// The server starts in this thread
spdlog::info("Starting server");
Simulators simulators(port, can_port);
return a.exec();
}
+127
View File
@@ -0,0 +1,127 @@
#include "simulators.hpp"
#include <magic_enum.hpp>
QString Simulators::message_parser(QString message)
{
json j = json::parse(message.toStdString());
if (j.contains("command"))
{
return QString::fromStdString(command_parser(j["command"]).dump());
}
else if (j.contains("event"))
{
event_handler(j["event"]);
return "";
}
else
{
return "";
}
}
json Simulators::command_parser(json command)
{
json j = "{}"_json;
try
{
std::string type = command.at("type");
Command cmd = magic_enum::enum_cast<Command>(type).value_or(Command::end);
switch (cmd)
{
case Command::get_UI_element:
spdlog::info("Command {}: {}", type, (size_t)command.at("id"));
j["response"]["type"] = type;
j["response"]["UI_item"] = invoke_active_simulator()->get_UI_item(command.at("id"))->to_json(command.at("id"));
break;
case Command::get_UI_elements:
spdlog::info("Command {}", type);
j["response"]["type"] = type;
j["response"]["UI_items"] = invoke_active_simulator()->get_UI_items()["UI_items"];
break;
case Command::switch_simulator:
spdlog::info("Command {} to {}", type, std::string(command.at("name")));
switch_simulator(command.at("name"));
j["response"]["type"] = type;
break;
case Command::get_active_simulator_name:
spdlog::info("Command {}", type);
j["response"]["type"] = type;
j["response"]["name"] = active_simulator_name();
break;
case Command::get_simulators:
spdlog::info("Command {}", type);
j["response"]["type"] = type;
j["response"]["simulators"] = list_simulators();
break;
default:
break;
}
}
catch (const json::exception &e)
{
spdlog::error("{} {}", __FUNCTION__, e.what());
}
return j;
}
void Simulators::event_handler(json event)
{
try
{
std::string type = event.at("type");
Event e = magic_enum::enum_cast<Event>(type).value_or(Event::end);
if (invoke_active_simulator() == nullptr)
{
spdlog::error("Event got called but there is no active simulator");
return;
}
switch (e)
{
case Event::clicked: {
spdlog::debug("Event {}: {}", type, (size_t)event.at("id"));
invoke_active_simulator()->get_UI_item(event.at("id"))->click();
break;
}
case Event::value_changed: {
spdlog::debug("Event {}: {} {}", type, (size_t)event.at("id"), (double)(event["value"]));
invoke_active_simulator()->get_UI_item(event.at("id"))->set_value((double)event.at("value"));
break;
}
case Event::text_changed: {
spdlog::debug("Event {}: {} {}", type, (size_t)event.at("id"), std::string(event.at("text")));
invoke_active_simulator()
->get_UI_item(event.at("id"))
->set_text(std::string(event.at("text")));
break;
}
case Event::selected: {
spdlog::debug("Event {}: {} {}", type, (size_t)event.at("id"), std::string(event.at("selected")));
invoke_active_simulator()
->get_UI_item(event.at("id"))
->set_selected(std::string(event.at("selected")));
break;
}
case Event::can_send: {
spdlog::debug("Event {}: {}", type, (size_t)event.at("id"));
invoke_active_simulator()
->get_UI_item(event.at("id"))
->can_send((uint32_t)event.at("sid"), (uint8_t)event.at("dlc"), (std::array<uint8_t, 8>)event.at("payload"));
break;
}
case Event::can_clear: {
spdlog::debug("Event {}: {}", type, (size_t)event.at("id"));
invoke_active_simulator()->get_UI_item(event.at("id"))->can_clear();
break;
}
default: {
break;
}
}
}
catch (const json::exception &e)
{
spdlog::error("{} {}", __FUNCTION__, e.what());
}
}
+70
View File
@@ -0,0 +1,70 @@
#include "simulator_base.hpp"
Simulator_base::Simulator_base(std::string name, std::chrono::milliseconds interval, QObject *parent)
: QObject(parent), m_name(name), m_timer(new QTimer(this)), m_interval(interval)
{
connect(m_timer, &QTimer::timeout, this, [this] { timer(); });
m_UI_items.reserve(50);
}
std::string Simulator_base::name() const
{
// Get the name of the simulator
return m_name;
}
void Simulator_base::add_UI_item(UI_item *item)
{
// Add a UI item to the simulator
connect(item, &UI_item::value_changed, this, [this] { emit sim_changed(); });
m_UI_items.push_back(item);
}
json Simulator_base::get_UI_items() const
{
// Get the UI items
json items;
items["name"] = m_name;
for (size_t i = 0; i < m_UI_items.size(); i++)
{
items["UI_items"].push_back(m_UI_items[i]->to_json(i));
}
return items;
}
UI_item *Simulator_base::get_UI_item(size_t id) const
{
// Get a UI item by id using the list
if (id < m_UI_items.size())
{
return m_UI_items[id];
}
return nullptr;
}
void Simulator_base::run()
{
// Start the timer
m_timer->start(m_interval);
run_at_startup();
}
void Simulator_base::stop()
{
// Stop the simulator
run_at_stop();
m_timer->stop();
}
void Simulator_base::shutdown()
{
// Shutdown the simulator
system("shutdown -h now");
}
void Simulator_base::reboot()
{
// Reboot the simulator
system("reboot");
}
+105
View File
@@ -0,0 +1,105 @@
#pragma once
#include "UI.hpp"
#include "spdlog/spdlog.h"
#include <QTimer>
class Simulator_base : public QObject
{
Q_OBJECT
public:
/**
* @brief Construct a new Simulator_base object
*
* @param name The name of the simulator
* @param interval The interval of the timer (default is 100ms)
*/
Simulator_base(std::string name, std::chrono::milliseconds interval = std::chrono::milliseconds(100),
QObject *parent = nullptr);
/**
* @brief Returns the name of the simulator
*
* @return std::string The name of the simulator
*/
std::string name() const;
/**
* @brief This function adds a UI item to the simulator
* only if the id is unique
* Use this function only in the constructor of the simulator
*
* @param item The UI item to add
*/
void add_UI_item(UI_item *item);
/**
* @brief Get the UI items object
*
* @return json The UI items in json format
*/
json get_UI_items() const;
/**
* @brief Get the UI item object by id
*
* @param id The id of the UI item
* @return UI_item* A pointer to the UI item object
*/
UI_item *get_UI_item(size_t id) const;
size_t ptr_to_id(UI_item *ptr) const
{
size_t id = 0;
while (id < m_UI_items.size())
{
if (m_UI_items[id] == ptr)
{
return id;
}
id++;
}
return (size_t)-1;
}
/**
* @brief Run the simulator
*/
void run();
/**
* @brief Stop the simulator
*/
void stop();
/**
* @brief Timer function
* This function is called every time the timer ticks
* Override this function in the derived class
*/
virtual void timer() {};
/**
* @brief Run at startup
* This function is called at startup
* Override this function in the derived class
*/
virtual void run_at_startup() {};
/**
* @brief Run at stop
* This function is called when a simulator is stoped
* Override this function in the derived class
*/
virtual void run_at_stop() {};
void shutdown();
void reboot();
signals:
void sim_changed();
protected:
std::string m_name;
std::vector<UI_item *> m_UI_items;
QTimer *m_timer;
std::chrono::milliseconds m_interval;
};
+168
View File
@@ -0,0 +1,168 @@
#include "simulators.hpp"
#include "can_debugger.hpp"
#include "test_sim.hpp"
#include <QDebug>
#include <unordered_set>
Simulators::Simulators(uint16_t port, QObject *parent) : QObject(parent), m_server(new Websocket(port, this))
{
INSERT_SIMULATOR(Test_Sim);
INSERT_SIMULATOR(Can_Debugger);
setup_connections();
}
void Simulators::setup_connections()
{
connect(m_server, &Websocket::on_message, this, [this](QWebSocket *conn, QString message) {
QString response = message_parser(message);
if (response.isEmpty() || response == "{}")
return;
m_server->send(conn, response);
});
}
std::string Simulators::active_simulator_name() const
{
return this->m_current_simulator;
}
void Simulators::run()
{
if (this->m_simulators.empty())
{
spdlog::warn("no simulators found");
return;
}
if (this->m_current_simulator.empty())
{
spdlog::warn("no active simulator");
return;
}
m_before = this->m_simulators.at(m_current_simulator)->get_UI_items();
this->m_simulators.at(m_current_simulator)->run();
}
void Simulators::stop()
{
if (this->m_simulators.empty())
{
spdlog::warn("no simulators found");
return;
}
if (this->m_current_simulator.empty())
{
spdlog::warn("no active simulator");
return;
}
this->m_simulators.at(m_current_simulator)->stop();
}
void Simulators::switch_simulator(std::string name)
{
if (this->m_simulators.empty())
{
spdlog::warn("no simulators found");
return;
}
if (!this->m_current_simulator.empty())
{
spdlog::info("stopping current simulator {} and switching to {}", m_current_simulator, name);
this->m_simulators.at(m_current_simulator)->stop();
}
if (this->m_simulators.find(name) == this->m_simulators.end())
{
spdlog::warn("simulator {} not found", name);
return;
}
this->stop();
if (!this->m_current_simulator.empty())
{
disconnect(m_simulators.at(m_current_simulator).get());
}
this->m_current_simulator = name;
connect(m_simulators.at(m_current_simulator).get(), &Simulator_base::sim_changed, this,
[this] { m_server->broadcast(QString::fromStdString(changed_UI_items().dump())); });
this->run();
}
std::vector<std::string> Simulators::list_simulators() const
{
std::vector<std::string> names;
for (const auto &simulator : this->m_simulators)
{
names.push_back(simulator.first);
}
return names;
}
Simulator_base *Simulators::invoke_active_simulator()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (this->m_simulators.empty())
{
return nullptr;
}
if (this->m_current_simulator.empty())
{
return nullptr;
}
m_before = this->m_simulators.at(m_current_simulator)->get_UI_items();
return this->m_simulators.at(m_current_simulator).get();
}
json Simulators::changed_UI_items()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (this->m_simulators.empty())
{
return "{}"_json;
}
if (this->m_current_simulator.empty())
{
return "{}"_json;
}
json after = this->m_simulators.at(m_current_simulator)->get_UI_items();
json changed;
changed["event"]["type"] = "ui_changed";
json diff = json::diff(m_before, after);
if (diff.empty())
{
return "{}"_json;
}
// diff to ui items list
// get index out of each item in diff /UI_items/0/**
// but the index can be the same for multiple items
// Then push the ui_item at that index to the changed json
std::unordered_set<size_t> addedIndices;
try
{
for (auto &item : diff)
{
std::string path = item["path"];
size_t index = std::stoul(path.substr(10, path.find("/", 10) - 10));
if (addedIndices.find(index) != addedIndices.end())
continue;
changed["event"]["UI_items"].push_back(after["UI_items"][index]);
addedIndices.insert(index);
}
}
catch (const std::exception &e)
{
spdlog::error(e.what());
}
m_before = after;
return changed;
}
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include <map>
#include <memory>
#include <mutex>
#include "simulator_base.hpp"
#include "websocket.hpp"
#define INSERT_SIMULATOR(type) m_simulators.insert(std::make_pair(type().name(), std::make_unique<type>()));
class Simulators : public QObject
{
Q_OBJECT
public:
enum class Command
{
get_UI_element,
get_UI_elements,
switch_simulator,
get_active_simulator_name,
get_simulators,
end,
begin = 0
};
enum class Event
{
clicked,
value_changed,
text_changed,
selected,
can_send,
can_clear,
ui_changed,
end,
begin = 0
};
Simulators(uint16_t port, QString can_dev, QObject *parent = nullptr);
void setup_connections();
std::string active_simulator_name() const;
void run();
void stop();
void switch_simulator(std::string name);
std::vector<std::string> list_simulators() const;
Simulator_base *invoke_active_simulator();
json changed_UI_items();
// parsing of websocket messages
QString message_parser(QString message);
json command_parser(json command);
void event_handler(json event);
private:
Websocket *m_server;
std::map<std::string, std::unique_ptr<Simulator_base>> m_simulators;
std::string m_current_simulator = "";
std::mutex m_mutex;
json m_before;
};
+27
View File
@@ -0,0 +1,27 @@
#include "can_debugger.hpp"
Can_Debugger::Can_Debugger(CAN_Wrapper* can_wrapper, QObject* parent) :
Simulator_base("Can_Debugger", can_wrapper, std::chrono::milliseconds(100), parent),
m_wrapper(can_wrapper),
m_can_ui(Color::Black, Color::White)
{
this->add_UI_item(&this->m_can_ui);
connect(&m_can_ui, &UI_can::on_send, this, [=, this](QCanBusFrame frame){m_wrapper->send_can_message(frame);});
}
void Can_Debugger::timer()
{
// Timer function
}
void Can_Debugger::run_at_startup()
{
connect(m_wrapper, &CAN_Wrapper::on_can_message, this, [=, this](QCanBusFrame frame){this->m_can_ui.add_received_message(frame);});
connect(m_wrapper, &CAN_Wrapper::on_can_send, this, [=, this](QCanBusFrame frame){this->m_can_ui.add_send_message(frame);});
}
void Can_Debugger::run_at_stop()
{
disconnect(m_wrapper, nullptr, nullptr, nullptr);
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "simulator_base.hpp"
class Can_Debugger : public Simulator_base {
Q_OBJECT
public:
Can_Debugger();
void timer() override;
void run_at_startup() override;
void run_at_stop() override;
private:
CAN_Wrapper* m_wrapper;
UI_can m_can_ui;
};
+73
View File
@@ -0,0 +1,73 @@
#include "test_sim.hpp"
Test_Sim::Test_Sim(QObject* parent) :
Simulator_base("Test_Sim", std::chrono::milliseconds(1000), parent),
m_button("Off", Color(Color::White), Color(Color::Red)),
m_combobox(Color(Color::White), Color(Color::Black), {"Option 1", "Option 2", "Option 3"}, 0),
m_label("Label", Color(Color::White), Color(Color::Black)),
m_checkbox("Option 1", Color(Color::White), Color(Color::Black)),
m_slider("Slider", Color(Color::White), Color(Color::Black), 0, 100, 0),
m_dial("Dial", Color(Color::White), Color(Color::Black), 0, 100, 0),
m_thermo("Thermo", "%", Color(Color::White), Color(Color::Black), 0, 100, 0),
m_led("Led", Color(Color::White), Color(Color::Black), Color(Color::Red))
{
this->m_thermo.set_start_color(Color(Color::Red));
this->m_thermo.set_end_color(Color(Color::Red));
this->m_thermo.add_color_stop(0.10, Color(Color::Yellow));
this->m_thermo.add_color_stop(0.50, Color(Color::Green));
this->m_thermo.add_color_stop(0.60, Color(Color::Yellow));
this->m_thermo.add_color_stop(0.90, Color(Color::Red));
this->add_UI_item(&this->m_button);
this->add_UI_item(&this->m_combobox);
this->add_UI_item(&this->m_label);
this->add_UI_item(&this->m_slider);
this->add_UI_item(&this->m_dial);
this->add_UI_item(&this->m_thermo);
this->add_UI_item(&this->m_led);
this->add_UI_item(&this->m_checkbox);
QObject::connect(&m_button, &UI_button::on_click, this, [=, this] { button_clicked(&m_button); });
QObject::connect(&m_slider, &UI_slider::on_change, this, [=, this](double value) { slider_changed(&m_slider, value); });
}
void Test_Sim::timer()
{
this->m_button.click();
if (this->m_slider.value() == this->m_slider.max())
{
this->m_slider.set_value(0);
}
this->m_slider.set_value(this->m_slider.value() + 1);
}
void Test_Sim::run_at_startup()
{
// Run at startup
spdlog::info("Test_Sim started");
}
void Test_Sim::button_clicked(UI_button* id)
{
if (id == &this->m_button)
{
bool state = this->m_button.bg_color() == Color(Color::Red) ? true : false;
this->m_button.set_bg_color(state ? Color(Color::Green) : Color(Color::Red));
this->m_button.set_text(state ? "On" : "Off");
this->m_led.set_led_color(state ? Color(Color::Green) : Color(Color::Red));
return;
}
}
void Test_Sim::slider_changed(UI_slider* id, double value)
{
if (id == &this->m_slider)
{
spdlog::info("Test slider changed to {}", value);
this->m_label.set_text(std::to_string(value));
this->m_dial.set_value(value);
this->m_thermo.set_value(value);
return;
}
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "simulator_base.hpp"
class Test_Sim : public Simulator_base {
Q_OBJECT
public:
Test_Sim(QObject* parent = nullptr);
void timer() override;
void run_at_startup() override;
private:
void button_clicked(UI_button* id);
void slider_changed(UI_slider* id, double value);
private:
UI_button m_button;
UI_combobox m_combobox;
UI_label m_label;
UI_checkbox m_checkbox;
UI_slider m_slider;
UI_dial m_dial;
UI_thermo m_thermo;
UI_led m_led;
};
+74
View File
@@ -0,0 +1,74 @@
#include "websocket.hpp"
#include "QtWebSockets/qwebsocket.h"
#include "QtWebSockets/qwebsocketserver.h"
#include <spdlog/spdlog.h>
Websocket::Websocket(uint16_t port, QObject *parent)
: QObject(parent),
m_pWebSocketServer(new QWebSocketServer(QStringLiteral("sim_us Server"), QWebSocketServer::NonSecureMode, this))
{
if (m_pWebSocketServer->listen(QHostAddress::Any, port))
{
spdlog::debug("Server listening on port {}", port);
connect(m_pWebSocketServer, &QWebSocketServer::newConnection, this, &Websocket::onNewConnection);
connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &Websocket::closed);
}
}
Websocket::~Websocket()
{
m_pWebSocketServer->close();
qDeleteAll(m_clients.begin(), m_clients.end());
}
void Websocket::broadcast(QString message)
{
foreach (QWebSocket *conn, m_clients)
{
conn->sendTextMessage(message);
}
}
void Websocket::send(QWebSocket *conn, QString message)
{
conn->sendTextMessage(message);
spdlog::trace("sending message: {}", message.toStdString());
}
void Websocket::onNewConnection()
{
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
connect(pSocket, &QWebSocket::textMessageReceived, this, &Websocket::processTextMessage);
connect(pSocket, &QWebSocket::binaryMessageReceived, this, &Websocket::processBinaryMessage);
connect(pSocket, &QWebSocket::disconnected, this, &Websocket::socketDisconnected);
m_clients << pSocket;
spdlog::debug("socket connected");
}
void Websocket::processTextMessage(QString message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
spdlog::trace("Message received: {}", message.toStdString());
emit on_message(pClient, message);
}
void Websocket::processBinaryMessage(QByteArray message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
spdlog::trace("Binary Message received: {}", message.data());
Q_UNUSED(message);
Q_UNUSED(pClient);
}
void Websocket::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
spdlog::debug("socket Disconnected");
if (pClient)
{
m_clients.removeAll(pClient);
pClient->deleteLater();
}
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QObject>
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)
class Websocket : public QObject
{
Q_OBJECT
public:
Websocket(uint16_t port, QObject *parent = nullptr);
~Websocket();
void broadcast(QString message);
void send(QWebSocket *conn, QString message);
signals:
void on_message(QWebSocket *conn, QString message);
void closed();
private slots:
void onNewConnection();
void processTextMessage(QString message);
void processBinaryMessage(QByteArray message);
void socketDisconnected();
private:
QWebSocketServer *m_pWebSocketServer;
QList<QWebSocket *> m_clients;
};