Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 143 additions & 55 deletions unity/Assets/SimpleExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using UnityEngine.UI;
using System;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

#if UNITY_ANDROID
using UnityEngine.Android;
Expand All @@ -16,6 +17,7 @@ public class SimpleExample : MonoBehaviour
EmotivUnityItf _eItf = EmotivUnityItf.Instance;
float _timerDataUpdate = 0;
const float TIME_UPDATE_DATA = 1f;
private string _lastMessageLog = ""; // tracks last displayed _eItf.MessageLog to avoid overwriting manual MessageLog updates

[SerializeField] public InputField HeadsetId; // headsetId
[SerializeField] public InputField RecordTitle; // record Title
Expand All @@ -36,16 +38,29 @@ public class SimpleExample : MonoBehaviour
[SerializeField] public Toggle FEToggle;
[SerializeField] public Toggle SYSToggle;

[SerializeField] public Text MessageLog;

// Enable horizontal wrapping for MessageLog
private void Awake()
{
if (MessageLog != null)
{
MessageLog.horizontalOverflow = HorizontalWrapMode.Wrap;
}
}
[SerializeField] public InputField MessageLog;
// recordid for export record
[SerializeField] public InputField RecordId; // record id for export record

// cached button references, populated once in Start() to avoid repeated GameObject.Find calls in Update()
private Button _signInBtn;
private Button _signOutBtn;
private Button _createSessionBtn;
private Button _queryHeadsetBtn;
private Button _startRecordBtn;
private Button _subscribeBtn;
private Button _unsubscribeBtn;
private Button _loadProfileBtn;
private Button _unloadProfileBtn;
private Button _saveProfileBtn;
private Button _rejectTrainingBtn;
private Button _eraseTrainingBtn;
private Button _acceptTrainingBtn;
private Button _startTrainingBtn;
private Button _stopRecordBtn;
private Button _injectMarkerBtn;
private Button _exportRecordBtn;
private Button _queryRecordBtn;

// for android
#if UNITY_ANDROID
Expand Down Expand Up @@ -146,7 +161,8 @@ private static void RequestPermission(string permissionName) {

void Start()
{

CacheButtonReferences();

#if USE_EMBEDDED_LIB && UNITY_STANDALONE_WIN && !UNITY_EDITOR
string[] args = Environment.GetCommandLineArgs();
if (args.Length > 1)
Expand Down Expand Up @@ -188,8 +204,12 @@ void Update()
// Check buttons interactable
CheckButtonsInteractable();

// Display message log
MessageLog.text = _eItf.MessageLog;
// Display message log only when it has changed
if (_eItf.MessageLog != _lastMessageLog)
{
_lastMessageLog = _eItf.MessageLog;
MessageLog.text = _eItf.MessageLog;
}

// Demo how to get detected headset lists
// List<Headset> detectedHeadsets = _eItf.GetDetectedHeadsets();
Expand Down Expand Up @@ -280,13 +300,13 @@ public void onStopRecordBtnClick() {
}

// export record to desktop
public void onExportRecordBtnClick()
public async void onExportRecordBtnClick()
{
Debug.Log("onExportRecordBtnClick");
string _recordId = _eItf.RecentRecord?.Uuid;
string _recordId = RecordId.text;
if (string.IsNullOrEmpty(_recordId))
{
UnityEngine.Debug.Log("The recordId is empty. Please export with a valid recordId.");
UnityEngine.Debug.LogError("The recordId must not be empty. Please export with a valid recordId.");
return;
}

Expand All @@ -308,9 +328,73 @@ public void onExportRecordBtnClick()
List<string> streamTypes = new List<string> { "EEG", "MOTION" }; // Specify the stream types you want to export
string format = "CSV"; // or "CSV", "EDFPLUS", "BDFPLUS"
string version = "V2"; // Optional, specify if needed
_eItf.ExportRecord(recordsToExport, folderPath, streamTypes, format, version);

try
{
ExportRecordResult result = await _eItf.ExportRecordAsync(recordsToExport, folderPath, streamTypes, format, version);

string msgLog = "Export records - success: " + result.SuccessRecordIds.Count + ", failed: " + result.FailedRecords.Count + "\n";
foreach (string recordId in result.SuccessRecordIds)
{
msgLog += "Success: " + recordId + "\n";
}
foreach (ExportRecordFailure failure in result.FailedRecords)
{
msgLog += "Failed: " + failure.RecordId + " (code " + failure.Code + "): " + failure.Message + "\n";
}
MessageLog.text = msgLog;
}
catch (Exception ex)
{
UnityEngine.Debug.LogError("onExportRecordBtnClick failed: " + ex.Message);
MessageLog.text = "Export records failed: " + ex.Message;
}
}

// query records with a startDatetime range
public async void onQueryRecordBtnClick()
{
Debug.Log("onQueryRecordBtnClick");
// demo to query with startDatetime range, the from and to are in ISO 8601 format
JObject query = new JObject(
new JProperty("startDatetime", new JObject(
new JProperty("from", "2026-01-06T16:32:50.572490+07:00"),
new JProperty("to", "2026-08-06T16:32:50.572490+07:00")
))
);

// demo to query with keywords which can be record title or record note
// JObject query = new JObject(
// new JProperty("keyword", "123abc")
// );

// If you want to query records of owner but not created by the application,
// you can set the licenseId to filter the records.
// If you set the licenseId, then you can set this parameter to further filter the records by application.
// JObject query = new JObject(
// new JProperty("applicationId", "com.emotivid.appname"),
// new JProperty("licenseId", "xxx--yyy")
// );

try
{
List<Record> records = await _eItf.QueryRecords(query, null, 100, 0, true, true);

string msgLog = "Query records: found " + records.Count + " record(s).\n";
foreach (Record record in records)
{
msgLog += "RecordId: " + record.Uuid + ", Title: " + record.Title + ", StartTime: " + record.StartDateTime +
", EndTime: " + record.EndDateTime + ", SyncStatus: " + record.SyncStatus + "\n";
}
MessageLog.text = msgLog;
}
catch (Exception ex)
{
UnityEngine.Debug.LogError("onQueryRecordBtnClick failed: " + ex.Message);
MessageLog.text = "Query records failed: " + ex.Message;
}
}

public void onInjectMarkerBtnClick()
{
Debug.Log("onInjectMarkerBtnClick " + MarkerValue.text + ":" + MarkerLabel.text);
Expand Down Expand Up @@ -417,51 +501,55 @@ void OnApplicationQuit()
_eItf.Stop();
}

private void CacheButtonReferences()
{
_signInBtn = GameObject.Find("AuthenPart").transform.Find("signInBtn").GetComponent<Button>();
_signOutBtn = GameObject.Find("AuthenPart").transform.Find("signOutBtn").GetComponent<Button>();
_createSessionBtn = GameObject.Find("SessionPart").transform.Find("createSessionBtn").GetComponent<Button>();
_queryHeadsetBtn = GameObject.Find("SessionPart").transform.Find("queryHeadset").GetComponent<Button>();
_startRecordBtn = GameObject.Find("RecordPart").transform.Find("startRecordBtn").GetComponent<Button>();
_subscribeBtn = GameObject.Find("SubscribeDataPart").transform.Find("subscribeBtn").GetComponent<Button>();
_unsubscribeBtn = GameObject.Find("SubscribeDataPart").transform.Find("unsubscribeBtn").GetComponent<Button>();
_loadProfileBtn = GameObject.Find("TrainingPart").transform.Find("loadProfileBtn").GetComponent<Button>();
_unloadProfileBtn = GameObject.Find("TrainingPart").transform.Find("unloadProfileBtn").GetComponent<Button>();
_saveProfileBtn = GameObject.Find("TrainingPart").transform.Find("saveProfileBtn").GetComponent<Button>();
_rejectTrainingBtn = GameObject.Find("TrainingPart").transform.Find("rejectTrainingBtn").GetComponent<Button>();
_eraseTrainingBtn = GameObject.Find("TrainingPart").transform.Find("eraseTrainingBtn").GetComponent<Button>();
_acceptTrainingBtn = GameObject.Find("TrainingPart").transform.Find("acceptTrainingBtn").GetComponent<Button>();
_startTrainingBtn = GameObject.Find("TrainingPart").transform.Find("startTrainingBtn").GetComponent<Button>();
_stopRecordBtn = GameObject.Find("RecordPart").transform.Find("stopRecordBtn").GetComponent<Button>();
_injectMarkerBtn = GameObject.Find("RecordPart").transform.Find("injectMarkerBtn").GetComponent<Button>();
_exportRecordBtn = GameObject.Find("RecordPart").transform.Find("exportRecordBtn").GetComponent<Button>();
_queryRecordBtn = GameObject.Find("RecordPart").transform.Find("queryRecordBtn").GetComponent<Button>();
}

private void CheckButtonsInteractable()
{
Button signInBtn = GameObject.Find("AuthenPart").transform.Find("signInBtn").GetComponent<Button>();
Button signOutBtn = GameObject.Find("AuthenPart").transform.Find("signOutBtn").GetComponent<Button>();
#if USE_EMBEDDED_LIB || UNITY_ANDROID || UNITY_IOS
ConnectToCortexStates connectionState = _eItf.GetConnectToCortexState();
signInBtn.interactable = (connectionState == ConnectToCortexStates.Login_notYet);
signOutBtn.interactable = (connectionState > ConnectToCortexStates.Login_notYet);
_signInBtn.interactable = (connectionState == ConnectToCortexStates.Login_notYet);
_signOutBtn.interactable = (connectionState > ConnectToCortexStates.Login_notYet);
#else
signInBtn.interactable = false;
signOutBtn.interactable = false;
_signInBtn.interactable = false;
_signOutBtn.interactable = false;
#endif

Button createSessionBtn = GameObject.Find("SessionPart").transform.Find("createSessionBtn").GetComponent<Button>();
// query headset button
Button queryHeadsetBtn = GameObject.Find("SessionPart").transform.Find("queryHeadset").GetComponent<Button>();
Button startRecordBtn = GameObject.Find("RecordPart").transform.Find("startRecordBtn").GetComponent<Button>();
Button subscribeBtn = GameObject.Find("SubscribeDataPart").transform.Find("subscribeBtn").GetComponent<Button>();
Button unsubscribeBtn = GameObject.Find("SubscribeDataPart").transform.Find("unsubscribeBtn").GetComponent<Button>();
Button loadProfileBtn = GameObject.Find("TrainingPart").transform.Find("loadProfileBtn").GetComponent<Button>();
Button unloadProfileBtn = GameObject.Find("TrainingPart").transform.Find("unloadProfileBtn").GetComponent<Button>();
Button saveProfileBtn = GameObject.Find("TrainingPart").transform.Find("saveProfileBtn").GetComponent<Button>();
Button rejectTrainingBtn = GameObject.Find("TrainingPart").transform.Find("rejectTrainingBtn").GetComponent<Button>();
Button eraseTrainingBtn = GameObject.Find("TrainingPart").transform.Find("eraseTrainingBtn").GetComponent<Button>();
Button acceptTrainingBtn = GameObject.Find("TrainingPart").transform.Find("acceptTrainingBtn").GetComponent<Button>();
Button startTrainingBtn = GameObject.Find("TrainingPart").transform.Find("startTrainingBtn").GetComponent<Button>();
Button stopRecordBtn = GameObject.Find("RecordPart").transform.Find("stopRecordBtn").GetComponent<Button>();
Button injectMarkerBtn = GameObject.Find("RecordPart").transform.Find("injectMarkerBtn").GetComponent<Button>();
Button exportRecordBtn = GameObject.Find("RecordPart").transform.Find("exportRecordBtn").GetComponent<Button>();

createSessionBtn.interactable = _eItf.IsAuthorizedOK;
queryHeadsetBtn.interactable = _eItf.IsAuthorizedOK;
startRecordBtn.interactable = _eItf.IsSessionCreated;
stopRecordBtn.interactable = _eItf.IsRecording;
exportRecordBtn.interactable = _eItf.IsAuthorizedOK && !_eItf.IsRecording;
injectMarkerBtn.interactable = _eItf.IsRecording;
subscribeBtn.interactable = _eItf.IsSessionCreated;
unsubscribeBtn.interactable = _eItf.IsSessionCreated;
loadProfileBtn.interactable = _eItf.IsSessionCreated;
saveProfileBtn.interactable = _eItf.IsProfileLoaded;
startTrainingBtn.interactable = _eItf.IsProfileLoaded;
rejectTrainingBtn.interactable = _eItf.IsProfileLoaded;
eraseTrainingBtn.interactable = _eItf.IsProfileLoaded;
acceptTrainingBtn.interactable = _eItf.IsProfileLoaded;
unloadProfileBtn.interactable = _eItf.IsProfileLoaded;
_createSessionBtn.interactable = _eItf.IsAuthorizedOK;
_queryHeadsetBtn.interactable = _eItf.IsAuthorizedOK;
_startRecordBtn.interactable = _eItf.IsSessionCreated;
_stopRecordBtn.interactable = _eItf.IsRecording;
_exportRecordBtn.interactable = _eItf.IsAuthorizedOK && !_eItf.IsRecording;
_queryRecordBtn.interactable = _eItf.IsAuthorizedOK;
_injectMarkerBtn.interactable = _eItf.IsRecording;
_subscribeBtn.interactable = _eItf.IsSessionCreated;
_unsubscribeBtn.interactable = _eItf.IsSessionCreated;
_loadProfileBtn.interactable = _eItf.IsSessionCreated;
_saveProfileBtn.interactable = _eItf.IsProfileLoaded;
_startTrainingBtn.interactable = _eItf.IsProfileLoaded;
_rejectTrainingBtn.interactable = _eItf.IsProfileLoaded;
_eraseTrainingBtn.interactable = _eItf.IsProfileLoaded;
_acceptTrainingBtn.interactable = _eItf.IsProfileLoaded;
_unloadProfileBtn.interactable = _eItf.IsProfileLoaded;
}

private List<string> GetStreamsList() {
Expand Down
Loading
Loading