/** * @file BinaryRecorder.h * @brief Per-source binary recorder writing MARTe2 FileWriter-compatible files. * * @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 * Records incoming signal packets to disk in the MARTe2 FileWriter binary * format, one rotating file per source. The capture path (CapturePacket, * Configure) runs on the session receive thread; all file I/O (FlushTick) * runs on the 30 Hz push thread. Only the staging double-buffer, the header * blob and a handful of control flags cross threads, guarded by a * FastPollingMutexSem. * * On-disk format (byte-identical to FileWriter for un-quantized signals): * [uint32 numSigs] * per signal: [uint16 TypeDescriptor.all][char[32] name][uint32 numElements] * rows: each row = all included signals concatenated, native little-endian. */ #ifndef STREAMHUB_BINARYRECORDER_H_ #define STREAMHUB_BINARYRECORDER_H_ #include #include "CompilerTypes.h" #include "TypeDescriptor.h" #include "FastPollingMutexSem.h" #include "UDPSProtocol.h" namespace StreamHub { using MARTe::uint8; using MARTe::uint16; using MARTe::uint32; using MARTe::uint64; using MARTe::float64; /** Maximum number of signals a recorder can include. */ static const uint32 kRecMaxSig = 256u; /** Configuration for a BinaryRecorder, parsed from the +Recorder cfg block. */ struct RecorderConfig { bool enabled; ///< Master enable bool autoStart; ///< Arm at startup char directory[512]; ///< Output directory uint32 maxFileBytes; ///< Roll threshold (MaxFileMB * 1024 * 1024) uint32 keepFiles; ///< Newest N kept per source uint32 stagingBytes; ///< Per-buffer hard cap (StagingMB * 1024 * 1024) uint32 flushIntervalSec; ///< fdatasync cadence uint32 minDiskFreeMB; ///< Stop-writing guard char signals[1024]; ///< "all" or comma-separated "src:sig" keys }; class BinaryRecorder { public: BinaryRecorder(); ~BinaryRecorder(); /** One-time setup: copy config, source id, create directory. */ void Init(const RecorderConfig &cfg, const char *sourceId); /** * Receive thread: (re)build the FileWriter header and per-signal row * layout for the included signals. Triggers a file reopen on the push * thread so the new layout takes effect with a fresh header. */ void Configure(const MARTe::UDPSSignalDescriptor *descs, uint32 nSigs, const bool *includeMask); /** Receive thread: append one packet's rows to the staging buffer. */ void CapturePacket(const uint8 *payload, const uint32 *sigOff, const uint32 *sigElems, uint8 publishMode, uint32 numSamples); /** Push thread: open pending file, swap+flush staging, rotate, fsync. */ void FlushTick(uint32 nowSec); void RequestArm(); ///< Any thread: arm recording. void RequestDisarm(); ///< Any thread: disarm, flush, close. bool IsEnabled() const; ///< True when the master enable is set. /** recInfo snapshot (push thread). */ void GetInfo(bool &recording, char *file, uint32 fileSz, uint64 &bytesWritten, uint64 &rowsWritten, uint64 &droppedRows, uint64 &freeMB) const; /** Map a UDPS type code to a MARTe2 TypeDescriptor.all (uint16). */ static uint16 TypeCodeToDescriptorAll(uint8 udpsTypeCode); /** Encode a physical value into native little-endian bytes; returns size. */ static uint32 EncodeNative(uint8 typeCode, float64 v, uint8 *dst); private: void OpenNewFile(); void CloseFile(); void PruneOldFiles(); bool HasSufficientDisk(); /* Configuration */ RecorderConfig config_; char sourceId_[128]; /* Layout (built on receive thread by Configure) */ uint32 srcIdx_[kRecMaxSig]; ///< Source signal index of included signal uint8 typeCode_[kRecMaxSig]; ///< UDPS type code uint8 quantType_[kRecMaxSig]; ///< UDPS quant code uint32 declaredElems_[kRecMaxSig];///< Element count per signal uint32 nativeBytes_[kRecMaxSig]; ///< Native bytes per element uint32 wireBytes_[kRecMaxSig]; ///< Wire bytes per element float64 rangeMin_[kRecMaxSig]; float64 rangeMax_[kRecMaxSig]; uint32 nIncluded_; uint32 rowBytes_; /* Header blob (receive thread writes, push thread snapshots) */ uint8 headerBlob_[4u + kRecMaxSig * 38u]; uint32 headerLen_; bool layoutReady_; /* Push-owned header snapshot (used for file I/O without holding the lock) */ uint8 pushHeader_[4u + kRecMaxSig * 38u]; uint32 pushHeaderLen_; uint32 pushRowBytes_; /* Staging double buffer */ uint8 *front_; uint8 *back_; uint32 frontLen_; uint32 backLen_; mutable MARTe::FastPollingMutexSem stagingMutex_; /* Control flags (guarded by stagingMutex_) */ bool pendingArm_; bool pendingDisarm_; bool pendingReopen_; /* Push-thread file state */ int fd_; uint64 fileOffset_; uint32 lastSyncSec_; uint32 fileSeq_; char curFilePath_[768]; /* State + counters */ bool armed_; bool diskLow_; uint64 bytesWritten_; uint64 rowsWritten_; uint64 droppedRows_; }; } /* namespace StreamHub */ #endif /* STREAMHUB_BINARYRECORDER_H_ */