Skip to content

Commit fb6ba0d

Browse files
core: frontend: Autopilot: add SITL configuration panel
Add a dedicated expansion panel (visible only when SITL is the running board) with a frame-type dropdown. Selecting a frame POSTs to /sitl_frame and automatically restarts the autopilot so the new frame takes effect (matching set_board behavior), with a loading spinner and success snackbar for feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 5039b66 commit fb6ba0d

5 files changed

Lines changed: 193 additions & 2 deletions

File tree

core/frontend/src/components/autopilot/AutopilotManagerUpdater.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { SemVer } from 'semver'
44
import Notifier from '@/libs/notifier'
55
import autopilot_data from '@/store/autopilot'
66
import autopilot from '@/store/autopilot_manager'
7-
import { Firmware, Vehicle } from '@/types/autopilot'
7+
import { Firmware, SITLFrame, Vehicle } from '@/types/autopilot'
88
import { Dictionary } from '@/types/common'
99
import { autopilot_service } from '@/types/frontend_services'
1010
import back_axios from '@/utils/api'
@@ -114,6 +114,20 @@ export async function fetchFirmwareVehicleType(): Promise<void> {
114114
}
115115
}
116116

117+
export async function fetchSitlFrame(): Promise<void> {
118+
try {
119+
const response: AxiosResponse = await back_axios({
120+
method: 'get',
121+
url: `${autopilot.API_URL}/sitl_frame`,
122+
timeout: 10000,
123+
})
124+
autopilot.setSitlFrame(response.data as SITLFrame)
125+
} catch (error) {
126+
autopilot.setSitlFrame(null)
127+
notifier.pushBackError('AUTOPILOT_SITL_FRAME_FETCH_FAIL', error)
128+
}
129+
}
130+
117131
export async function availableFirmwares(vehicleType: Vehicle): Promise<Firmware[]> {
118132
return back_axios({
119133
method: 'get',
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<template>
2+
<v-card
3+
elevation="0"
4+
class="pa-2"
5+
>
6+
<v-select
7+
:value="autopilot.sitl_frame"
8+
:items="sitl_frame_options"
9+
:disabled="autopilot.restarting || setting"
10+
:loading="autopilot.restarting || setting"
11+
label="SITL frame"
12+
class="ma-1 pa-0"
13+
@change="changeSitlFrame"
14+
/>
15+
<v-snackbar
16+
v-model="show_success"
17+
color="success"
18+
timeout="3000"
19+
>
20+
SITL frame set, autopilot restarted
21+
<template #action="{ attrs }">
22+
<v-btn
23+
text
24+
v-bind="attrs"
25+
@click="show_success = false"
26+
>
27+
Close
28+
</v-btn>
29+
</template>
30+
</v-snackbar>
31+
</v-card>
32+
</template>
33+
34+
<script lang="ts">
35+
import Vue from 'vue'
36+
37+
import { fetchSitlFrame, restart } from '@/components/autopilot/AutopilotManagerUpdater'
38+
import Notifier from '@/libs/notifier'
39+
import { OneMoreTime } from '@/one-more-time'
40+
import autopilot from '@/store/autopilot_manager'
41+
import { SITLFrame } from '@/types/autopilot'
42+
import { autopilot_service } from '@/types/frontend_services'
43+
import back_axios from '@/utils/api'
44+
45+
const notifier = new Notifier(autopilot_service)
46+
47+
export default Vue.extend({
48+
name: 'SitlConfiguration',
49+
data() {
50+
return {
51+
autopilot,
52+
fetch_sitl_frame_task: new OneMoreTime({ delay: 5000, disposeWith: this }),
53+
setting: false,
54+
show_success: false,
55+
}
56+
},
57+
computed: {
58+
sitl_frame_options(): {value: SITLFrame, text: string}[] {
59+
return Object.values(SITLFrame)
60+
.filter((frame) => frame !== SITLFrame.UNDEFINED)
61+
.map((frame) => ({ value: frame, text: frame }))
62+
},
63+
},
64+
mounted() {
65+
this.fetch_sitl_frame_task.setAction(fetchSitlFrame)
66+
},
67+
beforeDestroy() {
68+
autopilot.setSitlFrame(null)
69+
},
70+
methods: {
71+
async changeSitlFrame(frame: SITLFrame): Promise<void> {
72+
this.setting = true
73+
try {
74+
await back_axios({
75+
method: 'post',
76+
url: `${autopilot.API_URL}/sitl_frame`,
77+
params: { frame },
78+
timeout: 10000,
79+
})
80+
autopilot.setSitlFrame(frame)
81+
} catch (error) {
82+
notifier.pushBackError('AUTOPILOT_SITL_FRAME_SET_FAIL', error)
83+
this.setting = false
84+
return
85+
}
86+
this.setting = false
87+
88+
// The new frame is only picked up at SITL launch, so restart so it takes effect.
89+
// restart() pushes its own error notification on failure.
90+
try {
91+
await restart()
92+
this.show_success = true
93+
} catch {
94+
// already surfaced by restart()
95+
}
96+
},
97+
},
98+
})
99+
</script>

core/frontend/src/store/autopilot_manager.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
import store from '@/store'
66
import {
77
AutopilotEndpoint, FirmwareInfo, FirmwareVehicleType,
8-
FlightController, SerialEndpoint,
8+
FlightController, SerialEndpoint, SITLFrame,
99
} from '@/types/autopilot'
1010

1111
@Module({
@@ -29,6 +29,8 @@ class AutopilotManagerStore extends VuexModule {
2929

3030
firmware_vehicle_type: FirmwareVehicleType | null = null
3131

32+
sitl_frame: SITLFrame | null = null
33+
3234
updating_endpoints = true
3335

3436
updating_boards = true
@@ -72,6 +74,11 @@ class AutopilotManagerStore extends VuexModule {
7274
this.firmware_vehicle_type = firmware_vehicle_type
7375
}
7476

77+
@Mutation
78+
setSitlFrame(sitl_frame: SITLFrame | null): void {
79+
this.sitl_frame = sitl_frame
80+
}
81+
7582
@Mutation
7683
setAvailableEndpoints(available_endpoints: AutopilotEndpoint[]): void {
7784
this.available_endpoints = available_endpoints

core/frontend/src/types/autopilot.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,62 @@ export enum Platform {
3030
SITL_ARM = 'SITL_arm_linux_gnueabihf',
3131
}
3232

33+
// Mirrors the SITLFrame enum from
34+
// core/services/ardupilot_manager/typedefs.py
35+
export enum SITLFrame {
36+
QUADPLANE = 'quadplane',
37+
XPLANE = 'xplane',
38+
FIREFLY = 'firefly',
39+
PLUS_CONFIG = '+',
40+
QUAD = 'quad',
41+
COPTER = 'copter',
42+
X_CONFIG = 'x',
43+
BFXREV = 'bfxrev',
44+
BFX = 'bfx',
45+
DJIX = 'djix',
46+
CWX = 'cwx',
47+
HEXA = 'hexa',
48+
HEXA_CWX = 'hexa-cwx',
49+
HEXA_DJI = 'hexa-dji',
50+
OCTA = 'octa',
51+
OCTA_CWX = 'octa-cwx',
52+
OCTA_DJI = 'octa-dji',
53+
OCTA_QUAD_CWX = 'octa-quad-cwx',
54+
DODECA_HEXA = 'dodeca-hexa',
55+
TRI = 'tri',
56+
Y_SIX = 'y6',
57+
HELI = 'heli',
58+
HELI_DUAL = 'heli-dual',
59+
HELI_COMPOUND = 'heli-compound',
60+
SINGLECOPTER = 'singlecopter',
61+
COAXCOPTER = 'coaxcopter',
62+
ROVER = 'rover',
63+
ROVER_SKID = 'rover-skid',
64+
ROVER_VECTORED = 'rover-vectored',
65+
BALANCEBOT = 'balancebot',
66+
SAILBOAT = 'sailboat',
67+
MOTORBOAT = 'motorboat',
68+
MOTORBOAT_SKID = 'motorboat-skid',
69+
CRRCSIM = 'crrcsim',
70+
JSBSIM = 'jsbsim',
71+
FLIGHTAXIS = 'flightaxis',
72+
GAZEBO = 'gazebo',
73+
LAST_LETTER = 'last_letter',
74+
TRACKER = 'tracker',
75+
BALLOON = 'balloon',
76+
PLANE = 'plane',
77+
CALIBRATION = 'calibration',
78+
VECTORED = 'vectored',
79+
VECTORED_6DOF = 'vectored_6dof',
80+
SILENTWINGS = 'silentwings',
81+
MORSE = 'morse',
82+
AIRSIM = 'airsim',
83+
SCRIMMAGE = 'scrimmage',
84+
WEBOTS = 'webots',
85+
JSON = 'JSON',
86+
UNDEFINED = 'undefined',
87+
}
88+
3389
export enum EndpointType {
3490
udpin = 'udpin',
3591
udpout = 'udpout',

core/frontend/src/views/Autopilot.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@
6464
</v-expansion-panel-content>
6565
</v-expansion-panel>
6666
</v-expansion-panels>
67+
<v-expansion-panels v-if="is_sitl">
68+
<v-expansion-panel>
69+
<v-expansion-panel-header>
70+
SITL configuration
71+
</v-expansion-panel-header>
72+
<v-expansion-panel-content>
73+
<sitl-configuration />
74+
</v-expansion-panel-content>
75+
</v-expansion-panel>
76+
</v-expansion-panels>
6777
<v-expansion-panels v-if="settings.is_pirate_mode && isLinuxFlightController">
6878
<v-expansion-panel>
6979
<v-expansion-panel-header>
@@ -139,6 +149,7 @@ import AutopilotSerialConfiguration from '@/components/autopilot/AutopilotSerial
139149
import BoardChangeDialog from '@/components/autopilot/BoardChangeDialog.vue'
140150
import FirmwareManager from '@/components/autopilot/FirmwareManager.vue'
141151
import MasterEndpointManager from '@/components/autopilot/MasterEndpointManager.vue'
152+
import SitlConfiguration from '@/components/autopilot/SitlConfiguration.vue'
142153
import NotSafeOverlay from '@/components/common/NotSafeOverlay.vue'
143154
import { MavAutopilot } from '@/libs/MAVLink2Rest/mavlink2rest-ts/messages/mavlink2rest-enum'
144155
import Notifier from '@/libs/notifier'
@@ -161,6 +172,7 @@ export default Vue.extend({
161172
AutopilotSerialConfiguration,
162173
NotSafeOverlay,
163174
MasterEndpointManager,
175+
SitlConfiguration,
164176
},
165177
data() {
166178
return {
@@ -221,6 +233,9 @@ export default Vue.extend({
221233
is_external_board(): boolean {
222234
return autopilot.current_board?.name === 'Manual'
223235
},
236+
is_sitl(): boolean {
237+
return autopilot.current_board?.name === 'SITL'
238+
},
224239
current_board(): FlightController | null {
225240
return autopilot.current_board
226241
},

0 commit comments

Comments
 (0)