Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
BenoitGeslain committed May 14, 2024
2 parents dadee21 + 6a9da57 commit b39d26a
Show file tree
Hide file tree
Showing 27 changed files with 1,470 additions and 528 deletions.

Large diffs are not rendered by default.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace VHToolkit.Calibration {

public class QuickCalibration : MonoBehaviour {

[SerializeField] private Transform physicalHead;
[SerializeField] private Transform world;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ private void Update() {
}

/// <summary>
/// This function sets the state of the calibration so that the user can start calibration on the next frame
/// Function <c>Calibrate</c> sets the state of the calibration so that the user can start calibration on the next frame.
/// </summary>
public void Calibrate() => state = CalibrationState.FirstPoint;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.IO;
using UnityEngine;

namespace VHToolkit.Logging {
abstract class AbstractFileObserver<T> : IObserver<T> {
protected readonly StreamWriter writer;
private IDisposable unsubscriber;
public void OnCompleted() {
unsubscriber.Dispose();
writer.Dispose();
}

public void Subscribe(IObservable<T> observable) => unsubscriber = observable?.Subscribe(this);

public void OnError(Exception error) => Debug.LogError(error);

abstract public void OnNext(T value);

public AbstractFileObserver(string filename) {
writer = new StreamWriter(filename, append: true);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,23 @@
using Newtonsoft.Json;

namespace VHToolkit.Logging {

/// <summary>
/// Record type <c>TransformData</c> is a wrapper around Unity's <c>Transform</c>.
/// It allows for serializing the underlying <c>Transform</c>'s <c>position</c> and <c>rotation</c> properties.
/// </summary>
public record TransformData {
private readonly Transform obj;
public string Position => obj ? obj.position.ToString() : "NULL";
public string Orientation => obj ? obj.rotation.normalized.ToString() : "NULL";
public TransformData(Transform obj) => this.obj = obj;
}

/// <summary>
/// Record type <c>PhysicalLimbData</c> is a wrapper around <c>Limb</c>.
/// It allows for serializing the <c>position</c> and <c>rotation</c> properties of the underlying physical limb,
/// as well as those of all underlying virtual limbs.
/// </summary>
public record PhysicalLimbData {
private readonly Limb limb;
public string Position => limb.physicalLimb.position.ToString();
Expand All @@ -30,67 +40,38 @@ public record JsonRedirectionData {
public readonly DateTime TimeStamp = DateTime.Now;
private readonly Interaction script;

public string Technique => script switch {
/// <value>
/// Property <c>Strategy</c> is a string corresponding to the name of the current World Redirection
/// target selection strategy, if any, or the empty string.
/// </value>
public string Strategy => script switch {
WorldRedirection => (script as WorldRedirection).strategy.ToString(),
BodyRedirection => "",
_ => ""
BodyRedirection => String.Empty,
_ => String.Empty
};

public string Strategy => script switch {
/// <value>
/// Property <c>Technique</c> is a string corresponding to the name of the current redirection
/// technique, if any, or the empty string.
/// </value>
public string Technique => script switch {
WorldRedirection => (script as WorldRedirection).Technique.ToString(),
BodyRedirection => (script as BodyRedirection).Technique.ToString(),
_ => ""
};

public bool Redirecting => script.redirect;

public List<PhysicalLimbData> Limbs => script.scene.limbs.ConvertAll(l => new PhysicalLimbData(l));
public TransformData PhysicalHead => script.scene.physicalHead ? new(script.scene.physicalHead) : null;
public List<TransformData> Targets => script.scene.targets.ConvertAll(t => new TransformData(t));
public TransformData PhysicalTarget => script.scene.physicalTarget ? new(script.scene.physicalTarget) : null;
public TransformData VirtualTarget => script.scene.virtualTarget ? new(script.scene.virtualTarget) : null;
public string StrategyDirection => script.scene.forwardTarget != null ? script.scene.forwardTarget.ToString() : null;
public string StrategyDirection => script.scene.forwardTarget != null ? Convert.ToString(script.scene.forwardTarget.ToString()) : null;

public JsonRedirectionData(Interaction script) => this.script = script;
}

public class Logger<T> : MonoBehaviour, IObservable<T> {
public string logDirectoryPath = "LoggedData\\";
[SerializeField] protected string optionalFilenamePrefix;
protected readonly int bufferSize = 10; // number of records kept before writing to disk

protected Queue<T> records = new();
protected HashSet<IObserver<T>> observers = new();
protected Interaction script;

protected void WriteRecords(Queue<T> records) {
if (records.Count > bufferSize) {
foreach (var record in records) {
foreach (var observer in observers) {
observer.OnNext(record);
}
}
records.Clear();
}
}

private sealed class Unsubscriber : IDisposable {
private HashSet<IObserver<T>> _observers;
private IObserver<T> _observer;

public Unsubscriber(HashSet<IObserver<T>> observers, IObserver<T> observer) {
_observers = observers;
_observer = observer;
}

public void Dispose() => _observers.Remove(_observer);
}

IDisposable IObservable<T>.Subscribe(IObserver<T> observer) {
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
}

/// <summary>
/// Logs structured data in the JSON Lines format.
/// </summary>
Expand All @@ -105,31 +86,27 @@ private void Update() {
records.Enqueue(new JsonRedirectionData(script));
WriteRecords(records);
}

/// <summary>
/// Create a new log file.
/// </summary>
/// <param name="logDirectoryPath">The path to the directory where the file should be placed.</param>
/// <param name="optionalFilenamePrefix">An optional prefix string to appear in the filename before its timestamp.</param>
public void CreateNewFile(string logDirectoryPath, string optionalFilenamePrefix = "") {
Directory.CreateDirectory(logDirectoryPath);
var fileName = $"{logDirectoryPath}{optionalFilenamePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.jsonl";
var observer = new FileObserver(fileName);
var observer = new JsonFileObserver(fileName);
observer.Subscribe(this);
observers.Add(observer);
}

/// <summary>
/// The class <c>JsonFileObserver</c> implements the Observer pattern for <c>JsonRedirectionData</c> instances,
/// serializing the information which it receives and writing it to a file.
/// </summary>
private sealed class JsonFileObserver : AbstractFileObserver<JsonRedirectionData> {
public JsonFileObserver(string filename) : base(filename) { }

private sealed class FileObserver : IObserver<JsonRedirectionData> {
private readonly StreamWriter writer;
private IDisposable unsubscriber;
public void OnCompleted() {
unsubscriber.Dispose();
writer.Dispose();
}

public void Subscribe(IObservable<JsonRedirectionData> observable) => unsubscriber = observable?.Subscribe(this);

public void OnError(Exception error) => Debug.LogError(error);

public void OnNext(JsonRedirectionData value) => writer.WriteLine(JsonConvert.SerializeObject(value));

public FileObserver(string filename) => writer = new StreamWriter(filename, append: true);
override public void OnNext(JsonRedirectionData value) => writer.WriteLine(JsonConvert.SerializeObject(value));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;

using UnityEngine;

using VHToolkit.Redirection;

namespace VHToolkit.Logging {
public partial class Logger<T> : MonoBehaviour, IObservable<T> {
public string logDirectoryPath = "LoggedData\\";
[SerializeField] protected string optionalFilenamePrefix;
protected readonly int bufferSize = 10; // number of records kept before writing to disk

protected readonly Queue<T> records = new();
protected readonly HashSet<IObserver<T>> observers = new();
protected Interaction script;

protected void WriteRecords(Queue<T> records) {
if (records.Count > bufferSize) {
foreach (var record in records) {
foreach (var observer in observers) {
observer.OnNext(record);
}
}
records.Clear();
}
}

IDisposable IObservable<T>.Subscribe(IObserver<T> observer) {
observers.Add(observer);
return new HashSetUnsubscriber<T>(observers, observer);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b39d26a

Please sign in to comment.