using GeoVLog.Core.Models; using PureHDF; using System; using System.Collections.Generic; namespace GeoVLog.Core.Hdf5; /// /// Manages writing sensor data to an HDF5 log file. /// Provides registration of sensors and thread-safe appending of records to the file. /// public sealed class H5LogFileWriter : IDisposable { private readonly H5File _file; private H5NativeWriter _writer; // Use H5NativeWriter instead of H5FileWriter (PureHDF 2.1.1) private readonly Dictionary _map = new(); private readonly string _filePath; private int _unflushedCount; /// /// Number of records written since the last call. /// public int UnflushedCount => _unflushedCount; /// /// Initializes a new instance for writing log data to a given HDF5 file path. /// Opens/creates the file structure in memory and prepares it for writing to disk. /// /// The file path where the HDF5 log will be stored (will be created or overwritten). public H5LogFileWriter(string path) { // Create a new HDF5 file structure in memory (root group) _file = new H5File(); // Begin a write operation to the specified file on disk. // In PureHDF 2.1.1, BeginWrite returns an H5NativeWriter instead of H5FileWriter:contentReference[oaicite:6]{index=6}. _writer = _file.BeginWrite(path); _filePath = path; } /// /// Registers a sensor data stream with this log file, creating the corresponding dataset. /// /// The unique identifier of the sensor (from the domain model). /// The HDF5 dataset path (e.g., "/Sensors/GPS") where this sensor's data will be stored. /// If true, parsed data will be generated for each record and provided via the callback. /// The schema/type of the sensor, indicating how to parse its data. /// Optional callback invoked with the parsed record (and sensor ID) each time a new message is written (if parsing is enabled). public void RegisterSensor( SensorId id, string hdf5Path, bool enableParse, SensorSchema schema, Action? cb) { // Create or open the sensor's dataset (variable-length string dataset) and map a writer for it _map[id] = new H5SensorWriter(_file, _writer, hdf5Path, enableParse, schema, cb); } /// /// Enables or disables parsing for an already registered sensor. /// /// The sensor to update. /// True to parse messages; false to disable parsing. internal void SetParsingEnabled(SensorId id, bool enable) { if (_map.TryGetValue(id, out var writer)) { writer.SetParsingEnabled(enable); } } /// /// Writes a single sensor data record to the log file for the specified sensor. /// /// The sensor identifier (must be registered via ). /// The timestamp of the record (UTC). /// The raw sensor message bytes (in UTF-8 encoding). /// /// Appends a new entry to the sensor's dataset. The entry includes the timestamp and message text. /// This method is thread-safe when called on different sensors concurrently because each sensor writer uses internal locking. /// Note: PureHDF's internal writing may serialize writes to a single file:contentReference[oaicite:7]{index=7}. /// public void Write(SensorId id, DateTime utc, ReadOnlySpan msg) { // Delegate the write to the respective sensor writer _map[id].Write(id, utc, msg); _unflushedCount++; } /// /// Flushes the in-memory data to disk without closing the log file, allowing logging to continue. /// /// /// This operation finalizes the current write session by disposing the underlying HDF5 writer (ensuring all buffered data is written to disk), /// and then immediately begins a new write session on the same file. All registered sensor datasets remain open, and subsequent records /// will be appended after the data that was flushed. Use this periodically (based on time or record count) to limit data loss in case of a crash. /// public void Flush() { // Dispose the current writer to flush all data to disk and close the file _writer.Dispose(); // Begin a new write operation on the same file to continue logging _writer = _file.BeginWrite(_filePath); // Update all sensor writers to use the new writer instance foreach (var sensorWriter in _map.Values) { sensorWriter.UpdateWriter(_writer); } _unflushedCount = 0; } /// /// Finalizes the HDF5 file writing by flushing data to disk and releasing resources. /// public void Dispose() { // Dispose each sensor writer (currently they manage only in-memory dataset references) foreach (var writer in _map.Values) { writer.Dispose(); } // Flush and write the HDF5 file to disk by disposing the H5NativeWriter. _writer.Dispose(); // Note: Disposing _writer completes the file write operation and closes the file. } }