Skip to content

Commit

Permalink
Add output folder selection feature and refactor streamParameter send
Browse files Browse the repository at this point in the history
  • Loading branch information
timonmerk committed Nov 25, 2024
1 parent f3e25c7 commit b326e2a
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 61 deletions.
29 changes: 4 additions & 25 deletions gui_dev/src/pages/SourceSelection/FileSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -47,10 +48,6 @@ export const FileSelector = () => {
}
};

const handleFolderSelect = (folder) => {
setShowFolderBrowser(false);
};

return (
<TitledBox title="Read data from file">
<Button
Expand All @@ -69,7 +66,7 @@ export const FileSelector = () => {
Selected File: <i>{fileSource.name}</i>
</Typography>
)}
{fileSource.size != "0" && (
{fileSource.name && (
<Typography variant="body2">File Size: {fileSource.size}</Typography>
)}
{fileSource.path && (
Expand All @@ -83,15 +80,6 @@ export const FileSelector = () => {
>
Open File
</Button>
<Button
variant="contained"
onClick={() => {
setShowFolderBrowser(true);
}}
sx={{ width: "fit-content" }}
>
Select Folder
</Button>
{streamSetupMessage && (
<Typography
variant="body2"
Expand All @@ -109,15 +97,6 @@ export const FileSelector = () => {
onSelect={handleFileSelect}
/>
)}
{showFolderBrowser && (
<FileBrowser
isModal={true}
directory={fileBrowserDirRef.current}
onClose={() => setShowFolderBrowser(false)}
onSelect={handleFolderSelect}
onlyDirectories={true}
/>
)}
</TitledBox>
);
};
6 changes: 4 additions & 2 deletions gui_dev/src/pages/SourceSelection/SourceSelection.jsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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);
Expand Down Expand Up @@ -49,6 +50,7 @@ export const SourceSelection = () => {
color="primary"
to="/channels"
disabled={!isSourceValid}
onClick={sendStreamParametersToBackend}
>
Select Channels
</LinkButton>
Expand Down
42 changes: 36 additions & 6 deletions gui_dev/src/pages/SourceSelection/StreamParameters.jsx
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -17,6 +19,13 @@ export const StreamParameters = () => {
checkStreamParameters();
};

const [showFolderBrowser, setShowFolderBrowser] = useState(false);

const handleFolderSelect = (folder) => {
updateStreamParameter("outputDirectory", folder);
setShowFolderBrowser(false);
};

return (
<TitledBox title="Stream parameters">
<MyTextField
Expand All @@ -39,11 +48,32 @@ export const StreamParameters = () => {
value={streamParameters.experimentName}
onChange={(event) => handleOnChange(event, "experimentName")}
/>
<MyTextField
label="output directory"
value={streamParameters.outputDirectory}
onChange={(event) => handleOnChange(event, "outputDirectory")}
/>
<div style={{ display: "flex", alignItems: "flex-start", width: "100%" }}>
<MyTextField
label="output directory"
value={streamParameters.outputDirectory}
onChange={(event) => handleOnChange(event, "outputDirectory")}
style={{ flexGrow: 1 }}
/>
<Button
variant="contained"
sx={{ width: "200px", marginLeft: "20px", flexGrow: 0 }}
onClick={() => {
setShowFolderBrowser(true);
}}
>
Select Folder
</Button>
</div>
{showFolderBrowser && (
<FileBrowser
isModal={true}
directory={streamParameters.outputDirectory}
onClose={() => setShowFolderBrowser(false)}
onSelect={handleFolderSelect}
onlyDirectories={true}
/>
)}
</TitledBox>
);
};
48 changes: 39 additions & 9 deletions gui_dev/src/stores/sessionStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
},

Expand All @@ -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,
}),
});

Expand Down Expand Up @@ -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,
}),
});

Expand Down Expand Up @@ -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;
}
},

/*****************************/
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
19 changes: 15 additions & 4 deletions py_neuromodulation/gui/backend/app_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 ###
#######################
Expand Down
17 changes: 4 additions & 13 deletions py_neuromodulation/gui/backend/app_pynm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand All @@ -45,8 +47,6 @@ def __init__(

def start_run_function(
self,
out_dir: str = "",
experiment_name: str = "sub",
websocket_manager_features=None,
) -> None:

Expand Down Expand Up @@ -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,
Expand All @@ -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

Expand Down Expand Up @@ -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)

Expand All @@ -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
2 changes: 0 additions & 2 deletions py_neuromodulation/stream/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit b326e2a

Please sign in to comment.