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

@@ -0,0 +1,163 @@
#ifndef TCPS_CLIENT_H_
#define TCPS_CLIENT_H_
/**
* @file TCPSClient.h
* @brief Auto-reconnecting TCP receiver for the UDPS wire format.
*
* TCPSClient is the consumer-side counterpart of TCPSServer. It connects to a
* TCPSServer, reads UDPS frames (17-byte header + payload) from the byte stream
* and delivers CONFIG / DATA payloads through UDPSClientListener, so any
* listener written for UDPSClient works unchanged.
*
* It runs its own thread (SingleThreadService); every wait inside Execute() is
* bounded by 50 ms so Stop() is prompt. A partial frame survives across
* iterations. Bad magic, an oversize length, or a fragmented frame means the
* stream cannot be trusted any more: the connection is dropped and re-opened.
*
* There is no CONNECT / ACK / keepalive traffic: the TCP connection is the
* session and the server sends CONFIG on accept.
*/
#include "EmbeddedServiceMethodBinderI.h"
#include "FastPollingMutexSem.h"
#include "ExecutionInfo.h"
#include "SingleThreadService.h"
#include "StreamString.h"
#include "StructuredDataI.h"
#include "UDPSClient.h"
#include "UDPSProtocol.h"
#include "UDPSTransportI.h"
namespace MARTe {
class TCPSClient;
/**
* One stream of a (possibly multiplexed) TCPSClient connection, seen as an
* independent UDPSClientI. Obtained from TCPSClient::GetStream(). Start()/Stop()
* are reference counted so the shared connection runs while any stream wants it.
*/
class TCPSClientStream : public UDPSClientI {
public:
TCPSClientStream(TCPSClient &owner, uint32 index);
virtual ~TCPSClientStream();
/** The connection is configured through its TCPSClient; always succeeds. */
virtual bool Initialise(StructuredDataI &data);
virtual void SetListener(UDPSClientListener *newListener);
virtual bool Start();
virtual bool Stop();
virtual uint32 GetLastDataGap() const;
private:
TCPSClient &owner;
uint32 index;
bool started;
};
class TCPSClient : public UDPSClientI, public EmbeddedServiceMethodBinderI {
public:
/** Most streams one connection can multiplex. */
static const uint32 TCPS_CLIENT_MAX_STREAMS = UDPS_TCP_MAX_STREAMS;
/** Default delay between reconnect attempts (seconds). */
static const uint32 TCPS_CLIENT_DEFAULT_RECONNECT_DELAY_S = 2u;
TCPSClient();
virtual ~TCPSClient();
/**
* Keys:
* - ServerAddr (char*) IPv4 address or host name. Default 127.0.0.1.
* - Port (uint32) Server TCP port. Required.
* - StreamName (char*) Optional. When set, a SUBSCRIBE frame carrying
* this name is sent right after every connect, to
* select one stream of a TCPStreamService. A plain
* TCPSServer ignores (discards) it. A comma
* separated list ("a,b,c") multiplexes several
* streams over this one connection; stream i of
* the list is reached through GetStream(i).
* - SilenceTimeout (float32) Seconds without any received byte before the
* connection is considered dead. Default 0
* (disabled: TCP reports a dead peer itself).
* - ReconnectDelay (float32) Seconds between connection attempts. Default 2.
* - CPUMask (uint32) Receive thread affinity. Default 0xFFFFFFFF.
* - StackSize (uint32) Receive thread stack. Default 65536.
*/
virtual bool Initialise(StructuredDataI &data);
virtual void SetListener(UDPSClientListener *newListener);
/** Listener of stream @p index (0 = SetListener()). Call before Start(). */
void SetStreamListener(uint32 index, UDPSClientListener *newListener);
/** @return number of streams subscribed to (1 unless StreamName is a list). */
uint32 GetNumStreams() const;
/**
* @return a UDPSClientI for stream @p index, owned by this client (NULL if out of
* range). Valid after Initialise().
*/
UDPSClientI *GetStream(uint32 index);
/** Counter gap of the DATA frame of stream @p index being delivered. */
uint32 GetStreamDataGap(uint32 index) const;
/** Reference-counted Start()/Stop() used by the TCPSClientStream facades. */
bool AcquireStart();
bool ReleaseStop();
virtual bool Start();
virtual bool Stop();
/** Thread entry point; do not call directly. */
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info);
/** Packets the server dropped for this client just before the DATA frame being delivered. */
virtual uint32 GetLastDataGap() const;
/** @return true while a connection is established. */
bool IsConnected() const;
/** @return connections established since Start(). */
uint32 GetConnectionCount() const;
/** @return connections dropped because of a protocol violation. */
uint32 GetProtocolErrors() const;
private:
bool Connect();
void Disconnect();
/** One bounded receive step. @return false when the connection is lost. */
bool ReceiveStep();
/** Deliver the complete frame sitting in rxBuf. @return false on a protocol error. */
bool Dispatch();
/** Validate StreamName as one name or a comma separated list; sets numStreams. */
bool ParseStreamList();
StreamString serverAddr;
StreamString streamName;
uint32 serverPort;
uint64 silenceTimeoutTicks;
uint64 reconnectDelayTicks;
uint32 cpuMask;
uint32 stackSize;
uint32 numStreams;
UDPSClientListener *listeners[UDPS_TCP_MAX_STREAMS];
TCPSClientStream *facades[UDPS_TCP_MAX_STREAMS];
FastPollingMutexSem refMutex;
uint32 startRefs;
SingleThreadService threadService;
int fd;
volatile bool connected;
volatile bool stopping;
uint64 lastDataTicks;
uint64 disconnectTick;
volatile uint32 connectionCount;
volatile uint32 protocolErrors;
uint8 *rxBuf; ///< header + payload of the frame being received
uint32 rxHave; ///< bytes of the current frame received so far
uint32 rxWant; ///< total bytes the current frame needs (header first)
bool rxHeaderOk; ///< header validated; rxWant covers the payload
uint32 lastDataCounter[UDPS_TCP_MAX_STREAMS]; ///< counter of the last delivered DATA frame
bool lastDataCounterValid[UDPS_TCP_MAX_STREAMS]; ///< false until the first DATA frame of a connection
uint32 lastDataGap[UDPS_TCP_MAX_STREAMS]; ///< frames missing before the one being delivered
};
} // namespace MARTe
#endif // TCPS_CLIENT_H_