Skip to content

COR-5318: update export feature for csharp and some readme #233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 21, 2025
Merged
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
5 changes: 3 additions & 2 deletions csharp/CortexAccess/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ static class Config
* account on emotiv.com and create a Cortex app.
* https://www.emotiv.com/my-account/cortex-apps/
*/
public static string AppClientId = "The client id of your Cortex app goes here";
public static string AppClientSecret = "The client secret of your Cortex app goes here";
public static string AppClientId = "put_your_application_client_id_here";
public static string AppClientSecret = "put_your_application_client_secret_here";

// If you use an Epoc Flex headset, then you must put your configuration here
public static string FlexMapping = @"{
Expand Down Expand Up @@ -38,6 +38,7 @@ public static class WarningCode
public const int UserLoginOnAnotherOsUser = 16;
public const int EULAAccepted = 17;
public const int StreamWritingClosed = 18;
public const int DataPostProcessingFinished = 30;
public const int HeadsetWrongInformation = 100;
public const int HeadsetCannotConnected = 101;
public const int HeadsetConnectingTimeout = 102;
Expand Down
140 changes: 94 additions & 46 deletions csharp/CortexAccess/CtxClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public sealed class CortexClient
public event EventHandler<Record> OnUpdateRecord;
public event EventHandler<List<Record>> OnQueryRecords;
public event EventHandler<MultipleResultEventArgs> OnDeleteRecords;
public event EventHandler<MultipleResultEventArgs> ExportRecordsFinished;
public event EventHandler<JObject> OnInjectMarker;
public event EventHandler<JObject> OnUpdateMarker;
public event EventHandler<JObject> OnGetDetectionInfo;
Expand All @@ -133,6 +134,7 @@ public sealed class CortexClient
public event EventHandler<string> SessionClosedNotify;
public event EventHandler<HeadsetConnectEventArgs> HeadsetConnectNotify;
public event EventHandler<string> HeadsetScanFinished;
public event EventHandler<string> DataPostProcessingFinished;

// Constructor
static CortexClient()
Expand Down Expand Up @@ -425,72 +427,72 @@ private void HandleResponse(string method, JToken data)
{
OnGetTrainingTime(this, (double)data["time"]);
}
// export record
else if (method == "exportRecord")
{
JArray successList = (JArray)data["success"];
JArray failList = (JArray)data["failure"];
ExportRecordsFinished(this, new MultipleResultEventArgs(successList, failList));
}

}

// handle warning response
private void HandleWarning(int code, JToken messageData)
{
Console.WriteLine("handleWarning: " + code);
if (code == WarningCode.AccessRightGranted)
switch (code)
{
// granted access right
case WarningCode.AccessRightGranted:
OnAccessRightGranted(this, true);
}
else if (code == WarningCode.AccessRightRejected)
{
break;
case WarningCode.AccessRightRejected:
OnAccessRightGranted(this, false);
}
else if (code == WarningCode.EULAAccepted)
{
break;
case WarningCode.EULAAccepted:
OnEULAAccepted(this, true);
}
else if (code == WarningCode.UserLogin)
{
string message = messageData.ToString();
OnUserLogin(this, message);
}
else if (code == WarningCode.UserLogout)
{
string message = messageData.ToString();
OnUserLogout(this, message);
}
else if (code == WarningCode.HeadsetScanFinished)
{
string message = messageData["behavior"].ToString();
HeadsetScanFinished(this, message);
}
else if (code == WarningCode.StreamStop)
{
string sessionId = messageData["sessionId"].ToString();
SessionClosedNotify(this, sessionId);
}
else if (code == WarningCode.SessionAutoClosed)
{
string sessionId = messageData["sessionId"].ToString();
SessionClosedNotify(this, sessionId);
}
else if (code == WarningCode.HeadsetConnected)
{
break;
case WarningCode.UserLogin:
OnUserLogin(this, messageData.ToString());
break;
case WarningCode.UserLogout:
OnUserLogout(this, messageData.ToString());
break;
case WarningCode.HeadsetScanFinished:
HeadsetScanFinished(this, messageData["behavior"].ToString());
break;
case WarningCode.StreamStop:
case WarningCode.SessionAutoClosed:
SessionClosedNotify(this, messageData["sessionId"].ToString());
break;
case WarningCode.HeadsetConnected:
{
string headsetId = messageData["headsetId"].ToString();
string message = messageData["behavior"].ToString();
Console.WriteLine("handleWarning:" + message);
HeadsetConnectNotify(this, new HeadsetConnectEventArgs(true, message, headsetId));
}
else if (code == WarningCode.HeadsetWrongInformation ||
code == WarningCode.HeadsetCannotConnected ||
code == WarningCode.HeadsetConnectingTimeout)
{
break;
}
case WarningCode.HeadsetWrongInformation:
case WarningCode.HeadsetCannotConnected:
case WarningCode.HeadsetConnectingTimeout:
{
string headsetId = messageData["headsetId"].ToString();
string message = messageData["behavior"].ToString();
HeadsetConnectNotify(this, new HeadsetConnectEventArgs(false, message, headsetId));
}
else if (code == WarningCode.CortexAutoUnloadProfile)
{
// the current profile is unloaded automatically
break;
}
case WarningCode.CortexAutoUnloadProfile:
OnUnloadProfile(this, true);
break;
case WarningCode.DataPostProcessingFinished:
Console.WriteLine("Data post processing finished");
DataPostProcessingFinished(this, messageData["recordId"].ToString());
break;
default:
Console.WriteLine("Warning code: " + code + ", message: " + messageData.ToString());
break;
}

}
private void WebSocketClient_Closed(object sender, EventArgs e)
{
Expand Down Expand Up @@ -726,6 +728,52 @@ public void DeleteRecord(string cortexToken, List<string> records)
SendTextMessage(param, "deleteRecord", true);
}

// Export Records
// Required params: cortexToken, records, folderPath, streamTypes, format
public void ExportRecord(string cortexToken, List<string> records, string folderPath,
List<string> streamTypes, string format, string version = null,
List<string> licenseIds = null, bool includeDemographics = false,
bool includeMarkerExtraInfos = false, bool includeSurvey = false,
bool includeDeprecatedPM = false)
{
JObject param = new JObject();
param.Add("recordIds", JArray.FromObject(records));
param.Add("cortexToken", cortexToken);
param.Add("folder", folderPath);
param.Add("streamTypes", JArray.FromObject(streamTypes));
param.Add("format", format); // EDF, CSV, EDFPLUS, BDFPLUS

// If the format is "EDF", then you must omit version parameter.
// If the format is "CSV", then version parameter must be "V1" or "V2".
if (version != null)
{
param.Add("version", version);
}
if (licenseIds != null)
{
param.Add("licenseIds", JArray.FromObject(licenseIds));
}

if (includeDemographics)
{
param.Add("includeDemographics", includeDemographics);
}
if (includeMarkerExtraInfos)
{
param.Add("includeMarkerExtraInfos", includeMarkerExtraInfos);
}
if (includeSurvey)
{
param.Add("includeSurvey", includeSurvey);
}
if (includeDeprecatedPM)
{
param.Add("includeDeprecatedPM", includeDeprecatedPM);
}

SendTextMessage(param, "exportRecord", true);
}

// InjectMarker
// Required params: session, cortexToken, label, value, time
public void InjectMarker(string cortexToken, string sessionId, string label, JToken value, double time, string port = null)
Expand Down
20 changes: 17 additions & 3 deletions csharp/CortexAccess/DataStreamExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,23 +194,37 @@ private void MessageErrorRecieved(object sender, ErrorMsgEventArgs e)
Console.WriteLine("MessageErrorRecieved :code " + e.Code + " message " + e.MessageError);
}

// set Streams
/// <summary>
/// Add a data stream to the subscription list.
/// Call this before Start().
/// Example: AddStreams("eeg"), AddStreams("mot"), etc.
/// </summary>
/// <param name="stream">Stream name (e.g., "eeg", "mot", "pow", "met")</param>
public void AddStreams(string stream)
{
if (!_streams.Contains(stream))
{
_streams.Add(stream);
}
}
// start
/// <summary>
/// Start the workflow: authorization, headset finding, session creation, and data stream subscription.
/// You must call AddStreams() for each stream you want to subscribe before calling Start().
/// </summary>
/// <param name="licenseID">(Obsolete) This parameter is kept for backward compatibility. Always set to empty string "".</param>
/// <param name="activeSession">True to use a license-required session (needed for EEG, high performance metrics, etc.)</param>
/// <param name="wantedHeadsetId">Specify headset ID to connect (default: first headset found)</param>
public void Start(string licenseID="", bool activeSession = false, string wantedHeadsetId = "")
{
_wantedHeadsetId = wantedHeadsetId;
_isActiveSession = activeSession;
_authorizer.Start(licenseID);
}

// Unsubscribe
/// <summary>
/// Unsubscribe from data streams. If no streams are specified, unsubscribes from all currently subscribed streams.
/// </summary>
/// <param name="streams">List of stream names to unsubscribe (optional)</param>
public void UnSubscribe(List<string> streams = null)
{
if (streams == null)
Expand Down
4 changes: 4 additions & 0 deletions csharp/CortexAccess/HeadsetFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

namespace CortexAccess
{
/// <summary>
/// HeadsetFinder is a utility class for scanning, querying, and connecting to Emotiv headsets.
/// Use FindHeadset(headsetId) to start a timer to query headset and set desired headset. The class starts a timer to repeatedly query headsets until a connection is established.
/// </summary>
public class HeadsetFinder
{
private CortexClient _ctxClient;
Expand Down
Loading
Loading