Skip to content

Commit e1318d8

Browse files
committed
Added support for post-write file events
1 parent f623744 commit e1318d8

File tree

7 files changed

+194
-47
lines changed

7 files changed

+194
-47
lines changed

Assets/UXF/Prefabs/[UXF_Rig].prefab

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2673,7 +2673,7 @@ MonoBehaviour:
26732673
m_Script: {fileID: 11500000, guid: 62bd92b5564a1d343b07363d3453a980, type: 3}
26742674
m_Name:
26752675
m_EditorClassIdentifier:
2676-
debug: 1
2676+
debug: 0
26772677
--- !u!114 &114344481789201668
26782678
MonoBehaviour:
26792679
m_ObjectHideFlags: 1

Assets/UXF/Scripts/Etc/Events.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.IO;
23
using UnityEngine;
34
using UnityEngine.Events;
45

@@ -23,5 +24,35 @@ public class TrialEvent : UnityEvent<Trial>
2324

2425
}
2526

27+
/// <summary>
28+
/// Event containing a WriteFileInfo object as a parameter
29+
/// </summary>
30+
[Serializable]
31+
public class WriteFileEvent : UnityEvent<WriteFileInfo>
32+
{
33+
34+
}
35+
36+
37+
public struct WriteFileInfo
38+
{
39+
public WriteFileType fileType;
40+
public string basePath;
41+
public string[] paths;
42+
public string RelativePath { get { return Extensions.CombinePaths("", paths); }}
43+
public string FullPath { get { return Extensions.CombinePaths(basePath, paths); } }
44+
public WriteFileInfo(WriteFileType fileType, string basePath, params string[] paths)
45+
{
46+
this.fileType = fileType;
47+
this.basePath = basePath;
48+
this.paths = paths;
49+
}
50+
51+
}
52+
53+
public enum WriteFileType
54+
{
55+
Test, Trials, Tracker, Dictionary, ParticipantList, Log
56+
}
2657

2758
}

Assets/UXF/Scripts/FileIOManager.cs

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,30 @@ public class FileIOManager : MonoBehaviour
2222
[Tooltip("Enable to print debug messages to the console.")]
2323
public bool debug = false;
2424

25+
/// <summary>
26+
/// Event(s) to trigger when we write a file. Not performed in amin thread so cannot include most Unity actions.
27+
/// </summary>
28+
/// <returns></returns>
29+
[Tooltip("Event(s) to trigger when we write a file. Not performed in amin thread so cannot include most Unity actions.")]
30+
public WriteFileEvent onWriteFile = new WriteFileEvent();
31+
2532
/// <summary>
2633
/// Queue of actions which gets emptied on each frame in the main thread.
2734
/// </summary>
2835
public readonly Queue<System.Action> executeOnMainThreadQueue = new Queue<System.Action>();
2936

30-
BlockingQueue<System.Action> bq = new BlockingQueue<System.Action>();
31-
Thread t;
32-
33-
bool quitting = false;
34-
3537
/// <summary>
3638
/// An action which does nothing. Useful if a method requires an Action
3739
/// </summary>
3840
/// <returns></returns>
3941
public static System.Action doNothing = () => { };
4042

43+
BlockingQueue<System.Action> bq = new BlockingQueue<System.Action>();
44+
Thread t;
45+
46+
bool quitting = false;
47+
48+
4149
void Awake()
4250
{
4351
Begin();
@@ -160,10 +168,11 @@ public void ReadJSON(string fpath, System.Action<Dictionary<string, object>> cal
160168
/// </summary>
161169
/// <param name="destFileName"></param>
162170
/// <param name="serializableObject"></param>
163-
public void WriteJson(string destFileName, object serializableObject)
171+
public void WriteJson(object serializableObject, WriteFileInfo writeFileInfo)
164172
{
165173
string ppJson = MiniJSON.Json.Serialize(serializableObject);
166-
File.WriteAllText(destFileName, ppJson);
174+
File.WriteAllText(writeFileInfo.FullPath, ppJson);
175+
onWriteFile.Invoke(writeFileInfo);
167176
}
168177

169178
/// <summary>
@@ -172,7 +181,7 @@ public void WriteJson(string destFileName, object serializableObject)
172181
/// <param name="dataDict"></param>
173182
/// <param name="headers"></param>
174183
/// <param name="fpath"></param>
175-
public void WriteTrials(List<OrderedResultDict> dataDict, string[] headers, string fpath)
184+
public void WriteTrials(List<OrderedResultDict> dataDict, string[] headers, WriteFileInfo writeFileInfo)
176185
{
177186
string[] csvRows = new string[dataDict.Count + 1];
178187
csvRows[0] = string.Join(",", headers.ToArray());
@@ -188,23 +197,25 @@ public void WriteTrials(List<OrderedResultDict> dataDict, string[] headers, stri
188197
}
189198
}
190199

191-
File.WriteAllLines(fpath, csvRows);
200+
File.WriteAllLines(writeFileInfo.FullPath, csvRows);
201+
onWriteFile.Invoke(writeFileInfo);
192202
}
193203

194204
/// <summary>
195205
/// Writes a list of string arrays with a given header to a file at given path.
196206
/// </summary>
197-
/// <param name="header"></param>
207+
/// <param name="header">Row of headers</param>
198208
/// <param name="data"></param>
199209
/// <param name="fpath"></param>
200-
public void WriteCSV(string[] header, IList<string[]> data, string fpath)
210+
public void WriteCSV(string[] header, IList<string[]> data, WriteFileInfo writeFileInfo)
201211
{
202212
string[] csvRows = new string[data.Count + 1];
203213
csvRows[0] = string.Join(",", header);
204214
for (int i = 1; i <= data.Count; i++)
205215
csvRows[i] = string.Join(",", data[i-1]);
206216

207-
File.WriteAllLines(fpath, csvRows);
217+
File.WriteAllLines(writeFileInfo.FullPath, csvRows);
218+
onWriteFile.Invoke(writeFileInfo);
208219
}
209220

210221
/// <summary>
@@ -221,7 +232,7 @@ public void ReadCSV(string fpath, System.Action<DataTable> callback)
221232
}
222233
catch (FileNotFoundException)
223234
{
224-
235+
Debug.LogErrorFormat("Cannot find file {0}", fpath);
225236
}
226237

227238
System.Action action = new System.Action(() => callback.Invoke(data));
@@ -233,11 +244,12 @@ public void ReadCSV(string fpath, System.Action<DataTable> callback)
233244
/// </summary>
234245
/// <param name="data"></param>
235246
/// <param name="fpath"></param>
236-
public void WriteCSV(DataTable data, string fpath)
247+
public void WriteCSV(DataTable data, WriteFileInfo writeFileInfo)
237248
{
238-
var writer = new CSVFile.CSVWriter(fpath);
249+
var writer = new CSVFile.CSVWriter(writeFileInfo.FullPath);
239250
writer.Write(data, true);
240251
writer.Dispose();
252+
onWriteFile.Invoke(writeFileInfo);
241253
}
242254

243255
/// <summary>
@@ -257,7 +269,7 @@ void OnDestroy()
257269
}
258270

259271
/// <summary>
260-
/// Aborts the FileIOManager's thread.
272+
/// Aborts the FileIOManager's thread and joins the thread to the calling thread.
261273
/// </summary>
262274
public void End()
263275
{

Assets/UXF/Scripts/Session.cs

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class Session : MonoBehaviour
2222
/// </summary>
2323
[Tooltip("Enable to automatically safely end the session when this object is destroyed (or the application stops running).")]
2424
public bool endOnDestroy = true;
25-
25+
2626
/// <summary>
2727
/// List of blocks for this experiment
2828
/// </summary>
@@ -71,6 +71,7 @@ public class Session : MonoBehaviour
7171
[Tooltip("Event(s) to trigger when a trial ends. Can pass the instance of the Trial as a dynamic argument")]
7272
public TrialEvent onTrialEnd = new TrialEvent();
7373

74+
7475
[Header("Session information")]
7576

7677
[ReadOnly]
@@ -171,7 +172,11 @@ public class Session : MonoBehaviour
171172

172173
List<string> baseHeaders = new List<string> { "experiment", "ppid", "session_num", "trial_num", "block_num", "trial_num_in_block", "start_time", "end_time" };
173174

174-
string basePath;
175+
/// <summary>
176+
/// The path in which the experiment data are stored.
177+
/// </summary>
178+
/// <value></value>
179+
public string basePath { get; private set; }
175180

176181

177182
/// <summary>
@@ -187,7 +192,11 @@ public class Session : MonoBehaviour
187192
/// </summary>
188193
public string path { get { return Path.Combine(ppPath, folderName); } }
189194

190-
private string folderName { get { return SessionNumToName(number); } }
195+
/// <summary>
196+
/// Name of the Session folder
197+
/// </summary>
198+
/// <returns></returns>
199+
public string folderName { get { return SessionNumToName(number); } }
191200

192201

193202
/// <summary>
@@ -198,7 +207,7 @@ public class Session : MonoBehaviour
198207
/// <summary>
199208
/// Stores combined list of headers for the behavioural output.
200209
/// </summary>
201-
public List<string> headers { get { return baseHeaders.Concat(settingsToLog).Concat(customHeaders).Concat(trackingHeaders).ToList(); }}
210+
public List<string> headers { get { return baseHeaders.Concat(settingsToLog).Concat(customHeaders).Concat(trackingHeaders).ToList(); } }
202211

203212
/// <summary>
204213
/// Dictionary of objects for datapoints collected via the UI, or otherwise.
@@ -235,7 +244,7 @@ void InitFolder()
235244
if (!System.IO.Directory.Exists(ppPath))
236245
System.IO.Directory.CreateDirectory(ppPath);
237246
if (System.IO.Directory.Exists(path))
238-
Debug.LogWarning("Warning: Session already exists! Continuing will overwrite");
247+
Debug.LogWarning("Warning: Session already exists! Continuing will overwrite");
239248
else
240249
System.IO.Directory.CreateDirectory(path);
241250
}
@@ -248,16 +257,22 @@ void InitFolder()
248257
public string SaveTrackerData(Tracker tracker)
249258
{
250259
string fname = string.Format("{0}_{1}_T{2:000}.csv", tracker.objectName, tracker.measurementDescriptor, currentTrialNum);
251-
string fpath = Path.Combine(path, fname);
260+
261+
WriteFileInfo fileInfo = new WriteFileInfo(
262+
WriteFileType.Tracker,
263+
this.basePath,
264+
experimentName,
265+
ppid,
266+
folderName,
267+
fname
268+
);
252269

253270
List<string[]> dataCopy = tracker.GetDataCopy();
254271

255-
fileIOManager.ManageInWorker(() => fileIOManager.WriteCSV(tracker.header, dataCopy, fpath));
272+
fileIOManager.ManageInWorker(() => fileIOManager.WriteCSV(tracker.header, dataCopy, fileInfo));
256273

257274
// return relative path so it can be stored in behavioural data
258-
Uri fullPath = new Uri(fpath);
259-
Uri basePath = new Uri(experimentPath);
260-
return basePath.MakeRelativeUri(fullPath).ToString();
275+
return fileInfo.RelativePath;
261276

262277
}
263278

@@ -279,11 +294,21 @@ public void CopyFileToSessionFolder(string filePath)
279294
/// <param name="objectName">Name of the object (is used for file name)</param>
280295
public void WriteDictToSessionFolder(Dictionary<string, object> dict, string objectName)
281296
{
297+
282298
if (hasInitialised)
283299
{
284300
string fileName = string.Format("{0}.json", objectName);
285-
string filePath = Path.Combine(path, fileName);
286-
fileIOManager.ManageInWorker(() => fileIOManager.WriteJson(filePath, dict));
301+
302+
WriteFileInfo fileInfo = new WriteFileInfo(
303+
WriteFileType.Dictionary,
304+
this.basePath,
305+
experimentName,
306+
ppid,
307+
folderName,
308+
fileName
309+
);
310+
311+
fileIOManager.ManageInWorker(() => fileIOManager.WriteJson(dict, fileInfo));
287312
}
288313
else
289314
{
@@ -345,7 +370,7 @@ public void Begin(string experimentName, string participantId, string baseFolder
345370
onSessionBegin.Invoke(this);
346371

347372
// copy Settings to session folder
348-
373+
349374
WriteDictToSessionFolder(
350375
new Dictionary<string, object>(settings.baseDict), // makes a copy
351376
"settings");
@@ -369,13 +394,13 @@ public Block CreateBlock()
369394
public Block CreateBlock(int numberOfTrials)
370395
{
371396
if (numberOfTrials > 0)
372-
return new Block((uint) numberOfTrials, this);
397+
return new Block((uint)numberOfTrials, this);
373398
else
374399
throw new Exception("Invalid number of trials supplied");
375400
}
376401

377402
/// <summary>
378-
/// Get currently active trial.
403+
/// Get currently active trial. When not in a trial, gets previous trial.
379404
/// </summary>
380405
/// <returns>Currently active trial.</returns>
381406
public Trial GetTrial()
@@ -437,7 +462,7 @@ public void BeginNextTrial()
437462
Trial PrevTrial()
438463
{
439464
try
440-
{
465+
{
441466
// non zero indexed
442467
return trials.ToList()[currentTrialNum - 2];
443468
}
@@ -463,7 +488,7 @@ Trial FirstTrial()
463488
/// <returns></returns>
464489
Trial LastTrial()
465490
{
466-
var lastBlock = blocks[blocks.Count- 1];
491+
var lastBlock = blocks[blocks.Count - 1];
467492
return lastBlock.trials[lastBlock.trials.Count - 1];
468493
}
469494

@@ -500,7 +525,7 @@ public void End()
500525
// end logger
501526
if (logger != null)
502527
logger.Finalise();
503-
528+
504529
blocks = new List<Block>();
505530
_hasInitialised = false;
506531
}
@@ -510,10 +535,18 @@ void SaveResults()
510535
{
511536
List<OrderedResultDict> results = trials.Select(t => t.result).ToList();
512537
string fileName = "trial_results.csv";
513-
string filePath = Path.Combine(path, fileName);
538+
539+
WriteFileInfo fileInfo = new WriteFileInfo(
540+
WriteFileType.Trials,
541+
this.basePath,
542+
experimentName,
543+
ppid,
544+
folderName,
545+
fileName
546+
);
514547

515548
// in this case, write in main thread to block aborting
516-
fileIOManager.ManageInWorker(() => fileIOManager.WriteTrials(results, headers.ToArray(), filePath));
549+
fileIOManager.ManageInWorker(() => fileIOManager.WriteTrials(results, headers.ToArray(), fileInfo));
517550
}
518551

519552

@@ -534,7 +567,7 @@ void OnDestroy()
534567
End();
535568
}
536569
}
537-
570+
538571
/// <summary>
539572
/// Convert a session number to a session name
540573
/// </summary>
@@ -551,7 +584,7 @@ public static string SessionNumToName(int num)
551584
/// Exception thrown in cases where we try to access a trial that does not exist.
552585
/// </summary>
553586
public class NoSuchTrialException : Exception
554-
{
587+
{
555588
public NoSuchTrialException()
556589
{
557590
}

Assets/UXF/Scripts/SessionLogger.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,16 @@ void HandleLog(string logString, string stackTrace, LogType type)
6969
/// </summary>
7070
public void Finalise()
7171
{
72-
string filepath = Path.Combine(session.path, "log.csv");
73-
fileIOManager.ManageInWorker(() => fileIOManager.WriteCSV(table, filepath));
72+
WriteFileInfo fileInfo = new WriteFileInfo(
73+
WriteFileType.Log,
74+
session.basePath,
75+
session.experimentName,
76+
session.ppid,
77+
session.folderName,
78+
"log.csv"
79+
);
80+
81+
fileIOManager.ManageInWorker(() => fileIOManager.WriteCSV(table, fileInfo));
7482
Application.logMessageReceived -= HandleLog;
7583
}
7684

0 commit comments

Comments
 (0)