From ac3e4b2143eb64103a26fe0245ff5d4cbb3db368 Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 13 Feb 2026 18:16:41 +0100 Subject: [PATCH] added cpp TCP Interface to slsDetectorServer --- .../slsDetectorServer/CMakeLists.txt | 64 +++++++++++++ .../include/ClientInterface.h | 62 +++++++++++++ .../slsDetectorServer/src/ClientInterface.cpp | 91 +++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 slsDetectorServers/slsDetectorServer/CMakeLists.txt create mode 100644 slsDetectorServers/slsDetectorServer/include/ClientInterface.h create mode 100644 slsDetectorServers/slsDetectorServer/src/ClientInterface.cpp diff --git a/slsDetectorServers/slsDetectorServer/CMakeLists.txt b/slsDetectorServers/slsDetectorServer/CMakeLists.txt new file mode 100644 index 000000000..0c64408e2 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer/CMakeLists.txt @@ -0,0 +1,64 @@ +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/src/ClientInterface.cpp +) + +add_library(slsServerObject OBJECT + ${SOURCES} +) + +target_include_directories(slsServerObject PUBLIC + "$" + "$" +) + +target_link_libraries(slsServerObject + PUBLIC + slsProjectOptions + slsSupportStatic + slsDetectorObject + PRIVATE + slsProjectWarnings +) + +set(DETECTOR_LIBRARY_TARGETS slsServerObject) + +set(PUBLICHEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/include/ClientInterface.h +) + +#Shared library +if(SLS_BUILD_SHARED_LIBRARIES) + add_library(slsServerShared SHARED $) + target_link_libraries(slsServerShared PUBLIC slsServerObject) + set_target_properties(slsServerShared PROPERTIES + VERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH} + SOVERSION ${PACKAGE_VERSION_MAJOR} + LIBRARY_OUTPUT_NAME SlsServer + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + PUBLIC_HEADER "${PUBLICHEADERS}" + ) + list(APPEND DETECTOR_LIBRARY_TARGETS slsServerShared) +endif(SLS_BUILD_SHARED_LIBRARIES) + +#Static library +add_library(slsServerStatic STATIC $) +target_link_libraries(slsServerStatic PUBLIC slsServerObject) + +set_target_properties(slsServerStatic PROPERTIES + ARCHIVE_OUTPUT_NAME SlsServerStatic + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + PUBLIC_HEADER "${PUBLICHEADERS}" +) +list(APPEND DETECTOR_LIBRARY_TARGETS slsServerStatic) + +if((CMAKE_BUILD_TYPE STREQUAL "Release") AND SLS_LTO_AVAILABLE) + set_property(TARGET ${DETECTOR_LIBRARY_TARGETS} PROPERTY INTERPROCEDURAL_OPTIMIZATION True) +endif() + +install(TARGETS ${DETECTOR_LIBRARY_TARGETS} + EXPORT "${TARGETS_EXPORT_NAME}" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/sls +) diff --git a/slsDetectorServers/slsDetectorServer/include/ClientInterface.h b/slsDetectorServers/slsDetectorServer/include/ClientInterface.h new file mode 100644 index 000000000..c64820ae0 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer/include/ClientInterface.h @@ -0,0 +1,62 @@ +#pragma once +#include "sls/ServerSocket.h" +#include "sls/sls_detector_defs.h" +#include "sls/sls_detector_funcs.h" + +#include +#include +#include + +namespace sls { + +/** + * @brief ClientInterface class handles communication and processing of commands + * from Client to Server. + */ +class ClientInterface : private virtual slsDetectorDefs { + + protected: + // TODO probably requires std::variant + /// @brief map of function IDs and corresponding functions + std::unordered_map> + functionTable{}; // set in constructor of child process + + private: + /// @brief listener thread for TCP/IP communication with the client + std::unique_ptr tcpThread; + + /// @brief flag to signal the TCP/IP listener thread to stop + std::atomic killTcpThread{false}; + + /// @brief flag to indicate if the receiver is currently locked by a client + bool lockedByClient{false}; // TODO should it be atomic? + + uint16_t portNumber{}; + + /// @brief socket for TCP/IP communication with the client + ServerSocket server; + + public: + ~ClientInterface(); + + explicit ClientInterface( + const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO); + + // std::string getReceiverVersion(); + + private: + /// @brief starts the TCP/IP server to listen for client commands + void startTCPServer(); + + /** + * @brief decodes the received command and calls the corresponding function + * @param function_id The ID of the function recived by the server and to + * be executed + */ + ReturnCode processReceivedData(const detFuncs function_id, + ServerInterface &socket); + + bool checkifReceiverLocked(); +}; + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer/src/ClientInterface.cpp b/slsDetectorServers/slsDetectorServer/src/ClientInterface.cpp new file mode 100644 index 000000000..7a04b9a7c --- /dev/null +++ b/slsDetectorServers/slsDetectorServer/src/ClientInterface.cpp @@ -0,0 +1,91 @@ +#include "ClientInterface.h" + +#include "sls/logger.h" +#include "sls/string_utils.h" + +#include + +namespace sls { + +ClientInterface::ClientInterface(const uint16_t portNumber) + : portNumber(portNumber), server(portNumber) { + validatePortNumber(portNumber); + // parentThreadId = gettid(); + tcpThread = + std::make_unique(&ClientInterface::startTCPServer, this); +} + +ClientInterface::~ClientInterface() { + killTcpThread = true; + LOG(logINFO) << "Shutting down TCP Socket on port " << portNumber; + server.shutdown(); + LOG(logDEBUG) << "TCP Socket closed on port " << portNumber; + + /* + if (receiver) { + receiver->shutDownUDPSockets(); + } + */ + + tcpThread->join(); +} + +void ClientInterface::startTCPServer() { + const pid_t tcpThreadId = gettid(); + LOG(logINFOBLUE) << "Created [ TCP server Tid: " << tcpThreadId << "]"; + LOG(logINFO) << "SLS Receiver starting TCP Server on port " << portNumber + << '\n'; + + int function_id{}; // TODO should it be an enum type + while (!killTcpThread) { + LOG(logDEBUG1) << "Start accept loop"; + try { + auto socket = server.accept(); + try { + // is this to check if I can process a command? or what is that? + if (checkifReceiverLocked()) { + throw SocketError("Receiver locked\n"); + } + socket.Receive(function_id); + processReceivedData(static_cast(function_id), socket); + + } catch (const RuntimeError &e) { + // We had an error needs to be sent to client + char mess[MAX_STR_LENGTH]{}; + strcpy_safe(mess, e.what()); + socket.Send(FAIL); + socket.Send(mess); + } + // TODO handle exiting server if tcp command was to exit server + } catch (const RuntimeError &e) { + LOG(logERROR) << "Accept failed"; + } + } + + LOG(logINFOBLUE) << "Exiting [ TCP server Tid: " << tcpThreadId << "]"; +} + +ReturnCode ClientInterface::processReceivedData(const detFuncs function_id, + ServerInterface &socket) { + // TODO: is NUM_DET_FUNCTIONS correct? + if (function_id < 0 || function_id >= NUM_DET_FUNCTIONS) { + throw RuntimeError(UNRECOGNIZED_FNUM_ENUM + + std::to_string(function_id)); + } + LOG(logDEBUG1) << "calling function fnum: " << function_id << " (" + << getFunctionNameFromEnum((enum detFuncs)function_id) + << ")"; + ReturnCode returncode = (functionTable[function_id])( + socket); // how does it pass input arguments? + LOG(logDEBUG1) << "Function " + << getFunctionNameFromEnum((enum detFuncs)function_id) + << " finished"; + + return returncode; +} + +bool ClientInterface::checkifReceiverLocked() { + return lockedByClient && server.getThisClient() != server.getLockedBy(); +} + +} // namespace sls \ No newline at end of file