Add TCP control + UDP multicast mode to UDPStreamer

DataSource (C++):
- New optional config: MulticastGroup (e.g. "239.0.0.1") and DataPort
  (default Port+1); when set, enables multicast mode; absent = unicast
- Control plane: BasicTCPSocket listener on Port; CONNECT received over
  TCP triggers HandleTCPConnect() which sends CONFIG via the same TCP
  connection
- Data plane: BasicUDPSocket aimed at MulticastGroup:DataPort; all DATA
  fragments sent via dataSocket.Write() so any joined client receives them
- useMulticast is the single branch point; every unicast code path is
  unchanged when MulticastGroup is absent
- New methods: HandleTCPConnect(), IsMulticast()
- 5 new unit tests (ports 44710-44729); all 38 tests passing

WebUI hub (Go):
- SourceConfig gains MulticastGroup and DataPort fields (JSON, optional)
- UDPClient gains multicastGroup/dataPort; Run() routes to new
  runMulticastSession() when multicastGroup is non-empty
- runMulticastSession(): TCP dial for CONNECT/CONFIG, net.ListenMulticastUDP
  for DATA, background goroutine watches TCP for DISCONNECT/close
- All existing sm.Add() call sites updated (unicast callers pass "", 0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino Ferrari
2026-05-26 22:56:27 +02:00
parent f85ab8652c
commit b15e637f14
9 changed files with 864 additions and 58 deletions
@@ -31,6 +31,7 @@
/*---------------------------------------------------------------------------*/
/* Project header includes */
/*---------------------------------------------------------------------------*/
#include "BasicTCPSocket.h"
#include "BasicUDPSocket.h"
#include "CompilerTypes.h"
#include "EmbeddedServiceMethodBinderI.h"
@@ -275,6 +276,11 @@ public:
*/
bool IsClientConnected() const;
/**
* @brief Returns true when multicast mode is active (MulticastGroup was set in config).
*/
bool IsMulticast() const;
private:
/**
* @brief Serializes the CONFIG payload into buf and sets payloadSize.
@@ -296,10 +302,16 @@ private:
bool SendFragmented(uint8 type, uint32 counter, const uint8 *payload, uint32 payloadSize);
/**
* @brief Handles a single received command packet from a client.
* @brief Handles a single received command packet from a client (unicast mode).
*/
void HandleClientCommand(const uint8 *buf, uint32 size);
/**
* @brief Handles a CONNECT received on the TCP control socket (multicast mode).
* @details Validates magic+type, sets clientConnected, builds and sends CONFIG via tcpClient.
*/
void HandleTCPConnect(const uint8 *buf, uint32 size);
/**
* @brief Maps a MARTe2 TypeDescriptor to the UDPS_TYPECODE_* constants.
*/
@@ -330,11 +342,19 @@ private:
uint8 *wireBuffer; /**< Pre-allocated buffer for the serialized DATA payload */
uint32 totalWireBytes; /**< Total bytes of all signals after quantization + 8-byte timestamp prefix */
/* Networking */
BasicUDPSocket serverSocket; /**< Bound to port; receives client commands */
BasicUDPSocket clientSocket; /**< Configured to send to the connected client */
/* Networking — unicast mode */
BasicUDPSocket serverSocket; /**< Bound to port; receives client commands (unicast) */
BasicUDPSocket clientSocket; /**< Configured to send to the connected client (unicast) */
volatile bool clientConnected; /**< True when a client has successfully connected */
/* Networking — multicast mode (only used when useMulticast == true) */
StreamString multicastGroup; /**< Multicast group IP, e.g. "239.0.0.1"; empty = unicast */
uint16 dataPort; /**< UDP port for DATA datagrams in multicast mode */
bool useMulticast; /**< Derived from multicastGroup.Size() > 0 in Initialise() */
BasicTCPSocket tcpListener; /**< TCP server socket: accepts control connections */
BasicTCPSocket *tcpClient; /**< Accepted TCP control connection (heap by WaitConnection) */
BasicUDPSocket dataSocket; /**< UDP socket aimed at multicastGroup:dataPort */
/* Thread management */
SingleThreadService executor; /**< Background thread service */