POI that maps 1:1 onto an ArduPilot/Mission Planner mission item, so the Dashboard, Gateway, and flight controller all speak the same frame/command/param contract.
This page covers the build → upload → execute loop entirely from the frontend’s perspective. For the on-map rendering of waypoint spheres and flight-path lines see Map & Live Vehicle Tracking; for the Gateway’s mission REST endpoints and MAVLink storage format see the backend pages linked below.
Missions & Geofences API
Gateway REST surface for mission/point CRUD and
push_mission / start_mission actions.Mission & Geofence MAVLink Format
How the Gateway stores waypoints and converts them for upload (
to_mavlink_waypoint).DroneControlService & Rosbridge
Where
push_mission/start_mission dispatch to the vehicle over rosbridge.Map & Live Vehicle Tracking
Waypoint sphere rendering, flight-path lines, and AUTO-mode line advancement.
The POI (waypoint) data model
A mission is aMission { id, name, mission_points: POI[] }. Each POI (src/app/models/poi.model.ts) carries both a human-friendly identity and the MAVLink waypoint fields that Mission Planner uses:
| Field | Type | Notes |
|---|---|---|
id | string | Empty string for a not-yet-persisted point |
lat / lng | number | WGS84 degrees; become x_lat / y_long on upload |
altitude | number | Meters; becomes z_alt on upload |
type | base | fly | safepoint | UI marker color / semantics (safepoint is excluded from the flight-path line) |
label | string? | Display label (defaults to Point N) |
sequence | number | 1-based order; drives OrderList and map line ordering |
mission_id | number? | Owning mission |
frame | number? | 0 = GLOBAL, 3 = GLOBAL_RELATIVE_ALT (default), 2 = MISSION |
command | number? | MAVLink command (default 16) — see table below |
is_current | boolean? | Whether this is the active waypoint (default false) |
autocontinue | boolean? | Always sent as true from the editor — ArduPilot ignores it for waypoints |
param1–param4 | number? | Command-specific MAVLink parameters (default 0.0) |
The frontend
POI interface mirrors the Gateway MissionPoint ORM model exactly (skyhub_gateway_service/src/models/mission_point.py). If you add a waypoint field you must add it in both places plus to_dict / to_mavlink_waypoint on the backend, or it will be silently dropped on upload.Supported MAVLink commands
The editor exposes five commands. Their labels, icons, and per-command parameter meanings are hard-coded in three places that must stay in sync:edit-marker-dialog.component.ts (form options + paramConfigs), edit-mission.component.ts (commandTypes / commandIcons sidebar labels), and home.component.ts (map rendering skip-list).
| Command | MAVLink name | Sidebar label / icon | Needs lat/lng? | Notable params |
|---|---|---|---|---|
16 | MAV_CMD_NAV_WAYPOINT | Waypoint · pi-map-marker | Yes | param1 = hold time (s) |
22 | MAV_CMD_NAV_TAKEOFF | Takeoff · pi-arrow-up | Yes | param1 = min pitch (plane), param4 = yaw |
21 | MAV_CMD_NAV_LAND | Land · pi-arrow-down | Yes | param4 = yaw angle |
20 | MAV_CMD_NAV_RETURN_TO_LAUNCH | RTL · pi-home | No | takes no parameters |
178 | MAV_CMD_DO_CHANGE_SPEED | Set Speed · pi-gauge | No | param1 = speed type, param2 = target m/s |
Building a mission on the map
Create the mission
The create-mission dialog calls
MissionService.creatMission(name) → POST {environment.url}/mission. On success it emits the new Mission; HomeComponent.closeEditMissionModal sets it as the current mission (AppStateService.setCurrentMission) and flips on edit mode (setEditMissionMode(true)). Note the method name is intentionally creatMission (missing “e”) in mission.service.ts:14.Enter edit mode
editMissionMode$ opens the EditMissionComponent sidebar and sets the map cursor to a crosshair. Mission edit and geofence edit modes are mutually exclusive by construction in AppStateService.Click the map to add a waypoint
Map clicks are handled by
HomeComponent.onMapClickAddMarker (home.component.ts:1871). When a mission is loaded and edit mode is on (the mission && editMode branch, ~line 2062), it drops a Three.js sphere via addMissionPoint, builds a draft POI (default type: 'fly', altitude: 50, next sequence), and opens the edit-marker dialog. Ruler mode, geofence-point mode, pick-location mode, and go-to mode are checked earlier in the same handler and take precedence.Tune the waypoint in the edit-marker dialog
EditMarkerDialogComponent (edit-marker-dialog.component.ts) builds a reactive form with label, type, lat, lng, sequence, altitude, frame (default 3), command (default 16), autocontinue (forced true), and param1–param4. The visible parameter labels change per selected command via currentParamConfigs. “Pick from map” (startPickingLocation) lets you re-click the map to set coordinates.Save (create or update)
onSave branches on whether currentMarkerData.id exists. New points call MissionService.createMissionPoint([{ mission_id, ...formValue }]) → POST /mission/point; existing points merge into the ordered array and call updateMissionPoints → PATCH /mission/{id}/points. Both return the updated Mission, which flows back through onMissionPointSave → setCurrentMission, and the currentMission$ subscription re-runs renderMission.Adding a point at the vehicle’s current position
The sidebar’s Add button callsEditMissionComponent.onPointAddCurrentPos (edit-mission.component.ts:102). It reads the selected drone’s live position from AppStateService.getCurrentDronePosition(droneId) (a [lng, lat, alt] tuple from telemetry), assigns the next sequence, and defaults altitude from the telemetry Z value — except rovers, which are pinned to altitude: 0. It then emits pointEdit so the operator can review the point in the dialog before saving.
Reordering waypoints (OrderList)
TheEditMissionComponent sidebar renders waypoints in a PrimeNG p-orderList with [dragdrop]="true", sorted by sequence (edit-mission.component.html:33). Dragging fires onReorder, which:
- Rewrites
sequence = index + 1across the whole list based on the new visual order. - Persists via
MissionService.updateMissionPoints(reorderedPoints, missionId)→PATCH /mission/{missionId}/points. - On success calls
setCurrentMission(updatedMission)to re-render the map; on error it reverts the local array to the last-known sorted order.
Component & service reference
| File | Role in mission planning |
|---|---|
src/app/services/mission.service.ts | REST client: creatMission, createMissionPoint, updateMissionPoint, updateMissionPoints (bulk reorder), getMissions, deleteMissionPoint, plus updateCurrentMissionPointIndex (advances the active waypoint when within 3 m, used for AUTO-mode line drawing). |
src/app/home/edit-mission/edit-mission.component.ts | Waypoint sidebar: OrderList reorder, command name/icon maps, add-at-current-position, emits uploadMission. |
src/app/home/dialogs/edit-marker-dialog/edit-marker-dialog.component.ts | Per-waypoint form: command/frame options, per-command paramConfigs, coordinate gating, create vs update save. |
src/app/home/home.component.ts | Map-click add (onMapClickAddMarker), renderMission, onUploadMission, mission edit-mode wiring. |
src/app/services/drone-service/drone.service.ts | pushMission(droneId, missionId) and startMission(droneId, takeoffAltitude?) action clients. |
src/app/home/dialogs/takeoff-altitude-dialog/takeoff-altitude-dialog.component.ts | Prompts for takeoff altitude, calls startMission, then monitors altitude. |
src/app/home/dialogs/mission-confirmation-dialog/mission-confirmation-dialog.component.ts | 15 s countdown → switch to AUTO (or RTL on cancel). |
Uploading a mission to the vehicle
The sidebar Upload button →EditMissionComponent emits uploadMission → HomeComponent.onUploadMission (home.component.ts:3115). It validates that a mission, a selected drone, and at least one waypoint exist, shows the progress bar, then calls:
src/app/services/drone-service/drone.service.ts
response.result.waypoints_transferred and the selected drone’s mission_id is updated locally (setSelectedDrone + droneSubscriptionService.updateDroneInList).
What the Gateway does on upload
DroneControlService.push_mission (skyhub_gateway_service/src/service/drone_control_service.py:784) loads the stored MissionPoints, converts each with to_mavlink_waypoint (lat → x_lat, lng → y_long, altitude → z_alt), and prepends a synthetic TAKEOFF (command 22, frame 3, lat/lng 0,0) only if the first stored command isn’t already 22. It then calls the rosbridge /mavros/mission/push service and asserts wp_transfered == len(waypoints) before recording drone.mission_id.
Starting (executing) a mission
Starting is a separate action from uploading and is driven from the flight-control bar, not the mission sidebar.MissionControlComponent.onStartMission validates the mission has points, then opens the takeoff-altitude dialog in start_mission mode.
Step by step:
TakeoffAltitudeDialogComponent.confirm(takeoff-altitude-dialog.component.ts:76) callsDroneService.startMission(droneId, altitude)→POST /drone/action/start_missionwith{ drone_id, takeoff_altitude }.- Gateway
start_mission(drone_control_service.py:615) re-uploads the mission, setsGUIDED, arms, and issues aGUIDEDtakeoff to the requested altitude. It deliberately does not switch to AUTO itself. - The dialog monitors altitude by subscribing to
websocketService.relAltSubject; when relative altitude reachestarget - 0.5 mit opens the mission-confirmation dialog. MissionConfirmationDialogComponentruns a 15 s countdown, then callsDroneService.setMode(droneId, 'AUTO')to begin flying the waypoints. Cancelling instead issuessetMode(droneId, 'RTL').
The AUTO switch is a client-side decision, gated on live
RELATIVE_ALT telemetry. A server-side helper _monitor_takeoff_and_switch_to_auto exists in the Gateway but is not called by start_mission. If the telemetry Socket.IO stream drops during climb-out, the confirmation dialog never appears and the vehicle holds in GUIDED at takeoff altitude — keep the telemetry client healthy while a mission is arming.Rendering on the map (brief)
HomeComponent.renderMission (home.component.ts:1312) removes prior mission elements, draws the connecting flight-path line (renderMapLines), then adds a Threebox sphere per waypoint plus a dotted vertical ground line, skipping the coordinate-less commands [20, 178]. In AUTO mode, MissionService.updateCurrentMissionPointIndex advances the highlighted leg as the vehicle passes within 3 m of each point. The full 3D rendering pipeline (render order, Threebox layer, marker disposal) is documented in Map & Live Vehicle Tracking.
Related: geofence editing
Geofences share the map-click + MAVLink pattern but live in a sibling flow (EditGeofenceComponent, edit-geofence-point-dialog). Points use the ArduPilot fence commands from FENCE_COMMANDS (src/app/models/geofence.model.ts): 5001/5002 polygon vertex inclusion/exclusion and 5003/5004 circle inclusion/exclusion (param1 = radius). Circle geofences are limited to a single center point. They upload through DroneService.pushGeofence / syncGeofences / clearFence rather than push_mission. See Missions & Geofences API and Mission & Geofence MAVLink Format for the backend contract.
Gotchas to preserve
Three copies of the command map must stay in sync
Three copies of the command map must stay in sync
Command labels/icons/params are duplicated in
edit-marker-dialog.component.ts (commandOptions + paramConfigs), edit-mission.component.ts (commandTypes / commandIcons), and the [20, 178] skip-lists in home.component.ts. Add a command in all of them or the sidebar, dialog, and map will disagree.Coordinate-less commands must be skipped on the map
Coordinate-less commands must be skipped on the map
RTL (
20) and Set Speed (178) have no valid lat/lng. They are excluded from waypoint spheres, ground lines, and the flight-path line, and their coordinate inputs are disabled in the dialog. Removing either guard renders a marker at (0,0) off the coast of Africa.Upload ≠ start
Upload ≠ start
push_mission only transfers waypoints; start_mission re-uploads, arms, and takes off in GUIDED. They are separate endpoints and separate UI actions. The AUTO transition is client-driven via altitude telemetry, not part of start_mission.Backend adds exactly one TAKEOFF, nothing else
Backend adds exactly one TAKEOFF, nothing else
If the first stored waypoint isn’t command
22, the Gateway prepends a single TAKEOFF. There is no auto-generated return path, RTL, or speed item despite older docs — the stored sequence is uploaded verbatim otherwise.sequence drives everything
sequence drives everything
sequence orders the sidebar, the map line, and the upload. Reorders renumber it contiguously from 1; a gap or duplicate desyncs the views and can reorder the uploaded mission unexpectedly.
