142 lines
4.3 KiB
C#
142 lines
4.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
public class CsvLogger : MonoBehaviour
|
|
{
|
|
public static CsvLogger Instance { get; private set; }
|
|
|
|
[Header("File")]
|
|
[SerializeField] private string filePrefix = "session";
|
|
|
|
[Header("Optional Pose Sampling")]
|
|
[SerializeField] private bool logTransform = false;
|
|
[SerializeField] private Transform target; // e.g., Main Camera (HMD) or a controller
|
|
[SerializeField] private float sampleHz = 5f; // samples per second (0 = off)
|
|
|
|
// Deploying in WebGL does not allow for disk write, so we need
|
|
// a special way to download the data from the memory buffer as a web file.
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
private StringBuilder bufferWebGL;
|
|
#else
|
|
private StreamWriter writer;
|
|
#endif
|
|
|
|
private string filename;
|
|
private string filepath;
|
|
private float nextSampleTime;
|
|
private bool isConfigured = false;
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
|
|
Instance = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
//// Uncomment if you want the logging to begin automatically.
|
|
// StartLogger();
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
if(isConfigured) StopLogger();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!logTransform || target == null || sampleHz <= 0f) return;
|
|
|
|
if (Time.unscaledTime >= nextSampleTime)
|
|
{
|
|
nextSampleTime = Time.unscaledTime + (1f / sampleHz);
|
|
LogInternal("transform_sample", "");
|
|
}
|
|
}
|
|
|
|
public void StartLogger()
|
|
{
|
|
if(isConfigured) StopLogger();
|
|
|
|
string ts = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
|
filename = $"{filePrefix}_{ts}.csv";
|
|
string dir = Application.persistentDataPath;
|
|
filepath = Path.Combine(dir, filename);
|
|
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
bufferWebGL = new StringBuilder(4096);
|
|
bufferWebGL.AppendLine("timestamp_utc,scene,event,details,px,py,pz,rx,ry,rz,rw");
|
|
// Small breadcrumb to find the file path in Console:
|
|
Debug.Log($"[CsvLoggerWebGL] buffering in memory. File will download when stopped: {filename}");
|
|
#else
|
|
writer = new StreamWriter(filepath, false, Encoding.UTF8);
|
|
writer.WriteLine("timestamp_now,scene,event,details,px,py,pz,rx,ry,rz,rw");
|
|
writer.Flush();
|
|
// Small breadcrumb to find the file path in Console:
|
|
Debug.Log($"[CsvLogger] Writing to: {filepath}");
|
|
#endif
|
|
|
|
isConfigured = true;
|
|
}
|
|
|
|
public void StopLogger()
|
|
{
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
var bytes = Encoding.UTF8.GetBytes(bufferWebGL.ToString());
|
|
|
|
// In WEBGL: Download file when closing
|
|
if (WebGLFileSaver.IsSavingSupported())
|
|
{
|
|
Debug.Log($"[CsvLoggerWebGL] File saved as: {filename}");
|
|
WebGLFileSaver.SaveFile(bytes, filename);
|
|
}
|
|
#else
|
|
Debug.Log($"[CsvLogger] File available at: {filepath}");
|
|
writer?.Flush();
|
|
writer?.Close();
|
|
#endif
|
|
|
|
isConfigured = false; // a new logger must be started
|
|
}
|
|
|
|
public static void LogEvent(string eventToLog, string details = "")
|
|
{
|
|
if (Instance == null) return;
|
|
Instance.LogInternal(eventToLog, details);
|
|
}
|
|
|
|
private void LogInternal(string evt, string details)
|
|
{
|
|
if(!isConfigured) return;
|
|
|
|
string tstamp = DateTime.Now.ToString("o"); // ISO-8601 UTC
|
|
string scene = SceneManager.GetActiveScene().name;
|
|
|
|
// Default empty pose
|
|
float px = 0, py = 0, pz = 0, rx = 0, ry = 0, rz = 0, rw = 1;
|
|
|
|
// Record the transform of the target
|
|
if (target != null)
|
|
{
|
|
Vector3 p = target.position;
|
|
Quaternion r = target.rotation;
|
|
px = p.x; py = p.y; pz = p.z;
|
|
rx = r.x; ry = r.y; rz = r.z; rw = r.w;
|
|
}
|
|
|
|
// Escape commas in details by wrapping in quotes if needed
|
|
if (!string.IsNullOrEmpty(details) && details.Contains(",")) details = $"\"{details}\"";
|
|
|
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
bufferWebGL.AppendLine($"{tstamp},{scene},{evt},{details},{px},{py},{pz},{rx},{ry},{rz},{rw}");
|
|
#else
|
|
writer.WriteLine($"{tstamp},{scene},{evt},{details},{px},{py},{pz},{rx},{ry},{rz},{rw}");
|
|
writer.Flush();
|
|
#endif
|
|
}
|
|
|
|
} |