An easy-to-use platform for EEG experimentation in the classroom
1import React, { useCallback, useState } from 'react';
2import { Button } from '../ui/button';
3import { Link } from 'react-router-dom';
4import InputCollect from '../InputCollect';
5import { injectEmotivMarker } from '../../utils/eeg/emotiv';
6import { injectMuseMarker } from '../../utils/eeg/muse';
7import { EXPERIMENTS, DEVICES } from '../../constants/constants';
8import { ExperimentWindow } from '../ExperimentWindow';
9import { checkFileExists, getImages } from '../../utils/filesystem/storage';
10import {
11 ExperimentParameters,
12 ExperimentObject,
13} from '../../constants/interfaces';
14import { ExperimentActions as globalExperimentActions } from '../../actions';
15
16interface Props {
17 type: EXPERIMENTS;
18 title: string;
19 isRunning: boolean;
20 params: ExperimentParameters;
21 subject: string;
22 experimentObject: ExperimentObject;
23 group: string;
24 session: number;
25 deviceType: DEVICES;
26 isEEGEnabled: boolean;
27 ExperimentActions: typeof globalExperimentActions;
28}
29
30const Run: React.FC<Props> = ({
31 type,
32 title,
33 isRunning,
34 params,
35 subject,
36 experimentObject,
37 group,
38 session,
39 deviceType,
40 isEEGEnabled,
41 ExperimentActions,
42}) => {
43 const [isInputCollectOpen, setIsInputCollectOpen] = useState(
44 subject.length === 0
45 );
46
47 const handleStartExperiment = useCallback(async () => {
48 const filename = `${subject}-${group}-${session}-behavior.csv`;
49 const fileExists = await checkFileExists(title, subject, filename);
50 if (fileExists) {
51 const options = {
52 buttons: ['No', 'Yes'],
53 message:
54 'You already have a file with the same name. If you continue the experiment, the current file will be deleted. Do you really want to overwrite the data?',
55 };
56 const response = await window.electronAPI.showMessageBox(options);
57 if (response.response === 1) {
58 ExperimentActions.Start();
59 }
60 } else {
61 ExperimentActions.Start();
62 }
63 }, [subject, group, session, title, ExperimentActions]);
64
65 const handleCloseInputCollect = useCallback(
66 (newSubject: string, newGroup: string, newSession: number) => {
67 ExperimentActions.SetSubject(newSubject);
68 ExperimentActions.SetGroup(newGroup);
69 ExperimentActions.SetSession(newSession);
70 setIsInputCollectOpen(false);
71 },
72 [ExperimentActions]
73 );
74
75 const eventCallback = useCallback(
76 (event: string, time: number) => {
77 if (isEEGEnabled) {
78 if (deviceType === 'MUSE') {
79 injectMuseMarker(event, time);
80 } else {
81 injectEmotivMarker(event, time);
82 }
83 }
84 },
85 [isEEGEnabled, deviceType]
86 );
87
88 const onFinish = useCallback(
89 (csv) => {
90 ExperimentActions.Stop({ data: csv });
91 },
92 [ExperimentActions]
93 );
94
95 return (
96 <div
97 className="h-screen p-[3%] bg-gradient-to-b from-[#f9f9f9] to-[#f0f0ff]"
98 data-tid="container"
99 >
100 <div className="h-full">
101 {!isRunning && (
102 <div className="h-screen p-[3%] bg-gradient-to-b from-[#f9f9f9] to-[#f0f0ff]">
103 <div className="text-left">
104 <h1>{title}</h1>
105 <button
106 className="flex justify-end w-full"
107 onClick={() => setIsInputCollectOpen(true)}
108 aria-label="Edit"
109 >
110 ✏
111 </button>
112 <div>
113 Subject ID: <b>{subject}</b>
114 </div>
115 <div>
116 Group Name: <b>{group}</b>
117 </div>
118 <div>
119 Session Number: <b>{session}</b>
120 </div>
121 <div className="mt-6">
122 <Button
123 variant="default"
124 className="w-full"
125 onClick={handleStartExperiment}
126 disabled={!subject}
127 >
128 Run Experiment
129 </Button>
130 </div>
131 </div>
132 </div>
133 )}
134
135 {isRunning && (
136 <div className="h-full w-full">
137 <ExperimentWindow
138 title={title}
139 experimentObject={experimentObject}
140 params={params}
141 eventCallback={eventCallback}
142 onFinish={onFinish}
143 />
144 </div>
145 )}
146 </div>
147 <InputCollect
148 open={isInputCollectOpen}
149 onClose={handleCloseInputCollect}
150 onExit={() => setIsInputCollectOpen(false)}
151 header="Enter Data"
152 data={{ subject, group, session }}
153 />
154 </div>
155 );
156};
157
158export default Run;