-
-
Save julenka/20ece5141e821cb6d0a9cb530d3dc96d to your computer and use it in GitHub Desktop.
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Text; | |
using System.Threading.Tasks; | |
using UnityEngine; | |
#if WINDOWS_UWP | |
using Windows.Storage; | |
#endif | |
namespace holoutils | |
{ | |
/// <summary> | |
/// Component that Logs data to a CSV. | |
/// Assumes header is fixed. | |
/// Copy and paste this logger to create your own CSV logger. | |
/// CSV Logger breaks data up into settions (starts when application starts) which are folders | |
/// and instances which are files | |
/// A session starts when the application starts, it ends when the session ends. | |
/// | |
/// In Editor, writes to MyDocuments/SessionFolderRoot folder | |
/// On Device, saves data in the Pictures/SessionFolderRoot | |
/// | |
/// How to use: | |
/// Find the csvlogger | |
/// if it has not started a CSV, create one. | |
/// every frame, log stuff | |
/// Flush data regularly | |
/// | |
/// **Important: Requires the PicturesLibrary capability!** | |
/// </summary> | |
public class CSVLogger : MonoBehaviour | |
{ | |
#region Constants to modify | |
private const string DataSuffix = "data"; | |
private const string CSVHeader = "Timestamp,SessionID,RecordingID," + | |
"blah,blah,blah"; | |
private const string SessionFolderRoot = "CSVLogger"; | |
#endregion | |
#region private members | |
private string m_sessionPath; | |
private string m_filePath; | |
private string m_recordingId; | |
private string m_sessionId; | |
private StringBuilder m_csvData; | |
#endregion | |
#region public members | |
public string RecordingInstance => m_recordingId; | |
#endregion | |
// Use this for initialization | |
async void Start() | |
{ | |
await MakeNewSession(); | |
} | |
async Task MakeNewSession() | |
{ | |
m_sessionId = DateTime.Now.ToString("yyyyMMdd_HHmmss"); | |
string rootPath = ""; | |
#if WINDOWS_UWP | |
StorageFolder sessionParentFolder = await KnownFolders.PicturesLibrary | |
.CreateFolderAsync(SessionFolderRoot, | |
CreationCollisionOption.OpenIfExists); | |
rootPath = sessionParentFolder.Path; | |
#else | |
rootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), SessionFolderRoot); | |
if (!Directory.Exists(rootPath)) Directory.CreateDirectory(rootPath); | |
#endif | |
m_sessionPath = Path.Combine(rootPath, m_sessionId); | |
Directory.CreateDirectory(m_sessionPath); | |
Debug.Log("CSVLogger logging data to " + m_sessionPath); | |
} | |
public void StartNewCSV() | |
{ | |
m_recordingId = DateTime.Now.ToString("yyyyMMdd_HHmmssfff"); | |
var filename = m_recordingId + "-" + DataSuffix + ".csv"; | |
m_filePath = Path.Combine(m_sessionPath, filename); | |
if (m_csvData != null) | |
{ | |
EndCSV(); | |
} | |
m_csvData = new StringBuilder(); | |
m_csvData.AppendLine(CSVHeader); | |
} | |
public void EndCSV() | |
{ | |
if (m_csvData == null) | |
{ | |
return; | |
} | |
using (var csvWriter = new StreamWriter(m_filePath, true)) | |
{ | |
csvWriter.Write(m_csvData.ToString()); | |
} | |
m_recordingId = null; | |
m_csvData = null; | |
} | |
public void OnDestroy() | |
{ | |
EndCSV(); | |
} | |
public void AddRow(List<String> rowData) | |
{ | |
AddRow(string.Join(",", rowData.ToArray())); | |
} | |
public void AddRow(string row) | |
{ | |
m_csvData.AppendLine(row); | |
} | |
/// <summary> | |
/// Writes all current data to current file | |
/// </summary> | |
public void FlushData() | |
{ | |
using (var csvWriter = new StreamWriter(m_filePath, true)) | |
{ | |
csvWriter.Write(m_csvData.ToString()); | |
} | |
m_csvData.Clear(); | |
} | |
/// <summary> | |
/// Returns a row populated with common start data like | |
/// recording id, session id, timestamp | |
/// </summary> | |
/// <returns></returns> | |
public List<String> RowWithStartData() | |
{ | |
List<String> rowData = new List<String>(); | |
rowData.Add(Time.timeSinceLevelLoad.ToString("##.000")); | |
rowData.Add(m_recordingId); | |
rowData.Add(m_recordingId); | |
return rowData; | |
} | |
} | |
} |
Hi, glad you found this!. Did you make sure to call FlushData() after calling AddRow()?
I did! Thanks for replying. I managed to mitigate the issue by flushing and writing only once per second as opposed to once per frame. It seems the size of the buffer doesn't matter, but no matter how small the buffer, the flush causes a hiccup. Thanks for writing this code!
Hi,
This code has been so helpful. But this code is not creating a file in the folder.Would be glad if you could help.
I tried using it and the folder isn't creating in the pictures file on the HoloLens. Is there something I'm missing as I didn't make any changes?
Hi, I was able to recently test this code. Make sure that you are 1) flushing data regularly (once per second is good), and then 2) that you have enabled the PicturesLibrary capability when deploying to HoloLens. See this for more info: https://docs.microsoft.com/en-us/windows/mixed-reality/develop/unity/Configure-Unity-Project
Hi, thank you for providing this code! I am having a similar issue to those above. I enabled the PicturesLibrary capability in Unity settings and I doubled checked the capabilities in the package.appxmanifest. The session folder gets created in the PictureLibarary, however the file fails to generate. I regularly flushed the data within Update() and I also attempted sampling once per second with InvokeRepeating. Any thoughts?
I have modified the code to dump the data into the Hololens Device portal. It's working now for me.
Hi, make sure you are also calling MakeNewSession()
to make the folder and StartNewCSV()
to create a CSV. First verify that the CSV is being created when you are using the editor. The folder and file should go into my documents. If you have that and the PicturesLibrary capability set, then you should see a folder also being created in My Pictures.
Hello, I was using your code for two projects without issues, but recently, the call to Directory.CreateDirectory(m_sessionPath)
on line 74 stopped working very similar to the above. I updated the MakeNewSession code to m_sessionId = DateTime.Now.ToString("yyyyMMdd_HHmmss"); string rootPath = ""; m_sessionPath = Path.Combine(SessionFolderRoot, m_sessionId); #if WINDOWS_UWP StorageFolder sessionParentFolder = await KnownFolders.PicturesLibrary .CreateFolderAsync(m_sessionPath, CreationCollisionOption.OpenIfExists); rootPath = sessionParentFolder.Path; UnityEngine.Debug.Log("Logger logging data to " + sessionParentFolder.Path); #endif
and it now seems to work. Maybe something changed in the .NET namespace?
Hello, thank you for the code it works well in unity but the data is quite delayed on the Hololens. At first, I didn't include FlushData() in the code, .csv file was created properly in the unity editor but it was not created on the Hololens. But right after I put FlushData(), I can see the file created in Hololens's picture folder but it makes the game and sensor data delayed more than I expected which is around 30sec from the normal 4sec (before adding flush). Do you have any suggestions or recommendations?
Thank you for the code. I have just one question, where should I call the StartNewCSV()
and FlushData()
functions? The code that you provided has a async void Start()
function which does not allow me to have my basic void start(){}
. I'm guessing I should use the FlushData()
in the Update()
function. right?
I think StartNewCSV(
) here is to create a CSV file with the unity editor. If you want to create a CSV file in the Hololens, you need to use the Windows.Storage
for UWP. Referring to https://learn.microsoft.com/en-us/windows/uwp/files/quickstart-reading-and-writing-files
For more file-accessing codes for UWP, I found this helpful:
https://github.com/Microsoft/Windows-universal-samples/tree/main/Samples/FileAccess
Awesome, thank you! I'm trying to use this to log GazeProvider data, and Im calling this every frame (Update()). Seems like theres an issue with the threading though. The folder gets made but no .csv. SOmetimes the folder from a previous run gets made when I run the program the second time, which is why I think its a threading issue. Thoughts?