From d457057a6a10cd6ef507ad0754cfdf896dca494d Mon Sep 17 00:00:00 2001 From: Shariff Faleel Date: Fri, 23 May 2025 17:31:29 -0700 Subject: [PATCH 1/4] support configurable output data writers in recorder Refactored the recorder infrastructure to support configurable output data writers. The IDataWriter interface is now generic on a new IDataWriterInfo, allowing data writers to receive configuration via info objects (FileDataWriterInfo, NetworkDataWriterInfo) when instantiated. StartRecordingInternal and StartRecording accept an array of IDataWriterInfo to flexibly control recording destinations and configurations at runtime. This enables future extensibility, such as adding new writer types or enabling user selection of outputs. Default behavior remains writing to a file when no outputs are specified. Added info classes for file and network writers to contain configuration like file paths and network endpoints. This also makes GenerateFilePath public to allow setting file names externaly. --- Runtime/Core/Recorder/Recorder.cs | 29 ++++++++++-- Runtime/Core/Recorder/RecorderExtensions.cs | 6 +-- .../Core/Recorder/Writer/DataDispatcher.cs | 12 ++--- .../Core/Recorder/Writer/FileDataWriter.cs | 47 ++++++++++++++----- Runtime/Core/Recorder/Writer/IDataWriter.cs | 9 +++- .../Core/Recorder/Writer/NetworkDataWriter.cs | 27 +++++++++-- 6 files changed, 98 insertions(+), 32 deletions(-) diff --git a/Runtime/Core/Recorder/Recorder.cs b/Runtime/Core/Recorder/Recorder.cs index d955e48..88fdcb2 100644 --- a/Runtime/Core/Recorder/Recorder.cs +++ b/Runtime/Core/Recorder/Recorder.cs @@ -49,7 +49,7 @@ private PlumeRecorder(DataDispatcher dataDispatcher, RecorderContext ctx) /// Starts the recording process. If the recorder is already recording, throw a exception. /// /// - private void StartRecordingInternal(string name, string extraMetadata = "") + private void StartRecordingInternal(string name, string extraMetadata = "", IDataWriterInfo[] outputInfos = null) { if (_context.Status is RecorderStatus.Stopping) throw new InvalidOperationException( @@ -74,7 +74,29 @@ private void StartRecordingInternal(string name, string extraMetadata = "") RecordApplicationGlobalSettings(record); RecordApplicationCurrentSettings(record); - _dataDispatcher.Start(_context.CurrentRecord); + IDataWriter[] outputs; + + if (outputInfos == null) + { + IDataWriter fileDataWriter = (IDataWriter) new FileDataWriter(record); + outputs = new IDataWriter[] { fileDataWriter }; + } + else + { + List> outputsList = new List>(); + foreach(IDataWriterInfo info in outputInfos) + { + outputsList.Add(info switch + { + FileDataWriterInfo => (IDataWriter)new FileDataWriter(record, (FileDataWriterInfo)info), + NetworkDataWriterInfo => (IDataWriter)new NetworkDataWriter(record, (NetworkDataWriterInfo)info), + _ => throw new InvalidOperationException() + }); + } + outputs = outputsList.ToArray(); + } + + _dataDispatcher.Start(_context.CurrentRecord, outputs); // ReSharper disable once ForCanBeConvertedToForeach for (var i = 0; i < _context.Modules.Count; i++) @@ -272,6 +294,7 @@ private void Awake() if (recorderSettings.StartOnPlay) { + // TODO: allow configuring data writers in Settings StartRecordingInternal(recorderSettings.DefaultRecordPrefix, recorderSettings.DefaultRecordExtraMetadata); } } @@ -293,4 +316,4 @@ public void Dispose() module.Destroy(_context); } } -} \ No newline at end of file +} diff --git a/Runtime/Core/Recorder/RecorderExtensions.cs b/Runtime/Core/Recorder/RecorderExtensions.cs index 389a035..16f1248 100644 --- a/Runtime/Core/Recorder/RecorderExtensions.cs +++ b/Runtime/Core/Recorder/RecorderExtensions.cs @@ -29,10 +29,10 @@ private static void CheckInstantiated() throw new InvalidOperationException("PLUME recorder instance is not created yet."); } - public static void StartRecording(string name, string extraMetadata = "") + public static void StartRecording(string name, string extraMetadata = "", IDataWriterInfo[] dataWriterInfos = null) { CheckInstantiated(); - Instance.StartRecordingInternal(name, extraMetadata); + Instance.StartRecordingInternal(name, extraMetadata, dataWriterInfos); } public static async UniTask StopRecording() @@ -127,4 +127,4 @@ internal static void OnSceneLoaded() Instance.Awake(); } } -} \ No newline at end of file +} diff --git a/Runtime/Core/Recorder/Writer/DataDispatcher.cs b/Runtime/Core/Recorder/Writer/DataDispatcher.cs index 1649981..8487cc4 100644 --- a/Runtime/Core/Recorder/Writer/DataDispatcher.cs +++ b/Runtime/Core/Recorder/Writer/DataDispatcher.cs @@ -11,16 +11,12 @@ public class DataDispatcher private bool _running; private Thread _dispatcherThread; - private IDataWriter[] _outputs; + private IDataWriter[] _outputs; private NetworkStream _networkStream; - internal void Start(Record record) + internal void Start(Record record, IDataWriter[] outputs) { - var fileDataWriter = new FileDataWriter(record); - _outputs = new IDataWriter[] { fileDataWriter }; - - // var networkDataWriter = new NetworkDataWriter(recordIdentifier); - // _outputs = new IDataWriter[] { networkDataWriter }; + _outputs = outputs; _dispatcherThread = new Thread(() => DispatchLoop(record)) { @@ -150,4 +146,4 @@ public void OnApplicationPaused() } } } -} \ No newline at end of file +} diff --git a/Runtime/Core/Recorder/Writer/FileDataWriter.cs b/Runtime/Core/Recorder/Writer/FileDataWriter.cs index 5be3694..479dcff 100644 --- a/Runtime/Core/Recorder/Writer/FileDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/FileDataWriter.cs @@ -7,13 +7,14 @@ using K4os.Compression.LZ4.Streams; using PLUME.Sample; using UnityEngine; +using UnityEngine.Assertions; namespace PLUME.Core.Recorder.Writer { // TODO: add metadata file // TODO: add delayed write // TODO: use memory mapped files - public class FileDataWriter : IDataWriter, IDisposable + public class FileDataWriter : IDataWriter, IDisposable { private readonly Stream _stream; private readonly Stream _metaStream; @@ -21,14 +22,21 @@ public class FileDataWriter : IDataWriter, IDisposable private readonly Sample.RecordMetadata _metadata; private readonly RecordMetrics _metrics; - - public FileDataWriter(Record record) + + public FileDataWriterInfo Info { get; private set; } + + public FileDataWriter(Record record, FileDataWriterInfo fileDataWriterInfo=null) { - var outputDir = Application.persistentDataPath; + if (fileDataWriterInfo == null) + { + var outputDir = Application.persistentDataPath; - GenerateFilePath(outputDir, record.Metadata, out var filePath, out var metaFilePath); - - Logger.Log($"Record will be saved to '{filePath}'."); + GenerateFilePath(outputDir, record.Metadata.Name, out var filePath, out var metaFilePath); + fileDataWriterInfo = new FileDataWriterInfo(filePath:filePath, metaFilePath:metaFilePath); + } + Info = fileDataWriterInfo; + + Logger.Log($"Record will be saved to '{fileDataWriterInfo.FilePath}'."); PinnedMemory.MaxPooledSize = 0; @@ -44,8 +52,8 @@ public FileDataWriter(Record record) LZ4Codec.Enforce32 = false; } - _stream = LZ4Stream.Encode(File.Create(filePath), LZ4Level.L00_FAST); - _metaStream = File.Create(metaFilePath); + _stream = LZ4Stream.Encode(File.Create(fileDataWriterInfo.FilePath), LZ4Level.L00_FAST); + _metaStream = File.Create(fileDataWriterInfo.MetaFilePath); _metaCodedOutputStream = new CodedOutputStream(_metaStream); _metadata = record.Metadata.ToPayload(); @@ -57,13 +65,13 @@ public FileDataWriter(Record record) UpdateMetaFile(); } - private static void GenerateFilePath(string outputDir, RecordMetadata recordMetadata, + public static void GenerateFilePath(string outputDir, string recordMetadataName, out string filePath, out string metadataPath) { var invalidChars = Path.GetInvalidFileNameChars().ToList(); invalidChars.Add(' '); - var name = recordMetadata.Name; + var name = recordMetadataName; var safeName = new string(name.Select(c => invalidChars.Contains(c) ? '_' : c).ToArray()); var formattedDateTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH-mm-sszz"); @@ -133,4 +141,19 @@ public void Dispose() _metaCodedOutputStream.Dispose(); } } -} \ No newline at end of file + + public class FileDataWriterInfo: IDataWriterInfo + { + public string FilePath { get; private set; } + public string MetaFilePath { get; private set; } + + public FileDataWriterInfo(string filePath, string metaFilePath) + { + Assert.IsFalse(string.IsNullOrWhiteSpace(filePath), "filePath is null, empty or whitespace"); + Assert.IsFalse(string.IsNullOrWhiteSpace(metaFilePath), "metaFilePath is null, empty or whitespace"); + + FilePath = filePath; + MetaFilePath = metaFilePath; + } + } +} diff --git a/Runtime/Core/Recorder/Writer/IDataWriter.cs b/Runtime/Core/Recorder/Writer/IDataWriter.cs index 499540c..fab8f73 100644 --- a/Runtime/Core/Recorder/Writer/IDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/IDataWriter.cs @@ -1,7 +1,9 @@ namespace PLUME.Core.Recorder.Writer { - public interface IDataWriter + public interface IDataWriter where TDataWriterInfo:IDataWriterInfo { + public TDataWriterInfo Info { get; } + public void WriteTimelessData(DataChunks dataChunks); public void WriteTimestampedData(DataChunksTimestamped dataChunks); @@ -10,4 +12,7 @@ public interface IDataWriter public void Close(); } -} \ No newline at end of file + + public interface IDataWriterInfo + {} +} diff --git a/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs b/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs index c4785d7..093e0da 100644 --- a/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs @@ -8,14 +8,21 @@ namespace PLUME.Core.Recorder.Writer { - public class NetworkDataWriter : IDataWriter + public class NetworkDataWriter : IDataWriter { private readonly Stream _stream; - public NetworkDataWriter(RecordMetadata recordMetadata) + public NetworkDataWriterInfo Info { get; private set; } + + public NetworkDataWriter(Record record, NetworkDataWriterInfo networkDataWriterInfo = null) { + if (networkDataWriterInfo == null) + { + networkDataWriterInfo = new NetworkDataWriterInfo(ipAddress: "127.0.0.1", port: 8000); + } + Info = networkDataWriterInfo; // Create a tcp server - var server = new TcpListener(IPAddress.Parse("127.0.0.1"), 8000); + var server = new TcpListener(IPAddress.Parse(Info.IpAddress), Info.Port); server.Start(); var stream = server.AcceptTcpClient().GetStream(); @@ -50,4 +57,16 @@ public void Close() _stream.Close(); } } -} \ No newline at end of file + + public class NetworkDataWriterInfo : IDataWriterInfo + { + public string IpAddress { get; } + public int Port { get; } + + public NetworkDataWriterInfo(string ipAddress, int port) + { + IpAddress = ipAddress; + Port = port; + } + } +} From 8c5b8bb99ab732f2a4ef0b5e227331bf5147725d Mon Sep 17 00:00:00 2001 From: Shariff Faleel Date: Thu, 29 May 2025 17:23:50 -0700 Subject: [PATCH 2/4] refactor Recorder to use runtime-configurable IDataWriters Replaces the use of IDataWriterInfo-based writer construction with direct use of initialized IDataWriter instances throughout the Recorder and related classes. FileDataWriter and NetworkDataWriter now store configuration parameters directly and provide an Initialize method for setup at runtime, rather than being constructed via metadata wrapper classes. The Info and Info-based writer classes and interfaces are removed in favor of a simpler, explicit initialization paradigm. --- Runtime/Core/Recorder/Recorder.cs | 28 +++------ Runtime/Core/Recorder/RecorderExtensions.cs | 4 +- .../Core/Recorder/Writer/DataDispatcher.cs | 4 +- .../Core/Recorder/Writer/FileDataWriter.cs | 61 +++++++++---------- Runtime/Core/Recorder/Writer/IDataWriter.cs | 7 +-- .../Core/Recorder/Writer/NetworkDataWriter.cs | 33 ++++------ 6 files changed, 56 insertions(+), 81 deletions(-) diff --git a/Runtime/Core/Recorder/Recorder.cs b/Runtime/Core/Recorder/Recorder.cs index 88fdcb2..62094b7 100644 --- a/Runtime/Core/Recorder/Recorder.cs +++ b/Runtime/Core/Recorder/Recorder.cs @@ -15,6 +15,7 @@ using ProtoBurst; using Unity.Collections; using UnityEngine; +using UnityEngine.Assertions; using UnityEngine.Pool; using static PLUME.Core.Utils.SampleUtils; @@ -49,7 +50,7 @@ private PlumeRecorder(DataDispatcher dataDispatcher, RecorderContext ctx) /// Starts the recording process. If the recorder is already recording, throw a exception. /// /// - private void StartRecordingInternal(string name, string extraMetadata = "", IDataWriterInfo[] outputInfos = null) + private void StartRecordingInternal(string name, string extraMetadata = "", IDataWriter[] outputs = null) { if (_context.Status is RecorderStatus.Stopping) throw new InvalidOperationException( @@ -74,26 +75,17 @@ private void StartRecordingInternal(string name, string extraMetadata = "", IDat RecordApplicationGlobalSettings(record); RecordApplicationCurrentSettings(record); - IDataWriter[] outputs; - - if (outputInfos == null) + if (outputs == null) { - IDataWriter fileDataWriter = (IDataWriter) new FileDataWriter(record); - outputs = new IDataWriter[] { fileDataWriter }; + IDataWriter fileDataWriter = new FileDataWriter(); + outputs = new IDataWriter[] { fileDataWriter }; } - else + + Assert.IsFalse(outputs.Length == 0, "The outputs length is 0"); + + foreach (IDataWriter output in outputs) { - List> outputsList = new List>(); - foreach(IDataWriterInfo info in outputInfos) - { - outputsList.Add(info switch - { - FileDataWriterInfo => (IDataWriter)new FileDataWriter(record, (FileDataWriterInfo)info), - NetworkDataWriterInfo => (IDataWriter)new NetworkDataWriter(record, (NetworkDataWriterInfo)info), - _ => throw new InvalidOperationException() - }); - } - outputs = outputsList.ToArray(); + output.Initialize(record); } _dataDispatcher.Start(_context.CurrentRecord, outputs); diff --git a/Runtime/Core/Recorder/RecorderExtensions.cs b/Runtime/Core/Recorder/RecorderExtensions.cs index 16f1248..06bd256 100644 --- a/Runtime/Core/Recorder/RecorderExtensions.cs +++ b/Runtime/Core/Recorder/RecorderExtensions.cs @@ -29,10 +29,10 @@ private static void CheckInstantiated() throw new InvalidOperationException("PLUME recorder instance is not created yet."); } - public static void StartRecording(string name, string extraMetadata = "", IDataWriterInfo[] dataWriterInfos = null) + public static void StartRecording(string name, string extraMetadata = "", IDataWriter[] writers = null) { CheckInstantiated(); - Instance.StartRecordingInternal(name, extraMetadata, dataWriterInfos); + Instance.StartRecordingInternal(name, extraMetadata, writers); } public static async UniTask StopRecording() diff --git a/Runtime/Core/Recorder/Writer/DataDispatcher.cs b/Runtime/Core/Recorder/Writer/DataDispatcher.cs index 8487cc4..4afc3c7 100644 --- a/Runtime/Core/Recorder/Writer/DataDispatcher.cs +++ b/Runtime/Core/Recorder/Writer/DataDispatcher.cs @@ -11,10 +11,10 @@ public class DataDispatcher private bool _running; private Thread _dispatcherThread; - private IDataWriter[] _outputs; + private IDataWriter[] _outputs; private NetworkStream _networkStream; - internal void Start(Record record, IDataWriter[] outputs) + internal void Start(Record record, IDataWriter[] outputs) { _outputs = outputs; diff --git a/Runtime/Core/Recorder/Writer/FileDataWriter.cs b/Runtime/Core/Recorder/Writer/FileDataWriter.cs index 479dcff..7830569 100644 --- a/Runtime/Core/Recorder/Writer/FileDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/FileDataWriter.cs @@ -7,36 +7,48 @@ using K4os.Compression.LZ4.Streams; using PLUME.Sample; using UnityEngine; -using UnityEngine.Assertions; namespace PLUME.Core.Recorder.Writer { // TODO: add metadata file // TODO: add delayed write // TODO: use memory mapped files - public class FileDataWriter : IDataWriter, IDisposable + public class FileDataWriter : IDataWriter, IDisposable { - private readonly Stream _stream; - private readonly Stream _metaStream; - private readonly CodedOutputStream _metaCodedOutputStream; + private Stream _stream; + private Stream _metaStream; + private CodedOutputStream _metaCodedOutputStream; - private readonly Sample.RecordMetadata _metadata; - private readonly RecordMetrics _metrics; + private Sample.RecordMetadata _metadata; + private RecordMetrics _metrics; - public FileDataWriterInfo Info { get; private set; } + private string filePath; + private string metaFilePath; - public FileDataWriter(Record record, FileDataWriterInfo fileDataWriterInfo=null) + public FileDataWriter() { } + + /// The file path for the plm file. + /// The file path for the meta file. + public FileDataWriter(string filePath, string metaFilePath) + { + this.filePath = filePath; + this.metaFilePath = metaFilePath; + } + + /// + public void Initialize(Record record) { - if (fileDataWriterInfo == null) + if (string.IsNullOrWhiteSpace(filePath) ^ string.IsNullOrWhiteSpace(metaFilePath)) + { + throw new InvalidOperationException($"Only one of filePath and metaFilePath are specifed. Either both need to be specified, or neither should be"); + } + else if (string.IsNullOrWhiteSpace(filePath) && string.IsNullOrWhiteSpace(metaFilePath)) { var outputDir = Application.persistentDataPath; - GenerateFilePath(outputDir, record.Metadata.Name, out var filePath, out var metaFilePath); - fileDataWriterInfo = new FileDataWriterInfo(filePath:filePath, metaFilePath:metaFilePath); + GenerateFilePath(outputDir, record.Metadata.Name, out filePath, out metaFilePath); } - Info = fileDataWriterInfo; - - Logger.Log($"Record will be saved to '{fileDataWriterInfo.FilePath}'."); + Logger.Log($"Record will be saved to '{filePath}'."); PinnedMemory.MaxPooledSize = 0; @@ -52,8 +64,8 @@ public FileDataWriter(Record record, FileDataWriterInfo fileDataWriterInfo=null) LZ4Codec.Enforce32 = false; } - _stream = LZ4Stream.Encode(File.Create(fileDataWriterInfo.FilePath), LZ4Level.L00_FAST); - _metaStream = File.Create(fileDataWriterInfo.MetaFilePath); + _stream = LZ4Stream.Encode(File.Create(filePath), LZ4Level.L00_FAST); + _metaStream = File.Create(metaFilePath); _metaCodedOutputStream = new CodedOutputStream(_metaStream); _metadata = record.Metadata.ToPayload(); @@ -141,19 +153,4 @@ public void Dispose() _metaCodedOutputStream.Dispose(); } } - - public class FileDataWriterInfo: IDataWriterInfo - { - public string FilePath { get; private set; } - public string MetaFilePath { get; private set; } - - public FileDataWriterInfo(string filePath, string metaFilePath) - { - Assert.IsFalse(string.IsNullOrWhiteSpace(filePath), "filePath is null, empty or whitespace"); - Assert.IsFalse(string.IsNullOrWhiteSpace(metaFilePath), "metaFilePath is null, empty or whitespace"); - - FilePath = filePath; - MetaFilePath = metaFilePath; - } - } } diff --git a/Runtime/Core/Recorder/Writer/IDataWriter.cs b/Runtime/Core/Recorder/Writer/IDataWriter.cs index fab8f73..291b0d8 100644 --- a/Runtime/Core/Recorder/Writer/IDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/IDataWriter.cs @@ -1,8 +1,8 @@ namespace PLUME.Core.Recorder.Writer { - public interface IDataWriter where TDataWriterInfo:IDataWriterInfo + public interface IDataWriter { - public TDataWriterInfo Info { get; } + public void Initialize(Record record); public void WriteTimelessData(DataChunks dataChunks); @@ -12,7 +12,4 @@ public interface IDataWriter where TDataWriterInfo:IDataWri public void Close(); } - - public interface IDataWriterInfo - {} } diff --git a/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs b/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs index 093e0da..1cbbfaf 100644 --- a/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs @@ -8,21 +8,22 @@ namespace PLUME.Core.Recorder.Writer { - public class NetworkDataWriter : IDataWriter + public class NetworkDataWriter : IDataWriter { - private readonly Stream _stream; + private Stream _stream; + private string ipAddress; + private int port; - public NetworkDataWriterInfo Info { get; private set; } + public NetworkDataWriter(string ipAddress="127.0.0.1", int port=8000) + { + this.ipAddress = ipAddress; + this.port = port; + } - public NetworkDataWriter(Record record, NetworkDataWriterInfo networkDataWriterInfo = null) + public void Initialize(Record _) { - if (networkDataWriterInfo == null) - { - networkDataWriterInfo = new NetworkDataWriterInfo(ipAddress: "127.0.0.1", port: 8000); - } - Info = networkDataWriterInfo; // Create a tcp server - var server = new TcpListener(IPAddress.Parse(Info.IpAddress), Info.Port); + var server = new TcpListener(IPAddress.Parse(ipAddress), port); server.Start(); var stream = server.AcceptTcpClient().GetStream(); @@ -57,16 +58,4 @@ public void Close() _stream.Close(); } } - - public class NetworkDataWriterInfo : IDataWriterInfo - { - public string IpAddress { get; } - public int Port { get; } - - public NetworkDataWriterInfo(string ipAddress, int port) - { - IpAddress = ipAddress; - Port = port; - } - } } From 2f7d3b42d9ce94000b999ec821f54f2d057d79a4 Mon Sep 17 00:00:00 2001 From: Shariff Faleel Date: Fri, 6 Jun 2025 19:53:00 -0700 Subject: [PATCH 3/4] Fix lastFixedUpdate not being reset when restarting recorder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the recorder restarts, _lastFixedUpdateTime isn’t reset, so RunFixedUpdate tries to process every frame recorded during the downtime and causes a spike. To prevent this, we reset _lastFixedUpdateTime to the current time on restart, ensuring only new frames are processed. --- Runtime/Core/Recorder/Module/Frame/FrameRecorderModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Runtime/Core/Recorder/Module/Frame/FrameRecorderModule.cs b/Runtime/Core/Recorder/Module/Frame/FrameRecorderModule.cs index d490f99..6a5d3ab 100644 --- a/Runtime/Core/Recorder/Module/Frame/FrameRecorderModule.cs +++ b/Runtime/Core/Recorder/Module/Frame/FrameRecorderModule.cs @@ -61,6 +61,7 @@ void IRecorderModule.Awake(RecorderContext ctx) void IRecorderModule.StartRecording(RecorderContext ctx) { _lastUpdateTime = 0; + _lastFixedUpdateTime = ctx.CurrentRecord.Time; _shouldRunUpdate = true; _shouldSerialize = true; From 06f310363dbe8c23541e3d5c2213b9ffe3b59305 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Wed, 11 Jun 2025 20:17:25 -0700 Subject: [PATCH 4/4] Use SetMetaData to initialize data writers in Recorder Refactor the Recorder system to initialize IDataWriter outputs using the new SetMetaData method instead of Initialize. FileDataWriter now requires file paths on construction and accepts metadata via SetMetaData. NetworkDataWriter implements a no-op SetMetaData to satisfy the interface. This change improves separation of responsibilities by decoupling metadata setup from generic Initialize calls. --- Runtime/Core/Recorder/Recorder.cs | 8 +++- .../Core/Recorder/Writer/FileDataWriter.cs | 43 +++++++------------ Runtime/Core/Recorder/Writer/IDataWriter.cs | 2 +- .../Core/Recorder/Writer/NetworkDataWriter.cs | 13 ++---- 4 files changed, 27 insertions(+), 39 deletions(-) diff --git a/Runtime/Core/Recorder/Recorder.cs b/Runtime/Core/Recorder/Recorder.cs index 62094b7..0b94c70 100644 --- a/Runtime/Core/Recorder/Recorder.cs +++ b/Runtime/Core/Recorder/Recorder.cs @@ -77,7 +77,11 @@ private void StartRecordingInternal(string name, string extraMetadata = "", IDat if (outputs == null) { - IDataWriter fileDataWriter = new FileDataWriter(); + var outputDir = Application.persistentDataPath; + + FileDataWriter.GenerateFilePath(outputDir, name, out string filePath, out string metaFilePath); + + IDataWriter fileDataWriter = new FileDataWriter(filePath, metaFilePath); outputs = new IDataWriter[] { fileDataWriter }; } @@ -85,7 +89,7 @@ private void StartRecordingInternal(string name, string extraMetadata = "", IDat foreach (IDataWriter output in outputs) { - output.Initialize(record); + output.SetMetaData(recordMetadata); } _dataDispatcher.Start(_context.CurrentRecord, outputs); diff --git a/Runtime/Core/Recorder/Writer/FileDataWriter.cs b/Runtime/Core/Recorder/Writer/FileDataWriter.cs index 7830569..5ed2ce2 100644 --- a/Runtime/Core/Recorder/Writer/FileDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/FileDataWriter.cs @@ -7,6 +7,7 @@ using K4os.Compression.LZ4.Streams; using PLUME.Sample; using UnityEngine; +using UnityEngine.Assertions; namespace PLUME.Core.Recorder.Writer { @@ -15,39 +16,20 @@ namespace PLUME.Core.Recorder.Writer // TODO: use memory mapped files public class FileDataWriter : IDataWriter, IDisposable { - private Stream _stream; - private Stream _metaStream; - private CodedOutputStream _metaCodedOutputStream; + private readonly Stream _stream; + private readonly Stream _metaStream; + private readonly CodedOutputStream _metaCodedOutputStream; + private readonly RecordMetrics _metrics; private Sample.RecordMetadata _metadata; - private RecordMetrics _metrics; - - private string filePath; - private string metaFilePath; - - public FileDataWriter() { } /// The file path for the plm file. /// The file path for the meta file. public FileDataWriter(string filePath, string metaFilePath) { - this.filePath = filePath; - this.metaFilePath = metaFilePath; - } - - /// - public void Initialize(Record record) - { - if (string.IsNullOrWhiteSpace(filePath) ^ string.IsNullOrWhiteSpace(metaFilePath)) - { - throw new InvalidOperationException($"Only one of filePath and metaFilePath are specifed. Either both need to be specified, or neither should be"); - } - else if (string.IsNullOrWhiteSpace(filePath) && string.IsNullOrWhiteSpace(metaFilePath)) - { - var outputDir = Application.persistentDataPath; + Assert.IsFalse(string.IsNullOrWhiteSpace(filePath), "filePath is null or empty"); + Assert.IsFalse(string.IsNullOrWhiteSpace(metaFilePath), "metaFilePath is null or empty"); - GenerateFilePath(outputDir, record.Metadata.Name, out filePath, out metaFilePath); - } Logger.Log($"Record will be saved to '{filePath}'."); PinnedMemory.MaxPooledSize = 0; @@ -68,12 +50,15 @@ public void Initialize(Record record) _metaStream = File.Create(metaFilePath); _metaCodedOutputStream = new CodedOutputStream(_metaStream); - _metadata = record.Metadata.ToPayload(); _metrics = new RecordMetrics { IsSequential = true }; - + } + + public void SetMetaData(RecordMetadata metaData) + { + _metadata = metaData.ToPayload(); UpdateMetaFile(); } @@ -123,6 +108,10 @@ public void WriteTimestampedData(DataChunksTimestamped dataChunks) private void UpdateMetaFile() { + if (_metadata == null) + { + return; + } _metaStream.SetLength(0); _metaStream.Position = 0; _metaCodedOutputStream.WriteLength(_metadata.CalculateSize()); diff --git a/Runtime/Core/Recorder/Writer/IDataWriter.cs b/Runtime/Core/Recorder/Writer/IDataWriter.cs index 291b0d8..6f9e669 100644 --- a/Runtime/Core/Recorder/Writer/IDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/IDataWriter.cs @@ -2,7 +2,7 @@ namespace PLUME.Core.Recorder.Writer { public interface IDataWriter { - public void Initialize(Record record); + public void SetMetaData(RecordMetadata metadata); public void WriteTimelessData(DataChunks dataChunks); diff --git a/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs b/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs index 1cbbfaf..4872b5e 100644 --- a/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs +++ b/Runtime/Core/Recorder/Writer/NetworkDataWriter.cs @@ -10,17 +10,9 @@ namespace PLUME.Core.Recorder.Writer { public class NetworkDataWriter : IDataWriter { - private Stream _stream; - private string ipAddress; - private int port; + private readonly Stream _stream; public NetworkDataWriter(string ipAddress="127.0.0.1", int port=8000) - { - this.ipAddress = ipAddress; - this.port = port; - } - - public void Initialize(Record _) { // Create a tcp server var server = new TcpListener(IPAddress.Parse(ipAddress), port); @@ -37,6 +29,9 @@ public void Initialize(Record _) _stream = LZ4Stream.Encode(stream, LZ4Level.L00_FAST); } + public void SetMetaData(RecordMetadata _) + {} + public void WriteTimelessData(DataChunks dataChunks) { }