diff --git a/gcs/src/components/dashboard/tabsSection.jsx b/gcs/src/components/dashboard/tabsSection.jsx
index 9b782ceb7..9128515e6 100644
--- a/gcs/src/components/dashboard/tabsSection.jsx
+++ b/gcs/src/components/dashboard/tabsSection.jsx
@@ -3,30 +3,14 @@
contains tabs like data, action, missions, and camera.
*/
-// Native Imports
-import { useState, useCallback, useEffect } from "react"
-
// 3rd Party Imports
-import { Tabs, Button, Grid, NumberInput, Popover, Select } from "@mantine/core"
-import {
- useDisclosure,
- useLocalStorage,
- useSessionStorage,
-} from "@mantine/hooks"
-import { IconInfoCircle } from "@tabler/icons-react"
-import Webcam from "react-webcam"
-
-// Custom Components
-import DashboardDataModal from "../dashboardDataModal"
+import { Tabs } from "@mantine/core"
-// Helper Javascript Files
-import { socket } from "../../helpers/socket"
-import { DataMessage } from "../../helpers/dataDisplay"
-import {
- MISSION_STATES,
- COPTER_MODES_FLIGHT_MODE_MAP,
- PLANE_MODES_FLIGHT_MODE_MAP,
-} from "../../helpers/mavlinkConstants"
+// Tab Componenents
+import CameraTabsSection from "./tabsSectionTabs/CameraTabsSection"
+import ActionTabsSection from "./tabsSectionTabs/actionTabsSection"
+import MissionTabsSection from "./tabsSectionTabs/missionTabsSection"
+import DataTabsSection from "./tabsSectionTabs/dataTabsSection"
export default function TabsSection({
connected,
@@ -38,90 +22,8 @@ export default function TabsSection({
displayedData,
setDisplayedData,
}) {
- const [selectedBox, setSelectedBox] = useState(null)
- const [opened, { open, close }] = useDisclosure(false)
- const [newFlightModeNumber, setNewFlightModeNumber] = useState(3) // Default to AUTO mode
const tabPadding = "pt-6"
- const [takeoffAltitude, setTakeoffAltitude] = useLocalStorage({
- key: "takeoffAltitude",
- defaultValue: 10,
- })
-
- const handleDoubleClick = (box) => {
- setSelectedBox(box)
- open()
- }
-
- const handleCheckboxChange = (key, subkey, subvalue, boxId, isChecked) => {
- // Update wantedData on checkbox change
- if (isChecked) {
- const newDisplay = displayedData.map((item, index) => {
- if (index === boxId) {
- return {
- ...item,
- currently_selected: `${key}.${subkey}`,
- display_name: subvalue,
- }
- }
- return item
- })
- setDisplayedData(newDisplay)
- close()
- }
- }
-
- // Camera devices
- const [deviceId, setDeviceId] = useSessionStorage({
- key: "deviceId",
- defaultValue: null,
- })
- const [devices, setDevices] = useState([])
-
- const handleDevices = useCallback(
- (mediaDevices) =>
- setDevices(mediaDevices.filter(({ kind }) => kind === "videoinput")),
- [setDevices],
- )
-
- useEffect(() => {
- navigator.mediaDevices.enumerateDevices().then(handleDevices)
- }, [handleDevices])
-
- function getFlightModeMap() {
- if (aircraftType === 1) {
- return PLANE_MODES_FLIGHT_MODE_MAP
- } else if (aircraftType === 2) {
- return COPTER_MODES_FLIGHT_MODE_MAP
- }
-
- return {}
- }
-
- function armDisarm(arm, force = false) {
- // TODO: Add force arm ability
- socket.emit("arm_disarm", { arm: arm, force: force })
- }
-
- function setNewFlightMode(modeNumber) {
- if (modeNumber === null || modeNumber === currentFlightModeNumber) {
- return
- }
- socket.emit("set_current_flight_mode", { newFlightMode: modeNumber })
- }
-
- function controlMission(action) {
- socket.emit("control_mission", { action })
- }
-
- function takeoff() {
- socket.emit("takeoff", { alt: takeoffAltitude })
- }
-
- function land() {
- socket.emit("land")
- }
-
return (
@@ -130,214 +32,42 @@ export default function TabsSection({
Mission
Camera
-
-
-
-
- {displayedData.length > 0 ? (
- displayedData.map((data) => (
- handleDoubleClick(data)} // Pass boxId to the function
- >
-
-
- ))
- ) : (
-
-
-
Double Click to select data
-
- )}
-
-
-
-
-
-
-
- {/* Arming/Flight Modes */}
- {!connected ? (
-
-
- No actions are available right now. Connect a drone to begin
-
-
- ) : (
-
- {/* Flight mode */}
- {currentFlightModeNumber !== null && (
-
-
- )}
-
- {/* Arm, Takeoff, Land */}
-
-
-
- {/* Take off button with popover */}
-
-
-
-
-
-
-
-
-
-
- {/* Land Button */}
-
-
-
- )}
-
-
-
-
-
- {!connected ? (
-
-
- No mission actions are available right now. Connect a drone to
- begin
-
-
- ) : (
-
- {/* Mission Information Text */}
-
-
- Mission state:
- {MISSION_STATES[currentMissionData.mission_state]}
-
-
- Waypoint:
- {currentMissionData.seq}/{currentMissionData.total}
-
-
- Distance to WP:
- {(navControllerOutputData.wp_dist
- ? navControllerOutputData.wp_dist
- : 0
- ).toFixed(2)}
- m
-
-
-
- {/* Auto mode, start, and restart buttons */}
-
-
-
-
-
-
-
-
- )}
-
-
-
-
-
-
-
+ {/* Data */}
+
+
+ {/* Actions */}
+
+
+ {/* Mission */}
+
+
+ {/* Camera */}
+
)
}
+
+export const NoConnectionMsg = ({ message }) => {
+ return (
+
+ )
+}
diff --git a/gcs/src/components/dashboard/tabsSectionTabs/actionTabsSection.jsx b/gcs/src/components/dashboard/tabsSectionTabs/actionTabsSection.jsx
new file mode 100644
index 000000000..7d69045c2
--- /dev/null
+++ b/gcs/src/components/dashboard/tabsSectionTabs/actionTabsSection.jsx
@@ -0,0 +1,170 @@
+/**
+ * ActionTabsSection
+ * This file holds all relevant component to perform actions on the drone, within the action tab in tabsSection.
+ */
+
+// Native
+import { useState } from "react"
+
+// Mantine
+import { Button, NumberInput, Popover, Tabs, Select } from "@mantine/core"
+import { useLocalStorage } from "@mantine/hooks"
+
+// Mavlink
+import {
+ COPTER_MODES_FLIGHT_MODE_MAP,
+ PLANE_MODES_FLIGHT_MODE_MAP,
+} from "../../../helpers/mavlinkConstants"
+
+// Helper
+import { socket } from "../../../helpers/socket"
+import { NoConnectionMsg } from "../tabsSection"
+
+export default function ActionTabsSection({
+ connected,
+ tabPadding,
+ currentFlightModeNumber,
+ aircraftType,
+ getIsArmed,
+}) {
+ return (
+
+
+ {!connected ? (
+
+ ) : (
+
+ {/** Flight Mode */}
+
+ {/** Arm / Takeoff / Landing */}
+
+
+ )}
+
+
+ )
+}
+
+const FlightModeAction = ({ aircraftType, currentFlightModeNumber }) => {
+ const [newFlightModeNumber, setNewFlightModeNumber] = useState(3) // Default to AUTO mode
+
+ // flight mode handling
+ function setNewFlightMode(modeNumber) {
+ if (modeNumber === null || modeNumber === currentFlightModeNumber) {
+ return
+ }
+ socket.emit("set_current_flight_mode", { newFlightMode: modeNumber })
+ }
+
+ function getFlightModeMap() {
+ if (aircraftType === 1) {
+ return PLANE_MODES_FLIGHT_MODE_MAP
+ } else if (aircraftType === 2) {
+ return COPTER_MODES_FLIGHT_MODE_MAP
+ }
+
+ return {}
+ }
+
+ return (
+ <>
+ {currentFlightModeNumber !== null && (
+
+
+ )}
+ >
+ )
+}
+
+const ArmTakeoffLandAction = ({ getIsArmed }) => {
+ const [takeoffAltitude, setTakeoffAltitude] = useLocalStorage({
+ key: "takeoffAltitude",
+ defaultValue: 10,
+ })
+
+ function armDisarm(arm, force = false) {
+ // TODO: Add force arm ability
+ socket.emit("arm_disarm", { arm: arm, force: force })
+ }
+
+ function takeoff() {
+ socket.emit("takeoff", { alt: takeoffAltitude })
+ }
+
+ function land() {
+ socket.emit("land")
+ }
+
+ return (
+ <>
+
+
+
+ {/** Takeoff button with popover */}
+
+
+
+
+
+
+
+
+
+
+ {/** Land Button */}
+
+
+ >
+ )
+}
diff --git a/gcs/src/components/dashboard/tabsSectionTabs/cameraTabsSection.jsx b/gcs/src/components/dashboard/tabsSectionTabs/cameraTabsSection.jsx
new file mode 100644
index 000000000..d1a3451fe
--- /dev/null
+++ b/gcs/src/components/dashboard/tabsSectionTabs/cameraTabsSection.jsx
@@ -0,0 +1,51 @@
+/**
+ * CameraTabsSection
+ * This file contains all relevant components to select and display the drone camera output.
+ */
+
+// Native
+import { useCallback, useState, useEffect } from "react"
+
+// Mantine
+import { useSessionStorage } from "@mantine/hooks"
+import { Tabs, Select } from "@mantine/core"
+
+// Helper
+import Webcam from "react-webcam"
+
+export default function CameraTabsSection({ tabPadding }) {
+ // Camera devices
+ const [deviceId, setDeviceId] = useSessionStorage({
+ key: "deviceId",
+ defaultValue: null,
+ })
+ const [devices, setDevices] = useState([])
+
+ const handleDevices = useCallback(
+ (mediaDevices) =>
+ setDevices(mediaDevices.filter(({ kind }) => kind === "videoinput")),
+ [setDevices],
+ )
+
+ useEffect(() => {
+ navigator.mediaDevices.enumerateDevices().then(handleDevices)
+ }, [handleDevices])
+
+ return (
+
+
+
+
+ )
+}
diff --git a/gcs/src/components/dashboard/tabsSectionTabs/dataTabsSection.jsx b/gcs/src/components/dashboard/tabsSectionTabs/dataTabsSection.jsx
new file mode 100644
index 000000000..d0c011034
--- /dev/null
+++ b/gcs/src/components/dashboard/tabsSectionTabs/dataTabsSection.jsx
@@ -0,0 +1,88 @@
+/**
+ * DataTabsSection
+ * This file contains all relevant components to display drone data with the data tab in tabsSection.
+ */
+
+// Native
+import { useState } from "react"
+
+// Mantine
+import { useDisclosure } from "@mantine/hooks"
+import { Tabs, Grid } from "@mantine/core"
+
+// Custom Components
+import DashboardDataModal from "../../dashboardDataModal"
+
+// Icons
+import { IconInfoCircle } from "@tabler/icons-react"
+
+// Helper
+import { DataMessage } from "../../../helpers/dataDisplay"
+
+export default function DataTabsSection({
+ tabPadding,
+ displayedData,
+ setDisplayedData,
+}) {
+ const [selectedBox, setSelectedBox] = useState(null)
+ const [opened, { open, close }] = useDisclosure(false)
+
+ const handleDoubleClick = (box) => {
+ setSelectedBox(box)
+ open()
+ }
+
+ const handleCheckboxChange = (key, subkey, subvalue, boxId, isChecked) => {
+ // Update wantedData on checkbox change
+ if (isChecked) {
+ const newDisplay = displayedData.map((item, index) => {
+ if (index === boxId) {
+ return {
+ ...item,
+ currently_selected: `${key}.${subkey}`,
+ display_name: subvalue,
+ }
+ }
+ return item
+ })
+ setDisplayedData(newDisplay)
+ close()
+ }
+ }
+
+ return (
+
+
+
+ {displayedData.length > 0 ? (
+ displayedData.map((data) => (
+ handleDoubleClick(data)} // Pass boxId to the function
+ >
+
+
+ ))
+ ) : (
+
+
+
Double Click to select data
+
+ )}
+
+
+
+
+ )
+}
diff --git a/gcs/src/components/dashboard/tabsSectionTabs/missionTabsSection.jsx b/gcs/src/components/dashboard/tabsSectionTabs/missionTabsSection.jsx
new file mode 100644
index 000000000..d81da0f58
--- /dev/null
+++ b/gcs/src/components/dashboard/tabsSectionTabs/missionTabsSection.jsx
@@ -0,0 +1,144 @@
+/**
+ * MissionTabsSection
+ * This file contains all relevant components to display and modify mission status and information within the mission section
+ * in tabsSection.
+ */
+
+// Mantine
+import { Button, Tabs } from "@mantine/core"
+
+// Mavlink
+import {
+ MISSION_STATES,
+ COPTER_MODES_FLIGHT_MODE_MAP,
+ PLANE_MODES_FLIGHT_MODE_MAP,
+} from "../../../helpers/mavlinkConstants"
+
+// Helper
+import { socket } from "../../../helpers/socket"
+import { NoConnectionMsg } from "../tabsSection"
+
+export default function MissionTabsSection({
+ connected,
+ tabPadding,
+ currentMissionData,
+ navControllerOutputData,
+ currentFlightModeNumber,
+ aircraftType,
+}) {
+ return (
+
+
+ {!connected ? (
+
+ ) : (
+
+ {/** Mission Information */}
+
+
+ {/** Auto, Start and Restart Mission */}
+
+
+ )}
+
+
+ )
+}
+
+const MissionInfo = ({ currentMissionData, navControllerOutputData }) => {
+ return (
+ <>
+ {/** Mission Information */}
+
+
+ Mission State:
+ {MISSION_STATES[currentMissionData.mission_state]}
+
+
+ Waypoint:
+ {currentMissionData.seq}/{currentMissionData.total}
+
+
+ Distance to WP:
+ {(navControllerOutputData.wp_dist
+ ? navControllerOutputData.wp_dist
+ : 0
+ ).toFixed(2)}
+ ,
+
+
+ >
+ )
+}
+
+const AutoStartRestartMission = ({ aircraftType, currentFlightModeNumber }) => {
+ // this is repeated code, will be updated after socket functionality is changed
+ function setNewFlightMode(modeNumber) {
+ if (modeNumber === null || modeNumber === currentFlightModeNumber) {
+ return
+ }
+ socket.emit("set_current_flight_mode", { newFlightMode: modeNumber })
+ }
+
+ function controlMission(action) {
+ socket.emit("control_mission", { action })
+ }
+
+ function getFlightModeMap() {
+ if (aircraftType === 1) {
+ return PLANE_MODES_FLIGHT_MODE_MAP
+ } else if (aircraftType === 2) {
+ return COPTER_MODES_FLIGHT_MODE_MAP
+ }
+
+ return {}
+ }
+
+ return (
+ <>
+
+ {/** Auto Mode */}
+
+
+ {/** Start Mission */}
+
+
+ {/** Restart Mission */}
+
+
+ >
+ )
+}