"use client"; import { useState, useEffect, useRef } from "react"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Textarea } from "~/components/ui/textarea"; import { Badge } from "~/components/ui/badge"; import { Separator } from "~/components/ui/separator"; import { Alert, AlertDescription } from "~/components/ui/alert"; import { PageHeader } from "~/components/ui/page-header"; import { PageLayout } from "~/components/ui/page-layout"; import { ScrollArea } from "~/components/ui/scroll-area"; import { Wifi, WifiOff, AlertTriangle, CheckCircle, Play, Square, Trash2, Copy, } from "lucide-react"; export default function DebugPage() { const [connectionStatus, setConnectionStatus] = useState< "disconnected" | "connecting" | "connected" | "error" >("disconnected"); const [rosSocket, setRosSocket] = useState(null); const [logs, setLogs] = useState([]); const [messages, setMessages] = useState([]); const [testMessage, setTestMessage] = useState(""); const [selectedTopic, setSelectedTopic] = useState("/speech"); const [messageType, setMessageType] = useState("std_msgs/String"); const [lastError, setLastError] = useState(null); const [connectionAttempts, setConnectionAttempts] = useState(0); const logsEndRef = useRef(null); const messagesEndRef = useRef(null); const ROS_BRIDGE_URL = "ws://134.82.159.25:9090"; const addLog = (message: string, type: "info" | "error" | "success" = "info") => { const timestamp = new Date().toLocaleTimeString(); const logEntry = `[${timestamp}] [${type.toUpperCase()}] ${message}`; setLogs((prev) => [...prev.slice(-99), logEntry]); console.log(logEntry); }; const addMessage = (message: any, direction: "sent" | "received") => { const timestamp = new Date().toLocaleTimeString(); setMessages((prev) => [ ...prev.slice(-49), { timestamp, direction, data: message, }, ]); }; useEffect(() => { logsEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [logs]); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]); const connectToRos = () => { if (rosSocket?.readyState === WebSocket.OPEN) return; setConnectionStatus("connecting"); setConnectionAttempts((prev) => prev + 1); setLastError(null); addLog(`Attempting connection #${connectionAttempts + 1} to ${ROS_BRIDGE_URL}`); const socket = new WebSocket(ROS_BRIDGE_URL); // Connection timeout const timeout = setTimeout(() => { if (socket.readyState === WebSocket.CONNECTING) { addLog("Connection timeout (10s) - closing socket", "error"); socket.close(); } }, 10000); socket.onopen = () => { clearTimeout(timeout); setConnectionStatus("connected"); setRosSocket(socket); setLastError(null); addLog("✅ WebSocket connection established successfully", "success"); // Test basic functionality by advertising const advertiseMsg = { op: "advertise", topic: "/hristudio_debug", type: "std_msgs/String", }; socket.send(JSON.stringify(advertiseMsg)); addMessage(advertiseMsg, "sent"); }; socket.onmessage = (event) => { try { const data = JSON.parse(event.data); addMessage(data, "received"); if (data.level === "error") { addLog(`ROS Error: ${data.msg}`, "error"); } else if (data.op === "status") { addLog(`Status: ${data.msg} (Level: ${data.level})`); } else { addLog(`Received: ${data.op || "unknown"} operation`); } } catch (error) { addLog(`Failed to parse message: ${error}`, "error"); addMessage({ raw: event.data, error: String(error) }, "received"); } }; socket.onclose = (event) => { clearTimeout(timeout); const wasConnected = connectionStatus === "connected"; setConnectionStatus("disconnected"); setRosSocket(null); let reason = "Unknown reason"; if (event.code === 1000) { reason = "Normal closure"; addLog(`Connection closed normally: ${event.reason || reason}`); } else if (event.code === 1006) { reason = "Connection lost/refused"; setLastError("ROS Bridge server not responding - check if rosbridge_server is running"); addLog(`❌ Connection failed: ${reason} (${event.code})`, "error"); } else if (event.code === 1011) { reason = "Server error"; setLastError("ROS Bridge server encountered an error"); addLog(`❌ Server error: ${reason} (${event.code})`, "error"); } else { reason = `Code ${event.code}`; setLastError(`Connection closed with code ${event.code}: ${event.reason || "No reason given"}`); addLog(`❌ Connection closed: ${reason}`, "error"); } if (wasConnected) { addLog("Connection was working but lost - check network/server"); } }; socket.onerror = (error) => { clearTimeout(timeout); setConnectionStatus("error"); const errorMsg = "WebSocket error occurred"; setLastError(errorMsg); addLog(`❌ ${errorMsg}`, "error"); console.error("WebSocket error details:", error); }; }; const disconnectFromRos = () => { if (rosSocket) { addLog("Manually closing connection"); rosSocket.close(1000, "Manual disconnect"); } }; const sendTestMessage = () => { if (!rosSocket || connectionStatus !== "connected") { addLog("Cannot send message - not connected", "error"); return; } try { let message: any; if (selectedTopic === "/speech" && messageType === "std_msgs/String") { message = { op: "publish", topic: "/speech", type: "std_msgs/String", msg: { data: testMessage || "Hello from debug page" }, }; } else if (selectedTopic === "/cmd_vel") { message = { op: "publish", topic: "/cmd_vel", type: "geometry_msgs/Twist", msg: { linear: { x: 0.1, y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0 }, }, }; } else { // Generic message message = { op: "publish", topic: selectedTopic, type: messageType, msg: { data: testMessage || "test" }, }; } rosSocket.send(JSON.stringify(message)); addMessage(message, "sent"); addLog(`Sent message to ${selectedTopic}`, "success"); } catch (error) { addLog(`Failed to send message: ${error}`, "error"); } }; const subscribeToTopic = () => { if (!rosSocket || connectionStatus !== "connected") { addLog("Cannot subscribe - not connected", "error"); return; } const subscribeMsg = { op: "subscribe", topic: selectedTopic, type: messageType, }; rosSocket.send(JSON.stringify(subscribeMsg)); addMessage(subscribeMsg, "sent"); addLog(`Subscribed to ${selectedTopic}`, "success"); }; const clearLogs = () => { setLogs([]); setMessages([]); addLog("Logs cleared"); }; const copyLogs = () => { const logText = logs.join("\n"); navigator.clipboard.writeText(logText); addLog("Logs copied to clipboard", "success"); }; const getStatusIcon = () => { switch (connectionStatus) { case "connected": return ; case "connecting": return ; case "error": return ; default: return ; } }; const commonTopics = [ { topic: "/speech", type: "std_msgs/String" }, { topic: "/cmd_vel", type: "geometry_msgs/Twist" }, { topic: "/joint_angles", type: "naoqi_bridge_msgs/JointAnglesWithSpeed" }, { topic: "/naoqi_driver/joint_states", type: "sensor_msgs/JointState" }, { topic: "/naoqi_driver/bumper", type: "naoqi_bridge_msgs/Bumper" }, ]; return (
{/* Connection Control */} {getStatusIcon()} Connection Control Connect to ROS Bridge at {ROS_BRIDGE_URL}
{connectionStatus.toUpperCase()} Attempts: {connectionAttempts}
{lastError && ( {lastError} )}
{connectionStatus !== "connected" ? ( ) : ( )}
{/* Message Testing */}
setSelectedTopic(e.target.value)} placeholder="/speech" />
setMessageType(e.target.value)} placeholder="std_msgs/String" />
setTestMessage(e.target.value)} placeholder="Hello from debug page" />
{/* Quick Topic Buttons */}
{commonTopics.map((item) => ( ))}
{/* Connection Logs */} Connection Logs
Real-time connection and message logs ({logs.length}/100)
{logs.map((log, index) => (
{log}
))} {logs.length === 0 && (
No logs yet...
)}
{/* Message Inspector */} Message Inspector Raw WebSocket messages sent and received ({messages.length}/50)
{messages.map((msg, index) => (
{msg.direction === "sent" ? "SENT" : "RECEIVED"} {msg.timestamp}
                      {JSON.stringify(msg.data, null, 2)}
                    
))} {messages.length === 0 && (
No messages yet. Connect and send a test message to see data here.
)}
); }