diff --git a/gui_dev/src/pages/SourceSelection/FileSelector.jsx b/gui_dev/src/pages/SourceSelection/FileSelector.jsx index 7f165597..ffa53bab 100644 --- a/gui_dev/src/pages/SourceSelection/FileSelector.jsx +++ b/gui_dev/src/pages/SourceSelection/FileSelector.jsx @@ -19,11 +19,12 @@ export const FileSelector = () => { ); const setSourceType = useSessionStore((state) => state.setSourceType); - const fileBrowserDirRef = useRef("C:\\code\\py_neuromodulation\\py_neuromodulation\\data\\sub-testsub\\ses-EphysMedOff\\ieeg\\sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr"); + const fileBrowserDirRef = useRef( + "C:\\code\\py_neuromodulation\\py_neuromodulation\\data\\sub-testsub\\ses-EphysMedOff\\ieeg\\sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr" + ); const [isSelecting, setIsSelecting] = useState(false); const [showFileBrowser, setShowFileBrowser] = useState(false); - const [showFolderBrowser, setShowFolderBrowser] = useState(false); useEffect(() => { setSourceType("lsl"); @@ -47,10 +48,6 @@ export const FileSelector = () => { } }; - const handleFolderSelect = (folder) => { - setShowFolderBrowser(false); - }; - return ( - {streamSetupMessage && ( { onSelect={handleFileSelect} /> )} - {showFolderBrowser && ( - setShowFolderBrowser(false)} - onSelect={handleFolderSelect} - onlyDirectories={true} - /> - )} ); }; diff --git a/gui_dev/src/pages/SourceSelection/SourceSelection.jsx b/gui_dev/src/pages/SourceSelection/SourceSelection.jsx index 87225811..1a19fcd9 100644 --- a/gui_dev/src/pages/SourceSelection/SourceSelection.jsx +++ b/gui_dev/src/pages/SourceSelection/SourceSelection.jsx @@ -1,8 +1,6 @@ import { Outlet } from "react-router-dom"; import { useEffect } from "react"; - import { Stack, Typography } from "@mui/material"; - import { useSessionStore, WorkflowStage } from "@/stores"; import { LinkButton } from "@/components/utils"; import { StreamParameters } from "./StreamParameters"; @@ -11,6 +9,9 @@ export const SourceSelection = () => { const setSourceType = useSessionStore((state) => state.setSourceType); const setWorkflowStage = useSessionStore((state) => state.setWorkflowStage); const isSourceValid = useSessionStore((state) => state.isSourceValid); + const sendStreamParametersToBackend = useSessionStore( + (state) => state.sendStreamParametersToBackend + ); useEffect(() => { setWorkflowStage(WorkflowStage.SOURCE_SELECTION); @@ -49,6 +50,7 @@ export const SourceSelection = () => { color="primary" to="/channels" disabled={!isSourceValid} + onClick={sendStreamParametersToBackend} > Select Channels diff --git a/gui_dev/src/pages/SourceSelection/StreamParameters.jsx b/gui_dev/src/pages/SourceSelection/StreamParameters.jsx index 8eff9578..e98d76e6 100644 --- a/gui_dev/src/pages/SourceSelection/StreamParameters.jsx +++ b/gui_dev/src/pages/SourceSelection/StreamParameters.jsx @@ -1,7 +1,9 @@ -import { TextField } from "@mui/material"; import { useSessionStore } from "@/stores"; import { TitledBox } from "@/components"; import { MyTextField } from "@/components/utils"; +import { Button } from "@mui/material"; +import { useState } from "react"; +import { FileBrowser } from "@/components"; export const StreamParameters = () => { const streamParameters = useSessionStore((state) => state.streamParameters); @@ -17,6 +19,13 @@ export const StreamParameters = () => { checkStreamParameters(); }; + const [showFolderBrowser, setShowFolderBrowser] = useState(false); + + const handleFolderSelect = (folder) => { + updateStreamParameter("outputDirectory", folder); + setShowFolderBrowser(false); + }; + return ( { value={streamParameters.experimentName} onChange={(event) => handleOnChange(event, "experimentName")} /> - handleOnChange(event, "outputDirectory")} - /> +
+ handleOnChange(event, "outputDirectory")} + style={{ flexGrow: 1 }} + /> + +
+ {showFolderBrowser && ( + setShowFolderBrowser(false)} + onSelect={handleFolderSelect} + onlyDirectories={true} + /> + )}
); }; diff --git a/gui_dev/src/stores/sessionStore.js b/gui_dev/src/stores/sessionStore.js index b5919562..f7039160 100644 --- a/gui_dev/src/stores/sessionStore.js +++ b/gui_dev/src/stores/sessionStore.js @@ -109,7 +109,10 @@ export const useSessionStore = createStore("session", (set, get) => ({ checkStreamParameters: () => { // const { samplingRate, lineNoise, samplingRateFeatures } = get(); set({ - areParametersValid: get().streamParameters.samplingRate && get().streamParameters.lineNoise && get().streamParameters.samplingRateFeatures, + areParametersValid: + get().streamParameters.samplingRate && + get().streamParameters.lineNoise && + get().streamParameters.samplingRateFeatures, }); }, @@ -130,8 +133,6 @@ export const useSessionStore = createStore("session", (set, get) => ({ file_path: get().fileSource.path, sampling_rate_features: get().streamParameters.samplingRateFeatures, line_noise: get().streamParameters.lineNoise, - experiment_name: get().streamParameters.experimentName, - out_dir: get().streamParameters.outputDirectory, }), }); @@ -170,10 +171,8 @@ export const useSessionStore = createStore("session", (set, get) => ({ }, body: JSON.stringify({ stream_name: lslSource.availableStreams[0].name, - sampling_rate_features: streamParameters.samplingRate, + sampling_rate_features: streamParameters.samplingRateFeatures, line_noise: streamParameters.lineNoise, - experiment_name: streamParameters.experimentName, - out_dir: streamParameters.outputDirectory, }), }); @@ -202,7 +201,38 @@ export const useSessionStore = createStore("session", (set, get) => ({ }); throw error; } - + }, + + sendStreamParametersToBackend: async () => { + const streamParameters = get().streamParameters; + + try { + const response = await fetch(getBackendURL("/api/set-stream-params"), { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + sampling_rate: streamParameters.samplingRate, + sampling_rate_features: streamParameters.samplingRateFeatures, + line_noise: streamParameters.lineNoise, + experiment_name: streamParameters.experimentName, + out_dir: streamParameters.outputDirectory, + }), + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + } catch (error) { + console.error("Error sendin stream params:", error); + set({ + streamSetupMessage: `Error: ${error.message}`, + isStreamSetupCorrect: false, + }); + throw error; + } }, /*****************************/ @@ -279,7 +309,7 @@ export const useSessionStore = createStore("session", (set, get) => ({ "Content-Type": "application/json", }, // This needs to be adapted depending on the backend changes - body: JSON.stringify({ "action" : "start"}), + body: JSON.stringify({ action: "start" }), }); if (!response.ok) { @@ -303,7 +333,7 @@ export const useSessionStore = createStore("session", (set, get) => ({ "Content-Type": "application/json", }, // This needs to be adapted depending on the backend changes - body: JSON.stringify({ "action" : "stop"}), + body: JSON.stringify({ action: "stop" }), }); if (!response.ok) { diff --git a/py_neuromodulation/gui/backend/app_backend.py b/py_neuromodulation/gui/backend/app_backend.py index 6d6832a3..aadd4103 100644 --- a/py_neuromodulation/gui/backend/app_backend.py +++ b/py_neuromodulation/gui/backend/app_backend.py @@ -188,8 +188,6 @@ async def setup_lsl_stream(data: dict): lsl_stream_name=stream_name, sampling_rate_features=data["sampling_rate_features"], line_noise=data["line_noise"], - out_dir=data["out_dir"], - experiment_name=data["experiment_name"], ) return {"message": f"LSL stream '{stream_name}' setup successfully"} except Exception as e: @@ -207,13 +205,26 @@ async def setup_offline_stream(data: dict): file_path=data["file_path"], line_noise=float(data["line_noise"]), sampling_rate_features=float(data["sampling_rate_features"]), - out_dir=data["out_dir"], - experiment_name=data["experiment_name"], ) return {"message": "Offline stream setup successfully"} except ValueError: return {"message": "Offline stream could not be setup"} + @self.post("/api/set-stream-params") + async def set_stream_params(data: dict): + try: + self.pynm_state.stream.settings.sampling_rate_features_hz = float( + data["sampling_rate_features"] + ) + self.pynm_state.stream.line_noise = float(data["line_noise"]) + self.pynm_state.stream.sfreq = float(data["sampling_rate"]) + self.pynm_state.experiment_name = data["experiment_name"] + self.pynm_state.out_dir = data["out_dir"] + + return {"message": "Stream parameters updated successfully"} + except ValueError: + return {"message": "Stream parameters could not be updated"} + ####################### ### PYNM ABOUT INFO ### ####################### diff --git a/py_neuromodulation/gui/backend/app_pynm.py b/py_neuromodulation/gui/backend/app_pynm.py index 9a69a2c8..e98233cb 100644 --- a/py_neuromodulation/gui/backend/app_pynm.py +++ b/py_neuromodulation/gui/backend/app_pynm.py @@ -37,6 +37,8 @@ def __init__( self.lsl_stream_name = None self.stream_controller_process = None self.run_func_process = None + self.out_dir = None # will be set by set_stream_params + self.experiment_name = None # will be set by set_stream_params if default_init: self.stream: Stream = Stream(sfreq=1500, data=np.random.random([1, 1])) @@ -45,8 +47,6 @@ def __init__( def start_run_function( self, - out_dir: str = "", - experiment_name: str = "sub", websocket_manager_features=None, ) -> None: @@ -77,11 +77,12 @@ def start_run_function( # The run_func_thread is terminated through the stream_handling_queue # which initiates to break the data generator and save the features + out_dir = "" if self.out_dir == "default" else self.out_dir self.run_func_thread = Thread( target=self.stream.run, daemon=True, kwargs={ - "out_dir" : self.out_dir, + "out_dir" : out_dir, "experiment_name" : self.experiment_name, "stream_handling_queue" : self.stream_handling_queue, "is_stream_lsl" : is_stream_lsl, @@ -100,8 +101,6 @@ def setup_lsl_stream( lsl_stream_name: str | None = None, line_noise: float | None = None, sampling_rate_features: float | None = None, - out_dir: str = "", - experiment_name: str = "sub", ): from mne_lsl.lsl import resolve_streams @@ -151,17 +150,12 @@ def setup_lsl_stream( if channels.shape[0] == 0: self.logger.error(f"Stream {lsl_stream_name} not found") raise ValueError(f"Stream {lsl_stream_name} not found") - - self.out_dir = out_dir - self.experiment_name = experiment_name def setup_offline_stream( self, file_path: str, line_noise: float | None = None, sampling_rate_features: float | None = None, - out_dir: str = "", - experiment_name: str = "sub", ): data, sfreq, ch_names, ch_types, bads = read_mne_data(file_path) @@ -183,6 +177,3 @@ def setup_offline_stream( line_noise=line_noise, sampling_rate_features_hz=sampling_rate_features, ) - - self.out_dir = out_dir - self.experiment_name = experiment_name diff --git a/py_neuromodulation/stream/stream.py b/py_neuromodulation/stream/stream.py index d0f77192..21bcc471 100644 --- a/py_neuromodulation/stream/stream.py +++ b/py_neuromodulation/stream/stream.py @@ -37,8 +37,6 @@ def __init__( sampling_rate_features_hz: float | None = None, path_grids: _PathLike | None = None, coord_names: list | None = None, - stream_name: str - | None = "example_stream", # Timon: do we need those in the nmstream_abc? is_stream_lsl: bool = False, coord_list: list | None = None, verbose: bool = True,