Spaces:
Running
Running
| import React, { useState, useEffect, useRef } from 'react'; | |
| import { useNavigate } from 'react-router-dom'; | |
| import { useToast } from '@/hooks/use-toast'; | |
| import { generateSensorData, generateMotorData } from '@/lib/mockData'; | |
| import VisualizerPanel from '@/components/control/VisualizerPanel'; | |
| import MetricsPanel from '@/components/control/MetricsPanel'; | |
| import CommandBar from '@/components/control/CommandBar'; | |
| const Index = () => { | |
| const [command, setCommand] = useState(''); | |
| const [activeTab, setActiveTab] = useState<'SENSORS' | 'MOTORS'>('SENSORS'); | |
| const [isVoiceActive, setIsVoiceActive] = useState(true); | |
| const [showCamera, setShowCamera] = useState(false); | |
| const [hasPermissions, setHasPermissions] = useState(false); | |
| const [micLevel, setMicLevel] = useState(0); | |
| const [sensorData, setSensorData] = useState<any[]>([]); | |
| const [motorData, setMotorData] = useState<any[]>([]); | |
| const { toast } = useToast(); | |
| const navigate = useNavigate(); | |
| const videoRef = useRef<HTMLVideoElement>(null); | |
| const streamRef = useRef<MediaStream | null>(null); | |
| useEffect(() => { | |
| let audioContext: AudioContext | null = null; | |
| const getPermissions = async () => { | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ | |
| video: true, | |
| audio: true | |
| }); | |
| streamRef.current = stream; | |
| if (videoRef.current) { | |
| videoRef.current.srcObject = stream; | |
| } | |
| setHasPermissions(true); | |
| const AudioContextClass = window.AudioContext || (window as any).webkitAudioContext; | |
| if (AudioContextClass) { | |
| audioContext = new AudioContextClass(); | |
| const analyser = audioContext.createAnalyser(); | |
| const source = audioContext.createMediaStreamSource(stream); | |
| source.connect(analyser); | |
| let animationFrameId: number; | |
| const dataArray = new Uint8Array(analyser.frequencyBinCount); | |
| const updateMicLevel = () => { | |
| if (audioContext?.state === 'closed') return; | |
| analyser.getByteFrequencyData(dataArray); | |
| const average = dataArray.reduce((a, b) => a + b) / dataArray.length; | |
| setMicLevel(average); | |
| animationFrameId = requestAnimationFrame(updateMicLevel); | |
| }; | |
| updateMicLevel(); | |
| return () => { | |
| cancelAnimationFrame(animationFrameId); | |
| audioContext?.close(); | |
| }; | |
| } | |
| } catch (error) { | |
| console.error("Permission to access media devices was denied.", error); | |
| } | |
| }; | |
| let cleanup: (() => void) | undefined; | |
| getPermissions().then(returnedCleanup => { | |
| cleanup = returnedCleanup; | |
| }); | |
| return () => { | |
| if (streamRef.current) { | |
| streamRef.current.getTracks().forEach(track => track.stop()); | |
| } | |
| cleanup?.(); | |
| }; | |
| }, []); | |
| useEffect(() => { | |
| const interval = setInterval(() => { | |
| setSensorData(prev => [...prev, generateSensorData()].slice(-50)); | |
| setMotorData(prev => [...prev, generateMotorData()].slice(-50)); | |
| }, 100); | |
| return () => clearInterval(interval); | |
| }, []); | |
| const handleSendCommand = () => { | |
| if (command.trim()) { | |
| toast({ | |
| title: "Command Sent", | |
| description: `Robot command: "${command}"`, | |
| }); | |
| setCommand(''); | |
| } | |
| }; | |
| const handleGoBack = () => { | |
| navigate('/'); | |
| }; | |
| const handleEndSession = () => { | |
| if (streamRef.current) { | |
| streamRef.current.getTracks().forEach(track => track.stop()); | |
| } | |
| toast({ | |
| title: "Session Ended", | |
| description: "Robot control session terminated safely.", | |
| variant: "destructive", | |
| }); | |
| navigate('/'); | |
| }; | |
| return ( | |
| <div className="min-h-screen bg-black text-white flex flex-col"> | |
| <div className="flex-1 flex flex-col lg:flex-row"> | |
| <VisualizerPanel onGoBack={handleGoBack} /> | |
| <MetricsPanel | |
| activeTab={activeTab} | |
| setActiveTab={setActiveTab} | |
| sensorData={sensorData} | |
| motorData={motorData} | |
| hasPermissions={hasPermissions} | |
| streamRef={streamRef} | |
| isVoiceActive={isVoiceActive} | |
| micLevel={micLevel} | |
| /> | |
| </div> | |
| <CommandBar | |
| command={command} | |
| setCommand={setCommand} | |
| handleSendCommand={handleSendCommand} | |
| isVoiceActive={isVoiceActive} | |
| setIsVoiceActive={setIsVoiceActive} | |
| showCamera={showCamera} | |
| setShowCamera={setShowCamera} | |
| handleEndSession={handleEndSession} | |
| /> | |
| </div> | |
| ); | |
| }; | |
| export default Index; | |