feat: Initialize Turtle Control Center with React and WebSocket server

- Added index.html for the main entry point of the client application.
- Created package.json for client dependencies and scripts.
- Implemented App component with view controls and layout.
- Added CSS styles for the application and components.
- Developed ControlPanel component for turtle management and commands.
- Created Map3D component for 3D visualization of turtles.
- Established Zustand store for state management of turtles and WebSocket connection.
- Set up server with Express and WebSocket for handling turtle updates and commands.
- Added REST API endpoints for turtle status updates and command handling.
- Implemented start.sh script for setting up and running the application.
This commit is contained in:
MayaTheShy
2026-02-15 23:49:46 -05:00
parent dc6a9a94b7
commit 29dfde9f25
14 changed files with 1398 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
import { create } from 'zustand';
const WS_URL = 'ws://localhost:3002';
const API_URL = 'http://localhost:3001';
export const useTurtleStore = create((set, get) => ({
// State
turtles: {},
selectedTurtleId: null,
connected: false,
ws: null,
// WebSocket connection
connect: () => {
const ws = new WebSocket(WS_URL);
ws.onopen = () => {
console.log('✅ Connected to server');
set({ connected: true, ws });
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'initial_state') {
const turtlesMap = {};
data.turtles.forEach(turtle => {
turtlesMap[turtle.turtleID] = turtle;
});
set({ turtles: turtlesMap });
} else if (data.type === 'turtle_update') {
set(state => ({
turtles: {
...state.turtles,
[data.turtle.turtleID]: data.turtle
}
}));
} else if (data.type === 'turtle_disconnected') {
set(state => {
const newTurtles = { ...state.turtles };
delete newTurtles[data.turtleID];
return { turtles: newTurtles };
});
}
} catch (error) {
console.error('Error processing message:', error);
}
};
ws.onclose = () => {
console.log('❌ Disconnected from server');
set({ connected: false, ws: null });
// Attempt reconnect after 3 seconds
setTimeout(() => {
get().connect();
}, 3000);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
},
// Actions
selectTurtle: (turtleId) => {
set({ selectedTurtleId: turtleId });
},
sendCommand: async (turtleId, command, param = null) => {
const { ws } = get();
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
type: 'command',
turtleID: turtleId,
command,
param
}));
} else {
// Fallback to REST API
try {
await fetch(`${API_URL}/api/turtle/${turtleId}/command`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ command, param })
});
} catch (error) {
console.error('Error sending command:', error);
}
}
},
// Helper getters
getTurtleArray: () => {
return Object.values(get().turtles);
},
getSelectedTurtle: () => {
const { turtles, selectedTurtleId } = get();
return selectedTurtleId ? turtles[selectedTurtleId] : null;
}
}));