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 && ( -
- { - return { value: device.deviceId, label: device.label } - })} - value={deviceId} - onChange={setDeviceId} - /> - {deviceId !== null && ( - - )} -
- + {/* Data */} + + + {/* Actions */} + + + {/* Mission */} + + + {/* Camera */} + ) } + +export const NoConnectionMsg = ({ message }) => { + return ( +
+

{message}

+
+ ) +} 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 && ( +
+ { + return { value: device.deviceId, label: device.label } + })} + value={deviceId} + onChange={setDeviceId} + /> + {deviceId !== null && ( + + )} +
+ + ) +} 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 */} + +
+ + ) +}