Multi GCS update#14560
Conversation
There was a problem hiding this comment.
Pull request overview
Updates QGroundControl’s multi-GCS operator-control support to align with the finalized MAV_CMD_REQUEST_OPERATOR_CONTROL / CONTROL_STATUS protocol by moving control-status handling into a dedicated manager, gating manual-control outputs, and expanding the toolbar UI and settings needed for multi-operator workflows.
Changes:
- Introduces
GCSControlManagerto own CONTROL_STATUS parsing, operator-control request/release flow, and role state exposed to QML. - Gates joystick/RC override output in
Vehiclebased on operator-control ownership. - Refreshes the toolbar indicator/panel UI, adds a persisted “secondary GCS IDs” setting, and updates bundled indicator artwork and MAVLink dependency pin.
Reviewed changes
Copilot reviewed 11 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Vehicle/Vehicle.h | Exposes gcsControlManager to QML and adds an atomic gate setter for joystick output. |
| src/Vehicle/Vehicle.cc | Instantiates/routs control messages to GCSControlManager and gates joystick send paths. |
| src/Vehicle/GCSControlManager.h | New QObject/QML-facing manager API and properties for operator-control state and actions. |
| src/Vehicle/GCSControlManager.cc | Implements CONTROL_STATUS parsing, operator-control request/release, and takeover ACK handshake. |
| src/Vehicle/CMakeLists.txt | Adds new manager sources to the build. |
| src/Toolbar/GCSControlIndicator.qml | Reworks indicator UI for MAIN/SEC role display, roster, actions, and expanded settings UI. |
| src/Toolbar/CMakeLists.txt | Updates toolbar resource list to the new multi-GCS indicator SVGs. |
| src/Settings/FlyViewSettings.h | Adds operatorControlSecondaryGCS SettingFact. |
| src/Settings/FlyViewSettings.cc | Registers operatorControlSecondaryGCS SettingFact. |
| src/Settings/FlyView.SettingsGroup.json | Defines persisted operatorControlSecondaryGCS string setting metadata. |
| resources/gcscontrolIndicator/multigcs_lock_open.svg | New lock-open role glyph artwork. |
| resources/gcscontrolIndicator/multigcs_lock_closed.svg | New lock-closed role glyph artwork. |
| resources/gcscontrolIndicator/multigcs_line.svg | New “control relationship” line artwork. |
| resources/gcscontrolIndicator/multigcs_device.svg | New primary-role device glyph artwork. |
| resources/gcscontrolIndicator/multigcs_device_alt.svg | New secondary-role outlined device glyph artwork. |
| resources/gcscontrolIndicator/multigcs_aircraft.svg | New aircraft glyph artwork. |
| cmake/CustomOptions.cmake | Updates the pinned MAVLink git tag to a commit containing the finalized protocol. |
- Send the actual GCS sysid in param4 of MAV_CMD_REQUEST_OPERATOR_CONTROL (spec changed from #2158 to #2313; autopilot rejects param4=0), with optional param4/param5 range computed from configured secondary GCS ids - Gate MANUAL_CONTROL and RC override sends on operator control: block joystick output while another GCS is in control - Parse full CONTROL_STATUS (gcs_main + gcs_secondary[10]) and expose secondary GCS list to QML - Add release control support and secondary GCS range configuration UI to GCSControlIndicator - New persisted setting operatorControlSecondaryGCS (comma-separated secondary GCS sysids)
The request countdown could keep running (or restart from a stale value) after the request was already resolved: - The stop check in _handleControlStatus() missed the uncontrolled case: when the owning GCS releases control, CONTROL_STATUS arrives with gcs_main=0 and takeover not allowed, so neither stop condition matched. Add _sysid_in_control == 0 to the check. - The QML progress tracker only stopped on the takeover-allowed transition. Add watchers so it also stops when this GCS gains control or when the C++ side re-allows sending requests. - requestOperatorControlRemainingMsecs was declared CONSTANT but wraps QTimer::remainingTime(), so reopening the popup restored the countdown from a stale cached value. Tie it to sendControlRequestAllowedChanged instead. - releaseOperatorControl() left the request and takeover-revert timers running, firing stale signals after release. Stop them first. - A pending takeover revert also kept running when control moved to another GCS. Stop it on control loss in _handleControlStatus().
When takeover is not allowed, the autopilot forwards MAV_CMD_REQUEST_OPERATOR_CONTROL to the GCS in control as a notification. QGC showed the popup but never sent COMMAND_ACK, leaving the autopilot notification pending and the command unacknowledged at the MAVLink level. Reply with MAV_RESULT_ACCEPTED to the sender.
…onent The spec says a GCS should monitor for CONTROL_STATUS with the GCS_CONTROL_STATUS_FLAGS_SYSTEM_MANAGER flag and address MAV_CMD_REQUEST_OPERATOR_CONTROL to that component, which is not necessarily the autopilot. Learn the component id from CONTROL_STATUS and use it as the command target, falling back to the default component id until the first CONTROL_STATUS is seen.
_joystickSendAllowed was read and written with memory_order_relaxed. On weakly ordered architectures the store could in principle be observed by the joystick thread after other effects of the CONTROL_STATUS handling. Use release on the store and acquire on the loads so the gate change is properly ordered with the control status update that caused it.
The operator control request encodes secondary GCS as a contiguous sysid range, so non-contiguous configured ids (e.g. 200, 254) silently grant control to every id in between. Show a warning in the range configuration panel with the number of unconfigured ids the computed range would accept.
Replace the line/device/gcs icon set with a state-driven composition using new artwork (aircraft, line, solid/outlined device, open/closed lock): - aircraft (top-left): green when any GCS controls the vehicle - line (bottom-left): green only when this GCS has an operator role (its own control link) - role glyph (right), always white: solid device = primary, outlined device = secondary, lock when this GCS has no role - open when control is acquirable now (uncontrolled or takeover allowed), closed when a request is required - PRIM/SEC label shown in green when this GCS is primary/secondary The lock open/closed condition reuses controlGrantedImmediately, so it always agrees with the request button (Take Control / Acquire / Send Request). > Co-authored-by: alexdelatorre <alex.delatorre@lincesystems.com>
Replace the flat label list with a labelled status roster (Control status / Main GCS / Secondary GCS / Takeover), mark this GCS in each row, and move editable settings behind a More options expander with a control group toggle. Recognize a secondary's role even when the vehicle is uncontrolled (gcs_main == 0).
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
817d6ee to
47d0f04
Compare
Secondary/owner membership is the flight stack's configuration (MAV_GCS_SYSID/_HI on ArduPilot), not something a control request may redefine: CONTROL_STATUS specifies gcs_secondary as set by the flight stack, and MAV_CMD_REQUEST_OPERATOR_CONTROL.param5 has been removed from the spec (mavlink/mavlink#2535). Remove the secondary-GCS-IDs setting and its settings-panel UI, and the range computation derived from it. A control request now only asks for the gcs_main role: param4 carries this GCS's system id and param5 is always 0. Secondaries shown in the indicator continue to come from CONTROL_STATUS, which is now their only source.
…vehicle notify Address the operator-control review findings: - A request denied by the vehicle (requester not an authorized owner) was only logged while the UI kept a "Request sent" countdown and the request button stayed locked out. Cancel the countdown and show an app message on MAV_RESULT_DENIED and other non-pending rejections; keep the countdown only for FAILED/IN_PROGRESS (owner notified, request genuinely pending). - Per spec, CONTROL_STATUS without GCS_CONTROL_STATUS_FLAGS_SYSTEM_MANAGER describes only the emitting component; stop applying it to system-level control state. - A takeover request arriving on a non-active vehicle was ACKed and silently dropped (the popup is bound to the active vehicle). Show an app-level message directing the operator to that vehicle. Also: ignore forwarded releases (param1 != 1) instead of raising a takeover popup; use the sanitized timeout for the local request lockout; fix the received-request timeout fallback which mixed seconds into a milliseconds property (a param3=0 request would auto-close its popup in ~10 ms); refresh stale param5/comment references and drop an unused QML property binding.
|
The protocol model evolved after discussions in ArduPilot/ardupilot#33332 and mavlink/mavlink#2535, and the branch has been updated accordingly. The main change from the original description: Owner/secondary membership is flight-stack configuration. QGC no longer configures the secondary GCS set — the "configurable secondary-GCS range" and its Also since the description was written:
Retested against peterbarker/ardupilot#46 in multi-GCS SITL (4× QGC + MAVProxy). Executables for Linux Exectuables for Windows Updated screenshots of the panel with the new display-only membership: Compacted and extended popups now
If multi owner mode, shown from a GCS out of the range trying to request control
|
|
See the Build Results workflow run for details. |



What this does
Brings QGC's GCS operator-control support up to the final MAV_CMD_REQUEST_OPERATOR_CONTROL / CONTROL_STATUS protocol (mavlink/mavlink#2313), succeeding the earlier work in #12410.
Long story short - Now multiple GCS can share a vehicle with one primary holding exclusive manual control; others are recognized secondaries and can send commands, missions, etc but not manual control. Adds the operator-control panel, request/release flow, takeover handshake, and a configurable secondary-GCS range.
Tested against peterbarker/ardupilot#46 in multi-GCS SITL (4× QGC + MAVProxy for routing).
Changes
Protocol / Vehicle
UI (GCSControlIndicator)
Settings
operatorControlSecondaryGCS(secondary sysids, comma- or space-separated).EDIT - Pending items after discussions in ArduPilot/ardupilot#33332 and ArduPilot/mavlink#503
Some screenshots, Artwork and UI by @alexdelatorre
Top toolbar Icon, depending on status:
reduced popup, after single click, depending on status:
Command request from a GCS to the main GCS:
expanded panel with aditional options:
Secondary GCS input, detail of warning for user awareness: