Spaces:
Running
Running
File size: 4,054 Bytes
a8d792f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
"use client"
import { useState, useMemo } from "react"
import { Download } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { lerobot } from "@/lib/mock-api"
import { useLocalStorage } from "@/hooks/use-local-storage"
import { MotorCalibrationVisual } from "@/components/motor-calibration-visual"
import type { RobotConnection, LiveCalibrationData, WebCalibrationResults } from "@/types/robot"
interface CalibrationViewProps {
robot: RobotConnection
}
export function CalibrationView({ robot }: CalibrationViewProps) {
const [status, setStatus] = useState("Ready to calibrate.")
const [liveData, setLiveData] = useState<LiveCalibrationData | null>(null)
const [isCalibrating, setIsCalibrating] = useState(false)
const [calibrationProcess, setCalibrationProcess] = useState<{
stop: () => void
result: Promise<WebCalibrationResults>
} | null>(null)
const [calibrationResults, setCalibrationResults] = useLocalStorage<WebCalibrationResults | null>(
`calibration-${robot.robotId}`,
null,
)
const handleStart = async () => {
setIsCalibrating(true)
const process = await lerobot.calibrate(robot, {
onLiveUpdate: setLiveData,
onProgress: setStatus,
})
setCalibrationProcess(process)
}
const handleFinish = async () => {
if (calibrationProcess) {
calibrationProcess.stop()
const results = await calibrationProcess.result
setCalibrationResults(results)
setIsCalibrating(false)
setCalibrationProcess(null)
}
}
const downloadJson = () => {
if (!calibrationResults) return
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(calibrationResults, null, 2))
const downloadAnchorNode = document.createElement("a")
downloadAnchorNode.setAttribute("href", dataStr)
downloadAnchorNode.setAttribute("download", `${robot.robotId}_calibration.json`)
document.body.appendChild(downloadAnchorNode)
downloadAnchorNode.click()
downloadAnchorNode.remove()
}
const motorData = useMemo(
() =>
liveData
? Object.entries(liveData)
: lerobot.MOCK_MOTOR_NAMES.map((name) => [name, { current: 0, min: 4095, max: 0, range: 0 }]),
[liveData],
)
return (
<Card className="border-0 rounded-none">
<div className="p-4 border-b border-white/10 flex items-center justify-between">
<div className="flex items-center gap-4">
<div className="w-1 h-8 bg-primary"></div>
<div>
<h3 className="text-xl font-bold text-foreground font-mono tracking-wider uppercase">motor calibration</h3>
<p className="text-sm text-muted-foreground font-mono">move all joints to their limits</p>
</div>
</div>
<div className="flex gap-4">
{!isCalibrating ? (
<Button onClick={handleStart} size="lg">
Start Calibration
</Button>
) : (
<Button onClick={handleFinish} variant="destructive" size="lg">
Finish Recording
</Button>
)}
<Button onClick={downloadJson} variant="outline" size="lg" disabled={!calibrationResults}>
<Download className="w-4 h-4 mr-2" /> Download JSON
</Button>
</div>
</div>
<div className="pt-6 p-6">
<div className="flex items-center gap-4 py-2 px-4 text-sm font-sans text-muted-foreground">
<div className="w-40">Motor Name</div>
<div className="flex-1">Visual Range</div>
<div className="w-16 text-right">Current</div>
<div className="w-16 text-right">Min</div>
<div className="w-16 text-right">Max</div>
<div className="w-16 text-right">Range</div>
</div>
<div className="border-t border-white/10">
{motorData.map(([name, data]) => (
<MotorCalibrationVisual key={name} name={name} data={data} />
))}
</div>
</div>
</Card>
)
}
|