| import React, { useState, useEffect } from "react"; |
| import { Button } from "@/components/ui/button"; |
| import { useApi } from "@/contexts/ApiContext"; |
|
|
| interface JointData { |
| type: "joint_update"; |
| joints: Record<string, number>; |
| timestamp: number; |
| } |
|
|
| const WebSocketTest: React.FC = () => { |
| const { baseUrl, wsBaseUrl, fetchWithHeaders } = useApi(); |
| const [isConnected, setIsConnected] = useState(false); |
| const [lastMessage, setLastMessage] = useState<JointData | null>(null); |
| const [connectionStatus, setConnectionStatus] = |
| useState<string>("Disconnected"); |
| const [ws, setWs] = useState<WebSocket | null>(null); |
|
|
| const connect = () => { |
| |
| fetchWithHeaders(`${baseUrl}/health`) |
| .then((response) => response.json()) |
| .then((data) => { |
| console.log("Server health:", data); |
|
|
| |
| const websocket = new WebSocket(`${wsBaseUrl}/ws/joint-data`); |
|
|
| websocket.onopen = () => { |
| console.log("WebSocket connected"); |
| setIsConnected(true); |
| setConnectionStatus("Connected"); |
| setWs(websocket); |
| }; |
|
|
| websocket.onmessage = (event) => { |
| try { |
| const data: JointData = JSON.parse(event.data); |
| setLastMessage(data); |
| console.log("Received joint data:", data); |
| } catch (error) { |
| console.error("Error parsing message:", error); |
| } |
| }; |
|
|
| websocket.onclose = (event) => { |
| console.log("WebSocket closed:", event.code, event.reason); |
| setIsConnected(false); |
| setConnectionStatus(`Closed (${event.code})`); |
| setWs(null); |
| }; |
|
|
| websocket.onerror = (error) => { |
| console.error("WebSocket error:", error); |
| setConnectionStatus("Error"); |
| }; |
| }) |
| .catch((error) => { |
| console.error("Server health check failed:", error); |
| setConnectionStatus("Server unreachable"); |
| }); |
| }; |
|
|
| const disconnect = () => { |
| if (ws) { |
| ws.close(); |
| } |
| }; |
|
|
| useEffect(() => { |
| return () => { |
| if (ws) { |
| ws.close(); |
| } |
| }; |
| }, [ws]); |
|
|
| return ( |
| <div className="p-4 bg-gray-900 text-white rounded-lg"> |
| <h3 className="text-lg font-bold mb-4">WebSocket Connection Test</h3> |
| |
| <div className="space-y-4"> |
| <div className="flex items-center gap-4"> |
| <div |
| className={`w-3 h-3 rounded-full ${ |
| isConnected ? "bg-green-500" : "bg-red-500" |
| }`} |
| /> |
| <span>Status: {connectionStatus}</span> |
| </div> |
| |
| <div className="flex gap-2"> |
| <Button onClick={connect} disabled={isConnected}> |
| Connect |
| </Button> |
| <Button |
| onClick={disconnect} |
| disabled={!isConnected} |
| variant="outline" |
| > |
| Disconnect |
| </Button> |
| </div> |
| |
| {lastMessage && ( |
| <div className="bg-gray-800 p-3 rounded"> |
| <h4 className="font-semibold mb-2">Last Joint Data:</h4> |
| <div className="text-sm font-mono"> |
| <div> |
| Timestamp:{" "} |
| {new Date(lastMessage.timestamp * 1000).toLocaleTimeString()} |
| </div> |
| <div className="mt-2">Joints:</div> |
| {Object.entries(lastMessage.joints).map(([joint, value]) => ( |
| <div key={joint} className="ml-4"> |
| {joint}: {value.toFixed(4)} rad ( |
| {((value * 180) / Math.PI).toFixed(2)}°) |
| </div> |
| ))} |
| </div> |
| </div> |
| )} |
| |
| <div className="text-sm text-gray-400"> |
| <div>Expected URL: {wsBaseUrl}/ws/joint-data</div> |
| <div>Make sure your FastAPI server is running!</div> |
| </div> |
| </div> |
| </div> |
| ); |
| }; |
|
|
| export default WebSocketTest; |
|
|