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
18 changes: 11 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -351,12 +351,16 @@ else (HDF5_EXPORT)
set (ELEMEM_DEFINES ${ELEMEM_DEFINES} NO_HDF5)
endif (HDF5_EXPORT)

find_library (CBSDK_LIB "cbsdk" PATHS ${PROJECT_SOURCE_DIR}/lib64)
if (NOT CBSDK_LIB)
message (FATAL_ERROR
"cbsdk library missing from ${PROJECT_SOURCE_DIR}/lib64"
)
endif ()
if (WIN32)
find_library (CBSDK_LIB "cbsdk" PATHS ${PROJECT_SOURCE_DIR}/lib64)
if (NOT CBSDK_LIB)
message (FATAL_ERROR
"cbsdk library missing from ${PROJECT_SOURCE_DIR}/lib64"
)
endif ()
else (WIN32)
set(CBSDK_LIB "")
endif (WIN32)

if (WIN32)
find_library (CSTIM_LIB "CereStimDLL" PATHS ${PROJECT_SOURCE_DIR}/dll)
Expand All @@ -375,7 +379,7 @@ else (WIN32)
find_package(PkgConfig REQUIRED)
pkg_search_module(FFTW REQUIRED fftw3 IMPORTED_TARGET)
include_directories(${FFTW_INCLUDE_DIRS})
set(FFTW_LIB ${FFTW_LIBRARIES})
set(FFTW_LIB "-L${FFTW_LIBRARY_DIRS} -l${FFTW_LIBRARIES}")
endif (WIN32)


Expand Down
111 changes: 59 additions & 52 deletions docs/NetworkProtocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ Key

The key information for understanding the protocol below.

* Each message sent from the task side has a numerically increasing id number (unsigned 64-bit)
* Each message sent from the task side has a numerically increasing id number (unsigned 64-bit).

* Host (Elemem) responses should carry the id of the message they are responding to
* Host (Elemem) responses should carry the id of the message they are responding to.

* The StatusPanel flag indicates that it changes the status panel
* The StatusPanel flag indicates that it changes the status panel.

* Anything in angle brackets <> is a placeholder for a value. The word inside of them names the type of the value.

=============
Required Messages
Expand All @@ -49,23 +51,23 @@ Required Messages
These messages are required for Elemem to function

* CONNECTED:
* Message: {type”: “CONNECTED”, “id”: 42, time: <float>}
* Response: {type”: “CONNECTED_OK”, “id”: 42, time: <float>}
* Message: {"type": "CONNECTED", "id": 42, "time": <float>}
* Response: {"type": "CONNECTED_OK", "id": 42, "time": <float>}

* CONFIGURE:
* Message: {type”: “CONFIGURE”, “data: {stim_mode”: “open”, “experiment”: “RepFR2”, “subject”: “R1999J}, “id”: 42, time: <float>}
* Response Ok: {type”: “CONFIGURE_OK”, “id”: 42, time: <float>}
* Response Error: {type”: “CONFIGURE_ERROR”, “data: {error”: “What went wrong}, “id”: 42, time: <float>}
* Note: Experiments using STIMSELECT tags should include optional data entry tags: [<string>, <string>, ...]
* Message: {"type": "CONFIGURE", "data": {"stim_mode": "open", "experiment": "RepFR2", "subject": "R1999J"}, "id": 42, "time": <float>}
* Response Ok: {"type": "CONFIGURE_OK", "id": 42, "time": <float>}
* Response Error: {"type": "CONFIGURE_ERROR", "data": {"error": "What went wrong"}, "id": 42, "time": <float>}
* Note: Experiments using STIMSELECT tags should include optional data entry "tags": [<string>, <string>, ...]

* READY:
* Message: {type”: “READY”, “data: {}, “id”: 42, time: <float>}
* Response: {type”: “START”, “data: {}, “id”: 42, time: <float>}
* Message: {"type": "READY", "data": {}, "id": 42, "time": <float>}
* Response: {"type": "START", "data": {}, "id": 42, "time": <float>}

* HEARTBEAT:
* Message: {type”: “HEARTBEAT”, “data: {count: 27}, “id”: 42, time: <float>}
* Response: {type”: “HEARTBEAT_OK”, “data: {count: 27}, “id”: 42, time: <float>}
* Note: After CONFIGURE_OK received, 20 HEARTBEATs sent at 50ms intervals. Task side logs calculated average and max latency, and raises notification window if max greater than 20ms. data”: “count increases with each heartbeat. Sent once per second. 8 missed responses stops experiment.
* Message: {"type": "HEARTBEAT", "data": {"count": 27}, "id": 42, "time": <float>}
* Response: {"type": "HEARTBEAT_OK", "data": {"count": 27}, "id": 42, "time": <float>}
* Note: After CONFIGURE_OK received, 20 HEARTBEATs sent at 50ms intervals. Task side logs calculated average and max latency, and raises notification window if max greater than 20ms. "data": "count" increases with each heartbeat. Sent once per second. 8 missed responses stops experiment.

=============
Handled Messages
Expand All @@ -74,112 +76,117 @@ Handled Messages
These are messages that Elemem does something as a result of receiving them.

* EXIT:
* Message: {type”: “EXIT”, “data: {}, “id”: 42, time: <float>}
* Message: {"type": "EXIT", "data": {}, "id": 42, "time": <float>}
* Response: None
* Purpose: Used to end the session

* SESSION:
* Message: {"type": "SESSION", "data": {"session": [int]}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* TRIAL:
* Message: {type”: “TRIAL”, “data: {trial: [int], stim:[bool]}, “id”: 42, time: <float>}
* Message: {"type": "TRIAL", "data": {"trial": [int], "stim":[bool]}, "id": 42, "time": <float>}
* Response: None
* Purpose: Indicate which trial number you're on

* TRIALEND:
* Message: {type”: “TRIALEND”, “data: {}, “id”: 42, time: <float>}
* Message: {"type": "TRIALEND", "data": {}, "id": 42, "time": <float>}
* Reponse: None
* Purpose: Indicates the end of a trial

* STIMSELECT:
* Message: {type”: “STIMSELECT”, “data: {tag: <string>}, “id”: 42, time: <float>}
* Message: {"type": "STIMSELECT", "data": {"tag": <string>}, "id": 42, "time": <float>}
* Response: None
* Purpose: Selects the pre-approved stim configuration matching the tag for subsequent stim events.

* STIM:
* Message: {type”: “STIM”, “data: {}, “id”: 42, time: <float>}
* Message: {"type": "STIM", "data": {}, "id": 42, "time": <float>}
* Response: None
* Purpose: This triggers one open-loop stim event. Synchronized stimulation during word presentation can instead be triggered by the WORD event with data”:{“stim:true}.
* Purpose: This triggers one open-loop stim event. Synchronized stimulation during word presentation can instead be triggered by the WORD event with "data":{"stim":true}.

* CLSTIM:
* Message: {type”: “CLSTIM”, “data: {classifyms: 1366}, “id”: 42, time: <float>}
* Message: {"type": "CLSTIM", "data": {"classifyms": 1366}, "id": 42, "time": <float>}
* Response: None
* Purpose: This initiates a closed-loop classification epoch for the duration in milliseconds specified by classifyms. Stimulation is initiated following this duration as soon as processing is completed if the classification result is below the threshold, typically 0.5.

* CLSHAM:
* Message: {type”: “CLSHAM”, “data: {classifyms: 1366}, “id”: 42, time: <float>}
* Message: {"type": "CLSHAM", "data": {"classifyms": 1366}, "id": 42, "time": <float>}
* Response: None
* Purpose: This initiates a closed-loop classification epoch for the duration in milliseconds specified by classifyms. This is identical to CLSTIM except that no stimulation is performed, and instead an event is simply logged reporting whether or not stim would have been performed.

* CLNORMALIZE:
* Message: {type”: “CLNORMALIZE”, “data: { classifyms: 1366}, “id”: 42, time: <float>}
* Message: {"type": "CLNORMALIZE", "data": {" classifyms": 1366}, "id": 42, "time": <float>}
* Response: None
* Purpose: This initiates a closed-loop normalization update epoch for the duration in milliseconds specified by classifyms.

* WORD:
* Message: {type”: “WORD”, “data: {word: <string>, serialpos: [int], stim:[bool]}, “id”: 42, time: <float>}
* Message: {"type": "WORD", "data": {"word": <string>, "serialpos": [int], "stim":[bool]}, "id": 42, "time": <float>}
* Response: None
* Purpose: This can initiate a stimulation event if the "stim" field is set to true
* Note: The "word" and "serialpos" fields are optional, but should be set if available
* StatusPanel

* SESSION:
* Message: {“type”: “SESSION”, “data”: {“session”: [int]}, “id”: 42, “time”: <float>}
* Response: None
* StatusPanel
* TASK_STATUS:
* Message: {"type": "WORD", "data": {"status": <string>}, "id": 42, "time": <float>}

* REST:
* Message: {type”: “REST”, “data: {}, “id”: 42, time: <float>}
* [DEPRECATED] REST:
* Message: {"type": "REST", "data": {}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* ORIENT (Orientation Cross):
* Message: {type”: “ORIENT”, “data: {}, “id”: 42, time: <float>}
* [DEPRECATED] ORIENT (Orientation Cross):
* Message: {"type": "ORIENT", "data": {}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* COUNTDOWN:
* Message: {type”: “COUNTDOWN”, “data: {}, “id”: 42, time: <float>}
* [DEPRECATED] COUNTDOWN:
* Message: {"type": "COUNTDOWN", "data": {}, "id": 42, "time": <float>}
* Response: None
* StatusPanel
* DISTRACT:
* Message: {type”: “DISTRACT”, “data: {}, “id”: 42, time: <float>}
* [DEPRECATED] DISTRACT:
* Message: {"type": "DISTRACT", "data": {}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* RECALL:
* Message: {type”: “RECALL”, “data: {duration: <float>}, “id”: 42, time: <float>}
* [DEPRECATED] RECALL:
* Message: {"type": "RECALL", "data": {"duration": <float>}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* INSTRUCT:
* Message: {type”: “INSTRUCT”, “data: {}, “id”: 42, time: <float>}
* [DEPRECATED] INSTRUCT:
* Message: {"type": "INSTRUCT", "data": {}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* MATH:
* Message: {type”: “MATH”, “data: {problem: <string>, response: <string>, response_time_ms: [int], correct: [bool]}, “id”: 42, time: <float>}
* [DEPRECATED] MATH:
* Message: {"type": "MATH", "data": {"problem": <string>, "response": <string>, "response_time_ms": [int], "correct": [bool]}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* SYNC:
* Message: {type”: “SYNC”, “data: {}, “id”: 42, time: <float>}
* [DEPRECATED] SYNC:
* Message: {"type": "SYNC", "data": {}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* [NOT IMPLEMENTED] WAITING:
* Message: {type”: “WAITING”, “data: {}, “id”: 42, time: <float>}
* Message: {"type": "WAITING", "data": {}, "id": 42, "time": <float>}
* Response: None
* Note: Used when waiting on user input
* StatusPanel

* [NOT IMPLEMENTED] ISI (Inter-Stimulus Interval):
* Message: {type”: “ISI”, “data: {duration: <float>}, “id”: 42, time: <float>}
* Message: {"type": "ISI", "data": {"duration": <float>}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* [NOT IMPLEMENTED] VOCALIZATION:
* Message: {type”: “VOCALIZATION”, “data: {}, “id”: 42, time: <float>}
* Message: {"type": "VOCALIZATION", "data": {}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

* [NOT IMPLEMENTED] RECALL:
* Message: {type”: “RECALL”, “data: {}, “id”: 42, time: <float>}
* Message: {"type": "RECALL", "data": {}, "id": 42, "time": <float>}
* Response: None
* StatusPanel

Expand All @@ -196,14 +203,14 @@ Logged Events
These are the events that are logged.

* ELEMEM:
* Message: {type”: “ELEMEM”, “data: {version: <string>}, “id”: 0, time: <float>}
* Message: {"type": "ELEMEM", "data": {"version": <string>}, "id": 0, "id": 42, "time": <float>}
* Note: version is the date time string corresponding to the build time, and matches the version displayed under Help/About inside of Elemem.

* STIMMING:
* Message: {type”: “STIMMING”, “data: {electrode_pos: [uint], electrode_neg: [uint], amplitude: <float>, frequency: <float>, duration: <float>}, time: <float>}
* Message: {"type": "STIMMING", "data": {"electrode_pos": [uint], "electrode_neg": [uint], "amplitude": <float>, "frequency": <float>, "duration": <float>}, "id": 42, "time": <float>}
* Note: electrode_pos and electrode_neg are integer channel numbers, 0 indexed. Units for the other values are amplitude:uA, frequency:Hz, duration:us.

* EEGSTART:
* Message: {type”: “EEGSTART”, “data: {sub_dir: <string>}, “id”: 0, time: <float>}
* Note: sub_dir is the session directory name on Elemem (without full path information), for example, R1999J_2021-06-14_15-47-29. The time value from this is for converting the Elemem system time to the EEG file offsets.
* Message: {"type": "EEGSTART", "data": {"sub_dir": <string>}, "id": 0, "id": 42, "time": <float>}
* Note: sub_dir is the session directory name on Elemem (without full path information), for example, "R1999J_2021-06-14_15-47-29". The time value from this is for converting the Elemem system time to the EEG file offsets.

9 changes: 9 additions & 0 deletions src/TaskNetWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,17 @@ namespace CML {

std::string type;
uint64_t id = uint64_t(-1);
double time = -1;
if (!inp.TryGet(type, "type")) {
// Not a command. Ignore.
return;
}
if (!inp.TryGet(id, "id")) {
// Leave as uint64_t(-1) to disable.
}
if (!inp.TryGet(time, "time")) {
inp.Set(time, "task_time");
}

inp.Set(Time::Get()*1e3, "time");
hndl->event_log.Log(inp.Line());
Expand Down Expand Up @@ -136,6 +140,11 @@ namespace CML {
status_panel->SetEvent(type);
hndl->ExperimentExit();
}
else if (type == "TASK_STATUS") {
RC::RStr status;
inp.Get(status, "data", "status");
status_panel->SetEvent(status);
}
else {
if (type ==
RC::OneOf("ORIENT", "COUNTDOWN", "DISTRACT", "RECALL", "REST",
Expand Down