/** * @file UDPStreamer.h * @brief Header file for class UDPStreamer * @date 13/05/2026 * @author Martino Ferrari * * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and * the Development of Fusion Energy ('Fusion for Energy'). * Licensed under the EUPL, Version 1.1 or - as soon they will be approved * by the European Commission - subsequent versions of the EUPL (the "Licence") * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl * * @warning Unless required by applicable law or agreed to in writing, * software distributed under the Licence is distributed on an "AS IS" * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence permissions and limitations under the Licence. * * @details This header file contains the declaration of the class UDPStreamer * with all of its public, protected and private members. It may also include * definitions for inline methods which need to be visible to the compiler. */ #ifndef UDPSTREAMER_H_ #define UDPSTREAMER_H_ /*---------------------------------------------------------------------------*/ /* Standard header includes */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* Project header includes */ /*---------------------------------------------------------------------------*/ #include "BasicUDPSocket.h" #include "CompilerTypes.h" #include "EmbeddedServiceMethodBinderI.h" #include "EventSem.h" #include "FastPollingMutexSem.h" #include "MemoryDataSourceI.h" #include "SingleThreadService.h" #include "StreamString.h" /*---------------------------------------------------------------------------*/ /* Class declaration */ /*---------------------------------------------------------------------------*/ namespace MARTe { /** * @brief Quantization types for float signals. */ typedef enum { UDPStreamerQuantNone = 0u, /**< No quantization; raw wire format */ UDPStreamerQuantUint8 = 1u, /**< Quantize to uint8 [0, 255] */ UDPStreamerQuantInt8 = 2u, /**< Quantize to int8 [-127, 127] */ UDPStreamerQuantUint16 = 3u, /**< Quantize to uint16 [0, 65535] */ UDPStreamerQuantInt16 = 4u /**< Quantize to int16 [-32767, 32767] */ } UDPStreamerQuantType; /** * @brief Time reference modes for signals. */ typedef enum { UDPStreamerTimePacket = 0u, /**< Use the packet-level timestamp (HRT at Synchronise time) */ UDPStreamerTimeFullArray = 1u, /**< Time signal has same NumberOfElements; one timestamp per element */ UDPStreamerTimeFirstSample = 2u, /**< Time signal is scalar = timestamp of first element */ UDPStreamerTimeLastSample = 3u /**< Time signal is scalar = timestamp of last element */ } UDPStreamerTimeMode; /** * @brief Per-signal metadata used at runtime for serialization. */ struct UDPStreamerSignalInfo { StreamString name; /**< Signal name */ TypeDescriptor type; /**< MARTe2 type descriptor */ UDPStreamerQuantType quantType; /**< Quantization type (none = raw copy) */ uint8 numDimensions; /**< Number of dimensions (0=scalar, 1=array, 2=matrix) */ uint32 numElements; /**< Total number of elements (rows * cols) */ uint32 numRows; /**< Number of rows */ uint32 numCols; /**< Number of columns */ float64 rangeMin; /**< Min of physical range (for quantization) */ float64 rangeMax; /**< Max of physical range (for quantization) */ UDPStreamerTimeMode timeMode; /**< How time is encoded for this signal */ float64 samplingRate; /**< Hz, used for First/LastSample modes */ uint32 timeSignalIdx; /**< Index of the time-reference signal; 0xFFFFFFFFu = PacketTime */ StreamString unit; /**< Physical unit string */ uint32 srcByteSize; /**< Bytes in MARTe2 memory */ uint32 wireByteSize; /**< Bytes on the wire (may differ when quantized) */ uint32 bufferOffset; /**< Byte offset in the flat MemoryDataSourceI memory buffer */ }; /** * @brief Magic number for UDPStreamer packets: 'UDPS' in little-endian. */ static const uint32 UDPS_MAGIC = 0x53504455u; /** * @brief Packet type codes. */ static const uint8 UDPS_TYPE_DATA = 0u; /**< Server → Client: signal data */ static const uint8 UDPS_TYPE_CONFIG = 1u; /**< Server → Client: signal configuration */ static const uint8 UDPS_TYPE_ACK = 2u; /**< Client → Server: acknowledge counter */ static const uint8 UDPS_TYPE_CONNECT = 3u; /**< Client → Server: connect request */ static const uint8 UDPS_TYPE_DISCONNECT = 4u; /**< Client → Server: disconnect */ /** * @brief Wire packet header (17 bytes, packed). */ struct UDPSPacketHeader { uint32 magic; /**< Must equal UDPS_MAGIC */ uint8 type; /**< One of UDPS_TYPE_* */ uint32 counter; /**< Sequence counter (per data update, not per fragment) */ uint16 fragmentIdx; /**< 0-based index of this fragment */ uint16 totalFragments; /**< Total fragments for this update (1 = no fragmentation) */ uint32 payloadBytes; /**< Bytes of payload immediately following this header */ } __attribute__((packed)); /** * @brief Signal type codes transmitted in the CONFIG packet. * These are simplified type codes independent of MARTe2 internals. */ static const uint8 UDPS_TYPECODE_UINT8 = 0u; static const uint8 UDPS_TYPECODE_INT8 = 1u; static const uint8 UDPS_TYPECODE_UINT16 = 2u; static const uint8 UDPS_TYPECODE_INT16 = 3u; static const uint8 UDPS_TYPECODE_UINT32 = 4u; static const uint8 UDPS_TYPECODE_INT32 = 5u; static const uint8 UDPS_TYPECODE_UINT64 = 6u; static const uint8 UDPS_TYPECODE_INT64 = 7u; static const uint8 UDPS_TYPECODE_FLOAT32 = 8u; static const uint8 UDPS_TYPECODE_FLOAT64 = 9u; static const uint8 UDPS_TYPECODE_UNKNOWN = 255u; /** * @brief A DataSource that streams MARTe2 signals to a single UDP client. * * @details This output DataSource accepts signals from GAMs and forwards them * asynchronously to a connected UDP client. A dedicated background thread handles * all network I/O so that the real-time thread is only blocked for a fast spinlock * and a memcpy during Synchronise(). * * Protocol: * - Client sends CONNECT → server replies with CONFIG describing all signals. * - Server sends DATA packets (fragmented if needed) on every RT cycle. * - Client sends ACK packets (optional, for monitoring loss). * - Client sends DISCONNECT to terminate the session. * * Signal configuration syntax: *
 * +UDPStreamer1 = {
 *     Class = UDPStreamer
 *     Port = 44500              // Optional (default 44500)
 *     MaxPayloadSize = 1400     // Optional (default 1400); max payload bytes per UDP datagram
 *     CPUMask = 0x2             // Optional, affinity for background thread
 *     StackSize = 1048576       // Optional, stack size for background thread
 *     Signals = {
 *         Time = {
 *             Type = uint64
 *         }
 *         Pressure = {
 *             Type = float32
 *             NumberOfDimensions = 1
 *             NumberOfElements = 100
 *             Unit = "Pa"             // Optional
 *             RangeMin = 0.0          // Optional (required for quantization)
 *             RangeMax = 1000000.0    // Optional (required for quantization)
 *             QuantizedType = uint16  // Optional: none|uint8|int8|uint16|int16
 *             TimeMode = LastSample   // Optional: PacketTime|FullArray|FirstSample|LastSample
 *             TimeSignal = Time       // Required when TimeMode != PacketTime
 *             SamplingRate = 10000.0  // Required when TimeMode = FirstSample or LastSample
 *         }
 *         Temperature = {
 *             Type = float64
 *             Unit = "K"
 *             TimeMode = PacketTime
 *         }
 *     }
 * }
 * 
* * Notes: * - QuantizedType is only valid for float32/float64 signals. * - TimeMode = PacketTime uses the HRT counter captured in Synchronise(). * - TimeMode = FullArray requires TimeSignal to have the same NumberOfElements. * - TimeMode = FirstSample/LastSample requires a scalar TimeSignal and SamplingRate. */ class UDPStreamer : public MemoryDataSourceI, public EmbeddedServiceMethodBinderI { public: CLASS_REGISTER_DECLARATION() /** * @brief Constructor. Initialises all members to safe defaults. */ UDPStreamer(); /** * @brief Destructor. Stops the background thread and frees allocated memory. */ virtual ~UDPStreamer(); /** * @brief Parses top-level configuration parameters (Port, MaxPayloadSize, CPUMask, StackSize). * @return true if all mandatory parameters are valid. */ virtual bool Initialise(StructuredDataI &data); /** * @brief Validates signal types and builds the per-signal metadata array. * @details Reads per-signal custom fields (Unit, RangeMin, RangeMax, QuantizedType, * TimeMode, TimeSignal, SamplingRate) from signalsDatabase and populates signalInfos[]. * @return true if all signal configurations are valid. */ virtual bool SetConfiguredDatabase(StructuredDataI &data); /** * @brief Allocates signal memory (via parent) plus readyBuffer, scratchBuffer, wireBuffer. * @return true if all memory allocations succeed. */ virtual bool AllocateMemory(); /** * @brief Returns the broker name for the given signal direction. * @return "MemoryMapSynchronisedOutputBroker" for OutputSignals; "" otherwise. */ virtual const char8 *GetBrokerName(StructuredDataI &data, const SignalDirection direction); /** * @brief Called by the RT thread after all GAMs have written output signals. * @details Copies signal memory to readyBuffer under a fast spinlock, then posts * the dataSem to wake the background thread. Returns immediately. * @return true always. */ virtual bool Synchronise(); /** * @brief Opens the server socket and starts the background thread. * @return true if the socket and thread start successfully. */ virtual bool PrepareNextState(const char8 *const currentStateName, const char8 *const nextStateName); /** * @brief Background thread entry point. * @details Handles client CONNECT/DISCONNECT/ACK and sends DATA packets. */ virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info); /** * @brief Returns the listening port number. */ uint16 GetPort() const; /** * @brief Returns the maximum payload size per UDP datagram. */ uint32 GetMaxPayloadSize() const; /** * @brief Returns true if a client is currently connected. */ bool IsClientConnected() const; private: /** * @brief Serializes the CONFIG payload into buf and sets payloadSize. * @return true if serialization succeeds. */ bool BuildConfigPayload(uint8 *buf, uint32 bufSize, uint32 &payloadSize); /** * @brief Quantizes and serializes all signals from srcBuf into wireBuffer. * @param[in] srcBuf Flat source buffer (signal data in MARTe2 format). * @param[in] timestamp HRT counter to embed as packet timestamp. */ void QuantizeAndSerialize(const uint8 *srcBuf, uint64 timestamp); /** * @brief Sends payload as one or more fragmented UDP datagrams to the connected client. * @return true if all datagrams were sent without error. */ bool SendFragmented(uint8 type, uint32 counter, const uint8 *payload, uint32 payloadSize); /** * @brief Handles a single received command packet from a client. */ void HandleClientCommand(const uint8 *buf, uint32 size); /** * @brief Maps a MARTe2 TypeDescriptor to the UDPS_TYPECODE_* constants. */ static uint8 TypeDescriptorToCode(TypeDescriptor td); /* Configuration parameters */ uint16 port; /**< UDP server port */ uint32 maxPayloadSize; /**< Max payload bytes per UDP packet (excluding header) */ uint32 cpuMask; /**< Background thread CPU affinity */ uint32 stackSize; /**< Background thread stack size */ /* Signal metadata */ uint32 numSigs; /**< Number of signals */ UDPStreamerSignalInfo *signalInfos; /**< Per-signal metadata array */ /* Double-buffering for RT safety */ uint8 *readyBuffer; /**< Protected copy of signal memory for background thread */ uint8 *scratchBuffer; /**< Local copy used during serialization (avoids holding lock) */ uint32 totalSrcBytes; /**< Total bytes of signal data (= stateMemorySize) */ FastPollingMutexSem bufMutex; /**< Protects readyBuffer against concurrent access */ EventSem dataSem; /**< Wakes background thread when new data is ready */ volatile uint64 syncTimestamp; /**< HRT counter captured in Synchronise() */ /* Wire serialization buffer */ 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 */ volatile bool clientConnected; /**< True when a client has successfully connected */ /* Thread management */ SingleThreadService executor; /**< Background thread service */ /* Packet sequencing */ uint32 packetCounter; /**< Incremented for each DATA update sent */ }; } /* namespace MARTe */ #endif /* UDPSTREAMER_H_ */