Files
2026-07-01 16:39:34 +02:00

231 lines
9.2 KiB
C++

#ifndef UDPS_CLIENT_H_
#define UDPS_CLIENT_H_
/**
* @file UDPSClient.h
* @brief Auto-reconnecting UDPS receiver client (C++ MARTe2 library class).
*
* UDPSClient runs its own background thread (via SingleThreadService).
* On start it connects to a UDPSServer, receives CONFIG + DATA packets, and
* reassembles fragmented updates. If the server goes silent for longer than
* SilenceTimeout, it automatically disconnects and retries.
*
* Unicast mode: sends CONNECT to server UDP port; receives on an ephemeral UDP port.
* Multicast mode: TCP to server port for CONFIG; joins UDP multicast group for DATA.
*
* Threading: UDPSClientListener callbacks are invoked from the internal receive
* thread — the implementation must be thread-safe with respect to the caller.
*/
#include "BasicTCPSocket.h"
#include "BasicUDPSocket.h"
#include "EmbeddedServiceMethodBinderI.h"
#include "ExecutionInfo.h"
#include "HighResolutionTimer.h"
#include "InternetHost.h"
#include "SingleThreadService.h"
#include "StreamString.h"
#include "StructuredDataI.h"
#include "UDPSProtocol.h"
namespace MARTe {
// ---------------------------------------------------------------------------
// Listener interface
// ---------------------------------------------------------------------------
/**
* @brief Callback interface for UDPSClient events.
*
* Implement this interface and pass an instance to UDPSClient::SetListener().
* All callbacks are invoked from the UDPSClient internal thread.
*/
class UDPSClientListener {
public:
virtual ~UDPSClientListener() {}
/** Called when a fully-reassembled CONFIG payload is available. */
virtual void OnUDPSConfig(const uint8 *payload, uint32 payloadSize) {}
/** Called when a fully-reassembled DATA payload is available. */
virtual void OnUDPSData(const uint8 *payload, uint32 payloadSize) {}
/**
* @brief Called for every received DATA datagram (fragment).
* @param counter DATA packet counter from the UDPS header.
* @param nBytes Raw datagram size (header + payload).
* @param complete True iff this fragment completed the DATA reassembly.
*/
virtual void OnUDPSFragment(uint32 counter, uint32 nBytes, bool complete) {}
/** Called when the connection to the server has been established. */
virtual void OnUDPSConnected() {}
/** Called when the connection has been lost or closed. */
virtual void OnUDPSDisconnected() {}
};
// ---------------------------------------------------------------------------
// UDPSClient
// ---------------------------------------------------------------------------
/**
* @brief UDPS receiver client with auto-reconnect and fragment reassembly.
*/
class UDPSClient : public EmbeddedServiceMethodBinderI {
public:
/** Maximum number of concurrent in-flight reassembly slots. */
static const uint32 UDPS_CLIENT_MAX_REASSEMBLY_SLOTS = 4u;
/** Maximum size (bytes) of a single reassembled packet payload. A UDPS
* update for one source is fragmented into MaxPayloadSize chunks; this is
* the ceiling on the reassembled total, so it bounds the largest multi-
* fragment packet the client can deliver. Sized for large array bursts
* (e.g. 8x10000 float32 ~= 320 KiB) with headroom; stays well within the
* 256-fragment span the recvMask[32] tracks at typical chunk sizes. */
static const uint32 UDPS_CLIENT_MAX_PACKET_BYTES = 1048576u; // 1 MiB
/** Default silence timeout before reconnect (seconds). */
static const uint32 UDPS_CLIENT_DEFAULT_SILENCE_TIMEOUT_S = 5u;
/** Default delay between reconnect attempts (seconds). */
static const uint32 UDPS_CLIENT_DEFAULT_RECONNECT_DELAY_S = 2u;
/** Default maximum payload size (bytes, excluding 17-byte header). */
static const uint32 UDPS_CLIENT_DEFAULT_MAX_PAYLOAD = 1400u;
/** Default OS UDP receive socket buffer size (bytes). The Linux default
* (rmem_default, typically ~208 KiB) is easily overrun by high-throughput
* sources (e.g. multi-hundred-KiB bursts every few ms), causing silent
* kernel-level datagram drops. 4 MiB gives generous burst headroom. */
static const uint32 UDPS_CLIENT_DEFAULT_RECV_BUFFER = 4194304u; // 4 MiB
UDPSClient();
virtual ~UDPSClient();
/**
* @brief Read configuration from a StructuredDataI node.
*
* Expected keys:
* - ServerAddr (char*) Server IPv4 address. Required.
* - Port (uint16) Server UDP port (unicast) or TCP listen port (multicast). Required.
* - MulticastGroup (char*) IPv4 multicast address; presence enables multicast mode.
* - DataPort (uint16) UDP multicast data port (defaults to Port+1).
* - SilenceTimeout (uint32) Seconds of no data before reconnect. Default 5.
* - ReconnectDelay (uint32) Seconds to wait between reconnect attempts. Default 2.
* - MaxPayloadSize (uint32) Max payload bytes per datagram, excluding header. Default 1400.
* - CPUMask (uint32) CPU affinity mask for the receive thread. Default 0xFFFFFFFF.
* - StackSize (uint32) Stack size for the receive thread. Default 65536.
* - RecvBufferSize (uint32) OS UDP receive socket buffer size (bytes). Default 4 MiB.
*/
bool Initialise(StructuredDataI &data);
/**
* @brief Register the event listener. Must be called before Start().
*/
void SetListener(UDPSClientListener *listener);
/**
* @brief Start the receive thread.
* @return true on success.
*/
bool Start();
/**
* @brief Stop the receive thread and close all sockets.
* @return true on success.
*/
bool Stop();
/**
* @brief Internal thread entry point (EmbeddedServiceMethodBinderI).
* @details Do NOT call directly.
*/
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info);
private:
// -------------------------------------------------------------------------
// Fragment reassembly slot
// -------------------------------------------------------------------------
struct UDPSReassemblySlot {
uint32 counter; ///< Packet counter this slot belongs to
uint8 type; ///< UDPS_TYPE_DATA or UDPS_TYPE_CONFIG
uint16 totalFragments; ///< Expected fragment count
uint16 receivedFragments; ///< How many we have so far
uint8 recvMask[32]; ///< Bitmask: bit f set iff fragment f received
uint8 payload[UDPS_CLIENT_MAX_PACKET_BYTES]; ///< Assembled payload buffer
uint64 firstSeenTicks; ///< For GC (2 s stale detection)
bool active; ///< Slot in use
uint32 chunkSize; ///< Payload bytes per fragment (from first fragment)
uint32 assembledBytes; ///< Exact total payload bytes placed so far
};
// -------------------------------------------------------------------------
// Connection state machine helpers
// -------------------------------------------------------------------------
bool Connect();
void Disconnect();
bool ReceiveAndProcess();
bool ConnectUnicast();
bool ConnectMulticast();
/** Set the OS receive buffer size (SO_RCVBUF) on a UDP socket's raw handle. */
void SetRecvBufferSize(BasicUDPSocket &sock);
void ProcessDatagram(const uint8 *buf, uint32 size);
/** Read one full UDPS frame (header + payload) from the TCP control socket. */
bool ReceiveTCPFrame();
/** Read exactly n bytes from the TCP control socket. */
bool ReadExactTCP(uint8 *dst, uint32 n);
/** @return true iff this fragment completed the reassembly (payload delivered). */
bool PlaceFragment(const UDPSPacketHeader *hdr, const uint8 *payload, uint32 payloadBytes);
void GcReassemblySlots();
void DeliverAssembled(UDPSReassemblySlot &slot);
// -------------------------------------------------------------------------
// Configuration
// -------------------------------------------------------------------------
StreamString serverAddr;
uint16 serverPort;
StreamString multicastGroup;
uint16 dataPort;
bool useMulticast;
uint64 silenceTimeoutTicks;
uint64 reconnectDelayTicks;
uint32 maxPayloadSize;
uint32 cpuMask;
uint32 stackSize;
uint32 recvBufferSize;
// -------------------------------------------------------------------------
// Runtime state
// -------------------------------------------------------------------------
UDPSClientListener *listener;
SingleThreadService threadService;
bool connected;
uint64 lastDataTicks; ///< Ticks at last received DATA/CONFIG
uint64 disconnectTick; ///< Ticks when we disconnected (for delay)
// Unicast
BasicUDPSocket recvSocket; ///< Bound to ephemeral port; receives DATA
uint16 localPort; ///< Ephemeral port we're listening on
// Multicast
BasicTCPSocket tcpSocket; ///< TCP connection to server (for CONNECT + CONFIG)
BasicUDPSocket mcastSocket; ///< Joined multicast group
// Reassembly
UDPSReassemblySlot reassemblySlots[UDPS_CLIENT_MAX_REASSEMBLY_SLOTS];
uint64 lastGcTicks; ///< Ticks at last GC run
// Receive scratch buffer
uint8 recvBuf[65535u + UDPS_HEADER_SIZE];
};
} // namespace MARTe
#endif // UDPS_CLIENT_H_