nao6 ros2 integration updated

This commit is contained in:
2025-11-13 10:58:45 -05:00
parent 70882b9dbb
commit 86b5ed80c4
276 changed files with 4288 additions and 1552 deletions

0
src/app/(dashboard)/admin/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/admin/repositories/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/analytics/page.tsx Normal file → Executable file
View File

View File

@@ -0,0 +1,513 @@
"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<WebSocket | null>(null);
const [logs, setLogs] = useState<string[]>([]);
const [messages, setMessages] = useState<any[]>([]);
const [testMessage, setTestMessage] = useState("");
const [selectedTopic, setSelectedTopic] = useState("/speech");
const [messageType, setMessageType] = useState("std_msgs/String");
const [lastError, setLastError] = useState<string | null>(null);
const [connectionAttempts, setConnectionAttempts] = useState(0);
const logsEndRef = useRef<HTMLDivElement>(null);
const messagesEndRef = useRef<HTMLDivElement>(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 <CheckCircle className="h-4 w-4 text-green-600" />;
case "connecting":
return <Wifi className="h-4 w-4 animate-pulse text-blue-600" />;
case "error":
return <AlertTriangle className="h-4 w-4 text-red-600" />;
default:
return <WifiOff className="h-4 w-4 text-gray-400" />;
}
};
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 (
<PageLayout>
<PageHeader
title="ROS Bridge WebSocket Debug"
description="Debug and test WebSocket connection to ROS Bridge server"
/>
<div className="grid gap-6 md:grid-cols-2">
{/* Connection Control */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
{getStatusIcon()}
Connection Control
</CardTitle>
<CardDescription>
Connect to ROS Bridge at {ROS_BRIDGE_URL}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center gap-2">
<Badge
variant={
connectionStatus === "connected"
? "default"
: connectionStatus === "error"
? "destructive"
: "outline"
}
>
{connectionStatus.toUpperCase()}
</Badge>
<span className="text-sm text-muted-foreground">
Attempts: {connectionAttempts}
</span>
</div>
{lastError && (
<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
<AlertDescription className="text-sm">{lastError}</AlertDescription>
</Alert>
)}
<div className="flex gap-2">
{connectionStatus !== "connected" ? (
<Button
onClick={connectToRos}
disabled={connectionStatus === "connecting"}
className="flex-1"
>
<Play className="mr-2 h-4 w-4" />
{connectionStatus === "connecting" ? "Connecting..." : "Connect"}
</Button>
) : (
<Button
onClick={disconnectFromRos}
variant="outline"
className="flex-1"
>
<Square className="mr-2 h-4 w-4" />
Disconnect
</Button>
)}
</div>
<Separator />
{/* Message Testing */}
<div className="space-y-3">
<Label>Test Messages</Label>
<div className="grid grid-cols-2 gap-2">
<div>
<Label htmlFor="topic" className="text-xs">
Topic
</Label>
<Input
id="topic"
value={selectedTopic}
onChange={(e) => setSelectedTopic(e.target.value)}
placeholder="/speech"
/>
</div>
<div>
<Label htmlFor="msgType" className="text-xs">
Message Type
</Label>
<Input
id="msgType"
value={messageType}
onChange={(e) => setMessageType(e.target.value)}
placeholder="std_msgs/String"
/>
</div>
</div>
<div>
<Label htmlFor="testMsg" className="text-xs">
Test Message
</Label>
<Input
id="testMsg"
value={testMessage}
onChange={(e) => setTestMessage(e.target.value)}
placeholder="Hello from debug page"
/>
</div>
<div className="flex gap-2">
<Button
onClick={sendTestMessage}
disabled={connectionStatus !== "connected"}
size="sm"
className="flex-1"
>
Publish
</Button>
<Button
onClick={subscribeToTopic}
disabled={connectionStatus !== "connected"}
size="sm"
variant="outline"
className="flex-1"
>
Subscribe
</Button>
</div>
{/* Quick Topic Buttons */}
<div className="space-y-1">
<Label className="text-xs">Quick Topics</Label>
<div className="grid grid-cols-1 gap-1">
{commonTopics.map((item) => (
<Button
key={item.topic}
onClick={() => {
setSelectedTopic(item.topic);
setMessageType(item.type);
}}
variant="ghost"
size="sm"
className="justify-start text-xs"
>
{item.topic}
</Button>
))}
</div>
</div>
</div>
</CardContent>
</Card>
{/* Connection Logs */}
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
Connection Logs
<div className="flex gap-1">
<Button onClick={copyLogs} size="sm" variant="ghost">
<Copy className="h-4 w-4" />
</Button>
<Button onClick={clearLogs} size="sm" variant="ghost">
<Trash2 className="h-4 w-4" />
</Button>
</div>
</CardTitle>
<CardDescription>
Real-time connection and message logs ({logs.length}/100)
</CardDescription>
</CardHeader>
<CardContent>
<ScrollArea className="h-64 w-full rounded border p-2">
<div className="space-y-1 font-mono text-xs">
{logs.map((log, index) => (
<div
key={index}
className={`${
log.includes("ERROR")
? "text-red-600"
: log.includes("SUCCESS")
? "text-green-600"
: "text-slate-600"
}`}
>
{log}
</div>
))}
{logs.length === 0 && (
<div className="text-muted-foreground">No logs yet...</div>
)}
<div ref={logsEndRef} />
</div>
</ScrollArea>
</CardContent>
</Card>
{/* Message Inspector */}
<Card className="md:col-span-2">
<CardHeader>
<CardTitle>Message Inspector</CardTitle>
<CardDescription>
Raw WebSocket messages sent and received ({messages.length}/50)
</CardDescription>
</CardHeader>
<CardContent>
<ScrollArea className="h-64 w-full rounded border p-2">
<div className="space-y-2">
{messages.map((msg, index) => (
<div
key={index}
className={`rounded p-2 text-xs ${
msg.direction === "sent"
? "bg-blue-50 border-l-2 border-blue-400"
: "bg-green-50 border-l-2 border-green-400"
}`}
>
<div className="flex items-center justify-between mb-1">
<Badge
variant={msg.direction === "sent" ? "default" : "secondary"}
className="text-xs"
>
{msg.direction === "sent" ? "SENT" : "RECEIVED"}
</Badge>
<span className="text-muted-foreground">{msg.timestamp}</span>
</div>
<pre className="whitespace-pre-wrap text-xs">
{JSON.stringify(msg.data, null, 2)}
</pre>
</div>
))}
{messages.length === 0 && (
<div className="text-center text-muted-foreground py-8">
No messages yet. Connect and send a test message to see data here.
</div>
)}
<div ref={messagesEndRef} />
</div>
</ScrollArea>
</CardContent>
</Card>
</div>
</PageLayout>
);
}

View File

0
src/app/(dashboard)/experiments/[id]/designer/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/experiments/[id]/edit/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/experiments/[id]/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/experiments/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/layout.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/nao-test/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/not-found.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/participants/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/plugins/browse/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/plugins/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/profile/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/studies/[id]/analytics/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/studies/[id]/edit/page.tsx Normal file → Executable file
View File

View File

0
src/app/(dashboard)/studies/[id]/experiments/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/studies/[id]/page.tsx Normal file → Executable file
View File

View File

0
src/app/(dashboard)/studies/[id]/participants/page.tsx Normal file → Executable file
View File

View File

0
src/app/(dashboard)/studies/[id]/plugins/page.tsx Normal file → Executable file
View File

View File

View File

0
src/app/(dashboard)/studies/[id]/trials/new/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/studies/[id]/trials/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/studies/new/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/studies/page.tsx Normal file → Executable file
View File

0
src/app/api/auth/[...nextauth]/route.ts Normal file → Executable file
View File

0
src/app/api/test-trial/route.ts Normal file → Executable file
View File

0
src/app/api/trpc/[trpc]/route.ts Normal file → Executable file
View File

0
src/app/api/upload/route.ts Normal file → Executable file
View File

0
src/app/auth/signin/page.tsx Normal file → Executable file
View File

0
src/app/auth/signout/page.tsx Normal file → Executable file
View File

0
src/app/auth/signup/page.tsx Normal file → Executable file
View File

0
src/app/dashboard/layout.tsx Normal file → Executable file
View File

0
src/app/dashboard/page.tsx Normal file → Executable file
View File

0
src/app/layout.tsx Normal file → Executable file
View File

0
src/app/page.tsx Normal file → Executable file
View File

0
src/app/unauthorized/page.tsx Normal file → Executable file
View File

0
src/components/admin/AdminContent.tsx Normal file → Executable file
View File

0
src/components/admin/admin-user-table.tsx Normal file → Executable file
View File

0
src/components/admin/repositories-columns.tsx Normal file → Executable file
View File

0
src/components/admin/repositories-data-table.tsx Normal file → Executable file
View File

0
src/components/admin/role-management.tsx Normal file → Executable file
View File

0
src/components/admin/system-stats.tsx Normal file → Executable file
View File

0
src/components/dashboard/DashboardContent.tsx Normal file → Executable file
View File

0
src/components/dashboard/app-sidebar.tsx Normal file → Executable file
View File

0
src/components/dashboard/study-guard.tsx Normal file → Executable file
View File

0
src/components/experiments/ExperimentForm.tsx Normal file → Executable file
View File

0
src/components/experiments/ExperimentsGrid.tsx Normal file → Executable file
View File

0
src/components/experiments/ExperimentsTable.tsx Normal file → Executable file
View File

0
src/components/experiments/designer/ActionRegistry.ts Normal file → Executable file
View File

View File

0
src/components/experiments/designer/DesignerRoot.tsx Normal file → Executable file
View File

View File

0
src/components/experiments/designer/StepPreview.tsx Normal file → Executable file
View File

View File

View File

View File

View File

View File

View File

0
src/components/experiments/designer/state/hashing.ts Normal file → Executable file
View File

0
src/components/experiments/designer/state/store.ts Normal file → Executable file
View File

View File

0
src/components/experiments/experiments-columns.tsx Normal file → Executable file
View File

0
src/components/experiments/experiments-data-table.tsx Normal file → Executable file
View File

0
src/components/participants/ParticipantForm.tsx Normal file → Executable file
View File

0
src/components/participants/ParticipantsTable.tsx Normal file → Executable file
View File

0
src/components/participants/ParticipantsView.tsx Normal file → Executable file
View File

0
src/components/plugins/plugin-store-browse.tsx Normal file → Executable file
View File

0
src/components/plugins/plugins-columns.tsx Normal file → Executable file
View File

0
src/components/plugins/plugins-data-table.tsx Normal file → Executable file
View File

0
src/components/profile/password-change-form.tsx Normal file → Executable file
View File

0
src/components/profile/profile-edit-form.tsx Normal file → Executable file
View File

0
src/components/studies/InviteMemberDialog.tsx Normal file → Executable file
View File

0
src/components/studies/StudiesGrid.tsx Normal file → Executable file
View File

0
src/components/studies/StudiesTable.tsx Normal file → Executable file
View File

0
src/components/studies/StudyCard.tsx Normal file → Executable file
View File

0
src/components/studies/StudyForm.tsx Normal file → Executable file
View File

0
src/components/studies/studies-columns.tsx Normal file → Executable file
View File

0
src/components/studies/studies-data-table.tsx Normal file → Executable file
View File

0
src/components/theme/index.ts Normal file → Executable file
View File

0
src/components/theme/theme-provider.tsx Normal file → Executable file
View File

0
src/components/theme/theme-script.tsx Normal file → Executable file
View File

0
src/components/theme/theme-toggle.tsx Normal file → Executable file
View File

0
src/components/theme/toaster.tsx Normal file → Executable file
View File

0
src/components/trials/TrialForm.tsx Normal file → Executable file
View File

0
src/components/trials/TrialsGrid.tsx Normal file → Executable file
View File

0
src/components/trials/TrialsTable.tsx Normal file → Executable file
View File

0
src/components/trials/execution/EventsLog.tsx Normal file → Executable file
View File

0
src/components/trials/views/ObserverView.tsx Normal file → Executable file
View File

0
src/components/trials/views/ParticipantView.tsx Normal file → Executable file
View File

0
src/components/trials/views/WizardView.tsx Normal file → Executable file
View File

0
src/components/trials/wizard/ActionControls.tsx Normal file → Executable file
View File

0
src/components/trials/wizard/EventsLogSidebar.tsx Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More