#ifndef TCPS_SERVICE_STREAM_H_ #define TCPS_SERVICE_STREAM_H_ /** * @file TCPSServiceStream.h * @brief UDPSServerI handle that publishes one named stream through a * TCPStreamService instead of through a private listening socket. * * It is what a TCPStreamer uses when its `Service` key is set. All socket work * (accept, handshake, flush, eviction) is done by the service's threads; the * producer thread only builds the frame and copies it into the bounded queue of * every subscribed connection (SendData never blocks on a socket). */ #include "FastPollingMutexSem.h" #include "ReferenceT.h" #include "StreamString.h" #include "MutexSem.h" #include "TCPSStreamTapI.h" #include "TCPStreamService.h" #include "UDPSProtocol.h" #include "UDPSTransportI.h" namespace MARTe { struct TCPSConn; class TCPSServiceStream : public UDPSServerI { public: /** Default maximum payload bytes per frame (matches TCPSServer). */ static const uint32 TCPSS_DEFAULT_MAX_PAYLOAD = 60000u; TCPSServiceStream(const char8 *serviceObjectName, const char8 *streamName); virtual ~TCPSServiceStream(); /** Keys: MaxPayloadSize (uint32, 1..1 MiB, default 60000). */ virtual bool Initialise(StructuredDataI &data); /** Resolve the service by name and register the stream (fails on a duplicate name). */ virtual bool Start(); /** Unregister and drop every subscriber (idempotent). */ virtual bool Stop(); /** No-op: the service threads do the housekeeping. */ virtual void ServiceClients(); virtual bool SendConfig(const uint8 *payload, uint32 payloadSize); virtual bool SendData(uint32 counter, const uint8 *payload, uint32 payloadSize); virtual bool HasClients() const; virtual bool IsMulticast() const; /** @return the service's bound port (0 while the service is unresolved). */ virtual uint16 GetPort() const; const char8 *GetStreamName() const; uint32 GetClientCount() const; /** @return DATA frames discarded for slow subscribers (all time). */ uint64 GetClientDrops() const; uint32 GetMaxPayloadSize() const; /** Most taps one stream can carry. */ static const uint32 TCPSS_MAX_TAPS = 4u; /** * Observe every CONFIG / DATA this stream publishes. The cached CONFIG (if any) is * replayed to the new tap before AddTap returns. Taps run on the producer thread. * @return false if the tap table is full. */ bool AddTap(TCPSStreamTapI *tap); /** Remove a tap; once it returns no callback is running or will start. */ void RemoveTap(TCPSStreamTapI *tap); /* ---- called by TCPStreamService with its topology lock held ---- */ /** Add a connection and prime it with the cached CONFIG. @return false if full. */ bool AttachConn(TCPSConn *c, uint16 streamIdx); void DetachConn(TCPSConn *c); /** Ask every subscriber to close and forget them. */ void DropAllConns(); private: bool BuildFrame(uint8 *&buf, uint32 &cap, uint8 type, uint32 counter, const uint8 *payload, uint32 payloadSize, uint32 &frameBytes); void Distribute(bool isConfig, const uint8 *frame, uint32 frameBytes); StreamString serviceName; mutable StreamString streamName; ReferenceT service; bool registered; uint32 maxPayloadSize; FastPollingMutexSem mutex; ///< guards subs, numSubs, cachedConfig*, drops TCPSConn **subs; uint16 *subIdx; ///< stream index each subscriber knows this stream by uint32 capSubs; volatile uint32 numSubs; uint8 *cachedConfigFrame; uint32 cachedConfigBytes; uint32 cachedConfigCap; uint32 configCounter; uint64 drops; FastPollingMutexSem tapMutex; ///< held while a tap callback runs TCPSStreamTapI *taps[TCPSS_MAX_TAPS]; volatile uint32 numTaps; uint8 *dataFrame; ///< producer-thread scratch uint32 dataFrameCap; TCPSServiceStream(const TCPSServiceStream &); TCPSServiceStream &operator=(const TCPSServiceStream &); }; } // namespace MARTe #endif // TCPS_SERVICE_STREAM_H_