From 901159ae2df705e8bb64d47c8a1c1fbef62e4d22 Mon Sep 17 00:00:00 2001 From: Kasper Marinus Date: Mon, 2 Feb 2026 14:39:07 +0100 Subject: [PATCH] feat: stop experiment button --- .../MonitoringPage/MonitoringPage.module.css | 5 + src/pages/MonitoringPage/MonitoringPage.tsx | 124 ++++++++++-------- src/pages/MonitoringPage/MonitoringPageAPI.ts | 10 +- 3 files changed, 80 insertions(+), 59 deletions(-) diff --git a/src/pages/MonitoringPage/MonitoringPage.module.css b/src/pages/MonitoringPage/MonitoringPage.module.css index b7f2651..72e677c 100644 --- a/src/pages/MonitoringPage/MonitoringPage.module.css +++ b/src/pages/MonitoringPage/MonitoringPage.module.css @@ -98,6 +98,11 @@ University within the Software Project course. color: white; } +.stop { + background-color: red; + color: white; +} + .restartExperiment{ background-color: red; color: white; diff --git a/src/pages/MonitoringPage/MonitoringPage.tsx b/src/pages/MonitoringPage/MonitoringPage.tsx index a1634a3..e7ef003 100644 --- a/src/pages/MonitoringPage/MonitoringPage.tsx +++ b/src/pages/MonitoringPage/MonitoringPage.tsx @@ -6,17 +6,18 @@ import styles from './MonitoringPage.module.css'; // Store & API import useProgramStore from "../../utils/programStore"; -import { - nextPhase, - useExperimentLogger, - useStatusLogger, - pauseExperiment, - playExperiment, - type ExperimentStreamData, - type GoalUpdate, - type TriggerUpdate, - type CondNormsStateUpdate, - type PhaseUpdate +import { + nextPhase, + stopExperiment, + useExperimentLogger, + useStatusLogger, + pauseExperiment, + playExperiment, + type ExperimentStreamData, + type GoalUpdate, + type TriggerUpdate, + type CondNormsStateUpdate, + type PhaseUpdate } from "./MonitoringPageAPI"; import { graphReducer, runProgram } from '../VisProgPage/VisProgLogic.ts'; @@ -26,12 +27,12 @@ import type { GoalNode } from '../VisProgPage/visualProgrammingUI/nodes/GoalNode import type { TriggerNode } from '../VisProgPage/visualProgrammingUI/nodes/TriggerNode'; // Sub-components -import { - GestureControls, - SpeechPresets, - DirectSpeechInput, - StatusList, - RobotConnected +import { + GestureControls, + SpeechPresets, + DirectSpeechInput, + StatusList, + RobotConnected } from './MonitoringPageComponents'; import ExperimentLogs from "./components/ExperimentLogs.tsx"; @@ -76,7 +77,7 @@ function useExperimentLogic() { setGoalIndex(0); } } - } + } else if (data.type === 'goal_update') { const payload = data as GoalUpdate; const currentPhaseGoals = getGoalsInPhase(phaseIds[phaseIndex]) as GoalNode[]; @@ -97,7 +98,7 @@ function useExperimentLogic() { return nextState; }); } - } + } else if (data.type === 'trigger_update') { const payload = data as TriggerUpdate; setActiveIds((prev) => ({ ...prev, [payload.id]: payload.achieved })); @@ -111,7 +112,7 @@ function useExperimentLogic() { setActiveIds((prev) => { const hasChanges = payload.norms.some((u) => prev[u.id] !== u.active); if (!hasChanges) return prev; - + const nextState = { ...prev }; payload.norms.forEach((u) => { nextState[u.id] = u.active; }); return nextState; @@ -144,7 +145,7 @@ function useExperimentLogic() { } }, [setProgramState]); - const handleControlAction = async (action: "pause" | "play" | "nextPhase") => { + const handleControlAction = async (action: "pause" | "play" | "nextPhase" | "stop") => { try { setLoading(true); switch (action) { @@ -159,6 +160,9 @@ function useExperimentLogic() { case "nextPhase": await nextPhase(); break; + case "stop": + await stopExperiment(); + break; } } catch (err) { console.error(err); @@ -189,14 +193,14 @@ function useExperimentLogic() { /** * Visual indicator of progress through experiment phases. */ -function PhaseProgressBar({ - phaseIds, - phaseIndex, - isFinished -}: { - phaseIds: string[], - phaseIndex: number, - isFinished: boolean +function PhaseProgressBar({ + phaseIds, + phaseIndex, + isFinished +}: { + phaseIds: string[], + phaseIndex: number, + isFinished: boolean }) { return (
@@ -218,16 +222,16 @@ function PhaseProgressBar({ /** * Main control buttons (Play, Pause, Next, Reset). */ -function ControlPanel({ - loading, - isPlaying, - onAction, - onReset -}: { - loading: boolean, - isPlaying: boolean, - onAction: (a: "pause" | "play" | "nextPhase") => void, - onReset: () => void +function ControlPanel({ + loading, + isPlaying, + onAction, + onReset +}: { + loading: boolean, + isPlaying: boolean, + onAction: (a: "pause" | "play" | "nextPhase" | "stop") => void, + onReset: () => void }) { return (
@@ -245,17 +249,23 @@ function ControlPanel({ disabled={loading} >▶ - - + +
); @@ -353,11 +363,11 @@ const MonitoringPage: React.FC = () => { -
@@ -370,17 +380,17 @@ const MonitoringPage: React.FC = () => {

Phase Overview

- + {isFinished ? (

All phases have been successfully completed.

) : ( - )} @@ -398,4 +408,4 @@ const MonitoringPage: React.FC = () => { ); } -export default MonitoringPage; \ No newline at end of file +export default MonitoringPage; diff --git a/src/pages/MonitoringPage/MonitoringPageAPI.ts b/src/pages/MonitoringPage/MonitoringPageAPI.ts index cfad841..f024d05 100644 --- a/src/pages/MonitoringPage/MonitoringPageAPI.ts +++ b/src/pages/MonitoringPage/MonitoringPageAPI.ts @@ -32,6 +32,12 @@ export async function nextPhase(): Promise { sendAPICall(type, context) } +export async function stopExperiment(): Promise { + const type = "stop" + const context = "" + sendAPICall(type, context) +} + /** * Sends an API call to the CB for going to pause experiment @@ -95,14 +101,14 @@ export function useExperimentLogger(onUpdate?: (data: ExperimentStreamData) => v console.log("Closing Experiment Stream..."); eventSource.close(); }; - }, []); + }, []); } /** * A hook that listens to the status stream that updates active conditional norms * via updates sent from the backend */ -export function useStatusLogger(onUpdate?: (data: ExperimentStreamData) => void) { +export function useStatusLogger(onUpdate?: (data: ExperimentStreamData) => void) { const callbackRef = React.useRef(onUpdate); React.useEffect(() => {