Implemented history writer
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @file HistoryWriter.h
|
||||
* @brief Disk-backed circular history storage for StreamHub signals.
|
||||
*
|
||||
* Each signal is stored in a separate binary file (.shist) with a fixed header
|
||||
* and a pre-allocated circular data region of (float64 time, float64 value) pairs.
|
||||
* The file size is determined at creation and never grows.
|
||||
*
|
||||
* File layout (per signal):
|
||||
* Offset Size Field
|
||||
* 0 4 Magic: "SHR1"
|
||||
* 4 4 uint32 version (1)
|
||||
* 8 4 uint32 capacity (max pairs)
|
||||
* 12 4 uint32 head (next write position, 0-based, wraps)
|
||||
* 16 4 uint32 count (valid entries, <= capacity)
|
||||
* 20 4 uint32 decimation
|
||||
* 24 8 float64 tOldest
|
||||
* 32 8 float64 tNewest
|
||||
* 40 24 reserved (pad to 64 bytes)
|
||||
* 64 ... data: capacity × 16 bytes (8 time + 8 value)
|
||||
*
|
||||
* Thread safety: WriteTick() is called from the push loop only.
|
||||
* ReadRange() uses pread() and is safe for concurrent reads from WS threads.
|
||||
*/
|
||||
|
||||
#ifndef STREAMHUB_HISTORY_WRITER_H_
|
||||
#define STREAMHUB_HISTORY_WRITER_H_
|
||||
|
||||
#include "CompilerTypes.h"
|
||||
#include "StreamString.h"
|
||||
#include "StructuredDataI.h"
|
||||
#include "UDPSourceSession.h"
|
||||
|
||||
namespace StreamHub {
|
||||
|
||||
using MARTe::uint8;
|
||||
using MARTe::uint16;
|
||||
using MARTe::uint32;
|
||||
using MARTe::uint64;
|
||||
using MARTe::float64;
|
||||
using MARTe::StreamString;
|
||||
using MARTe::StructuredDataI;
|
||||
|
||||
/** Maximum number of sessions (matches kMaxSessions). */
|
||||
static const uint32 kHistMaxSessions = 32u;
|
||||
|
||||
/** Header size in bytes. */
|
||||
static const uint32 kHistHeaderSize = 64u;
|
||||
|
||||
/** Magic bytes. */
|
||||
static const char kHistMagic[4] = {'S','H','R','1'};
|
||||
|
||||
class HistoryWriter {
|
||||
public:
|
||||
|
||||
HistoryWriter();
|
||||
~HistoryWriter();
|
||||
|
||||
/**
|
||||
* @brief Parse +History block from StreamHub config.
|
||||
*
|
||||
* Expected keys inside a +History node:
|
||||
* Directory (string, required)
|
||||
* DurationHours (float64, default 1)
|
||||
* Decimation (uint32, default 1)
|
||||
* FlushIntervalSec (uint32, default 5)
|
||||
* MinDiskFreeMB (uint32, default 500)
|
||||
*/
|
||||
bool Initialise(StructuredDataI &cfg);
|
||||
|
||||
/** @return true if history storage is enabled and initialised. */
|
||||
bool IsEnabled() const { return enabled_; }
|
||||
|
||||
/**
|
||||
* @brief Called when a source becomes configured.
|
||||
* Creates / reopens per-signal .shist files.
|
||||
*/
|
||||
void OnSourceConfigured(uint32 sessionIdx, const char *sourceId,
|
||||
UDPSourceSession &sess);
|
||||
|
||||
/**
|
||||
* @brief Called from the push loop to write new samples for one session.
|
||||
* Uses per-signal ReadSince cursors. Applies decimation.
|
||||
*/
|
||||
void WriteTick(uint32 sessionIdx, UDPSourceSession &sess);
|
||||
|
||||
/**
|
||||
* @brief Flush in-memory header state to disk (periodic).
|
||||
*/
|
||||
void FlushHeaders();
|
||||
|
||||
/**
|
||||
* @brief Read a time range for one signal from disk.
|
||||
* @return Number of (t,v) pairs written to tOut/vOut.
|
||||
*/
|
||||
uint32 ReadRange(const char *sourceId, const char *signalName,
|
||||
float64 t0, float64 t1,
|
||||
float64 *tOut, float64 *vOut, uint32 maxOut) const;
|
||||
|
||||
/**
|
||||
* @brief Get the time range stored on disk for a signal.
|
||||
* @return true if the signal has history data.
|
||||
*/
|
||||
bool GetTimeRange(const char *sourceId, const char *signalName,
|
||||
float64 &t0, float64 &t1) const;
|
||||
|
||||
/**
|
||||
* @brief Build a JSON fragment for historyInfo response.
|
||||
* Writes into buf at offset, growing via the provided append function.
|
||||
*/
|
||||
void AppendInfoJSON(char *&buf, uint32 &off, uint32 &cap) const;
|
||||
|
||||
/** @brief Check available disk space. */
|
||||
bool HasSufficientDisk() const;
|
||||
|
||||
/** @brief Duration in hours. */
|
||||
float64 DurationHours() const { return durationHours_; }
|
||||
|
||||
/** @brief Decimation factor. */
|
||||
uint32 Decimation() const { return decimation_; }
|
||||
|
||||
private:
|
||||
|
||||
struct SignalFile {
|
||||
int fd; /**< File descriptor (-1 = not open) */
|
||||
uint32 capacity; /**< Total capacity in pairs */
|
||||
uint32 head; /**< Next write position */
|
||||
uint32 count; /**< Valid entries */
|
||||
float64 tOldest;
|
||||
float64 tNewest;
|
||||
uint64 readCursor; /**< For ReadSince from ring buffer */
|
||||
uint64 decimCounter; /**< Decimation sample counter */
|
||||
bool headerDirty; /**< True if header needs flushing */
|
||||
char sourceId[64]; /**< Source ID for lookup */
|
||||
char signalName[64]; /**< Signal name for lookup */
|
||||
};
|
||||
|
||||
/** Per-signal scratch for reading from rings. */
|
||||
static const uint32 kReadScratch = 65536u;
|
||||
float64 *scratchT_;
|
||||
float64 *scratchV_;
|
||||
|
||||
/** Maximum signals tracked per session for history. */
|
||||
static const uint32 kHistMaxSignals = 64u;
|
||||
|
||||
/** Heap-allocated to keep StreamHub object size manageable. */
|
||||
SignalFile (*files_)[kHistMaxSignals]; /**< [kHistMaxSessions][kHistMaxSignals] */
|
||||
bool (*fileActive_)[kHistMaxSignals]; /**< [kHistMaxSessions][kHistMaxSignals] */
|
||||
|
||||
bool enabled_;
|
||||
char directory_[512];
|
||||
float64 durationHours_;
|
||||
uint32 decimation_;
|
||||
uint32 flushIntervalSec_;
|
||||
uint32 minDiskFreeMB_;
|
||||
bool diskLow_; /**< True when disk space is below threshold */
|
||||
|
||||
/** Create or reopen a .shist file for one signal. */
|
||||
bool OpenSignalFile(uint32 sessionIdx, uint32 sigIdx,
|
||||
const char *sourceId, const char *signalName,
|
||||
float64 estimatedRateHz);
|
||||
|
||||
/** Write a batch of (t,v) pairs to a signal file. */
|
||||
void WritePairs(SignalFile &sf, const float64 *t, const float64 *v,
|
||||
uint32 n);
|
||||
|
||||
/** Flush one signal file header to disk. */
|
||||
void FlushHeader(SignalFile &sf);
|
||||
|
||||
/** Find a signal file by sourceId:signalName. */
|
||||
const SignalFile* FindFile(const char *sourceId, const char *signalName) const;
|
||||
};
|
||||
|
||||
} /* namespace StreamHub */
|
||||
|
||||
#endif /* STREAMHUB_HISTORY_WRITER_H_ */
|
||||
Reference in New Issue
Block a user