Skip to content

Instantly share code, notes, and snippets.

@julenka
Last active August 11, 2023 05:50
Show Gist options
  • Save julenka/20ece5141e821cb6d0a9cb530d3dc96d to your computer and use it in GitHub Desktop.
Save julenka/20ece5141e821cb6d0a9cb530d3dc96d to your computer and use it in GitHub Desktop.
Log data to CSV on HoloLens from a Unity project. Writes to Pictures folder instead of applicationDataPath so that files can be accessed from File Explorer (File Explorer -> HoloLens -> Pictures -> YourFolder). Doesn't require using Device portal.
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;
}
}
}
@Lilaxc
Copy link

Lilaxc commented Mar 3, 2023

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?

@kukalbriiwa7
Copy link

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?

@stonycat
Copy link

stonycat commented Aug 11, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment