Sending data to felix-toflx
Various send_data functions are provided to send data to felix-toflx. felix-toflx is publishing the necessary
parameters to establish network connections on the bus. Before data can be sent, the connection to felix-toflx must
be established calling init_send_data. felix-client will re-use existing connections if possible. If a connection
is lost (e.g. because felix-toflx went down), the client will automatically attempt to re-establish the connection.
There is no function to close connections.
Synchronous vs asynchronous operations
Connections can be established synchronously or asynchronously. The synchronous functions block until the connection is established or a timeout occurs. The asynchronous functions return immediately and informs the user using a callback. Sending data is asynchronous by default.
Establishing a connection
Connections are established using the
init_send_data
(synchronous) or init_send_data_nb
(asynchronous) function.
-
virtual void FelixClientThreadExtension520::init_send_data(std::uint64_t fid, std::chrono::milliseconds timeout) = 0
Initialize a send connection to FELIX.
This function checks if a connection has to be established and opens a new connection if needed. The function will block until the connection is established or the timeout is reached. If the function returns without throwing an exception, the connection is established.
- Throws:
felix::SendConnectionTimeoutException – if the connection was not established within the timeout
felix::SendConnectionRefusedException – if the connection was refused
felix::BusException – if the tag is not found in the bus
- Parameters:
fid – The tag to send data to
timeout – The timeout for establishing the connection
-
virtual void FelixClientThreadExtension520::init_send_data_nb(std::uint64_t fid) = 0
Asynchronously initialize a send connection to FELIX.
This function checks if a connection has to be established and opens a new connection if needed. The function will return immediately. The result is communicated through the on_connect callback. If the connection is refused, the on_refused callback is called. If already the connection request fails, an exception is thrown.
- Throws:
felix::BusException – if the tag is not found in the bus
- Parameters:
fid – The tag to send data to
Both functions can report a felix::BusException if the lookup in the bus failed. The underlying reason is reported
by the exception and can be retrieved by calling get_underlying_message.
The synchronous function can also report a felix::SendConnectionTimeoutException if the connection could not be
established within the timeout and a felix::SendConnectionRefusedException if the connection was refused by the
server.
The asynchronous function should call one of the two callbacks This callback
or This callback to inform the user about the result of
the connection.
-
typedef std::function<void(uint64_t fid)> FelixClientThreadInterface::OnConnectCallback
-
typedef std::function<void(uint64_t fid)> FelixClientThreadExtension520::OnConnectionRefusedCallback
Tip
As for subscriptions, it is recommended to use the synchronous function to establish a connection. The asynchronous should only be used if you have a good reason to do so. However, conpared to subscriptions, handling the asynchronous case is much easier since only a connection needs to be established which should always result in one of the two callbacks being called. The connection is either established or refused. For subscriptions, messages need to be exchanged over the network which has an inherent chance to fail and not cause any callback to be called.
Caution
If you use asynchronous connections, make sure that callbacks are not blocking.
Sending data
Warning
Calling send_data without establishing a connection first is deprecated and will be removed in a future release.
There are two functions to send data to felix-toflx. The
first function
sends a single message, the
second one
sends a list of messages.
-
virtual void FelixClientThreadExtension520::send_data(std::uint64_t fid, std::span<const std::uint8_t> data, bool flush) = 0
Function to send a message to a remote fid.
This function sends a single message to FELIX. If a buffered sender is used (depending on information in the bus), the flush parameter can be used to flush the buffer and send the message immediately.
Note
Can be called without init_send_data but this behavior will change and throw an exception in the future.
- Throws:
felix::SendWhileConnectionDownException – if the connection was lost and is being re-established
felix::ResourceNotAvailableException – if no network buffers were available (try again)
felix::MessageTooBigException – if message was too big (retrying will fail again)
felix::BusException – if the tag is not found in the bus
- Parameters:
fid – The tag to send data to
data – The data to send
flush – Flush the buffer
-
virtual void FelixClientThreadExtension520::send_data(std::uint64_t fid, const std::vector<std::span<const std::uint8_t>> &msgs) = 0
Function to send multiple messages to a remote fid.
This function sends multiple messages to FELIX. If a buffered sender is used (depending on information in the bus) it is the same as calling send_data for each message. For unbuffered sending, messages will still be send together in one transaction (or as slittle as possible to fit into buffers).
Note
Can be called without init_send_data but this behavior will change and throw an exception in the future.
- Throws:
felix::SendWhileConnectionDownException – if the connection was lost and is being re-established
felix::ResourceNotAvailableException – if no network buffers were available (try again)
felix::MessageTooBigException – if message was too big (retrying will fail again)
felix::BusException – if the tag is not found in the bus
- Parameters:
fid – The tag to send data to
data – The data to send
flush – Flush the buffer
Both functions take the FID of the stream to send data to and the data to be sent as a span (essentially a pointer with a
size). The first function also takes a boolean parameter flush. If flush is set to true, the data is sent immediately.
The second function assumes flushes automatically.
Tip
The second one
function taking multiple messages ensures that the messages are sent in the same network transaction to avoid issues if
message timing is important.
Both functions make a copy of the data to be sent. Therefore, the user can safely modify or delete the data after the function has returned.
Both functions can throw four exceptions indicating failure. If the function returns without exception, the data will be sent:
felix::SendWhileConnectionDownException: the connection to felix-toflx is currently down. It was established at some point, but was lost subsequently. The reason might be that the server is down. felix-client will attempt to reconnect automatically.felix::ResourceNotAvailableException: no network resources are available to send the data. This can happen data is sent too fast. The user should wait a bit and try again. If the issue persists, there might be an issue with the server.felix::MessageTooBigException: the message is too big to be sent. The maximum size of a message is defined by the server and specified in the bus. Change the command line parameters of felix-toflx to increase the buffer size.felix::BusException: the lookup in the bus failed.
Caution
Once the deprecated behavior that send_data can be called without establishing a connection first is removed, the function
will throw a felix::SendBeforeOpenException if the connection was not established before calling send_data.
Send methods
The send method is determined by the settings in the bus file which are determined by the felix-toflx command line parameters. In general, RDMA relies on the allocation of network buffers to send data while TCP tries to optimize the memory consumption by allocating memory in demand when using unbuffered sending. Here is a list of the possible send methods:
buffered, TCP/RDMA, single message: Copy data into pre-allocated buffersbuffered, TCP/RDMA, multiple messages: Copy data into pre-allocated buffers. Flush buffer at the end. Data might be split into multiple network transactions if they do not fit into the current buffer.unbuffered, TCP, single message: Allocate memory on demand. Copy data into the allocated memory. Then send it.unbuffered, TCP, multiple messages: Allocate memory on demand. Copy data into the allocated memory. Flush buffer at the end. Data is guaranteed to be sent in the same network transaction.unbuffered, RDMA, single message: Copy data into pre-allocated buffers. Flush buffer.unbuffered, RDMA, multiple messages: Copy data into pre-allocated buffers. Flush buffer at the end. Data is guaranteed to be sent in the same network transaction.
Closing send connections
There is no function to close the connection to felix-toflx. The connection is closed automatically when the felix-client-thread instance is destroyed.
Deprecated API
Calling send_data without establishing a connection first is deprecated and will be removed in a future release.
All send_data functions taking a pair of const uint8_t* and size_t are deprecated.
All non-blocking send_data functions are deprecated. send_data is now always non-blocking in the sense of the previous
non-blocking functions.
Example
1try {
2 fct.init_send_data(fid, 1000ms);
3} catch (const felix::FelixClientException& e) {
4 std::println("Failed to establish connection: {}", e.what());
5 return;
6}
7
8const auto data = std::vector<std::uint8_t>{0, 1, 2, 3, 4, 5};
9
10try {
11 client.send_data(fid, data, true);
12} catch (const felix::FelixClientException& e) {
13 std::println("Failed to send data: {}", e.what());
14 return;
15}
(Incomplete) example using asynchronous connection establishment (exception handling omitted):
1const auto fid = 0x1000000000080000;
2
3std::atomic_bool connected{false};
4
5void on_connect_callback(const std::uint64_t fid_connected) {
6 std::println("Connected to FID: {:#x}", fid_connected);
7 if (fid_connected == fid) {
8 connected = true;
9 }
10}
11
12fct.init_send_data_nb(fid);
13
14connected.wait(false);
15// Questions:
16// - What about timeout?
17// - Cancel wait if the connection was refused?
18// - If using a timeout, what happens if the connection is established after the timeout?
19
20const auto data = std::vector<std::uint8_t>{0, 1, 2, 3, 4, 5};
21client.send_data(fid, data, true);