From 524ab94de072f1cd6de2a25bb04a3740da2098d6 Mon Sep 17 00:00:00 2001 From: Kush Makkapati Date: Fri, 19 Sep 2025 16:32:53 +0100 Subject: [PATCH 1/4] Fix guided mode bug and convert to redux --- gcs/src/components/dashboard/map.jsx | 37 +++----------------- gcs/src/components/missions/missionsMap.jsx | 19 ++++------ gcs/src/redux/middleware/socketMiddleware.js | 37 +++++++++++++------- gcs/src/redux/slices/droneInfoSlice.js | 11 ++++++ 4 files changed, 47 insertions(+), 57 deletions(-) diff --git a/gcs/src/components/dashboard/map.jsx b/gcs/src/components/dashboard/map.jsx index 7286f9832..46145062b 100644 --- a/gcs/src/components/dashboard/map.jsx +++ b/gcs/src/components/dashboard/map.jsx @@ -11,12 +11,7 @@ import React, { useEffect, useRef, useState } from "react" // Maplibre and mantine imports import { Button, Divider, Modal, NumberInput } from "@mantine/core" -import { - useClipboard, - useDisclosure, - useLocalStorage, - useSessionStorage, -} from "@mantine/hooks" +import { useClipboard, useDisclosure, useLocalStorage } from "@mantine/hooks" import "maplibre-gl/dist/maplibre-gl.css" import Map from "react-map-gl/maplibre" @@ -25,6 +20,7 @@ import { useSelector } from "react-redux" import { selectFlightModeString, selectGPS, + selectGuidedModePinData, selectHomePosition, } from "../../redux/slices/droneInfoSlice" import { selectCurrentMissionItems } from "../../redux/slices/missionSlice" @@ -32,11 +28,7 @@ import { selectCurrentMissionItems } from "../../redux/slices/missionSlice" // Helper scripts import { intToCoord } from "../../helpers/dataFormatters" import { filterMissionItems } from "../../helpers/filterMissions" -import { - showErrorNotification, - showNotification, - showSuccessNotification, -} from "../../helpers/notification" +import { showNotification } from "../../helpers/notification" import { useSettings } from "../../helpers/settings" import { socket } from "../../helpers/socket" @@ -51,7 +43,6 @@ import useContextMenu from "../mapComponents/useContextMenu" import { envelope, featureCollection, point } from "@turf/turf" import resolveConfig from "tailwindcss/resolveConfig" import tailwindConfig from "../../../tailwind.config" -import { selectConnectedToDrone } from "../../redux/slices/droneConnectionSlice" import FenceItems from "../mapComponents/fenceItems" import HomeMarker from "../mapComponents/homeMarker" const tailwindColors = resolveConfig(tailwindConfig).theme.colors @@ -60,11 +51,11 @@ const coordsFractionDigits = 7 function MapSectionNonMemo({ passedRef, onDragstart, mapId = "dashboard" }) { // Redux - const connected = useSelector(selectConnectedToDrone) const gpsData = useSelector(selectGPS) const missionItems = useSelector(selectCurrentMissionItems) const homePosition = useSelector(selectHomePosition) // use actual home position const flightModeString = useSelector(selectFlightModeString) + const guidedModePinData = useSelector(selectGuidedModePinData) const [position, setPosition] = useState(null) const [firstCenteredToDrone, setFirstCenteredToDrone] = useState(false) @@ -102,26 +93,6 @@ function MapSectionNonMemo({ passedRef, onDragstart, mapId = "dashboard" }) { const [opened, { open, close }] = useDisclosure(false) const clipboard = useClipboard({ timeout: 500 }) - const [guidedModePinData, setGuidedModePinData] = useSessionStorage({ - key: "guidedModePinData", - defaultValue: null, - }) - - useEffect(() => { - socket.on("nav_reposition_result", (msg) => { - if (!msg.success) { - showErrorNotification(msg.message) - } else { - showSuccessNotification(msg.message) - setGuidedModePinData(msg.data) - } - }) - - return () => { - socket.off("nav_reposition_result") - } - }, [connected]) - useEffect(() => { // Check latest gpsData point is valid if ( diff --git a/gcs/src/components/missions/missionsMap.jsx b/gcs/src/components/missions/missionsMap.jsx index c4c13edd5..9b43fb1c0 100644 --- a/gcs/src/components/missions/missionsMap.jsx +++ b/gcs/src/components/missions/missionsMap.jsx @@ -10,12 +10,7 @@ import React, { useEffect, useRef, useState } from "react" // Maplibre and mantine imports -import { - useClipboard, - useLocalStorage, - usePrevious, - useSessionStorage, -} from "@mantine/hooks" +import { useClipboard, useLocalStorage, usePrevious } from "@mantine/hooks" import "maplibre-gl/dist/maplibre-gl.css" import Map from "react-map-gl/maplibre" @@ -47,6 +42,7 @@ import { useDispatch, useSelector } from "react-redux" import { selectFlightModeString, selectGPS, + selectGuidedModePinData, } from "../../redux/slices/droneInfoSlice" import { clearDrawingItems, @@ -81,11 +77,7 @@ function MapSectionNonMemo({ const flightModeString = useSelector(selectFlightModeString) const currentTab = useSelector(selectActiveTab) const contextMenuState = useSelector(selectContextMenu) - - const [guidedModePinData] = useSessionStorage({ - key: "guidedModePinData", - defaultValue: null, - }) + const guidedModePinData = useSelector(selectGuidedModePinData) const [position, setPosition] = useState(null) const { getSetting } = useSettings() @@ -322,7 +314,10 @@ function MapSectionNonMemo({ addNewPolygonVertex(lat, lon) } else { dispatch( - createNewDefaultDrawingItem({ x: coordToInt(lat), y: coordToInt(lon) }), + createNewDefaultDrawingItem({ + x: coordToInt(lat), + y: coordToInt(lon), + }), ) } }} diff --git a/gcs/src/redux/middleware/socketMiddleware.js b/gcs/src/redux/middleware/socketMiddleware.js index 3ef4e831f..6061ca0f6 100644 --- a/gcs/src/redux/middleware/socketMiddleware.js +++ b/gcs/src/redux/middleware/socketMiddleware.js @@ -34,6 +34,7 @@ import { setExtraData, setGpsData, setGpsRawIntData, + setGuidedModePinData, setHeartbeatData, setHomePosition, setLastGraphMessage, @@ -94,6 +95,7 @@ const DroneSpecificSocketEvents = Object.freeze({ onNavResult: "nav_result", onHomePositionResult: "home_position_result", onIncomingMsg: "incoming_msg", + onNavRepositionResult: "nav_reposition_result", onGetLoiterRadiusResult: "nav_get_loiter_radius_result", onSetLoiterRadiusResult: "nav_set_loiter_radius_result", }) @@ -271,6 +273,7 @@ const socketMiddleware = (store) => { } } + store.dispatch(setGuidedModePinData({ lat: 0, lon: 0, alt: 0 })) store.dispatch(setRebootData({})) store.dispatch(setAutoPilotRebootModalOpen(false)) }) @@ -366,9 +369,18 @@ const socketMiddleware = (store) => { store.dispatch(setFetchingVars(false)) }) - /* - Missions - */ + socket.socket.on( + DroneSpecificSocketEvents.onNavRepositionResult, + (msg) => { + if (msg.success) { + store.dispatch(queueSuccessNotification(msg.message)) + store.dispatch(setGuidedModePinData(msg.data)) + } else { + store.dispatch(queueErrorNotification(msg.message)) + } + }, + ) + socket.socket.on( DroneSpecificSocketEvents.onGetLoiterRadiusResult, (msg) => { @@ -389,16 +401,17 @@ const socketMiddleware = (store) => { : queueNotification({ type: "error", message: msg.message }), ) }, - ), - /* + ) + + /* Missions */ - socket.socket.on( - MissionSpecificSocketEvents.onCurrentMissionAll, - (msg) => { - store.dispatch(setCurrentMissionItems(msg)) - }, - ) + socket.socket.on( + MissionSpecificSocketEvents.onCurrentMissionAll, + (msg) => { + store.dispatch(setCurrentMissionItems(msg)) + }, + ) socket.socket.on( MissionSpecificSocketEvents.onCurrentMission, @@ -639,7 +652,7 @@ const socketMiddleware = (store) => { ) /* - Generic Drone Date + Generic Drone Data */ socket.socket.on(DroneSpecificSocketEvents.onIncomingMsg, (msg) => { incomingMessageHandler(msg) diff --git a/gcs/src/redux/slices/droneInfoSlice.js b/gcs/src/redux/slices/droneInfoSlice.js index bf524250f..ca65d28cf 100644 --- a/gcs/src/redux/slices/droneInfoSlice.js +++ b/gcs/src/redux/slices/droneInfoSlice.js @@ -52,6 +52,11 @@ const droneInfoSlice = createSlice({ extraDroneData: [ ...defaultDataMessages, // TODO: Should also be stored in local storage, values set to 0 on launch but actual messages stored ], + guidedModePinData: { + lat: 0, // Stored in coords not int + lon: 0, // Stores in coords not int + alt: 0, + }, graphs: { selectedGraphs: { graph_a: null, @@ -166,6 +171,9 @@ const droneInfoSlice = createSlice({ state.navControllerData.loiterRadius = action.payload } }, + setGuidedModePinData: (state, action) => { + state.guidedModePinData = action.payload + }, }, selectors: { selectAttitude: (state) => state.attitudeData, @@ -194,6 +202,7 @@ const droneInfoSlice = createSlice({ selectBatteryData: (state) => state.batteryData.sort((b1, b2) => b1.id - b2.id), + selectGuidedModePinData: (state) => state.guidedModePinData, selectExtraDroneData: (state) => state.extraDroneData, selectStatusText: (state) => state.statusText, selectGraphValues: (state) => state.graphs.selectedGraphs, @@ -219,6 +228,7 @@ export const { setGraphValues, setLastGraphMessage, setLoiterRadius, + setGuidedModePinData, } = droneInfoSlice.actions // Memoized selectors because redux is a bitch @@ -295,6 +305,7 @@ export const { selectFlightMode, selectAircraftType, selectBatteryData, + selectGuidedModePinData, selectExtraDroneData, selectGraphValues, selectLastGraphMessage, From 360a8a86e483e25944f606f2cefe1774cc977f30 Mon Sep 17 00:00:00 2001 From: Kush Makkapati Date: Fri, 19 Sep 2025 16:34:19 +0100 Subject: [PATCH 2/4] Update gcs/src/redux/slices/droneInfoSlice.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- gcs/src/redux/slices/droneInfoSlice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcs/src/redux/slices/droneInfoSlice.js b/gcs/src/redux/slices/droneInfoSlice.js index ca65d28cf..be77d40e8 100644 --- a/gcs/src/redux/slices/droneInfoSlice.js +++ b/gcs/src/redux/slices/droneInfoSlice.js @@ -54,7 +54,7 @@ const droneInfoSlice = createSlice({ ], guidedModePinData: { lat: 0, // Stored in coords not int - lon: 0, // Stores in coords not int + lon: 0, // Stored in coords not int alt: 0, }, graphs: { From 4312b4dac034e2985525a13c4b769db291410081 Mon Sep 17 00:00:00 2001 From: Kush Makkapati Date: Fri, 19 Sep 2025 17:04:40 +0100 Subject: [PATCH 3/4] Moved map notification to use redux notification queue --- gcs/src/components/dashboard/map.jsx | 7 ++++--- gcs/src/components/layout.jsx | 13 ++++++++++--- gcs/src/helpers/notification.js | 11 ++++++++++- gcs/src/main.jsx | 8 ++++---- gcs/src/redux/slices/notificationSlice.js | 4 ++++ 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/gcs/src/components/dashboard/map.jsx b/gcs/src/components/dashboard/map.jsx index 46145062b..db71919d6 100644 --- a/gcs/src/components/dashboard/map.jsx +++ b/gcs/src/components/dashboard/map.jsx @@ -16,7 +16,7 @@ import "maplibre-gl/dist/maplibre-gl.css" import Map from "react-map-gl/maplibre" // Redux -import { useSelector } from "react-redux" +import { useDispatch, useSelector } from "react-redux" import { selectFlightModeString, selectGPS, @@ -28,7 +28,6 @@ import { selectCurrentMissionItems } from "../../redux/slices/missionSlice" // Helper scripts import { intToCoord } from "../../helpers/dataFormatters" import { filterMissionItems } from "../../helpers/filterMissions" -import { showNotification } from "../../helpers/notification" import { useSettings } from "../../helpers/settings" import { socket } from "../../helpers/socket" @@ -43,6 +42,7 @@ import useContextMenu from "../mapComponents/useContextMenu" import { envelope, featureCollection, point } from "@turf/turf" import resolveConfig from "tailwindcss/resolveConfig" import tailwindConfig from "../../../tailwind.config" +import { queueInfoNotification } from "../../redux/slices/notificationSlice" import FenceItems from "../mapComponents/fenceItems" import HomeMarker from "../mapComponents/homeMarker" const tailwindColors = resolveConfig(tailwindConfig).theme.colors @@ -51,6 +51,7 @@ const coordsFractionDigits = 7 function MapSectionNonMemo({ passedRef, onDragstart, mapId = "dashboard" }) { // Redux + const dispatch = useDispatch() const gpsData = useSelector(selectGPS) const missionItems = useSelector(selectCurrentMissionItems) const homePosition = useSelector(selectHomePosition) // use actual home position @@ -335,7 +336,7 @@ function MapSectionNonMemo({ passedRef, onDragstart, mapId = "dashboard" }) { clipboard.copy( `${clickedGpsCoords.lat}, ${clickedGpsCoords.lng}`, ) - showNotification("Copied to clipboard") + dispatch(queueInfoNotification("Copied to clipboard")) }} >
diff --git a/gcs/src/components/layout.jsx b/gcs/src/components/layout.jsx index edd4cf749..cdb2233cc 100644 --- a/gcs/src/components/layout.jsx +++ b/gcs/src/components/layout.jsx @@ -12,6 +12,7 @@ import { Notifications } from "@mantine/notifications" // Helpers and custom component imports import { showErrorNotification, + showInfoNotification, showSuccessNotification, } from "../helpers/notification" import { socket } from "../helpers/socket" @@ -58,9 +59,15 @@ export default function Layout({ children, currentPage }) { // Show queued notifications useEffect(() => { if (notificationQueue.length !== 0) { - ;(notificationQueue[0].type === "error" - ? showErrorNotification - : showSuccessNotification)(notificationQueue[0].message) + const message = notificationQueue[0].message + console.log(message) + if (notificationQueue[0].type === "error") { + showErrorNotification(message) + } else if (notificationQueue[0].type === "success") { + showSuccessNotification(message) + } else { + showInfoNotification(message) + } dispatch(notificationShown()) } }, [notificationQueue, dispatch]) diff --git a/gcs/src/helpers/notification.js b/gcs/src/helpers/notification.js index a383cebee..05192bfae 100644 --- a/gcs/src/helpers/notification.js +++ b/gcs/src/helpers/notification.js @@ -1,5 +1,5 @@ /* - Notification system. This contains all the styles for each type of notification in an + Notification system. This contains all the styles for each type of notification in an easy to use wrapper. */ @@ -36,6 +36,15 @@ export function showSuccessNotification(message) { }) } +export function showInfoNotification(message) { + notifications.show({ + title: "Info", + message: message, + color: tailwindColors.blue[600], + ...notificationTheme, + }) +} + export function showNotification(title, message) { notifications.show({ title: title, diff --git a/gcs/src/main.jsx b/gcs/src/main.jsx index 31b96bd63..8ca07c744 100644 --- a/gcs/src/main.jsx +++ b/gcs/src/main.jsx @@ -2,25 +2,25 @@ import "./css/index.css" // Needs to be at the top of the file import "./css/resizable.css" // Style imports +import "@mantine/code-highlight/styles.css" import "@mantine/core/styles.css" import "@mantine/notifications/styles.css" import "@mantine/spotlight/styles.css" -import "@mantine/code-highlight/styles.css" import "@mantine/tiptap/styles.css" // React imports -import { HashRouter } from "react-router-dom" import ReactDOM from "react-dom/client" +import { HashRouter } from "react-router-dom" // Mantine imports import { MantineProvider } from "@mantine/core" // Component imports -import AppContent from "./components/mainContent.jsx" import { CustomMantineTheme } from "./components/customMantineTheme.jsx" +import AppContent from "./components/mainContent.jsx" // Redux -import { store } from "./redux/store.js" import { Provider } from "react-redux" +import { store } from "./redux/store.js" ReactDOM.createRoot(document.getElementById("root")).render( // diff --git a/gcs/src/redux/slices/notificationSlice.js b/gcs/src/redux/slices/notificationSlice.js index 9d6e63ba1..7a78ed541 100644 --- a/gcs/src/redux/slices/notificationSlice.js +++ b/gcs/src/redux/slices/notificationSlice.js @@ -12,6 +12,9 @@ const notificationSlice = createSlice({ queueNotification: (state, action) => { state.notifications.push(action.payload) }, + queueInfoNotification: (state, action) => { + state.notifications.push({ type: "info", message: action.payload }) + }, queueErrorNotification: (state, action) => { state.notifications.push({ type: "error", message: action.payload }) }, @@ -27,6 +30,7 @@ const notificationSlice = createSlice({ export const { notificationShown, queueNotification, + queueInfoNotification, queueErrorNotification, queueSuccessNotification, } = notificationSlice.actions From c9e9ae43c318fdea43d1a78061613196a3b6c46d Mon Sep 17 00:00:00 2001 From: Kush Makkapati Date: Fri, 19 Sep 2025 17:22:32 +0100 Subject: [PATCH 4/4] Remove console log --- gcs/src/components/layout.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/gcs/src/components/layout.jsx b/gcs/src/components/layout.jsx index cdb2233cc..860fb8d34 100644 --- a/gcs/src/components/layout.jsx +++ b/gcs/src/components/layout.jsx @@ -60,7 +60,6 @@ export default function Layout({ children, currentPage }) { useEffect(() => { if (notificationQueue.length !== 0) { const message = notificationQueue[0].message - console.log(message) if (notificationQueue[0].type === "error") { showErrorNotification(message) } else if (notificationQueue[0].type === "success") {