import React, { use } from 'react'; import styles from './MonitoringPage.module.css'; import useProgramStore from "../../utils/programStore.ts"; import { GestureControls, SpeechPresets, DirectSpeechInput, StatusList } from './Components'; import { nextPhase, useExperimentLogger } from ".//MonitoringPageAPI.ts" import type { GoalNodeData } from '../VisProgPage/visualProgrammingUI/nodes/GoalNode.tsx'; import type { TriggerNodeData } from '../VisProgPage/visualProgrammingUI/nodes/TriggerNode.tsx'; import type { NormNodeData } from '../VisProgPage/visualProgrammingUI/nodes/NormNode.tsx'; const MonitoringPage: React.FC = () => { const getPhaseIds = useProgramStore((s) => s.getPhaseIds); const getNormsInPhase = useProgramStore((s) => s.getNormsInPhase); const getGoalsInPhase = useProgramStore((s) => s.getGoalsInPhase); const getTriggersInPhase = useProgramStore((s) => s.getTriggersInPhase); // Can be used to block actions until feedback from CB. const [loading, setLoading] = React.useState(false); const [activeIds, setActiveIds] = React.useState>({}); const [goalIndex, setGoalIndex] = React.useState(0); const phaseIds = getPhaseIds(); const [phaseIndex, setPhaseIndex] = React.useState(0); const isFinished = phaseIndex >= phaseIds.length; //determines if experiment is over const handleStreamUpdate = React.useCallback((data: any) => { // Check for phase updates if (data.type === 'phase_update' && data.phase_id) { const allIds = getPhaseIds(); const newIndex = allIds.indexOf(data.phase_id); if (newIndex !== -1) { setPhaseIndex(newIndex); setGoalIndex(0); //when phase change we reset the index } } else if (data.type === 'goal_update') { const currentPhaseGoals = getGoalsInPhase(phaseIds[phaseIndex]) as any[]; const gIndex = currentPhaseGoals.findIndex((g: any) => g.id === data.id); if (gIndex !== -1) { //set current goal to the goal that is just started setGoalIndex(gIndex); // All previous goals are set to "active" which means they are achieved setActiveIds((prev) => { const nextState = { ...prev }; // We loop until i is LESS than gIndex. // This leaves currentPhaseGoals[gIndex] as isActive: false. for (let i = 0; i < gIndex; i++) { nextState[currentPhaseGoals[i].id ] = true; } return nextState; }); console.log(`Now pursuing goal: ${data.id}. Previous goals marked achieved.`); } } else if (data.type === 'trigger_update') { setActiveIds((prev) => ({ ...prev, [data.id]: data.achieved // data.id is de key, achieved is true/false })); } else if (data.type === 'cond_norms_state_update') { setActiveIds((prev) => { const nextState = { ...prev }; data.norms.forEach((normUpdate: { id: string; active: boolean }) => { nextState[normUpdate.id] = normUpdate.active; }); return nextState; }); console.log("Updated conditional norms state:", data.norms); } }, [getPhaseIds]); useExperimentLogger(handleStreamUpdate); if (phaseIds.length === 0) { return

No program loaded.

; } const phaseId = phaseIds[phaseIndex]; const goals = (getGoalsInPhase(phaseId) as any[]).map(g => ({ ...g, label: g.name, achieved: activeIds[g.id] ?? false })); const triggers = (getTriggersInPhase(phaseId) as any[]).map(t => ({ ...t, label: (() => { let prefix = ""; if (t.condition?.keyword) { prefix = `if keywords said: "${t.condition.keyword}"`; } else if (t.condition?.name) { prefix = `if LLM belief: ${t.condition.name}`; } else { //fallback prefix = t.label || "Trigger"; } const stepLabels = t.plan?.steps?.map((step: any) => { if (step.text) { return `say: "${step.text}"`; } if (step.gesture) { return `perform gesture: ${step.gesture.name || step.gesture.type}`; } if (step.goal) { return `perform LLM: ${step.goal}`; } return "Action"; // Fallback }) || []; const planText = stepLabels.length > 0 ? `➔ Do: ${stepLabels.join(", ")}` : "➔ (No actions set)"; return `${prefix} ${planText}`; })(), isActive: activeIds[t.id] ?? false })); const norms = (getNormsInPhase(phaseId) as NormNodeData[]) .filter(n => !n.condition) .map(n => ({ ...n, label: n.norm, })); const conditionalNorms = (getNormsInPhase(phaseId) as any[]) .filter(n => !!n.condition) // Only items with a condition .map(n => ({ ...n, label: (() => { let prefix = ""; if (n.condition?.keyword) { prefix = `if keywords said: "${n.condition.keyword}"`; } else if (n.condition?.name) { prefix = `if LLM belief: ${n.condition.name}`; } return `${prefix} ➔ Norm: ${n.norm}`; })(), achieved: activeIds[n.id] ?? false })); // Handle logic of 'next' button. const handleNext = async () => { try { setLoading(true); await nextPhase(); } catch (err) { console.log("Monitoring Page could not advance to the next phase:") console.error(err); } finally { setLoading(false); } }; return (
{/* HEADER */}

Experiment Overview

Phase {` ${phaseIndex + 1}`}

{phaseIds.map((id, index) => ( {index + 1} ))}

Experiment Controls

Connection:

● Robot is connected

{/* MAIN GRID */}

Phase Overview

{isFinished ? (

All phases have been successfully completed.

) : ( <> )}
{/* LOGS */} {/* FOOTER */}
); } export default MonitoringPage;