Skip to content

Commit 3e33929

Browse files
authored
847 show feedback when writing multiple params (#848)
* Start adding parameter saving progress modal and fix some bugs with paramsController * Fix bug with writing params failing * Show which params failed to be set * Fix mypy typing error * Address copilot review comments * Add variable plural to failed to write modal title * Fix broken pytests * Fix flight modes test
1 parent 4ecc991 commit 3e33929

11 files changed

Lines changed: 482 additions & 85 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Modal, Table } from "@mantine/core"
2+
import { useDispatch, useSelector } from "react-redux"
3+
import {
4+
selectParamsFailedToWrite,
5+
selectParamsFailedToWriteModalOpen,
6+
setParamsFailedToWriteModalOpen,
7+
} from "../../redux/slices/paramsSlice"
8+
9+
export default function ParamsFailedToWriteModal() {
10+
const dispatch = useDispatch()
11+
const opened = useSelector(selectParamsFailedToWriteModalOpen)
12+
const paramsFailedToWriteList = useSelector(selectParamsFailedToWrite)
13+
14+
return (
15+
<Modal
16+
opened={opened}
17+
onClose={() => dispatch(setParamsFailedToWriteModalOpen(false))}
18+
title={`Failed to write ${paramsFailedToWriteList.length} parameter${paramsFailedToWriteList.length > 1 ? "s" : ""}`}
19+
centered
20+
overlayProps={{
21+
backgroundOpacity: 0.55,
22+
blur: 3,
23+
}}
24+
>
25+
<div className="flex flex-col items-center justify-center gap-4 w-full">
26+
<Table.ScrollContainer maxHeight={600} className="w-full">
27+
<Table striped highlightOnHover withColumnBorders>
28+
<Table.Thead>
29+
<Table.Tr>
30+
<Table.Th className="w-56">Parameter</Table.Th>
31+
<Table.Th className="w-40">Value</Table.Th>
32+
</Table.Tr>
33+
</Table.Thead>
34+
<Table.Tbody>
35+
{paramsFailedToWriteList
36+
.sort((a, b) => a.param_id.localeCompare(b.param_id))
37+
.map((param) => (
38+
<Table.Tr key={param.param_id}>
39+
<Table.Td>{param.param_id}</Table.Td>
40+
<Table.Td>{param.param_value}</Table.Td>
41+
</Table.Tr>
42+
))}
43+
</Table.Tbody>
44+
</Table>
45+
</Table.ScrollContainer>
46+
</div>
47+
</Modal>
48+
)
49+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Modal, Progress } from "@mantine/core"
2+
import { useDispatch, useSelector } from "react-redux"
3+
import {
4+
selectParamsWriteProgressData,
5+
selectParamsWriteProgressModalOpen,
6+
setParamsWriteProgressModalOpen,
7+
} from "../../redux/slices/paramsSlice"
8+
9+
export default function ParamsWriteModal() {
10+
const dispatch = useDispatch()
11+
const opened = useSelector(selectParamsWriteProgressModalOpen)
12+
const progressData = useSelector(selectParamsWriteProgressData)
13+
const progressPercentage =
14+
progressData.current_index && progressData.total_params
15+
? (progressData.current_index / progressData.total_params) * 100
16+
: 0
17+
18+
return (
19+
<Modal
20+
opened={opened}
21+
onClose={() => dispatch(setParamsWriteProgressModalOpen(false))}
22+
title={"Writing params to drone"}
23+
closeOnClickOutside={false}
24+
closeOnEscape={false}
25+
withCloseButton={false}
26+
centered
27+
overlayProps={{
28+
backgroundOpacity: 0.55,
29+
blur: 3,
30+
}}
31+
>
32+
<div className="flex flex-col items-center justify-center mt-4">
33+
{progressData.message && (
34+
<p className="text-center mb-2">{progressData.message}</p>
35+
)}
36+
37+
<Progress
38+
animated
39+
size="lg"
40+
transitionDuration={300}
41+
value={progressPercentage}
42+
className="w-full mx-auto my-auto"
43+
/>
44+
</div>
45+
</Modal>
46+
)
47+
}

gcs/src/helpers/notification.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const tailwindColors = resolveConfig(tailwindConfig).theme.colors
1414
export const redColor = tailwindColors.red[600]
1515
export const greenColor = tailwindColors.green[600]
1616
export const blueColor = tailwindColors.blue[600]
17+
export const yellowColor = tailwindColors.yellow[600]
1718

1819
const notificationTheme = {
1920
style: {
@@ -40,6 +41,15 @@ export function showSuccessNotification(message) {
4041
})
4142
}
4243

44+
export function showWarningNotification(message) {
45+
notifications.show({
46+
title: "Warning",
47+
message: message,
48+
color: yellowColor,
49+
...notificationTheme,
50+
})
51+
}
52+
4353
export function showInfoNotification(message) {
4454
notifications.show({
4555
title: "Info",

gcs/src/params.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import { Row } from "./components/params/row.jsx"
2929
// Redux
3030
import { useDispatch, useSelector } from "react-redux"
3131
import LoadParamsFileModal from "./components/params/loadParamsFileModal.jsx"
32+
import ParamsFailedToWriteModal from "./components/params/paramsFailedToWriteModal.jsx"
33+
import ParamsWriteModal from "./components/params/paramsWriteModal.jsx"
3234
import { EXCLUDE_PARAMS_LOAD } from "./helpers/mavlinkConstants.js"
3335
import { showErrorNotification } from "./helpers/notification.js"
3436
import { selectConnectedToDrone } from "./redux/slices/droneConnectionSlice.js"
@@ -210,6 +212,8 @@ export default function Params() {
210212
<Layout currentPage="params">
211213
<AutopilotRebootModal />
212214
<LoadParamsFileModal />
215+
<ParamsWriteModal />
216+
<ParamsFailedToWriteModal />
213217

214218
{connected ? (
215219
<div className="flex flex-col h-screen overflow-hidden">

gcs/src/redux/middleware/emitters.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
emitRebootAutopilot,
5151
emitRefreshParams,
5252
emitSetMultipleParams,
53+
setParamsWriteProgressModalOpen,
5354
} from "../slices/paramsSlice"
5455
import { resetMessages } from "../slices/statusTextSlice"
5556

@@ -269,7 +270,10 @@ export function handleEmitters(socket, store, action) {
269270
},
270271
{
271272
emitter: emitSetMultipleParams,
272-
callback: () => socket.socket.emit("set_multiple_params", action.payload),
273+
callback: () => {
274+
socket.socket.emit("set_multiple_params", action.payload)
275+
store.dispatch(setParamsWriteProgressModalOpen(true))
276+
},
273277
},
274278
{
275279
emitter: emitExportParamsToFile,

gcs/src/redux/middleware/socketMiddleware.js

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { FRAME_CLASS_MAP } from "../../helpers/mavlinkConstants.js"
2727
import {
2828
showErrorNotification,
2929
showSuccessNotification,
30+
showWarningNotification,
3031
} from "../../helpers/notification.js"
3132
import SocketFactory from "../../helpers/socket"
3233
import {
@@ -90,13 +91,18 @@ import {
9091
setUpdatePlannedHomePositionFromLoadModal,
9192
} from "../slices/missionSlice"
9293
import {
94+
resetParamsWriteProgressData,
9395
setAutoPilotRebootModalOpen,
9496
setFetchingVars,
9597
setFetchingVarsProgress,
9698
setHasFetchedOnce,
9799
setModifiedParams,
98100
setParams,
99101
setParamSearchValue,
102+
setParamsFailedToWrite,
103+
setParamsFailedToWriteModalOpen,
104+
setParamsWriteProgressData,
105+
setParamsWriteProgressModalOpen,
100106
setRebootData,
101107
setShownParams,
102108
updateParamValue,
@@ -136,6 +142,7 @@ const ParamSpecificSocketEvents = Object.freeze({
136142
onParamSetSuccess: "param_set_success",
137143
onParamError: "params_error",
138144
onExportParamsResult: "export_params_result",
145+
onSetMultipleParamsProgress: "set_multiple_params_progress",
139146
})
140147

141148
const MissionSpecificSocketEvents = Object.freeze({
@@ -549,17 +556,49 @@ const socketMiddleware = (store) => {
549556
)
550557

551558
socket.socket.on(ParamSpecificSocketEvents.onParamSetSuccess, (msg) => {
552-
showSuccessNotification(msg.message)
553-
store.dispatch(setModifiedParams([]))
559+
const paramsSetSuccessfully = msg.data.params_set_successfully
560+
const paramsNotSet = msg.data.params_could_not_set
561+
if (paramsNotSet.length > 0 && paramsSetSuccessfully.length > 0) {
562+
showWarningNotification(msg.message)
563+
} else if (paramsNotSet.length > 0) {
564+
showErrorNotification(msg.message)
565+
} else {
566+
showSuccessNotification(msg.message)
567+
}
568+
569+
const modifiedParams = store.getState().paramsSlice.modifiedParams
570+
571+
// Only clear the params that got set successfully
572+
store.dispatch(
573+
setModifiedParams(
574+
modifiedParams.filter(
575+
(param) =>
576+
!paramsSetSuccessfully.some(
577+
(setParam) => setParam.param_id === param.param_id,
578+
),
579+
),
580+
),
581+
)
582+
554583
// Update the param in the params list also
555-
for (let param of msg.data) {
584+
for (let param of paramsSetSuccessfully) {
556585
store.dispatch(updateParamValue(param))
557586
}
587+
588+
store.dispatch(resetParamsWriteProgressData())
589+
store.dispatch(setParamsWriteProgressModalOpen(false))
590+
591+
if (paramsNotSet.length !== 0) {
592+
store.dispatch(setParamsFailedToWrite(paramsNotSet))
593+
store.dispatch(setParamsFailedToWriteModalOpen(true))
594+
}
558595
})
559596

560597
socket.socket.on(ParamSpecificSocketEvents.onParamError, (msg) => {
561598
showErrorNotification(msg.message)
562599
store.dispatch(setFetchingVars(false))
600+
store.dispatch(resetParamsWriteProgressData())
601+
store.dispatch(setParamsWriteProgressModalOpen(false))
563602
})
564603

565604
socket.socket.on(
@@ -573,6 +612,20 @@ const socketMiddleware = (store) => {
573612
},
574613
)
575614

615+
socket.socket.on(
616+
ParamSpecificSocketEvents.onSetMultipleParamsProgress,
617+
(msg) => {
618+
store.dispatch(
619+
setParamsWriteProgressData({
620+
message: msg.message,
621+
param_id: msg.param_id,
622+
current_index: msg.current_index,
623+
total_params: msg.total_params,
624+
}),
625+
)
626+
},
627+
)
628+
576629
socket.socket.on(
577630
DroneSpecificSocketEvents.onNavRepositionResult,
578631
(msg) => {

gcs/src/redux/slices/paramsSlice.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ const paramsSlice = createSlice({
1616
loadParamsFileModalOpen: false,
1717
loadedFileName: "",
1818
loadedParams: [],
19+
paramsWriteProgressData: {
20+
param_id: "",
21+
current_index: 0,
22+
total_params: 0,
23+
},
24+
paramsWriteProgressModalOpen: false,
25+
paramsFailedToWrite: [],
26+
paramsFailedToWriteModalOpen: false,
1927
},
2028
reducers: {
2129
setRebootData: (state, action) => {
@@ -128,6 +136,25 @@ const paramsSlice = createSlice({
128136
setLoadedParams: (state, action) => {
129137
state.loadedParams = action.payload
130138
},
139+
setParamsWriteProgressData: (state, action) => {
140+
state.paramsWriteProgressData = action.payload
141+
},
142+
resetParamsWriteProgressData: (state) => {
143+
state.paramsWriteProgressData = {
144+
param_id: "",
145+
current_index: 0,
146+
total_params: 0,
147+
}
148+
},
149+
setParamsWriteProgressModalOpen: (state, action) => {
150+
state.paramsWriteProgressModalOpen = action.payload
151+
},
152+
setParamsFailedToWrite: (state, action) => {
153+
state.paramsFailedToWrite = action.payload
154+
},
155+
setParamsFailedToWriteModalOpen: (state, action) => {
156+
state.paramsFailedToWriteModalOpen = action.payload
157+
},
131158

132159
// Emitters (empty objects to be captured in the middleware)
133160
emitRebootAutopilot: () => {},
@@ -152,6 +179,12 @@ const paramsSlice = createSlice({
152179
selectLoadParamsFileModalOpen: (state) => state.loadParamsFileModalOpen,
153180
selectLoadedFileName: (state) => state.loadedFileName,
154181
selectLoadedParams: (state) => state.loadedParams,
182+
selectParamsWriteProgressData: (state) => state.paramsWriteProgressData,
183+
selectParamsWriteProgressModalOpen: (state) =>
184+
state.paramsWriteProgressModalOpen,
185+
selectParamsFailedToWrite: (state) => state.paramsFailedToWrite,
186+
selectParamsFailedToWriteModalOpen: (state) =>
187+
state.paramsFailedToWriteModalOpen,
155188
},
156189
})
157190

@@ -174,6 +207,11 @@ export const {
174207
setLoadParamsFileModalOpen,
175208
setLoadedFileName,
176209
setLoadedParams,
210+
setParamsWriteProgressData,
211+
resetParamsWriteProgressData,
212+
setParamsWriteProgressModalOpen,
213+
setParamsFailedToWrite,
214+
setParamsFailedToWriteModalOpen,
177215
emitRebootAutopilot,
178216
emitRefreshParams,
179217
emitSetMultipleParams,
@@ -194,6 +232,10 @@ export const {
194232
selectLoadParamsFileModalOpen,
195233
selectLoadedFileName,
196234
selectLoadedParams,
235+
selectParamsWriteProgressData,
236+
selectParamsWriteProgressModalOpen,
237+
selectParamsFailedToWrite,
238+
selectParamsFailedToWriteModalOpen,
197239
} = paramsSlice.selectors
198240

199241
export default paramsSlice

0 commit comments

Comments
 (0)