import React, { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Slider } from '@/components/ui/slider'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Progress } from '@/components/ui/progress'
import {
Bot,
Play,
Square,
RotateCcw,
Settings,
Eye,
Activity,
Zap,
Camera,
Mic,
Arm,
Navigation,
Battery,
Wifi,
WifiOff
} from 'lucide-react'
import { YahboomControls } from '@/components/YahboomControls'
import { MapVisualization } from '@/components/MapVisualization'
export default function YahboomControlPage() {
const [isConnected, setIsConnected] = useState(false)
const [robotData, setRobotData] = useState<any>(null)
const [mapData, setMapData] = useState<any>(null)
const [commandHistory, setCommandHistory] = useState<string[]>([])
// Mock robot data for Yahboom ROSMASTER (supports M1 and X3 models)
const mockRobotData = {
robot_id: 'yahboom_01',
robot_type: 'yahboom',
name: 'Yahboom ROSMASTER', // Supports both M1 and X3/X3 Plus models
connected: true,
battery: 78,
position: { x: 1.2, y: 0.8, z: 0.0 },
rotation: { yaw: 45.0 },
status: 'active',
sensors: {
camera: { streaming: true, fps: 30, resolution: '1080p' },
lidar: { status: 'active', range: 12.0 },
ultrasonic: [2.1, 2.3, 1.8, 2.5, 1.9, 2.2],
imu: { roll: 0.1, pitch: -0.2, yaw: 45.0 }
},
arm: {
enabled: true,
joint_angles: [0, 30, 45, 0, 60, 0],
gripper_open: false
}
}
useEffect(() => {
// Simulate connection and data loading
setTimeout(() => {
setIsConnected(true)
setRobotData(mockRobotData)
}, 1000)
}, [])
const handleRobotCommand = (robotId: string, command: any) => {
console.log('Sending command to Yahboom:', command)
setCommandHistory(prev => [`${new Date().toLocaleTimeString()}: ${JSON.stringify(command)}`, ...prev.slice(0, 9)])
// Simulate command response
setTimeout(() => {
// Update robot data based on command
if (command.action === 'move') {
setRobotData(prev => ({
...prev,
position: {
...prev.position,
x: prev.position.x + (command.linear_x || 0) * 0.1,
y: prev.position.y + (command.linear_y || 0) * 0.1
}
}))
}
}, 500)
}
const handleArmCommand = (jointAngles: number[]) => {
setRobotData(prev => ({
...prev,
arm: {
...prev.arm,
joint_angles: jointAngles
}
}))
}
const handleGripperCommand = (action: 'open' | 'close') => {
setRobotData(prev => ({
...prev,
arm: {
...prev.arm,
gripper_open: action === 'open'
}
}))
}
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50">
<div className="container mx-auto px-4 py-8 max-w-7xl">
{/* Header */}
<div className="mb-8">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">
<div className="p-3 bg-blue-100 rounded-lg">
<Bot className="h-8 w-8 text-blue-600" />
</div>
<div>
<h1 className="text-3xl font-bold text-gray-900">Yahboom ROSMASTER Control</h1>
<p className="text-gray-600">ROS 2-powered AI wheeled robot with multimodal capabilities</p>
</div>
</div>
<div className="flex items-center space-x-4">
<Badge variant={isConnected ? "default" : "destructive"} className="px-3 py-1">
{isConnected ? (
<>
<Wifi className="w-4 h-4 mr-2" />
Connected
</>
) : (
<>
<WifiOff className="w-4 h-4 mr-2" />
Disconnected
</>
)}
</Badge>
{robotData && (
<div className="flex items-center space-x-2 text-sm text-gray-600">
<Battery className="w-4 h-4" />
<span>{robotData.battery}%</span>
<Activity className="w-4 h-4 ml-4" />
<span>{robotData.status}</span>
</div>
)}
</div>
</div>
</div>
{!isConnected && (
<Alert className="mb-6 border-amber-200 bg-amber-50">
<Activity className="h-4 w-4 text-amber-600" />
<AlertDescription className="text-amber-800">
Connecting to Yahboom ROSMASTER... Please ensure the robot is powered on and ROS 2 is running.
</AlertDescription>
</Alert>
)}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Main Control Panel */}
<div className="lg:col-span-2 space-y-6">
{/* Robot Status */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Bot className="h-5 w-5" />
<span>Robot Status</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="text-center">
<div className="text-2xl font-bold text-blue-600">{robotData?.battery || 0}%</div>
<div className="text-sm text-gray-600">Battery</div>
<Progress value={robotData?.battery || 0} className="mt-2" />
</div>
<div className="text-center">
<div className="text-2xl font-bold text-green-600">
{robotData?.position ? `${robotData.position.x.toFixed(1)}, ${robotData.position.y.toFixed(1)}` : '0.0, 0.0'}
</div>
<div className="text-sm text-gray-600">Position (m)</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-purple-600">{robotData?.rotation?.yaw?.toFixed(0) || 0}°</div>
<div className="text-sm text-gray-600">Heading</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-orange-600">{robotData?.status || 'offline'}</div>
<div className="text-sm text-gray-600">Status</div>
</div>
</div>
</CardContent>
</Card>
{/* Yahboom Specific Controls */}
{robotData && (
<YahboomControls
robot={{
robot_id: robotData.robot_id,
robot_type: robotData.robot_type,
connected: robotData.connected,
battery: robotData.battery,
model: 'ROSMASTER X3',
platform: 'raspbot_v2'
}}
onCommand={handleRobotCommand}
/>
)}
{/* Sensor Data */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Eye className="h-5 w-5" />
<span>Sensor Data</span>
</CardTitle>
</CardHeader>
<CardContent>
<Tabs defaultValue="camera" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="camera">Camera</TabsTrigger>
<TabsTrigger value="lidar">LiDAR</TabsTrigger>
<TabsTrigger value="imu">IMU</TabsTrigger>
</TabsList>
<TabsContent value="camera" className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Status:</span>
<Badge variant={robotData?.sensors?.camera?.streaming ? "default" : "secondary"}>
{robotData?.sensors?.camera?.streaming ? 'Streaming' : 'Offline'}
</Badge>
</div>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Resolution:</span>
<span className="text-sm text-gray-600">{robotData?.sensors?.camera?.resolution || 'N/A'}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">FPS:</span>
<span className="text-sm text-gray-600">{robotData?.sensors?.camera?.fps || 0}</span>
</div>
</TabsContent>
<TabsContent value="lidar" className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Status:</span>
<Badge variant="default">Active</Badge>
</div>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Range:</span>
<span className="text-sm text-gray-600">{robotData?.sensors?.lidar?.range || 0}m</span>
</div>
<div className="text-sm text-gray-600 mt-2">
LiDAR provides 360° obstacle detection and SLAM navigation
</div>
</TabsContent>
<TabsContent value="imu" className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Roll:</span>
<span className="text-sm text-gray-600">{robotData?.sensors?.imu?.roll?.toFixed(1) || 0}°</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Pitch:</span>
<span className="text-sm text-gray-600">{robotData?.sensors?.imu?.pitch?.toFixed(1) || 0}°</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Yaw:</span>
<span className="text-sm text-gray-600">{robotData?.sensors?.imu?.yaw?.toFixed(1) || 0}°</span>
</div>
</TabsContent>
</Tabs>
</CardContent>
</Card>
</div>
{/* Sidebar */}
<div className="space-y-6">
{/* AI Multimodal Controls */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Zap className="h-5 w-5" />
<span>AI Multimodal</span>
</CardTitle>
<CardDescription>Voice, vision, and text AI capabilities</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<Button className="w-full" variant="outline">
<Mic className="w-4 h-4 mr-2" />
Voice Command
</Button>
<Button className="w-full" variant="outline">
<Camera className="w-4 h-4 mr-2" />
Visual Analysis
</Button>
<Button className="w-full" variant="outline">
<Bot className="w-4 h-4 mr-2" />
Text Query
</Button>
</CardContent>
</Card>
{/* Robotic Arm Controls */}
{robotData?.arm?.enabled && (
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Arm className="h-5 w-5" />
<span>Robotic Arm</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<div className="space-y-2">
<label className="text-sm font-medium">Joint Angles</label>
{['Base', 'Shoulder', 'Elbow', 'Wrist Roll', 'Wrist Pitch', 'Gripper'].map((joint, index) => (
<div key={joint} className="flex items-center space-x-2">
<span className="text-xs w-20">{joint}:</span>
<Slider
value={[robotData.arm.joint_angles[index]]}
onValueChange={([value]) => {
const newAngles = [...robotData.arm.joint_angles]
newAngles[index] = value
handleArmCommand(newAngles)
}}
min={-180}
max={180}
step={5}
className="flex-1"
/>
<span className="text-xs w-12">{robotData.arm.joint_angles[index]}°</span>
</div>
))}
</div>
<div className="flex space-x-2">
<Button
size="sm"
variant={robotData.arm.gripper_open ? "default" : "outline"}
onClick={() => handleGripperCommand('open')}
className="flex-1"
>
Open
</Button>
<Button
size="sm"
variant={!robotData.arm.gripper_open ? "default" : "outline"}
onClick={() => handleGripperCommand('close')}
className="flex-1"
>
Close
</Button>
</div>
</CardContent>
</Card>
)}
{/* Command History */}
<Card>
<CardHeader>
<CardTitle className="text-lg">Command History</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2 max-h-48 overflow-y-auto">
{commandHistory.length === 0 ? (
<p className="text-sm text-gray-500 text-center py-4">No commands sent yet</p>
) : (
commandHistory.map((command, index) => (
<div key={index} className="text-xs font-mono bg-gray-100 p-2 rounded">
{command}
</div>
))
)}
</div>
</CardContent>
</Card>
</div>
</div>
{/* Map Visualization */}
<Card className="mt-8">
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Navigation className="h-5 w-5" />
<span>Navigation & Mapping</span>
</CardTitle>
<CardDescription>Real-time SLAM mapping and navigation</CardDescription>
</CardHeader>
<CardContent>
<MapVisualization
robots={robotData ? [robotData] : []}
mapData={mapData}
onRobotCommand={handleRobotCommand}
className="w-full"
/>
</CardContent>
</Card>
</div>
</div>
)
}