feat(tcp): TCPStreamer DataSources, in-process TCPStreamService, stream multiplexing and TCPStreamJoin

Add TCPStreamer/TCPStreamerClient (TCP transport behind the UDPSServerI/UDPSClientI
seam), an optional single in-process TCPStreamService serving many streams over
worker threads, multiplexed SUBSCRIBE (comma list, stream index in fragmentIdx),
and TCPStreamJoin, which merges several streams into one sample-and-hold stream
paced by a master. StreamHub takes a comma list of streams over one connection.
Includes GTests, E2E scenarios s58-s73 and docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-11-06 13:41:27 +01:00
1 parent 5562877c99
commit 50d85363a3
79 files changed
+12348 -138

No files matched your search

@@ -11,6 +11,7 @@
#define STREAMHUB_UDP_SOURCE_SESSION_H_
#include "UDPSClient.h"
#include "TCPSClient.h"
#include "UDPSProtocol.h"
#include "FastPollingMutexSem.h"
#include "HighResolutionTimer.h"
@@ -158,11 +159,23 @@ public:
* @param maxPts Ring buffer capacity per signal.
* @param mcGroup Multicast group (empty string = unicast).
* @param dataPort Multicast data port (0 = port+1).
* @param transport "udp" (default) or "tcp". TCP ignores mcGroup/dataPort.
* @param streamName TCP only: stream to subscribe to on a TCPStreamService
* ("" = plain TCPStreamer, no subscription).
* @param external Optional receiver owned by the caller (one stream of a
* multiplexed TCPSClient, see TCPSClient::GetStream()). When set the
* session does not create or configure its own client; call
* DetachClient() before the receiver is destroyed.
* @return true on success.
*/
bool Initialise(const char *id, const char *label,
const char *addr, uint16 port, uint32 maxPts,
const char *mcGroup = "", uint16 dataPort = 0u);
const char *mcGroup = "", uint16 dataPort = 0u,
const char *transport = "udp", const char *streamName = "",
MARTe::UDPSClientI *external = static_cast<MARTe::UDPSClientI *>(0));
/** @brief Stop and forget an external receiver (no-op for built-in receivers). */
void DetachClient();
/** @brief Start the UDPSClient background thread. */
bool Start();
@@ -236,6 +249,12 @@ public:
/** @return Multicast data port (0 = port+1 default). */
uint16 GetDataPort() const;
/** @return "udp" or "tcp". */
StreamString GetTransport() const;
/** @return TCP stream name ("" = none). */
StreamString GetStreamName() const;
/** @return true after the first CONFIG packet has been fully parsed. */
bool IsConfigured() const;
@@ -443,13 +462,18 @@ private:
uint16 port_;
StreamString mcGroup_; ///< Multicast group ("" = unicast)
uint16 dataPort_; ///< Multicast data port (0 = port+1)
StreamString transport_; ///< "udp" or "tcp"
StreamString streamName_;///< TCPStreamService stream name ("" = none)
uint32 maxPoints_;
uint32 ringTemporal_; ///< Ring capacity for multi-element signals
uint32 ringScalar_; ///< Ring capacity for scalar signals
bool initialised_;
/* UDPSClient (owns the background thread) */
UDPSClient client_;
/* Receivers (each owns a background thread); client_ points at the active one. */
UDPSClient udpClient_;
MARTe::TCPSClient tcpClient_;
MARTe::UDPSClientI *client_;
bool external_; ///< client_ is owned by the caller
/* Signal metadata — protected by metaMutex_ */
mutable FastPollingMutexSem metaMutex_;