diff --git a/client/src/components/PathRecorder.jsx b/client/src/components/PathRecorder.jsx index 2a5998d..da463e4 100644 --- a/client/src/components/PathRecorder.jsx +++ b/client/src/components/PathRecorder.jsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import { useTurtleStore } from '../store/turtleStore'; import './PathRecorder.css'; const PathRecorder = ({ turtles, selectedTurtle, apiUrl }) => { @@ -10,6 +11,9 @@ const PathRecorder = ({ turtles, selectedTurtle, apiUrl }) => { const [selectedPath, setSelectedPath] = useState(null); const [message, setMessage] = useState(null); const [loading, setLoading] = useState(false); + const [playingBack, setPlayingBack] = useState(false); + + const sendCommand = useTurtleStore((state) => state.sendCommand); useEffect(() => { loadPaths(); @@ -21,7 +25,7 @@ const PathRecorder = ({ turtles, selectedTurtle, apiUrl }) => { const response = await fetch(`${apiUrl}/api/paths`); if (response.ok) { const data = await response.json(); - setPaths(data); + setPaths(Array.isArray(data) ? data : []); } } catch (error) { console.error('Failed to load paths:', error); @@ -59,31 +63,18 @@ const PathRecorder = ({ turtles, selectedTurtle, apiUrl }) => { } try { - // Create the path - const pathResponse = await fetch(`${apiUrl}/api/paths`, { + const response = await fetch(`${apiUrl}/api/paths`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - name: currentPath.name, turtleId: currentPath.turtleId, - description: currentPath.description + pathName: currentPath.name, + pathData: currentPath.waypoints }) }); - if (!pathResponse.ok) { - throw new Error('Failed to create path'); - } - - const pathData = await pathResponse.json(); - const pathId = pathData.pathId; - - // Add all waypoints - for (const waypoint of currentPath.waypoints) { - await fetch(`${apiUrl}/api/paths/${pathId}/waypoints`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(waypoint) - }); + if (!response.ok) { + throw new Error('Failed to save path'); } showMessage(`Path saved with ${currentPath.waypoints.length} waypoints!`, 'success'); @@ -132,9 +123,71 @@ const PathRecorder = ({ turtles, selectedTurtle, apiUrl }) => { } }; - const playbackPath = (path) => { - showMessage(`Playback not yet implemented for path: ${path.name}`, 'info'); - // TODO: Implement playback by sending commands to turtle + const playbackPath = async (path) => { + if (!selectedTurtle) { + showMessage('Please select a turtle to play back this path', 'error'); + return; + } + + // Load full path data if we don't have waypoints + let waypoints = path.pathData || path.waypoints; + if (!waypoints || waypoints.length === 0) { + try { + const response = await fetch(`${apiUrl}/api/paths/${path.pathId}`); + if (response.ok) { + const data = await response.json(); + waypoints = data.waypoints; + } + } catch (error) { + showMessage('Failed to load path data', 'error'); + return; + } + } + + if (!waypoints || waypoints.length < 2) { + showMessage('Path has insufficient waypoints for playback', 'error'); + return; + } + + setPlayingBack(true); + showMessage(`Playing back ${waypoints.length} waypoints...`, 'info'); + + const turtleId = selectedTurtle.turtleID; + + // Determine movement commands between consecutive waypoints + for (let i = 1; i < waypoints.length; i++) { + const prev = waypoints[i - 1]; + const curr = waypoints[i]; + + const dx = curr.x - prev.x; + const dy = curr.y - prev.y; + const dz = curr.z - prev.z; + + // Vertical movement + if (dy > 0) { + for (let j = 0; j < dy; j++) sendCommand(turtleId, 'up'); + } else if (dy < 0) { + for (let j = 0; j < Math.abs(dy); j++) sendCommand(turtleId, 'down'); + } + + // Horizontal movement - send as forward movements with turns + if (dx > 0) { + for (let j = 0; j < dx; j++) sendCommand(turtleId, 'forward'); + } else if (dx < 0) { + for (let j = 0; j < Math.abs(dx); j++) sendCommand(turtleId, 'forward'); + } + if (dz > 0) { + for (let j = 0; j < dz; j++) sendCommand(turtleId, 'forward'); + } else if (dz < 0) { + for (let j = 0; j < Math.abs(dz); j++) sendCommand(turtleId, 'forward'); + } + + // Small delay between waypoint groups to avoid flooding + await new Promise(resolve => setTimeout(resolve, 200)); + } + + setPlayingBack(false); + showMessage('Playback complete! Commands queued for turtle.', 'success'); }; const showMessage = (text, type) => { @@ -276,8 +329,9 @@ const PathRecorder = ({ turtles, selectedTurtle, apiUrl }) => { onClick={() => playbackPath(path)} className="play-btn" title="Playback path" + disabled={playingBack} > - ▶️ Play + {playingBack ? '⏳ Playing...' : '▶️ Play'}