WaypointPush service items and dispatched over the rosbridge connection. This page documents that ORM → MAVLink translation, the one piece of logic the gateway injects (an auto-prepended TAKEOFF), and the fence command mapping — plus a stale documentation warning you must not trust.
This format is the shared contract between three surfaces: the Dashboard mission planner writes these fields, the gateway persists and translates them here, and the drone’s MAVROS consumes them. See Missions & Geofences API for the CRUD endpoints, Dashboard mission planning for the editor, and MAVLink routing for the on-drone side.
End-to-end upload flow
The MissionPoint model
src/models/mission_point.py carries both a semantic classification (type) used by the UI and the full MAVLink waypoint payload. A single row is one waypoint.
mission_id and user_id are both ON DELETE CASCADE FKs. The parent Mission (src/models/mission.py) is just a header (user_id, name, unique on uq_user_name) with a mission_points relationship. Full schema context lives in Database Schema Overview.
MAVLink command / frame reference
These are the command codes the fields hold. Only16 and 22 are produced automatically by the gateway; the rest come from whatever the Dashboard persisted.
to_mavlink_waypoint() conversion
Each row emits exactly one rosbridge waypoint dict via MissionPoint.to_mavlink_waypoint() (src/models/mission_point.py:59):
src/models/mission_point.py
push_mission() — the auto-TAKEOFF prepend
DroneControlService.push_mission() (src/service/drone_control_service.py:784) is the only place the gateway adds logic on top of the stored rows. It is invoked from POST /api/v1/drone/action/push_mission and internally by start_mission().
1
Load & convert
mission_service.get_mission_points(mission_id, user_id) returns rows ordered by sequence; each is mapped through to_mavlink_waypoint().2
Prepend TAKEOFF if missing
If the list is non-empty and the first command is not
22, a synthetic TAKEOFF is inserted at index 0: frame=3, command=22, x_lat=0.0, y_long=0.0 (ArduPilot takes off from current position when lat/lng are 0). The takeoff altitude is custom_takeoff_altitude if provided, otherwise the first waypoint’s z_alt (falling back to 10.0).3
Push over rosbridge
Sends a
call_service frame to PUSH_MISSION = ("/mavros/mission/push", "mavros_msgs/srv/WaypointPush") with {"start_index": 0, "waypoints": [...]}, waiting up to ROSBRIDGE_SERVICE_TIMEOUT (30s) for a response.4
Verify transfer count
Requires
response["success"] and values["wp_transfered"] == len(mavlink_waypoints); otherwise raises Mission upload incomplete.5
Record last-uploaded mission
On success,
drone_service.update({"id": drone_id, "mission_id": mission_id}) stamps the drone row so it remembers the mission it is carrying.src/service/drone_control_service.py
wp_transfered is spelled with one r — that is the actual field name in the MAVROS WaypointPush response, not a typo in this codebase. Do not “correct” it.Relationship to start_mission
POST /api/v1/drone/action/start_mission {drone_id, takeoff_altitude?} calls push_mission() again (re-uploading the drone’s assigned mission), then sets GUIDED mode, arms, and issues a GUIDED takeoff. It does not switch the vehicle to AUTO; the Dashboard monitors relative altitude and performs the AUTO switch. A server-side helper _monitor_takeoff_and_switch_to_auto() (src/service/drone_control_service.py:676) exists for this but is not invoked by start_mission(). See DroneControlService & Rosbridge Dispatch for the command layer.
Geofences: fence commands 5001-5004
Geofences reuse the same MAVLink item shape but with fence commands instead of nav commands. AGeofence (src/models/geofence.py) is type (polygon / circle) × fence_type (inclusion / exclusion, default exclusion) with cascade-deleted GeofencePoint children.
src/models/geofence_point.py defines the command mapping:
get_fence_command(geofence_type, fence_type) selects the code from the type × fence_type pair. Each vertex/center is a GeofencePoint (lat, lng, sequence, frame default 3, command NOT NULL, param1-param4).
GeofencePoint.to_mavlink_fence_item() (src/models/geofence_point.py:66) mirrors to_mavlink_waypoint() but forces z_alt = 0.0 because fences are 2D:
src/models/geofence_point.py
push_geofence() and friends
DroneControlService.push_geofence() (src/service/drone_control_service.py:866) loads the geofence with points, computes the command via get_fence_command(geofence.type, geofence.fence_type or "exclusion"), then overrides every item’s command with that value. For polygons it also overwrites param1 with the total vertex count (len(fence_items)); circles keep the stored radius. It pushes via a dedicated service:
src/utils/mavros_topics.py
push_mission: response.success and wp_transfered == len(fence_items).
Endpoint summary
All actions arePOST under /api/v1, JWT-protected (@jwt_required()), defined in src/routes/drone_routes.py.
Example upload:
Missions & Geofences API
CRUD endpoints that populate these tables.
DroneControlService
How
call_service frames reach the drone over rosbridge.Rosbridge Connection
The
send_service_call_with_response transport and timeouts.Schema Overview
Full column list and cascade rules for these tables.

