Spaces:
Running
Running
File size: 8,806 Bytes
18b0fa5 |
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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
<script lang="ts">
import * as Dialog from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import * as Card from "@/components/ui/card";
import * as Alert from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import { toast } from "svelte-sonner";
import { robotManager } from "$lib/robot/RobotManager.svelte";
import { getApiBaseUrl, getWebSocketBaseUrl } from "$lib/utils/config";
import type { Robot } from "$lib/robot/Robot.svelte";
interface Props {
open: boolean;
robot: Robot | null;
}
let { open = $bindable(), robot }: Props = $props();
// Robot selection modal state for server slaves
let showRobotSelectionModal = $state(false);
let availableServerRobots = $state<{ id: string; name: string; robot_type: string }[]>([]);
let selectedServerRobotId = $state<string>("");
// Get URLs from configuration
const apiBaseUrl = getApiBaseUrl();
const wsBaseUrl = getWebSocketBaseUrl();
// Slave connection functions
async function connectMockSlave() {
if (!robot) return;
try {
await robotManager.connectMockSlave(robot.id, 50);
} catch (err) {
toast.error("Failed to Connect Mock Slave", {
description: `Could not connect mock slave: ${err}`
});
console.error(err);
}
}
async function connectUSBSlave() {
if (!robot) return;
try {
await robotManager.connectUSBSlave(robot.id);
} catch (err) {
toast.error("Failed to Connect USB Slave", {
description: `Could not connect USB slave: ${err}`
});
console.error(err);
}
}
async function connectRemoteServerSlave() {
if (!robot) return;
try {
// First, fetch available robots from the server
const response = await fetch(`${apiBaseUrl}/api/robots`);
if (!response.ok) {
throw new Error(`Server responded with ${response.status}: ${response.statusText}`);
}
const robots = await response.json();
if (robots.length === 0) {
toast.error("No Server Robots Available", {
description: "No robots available on the server. Create a robot on the server first."
});
return;
}
// Show modal for robot selection
availableServerRobots = robots;
selectedServerRobotId = robots[0]?.id || "";
showRobotSelectionModal = true;
} catch (err) {
toast.error("Failed to Fetch Server Robots", {
description: `Could not fetch server robots: ${err}`
});
console.error(err);
}
}
async function confirmRobotSelection() {
if (!robot || !selectedServerRobotId) return;
try {
await robotManager.connectRemoteServerSlave(
robot.id,
wsBaseUrl,
undefined,
selectedServerRobotId
);
// Close modal
showRobotSelectionModal = false;
} catch (err) {
toast.error("Failed to Connect Remote Server Slave", {
description: `Could not connect remote server slave: ${err}`
});
console.error(err);
}
}
function cancelRobotSelection() {
showRobotSelectionModal = false;
selectedServerRobotId = "";
}
async function disconnectSlave(slaveId: string) {
if (!robot) return;
try {
await robotManager.disconnectSlave(robot.id, slaveId);
} catch (err) {
toast.error("Failed to Disconnect Slave", {
description: `Could not disconnect slave: ${err}`
});
console.error(err);
}
}
</script>
<Dialog.Root bind:open>
<Dialog.Content
class="max-h-[80vh] max-w-xl overflow-y-auto border-slate-600 bg-slate-900 text-slate-100"
>
<Dialog.Header class="pb-3">
<Dialog.Title class="flex items-center gap-2 text-lg font-bold text-slate-100">
<span class="icon-[mdi--devices] size-5 text-blue-400"></span>
Slave Connection
</Dialog.Title>
<Dialog.Description class="text-sm text-slate-400">
Configure slave targets for robot {robot?.id}
</Dialog.Description>
</Dialog.Header>
{#if robot}
<div class="space-y-4">
<!-- Current Status - Compact -->
<div
class="flex items-center justify-between rounded-lg border border-blue-500/30 bg-blue-900/20 p-3"
>
<div class="flex items-center gap-2">
<span class="icon-[fa6-solid--ear-listen] size-4 text-blue-400"></span>
<span class="text-sm font-medium text-blue-300">Slave Status</span>
</div>
<Badge variant="default" class="bg-blue-600 text-xs">
{robot.connectedSlaves.length} / {robot.slaves.length} Connected
</Badge>
</div>
<!-- Slave Controls -->
<Card.Root class="border-blue-500/30 bg-blue-500/5">
<Card.Header class="pb-2">
<Card.Title class="flex items-center gap-2 text-base text-blue-200">
<span class="icon-[mdi--devices] size-4"></span>
Connection Options
</Card.Title>
</Card.Header>
<Card.Content class="space-y-3">
<div class="space-y-2">
<Button
variant="secondary"
onclick={connectMockSlave}
class="h-8 w-full bg-yellow-600 text-sm text-white hover:bg-yellow-700"
>
<span class="icon-[mdi--robot-confused] mr-2 size-4"></span>
Add Mock Slave
</Button>
<Button
variant="secondary"
onclick={connectUSBSlave}
class="h-8 w-full bg-green-600 text-sm text-white hover:bg-green-700"
>
<span class="icon-[mdi--usb] mr-2 size-4"></span>
Add USB Slave
</Button>
<Button
variant="secondary"
onclick={connectRemoteServerSlave}
class="h-8 w-full bg-purple-600 text-sm text-white hover:bg-purple-700"
>
<span class="icon-[mdi--cloud] mr-2 size-4"></span>
Add Remote Server Slave
</Button>
</div>
{#if robot.slaves.length > 0}
<Separator />
<div class="space-y-2">
<p class="text-xs font-medium text-blue-300">Connected Slaves:</p>
<div class="max-h-32 space-y-1 overflow-y-auto">
{#each robot.slaves as slave}
<div class="flex items-center justify-between rounded-md bg-slate-700/50 p-2">
<div class="flex items-center gap-2">
<span class="icon-[mdi--circle] size-2 text-green-400"></span>
<span class="text-sm text-slate-300">{slave.name}</span>
<Badge variant="outline" class="text-xs">{slave.id.slice(0, 8)}</Badge>
</div>
<Button
variant="destructive"
size="sm"
onclick={() => disconnectSlave(slave.id)}
class="h-6 px-2 text-xs"
>
<span class="icon-[mdi--close] size-3"></span>
</Button>
</div>
{/each}
</div>
</div>
{/if}
</Card.Content>
</Card.Root>
<!-- Quick Info -->
<div class="rounded border border-slate-700 bg-slate-800/30 p-2 text-xs text-slate-500">
<span class="icon-[mdi--information] mr-1 size-3"></span>
Slaves are output targets. Multiple can be connected simultaneously.
</div>
</div>
{/if}
</Dialog.Content>
</Dialog.Root>
<!-- Robot Selection Modal for Remote Server Slaves -->
{#if showRobotSelectionModal}
<Dialog.Root open={true}>
<Dialog.Content class="max-w-md border-slate-600 bg-slate-900 text-slate-100">
<Dialog.Header>
<Dialog.Title class="flex items-center gap-2 text-slate-100">
<span class="icon-[ix--robotic-arm] size-5"></span>
Select Server Robot
</Dialog.Title>
<Dialog.Description class="text-slate-400">
Choose which server robot to connect as a slave target
</Dialog.Description>
</Dialog.Header>
<div class="space-y-4">
<div class="space-y-2">
<label for="server-robot-select" class="text-sm font-medium text-slate-300"
>Available robots on server:</label
>
<select
bind:value={selectedServerRobotId}
class="w-full rounded-md border border-slate-600 bg-slate-700 px-3 py-2 text-sm text-slate-100"
id="server-robot-select"
>
{#each availableServerRobots as serverRobot}
<option value={serverRobot.id}>
{serverRobot.name} ({serverRobot.id}) - {serverRobot.robot_type}
</option>
{/each}
</select>
</div>
<Alert.Root>
<span class="icon-[mdi--information] size-4"></span>
<Alert.Description>
This will connect your local robot <strong>"{robot?.id}"</strong> as a slave to receive commands
from the selected server robot.
</Alert.Description>
</Alert.Root>
</div>
<Dialog.Footer class="flex justify-end gap-3">
<Button variant="outline" onclick={cancelRobotSelection}>Cancel</Button>
<Button
onclick={confirmRobotSelection}
disabled={!selectedServerRobotId}
class="bg-purple-600 hover:bg-purple-700"
>
<span class="icon-[mdi--link] mr-1 size-4"></span>
Connect Slave
</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
{/if}
|