Ferenc Szontágh
2024-06-27 920507bff803647c79dfce27c4c265b2caee7f8d
some refactor, addedd google loggin', corrected plugin handling
11 files modified
13 files added
4 files deleted
740 ■■■■ changed files
.gitignore 3 ●●●● patch | view | raw | blame | history
.gitmodules 15 ●●●●● patch | view | raw | blame | history
.vscode/settings.json 80 ●●●●● patch | view | raw | blame | history
CMakeLists.txt 42 ●●●● patch | view | raw | blame | history
external/cista 1 ●●●● patch | view | raw | blame | history
external/rocksdbwrapper @ 3ac954 1 ●●●● patch | view | raw | blame | history
external/tser @ 9f4423 1 ●●●● patch | view | raw | blame | history
src/Command.h 18 ●●●●● patch | view | raw | blame | history
src/IPC.cpp 42 ●●●● patch | view | raw | blame | history
src/IPC.h 20 ●●●●● patch | view | raw | blame | history
src/PluginInterface.h 3 ●●●● patch | view | raw | blame | history
src/Serialize.h 24 ●●●●● patch | view | raw | blame | history
src/Server.cpp 130 ●●●● patch | view | raw | blame | history
src/Server.h 6 ●●●●● patch | view | raw | blame | history
src/main.cpp 7 ●●●●● patch | view | raw | blame | history
src/plugins/CMakeLists.txt 6 ●●●●● patch | view | raw | blame | history
src/plugins/DatabasePlugin/CMakeLists.txt 12 ●●●●● patch | view | raw | blame | history
src/plugins/DatabasePlugin/DataBasePlugin.cpp 21 ●●●●● patch | view | raw | blame | history
src/plugins/DatabasePlugin/DataBasePlugin.h 24 ●●●●● patch | view | raw | blame | history
src/plugins/SamplePlugin.cpp 18 ●●●●● patch | view | raw | blame | history
src/plugins/SamplePlugin.h 13 ●●●●● patch | view | raw | blame | history
src/plugins/SamplePlugin/CMakeLists.txt 12 ●●●●● patch | view | raw | blame | history
src/plugins/SamplePlugin/SamplePlugin.cpp 27 ●●●●● patch | view | raw | blame | history
src/plugins/SamplePlugin/SamplePlugin.h 22 ●●●●● patch | view | raw | blame | history
src/plugins/TcpServerPlugin/CMakeLists.txt 31 ●●●●● patch | view | raw | blame | history
src/plugins/TcpServerPlugin/TcpServerPlugin.cpp 122 ●●●●● patch | view | raw | blame | history
src/plugins/TcpServerPlugin/TcpServerPlugin.h 38 ●●●●● patch | view | raw | blame | history
src/plugins/TcpServerPlugin/external/sockets-cpp @ 4b46f5 1 ●●●● patch | view | raw | blame | history
.gitignore
@@ -1,4 +1,3 @@
build
# Compiled Object files
*.slo
*.lo
@@ -20,3 +19,5 @@
*.exe
*.out
*.app
build
.vscode
.gitmodules
@@ -1,6 +1,9 @@
[submodule "external/serio"]
    path = external/serio
    url = https://github.com/ShahriarRezghi/serio.git
[submodule "external/cista"]
    path = external/cista
    url = https://github.com/felixguendling/cista.git
[submodule "src/plugins/TcpServerPlugin/external/sockets-cpp"]
    path = src/plugins/TcpServerPlugin/external/sockets-cpp
    url = https://github.com/CJLove/sockets-cpp.git
[submodule "external/tser"]
    path = external/tser
    url = https://github.com/KonanM/tser.git
[submodule "external/rocksdbwrapper"]
    path = external/rocksdbwrapper
    url = https://git.spamming.hu/r/fcloud/RocksDBWrapper.git
.vscode/settings.json
@@ -1,5 +1,83 @@
{
    "files.associations": {
        "string": "cpp"
        "string": "cpp",
        "ostream": "cpp",
        "optional": "cpp",
        "memory": "cpp",
        "unordered_map": "cpp",
        "condition_variable": "cpp",
        "any": "cpp",
        "array": "cpp",
        "atomic": "cpp",
        "bit": "cpp",
        "*.tcc": "cpp",
        "bitset": "cpp",
        "cctype": "cpp",
        "cfenv": "cpp",
        "charconv": "cpp",
        "chrono": "cpp",
        "cinttypes": "cpp",
        "clocale": "cpp",
        "cmath": "cpp",
        "codecvt": "cpp",
        "compare": "cpp",
        "complex": "cpp",
        "concepts": "cpp",
        "csignal": "cpp",
        "cstdarg": "cpp",
        "cstddef": "cpp",
        "cstdint": "cpp",
        "cstdio": "cpp",
        "cstdlib": "cpp",
        "cstring": "cpp",
        "ctime": "cpp",
        "cwchar": "cpp",
        "cwctype": "cpp",
        "deque": "cpp",
        "forward_list": "cpp",
        "list": "cpp",
        "map": "cpp",
        "set": "cpp",
        "unordered_set": "cpp",
        "vector": "cpp",
        "exception": "cpp",
        "expected": "cpp",
        "algorithm": "cpp",
        "functional": "cpp",
        "iterator": "cpp",
        "memory_resource": "cpp",
        "numeric": "cpp",
        "random": "cpp",
        "ratio": "cpp",
        "source_location": "cpp",
        "string_view": "cpp",
        "system_error": "cpp",
        "tuple": "cpp",
        "type_traits": "cpp",
        "utility": "cpp",
        "format": "cpp",
        "fstream": "cpp",
        "initializer_list": "cpp",
        "iomanip": "cpp",
        "iosfwd": "cpp",
        "iostream": "cpp",
        "istream": "cpp",
        "limits": "cpp",
        "mutex": "cpp",
        "new": "cpp",
        "numbers": "cpp",
        "ranges": "cpp",
        "semaphore": "cpp",
        "span": "cpp",
        "sstream": "cpp",
        "stdexcept": "cpp",
        "stop_token": "cpp",
        "streambuf": "cpp",
        "thread": "cpp",
        "typeindex": "cpp",
        "typeinfo": "cpp",
        "valarray": "cpp",
        "variant": "cpp",
        "regex": "cpp"
    }
}
CMakeLists.txt
@@ -1,21 +1,44 @@
cmake_minimum_required(VERSION 3.10)
project(MyServerProject)
set(PROJECT_NAME "FServer")
project(${PROJECT_NAME})
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fsanitize=address")
# Add Address Sanitizer flags if available
find_library(ASAN_LIBRARY asan)
if (ASAN_LIBRARY)
    message(STATUS "Address Sanitizer found: ${ASAN_LIBRARY}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
else()
    message(WARNING "Address Sanitizer not found. Proceeding without ASAN.")
endif()
find_package (glog REQUIRED)
# Include the nlohmann/json library
find_package(nlohmann_json REQUIRED)
find_package(msgpack REQUIRED)
include_directories(${CMAKE_SOURCE_DIR}/src)
include_directories(${CMAKE_SOURCE_DIR}/plugins)
add_subdirectory(external/serio)
# Add external libraries
add_subdirectory(external/tser)
include_directories(${CMAKE_SOURCE_DIR}/external/tser/include)
# Set the output directories for executables and libraries
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
# Main server executable
add_executable(server
add_executable(${PROJECT_NAME}
    src/main.cpp
    src/Server.cpp
    src/IPC.cpp
@@ -23,8 +46,9 @@
)
# Link libraries
target_link_libraries(server ${SERIO_LIBRARIES} nlohmann_json::nlohmann_json)
target_include_directories(server PUBLIC ${SERIO_INCLUDE_DIRS})
message(STATUS "Serio: \n\t${SERIO_LIBRARIES} \n\t${SERIO_INCLUDE_DIRS}")
# Add the plugins subdirectory
add_subdirectory(src/plugins)
target_link_libraries(${PROJECT_NAME} nlohmann_json::nlohmann_json glog::glog)
# Add the plugins subdirectories
add_subdirectory(${CMAKE_SOURCE_DIR}/src/plugins/SamplePlugin)
add_subdirectory(${CMAKE_SOURCE_DIR}/src/plugins/TcpServerPlugin)
add_subdirectory(${CMAKE_SOURCE_DIR}/src/plugins/DatabasePlugin)
external/cista
File was deleted
external/rocksdbwrapper
New file
@@ -0,0 +1 @@
Subproject commit 3ac954922108b07fb3f7a7ce1c727bfcfad8b263
external/tser
New file
@@ -0,0 +1 @@
Subproject commit 9f44236de8fc9e1af5bbea443c8fe8b5a4952ec5
src/Command.h
@@ -1,30 +1,28 @@
#ifndef COMMAND_H
#define COMMAND_H
#include <serio/serio.h>
#include <tser/tser.hpp>
#include <string>
#include <unordered_map>
#include <cstdint>
// Define the command types as an enum
enum class CommandType {
enum class CommandType
{
    PluginRegistered = 1000,
    PluginList = 1001
};
SERIO_REGISTER(CommandType::PluginRegistered, CommandType::PluginList)
// Map the CommandType enum to its string representation
static const std::unordered_map<CommandType, std::string> CommandTypeToString = {
    {CommandType::PluginRegistered, "PluginRegistered"},
    {CommandType::PluginList, "PluginList"}
};
    {CommandType::PluginList, "PluginList"}};
struct Command {
struct Command
{
    DEFINE_SERIALIZABLE(Command, commandType, payload)
    CommandType commandType;
    std::string payload;
    SERIO_REGISTER(commandType, payload);
};
#endif // COMMAND_H
src/IPC.cpp
@@ -1,27 +1,47 @@
#include "IPC.h"
#include <iostream>
IPC::IPC() {}
IPC::IPC()
{
    DLOG(INFO) << "IPC ready.. queue size at init: " << messageQueue.size();
}
void IPC::registerHandler(std::shared_ptr<IPlugin> handler) {
IPC::~IPC()
{
    while (!messageQueue.empty())
    {
        messageQueue.pop();
    }
}
void IPC::registerHandler(std::shared_ptr<IPlugin> handler)
{
    handlers.push_back(handler);
}
void IPC::sendMessage(const Command& cmd) {
void IPC::sendMessage(const Command &cmd)
    {
        std::lock_guard<std::mutex> lock(queueMutex);
        messageQueue.push(cmd);
    }
    std::lock_guard lock(queueMutex);
    std::string val;
    tser::Serialize(cmd, val);
    messageQueue.push(std::move(val));
    queueCondVar.notify_one();
}
std::optional<Command> IPC::receiveMessage() {
std::optional<Command> IPC::receiveMessage()
{
    std::unique_lock<std::mutex> lock(queueMutex);
    queueCondVar.wait(lock, [this] { return !messageQueue.empty(); });
    queueCondVar.wait(lock, [this]
                      { return !messageQueue.empty(); });
    if (!messageQueue.empty()) {
        Command cmd = messageQueue.front();
    if (!messageQueue.empty())
    {
        std::string a = std::move(messageQueue.front());
        messageQueue.pop();
        return cmd;
        Command c;
        tser::DeSerialize(a, c);
        return c;
    }
    return std::nullopt;
}
src/IPC.h
@@ -1,26 +1,32 @@
#ifndef IPC_H
#define IPC_H
#include <msgpack.h>
#include <optional>
#include <queue>
#include <mutex>
#include <condition_variable>
#include "Command.h"
#include "PluginInterface.h"
#include <memory>
#include <optional>
#include "tser/tser.hpp"
#include "Serialize.h"
#include "PluginInterface.h"  // Include your IPlugin interface header
#include "Command.h"  // Include your Command structure header
#include <glog/logging.h>
class IPC {
class IPC
{
public:
    IPC();
    ~IPC();
    void registerHandler(std::shared_ptr<IPlugin> handler);
    void sendMessage(const Command& cmd);
    std::optional<Command> receiveMessage();
private:
    std::queue<Command> messageQueue;
    std::queue<std::string> messageQueue;
    std::vector<std::shared_ptr<IPlugin>> handlers;
    std::mutex queueMutex;
    std::condition_variable queueCondVar;
    std::vector<std::shared_ptr<IPlugin>> handlers;
};
#endif // IPC_H
src/PluginInterface.h
@@ -9,8 +9,7 @@
    virtual ~IPlugin() = default;
    virtual void handleMessage(const Command& cmd) = 0;
    virtual void updateConfig(const nlohmann::json& config) = 0;
    virtual const std::string getPluginName() = 0;
};
extern "C" IPlugin* create();
#endif // PLUGIN_INTERFACE_H
src/Serialize.h
New file
@@ -0,0 +1,24 @@
#ifndef __SERIALIZE_H
#define __SERIALIZE_H
#include <tser/tser.hpp>
namespace tser
{
    template <typename T>
    inline void DeSerialize(const std::string &data, T &value)
    {
        tser::BinaryArchive archive(0);
        archive.initialize(data);
        value = archive.load<T>();
    };
    template <typename T>
    inline void Serialize(const T &value, std::string &data)
    {
        tser::BinaryArchive archive(0);
        archive.save(value);
        data = archive.get_buffer().data();
    };
}
#endif
src/Server.cpp
@@ -1,73 +1,123 @@
#include "Server.h"
#include <iostream>
#include <csignal>
#include <chrono>
// Initialize the static instance pointer
Server* Server::instance = nullptr;
Server::Server() : reloadConfigFlag(false) {
Server::Server() : reloadConfigFlag(false)
{
    instance = this; // Set the instance pointer
    ipc = std::make_unique<IPC>();
    signal(SIGUSR1, Server::handleSignal);
    if (!config.loadFromFile("config.json")) {
    if (!config.loadFromFile("/etc/fserver/config.json"))
    {
        std::cerr << "Failed to load config.json" << std::endl;
    }
    loadPlugins();
    for (int i = 0; i < 4; ++i) {
        threadPool.emplace_back(std::make_unique<std::thread>(&Server::handleIPC, this));
    for (int i = 0; i < 4; ++i)
    {
        threadPool.emplace_back(&Server::handleIPC, this);
    }
}
Server::~Server() {
    for (auto& handle : pluginHandles) {
Server::~Server()
{
    // Join all threads
    for (auto &thread : threadPool)
    {
        if (thread.joinable())
        {
            thread.join();
        }
    }
    // Close all plugin handles
    for (auto &handle : pluginHandles)
    {
        dlclose(handle);
    }
}
Server& Server::getInstance() {
Server &Server::getInstance()
{
    return *instance;
}
void Server::run() {
void Server::run()
{
    mainLoop();
}
void Server::reloadConfig() {
    if (config.loadFromFile("config.json")) {
        std::cout << "Configuration reloaded." << std::endl;
        for (auto& plugin : plugins) {
            plugin->updateConfig(config.getConfig());
void Server::reloadConfig()
{
    if (config.loadFromFile("/etc/fserver/config.json"))
    {
        // std::cout << "Configuration reloaded." << std::endl;
        LOG(INFO) << "Configuration reloaded /etc/fserver/config.json";
        // Stop and clear existing plugins
        for (auto &plugin : plugins)
        {
            plugin->updateConfig({}); // Optionally reset plugin config
        }
    } else {
        std::cerr << "Failed to reload configuration." << std::endl;
        plugins.clear();
        // Close and clear plugin handles
        for (auto &handle : pluginHandles)
        {
            dlclose(handle);
        }
        pluginHandles.clear();
        loadPlugins();
    }
    else
    {
        LOG(ERROR) << "Failed to reload configuration";
    }
}
void Server::loadPlugins() {
    const char* pluginPath = "./plugins/libSamplePlugin.so";
    void* handle = dlopen(pluginPath, RTLD_LAZY);
    if (!handle) {
        std::cerr << "Cannot open library: " << dlerror() << '\n';
        return;
void Server::loadPlugins()
{
    auto configData = config.getConfig();
    auto pluginsArray = configData["plugins"];
    for (const auto &pluginName : pluginsArray)
    {
        std::string pluginPath = "./plugins/" + pluginName.get<std::string>();
        void *handle = dlopen(pluginPath.c_str(), RTLD_LAZY);
        if (!handle)
        {
            LOG(WARNING) << "Can not open lib: " << dlerror();
            continue;
    }
    pluginHandles.push_back(handle);
    typedef IPlugin* (*create_t)();
        typedef IPlugin *(*create_t)(IPC *);
    create_t create_plugin = (create_t)dlsym(handle, "create");
    const char* dlsym_error = dlerror();
    if (dlsym_error) {
        std::cerr << "Cannot load symbol create: " << dlsym_error << '\n';
        return;
        if (dlsym_error)
        {
            LOG(ERROR) << "Cannot load symbol create: " << dlsym_error;
            dlclose(handle);
            continue;
    }
    std::shared_ptr<IPlugin> plugin(create_plugin());
        std::shared_ptr<IPlugin> plugin(create_plugin(ipc.get()));
    plugins.push_back(plugin);
    ipc.registerHandler(plugin);
        ipc->registerHandler(plugin);
    plugin->updateConfig(config.getConfig()); // Forward the config to the plugin
        LOG(INFO) << "Plugin loaded: " << plugin->getPluginName();
    }
}
void Server::mainLoop() {
    while (true) {
        if (reloadConfigFlag.load()) {
void Server::mainLoop()
{
    while (true)
    {
        if (reloadConfigFlag.load())
        {
            reloadConfig();
            reloadConfigFlag.store(false);
        }
@@ -75,19 +125,25 @@
    }
}
void Server::handleIPC() {
    while (true) {
        auto message = ipc.receiveMessage();
        if (message.has_value()) {
            for (auto& plugin : plugins) {
void Server::handleIPC()
{
    while (true)
    {
        auto message = ipc->receiveMessage();
        if (message.has_value())
        {
            for (auto &plugin : plugins)
            {
                plugin->handleMessage(message.value());
            }
        }
    }
}
void Server::handleSignal(int signal) {
    if (signal == SIGUSR1) {
void Server::handleSignal(int signal)
{
    if (signal == SIGUSR1)
    {
        std::cout << "Received signal to reload configuration." << std::endl;
        Server::getInstance().reloadConfigFlag.store(true);
    }
src/Server.h
@@ -6,9 +6,11 @@
#include <memory>
#include <dlfcn.h>
#include <atomic>
#include <glog/logging.h>
#include "IPC.h"
#include "PluginInterface.h"
#include "Config.h"
class Server {
public:
@@ -25,10 +27,10 @@
    void handleIPC();
    static void handleSignal(int signal);
    std::vector<std::unique_ptr<std::thread>> threadPool;
    std::vector<std::thread> threadPool;  // Use std::thread directly
    std::vector<void*> pluginHandles;
    std::vector<std::shared_ptr<IPlugin>> plugins;
    IPC ipc;
    std::unique_ptr<IPC> ipc;
    Config config;
    std::atomic<bool> reloadConfigFlag;
src/main.cpp
@@ -1,7 +1,14 @@
#include "Server.h"
#include <iostream>
#include <glog/logging.h>
int main() {
    try {
        LOG(INFO) << "Starting up server...";
    Server server;
    server.run();
    } catch (const std::exception& e) {
        LOG(FATAL) << "Exception: " << e.what();
    }
    return 0;
}
src/plugins/CMakeLists.txt
File was deleted
src/plugins/DatabasePlugin/CMakeLists.txt
New file
@@ -0,0 +1,12 @@
# Sample Plugin compilation
add_library(DataBasePlugin SHARED
    DataBasePlugin.cpp
)
target_link_libraries(DataBasePlugin nlohmann_json::nlohmann_json)
target_include_directories(DataBasePlugin PUBLIC ${CMAKE_SOURCE_DIR}/external/rocksdbwrapper)
# Set the output directory for the plugins
set_target_properties(DataBasePlugin PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins
)
src/plugins/DatabasePlugin/DataBasePlugin.cpp
New file
@@ -0,0 +1,21 @@
#include "DataBasePlugin.h"
extern "C" IPlugin *create(IPC *ipc)
{
    return new DataBasePlugin(ipc);
}
DataBasePlugin::DataBasePlugin(IPC* ipc) : ipc(ipc)
{
}
void DataBasePlugin::handleMessage(const Command &cmd)
{
    auto it = CommandTypeToString.find(cmd.commandType);
}
void DataBasePlugin::updateConfig(const nlohmann::json &config)
{
    DLOG(INFO) << "Updated plugin config: " << config.dump() << std::endl;
}
const std::string DataBasePlugin::getPluginName() { return this->plugin_name; }
src/plugins/DatabasePlugin/DataBasePlugin.h
New file
@@ -0,0 +1,24 @@
#ifndef DB_PLUGIN_H
#define DB_PLUGIN_H
#include "PluginInterface.h"
#include "IPC.h"
#include <iostream>
#include <glog/logging.h>
#include "RocksDBWrapper.h"
class DataBasePlugin : public IPlugin
{
private:
    IPC *ipc;
public:
    DataBasePlugin(IPC *ipc);
    void handleMessage(const Command &cmd) override;
    void updateConfig(const nlohmann::json &config) override;
    const std::string getPluginName() override;
    const std::string plugin_name = "RocksDB";
};
#endif // DB_PLUGIN_H
src/plugins/SamplePlugin.cpp
File was deleted
src/plugins/SamplePlugin.h
File was deleted
src/plugins/SamplePlugin/CMakeLists.txt
New file
@@ -0,0 +1,12 @@
# Sample Plugin compilation
add_library(SamplePlugin SHARED
    SamplePlugin.cpp
)
target_link_libraries(SamplePlugin nlohmann_json::nlohmann_json)
target_include_directories(SamplePlugin PUBLIC ${CMAKE_SOURCE_DIR}/external/cista/include)
# Set the output directory for the plugins
set_target_properties(SamplePlugin PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins
)
src/plugins/SamplePlugin/SamplePlugin.cpp
New file
@@ -0,0 +1,27 @@
#include "SamplePlugin.h"
extern "C" IPlugin *create(IPC *ipc)
{
    return new SamplePlugin(ipc);
}
SamplePlugin::SamplePlugin(IPC* ipc) : ipc(ipc)
{
}
void SamplePlugin::handleMessage(const Command &cmd)
{
    auto it = CommandTypeToString.find(cmd.commandType);
    if (it != CommandTypeToString.end())
    {
        DLOG(INFO) << "Handled command: " << it->second << " with payload: " << cmd.payload;
    }
}
void SamplePlugin::updateConfig(const nlohmann::json &config)
{
    DLOG(INFO) << "Updated plugin config: " << config.dump() << std::endl;
}
const std::string SamplePlugin::getPluginName() { return this->plugin_name;  }
src/plugins/SamplePlugin/SamplePlugin.h
New file
@@ -0,0 +1,22 @@
#ifndef SAMPLE_PLUGIN_H
#define SAMPLE_PLUGIN_H
#include "PluginInterface.h"
#include "IPC.h"
#include <iostream>
#include <glog/logging.h>
class SamplePlugin : public IPlugin
{
private:
    IPC *ipc;
public:
    SamplePlugin(IPC *ipc);
    void handleMessage(const Command &cmd) override;
    void updateConfig(const nlohmann::json &config) override;
    const std::string getPluginName() override;
    const std::string plugin_name = "SamplePlugin";
};
#endif // SAMPLE_PLUGIN_H
src/plugins/TcpServerPlugin/CMakeLists.txt
New file
@@ -0,0 +1,31 @@
# Tcp Server Plugin compilation
add_library(TcpServerPlugin SHARED
    TcpServerPlugin.cpp ${CMAKE_SOURCE_DIR}/src/IPC.cpp
)
include(ExternalProject)
ExternalProject_Add(socketscpp
    GIT_REPOSITORY    https://github.com/CJLove/sockets-cpp.git
    GIT_TAG           master
    UPDATE_COMMAND    ""
    PATCH_COMMAND     ""
    CONFIGURE_COMMAND ""
    BUILD_COMMAND     ""
    INSTALL_COMMAND   ""
    LOG_DOWNLOAD      ON
)
# Set include directories for socketscpp
ExternalProject_Get_Property(socketscpp source_dir)
set(SOCKETSCPP_INCLUDE_DIR ${source_dir}/include/sockets-cpp)
target_link_libraries(TcpServerPlugin nlohmann_json::nlohmann_json)
target_include_directories(TcpServerPlugin PUBLIC ${SOCKETSCPP_INCLUDE_DIR})
# Set the output directory for the plugins
set_target_properties(TcpServerPlugin PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins
)
add_dependencies(TcpServerPlugin socketscpp)
src/plugins/TcpServerPlugin/TcpServerPlugin.cpp
New file
@@ -0,0 +1,122 @@
#include "TcpServerPlugin.h"
#include <iostream>
TcpServerPlugin::TcpServerPlugin(IPC *ipc) : ipc(ipc), running(false)
{
    DLOG(INFO) << "TcpServerPlugin constructor called";
}
TcpServerPlugin::~TcpServerPlugin()
{
    DLOG(INFO) << "TcpServerPlugin destructor called";
    stopServer();
}
void TcpServerPlugin::handleMessage(const Command &cmd)
{
    DLOG(INFO) << "TcpServerPlugin::handleMessage called with command: " << CommandTypeToString.at(cmd.commandType);
}
void TcpServerPlugin::updateConfig(const nlohmann::json &config)
{
    DLOG(INFO) << "TcpServerPlugin::updateConfig called";
    this->config = config;
    if (running)
    {
        stopServer();
    }
    startServer();
}
const std::string TcpServerPlugin::getPluginName()
{
    return this->plugin_name;
}
void TcpServerPlugin::startServer()
{
    DLOG(INFO) << "TcpServerPlugin::startServer called";
    sockets::SocketOpt options;
    options.m_listenAddr = config["listen_addr"].get<std::string>();
    options.m_rxBufSize = 65536;
    options.m_txBufSize = 65536;
    if (config.contains("rxBufSize"))
    {
        options.m_rxBufSize = config["rxBufSize"].get<int>();
    }
    if (config.contains("txBufSize"))
    {
        options.m_txBufSize = config["txBufSize"].get<int>();
    }
    server = std::make_unique<sockets::TcpServer<TcpServerPlugin>>(*this, &options);
    auto result = server->start(config["port"].get<uint16_t>());
    if (!result.m_success)
    {
        LOG(FATAL) << "Failed to start TCP server: " << result.m_msg;
        return;
    }
    running = true;
    Command cmd;
    cmd.commandType = CommandType::PluginRegistered;
    cmd.payload = "TCP Server started";
    ipc->sendMessage(cmd);
}
void TcpServerPlugin::stopServer()
{
    DLOG(INFO) << "TcpServerPlugin::stopServer called";
    if (server)
    {
        server->finish();
        server.reset();
    }
    running = false;
    Command cmd;
    cmd.commandType = CommandType::PluginList;
    cmd.payload = "TCP Server stopped";
    ipc->sendMessage(cmd);
}
void TcpServerPlugin::onClientConnect(sockets::ClientHandle client)
{
    Command cmd;
    cmd.commandType = CommandType::PluginRegistered;
    cmd.payload = "New client connected: " + std::to_string(client);
    ipc->sendMessage(cmd);
    DLOG(INFO) << "New client connected: " + std::to_string(client);
}
void TcpServerPlugin::onClientDisconnect(sockets::ClientHandle client, const sockets::SocketRet &ret)
{
    Command cmd;
    cmd.commandType = CommandType::PluginRegistered;
    cmd.payload = "Client disconnected: " + std::to_string(client);
    ipc->sendMessage(cmd);
    DLOG(INFO) << "Client disconnected: " + std::to_string(client);
}
void TcpServerPlugin::onReceiveClientData(sockets::ClientHandle client, const char *data, size_t size)
{
    std::string message(data, size);
    Command cmd;
    cmd.commandType = CommandType::PluginRegistered;
    cmd.payload = "Message from client: " + message;
    ipc->sendMessage(cmd);
    // Echo back the message to the client
    server->sendClientMessage(client, data, size);
}
extern "C" IPlugin *create(IPC *ipc)
{
    return new TcpServerPlugin(ipc);
}
src/plugins/TcpServerPlugin/TcpServerPlugin.h
New file
@@ -0,0 +1,38 @@
#ifndef TCPSERVERPLUGIN_H
#define TCPSERVERPLUGIN_H
#include "PluginInterface.h"
#include "IPC.h"
#include <nlohmann/json.hpp>
#include <memory>
#include <TcpServer.h>
#include <glog/logging.h>
class TcpServerPlugin : public IPlugin {
public:
    TcpServerPlugin(IPC *ipc);
    ~TcpServerPlugin();
    void handleMessage(const Command& cmd) override;
    void updateConfig(const nlohmann::json& config) override;
    const std::string getPluginName() override;
    void onClientConnect(sockets::ClientHandle client);
    void onClientDisconnect(sockets::ClientHandle client, const sockets::SocketRet& ret);
    void onReceiveClientData(sockets::ClientHandle client, const char* data, size_t size);
    const std::string plugin_name = "TcpServer";
private:
    void startServer();
    void stopServer();
    IPC *ipc;
    nlohmann::json config;
    bool running;
    std::unique_ptr<sockets::TcpServer<TcpServerPlugin>> server;
};
extern "C" IPlugin* create(IPC * ipc);
#endif // TCPSERVERPLUGIN_H
src/plugins/TcpServerPlugin/external/sockets-cpp
New file
@@ -0,0 +1 @@
Subproject commit 4b46f598c7e64011a5f244eee80d57963d5decd1