mirror of
https://github.com/Sani7/remote-ui.git
synced 2026-10-27 06:01:14 +01:00
Fix rebase errors
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
)
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user