Network

The network components are used to communicate with the outside world. When the controller is run in network mode a server is started which listens to incoming connections. Each connection is handled by a client object.

Additionally, there are other modules within this component to abstract the measurement state and the client configuration. The different message types are defined as well.

Classes and structures

Server class

class Server : public std::enable_shared_from_this<Server>

Represents a server that handles client connections and data transfer.

Represents a server that listens for connections and handles client interactions.

The Server class is responsible for managing client connections, handling data transfer, and controlling the measurement configuration. It provides methods to start and stop the server, set and retrieve the measurement configuration, and perform DMA data transfer.

The server runs on a specified port and communicates with clients using TCP sockets. It maintains a list of connected clients and allows sending notifications to all clients.

The server is designed to be used in conjunction with a controller object, which provides the necessary functionality for controlling the measurement process.

The Server class is responsible for managing client connections, handling data transfer, and controlling the measurement process. It provides methods to start and stop the server, configure measurement settings, and manage DMA data transfer threads.

Public Functions

Server(std::shared_ptr<controller::Controller> controller, kn::port_t port, std::atomic<bool> *exitSignal)

Constructs a Server object.

Parameters:
  • controller – The controller object for handling the measurement process.

  • port – The port on which the server listens for connections.

  • exitSignal – The exit signal (handled by SIGINT by Controller).

~Server() = default

Destroys the Server object.

Server(const Server&) = delete
Server(Server&&) = delete
void operator=(const Server&) = delete
void operator=(Server&&) = delete
std::mutex &getMessageMutex()

Gets the message mutex.

This mutex is used to make sure that each message is handled after each other. It is accessed by the clients when handling a message.

Returns:

The message mutex.

void run()

Runs the server.

It calls stop() when the server is stopped (by SIGINT signal).

void stop()

Stops the server.

TmpMeasurementConfig getMeasurementConfig()

Gets the temporary measurement configuration.

The copy is a non-atomic version of the measurement configuration.

Returns:

The temporary measurement configuration.

void setMeasurementConfig(TmpMeasurementConfig config)

Sets the temporary measurement configuration.

The atomic measurement configuration is overwritten with the temporary measurement configuration.

Parameters:

config – The temporary measurement configuration.

void applyMeasurementConfig()

Applies the temporary measurement configuration.

It sets the corresponding measurement configuration in the controller object (hardware registers).

void startDmaDataThread()

Starts the DMA data thread for each measurement.

void destroyDmaDataThread()

Periodically checks if the DMA data thread is finished and destroys it.

bool startMeasurement()

Starts a measurement.

Returns:

True if the measurement process started successfully, false otherwise.

void stopMeasurement()

Stops the measurement process.

It sets the remaining measurement time to 1ms to let the DMA data thread finish by itself.

Private Functions

void notifyClients(MessageType type, nlohmann::json data)

Notifies all clients with the specified message type and data.

Parameters:
  • type – The message type.

  • data – The message data.

void startExitThread()

Starts the thread for handling the exit signal.

void startDmaThreadDestroyThread()

Starts the thread for handling the DMA data thread.

void printMeasurementInfo()

Prints the measurement information to the console.

Private Members

kn::port_t mPort

The port on which the server listens for connections.

kn::tcp_socket mListenSocket

The socket on which the server listens for connections.

std::unordered_map<uint32_t, std::unique_ptr<Client>> mClients = {}

The map of connected clients.

std::mutex mClientsMutex = {}

The mutex for accessing the map of connected clients.

std::mutex mMessageMutex = {}

The mutex so that each message is handled after each other.

std::unique_ptr<std::thread> mExitThread = nullptr

The thread for handling the exit signal.

std::unique_ptr<std::thread> mDmaDataThread = nullptr

The thread for handling the DMA data.

std::atomic<bool> mDmaDataThreadFinished = false

Indicates if the DMA data thread is finished.

std::unique_ptr<std::thread> mDestroyDmaThreadThread = nullptr

The thread for destroying the DMA data thread.

std::shared_ptr<controller::Controller> mController

The controller object for handling the measurement process.

std::atomic<bool> *mExit

The exit signal (handled by SIGINT by Controller).

MeasurementConfig mMeasurementConfig = {}

The measurement configuration.

Client class

class Client

Represents a client connection in the network component.

The Client class manages the communication with a client connected to the server. It handles sending and receiving data, processing messages, and managing the client’s configuration.

Public Functions

Client(std::unique_ptr<kn::tcp_socket> sock, std::shared_ptr<Server> server, std::shared_ptr<controller::Controller> controller, uint32_t id)

Constructor for the Client class.

Parameters:
  • sock – The socket for the client connection.

  • server – The server object.

  • controller – The controller object.

  • id – The ID of the client.

~Client()

Destructor for the Client class.

Client() = delete
Client(const Client&) = delete
Client(Client&&) = delete
void operator=(const Client&) = delete
void operator=(Client&&) = delete
void run()

Starts the client.

It starts the threads for sending and receiving data.

bool isRemovable()

Checks if the client can be removed.

Returns:

True if the client can be removed, false otherwise.

bool getWantsData() const

Checks if the client wants to receive DMA data.

Returns:

True if the client wants to receive DMA data, false otherwise.

void setSendDataFailed(bool sendDataFailed)

Sets the flag indicating if sending of DMA data failed.

Parameters:

sendDataFailed – True if sending of DMA data failed, false otherwise.

bool getSendDataFailed() const

Checks if sending of DMA data failed.

Returns:

True if sending of DMA data failed, false otherwise.

bool isSocketValid(kn::socket_status status) const

Checks if the socket status is valid.

Parameters:

status – The socket status to check.

Returns:

True if the socket status is valid, false otherwise.

bool receiveData()

Receives data from the client.

Returns:

True if data was received successfully, false otherwise.

bool sendData()

Sends data to the client.

Returns:

True if data was sent successfully, false otherwise.

void handleMessage(MessageType type, nlohmann::json data)

Handles a message received from the client.

Parameters:
  • type – The type of the message.

  • data – The data of the message.

void handleStart(nlohmann::json data)

Handles a start message received from the client.

It starts the measurement if the client is connected and the controller is not running. It optionally may set the client and measurement configuration.

Example of the JSON data to send without configuration:

{}

Example of the JSON data to send with configuration:

{
  "client-config": {"wants-data": false},
  "measurement-config": {
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

Response (error):

{
  "status": {
    "type": "error",
    "message": "measurement already running"
  }
}

Response (success):

{
  "status": {
    "type": "success"
  },
  "client-config": {"wants-data": false},
  "measurement-config": {
    "state": "running",
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

Parameters:

data – The data of the message.

void handleStop(nlohmann::json data)

Handles a stop message received from the client.

It stops the measurement if the client is connected and the controller is running. This message does not require any user data.

Example of the JSON data to send:

{}

Response (error):

{
  "status": {
    "type": "error",
    "message": "measurement not running"
  }
}

Response (success):

{
  "status": {
    "type": "success"
  }
}

Parameters:

data – The data of the message.

void handleConnect(nlohmann::json data)

Handles a connect message received from the client.

It is the first message to send to the server.

The connect message must include the client version. The format of the version is “vX.Y.Z”. The response contains the server version and the client and measurement configuration on success.

Example of the JSON data to send:

{
   "version": "v0.0.1",
}

Response (error):

{
  "status": {
    "type": "error",
    "message": "version mismatch"
  },
  "version": "v0.0.1"
}

Response (success):

{
  "status": {
    "type": "success"
  },
  "version": "v0.0.1",
  "client-config": {"wants-data": false},
  "measurement-config": {
    "state": "running",
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

Parameters:

data – The data of the message.

void handleState(nlohmann::json data)

Handles a state message received from the client.

It returns the current measurement state (idle, running or stopped). This message does not require any user data.

Example of the JSON data send:

{}

Response (error):

{
  "status": {
    "type": "error",
    "message": "not connected"
  }
}

Response (success):

{
  "status": {
    "type": "success"
  },
  "measurement-config": {
    "state": "running"
  }
}

Parameters:

data – The data of the message.

void handleSettings(nlohmann::json data)

Handles a settings message received from the client.

It sets the client and measurement configuration if the client is connected. If no configuration is sent, the current configuration is returned.

Example of the JSON data to send without configuration:

{}

Example of the JSON data to send with configuration:

{
  "client-config": {"wants-data": false},
  "measurement-config": {
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

Response (error):

{
  "status": {
    "type": "error",
    "message": "cannot change measurement config during measurement"
  }
}

Response (success):

{
  "status": {
    "type": "success"
  },
  "client-config": {"wants-data": false},
  "measurement-config": {
    "state": "running",
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

Parameters:

data – The data of the message.

TmpMeasurementConfig parseMeasurementConfig(nlohmann::json data, TmpMeasurementConfig measurementConfig)

Parses the measurement configuration from a JSON object.

Parameters:
  • data – The JSON object containing the measurement configuration.

  • measurementConfig – The current measurement configuration.

Returns:

The parsed measurement configuration.

ClientConfig parseClientConfig(nlohmann::json data, ClientConfig clientConfig)

Parses the client configuration from a JSON object.

Parameters:
  • data – The JSON object containing the client configuration.

  • clientConfig – The current client configuration.

Returns:

The parsed client configuration.

bool validateChannels(uint32_t channels)

Validates the channels specified in the client configuration.

The channels are valid if it’s value is Channel::CHANNEL_1, Channel::CHANNEL_2, or Channel::BOTH.

Parameters:

channels – The channels to validate.

Returns:

True if the channels are valid, false otherwise.

void applyClientConfig(ClientConfig clientConfig)

Applies the client configuration.

Parameters:

clientConfig – The client configuration to apply.

void addMessageToQueue(MessageType type, nlohmann::json data)

Adds a message to the send queue.

Parameters:
  • type – The type of the message.

  • data – The data of the message.

void waitSendDmaData()

Waits for the sending of DMA data to complete.

This function runs in a loop until the sending of DMA data is complete.

bool getDmaDescriptorHandled(uint8_t dmaId, uint32_t dmaDescriptor)

Checks if a DMA descriptor was handled.

Parameters:
  • dmaId – The ID of the DMA.

  • dmaDescriptor – The descriptor of the DMA.

Returns:

True if the DMA descriptor was handled, false otherwise.

void setDmaDescriptorHandled(uint8_t dmaId, uint32_t dmaDescriptor, bool value)

Sets the flag indicating if a DMA descriptor was handled.

Parameters:
  • dmaId – The ID of the DMA.

  • dmaDescriptor – The descriptor of the DMA.

  • value – The value to set.

void resetDmaDescriptorHandled()

Resets the flags indicating if DMA descriptors were handled.

All values are set to true.

Private Members

std::unique_ptr<kn::tcp_socket> mSocket = nullptr

The socket for the client connection.

ClientConfig mClientConfig = {}

The configuration for the client.

bool mSendDataFailed = false

Indicates if the sending of DMA data was too slow and thus failed.

std::queue<std::tuple<MessageType, nlohmann::json>> mSendQueue = {}

The queue for messages to send to the client.

std::mutex mQueueMutex = {}

The mutex for accessing the send queue.

std::unique_ptr<std::thread> mSendThread = nullptr

The thread for sending data to the client.

std::unique_ptr<std::thread> mReceiveThread = nullptr

The thread for receiving data from the client.

std::shared_ptr<Server> mServer

The server object.

std::weak_ptr<controller::Controller> mController

The controller object.

std::array<std::array<bool, DMA_DESCRIPTOR_PER_DMA>, DMA_COUNT> mDmaDescriptorHandled = {}

Indicates if a DMA descriptor was handled.

std::mutex mDmaDescriptorHandledMutex = {}

The mutex for accessing the DMA descriptor handled array.

bool mConnected = false

Indicates if the client is connected.

std::atomic<bool> mIsRemovable = false

Indicates if the client can be removed.

uint32_t mId

The ID of the client.

std::atomic<bool> mExit = false

The exit signal (set when the client disconnects or indirectly by SIGINT by Controller).

MeasurementConfig and TmpMeasurementConfig

The measurement configuration is implemented in two different classes because the measurement holds atomic values. To modify the measurement configuration a temporary configuration is used by copying the current configuration into a non-atomic version. This is then modified and copied back into the atomic version.

class MeasurementConfig

The MeasurementConfig class represents the configuration for a measurement.

The MeasurementConfig class is used to store the configuration for a measurement. All values are stored as atomic values to allow for concurrent access.

Public Functions

inline MeasurementConfig &operator=(const TmpMeasurementConfig &other)

Copy assignment operator from TmpMeasurementConfig.

Parameters:

other – The TmpMeasurementConfig object to copy from.

Returns:

A reference to the modified MeasurementConfig object.

inline operator TmpMeasurementConfig() const

Conversion operator to convert to a non-atomic copy.

Returns:

A non-atomic copy of the MeasurementConfig object.

inline MeasurementState getState() const

Get the state of the measurement.

Returns:

The state of the measurement.

inline void setState(MeasurementState state)

Set the state of the measurement.

Parameters:

state – The state to set.

inline controller::Channel getChannels() const

Get the channels for the measurement.

The channels to be used for the DMA operations (0 -> first, 1 -> second, 3 -> both).

Returns:

The channels for the measurement.

inline void setChannels(controller::Channel channels)

Set the channels for the measurement.

Parameters:

channels – The channels to set.

inline uint32_t getMeasurementTime() const

Get the measurement time.

Returns:

The measurement time.

inline void setMeasurementTime(uint32_t measurementTime)

Set the measurement time.

Parameters:

measurementTime – The measurement time to set.

inline int32_t getTriggerValue() const

Get the trigger value.

Returns:

The trigger value.

inline void setTriggerValue(int32_t triggerValue)

Set the trigger value.

Parameters:

triggerValue – The trigger value to set.

inline uint32_t getPreGate() const

Get the pre-gate value.

Returns:

The pre-gate value.

inline void setPreGate(uint32_t preGate)

Set the pre-gate value.

Parameters:

preGate – The pre-gate value to set.

inline uint32_t getLongGate() const

Get the long-gate value.

Returns:

The long-gate value.

inline void setLongGate(uint32_t longGate)

Set the long-gate value.

Parameters:

longGate – The long-gate value to set.

Private Members

std::atomic<MeasurementState> mState = MeasurementState::IDLE

The measurement state.

std::atomic<controller::Channel> mChannels = controller::Channel::NONE

The channels to be used for the DMA operations (0 -> first, 1 -> second, 3 -> both).

std::atomic<uint32_t> mMeasurementTime = 0

The measurement time.

std::atomic<int32_t> mTriggerValue = 0

The trigger value in units of the ADC.

std::atomic<uint32_t> mPreGate = 0

The number of samples for the pre gate.

std::atomic<uint32_t> mLongGate = 0

The number of samples for the long gate.

class TmpMeasurementConfig

The TmpMeasurementConfig class represents a non-atomic version of the MeasurementConfig class.

Public Functions

inline TmpMeasurementConfig &operator=(const MeasurementConfig &other)

Copy assignment operator from MeasurementConfig.

Parameters:

other – The MeasurementConfig object to copy from.

Returns:

A reference to the modified TmpMeasurementConfig object.

inline MeasurementState getState() const

Get the state of the measurement.

Returns:

The state of the measurement.

inline void setState(MeasurementState state)

Set the state of the measurement.

Parameters:

state – The state to set.

inline controller::Channel getChannels() const

Get the channels for the measurement.

The channels to be used for the DMA operations (0 -> first, 1 -> second, 3 -> both).

Returns:

The channels for the measurement.

inline void setChannels(controller::Channel channels)

Set the channels for the measurement.

Parameters:

channels – The channels to set.

inline uint32_t getMeasurementTime() const

Get the measurement time.

Returns:

The measurement time.

inline void setMeasurementTime(uint32_t measurementTime)

Set the measurement time.

Parameters:

measurementTime – The measurement time to set.

inline int32_t getTriggerValue() const

Get the trigger value.

Returns:

The trigger value.

inline void setTriggerValue(int32_t triggerValue)

Set the trigger value.

Parameters:

triggerValue – The trigger value to set.

inline uint32_t getPreGate() const

Get the pre-gate value.

Returns:

The pre-gate value.

inline void setPreGate(uint32_t preGate)

Set the pre-gate value.

Parameters:

preGate – The pre-gate value to set.

inline uint32_t getLongGate() const

Get the long-gate value.

Returns:

The long-gate value.

inline void setLongGate(uint32_t longGate)

Set the long-gate value.

Parameters:

longGate – The long-gate value to set.

Private Members

MeasurementState mState = MeasurementState::IDLE

The measurement state.

controller::Channel mChannels = controller::Channel::NONE

The channels to be used for the DMA operations (0 -> first, 1 -> second, 3 -> both).

uint32_t mMeasurementTime = 0

The measurement time.

int32_t mTriggerValue = 0

The trigger value in units of the ADC.

uint32_t mPreGate = 0

The number of samples for the pre gate.

uint32_t mLongGate = 0

The number of samples for the long gate.

Client configuration structure

struct ClientConfig

The ClientConfig struct represents the configuration for a client.

Public Members

bool mWantsData = false

Indicates whether the client wants data.

Message types enumeration

enum class nexmess::components::network::MessageType : uint8_t

Enumeration representing the available message types.

Each message follows the same structure:

  • The first unsigned byte is the message type

  • The following four bytes (unsigned 32 bit integer) are the message length

  • The rest of the message is the actual message

Values:

enumerator DMA0

DMA0 is data for the first DMA (raw data)

This message is used to send the raw data from the first DMA to the client. The raw data consists of 16 bit signed integers representing the arbitrary values from the ADC.

If this message was sent by the client an error is returned as JSON.

{
  "status", {
    "type": "error",
    "message": "received message type only sent by server"
  }
}
enumerator DMA1

DMA1 is data for the first DMA (raw data)

See DMA0 for more information.

enumerator START

START is a message to start the acquisition.

It starts the measurement if the client is connected. It optionally may set the client and measurement configuration.

Example of the JSON data to send without configuration:

{}

Example of the JSON data to send with configuration:

{
  "client-config": {"wants-data": false},
  "measurement-config": {
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

Response (error):

{
  "status": {
    "type": "error",
    "message": "measurement already running"
  }
}

The following error messages are possible:

  • ”not connected”

  • ”measurement already running”

  • ”channels must be 1, 2 or 3 (for both)”

  • ”could not start measurement”

Response (success):

{
  "status": {
    "type": "success"
  },
  "client-config": {"wants-data": false},
  "measurement-config": {
    "state": "running",
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

enumerator STOP

STOP is a message to stop the acquisition.

It stops the measurement if the client is connected. This message does not require any user data.

Example of the JSON data to send:

{}

Response (error):

{
  "status": {
    "type": "error",
    "message": "measurement not running"
  }
}

The following error messages are possible:

  • ”not connected”

  • ”measurement not running”

Response (success):

{
  "status": {
    "type": "success"
  }
}

Param data:

The data of the message.

enumerator CONNECT

CONNECT is a message to connect to the server.

It is the first message to send to the server.

The connect message must include the client version. The format of the version is “vX.Y.Z”. The response contains the server version and the client and measurement configuration on success.

Example of the JSON data to send:

{
  "version": "v0.0.1",
}

Response (error):

{
  "status": {
    "type": "error",
    "message": "version mismatch"
  },
  "version": "v0.0.1"
}

The following error messages are possible:

  • ”already connected”

  • ”no version given”

  • ”invalid version given”

  • ”version mismatch”

Response (success):

{
  "status": {
    "type": "success"
  },
  "version": "v0.0.1",
  "client-config": {"wants-data": false},
  "measurement-config": {
    "state": "running",
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

enumerator STATE

STATE is a message to get the current state of the server.

It returns the current measurement state (idle, running or stopped). This message does not require any user data.

Example of the JSON data send:

{}

Response (error):

{
  "status": {
    "type": "error",
    "message": "not connected"
  }
}

The following error messages are possible:

  • ”not connected”

Response (success):

{
  "status": {
    "type": "success"
  },
  "measurement-config": {
    "state": "running"
  }
}

enumerator SETTINGS

SETTINGS is a message to send the settings to the server.

It sets the client and measurement configuration if the client is connected. If no configuration is sent, the current configuration is returned.

Example of the JSON data to send without configuration:

{}

Example of the JSON data to send with configuration:

{
  "client-config": {"wants-data": false},
  "measurement-config": {
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

Response (error):

{
  "status": {
    "type": "error",
    "message": "cannot change measurement config during measurement"
  }
}

The following error messages are possible:

  • ”not connected”

  • ”cannot change measurement config during measurement”

  • ”channels must be 1, 2 or 3 (for both)”

Response (success):

{
  "status": {
    "type": "success"
  },
  "client-config": {"wants-data": false},
  "measurement-config": {
    "state": "running",
    "channels": 1,
    "measurement-time": 100,
    "trigger-value": 15,
    "pre-gate": 10,
    "long-gate": 100
  }
}

enumerator NOTIFY

NOTIFY is a message to notify the clients about things.

It is used to notify the clients about things like the measurement state.

If this message was sent by the client an error is returned as JSON.

{
  "status", {
    "type": "error",
    "message": "received message type only sent by server"
  }
}

The JSON of a notification always contains a “status” object with a type string value. The type string value matches the name of the actual notification object with further information.

The following notifications are possible:

Measurement config:

{
  "status": {
    "type": "measurement-config"
  },
  "measurement-config": {
    "state": "stopped"
  }
}

The state my be “idle”, “running” or “stopped”.

Buffer full:

{
  "status": {
    "type": "dma",
    "message": "buffer full"
  },
  "dma": {
    "id": 1
  }
}

The id is the id of the DMA (0 or 1).

enumerator NONE

NONE marks the last Type (is used to determine valid types)