Pre-conf work 2025

This commit is contained in:
2025-09-02 08:25:41 -04:00
parent 550021a18e
commit 4acbec6288
75 changed files with 8047 additions and 5228 deletions

View File

@@ -1,18 +1,25 @@
"use client";
import {
Activity, AlertTriangle, Battery,
BatteryLow, Bot, CheckCircle,
Clock, RefreshCw, Signal,
SignalHigh,
SignalLow,
SignalMedium, WifiOff
Activity,
AlertTriangle,
Battery,
BatteryLow,
Bot,
CheckCircle,
Clock,
RefreshCw,
Signal,
SignalHigh,
SignalLow,
SignalMedium,
WifiOff,
} from "lucide-react";
import { useEffect, useState } from "react";
import { Alert, AlertDescription } from "~/components/ui/alert";
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { Progress } from "~/components/ui/progress";
interface RobotStatusProps {
@@ -37,10 +44,10 @@ interface RobotStatus {
z?: number;
orientation?: number;
};
sensors?: Record<string, any>;
sensors?: Record<string, string>;
}
export function RobotStatus({ trialId }: RobotStatusProps) {
export function RobotStatus({ trialId: _trialId }: RobotStatusProps) {
const [robotStatus, setRobotStatus] = useState<RobotStatus | null>(null);
const [lastUpdate, setLastUpdate] = useState<Date>(new Date());
const [refreshing, setRefreshing] = useState(false);
@@ -62,32 +69,43 @@ export function RobotStatus({ trialId }: RobotStatusProps) {
position: {
x: 1.2,
y: 0.8,
orientation: 45
orientation: 45,
},
sensors: {
lidar: "operational",
camera: "operational",
imu: "operational",
odometry: "operational"
}
odometry: "operational",
},
};
setRobotStatus(mockStatus);
// Simulate periodic updates
const interval = setInterval(() => {
setRobotStatus(prev => {
setRobotStatus((prev) => {
if (!prev) return prev;
return {
...prev,
batteryLevel: Math.max(0, (prev.batteryLevel || 0) - Math.random() * 0.5),
signalStrength: Math.max(0, Math.min(100, (prev.signalStrength || 0) + (Math.random() - 0.5) * 10)),
batteryLevel: Math.max(
0,
(prev.batteryLevel ?? 0) - Math.random() * 0.5,
),
signalStrength: Math.max(
0,
Math.min(
100,
(prev.signalStrength ?? 0) + (Math.random() - 0.5) * 10,
),
),
lastHeartbeat: new Date(),
position: prev.position ? {
...prev.position,
x: prev.position.x + (Math.random() - 0.5) * 0.1,
y: prev.position.y + (Math.random() - 0.5) * 0.1,
} : undefined
position: prev.position
? {
...prev.position,
x: prev.position.x + (Math.random() - 0.5) * 0.1,
y: prev.position.y + (Math.random() - 0.5) * 0.1,
}
: undefined,
};
});
setLastUpdate(new Date());
@@ -103,35 +121,35 @@ export function RobotStatus({ trialId }: RobotStatusProps) {
icon: CheckCircle,
color: "text-green-600",
bgColor: "bg-green-100",
label: "Connected"
label: "Connected",
};
case "connecting":
return {
icon: RefreshCw,
color: "text-blue-600",
bgColor: "bg-blue-100",
label: "Connecting"
label: "Connecting",
};
case "disconnected":
return {
icon: WifiOff,
color: "text-gray-600",
bgColor: "bg-gray-100",
label: "Disconnected"
label: "Disconnected",
};
case "error":
return {
icon: AlertTriangle,
color: "text-red-600",
bgColor: "bg-red-100",
label: "Error"
label: "Error",
};
default:
return {
icon: WifiOff,
color: "text-gray-600",
bgColor: "bg-gray-100",
label: "Unknown"
label: "Unknown",
};
}
};
@@ -159,182 +177,173 @@ export function RobotStatus({ trialId }: RobotStatusProps) {
if (!robotStatus) {
return (
<div className="space-y-4">
<div className="flex items-center space-x-2">
<Bot className="h-4 w-4 text-slate-600" />
<h3 className="font-medium text-slate-900">Robot Status</h3>
<div className="rounded-lg border p-4 text-center">
<div className="text-slate-500">
<Bot className="mx-auto mb-2 h-8 w-8 opacity-50" />
<p className="text-sm">No robot connected</p>
</div>
</div>
<Card className="shadow-sm">
<CardContent className="p-4 text-center">
<div className="text-slate-500">
<Bot className="h-8 w-8 mx-auto mb-2 opacity-50" />
<p className="text-sm">No robot connected</p>
</div>
</CardContent>
</Card>
</div>
);
}
const statusConfig = getConnectionStatusConfig(robotStatus.connectionStatus);
const StatusIcon = statusConfig.icon;
const SignalIcon = getSignalIcon(robotStatus.signalStrength || 0);
const BatteryIcon = getBatteryIcon(robotStatus.batteryLevel || 0);
const SignalIcon = getSignalIcon(robotStatus.signalStrength ?? 0);
const BatteryIcon = getBatteryIcon(robotStatus.batteryLevel ?? 0);
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<Bot className="h-4 w-4 text-slate-600" />
<h3 className="font-medium text-slate-900">Robot Status</h3>
</div>
<div className="flex items-center justify-end">
<Button
variant="ghost"
size="sm"
onClick={handleRefreshStatus}
disabled={refreshing}
>
<RefreshCw className={`h-3 w-3 ${refreshing ? 'animate-spin' : ''}`} />
<RefreshCw
className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`}
/>
</Button>
</div>
{/* Main Status Card */}
<Card className="shadow-sm">
<CardContent className="p-4">
<div className="space-y-3">
{/* Robot Info */}
<div className="flex items-center justify-between">
<div className="font-medium text-slate-900">{robotStatus.name}</div>
<Badge className={`${statusConfig.bgColor} ${statusConfig.color}`} variant="secondary">
<StatusIcon className="mr-1 h-3 w-3" />
{statusConfig.label}
</Badge>
</div>
{/* Connection Details */}
<div className="text-sm text-slate-600">
Protocol: {robotStatus.communicationProtocol}
</div>
{/* Status Indicators */}
<div className="grid grid-cols-2 gap-3">
{/* Battery */}
{robotStatus.batteryLevel !== undefined && (
<div className="space-y-1">
<div className="flex items-center space-x-1 text-xs text-slate-600">
<BatteryIcon className={`h-3 w-3 ${
robotStatus.batteryLevel <= 20 ? 'text-red-500' : 'text-green-500'
}`} />
<span>Battery</span>
</div>
<div className="flex items-center space-x-2">
<Progress
value={robotStatus.batteryLevel}
className="flex-1 h-1.5"
/>
<span className="text-xs font-medium w-8">
{Math.round(robotStatus.batteryLevel)}%
</span>
</div>
</div>
)}
{/* Signal Strength */}
{robotStatus.signalStrength !== undefined && (
<div className="space-y-1">
<div className="flex items-center space-x-1 text-xs text-slate-600">
<SignalIcon className="h-3 w-3" />
<span>Signal</span>
</div>
<div className="flex items-center space-x-2">
<Progress
value={robotStatus.signalStrength}
className="flex-1 h-1.5"
/>
<span className="text-xs font-medium w-8">
{Math.round(robotStatus.signalStrength)}%
</span>
</div>
</div>
)}
</div>
</div>
</CardContent>
</Card>
{/* Current Mode */}
<Card className="shadow-sm">
<CardContent className="p-3">
<div className="rounded-lg border p-4">
<div className="space-y-3">
{/* Robot Info */}
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<Activity className="h-3 w-3 text-slate-600" />
<span className="text-sm text-slate-600">Mode:</span>
</div>
<Badge variant="outline" className="text-xs">
{robotStatus.currentMode.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}
<div className="font-medium text-slate-900">{robotStatus.name}</div>
<Badge
className={`${statusConfig.bgColor} ${statusConfig.color}`}
variant="secondary"
>
<StatusIcon className="mr-1 h-3 w-3" />
{statusConfig.label}
</Badge>
</div>
{robotStatus.isMoving && (
<div className="flex items-center space-x-1 mt-2 text-xs text-blue-600">
<div className="w-1.5 h-1.5 rounded-full bg-blue-500 animate-pulse"></div>
<span>Robot is moving</span>
</div>
)}
</CardContent>
</Card>
{/* Connection Details */}
<div className="text-sm text-slate-600">
Protocol: {robotStatus.communicationProtocol}
</div>
{/* Status Indicators */}
<div className="grid grid-cols-2 gap-3">
{/* Battery */}
{robotStatus.batteryLevel !== undefined && (
<div className="space-y-1">
<div className="flex items-center space-x-1 text-xs text-slate-600">
<BatteryIcon className="h-3 w-3" />
<span>Battery</span>
</div>
<div className="flex items-center space-x-2">
<Progress
value={robotStatus.batteryLevel}
className="h-1.5 flex-1"
/>
<span className="w-8 text-xs font-medium">
{Math.round(robotStatus.batteryLevel)}%
</span>
</div>
</div>
)}
{/* Signal Strength */}
{robotStatus.signalStrength !== undefined && (
<div className="space-y-1">
<div className="flex items-center space-x-1 text-xs text-slate-600">
<SignalIcon className="h-3 w-3" />
<span>Signal</span>
</div>
<div className="flex items-center space-x-2">
<Progress
value={robotStatus.signalStrength}
className="h-1.5 flex-1"
/>
<span className="w-8 text-xs font-medium">
{Math.round(robotStatus.signalStrength)}%
</span>
</div>
</div>
)}
</div>
</div>
</div>
{/* Current Mode */}
<div className="rounded-lg border p-3">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<Activity className="h-3 w-3 text-slate-600" />
<span className="text-sm text-slate-600">Mode:</span>
</div>
<Badge variant="outline" className="text-xs">
{robotStatus.currentMode
.replace(/_/g, " ")
.replace(/\b\w/g, (l) => l.toUpperCase())}
</Badge>
</div>
{robotStatus.isMoving && (
<div className="mt-2 flex items-center space-x-1 text-xs">
<div className="h-1.5 w-1.5 animate-pulse rounded-full"></div>
<span>Robot is moving</span>
</div>
)}
</div>
{/* Position Info */}
{robotStatus.position && (
<Card className="shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-slate-700">Position</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<div className="rounded-lg border p-4">
<div className="mb-3 text-sm font-medium text-slate-700">
Position
</div>
<div>
<div className="grid grid-cols-2 gap-2 text-xs">
<div className="flex justify-between">
<span className="text-slate-600">X:</span>
<span className="font-mono">{robotStatus.position.x.toFixed(2)}m</span>
<span className="font-mono">
{robotStatus.position.x.toFixed(2)}m
</span>
</div>
<div className="flex justify-between">
<span className="text-slate-600">Y:</span>
<span className="font-mono">{robotStatus.position.y.toFixed(2)}m</span>
<span className="font-mono">
{robotStatus.position.y.toFixed(2)}m
</span>
</div>
{robotStatus.position.orientation !== undefined && (
<div className="flex justify-between col-span-2">
<div className="col-span-2 flex justify-between">
<span className="text-slate-600">Orientation:</span>
<span className="font-mono">{Math.round(robotStatus.position.orientation)}°</span>
<span className="font-mono">
{Math.round(robotStatus.position.orientation)}°
</span>
</div>
)}
</div>
</CardContent>
</Card>
</div>
</div>
)}
{/* Sensors Status */}
{robotStatus.sensors && (
<Card className="shadow-sm">
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-slate-700">Sensors</CardTitle>
</CardHeader>
<CardContent className="pt-0">
<div className="rounded-lg border p-4">
<div className="mb-3 text-sm font-medium text-slate-700">Sensors</div>
<div>
<div className="space-y-1">
{Object.entries(robotStatus.sensors).map(([sensor, status]) => (
<div key={sensor} className="flex items-center justify-between text-xs">
<div
key={sensor}
className="flex items-center justify-between text-xs"
>
<span className="text-slate-600 capitalize">{sensor}:</span>
<Badge
variant="outline"
className={`text-xs ${
status === 'operational'
? 'text-green-600 border-green-200'
: 'text-red-600 border-red-200'
}`}
>
<Badge variant="outline" className="text-xs">
{status}
</Badge>
</div>
))}
</div>
</CardContent>
</Card>
</div>
</div>
)}
{/* Error Alert */}
@@ -348,7 +357,7 @@ export function RobotStatus({ trialId }: RobotStatusProps) {
)}
{/* Last Update */}
<div className="text-xs text-slate-500 flex items-center space-x-1">
<div className="flex items-center space-x-1 text-xs text-slate-500">
<Clock className="h-3 w-3" />
<span>Last update: {lastUpdate.toLocaleTimeString()}</span>
</div>