diff --git a/src/components/panels/ArmControlPanel.tsx b/src/components/panels/ArmControlPanel.tsx new file mode 100644 index 0000000..bfc7894 --- /dev/null +++ b/src/components/panels/ArmControlPanel.tsx @@ -0,0 +1,200 @@ +'use client'; + +import React, { useEffect, useState } from 'react'; +import ROSLIB from 'roslib'; +import { useROS } from '@/ros/ROSContext'; + +const ArmControlPanel: React.FC = () => { + const { ros } = useROS(); + + const [poseNames, setPoseNames] = useState([]); + const [selectedPose, setSelectedPose] = useState(''); + const [response, setResponse] = useState<{ success: boolean; message: string } | null>(null); + + const refreshPoseNames = () => { + if (!ros) return; + + const service = new ROSLIB.Service({ + ros, + name: '/get_names_poses', + serviceType: 'interfaces/srv/GetPoses', + }); + + const request = new ROSLIB.ServiceRequest({}); + + service.callService(request, (result: any) => { + const names = result.pose_names ?? []; + setPoseNames(names); + + if (names.length > 0 && !selectedPose) { + setSelectedPose(names[0]); + } + }); + }; + + useEffect(() => { + refreshPoseNames(); + }, [ros]); + + const handleGo = () => { + if (!ros) { + alert('ROS is not connected'); + return; + } + + if (!selectedPose) { + setResponse({ success: false, message: 'No pose selected' }); + return; + } + + const service = new ROSLIB.Service({ + ros, + name: '/go_to_pose', + serviceType: 'interfaces/srv/GoToPose', + }); + + const request = new ROSLIB.ServiceRequest({ + name: selectedPose, + }); + + service.callService(request, (result: any) => { + setResponse({ + success: result.success, + message: result.message, + }); + }); + }; + + const handleStop = () => { + if (!ros) { + alert('ROS is not connected'); + return; + } + + const service = new ROSLIB.Service({ + ros, + name: '/stop_motion', + serviceType: 'std_srvs/srv/Trigger', + }); + + const request = new ROSLIB.ServiceRequest({}); + + service.callService(request, (result: any) => { + setResponse({ + success: result.success, + message: result.message, + }); + }); + }; + + return ( +
+
+ + +
+ + + + + + {response && ( +
+ {response.message} +
+ )} + + +
+ ); +}; + +export default ArmControlPanel; \ No newline at end of file diff --git a/src/components/panels/MosaicDashboard.tsx b/src/components/panels/MosaicDashboard.tsx index 692a5cf..996bf75 100644 --- a/src/components/panels/MosaicDashboard.tsx +++ b/src/components/panels/MosaicDashboard.tsx @@ -21,6 +21,7 @@ import MotorStatusPanel from './MotorStatusPanel'; import AntennaControlPanel from './AntennaControlPanel'; import ScienceControlPanel from './ScienceControlPanel'; import { CO2Graph, MethaneGraph } from './ScienceGraphPanels'; +import ArmControlPanel from './ArmControlPanel'; type TileType = | 'mapView' @@ -35,7 +36,8 @@ type TileType = | 'antennaControlPanel' | 'scienceControlPanel' | 'co2Graph' - | 'methaneGraph'; + | 'methaneGraph' + | 'armControlPanel'; type TileId = `${TileType}:${number}`; @@ -53,6 +55,7 @@ const TILE_DISPLAY_NAMES: Record = { scienceControlPanel: 'Science Motor Control', co2Graph: 'CO2 Graph', methaneGraph: 'Methane Graph', + armControlPanel: 'Arm Control', }; const ALL_TILE_TYPES: TileType[] = [ @@ -68,7 +71,8 @@ const ALL_TILE_TYPES: TileType[] = [ 'antennaControlPanel', 'scienceControlPanel', 'co2Graph', - 'methaneGraph' + 'methaneGraph', + 'armControlPanel', ]; function tileTypeOf(id: TileId): TileType { @@ -118,10 +122,15 @@ function buildDefaultLayout(makeTileId: (type: TileType) => TileId): MosaicNode< direction: 'row', first: { direction: 'row', - first: makeTileId('rosMonitor'), + first: { + direction: 'column', + first: makeTileId('antennaControlPanel'), + second: makeTileId('MotorStatusPanel'), + splitPercentage: 35, + }, second: makeTileId('networkHealthMonitor'), }, - second: makeTileId('orientationDisplay'), + second: makeTileId('rosMonitor'), splitPercentage: 55, }, splitPercentage: 55, @@ -139,7 +148,7 @@ function buildDefaultLayout(makeTileId: (type: TileType) => TileId): MosaicNode< }, splitPercentage: 50, }, - splitPercentage: 50, + splitPercentage: 60, }, splitPercentage: 60, }; @@ -398,6 +407,12 @@ const MosaicDashboard: React.FC = () => { ) + case 'armControlPanel': + return( + + + + ) default: return
Unknown tile
;