diff --git a/src/components/panels/HeadlightControlPanel.tsx b/src/components/panels/HeadlightControlPanel.tsx new file mode 100644 index 0000000..7c10168 --- /dev/null +++ b/src/components/panels/HeadlightControlPanel.tsx @@ -0,0 +1,193 @@ +'use client'; + +import React, { useEffect, useRef, useState } from 'react'; +import ROSLIB from 'roslib'; +import { useROS } from '@/ros/ROSContext'; + +const HeadlightControlPanel: React.FC = () => { + const { ros } = useROS(); + + const [leftValue, setLeftValue] = useState(0); + const [rightValue, setRightValue] = useState(0); + + const leftTopicRef = useRef(null); + const rightTopicRef = useRef(null); + + useEffect(() => { + if (!ros) { + leftTopicRef.current = null; + rightTopicRef.current = null; + return; + } + + leftTopicRef.current = new ROSLIB.Topic({ + ros, + name: '/left_headlight', + messageType: 'std_msgs/Int8', + }); + + rightTopicRef.current = new ROSLIB.Topic({ + ros, + name: '/right_headlight', + messageType: 'std_msgs/Int8', + }); + + return () => { + try { + leftTopicRef.current?.unadvertise(); + rightTopicRef.current?.unadvertise(); + } catch { + // ignore cleanup errors + } + + leftTopicRef.current = null; + rightTopicRef.current = null; + }; + }, [ros]); + + useEffect(() => { + if (!ros) return; + + leftTopicRef.current?.publish( + new ROSLIB.Message({ + data: leftValue, + }) + ); + + rightTopicRef.current?.publish( + new ROSLIB.Message({ + data: rightValue, + }) + ); + }, [ros, leftValue, rightValue]); + + const allOff = () => { + setLeftValue(0); + setRightValue(0); + }; + + return ( +
+
+
+ + +
+ setLeftValue(Number(e.target.value))} + className="vertical-slider" + disabled={!ros} + /> +
+ + {leftValue}% +
+ +
+ + +
+ setRightValue(Number(e.target.value))} + className="vertical-slider" + disabled={!ros} + /> +
+ + {rightValue}% +
+
+ + + + +
+ ); +}; + +export default HeadlightControlPanel; \ No newline at end of file diff --git a/src/components/panels/MosaicDashboard.tsx b/src/components/panels/MosaicDashboard.tsx index b816e10..83dfb90 100644 --- a/src/components/panels/MosaicDashboard.tsx +++ b/src/components/panels/MosaicDashboard.tsx @@ -26,6 +26,7 @@ import PDBRailsPanel from './PDBRails'; import ArmControlPanel from './ArmControlPanel'; import WebRTCClient from './WebRTCClient'; import TimerPanel from './TimerPanel'; +import HeadlightControlPanel from './HeadlightControlPanel'; import { ROVER_IP } from '@/constants'; @@ -47,6 +48,7 @@ type TileType = | 'armControlPanel' | 'webRTCClient' | 'timerPanel' + | 'headlightControlPanel' ; type TileId = `${TileType}:${number}`; @@ -69,6 +71,7 @@ const TILE_DISPLAY_NAMES: Record = { armControlPanel: 'Arm Control', webRTCClient: 'WebRTC Client', timerPanel: 'Multi-Timer', + headlightControlPanel: 'Headlights', }; const ALL_TILE_TYPES: TileType[] = [ @@ -89,6 +92,7 @@ const ALL_TILE_TYPES: TileType[] = [ 'armControlPanel', 'webRTCClient', 'timerPanel', + 'headlightControlPanel', ]; function tileTypeOf(id: TileId): TileType { @@ -455,6 +459,12 @@ const MosaicDashboard: React.FC = () => { ); + case 'headlightControlPanel': + return ( + + + + ); default: return
Unknown tile
; }