From c85aacc71d5754cff8faf8c8703e3641a1500073 Mon Sep 17 00:00:00 2001 From: Nathaniel Hargrave Date: Fri, 7 Aug 2026 09:12:01 -0600 Subject: [PATCH 1/3] Map scale + slightly broken screenshot --- src/components/BreadCrumbTrail.tsx | 22 +++++++++++++++++++--- src/components/panels/MapView.tsx | 30 ++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/components/BreadCrumbTrail.tsx b/src/components/BreadCrumbTrail.tsx index 895b86c..2199b89 100644 --- a/src/components/BreadCrumbTrail.tsx +++ b/src/components/BreadCrumbTrail.tsx @@ -5,7 +5,6 @@ import { Circle, Polyline, useMap } from 'react-leaflet'; import { useROS } from '@/ros/ROSContext'; import { useWaypoints } from '@/contexts/WaypointContext'; import ROSLIB from 'roslib'; -import L from 'leaflet' interface Breadcrumb { coordinate: [number, number]; @@ -14,7 +13,11 @@ interface Breadcrumb { altitude?: number; } -const BreadcrumbTrail: React.FC = () => { +type BreadcrumbTrailProps = { + downloadPNG: Function, +} + +const BreadcrumbTrail: React.FC = ({ downloadPNG }) => { const map = useMap(); const { ros, connectionStatus } = useROS(); const { addWaypoint } = useWaypoints(); @@ -222,6 +225,19 @@ const BreadcrumbTrail: React.FC = () => { > Re-center + {lastFix && ( - Drill Height: {(DRILL_HEIGHT + (((elevator?.position ?? 0) - zero) * CM_PER_TICK)).toFixed(2)} cm above ground - Drill Current: {drill?.output_current?.toFixed(2) ?? '--'} A - Drill Percent: {((drill?.output_percent ?? 0) * 100).toFixed(0)}% - -
- Elevator Current: {elevator?.output_current?.toFixed(2) ?? '--'} A - Elevator Percent: {((elevator?.output_percent ?? 0) * 100).toFixed(0)}% -
-
{motors.map((motor) => { background: #00ffcc; transition: width 0.1s linear; } - - .drill-info { - border: 1px solid #3a3a3a; - border-radius: 10px; - padding: .75rem; - margin-bottom: 1rem; - box-shadow: 0 3px 10px rgba(0,0,0,.4); - background: linear-gradient(145deg,#2b2b2b,#202020); - color: #eaeaea; - } - - .right { - vertical-align: -moz-middle-with-baseline; - vertical-align: -webkit-baseline-middle; - } - - .drill-reading { - font-weight: 600; - } `}
); diff --git a/src/components/panels/ScienceSensorPanel.tsx b/src/components/panels/ScienceSensorPanel.tsx index 1825fa4..578f26e 100644 --- a/src/components/panels/ScienceSensorPanel.tsx +++ b/src/components/panels/ScienceSensorPanel.tsx @@ -27,13 +27,40 @@ interface CO2Point { co2: number; } -type SensorKey = 'adc1' | 'adc2' | 'adc3' | 'co2'; +interface DrillPoint { + time: number; + drill_cur: number; +} + +interface ElevatorPoint { + time: number; + height: number; + elev_cur: number; +} + +type SensorKey = 'adc1' | 'adc2' | 'adc3' | 'co2' | 'motor'; type Temp = { temperature: number; humidity: number; } +interface MotorStatus { + temperature: number; + bus_voltage: number; + output_percent: number; + output_voltage: number; + output_current: number; + position: number; + velocity: number; + fwd_limit: boolean; + rev_limit: boolean; + active_errors: number; +} + +const CM_PER_TICK = 0.15 / 13 / 70; // 1.5 mm pitch thread, 1:13 gear ratio, 12-bit encoder +const DRILL_HEIGHT = 52; + const SENSOR_OPTIONS: { key: SensorKey; label: string; @@ -64,6 +91,12 @@ const SENSOR_OPTIONS: { color: '#ff4d4d', unit: '', }, + { + key: 'motor', + label: 'Motors', + color: '', + unit: '', + }, ]; const ScienceSensorPanel: React.FC = () => { @@ -71,9 +104,12 @@ const ScienceSensorPanel: React.FC = () => { const [adc, setAdc] = useState([]); const [co2, setCo2] = useState([]); + const [drill, setDrill] = useState([]); + const [elevator, setElevator] = useState([]); const [temp, setTemp] = useState(); const [selectedSensor, setSelectedSensor] = useState('adc1'); const [windowSize, setWindowSize] = useState(30); + const [zero, setZero] = useState(0); // TODO: Service for resetting talon encoder estimate const containerRef = useRef(null); @@ -159,9 +195,66 @@ const ScienceSensorPanel: React.FC = () => { }; }, [ros]); - const latestValue = selectedSensor === 'co2' ? + useEffect(() => { + if (!ros) return; + + const drillTopic = new ROSLIB.Topic({ + ros, + name: '/drill/status', + messageType: 'ros_phoenix/msg/MotorStatus', + }); + + const handleDrillReading = (msg: any) => { + const newPoint: DrillPoint = { + time: Date.now(), + drill_cur: (msg as MotorStatus).output_current, + }; + + setDrill((prev) => { + const updated = [...prev, newPoint]; + return updated.length > windowSize ? updated.slice(-windowSize) : updated; + }); + }; + + drillTopic.subscribe(handleDrillReading); + + return () => { + drillTopic.unsubscribe(handleDrillReading); + }; + }, [ros]); + + useEffect(() => { + if (!ros) return; + + const elevatorTopic = new ROSLIB.Topic({ + ros, + name: '/elevator/status', + messageType: 'ros_phoenix/msg/MotorStatus', + }); + + const handleElevatorReading = (msg: any) => { + const newPoint: ElevatorPoint = { + time: Date.now(), + elev_cur: (msg as MotorStatus).output_current, + height: DRILL_HEIGHT + (((msg as MotorStatus).position) * CM_PER_TICK) - zero, + }; + + setElevator((prev) => { + const updated = [...prev, newPoint]; + return updated.length > windowSize ? updated.slice(-windowSize) : updated; + }); + }; + + elevatorTopic.subscribe(handleElevatorReading); + + return () => { + elevatorTopic.unsubscribe(handleElevatorReading); + }; + }, [ros]); + + const latestValue = selectedSensor=== 'motor' ? null : (selectedSensor === 'co2' ? (co2.length > 0 ? co2[co2.length - 1][selectedSensor] : null) - : (adc.length > 0 ? adc[adc.length - 1][selectedSensor] : null); + : (adc.length > 0 ? adc[adc.length - 1][selectedSensor] : null)); const formatTime = (time: number) => new Date(time).toLocaleTimeString([], { @@ -192,9 +285,33 @@ const ScienceSensorPanel: React.FC = () => {

Science Sensor Reading

Temperature: {temp?.temperature?.toFixed(1)}°C Humidity: {temp?.humidity?.toFixed(1)}%

-

- {selectedOption.label}: {latestValue !== null ? formatValue(latestValue) : '--'} -

+ {selectedSensor === 'motor' ? ( +

+ + Height: {elevator.length > 0 ? elevator[elevator.length - 1].height.toFixed(2) : 0} cm + Elev. Cur: {elevator.length > 0 ? elevator[elevator.length - 1].elev_cur.toFixed(2) : 0} A + Drill Cur: {drill.length > 0 ? drill[drill.length - 1].drill_cur.toFixed(2) : 0} A +

+ ) : ( +

+ {selectedOption.label}: {latestValue !== null ? formatValue(latestValue) : '--'} +

+ )}
@@ -224,61 +341,156 @@ const ScienceSensorPanel: React.FC = () => {
- - - - - - - formatValue(Number(value))} - tick={{ fill: '#aaa', fontSize: 10 }} - axisLine={{ stroke: '#444' }} - tickLine={{ stroke: '#444' }} - width={55} - /> - - [ - formatValue(Number(value)), - selectedOption.label, - ]} - labelFormatter={(value) => formatTime(Number(value))} - contentStyle={{ - background: '#222', - border: '1px solid #444', - borderRadius: '8px', - color: '#fff', - }} - /> - - - - - - + {selectedSensor === 'motor' ? ( + + + + + + + formatValue(Number(value))} + tick={{ fill: '#aaa', fontSize: 10 }} + axisLine={{ stroke: '#444' }} + tickLine={{ stroke: '#444' }} + width={55} + unit="A" + /> + formatValue(Number(value))} + tick={{ fill: '#aaa', fontSize: 10 }} + axisLine={{ stroke: '#444' }} + tickLine={{ stroke: '#444' }} + width={55} + orientation="right" + unit="cm" + /> + [ + value.toFixed(2), + name, + ]} + labelFormatter={(value) => formatTime(Number(value))} + contentStyle={{ + background: '#222', + border: '1px solid #444', + borderRadius: '8px', + color: '#fff', + }} + /> + + + + + + + + + ) : ( + + + + + + + formatValue(Number(value))} + tick={{ fill: '#aaa', fontSize: 10 }} + axisLine={{ stroke: '#444' }} + tickLine={{ stroke: '#444' }} + width={55} + /> + + [ + formatValue(Number(value)), + selectedOption.label, + ]} + labelFormatter={(value) => formatTime(Number(value))} + contentStyle={{ + background: '#222', + border: '1px solid #444', + borderRadius: '8px', + color: '#fff', + }} + /> + + + + + + + )}