leLab / src /pages /Index.tsx
jurmy24's picture
initial commit
9d3c32a
raw
history blame
4.63 kB
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;