feat: Enhance PathRecorder component; implement path playback functionality and improve path loading logic

This commit is contained in:
MayaTheShy
2026-02-20 01:31:45 -05:00
parent f0afbca74b
commit bd68a79cd5

View File

@@ -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'}
</button>
<button
onClick={() => deletePath(path.pathId)}