Dev/matterhorn server funcs (#1469)
Build on RHEL9 docker image / build (push) Successful in 4m15s
Build on RHEL8 docker image / build (push) Successful in 5m21s
Build and Deploy on local RHEL9 / build (push) Successful in 2m12s
Build and Deploy on local RHEL8 / build (push) Successful in 5m12s
Run Simulator Tests on local RHEL9 / build (push) Successful in 18m55s
Run Simulator Tests on local RHEL8 / build (push) Successful in 22m19s

* added fetch fmt server library

* added first draft of matterhorn

* added cpp TCP Interface to slsDetectorServer

* bug: added std::signal for proper handling of ctr+c

* added compile option to set log level

* WIP

* dont use c project settings when building matterhornserver

* updated logger

* WIP

* WIP

* linked fmt to slsProjectOptions

* solved merge conflict

* some refactoring

* cleaned up logs

* WIP

* generated register defs from csv file

* oops given in hex

* properly added fmt as a dependency

* some format changes

* WIP

* used CRTP for virtual detector

* WIP

* added udp functions to matterhornserver

* fixed build

* added Server class usable for all detectors

* removed stopserver

* added some more functions

* wrong overload

* porper cleanup of matterhorn app

* PR Review

* refactored directory structure

* added shared memory for aqcuisition status

* implemented bus read and write

* added Matterhorn data class

* modified memory model

* added SPI communication

* moved memory for Bus communication to BucCommunication class

* WIP added HelperFunction for mask conversion

* completed HelperFunctions

* added tests for MatterHorn Helper Functions

* removed shared memory in destructor

* fixed SPI communication

* moved SharedMemory to slsSupportLib

* added toolchain file for cross compilation

* fix constructor for HarwareSPICommunication is public

* update receiver parameters function

* updated mac address

* uploaded matterhorn server binary tracked as lfs

* small fixes from tests on board

* automatically update matterhorn api version when cross compiling binaries

* fix bug in SPI read

* copy to server_directory/bin when cross compiling

* fixed cmake list

* update generate_registerdefs.py

* fixed copy of server binary

* always return error message

* add error log when opening dev/mem

* code review

* fixed eiger, mythen tests for dynamic range

* second Review

* member function getDerived for better readability

* split code into implementation class and actual class doing tcp communication

* added slsWarnings and c++17 compiler option

* added git lfs to workflow

* fix gitea workflows

* bug: add extra clock register

* implement initial checks

* bug: client excpetcs 4 bytes

* refactoring directory structure and rename to .hpp

* check if detector already set up

* missed .h to .hpp

* Code Review 3

* use temmplate value for stopserver

* use internal CMAKE_CROSSCOMPILING variable

* moved boolean stop server flag to DetectorImpl - moving template parameter up

* missed a file

* replace baseclass:: with this

* add Matterhorn server in github workflow

* whatever workflow bug- lets see

* fixed receiving trim bits changed to int64_t
This commit is contained in:
2026-07-16 10:10:14 +02:00
committed by GitHub
parent c3ad3d2e73
commit 7db947e4d2
65 changed files with 2935 additions and 613 deletions
@@ -0,0 +1,87 @@
#pragma once
#include "RegisterHelperStructs.hpp"
#include "fmt/format.h"
#include <cstdint>
#include <fcntl.h>
#include <map>
#include <memory>
#include <stdexcept>
#include <sys/mman.h>
#include <vector>
// TODO: maybe should be templated on address type (e.g. uint32_t register or
// uint64_t register) for more flexibility?
namespace sls {
template <typename IPCores, typename MemoryModel> class BusCommunication {
using IPCoreEnumType = typename IPCores::ipcore_enum_type;
public:
BusCommunication();
void mapToMemory();
uint32_t readRegister(const Register &register_) const;
void writeRegister(const Register &register_, const uint32_t data);
private:
void bus_w(const uint32_t offset, IPCoreEnumType baseadress,
const uint32_t data);
uint32_t bus_r(const uint32_t offset, IPCoreEnumType baseadress) const;
/// @brief map from id of IP Core to memory model for the register block of
/// the IP core
std::map<IPCoreEnumType, MemoryModel> ipcoreregisterblocks{};
};
template <typename IPCores, typename MemoryModel>
BusCommunication<IPCores, MemoryModel>::BusCommunication() {
for (const auto &ip_core : IPCores::ipcores) {
ipcoreregisterblocks.emplace(ip_core,
MemoryModel{static_cast<uint32_t>(ip_core),
IPCores::ip_core_block_size});
}
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::mapToMemory() {
for (auto &map_elem : ipcoreregisterblocks) {
map_elem.second.mapToMemory();
}
}
template <typename IPCores, typename MemoryModel>
uint32_t BusCommunication<IPCores, MemoryModel>::readRegister(
const Register &register_) const {
return bus_r(register_.offset_in_bytes, register_.ip_core);
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::writeRegister(
const Register &register_, const uint32_t data) {
bus_w(register_.offset_in_bytes, register_.ip_core, data);
}
template <typename IPCores, typename MemoryModel>
uint32_t BusCommunication<IPCores, MemoryModel>::bus_r(
const uint32_t offset, const IPCoreEnumType baseadress) const {
auto ptr1 = ipcoreregisterblocks.at(baseadress).getMappedMemoryPtr() +
offset / (sizeof(uint32_t));
return *ptr1;
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::bus_w(
const uint32_t offset, const IPCoreEnumType baseadress,
const uint32_t data) {
auto ptr1 = ipcoreregisterblocks.at(baseadress).getMappedMemoryPtr() +
offset / (sizeof(uint32_t));
*ptr1 = data;
}
} // namespace sls
@@ -1,271 +0,0 @@
#pragma once
#include "TCPInterface.h"
// #include "communication_funcs.h"
#include "sls/logger.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
#include "sls/versionAPI.h"
#include <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
namespace sls {
// TODO move to defs?
/// @brief struct saving udp details (one UDP port per module)
struct UDPInfo {
uint16_t srcport{};
uint16_t dstport{};
uint64_t srcmac{};
uint64_t dstmac{};
uint32_t srcip{};
uint32_t dstip{};
};
using ReturnCode = slsDetectorDefs::ReturnCode;
template <typename DerivedDetectorServer> class DetectorServer {
public:
/**
* Constructor
* Creates a detector server.
* Assembles a detector server using TCP and UDP detector interfaces
* throws an exception in case of failure
* @param port TCP/IP port number
*/
explicit DetectorServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO);
protected:
/// @brief TCP/IP interface for communication with the client
std::unique_ptr<TCPInterface> tcpInterface;
std::array<UDPInfo, 1>
udpDetails{}; // TODO: for now only one receiver per module
/// @brief TODO what is this?
bool updateMode{true};
private:
ReturnCode processFunction(const detFuncs function_id,
ServerInterface &socket);
// TODO dont know what this does?
ReturnCode get_update_mode(ServerInterface &socket) const;
ReturnCode get_source_udp_mac(ServerInterface &socket) const;
ReturnCode set_source_udp_mac(ServerInterface &socket);
ReturnCode get_source_udp_ip(ServerInterface &socket) const;
ReturnCode set_source_udp_ip(ServerInterface &socket);
ReturnCode get_source_udp_port(ServerInterface &socket) const;
ReturnCode set_destination_udp_mac(ServerInterface &socket);
ReturnCode get_destination_udp_mac(ServerInterface &socket) const;
ReturnCode set_destination_udp_ip(ServerInterface &socket);
ReturnCode get_destination_udp_ip(ServerInterface &socket) const;
ReturnCode set_destination_udp_port(ServerInterface &socket);
ReturnCode get_destination_udp_port(ServerInterface &socket) const;
};
template <typename DerivedDetectorServer>
DetectorServer<DerivedDetectorServer>::DetectorServer(uint16_t port) {
validatePortNumber(port);
udpDetails[0].srcport = DEFAULT_UDP_SRC_PORTNO;
udpDetails[0].dstport = DEFAULT_UDP_DST_PORTNO;
std::function<ReturnCode(const detFuncs &, ServerInterface &)> fn =
[this](const detFuncs &function_id, ServerInterface &socket) {
return this->processFunction(function_id, socket);
};
tcpInterface = std::make_unique<TCPInterface>(fn, port);
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::processFunction(
const detFuncs function_id, ServerInterface &socket) {
switch (function_id) {
case detFuncs::F_GET_SERVER_VERSION:
return static_cast<DerivedDetectorServer *>(this)->get_version(socket);
case detFuncs::F_GET_DETECTOR_TYPE:
return static_cast<DerivedDetectorServer *>(this)->get_detector_type(
socket);
case detFuncs::F_INITIAL_CHECKS:
return static_cast<DerivedDetectorServer *>(this)->initial_checks(
socket);
case detFuncs::F_GET_NUM_INTERFACES:
return static_cast<DerivedDetectorServer *>(this)
->get_num_udp_interfaces(socket);
case detFuncs::F_GET_UPDATE_MODE:
return get_update_mode(socket);
case detFuncs::F_SET_SOURCE_UDP_MAC:
return set_source_udp_mac(socket);
case detFuncs::F_GET_SOURCE_UDP_MAC:
return get_source_udp_mac(socket);
case detFuncs::F_SET_SOURCE_UDP_IP:
return set_source_udp_ip(socket);
case detFuncs::F_GET_SOURCE_UDP_IP:
return get_source_udp_ip(socket);
case detFuncs::F_SET_DEST_UDP_MAC:
return set_destination_udp_mac(socket);
case detFuncs::F_GET_DEST_UDP_MAC:
return get_destination_udp_mac(socket);
case detFuncs::F_SET_DEST_UDP_IP:
return set_destination_udp_ip(socket);
case detFuncs::F_GET_DEST_UDP_IP:
return get_destination_udp_ip(socket);
case detFuncs::F_SET_DEST_UDP_PORT:
return set_destination_udp_port(socket);
case detFuncs::F_GET_DEST_UDP_PORT:
return get_destination_udp_port(socket);
default:
LOG(logDEBUG) << "Checking specific server functions for function ID: "
<< function_id;
// process detector specific functions
static_cast<DerivedDetectorServer *>(this)->processFunction(function_id,
socket);
}
return ReturnCode::FAIL;
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_update_mode(
ServerInterface &socket) const {
return static_cast<ReturnCode>(
socket.sendResult(static_cast<int>(updateMode)));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_source_udp_mac(
ServerInterface &socket) {
uint64_t newsrcudpMac;
try {
int ret = socket.Receive<uint64_t>(newsrcudpMac);
} catch (const SocketError &e) {
LOG(logERROR) << "Failed to receive new source UDP MAC address: "
<< e.what();
return ReturnCode::FAIL;
}
udpDetails[0].srcmac = newsrcudpMac;
// TODO: configuremac, check unicast address
return ReturnCode::OK;
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_source_udp_mac(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].srcmac));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_source_udp_ip(
ServerInterface &socket) {
uint32_t newSrcIp;
try {
int ret = socket.Receive(newSrcIp);
} catch (const SocketError &e) {
LOG(logERROR) << "Failed to receive new source UDP IP address: "
<< e.what();
return ReturnCode::FAIL;
}
udpDetails[0].srcip = newSrcIp;
return ReturnCode::OK;
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_source_udp_ip(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].srcip));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_destination_udp_mac(
ServerInterface &socket) {
uint64_t newDstMac;
try {
int ret = socket.Receive<uint64_t>(newDstMac);
} catch (const SocketError &e) {
LOG(logERROR) << "Failed to receive new destination UDP MAC address: "
<< e.what();
return ReturnCode::FAIL;
}
udpDetails[0].dstmac = newDstMac;
// TODO: configuremac, check unicast address
return ReturnCode::OK;
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_mac(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstmac));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_destination_udp_ip(
ServerInterface &socket) {
uint32_t newDstIp;
try {
int ret = socket.Receive(newDstIp);
} catch (const SocketError &e) {
LOG(logERROR) << "Failed to receive new destination UDP IP address: "
<< e.what();
return ReturnCode::FAIL;
}
udpDetails[0].dstip = newDstIp;
return ReturnCode::OK;
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_ip(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstip));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_destination_udp_port(
ServerInterface &socket) {
uint16_t newDstPort;
try {
int ret = socket.Receive(newDstPort);
} catch (const SocketError &e) {
LOG(logERROR) << "Failed to receive new destination UDP port number: "
<< e.what();
return ReturnCode::FAIL;
}
udpDetails[0].dstport = newDstPort;
return ReturnCode::OK;
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_port(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstport));
};
} // namespace sls
@@ -0,0 +1,523 @@
#pragma once
#include "DetectorServerImpl.hpp"
#include "TCPInterface.hpp"
#include "helpers/Helpers.hpp"
#include "helpers/type_traits.hpp"
#include "sls/logger.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
#include "sls/versionAPI.h"
#include <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
namespace sls {
using ReturnCode = slsDetectorDefs::ReturnCode;
template <typename DerivedDetectorServer> class DetectorServer {
public:
/**
* Constructor
* Creates a detector server.
* Assembles a detector server using TCP and UDP detector interfaces
* throws an exception in case of failure
* @param port TCP/IP port number
*/
explicit DetectorServer(
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedDetectorServer>::value>>
impl_,
uint16_t port = DEFAULT_TCP_CNTRL_PORTNO);
~DetectorServer() = default;
protected:
/// @brief TCP/IP interface for communication with the client
std::unique_ptr<TCPInterface> tcpInterface;
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedDetectorServer>::value>>
impl;
auto *getImpl() {
return static_cast<typename implementation_type_trait<
DerivedDetectorServer>::ImplType *>(impl.get());
}
const auto *getImpl() const {
return static_cast<const typename implementation_type_trait<
DerivedDetectorServer>::ImplType *>(impl.get());
}
private:
/// @brief get derived class
DerivedDetectorServer *getDerived() {
return static_cast<DerivedDetectorServer *>(this);
}
const DerivedDetectorServer *getDerived() const {
return static_cast<const DerivedDetectorServer *>(this);
}
ProcessedResult processFunction(const detFuncs function_id,
ServerInterface &socket);
// TODO dont know what this does?
ProcessedResult get_update_mode(ServerInterface &socket) const;
ProcessedResult get_source_udp_mac(ServerInterface &socket) const;
ProcessedResult set_source_udp_mac(ServerInterface &socket);
ProcessedResult get_source_udp_ip(ServerInterface &socket) const;
ProcessedResult set_source_udp_ip(ServerInterface &socket);
ProcessedResult get_source_udp_port(ServerInterface &socket) const;
ProcessedResult set_destination_udp_mac(ServerInterface &socket);
ProcessedResult get_destination_udp_mac(ServerInterface &socket) const;
ProcessedResult set_destination_udp_ip(ServerInterface &socket);
ProcessedResult get_destination_udp_ip(ServerInterface &socket) const;
ProcessedResult set_destination_udp_port(ServerInterface &socket);
ProcessedResult get_destination_udp_port(ServerInterface &socket) const;
ProcessedResult get_num_frames(ServerInterface &socket) const;
ProcessedResult set_num_frames(ServerInterface &socket);
ProcessedResult get_num_triggers(ServerInterface &socket) const;
ProcessedResult set_num_triggers(ServerInterface &socket);
ProcessedResult get_version(ServerInterface &socket) const;
ProcessedResult get_num_udp_interfaces(ServerInterface &socket) const;
ProcessedResult get_detector_type(ServerInterface &socket) const;
ProcessedResult get_receiver_parameters(ServerInterface &socket) const;
ProcessedResult get_run_status(ServerInterface &socket) const;
ProcessedResult initial_checks(ServerInterface &socket) const;
ProcessedResult
set_module_position_and_update_srcudpmac(ServerInterface &socket);
};
template <typename DerivedDetectorServer>
DetectorServer<DerivedDetectorServer>::DetectorServer(
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedDetectorServer>::value>>
impl_,
uint16_t port)
: impl(std::move(impl_)) {
validatePortNumber(port);
std::function<ProcessedResult(const detFuncs &, ServerInterface &)> fn =
[this](const detFuncs &function_id, ServerInterface &socket) {
return this->processFunction(function_id, socket);
};
tcpInterface = std::make_unique<TCPInterface>(fn, port);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::processFunction(
const detFuncs function_id, ServerInterface &socket) {
switch (function_id) {
case detFuncs::F_GET_SERVER_VERSION:
return get_version(socket);
case detFuncs::F_GET_DETECTOR_TYPE:
return get_detector_type(socket);
case detFuncs::F_INITIAL_CHECKS:
return initial_checks(socket);
case detFuncs::F_GET_NUM_INTERFACES:
return get_num_udp_interfaces(socket);
case detFuncs::F_GET_UPDATE_MODE:
return get_update_mode(socket);
case detFuncs::F_SET_SOURCE_UDP_MAC:
return set_source_udp_mac(socket);
case detFuncs::F_GET_SOURCE_UDP_MAC:
return get_source_udp_mac(socket);
case detFuncs::F_SET_SOURCE_UDP_IP:
return set_source_udp_ip(socket);
case detFuncs::F_GET_SOURCE_UDP_IP:
return get_source_udp_ip(socket);
case detFuncs::F_SET_DEST_UDP_MAC:
return set_destination_udp_mac(socket);
case detFuncs::F_GET_DEST_UDP_MAC:
return get_destination_udp_mac(socket);
case detFuncs::F_SET_DEST_UDP_IP:
return set_destination_udp_ip(socket);
case detFuncs::F_GET_DEST_UDP_IP:
return get_destination_udp_ip(socket);
case detFuncs::F_SET_DEST_UDP_PORT:
return set_destination_udp_port(socket);
case detFuncs::F_GET_DEST_UDP_PORT:
return get_destination_udp_port(socket);
case detFuncs::F_GET_RUN_STATUS:
return get_run_status(socket);
case detFuncs::F_GET_NUM_FRAMES:
return get_num_frames(socket);
case detFuncs::F_SET_NUM_FRAMES:
return set_num_frames(socket);
case detFuncs::F_GET_NUM_TRIGGERS:
return get_num_triggers(socket);
case detFuncs::F_SET_NUM_TRIGGERS:
return set_num_triggers(socket);
case detFuncs::F_GET_RECEIVER_PARAMETERS:
return get_receiver_parameters(socket);
case detFuncs::F_SET_POSITION:
return set_module_position_and_update_srcudpmac(socket);
default:
LOG(logDEBUG) << "Checking specific server functions for function ID: "
<< function_id;
// process detector specific functions
return getDerived()->processFunction(function_id, socket);
}
return return_fail("Function not implemented");
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_update_mode(
ServerInterface &socket) const {
const bool updateMode = impl->get_update_mode();
// TODO: catch the socket error during Send and add error message to the
// ProcessedResult but DatSocket shared with receiver - some refactoring
return send_result(socket, static_cast<uint32_t>(updateMode));
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_source_udp_mac(
ServerInterface &socket) const {
auto srcUdpMac = impl->get_source_udp_mac();
return send_result(socket, srcUdpMac);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_source_udp_mac(
ServerInterface &socket) {
uint64_t newsrcudpMac;
try {
(void)socket.Receive<uint64_t>(newsrcudpMac);
} catch (const SocketError &e) {
LOG(logERROR) << "Failed to receive new source UDP MAC address: "
<< e.what();
return return_fail("Failed to receive new source UDP MAC address: " +
std::string(e.what()));
}
try {
getImpl()->set_source_udp_mac(newsrcudpMac);
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to set source UDP MAC address: " << e.what();
return_fail("Failed to set source UDP MAC address: " +
std::string(e.what()));
}
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_source_udp_ip(
ServerInterface &socket) {
uint32_t newSrcIp;
try {
(void)socket.Receive(newSrcIp);
} catch (const SocketError &e) {
auto error_message = "Failed to receive new source UDP IP address: " +
std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
impl->set_source_udp_ip(newSrcIp);
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_source_udp_ip(
ServerInterface &socket) const {
uint32_t src_UdpIp = impl->get_source_udp_ip();
return send_result(socket, src_UdpIp);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_destination_udp_mac(
ServerInterface &socket) {
uint64_t newDstMac;
try {
(void)socket.Receive<uint64_t>(newDstMac);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive new destination UDP MAC address: " +
std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
impl->set_destination_udp_mac(newDstMac);
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_mac(
ServerInterface &socket) const {
auto dstUdpMac = impl->get_destination_udp_mac();
return send_result(socket, dstUdpMac);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_destination_udp_ip(
ServerInterface &socket) {
uint32_t newDstIp;
try {
(void)socket.Receive(newDstIp);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive new destination UDP IP address: " +
std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
impl->set_destination_udp_ip(newDstIp);
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_ip(
ServerInterface &socket) const {
uint32_t dstUdpIp = impl->get_destination_udp_ip();
return send_result(socket, dstUdpIp);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_destination_udp_port(
ServerInterface &socket) {
uint16_t newDstPort;
try {
(void)socket.Receive(newDstPort);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive new destination UDP port number: " +
std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
impl->set_destination_udp_port(newDstPort);
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_port(
ServerInterface &socket) const {
uint16_t dstUdpPort = impl->get_destination_udp_port();
return send_result(socket, dstUdpPort);
};
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_num_frames(
ServerInterface &socket) const {
uint64_t num_frames{};
try {
num_frames = getImpl()->get_num_frames();
} catch (const std::exception &e) {
auto error_message =
"Failed to get number of frames: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
return send_result(socket, num_frames);
}
template <typename DerivedDetectorServer>
ProcessedResult
DetectorServer<DerivedDetectorServer>::set_num_frames(ServerInterface &socket) {
int64_t num_frames{};
try {
(void)socket.Receive(num_frames);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive number of frames: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
try {
getImpl()->set_num_frames(num_frames);
} catch (const std::exception &e) {
auto error_message =
"Failed to set number of frames: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_num_triggers(
ServerInterface &socket) const {
uint64_t num_triggers{};
try {
num_triggers = static_cast<uint64_t>(getImpl()->get_num_triggers());
} catch (const std::exception &e) {
auto error_message =
"Failed to get number of triggers: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
return send_result(socket, num_triggers);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_num_triggers(
ServerInterface &socket) {
int64_t num_triggers{};
try {
(void)socket.Receive(num_triggers);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive number of triggers: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
try {
getImpl()->set_num_triggers(num_triggers);
} catch (const std::exception &e) {
auto error_message =
"Failed to set number of triggers: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_version(
ServerInterface &socket) const {
auto version =
getImpl()->get_server_version(); // TODO: get Impl from derived server
char version_cstr[MAX_STR_LENGTH]{};
std::snprintf(version_cstr, sizeof(version_cstr), "%s",
version.c_str()); // ensures temination
LOG(TLogLevel::logDEBUG) << "Server Version: " << version;
return send_result(
socket,
version_cstr); // TODO: check what would be possible return codes!!!
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_num_udp_interfaces(
ServerInterface &socket) const {
int num_udp_interfaces = getImpl()->get_num_udp_interfaces();
return send_result(socket, num_udp_interfaces);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_detector_type(
ServerInterface &socket) const {
uint32_t detectortype = getImpl()->get_detector_type();
return send_result(socket, detectortype);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_receiver_parameters(
ServerInterface &socket) const {
slsDetectorDefs::rxParameters rx_params =
getImpl()->get_receiver_parameters();
return send_result(socket, rx_params);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_run_status(
ServerInterface &socket) const {
slsDetectorDefs::runStatus status = getImpl()->get_run_status();
return send_result(socket, status);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::initial_checks(
ServerInterface &socket) const {
auto detectorsetupstatus = getImpl()->get_detector_setup_status();
// TODO: should there be a time limit?
while (detectorsetupstatus.setup_status ==
detector_setup_status::NOT_SETUP) {
std::this_thread::sleep_for(std::chrono::seconds(1));
detectorsetupstatus = getImpl()->get_detector_setup_status();
}
if (detectorsetupstatus.setup_status ==
detector_setup_status::FAILED_SETUP) {
return return_fail("Initial checks failed: " +
detectorsetupstatus.error_message);
} else {
return send_result<bool>(socket, true);
}
}
template <typename DerivedDetectorServer>
ProcessedResult
DetectorServer<DerivedDetectorServer>::set_module_position_and_update_srcudpmac(
ServerInterface &socket) {
std::array<int, 2> position_info{}; // [num_modules_in_y, module_index]
try {
(void)socket.Receive(position_info.data(),
position_info.size() * sizeof(int));
} catch (const SocketError &e) {
LOG(logERROR)
<< "Failed to receive num modules in y dimension and module index: "
<< e.what();
return_fail(
"Failed to receive num modules in y dimension and module index: " +
std::string(e.what()));
}
try {
getImpl()->set_module_position_and_update_srcudpmac(position_info);
} catch (const std::exception &e) {
return_fail("Failed to set module position: " + std::string(e.what()));
}
return send_ok(socket);
}
} // namespace sls
@@ -0,0 +1,109 @@
#pragma once
#include "sls/SharedMemory.h"
#include <array>
#include <atomic>
namespace sls {
// TODO move to defs?
/// @brief struct saving udp details (one UDP port per module)
struct UDPInfo {
uint16_t srcport{};
uint16_t dstport{};
uint64_t srcmac{};
uint64_t dstmac{};
uint32_t srcip{};
uint32_t dstip{};
};
/// @brief struct to store detector setup status
struct detector_setup_status {
enum SETUP_STATUS : uint8_t {
FAILED_SETUP = 0,
SUCCESSFUL_SETUP = 1,
NOT_SETUP = 2
};
/// @brief true if setupDetector() was successful, false otherwise
SETUP_STATUS setup_status{NOT_SETUP};
/// @brief error message if setupDetector() failed, empty otherwise
std::string error_message{};
};
/// @brief Shared memory structure for stop server to store run status
struct acquisitionStatus {
/* FIXED PATTERN FOR STATIC FUNCTIONS. DO NOT CHANGE, ONLY APPEND ------*/
int shmversion;
bool isValid{true}; // false if freed to block access from python or c++ api
std::atomic<slsDetectorDefs::runStatus> scanStatus{
slsDetectorDefs::runStatus::IDLE}; // idle, running or error
std::atomic<bool> scanStop{false};
// TODO: only neccessary for virtual, maybe have two shared memory
// structures, one for virtual
std::atomic<slsDetectorDefs::runStatus> status{
slsDetectorDefs::runStatus::IDLE};
std::atomic<bool> stop{false};
};
template <bool isStopServer> class DetectorServerImpl {
public:
DetectorServerImpl();
~DetectorServerImpl();
bool get_update_mode() const;
uint64_t get_source_udp_mac() const;
void set_source_udp_ip(const uint32_t srcip);
uint32_t get_source_udp_ip() const;
void set_destination_udp_ip(const uint32_t dstip);
uint32_t get_destination_udp_ip() const;
void set_destination_udp_mac(const uint64_t dstmac);
uint64_t get_destination_udp_mac() const;
void set_destination_udp_port(const uint16_t dstport);
uint16_t get_destination_udp_port() const;
detector_setup_status get_detector_setup_status() const;
protected:
std::array<UDPInfo, 1>
udpDetails{}; // TODO: for now only one receiver per module
/// @brief TODO what is this?
bool updateMode{
false}; // what should the default be - can update module size etc.
/// @brief shared mempory with aquisition status
mutable SharedMemory<acquisitionStatus> shm{
0, 0}; // TODO: is mutable really neccessary?
/// @brief sets source UDP MAC address in udpDetails and updates udp
/// header
/// @param srcmac
void updateSrcMacAddress(const uint64_t srcmac);
/// @brief true if setupDetector() was successful, false otherwise
detector_setup_status detectorSetupStatus{};
/// @brief true if the derived server is a stop server, false otherwise
static constexpr bool stop_server = isStopServer;
private:
/// @brief creates and maps shared memory
void createSharedMemory();
};
} // namespace sls
@@ -0,0 +1,68 @@
#pragma once
#include "fmt/format.h"
#include "sls/logger.h"
#include "sls/sls_detector_exceptions.h"
#include <cstdint>
#include <vector>
namespace sls {
/// @brief class to handle memory mapping and access for hardware IP cores
class HardwareMemoryModel {
public:
HardwareMemoryModel(const uint32_t IPcore_base_address,
const size_t size_memory_space_);
~HardwareMemoryModel();
void mapToMemory();
void unmapMemory();
volatile uint32_t *getMappedMemoryPtr() const;
private:
volatile uint32_t *mapped_memory_ptr{nullptr};
/// @brief offset of the IP core base address in the memory space, used for
/// mapping
const size_t IPCore_base_address{0};
/// @brief size mapped memory region [bytes]
const size_t size_memory_space{0};
};
/// @brief class to handle memory mapping and access for virtual IP cores (e.g.
/// use software implementation of memory)
template <typename DataType> class VirtualMemoryModel {
public:
// IPcore_base_address is not used for virtual memory model but kept for
// compatibility with HardwareMemoryModel interface
VirtualMemoryModel(const uint32_t IPcore_base_address,
const size_t size_memory_space_)
: size_memory_space(size_memory_space_) {
(void)IPcore_base_address; // suppress unused parameter warning
}
~VirtualMemoryModel() = default;
void mapToMemory() {
mapped_memory.resize(
size_memory_space /
sizeof(DataType)); // TODO: should it be zero initialized?
}
DataType *getMappedMemoryPtr() { return mapped_memory.data(); }
const DataType *getMappedMemoryPtr() const { return mapped_memory.data(); }
private:
std::vector<DataType> mapped_memory{};
/// @brief size mapped memory region [bytes]
const size_t size_memory_space{0};
};
} // namespace sls
@@ -0,0 +1,62 @@
#pragma once
#include <cstdint>
#include <exception>
#include <fmt/format.h>
#include <string_view>
namespace sls {
enum class IPCore : uint32_t; // forward declaration of IPCore enum class
/// @brief struct representing 32 bit register
struct Register {
/// @brief IP core address space
const IPCore ip_core{}; // TODO replace by enum type
/// @brief Offset of the register in bytes from the base address of the IP
/// core
const uint32_t offset_in_bytes{};
};
struct RegisterField {
/// @brief Register to which the field belongs
const Register register_{};
/// @brief Bit position of the least significant bit of the field in the
/// register
const uint32_t bit_position{};
/// @brief Bitmask for the field
const uint32_t bitmask{};
};
// TODO: maybe static member function of RegisterField?
template <typename T>
void setRegisterField(uint32_t &registervalue, const RegisterField &reg_field,
T field_value) {
if (field_value > static_cast<T>(reg_field.bitmask)) {
throw std::invalid_argument(
fmt::format("Value {} cannot fit in field with bitmask {}",
field_value, reg_field.bitmask));
}
// Clear the bits corresponding to the field
registervalue &= ~(reg_field.bitmask << reg_field.bit_position);
// Set the new value for the field
registervalue |= (static_cast<uint32_t>(field_value) & reg_field.bitmask)
<< reg_field.bit_position;
}
template <typename T>
T getRegisterField(const uint32_t &registervalue,
const RegisterField &reg_field) {
// Extract the bits corresponding to the field and shift them to get the
// value
auto field_value =
(registervalue >> reg_field.bit_position) & reg_field.bitmask;
return static_cast<T>(field_value);
}
} // namespace sls
@@ -10,6 +10,31 @@
namespace sls {
using ReturnCode = slsDetectorDefs::ReturnCode;
struct ProcessedResult {
/// @brief return code of the processed command
slsDetectorDefs::ReturnCode returnCode{};
/// @brief error message to be sent to client in case of failure
std::string error_message{};
};
// communication helpers
inline ProcessedResult return_fail(std::string_view error_message) {
return ProcessedResult{ReturnCode::FAIL,
static_cast<std::string>(error_message)};
}
inline ProcessedResult send_ok(ServerInterface &socket) {
return ProcessedResult{
static_cast<ReturnCode>(socket.Send(ReturnCode::OK))};
}
template <typename T>
inline ProcessedResult send_result(ServerInterface &socket, const T &value) {
return ProcessedResult{static_cast<ReturnCode>(socket.sendResult(value))};
}
/**
* @brief TCPInterface class handles communication and processing of commands
* from Client to Server.
@@ -19,9 +44,10 @@ class TCPInterface {
public:
~TCPInterface();
TCPInterface(std::function<slsDetectorDefs::ReturnCode(
const detFuncs &, ServerInterface &)> &processFunction_,
const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO);
TCPInterface(
std::function<ProcessedResult(const detFuncs &, ServerInterface &)>
&processFunction_,
const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO);
/// @brief creates tcp thread
void startTCPServer();
@@ -40,12 +66,11 @@ class TCPInterface {
* @param function_id The ID of the function recived by the server and to
* be executed
*/
slsDetectorDefs::ReturnCode processReceivedData(const detFuncs function_id,
ServerInterface &socket);
ProcessedResult processReceivedData(const detFuncs function_id,
ServerInterface &socket);
/// @brief map of function IDs and corresponding functions
std::function<slsDetectorDefs::ReturnCode(const detFuncs &,
ServerInterface &)>
std::function<ProcessedResult(const detFuncs &, ServerInterface &)>
processFunction;
/// @brief TCP/IP port number for the detector server
@@ -0,0 +1,19 @@
/** @file Defs.hpp
* @brief this file contains some definitions used in the slsDetectorServer_cpp
* project.
*/
#pragma once
#include <cstdint>
namespace sls {
/// @brief Individual/Group bit offset in a 48 bit MAC address - 0 indicates
/// unicast mac address
constexpr uint8_t INDIVIDUAL_GROUP_BIT_OFFSET = 40; // 1000 0000
/// @brief Universal/Local bit offset in a 48 bit MAC address - 1 indicates
/// locally administered mac address, 0 indicates universally administered mac
/// address
constexpr uint8_t UNIVERSAL_LOCAL_BIT_OFFSET = 41; // 0100 0000
} // namespace sls
@@ -0,0 +1,64 @@
#pragma once
#include "Defs.hpp"
#include "DetectorServerImpl.hpp"
#include "sls/SharedMemory.h"
#include "sls/sls_detector_defs.h"
#include <cstdint>
#include <cstdlib>
namespace sls {
constexpr uint64_t mac_mask = 0xffffffffffff0000;
constexpr uint8_t offset_row_position_in_mac = 8; // given in bits
constexpr uint8_t offset_col_position_in_mac = 0; // given in bits
/// @brief generates a random locally administered unicast MAC address for the
/// source UDP
/// @return generated MAC address
inline uint64_t generateRandomMacAddress() {
uint64_t mac =
0xAA0000000000; // locally administered unicast address (0xA: 0b1010) //
// TODO maybe 0x02000000000 better?
for (int i = 2; i < 5; ++i) {
mac |= (static_cast<uint64_t>(std::rand() % 256) << (i * 8));
}
return mac;
}
/// @brief generates a MAC address based on the module's row and column
/// position, last 32 bits of the MAC address are set to module_row and
/// module_col
/// @param module_row
/// @param module_col
/// @return generated MAC address
inline uint64_t generateMacAddressfromModulePosition(const uint8_t module_row,
const uint8_t module_col) {
uint64_t newSrcMac = generateRandomMacAddress();
newSrcMac = (newSrcMac & mac_mask) |
(module_row << offset_row_position_in_mac) |
(module_col << offset_col_position_in_mac);
return newSrcMac;
}
/// @brief check that mac is unicast and locally administered
/// @param mac
/// @return true if mac is valid, false otherwise
inline bool isValidMac(const uint64_t mac) {
if ((mac << INDIVIDUAL_GROUP_BIT_OFFSET) == 0 &&
(mac << UNIVERSAL_LOCAL_BIT_OFFSET) == 1) {
return true;
}
return false;
}
inline void freeSharedMemory() {
SharedMemory<acquisitionStatus> shm(0, -1, "server");
if (shm.exists()) {
shm.removeSharedMemory();
}
}
} // namespace sls
@@ -0,0 +1,57 @@
#pragma once
#include <type_traits>
namespace sls {
// forward declares
template <bool isStopServer> class MatterhornServer;
template <bool isStopServer> class VirtualMatterhornServer;
template <bool isStopServer> class MatterhornServerImpl;
template <bool isStopServer> class VirtualMatterhornServerImpl;
template <typename DerivedServer> class BaseMatterhornServer;
// type trait to get implementation type
template <typename DetectorServer> struct implementation_type_trait;
template <bool isStopServer>
struct implementation_type_trait<
BaseMatterhornServer<MatterhornServer<isStopServer>>> {
using ImplType = MatterhornServerImpl<isStopServer>;
};
template <bool isStopServer>
struct implementation_type_trait<
BaseMatterhornServer<VirtualMatterhornServer<isStopServer>>> {
using ImplType = VirtualMatterhornServerImpl<isStopServer>;
};
// type trait to get stop server flag from Detector Server
template <typename DetectorServerImpl>
struct is_stop_server : std::false_type {};
template <>
struct is_stop_server<VirtualMatterhornServerImpl<true>> : std::true_type {};
template <>
struct is_stop_server<MatterhornServerImpl<true>> : std::true_type {};
template <>
struct is_stop_server<VirtualMatterhornServer<true>> : std::true_type {};
template <> struct is_stop_server<MatterhornServer<true>> : std::true_type {};
template <>
struct is_stop_server<BaseMatterhornServer<VirtualMatterhornServer<true>>>
: std::true_type {};
template <>
struct is_stop_server<BaseMatterhornServer<MatterhornServer<true>>>
: std::true_type {};
} // namespace sls